mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +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
|
||||
// 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) +
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user