mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-07-05 20:32:31 +00:00
[DEV-126] Get rid of tfSpent in txoFlags (#69)
This commit is contained in:
parent
fae411706e
commit
18f54d13a4
@ -482,10 +482,7 @@ func recycleOutpointKey(key *[]byte) {
|
|||||||
|
|
||||||
// utxoEntryHeaderCode returns the calculated header code to be used when
|
// utxoEntryHeaderCode returns the calculated header code to be used when
|
||||||
// serializing the provided utxo entry.
|
// serializing the provided utxo entry.
|
||||||
func utxoEntryHeaderCode(entry *UTXOEntry) (uint64, error) {
|
func utxoEntryHeaderCode(entry *UTXOEntry) uint64 {
|
||||||
if entry.IsSpent() {
|
|
||||||
return 0, AssertError("attempt to serialize spent UTXO header")
|
|
||||||
}
|
|
||||||
|
|
||||||
// As described in the serialization format comments, the header code
|
// As described in the serialization format comments, the header code
|
||||||
// encodes the height shifted over one bit and the coinbase flag in the
|
// 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
|
headerCode |= 0x01
|
||||||
}
|
}
|
||||||
|
|
||||||
return headerCode, nil
|
return headerCode
|
||||||
}
|
}
|
||||||
|
|
||||||
// serializeUTXOEntry returns the entry serialized to a format that is suitable
|
// serializeUTXOEntry returns the entry serialized to a format that is suitable
|
||||||
// for long-term storage. The format is described in detail above.
|
// for long-term storage. The format is described in detail above.
|
||||||
func serializeUTXOEntry(entry *UTXOEntry) ([]byte, error) {
|
func serializeUTXOEntry(entry *UTXOEntry) ([]byte, error) {
|
||||||
// Spent outputs have no serialization.
|
|
||||||
if entry.IsSpent() {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode the header code.
|
// Encode the header code.
|
||||||
headerCode, err := utxoEntryHeaderCode(entry)
|
headerCode := utxoEntryHeaderCode(entry)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the size needed to serialize the entry.
|
// Calculate the size needed to serialize the entry.
|
||||||
size := serializeSizeVLQ(headerCode) +
|
size := serializeSizeVLQ(headerCode) +
|
||||||
|
@ -426,18 +426,6 @@ func TestUtxoSerialization(t *testing.T) {
|
|||||||
serialized: hexToBytes("03320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"),
|
serialized: hexToBytes("03320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"),
|
||||||
},
|
},
|
||||||
// From tx in main blockchain:
|
// 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
|
// 8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb:1
|
||||||
{
|
{
|
||||||
name: "height 100001, not coinbase",
|
name: "height 100001, not coinbase",
|
||||||
@ -449,18 +437,6 @@ func TestUtxoSerialization(t *testing.T) {
|
|||||||
},
|
},
|
||||||
serialized: hexToBytes("8b99420700ee8bd501094a7d5ca318da2506de35e1cb025ddc"),
|
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 {
|
for i, test := range tests {
|
||||||
@ -478,12 +454,6 @@ func TestUtxoSerialization(t *testing.T) {
|
|||||||
continue
|
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.
|
// Deserialize to a utxo entry.
|
||||||
utxoEntry, err := deserializeUTXOEntry(test.serialized)
|
utxoEntry, err := deserializeUTXOEntry(test.serialized)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -492,14 +462,6 @@ func TestUtxoSerialization(t *testing.T) {
|
|||||||
continue
|
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
|
// Ensure the deserialized entry has the same properties as the
|
||||||
// ones in the test entry.
|
// ones in the test entry.
|
||||||
if utxoEntry.Amount() != test.entry.Amount() {
|
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
|
// TestUtxoEntryDeserializeErrors performs negative tests against deserializing
|
||||||
// unspent transaction outputs to ensure error paths work as expected.
|
// unspent transaction outputs to ensure error paths work as expected.
|
||||||
func TestUtxoEntryDeserializeErrors(t *testing.T) {
|
func TestUtxoEntryDeserializeErrors(t *testing.T) {
|
||||||
|
@ -42,24 +42,6 @@ func (entry *UTXOEntry) BlockHeight() int32 {
|
|||||||
return entry.blockHeight
|
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.
|
// Amount returns the amount of the output.
|
||||||
func (entry *UTXOEntry) Amount() int64 {
|
func (entry *UTXOEntry) Amount() int64 {
|
||||||
return entry.amount
|
return entry.amount
|
||||||
@ -91,9 +73,6 @@ type txoFlags uint8
|
|||||||
const (
|
const (
|
||||||
// tfCoinBase indicates that a txout was contained in a coinbase tx.
|
// tfCoinBase indicates that a txout was contained in a coinbase tx.
|
||||||
tfCoinBase txoFlags = 1 << iota
|
tfCoinBase txoFlags = 1 << iota
|
||||||
|
|
||||||
// tfSpent indicates that a txout is spent.
|
|
||||||
tfSpent
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// utxoCollection represents a set of UTXOs indexed by their outPoints
|
// utxoCollection represents a set of UTXOs indexed by their outPoints
|
||||||
|
@ -349,7 +349,7 @@ func CountP2SHSigOps(tx *util.Tx, isCoinBaseTx bool, utxoSet UTXOSet) (int, erro
|
|||||||
for txInIndex, txIn := range msgTx.TxIn {
|
for txInIndex, txIn := range msgTx.TxIn {
|
||||||
// Ensure the referenced input transaction is available.
|
// Ensure the referenced input transaction is available.
|
||||||
entry, ok := utxoSet.Get(txIn.PreviousOutPoint)
|
entry, ok := utxoSet.Get(txIn.PreviousOutPoint)
|
||||||
if !ok || entry.IsSpent() {
|
if !ok {
|
||||||
str := fmt.Sprintf("output %v referenced from "+
|
str := fmt.Sprintf("output %v referenced from "+
|
||||||
"transaction %s:%d either does not exist or "+
|
"transaction %s:%d either does not exist or "+
|
||||||
"has already been spent", txIn.PreviousOutPoint,
|
"has already been spent", txIn.PreviousOutPoint,
|
||||||
@ -792,7 +792,7 @@ func (dag *BlockDAG) ensureNoDuplicateTx(node *blockNode, block *util.Block) err
|
|||||||
// is fully spent.
|
// is fully spent.
|
||||||
for outpoint := range fetchSet {
|
for outpoint := range fetchSet {
|
||||||
utxo, ok := dag.virtual.GetUTXOEntry(outpoint)
|
utxo, ok := dag.virtual.GetUTXOEntry(outpoint)
|
||||||
if ok && !utxo.IsSpent() {
|
if ok {
|
||||||
str := fmt.Sprintf("tried to overwrite transaction %v "+
|
str := fmt.Sprintf("tried to overwrite transaction %v "+
|
||||||
"at block height %d that is not fully spent",
|
"at block height %d that is not fully spent",
|
||||||
outpoint.Hash, utxo.BlockHeight())
|
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 {
|
for txInIndex, txIn := range tx.MsgTx().TxIn {
|
||||||
// Ensure the referenced input transaction is available.
|
// Ensure the referenced input transaction is available.
|
||||||
entry, ok := utxoSet.Get(txIn.PreviousOutPoint)
|
entry, ok := utxoSet.Get(txIn.PreviousOutPoint)
|
||||||
if !ok || entry.IsSpent() {
|
if !ok {
|
||||||
str := fmt.Sprintf("output %v referenced from "+
|
str := fmt.Sprintf("output %v referenced from "+
|
||||||
"transaction %s:%d either does not exist or "+
|
"transaction %s:%d either does not exist or "+
|
||||||
"has already been spent", txIn.PreviousOutPoint,
|
"has already been spent", txIn.PreviousOutPoint,
|
||||||
|
@ -709,8 +709,8 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, isNew, rateLimit, rejectDu
|
|||||||
prevOut := wire.OutPoint{Hash: *txHash}
|
prevOut := wire.OutPoint{Hash: *txHash}
|
||||||
for txOutIdx := range tx.MsgTx().TxOut {
|
for txOutIdx := range tx.MsgTx().TxOut {
|
||||||
prevOut.Index = uint32(txOutIdx)
|
prevOut.Index = uint32(txOutIdx)
|
||||||
entry, ok := mp.mpUTXOSet.Get(prevOut)
|
_, ok := mp.mpUTXOSet.Get(prevOut)
|
||||||
if ok && !entry.IsSpent() {
|
if ok {
|
||||||
return nil, nil, txRuleError(wire.RejectDuplicate,
|
return nil, nil, txRuleError(wire.RejectDuplicate,
|
||||||
"transaction already exists")
|
"transaction already exists")
|
||||||
}
|
}
|
||||||
|
@ -486,8 +486,8 @@ mempoolLoop:
|
|||||||
prioItem := &txPrioItem{tx: tx}
|
prioItem := &txPrioItem{tx: tx}
|
||||||
for _, txIn := range tx.MsgTx().TxIn {
|
for _, txIn := range tx.MsgTx().TxIn {
|
||||||
originHash := &txIn.PreviousOutPoint.Hash
|
originHash := &txIn.PreviousOutPoint.Hash
|
||||||
entry, ok := blockUtxos.Get(txIn.PreviousOutPoint)
|
_, ok := blockUtxos.Get(txIn.PreviousOutPoint)
|
||||||
if !ok || entry.IsSpent() {
|
if !ok {
|
||||||
if !g.txSource.HaveTransaction(originHash) {
|
if !g.txSource.HaveTransaction(originHash) {
|
||||||
log.Tracef("Skipping tx %s because it "+
|
log.Tracef("Skipping tx %s because it "+
|
||||||
"references unspent output %s "+
|
"references unspent output %s "+
|
||||||
@ -716,12 +716,12 @@ mempoolLoop:
|
|||||||
merkles := blockdag.BuildMerkleTreeStore(blockTxns)
|
merkles := blockdag.BuildMerkleTreeStore(blockTxns)
|
||||||
var msgBlock wire.MsgBlock
|
var msgBlock wire.MsgBlock
|
||||||
msgBlock.Header = wire.BlockHeader{
|
msgBlock.Header = wire.BlockHeader{
|
||||||
Version: nextBlockVersion,
|
Version: nextBlockVersion,
|
||||||
NumPrevBlocks: byte(len(virtualBlock.TipHashes())),
|
NumPrevBlocks: byte(len(virtualBlock.TipHashes())),
|
||||||
PrevBlocks: virtualBlock.TipHashes(),
|
PrevBlocks: virtualBlock.TipHashes(),
|
||||||
MerkleRoot: *merkles[len(merkles)-1],
|
MerkleRoot: *merkles[len(merkles)-1],
|
||||||
Timestamp: ts,
|
Timestamp: ts,
|
||||||
Bits: reqDifficulty,
|
Bits: reqDifficulty,
|
||||||
}
|
}
|
||||||
for _, tx := range blockTxns {
|
for _, tx := range blockTxns {
|
||||||
if err := msgBlock.AddTransaction(tx.MsgTx()); err != nil {
|
if err := msgBlock.AddTransaction(tx.MsgTx()); err != nil {
|
||||||
|
@ -60,7 +60,7 @@ func calcInputValueAge(tx *wire.MsgTx, utxoSet blockdag.UTXOSet, nextBlockHeight
|
|||||||
// Don't attempt to accumulate the total input age if the
|
// Don't attempt to accumulate the total input age if the
|
||||||
// referenced transaction output doesn't exist.
|
// referenced transaction output doesn't exist.
|
||||||
entry, ok := utxoSet.Get(txIn.PreviousOutPoint)
|
entry, ok := utxoSet.Get(txIn.PreviousOutPoint)
|
||||||
if ok && !entry.IsSpent() {
|
if ok {
|
||||||
// Inputs with dependencies currently in the mempool
|
// Inputs with dependencies currently in the mempool
|
||||||
// have their block height set to a special constant.
|
// have their block height set to a special constant.
|
||||||
// Their input age should computed as zero since their
|
// Their input age should computed as zero since their
|
||||||
|
@ -879,7 +879,7 @@ func (sm *SyncManager) haveInventory(invVect *wire.InvVect) (bool, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
if entry != nil && !entry.IsSpent() {
|
if entry != nil {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2569,7 +2569,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte
|
|||||||
// transaction already in the main chain. Mined transactions
|
// transaction already in the main chain. Mined transactions
|
||||||
// that are spent by a mempool transaction are not affected by
|
// that are spent by a mempool transaction are not affected by
|
||||||
// this.
|
// this.
|
||||||
if entry == nil || entry.IsSpent() {
|
if entry == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user