[DEV-126] Get rid of tfSpent in txoFlags (#69)

This commit is contained in:
Ori Newman 2018-09-30 12:30:40 +03:00 committed by stasatdaglabs
parent fae411706e
commit 18f54d13a4
9 changed files with 18 additions and 122 deletions

View File

@ -482,10 +482,7 @@ func recycleOutpointKey(key *[]byte) {
// utxoEntryHeaderCode returns the calculated header code to be used when
// serializing the provided utxo entry.
func utxoEntryHeaderCode(entry *UTXOEntry) (uint64, error) {
if entry.IsSpent() {
return 0, AssertError("attempt to serialize spent UTXO header")
}
func utxoEntryHeaderCode(entry *UTXOEntry) uint64 {
// As described in the serialization format comments, the header code
// encodes the height shifted over one bit and the coinbase flag in the
@ -495,22 +492,15 @@ func utxoEntryHeaderCode(entry *UTXOEntry) (uint64, error) {
headerCode |= 0x01
}
return headerCode, nil
return headerCode
}
// serializeUTXOEntry returns the entry serialized to a format that is suitable
// for long-term storage. The format is described in detail above.
func serializeUTXOEntry(entry *UTXOEntry) ([]byte, error) {
// Spent outputs have no serialization.
if entry.IsSpent() {
return nil, nil
}
// Encode the header code.
headerCode, err := utxoEntryHeaderCode(entry)
if err != nil {
return nil, err
}
headerCode := utxoEntryHeaderCode(entry)
// Calculate the size needed to serialize the entry.
size := serializeSizeVLQ(headerCode) +

View File

@ -426,18 +426,6 @@ func TestUtxoSerialization(t *testing.T) {
serialized: hexToBytes("03320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"),
},
// From tx in main blockchain:
// b7c3332bc138e2c9429818f5fed500bcc1746544218772389054dc8047d7cd3f:0
{
name: "height 1, coinbase, spent",
entry: &UTXOEntry{
amount: 5000000000,
pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"),
blockHeight: 1,
packedFlags: tfCoinBase | tfSpent,
},
serialized: nil,
},
// From tx in main blockchain:
// 8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb:1
{
name: "height 100001, not coinbase",
@ -449,18 +437,6 @@ func TestUtxoSerialization(t *testing.T) {
},
serialized: hexToBytes("8b99420700ee8bd501094a7d5ca318da2506de35e1cb025ddc"),
},
// From tx in main blockchain:
// 8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb:1
{
name: "height 100001, not coinbase, spent",
entry: &UTXOEntry{
amount: 1000000,
pkScript: hexToBytes("76a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac"),
blockHeight: 100001,
packedFlags: tfSpent,
},
serialized: nil,
},
}
for i, test := range tests {
@ -478,12 +454,6 @@ func TestUtxoSerialization(t *testing.T) {
continue
}
// Don't try to deserialize if the test entry was spent since it
// will have a nil serialization.
if test.entry.IsSpent() {
continue
}
// Deserialize to a utxo entry.
utxoEntry, err := deserializeUTXOEntry(test.serialized)
if err != nil {
@ -492,14 +462,6 @@ func TestUtxoSerialization(t *testing.T) {
continue
}
// The deserialized entry must not be marked spent since unspent
// entries are not serialized.
if utxoEntry.IsSpent() {
t.Errorf("deserializeUTXOEntry #%d (%s) output should "+
"not be marked spent", i, test.name)
continue
}
// Ensure the deserialized entry has the same properties as the
// ones in the test entry.
if utxoEntry.Amount() != test.entry.Amount() {
@ -530,41 +492,6 @@ func TestUtxoSerialization(t *testing.T) {
}
}
// TestUtxoEntryHeaderCodeErrors performs negative tests against unspent
// transaction output header codes to ensure error paths work as expected.
func TestUtxoEntryHeaderCodeErrors(t *testing.T) {
t.Parallel()
tests := []struct {
name string
entry *UTXOEntry
code uint64
errType error
}{
{
name: "Force assertion due to spent output",
entry: &UTXOEntry{packedFlags: tfSpent},
errType: AssertError(""),
},
}
for _, test := range tests {
// Ensure the expected error type is returned and the code is 0.
code, err := utxoEntryHeaderCode(test.entry)
if reflect.TypeOf(err) != reflect.TypeOf(test.errType) {
t.Errorf("utxoEntryHeaderCode (%s): expected error "+
"type does not match - got %T, want %T",
test.name, err, test.errType)
continue
}
if code != 0 {
t.Errorf("utxoEntryHeaderCode (%s): unexpected code "+
"on error - got %d, want 0", test.name, code)
continue
}
}
}
// TestUtxoEntryDeserializeErrors performs negative tests against deserializing
// unspent transaction outputs to ensure error paths work as expected.
func TestUtxoEntryDeserializeErrors(t *testing.T) {

View File

@ -42,24 +42,6 @@ func (entry *UTXOEntry) BlockHeight() int32 {
return entry.blockHeight
}
// IsSpent returns whether or not the output has been spent based upon the
// current state of the unspent transaction output view it was obtained from.
func (entry *UTXOEntry) IsSpent() bool {
return entry.packedFlags&tfSpent == tfSpent
}
// Spend marks the output as spent. Spending an output that is already spent
// has no effect.
func (entry *UTXOEntry) Spend() {
// Nothing to do if the output is already spent.
if entry.IsSpent() {
return
}
// Mark the output as spent.
entry.packedFlags |= tfSpent
}
// Amount returns the amount of the output.
func (entry *UTXOEntry) Amount() int64 {
return entry.amount
@ -91,9 +73,6 @@ type txoFlags uint8
const (
// tfCoinBase indicates that a txout was contained in a coinbase tx.
tfCoinBase txoFlags = 1 << iota
// tfSpent indicates that a txout is spent.
tfSpent
)
// utxoCollection represents a set of UTXOs indexed by their outPoints

View File

@ -349,7 +349,7 @@ func CountP2SHSigOps(tx *util.Tx, isCoinBaseTx bool, utxoSet UTXOSet) (int, erro
for txInIndex, txIn := range msgTx.TxIn {
// Ensure the referenced input transaction is available.
entry, ok := utxoSet.Get(txIn.PreviousOutPoint)
if !ok || entry.IsSpent() {
if !ok {
str := fmt.Sprintf("output %v referenced from "+
"transaction %s:%d either does not exist or "+
"has already been spent", txIn.PreviousOutPoint,
@ -792,7 +792,7 @@ func (dag *BlockDAG) ensureNoDuplicateTx(node *blockNode, block *util.Block) err
// is fully spent.
for outpoint := range fetchSet {
utxo, ok := dag.virtual.GetUTXOEntry(outpoint)
if ok && !utxo.IsSpent() {
if ok {
str := fmt.Sprintf("tried to overwrite transaction %v "+
"at block height %d that is not fully spent",
outpoint.Hash, utxo.BlockHeight())
@ -825,7 +825,7 @@ func CheckTransactionInputs(tx *util.Tx, txHeight int32, utxoSet UTXOSet, dagPar
for txInIndex, txIn := range tx.MsgTx().TxIn {
// Ensure the referenced input transaction is available.
entry, ok := utxoSet.Get(txIn.PreviousOutPoint)
if !ok || entry.IsSpent() {
if !ok {
str := fmt.Sprintf("output %v referenced from "+
"transaction %s:%d either does not exist or "+
"has already been spent", txIn.PreviousOutPoint,

View File

@ -709,8 +709,8 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, isNew, rateLimit, rejectDu
prevOut := wire.OutPoint{Hash: *txHash}
for txOutIdx := range tx.MsgTx().TxOut {
prevOut.Index = uint32(txOutIdx)
entry, ok := mp.mpUTXOSet.Get(prevOut)
if ok && !entry.IsSpent() {
_, ok := mp.mpUTXOSet.Get(prevOut)
if ok {
return nil, nil, txRuleError(wire.RejectDuplicate,
"transaction already exists")
}

View File

@ -486,8 +486,8 @@ mempoolLoop:
prioItem := &txPrioItem{tx: tx}
for _, txIn := range tx.MsgTx().TxIn {
originHash := &txIn.PreviousOutPoint.Hash
entry, ok := blockUtxos.Get(txIn.PreviousOutPoint)
if !ok || entry.IsSpent() {
_, ok := blockUtxos.Get(txIn.PreviousOutPoint)
if !ok {
if !g.txSource.HaveTransaction(originHash) {
log.Tracef("Skipping tx %s because it "+
"references unspent output %s "+
@ -716,12 +716,12 @@ mempoolLoop:
merkles := blockdag.BuildMerkleTreeStore(blockTxns)
var msgBlock wire.MsgBlock
msgBlock.Header = wire.BlockHeader{
Version: nextBlockVersion,
Version: nextBlockVersion,
NumPrevBlocks: byte(len(virtualBlock.TipHashes())),
PrevBlocks: virtualBlock.TipHashes(),
MerkleRoot: *merkles[len(merkles)-1],
Timestamp: ts,
Bits: reqDifficulty,
PrevBlocks: virtualBlock.TipHashes(),
MerkleRoot: *merkles[len(merkles)-1],
Timestamp: ts,
Bits: reqDifficulty,
}
for _, tx := range blockTxns {
if err := msgBlock.AddTransaction(tx.MsgTx()); err != nil {

View File

@ -60,7 +60,7 @@ func calcInputValueAge(tx *wire.MsgTx, utxoSet blockdag.UTXOSet, nextBlockHeight
// Don't attempt to accumulate the total input age if the
// referenced transaction output doesn't exist.
entry, ok := utxoSet.Get(txIn.PreviousOutPoint)
if ok && !entry.IsSpent() {
if ok {
// Inputs with dependencies currently in the mempool
// have their block height set to a special constant.
// Their input age should computed as zero since their

View File

@ -879,7 +879,7 @@ func (sm *SyncManager) haveInventory(invVect *wire.InvVect) (bool, error) {
if !ok {
return false, nil
}
if entry != nil && !entry.IsSpent() {
if entry != nil {
return true, nil
}
}

View File

@ -2569,7 +2569,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte
// transaction already in the main chain. Mined transactions
// that are spent by a mempool transaction are not affected by
// this.
if entry == nil || entry.IsSpent() {
if entry == nil {
return nil, nil
}