From 33036278acf57ec21da635112ff13d9b19045553 Mon Sep 17 00:00:00 2001 From: Svarog Date: Thu, 2 May 2019 16:50:55 +0300 Subject: [PATCH] [NOD-144] Use chainHeight in SelectedAncestor, and update all logic that uses it (#281) * [NOD-144] Use chainHeight in SelectedAncestor, and update all logic that uses it * [NOD-144] Moved UnminedHeight to blockdag, and updated all references --- blockdag/blocknode.go | 23 +++++------- blockdag/dag.go | 40 +++++++++++--------- blockdag/dag_test.go | 73 +++++++++++++++++++++---------------- blockdag/dagio.go | 12 +++--- blockdag/dagio_test.go | 20 +++++----- blockdag/thresholdstate.go | 6 +-- blockdag/utxoset.go | 34 ++++++++++++----- blockdag/validate.go | 24 ++++++------ blockdag/validate_test.go | 24 ++++++------ mempool/estimatefee.go | 18 ++++----- mempool/estimatefee_test.go | 3 +- mempool/mempool.go | 6 +-- mempool/mempool_test.go | 8 ++-- mining/policy.go | 13 ++----- server/rpc/rpcserver.go | 2 +- 15 files changed, 163 insertions(+), 143 deletions(-) diff --git a/blockdag/blocknode.go b/blockdag/blocknode.go index 0c4e07ee5..9478470e6 100644 --- a/blockdag/blocknode.go +++ b/blockdag/blocknode.go @@ -194,34 +194,31 @@ func (node *blockNode) Header() *wire.BlockHeader { } } -// SelectedAncestor returns the ancestor block node at the provided height by following -// the selected chain backwards from this node. The returned block will be nil when a -// height is requested that is after the height of the passed node or is less than zero. -// -// When there's no chain-block of the requested height, the block with the highest height -// that is lower than requested height would be returned. +// SelectedAncestor returns the ancestor block node at the provided chain-height by following +// the selected-parents chain backwards from this node. The returned block will be nil when a +// height is requested that is after the height of the passed node. // // This function is safe for concurrent access. -func (node *blockNode) SelectedAncestor(height uint64) *blockNode { - if height < 0 || height > node.height { +func (node *blockNode) SelectedAncestor(chainHeight uint64) *blockNode { + if chainHeight < 0 || chainHeight > node.chainHeight { return nil } n := node - for ; n != nil && n.height > height; n = n.selectedParent { + for ; n != nil && n.chainHeight != chainHeight; n = n.selectedParent { // Intentionally left blank } return n } -// RelativeAncestor returns the ancestor block node a relative 'distance' blocks -// before this node. This is equivalent to calling Ancestor with the node's -// height minus provided distance. +// RelativeAncestor returns the ancestor block node a relative 'distance' of +// chain-blocks before this node. This is equivalent to calling Ancestor with +// the node's chain-height minus provided distance. // // This function is safe for concurrent access. func (node *blockNode) RelativeAncestor(distance uint64) *blockNode { - return node.SelectedAncestor(node.height - distance) + return node.SelectedAncestor(node.chainHeight - distance) } // PastMedianTime returns the median time of the previous few blocks diff --git a/blockdag/dag.go b/blockdag/dag.go index f545e59f4..f07f207a9 100644 --- a/blockdag/dag.go +++ b/blockdag/dag.go @@ -328,14 +328,14 @@ func (dag *BlockDAG) addOrphanBlock(block *util.Block) { } // SequenceLock represents the converted relative lock-time in seconds, and -// absolute block-height for a transaction input's relative lock-times. +// absolute block-chain-height for a transaction input's relative lock-times. // According to SequenceLock, after the referenced input has been confirmed // within a block, a transaction spending that input can be included into a // block either after 'seconds' (according to past median time), or once the -// 'BlockHeight' has been reached. +// 'BlockChainHeight' has been reached. type SequenceLock struct { - Seconds int64 - BlockHeight int64 + Seconds int64 + BlockChainHeight int64 } // CalcSequenceLock computes a relative lock-time SequenceLock for the passed @@ -368,7 +368,7 @@ func (dag *BlockDAG) calcSequenceLock(node *blockNode, utxoSet UTXOSet, tx *util // A value of -1 for each relative lock type represents a relative time // lock value that will allow a transaction to be included in a block // at any given height or time. - sequenceLock := &SequenceLock{Seconds: -1, BlockHeight: -1} + sequenceLock := &SequenceLock{Seconds: -1, BlockChainHeight: -1} // Sequence locks don't apply to block reward transactions Therefore, we // return sequence lock values of -1 indicating that this transaction @@ -379,7 +379,7 @@ func (dag *BlockDAG) calcSequenceLock(node *blockNode, utxoSet UTXOSet, tx *util // Grab the next height from the PoV of the passed blockNode to use for // inputs present in the mempool. - nextHeight := node.height + 1 + nextChainHeight := node.chainHeight + 1 mTx := tx.MsgTx() for txInIndex, txIn := range mTx.TxIn { @@ -392,12 +392,12 @@ func (dag *BlockDAG) calcSequenceLock(node *blockNode, utxoSet UTXOSet, tx *util return sequenceLock, ruleError(ErrMissingTxOut, str) } - // If the input height is set to the mempool height, then we + // If the input chain-height is set to the mempool height, then we // assume the transaction makes it into the next block when // evaluating its sequence blocks. - inputHeight := entry.BlockHeight() - if inputHeight == 0x7fffffff { - inputHeight = nextHeight + inputChainHeight := entry.BlockChainHeight() + if entry.IsUnmined() { + inputChainHeight = nextChainHeight } // Given a sequence number, we apply the relative time lock @@ -418,11 +418,11 @@ func (dag *BlockDAG) calcSequenceLock(node *blockNode, utxoSet UTXOSet, tx *util // which this input was included within so we can // compute the past median time for the block prior to // the one which included this referenced output. - prevInputHeight := inputHeight - 1 - if prevInputHeight < 0 { - prevInputHeight = 0 + prevInputChainHeight := inputChainHeight - 1 + if prevInputChainHeight < 0 { + prevInputChainHeight = 0 } - blockNode := node.SelectedAncestor(prevInputHeight) + blockNode := node.SelectedAncestor(prevInputChainHeight) medianTime := blockNode.PastMedianTime() // Time based relative time-locks as defined by BIP 68 @@ -442,9 +442,9 @@ func (dag *BlockDAG) calcSequenceLock(node *blockNode, utxoSet UTXOSet, tx *util // the input's height as its converted absolute // lock-time. We subtract one from the relative lock in // order to maintain the original lockTime semantics. - blockHeight := int64(inputHeight) + relativeLock - 1 - if blockHeight > sequenceLock.BlockHeight { - sequenceLock.BlockHeight = blockHeight + blockChainHeight := int64(inputChainHeight) + relativeLock - 1 + if blockChainHeight > sequenceLock.BlockChainHeight { + sequenceLock.BlockChainHeight = blockChainHeight } } } @@ -1130,6 +1130,12 @@ func (dag *BlockDAG) Height() uint64 { return dag.virtual.tips().maxHeight() } +// ChainHeight return the chain-height of the selected tip. In other words - it returns +// the length of the dag's selected-parent chain +func (dag *BlockDAG) ChainHeight() uint64 { + return dag.selectedTip().chainHeight +} + // BlockCount returns the number of blocks in the DAG func (dag *BlockDAG) BlockCount() uint64 { return dag.blockCount diff --git a/blockdag/dag_test.go b/blockdag/dag_test.go index f17953852..722f73c3b 100644 --- a/blockdag/dag_test.go +++ b/blockdag/dag_test.go @@ -256,7 +256,7 @@ func TestCalcSequenceLock(t *testing.T) { TxID: *targetTx.ID(), Index: 0, } - prevUtxoHeight := uint64(numBlocksToGenerate) - 4 + prevUtxoChainHeight := uint64(numBlocksToGenerate) - 4 // Obtain the median time past from the PoV of the input created above. // The MTP for the input is the MTP from the PoV of the block *prior* @@ -268,7 +268,7 @@ func TestCalcSequenceLock(t *testing.T) { // the MTP will be calculated from the PoV of the yet-to-be-mined // block. nextMedianTime := node.PastMedianTime().Unix() - nextBlockHeight := int32(numBlocksToGenerate) + 1 + nextBlockChainHeight := int32(numBlocksToGenerate) + 1 // Add an additional transaction which will serve as our unconfirmed // output. @@ -278,11 +278,10 @@ func TestCalcSequenceLock(t *testing.T) { Index: 0, } - // Adding a utxo with a height of 0x7fffffff indicates that the output - // is currently unmined. - utxoSet.AddTx(unConfTx, 0x7fffffff) + utxoSet.AddTx(unConfTx, UnminedChainHeight) tests := []struct { + name string tx *wire.MsgTx utxoSet UTXOSet mempool bool @@ -292,11 +291,12 @@ func TestCalcSequenceLock(t *testing.T) { // This sequence number has the high bit set, so sequence locks // should be disabled. { + name: "single input, max sequence number", tx: wire.NewNativeMsgTx(1, []*wire.TxIn{{PreviousOutPoint: utxo, Sequence: wire.MaxTxInSequenceNum}}, nil), utxoSet: utxoSet, want: &SequenceLock{ - Seconds: -1, - BlockHeight: -1, + Seconds: -1, + BlockChainHeight: -1, }, }, // A transaction with a single input whose lock time is @@ -306,11 +306,12 @@ func TestCalcSequenceLock(t *testing.T) { // seconds lock-time should be just before the median time of // the targeted block. { + name: "single input, seconds lock time below time granularity", tx: wire.NewNativeMsgTx(1, []*wire.TxIn{{PreviousOutPoint: utxo, Sequence: LockTimeToSequence(true, 2)}}, nil), utxoSet: utxoSet, want: &SequenceLock{ - Seconds: medianTime - 1, - BlockHeight: -1, + Seconds: medianTime - 1, + BlockChainHeight: -1, }, }, // A transaction with a single input whose lock time is @@ -318,11 +319,12 @@ func TestCalcSequenceLock(t *testing.T) { // seconds after the median past time of the last block in the // chain. { + name: "single input, 1023 seconds after median time", tx: wire.NewNativeMsgTx(1, []*wire.TxIn{{PreviousOutPoint: utxo, Sequence: LockTimeToSequence(true, 1024)}}, nil), utxoSet: utxoSet, want: &SequenceLock{ - Seconds: medianTime + 1023, - BlockHeight: -1, + Seconds: medianTime + 1023, + BlockChainHeight: -1, }, }, // A transaction with multiple inputs. The first input has a @@ -332,6 +334,7 @@ func TestCalcSequenceLock(t *testing.T) { // bit set. So the first lock should be selected as it's the // latest lock that isn't disabled. { + name: "multiple varied inputs", tx: wire.NewNativeMsgTx(1, []*wire.TxIn{{ PreviousOutPoint: utxo, @@ -347,8 +350,8 @@ func TestCalcSequenceLock(t *testing.T) { nil), utxoSet: utxoSet, want: &SequenceLock{ - Seconds: medianTime + (5 << wire.SequenceLockTimeGranularity) - 1, - BlockHeight: int64(prevUtxoHeight) + 3, + Seconds: medianTime + (5 << wire.SequenceLockTimeGranularity) - 1, + BlockChainHeight: int64(prevUtxoChainHeight) + 3, }, }, // Transaction with a single input. The input's sequence number @@ -356,17 +359,19 @@ func TestCalcSequenceLock(t *testing.T) { // sequence lock should have a value of -1 for seconds, but a // height of 2 meaning it can be included at height 3. { + name: "single input, lock-time in blocks", tx: wire.NewNativeMsgTx(1, []*wire.TxIn{{PreviousOutPoint: utxo, Sequence: LockTimeToSequence(false, 3)}}, nil), utxoSet: utxoSet, want: &SequenceLock{ - Seconds: -1, - BlockHeight: int64(prevUtxoHeight) + 2, + Seconds: -1, + BlockChainHeight: int64(prevUtxoChainHeight) + 2, }, }, // A transaction with two inputs with lock times expressed in // seconds. The selected sequence lock value for seconds should // be the time further in the future. { + name: "two inputs, lock-times in seconds", tx: wire.NewNativeMsgTx(1, []*wire.TxIn{{ PreviousOutPoint: utxo, Sequence: LockTimeToSequence(true, 5120), @@ -376,8 +381,8 @@ func TestCalcSequenceLock(t *testing.T) { }}, nil), utxoSet: utxoSet, want: &SequenceLock{ - Seconds: medianTime + (10 << wire.SequenceLockTimeGranularity) - 1, - BlockHeight: -1, + Seconds: medianTime + (10 << wire.SequenceLockTimeGranularity) - 1, + BlockChainHeight: -1, }, }, // A transaction with two inputs with lock times expressed in @@ -385,6 +390,7 @@ func TestCalcSequenceLock(t *testing.T) { // be the height further in the future, so a height of 10 // indicating it can be included at height 11. { + name: "two inputs, lock-times in blocks", tx: wire.NewNativeMsgTx(1, []*wire.TxIn{{ PreviousOutPoint: utxo, @@ -396,14 +402,15 @@ func TestCalcSequenceLock(t *testing.T) { nil), utxoSet: utxoSet, want: &SequenceLock{ - Seconds: -1, - BlockHeight: int64(prevUtxoHeight) + 10, + Seconds: -1, + BlockChainHeight: int64(prevUtxoChainHeight) + 10, }, }, // A transaction with multiple inputs. Two inputs are time // based, and the other two are block based. The lock lying // further into the future for both inputs should be chosen. { + name: "four inputs, two lock-times in time, two lock-times in blocks", tx: wire.NewNativeMsgTx(1, []*wire.TxIn{{ PreviousOutPoint: utxo, @@ -421,8 +428,8 @@ func TestCalcSequenceLock(t *testing.T) { nil), utxoSet: utxoSet, want: &SequenceLock{ - Seconds: medianTime + (13 << wire.SequenceLockTimeGranularity) - 1, - BlockHeight: int64(prevUtxoHeight) + 8, + Seconds: medianTime + (13 << wire.SequenceLockTimeGranularity) - 1, + BlockChainHeight: int64(prevUtxoChainHeight) + 8, }, }, // A transaction with a single unconfirmed input. As the input @@ -432,43 +439,45 @@ func TestCalcSequenceLock(t *testing.T) { // *next* block height, indicating it can be included 2 blocks // after that. { + name: "single input, unconfirmed, lock-time in blocks", tx: wire.NewNativeMsgTx(1, []*wire.TxIn{{PreviousOutPoint: unConfUtxo, Sequence: LockTimeToSequence(false, 2)}}, nil), utxoSet: utxoSet, mempool: true, want: &SequenceLock{ - Seconds: -1, - BlockHeight: int64(nextBlockHeight) + 1, + Seconds: -1, + BlockChainHeight: int64(nextBlockChainHeight) + 1, }, }, // A transaction with a single unconfirmed input. The input has // a time based lock, so the lock time should be based off the // MTP of the *next* block. { + name: "single input, unconfirmed, lock-time in seoncds", tx: wire.NewNativeMsgTx(1, []*wire.TxIn{{PreviousOutPoint: unConfUtxo, Sequence: LockTimeToSequence(true, 1024)}}, nil), utxoSet: utxoSet, mempool: true, want: &SequenceLock{ - Seconds: nextMedianTime + 1023, - BlockHeight: -1, + Seconds: nextMedianTime + 1023, + BlockChainHeight: -1, }, }, } t.Logf("Running %v SequenceLock tests", len(tests)) - for i, test := range tests { + for _, test := range tests { utilTx := util.NewTx(test.tx) seqLock, err := dag.CalcSequenceLock(utilTx, utxoSet, test.mempool) if err != nil { - t.Fatalf("test #%d, unable to calc sequence lock: %v", i, err) + t.Fatalf("test '%s', unable to calc sequence lock: %v", test.name, err) } if seqLock.Seconds != test.want.Seconds { - t.Fatalf("test #%d got %v seconds want %v seconds", - i, seqLock.Seconds, test.want.Seconds) + t.Fatalf("test '%s' got %v seconds want %v seconds", + test.name, seqLock.Seconds, test.want.Seconds) } - if seqLock.BlockHeight != test.want.BlockHeight { - t.Fatalf("test #%d got height of %v want height of %v ", - i, seqLock.BlockHeight, test.want.BlockHeight) + if seqLock.BlockChainHeight != test.want.BlockChainHeight { + t.Fatalf("test '%s' got chain-height of %v want chain-height of %v ", + test.name, seqLock.BlockChainHeight, test.want.BlockChainHeight) } } } diff --git a/blockdag/dagio.go b/blockdag/dagio.go index db36e465d..5c2b0b022 100644 --- a/blockdag/dagio.go +++ b/blockdag/dagio.go @@ -228,7 +228,7 @@ func utxoEntryHeaderCode(entry *UTXOEntry) uint64 { // As described in the serialization format comments, the header code // encodes the height shifted over one bit and the block reward flag in the // lowest bit. - headerCode := uint64(entry.BlockHeight()) << 1 + headerCode := uint64(entry.BlockChainHeight()) << 1 if entry.IsBlockReward() { headerCode |= 0x01 } @@ -286,7 +286,7 @@ func deserializeUTXOEntry(serialized []byte) (*UTXOEntry, error) { // Bit 0 indicates whether the containing transaction is a block reward. // Bits 1-x encode height of containing transaction. isBlockReward := code&0x01 != 0 - blockHeight := code >> 1 + blockChainHeight := code >> 1 // Decode the compressed unspent transaction output. amount, pkScript, _, err := decodeCompressedTxOut(serialized[offset:]) @@ -296,10 +296,10 @@ func deserializeUTXOEntry(serialized []byte) (*UTXOEntry, error) { } entry := &UTXOEntry{ - amount: amount, - pkScript: pkScript, - blockHeight: blockHeight, - packedFlags: 0, + amount: amount, + pkScript: pkScript, + blockChainHeight: blockChainHeight, + packedFlags: 0, } if isBlockReward { entry.packedFlags |= tfBlockReward diff --git a/blockdag/dagio_test.go b/blockdag/dagio_test.go index ebe305449..ca885cdd8 100644 --- a/blockdag/dagio_test.go +++ b/blockdag/dagio_test.go @@ -51,10 +51,10 @@ func TestUtxoSerialization(t *testing.T) { { name: "height 1, coinbase", entry: &UTXOEntry{ - amount: 5000000000, - pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"), - blockHeight: 1, - packedFlags: tfBlockReward, + amount: 5000000000, + pkScript: hexToBytes("410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"), + blockChainHeight: 1, + packedFlags: tfBlockReward, }, serialized: hexToBytes("03320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"), }, @@ -63,10 +63,10 @@ func TestUtxoSerialization(t *testing.T) { { name: "height 100001, not coinbase", entry: &UTXOEntry{ - amount: 1000000, - pkScript: hexToBytes("76a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac"), - blockHeight: 100001, - packedFlags: 0, + amount: 1000000, + pkScript: hexToBytes("76a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac"), + blockChainHeight: 100001, + packedFlags: 0, }, serialized: hexToBytes("8b99420700ee8bd501094a7d5ca318da2506de35e1cb025ddc"), }, @@ -110,10 +110,10 @@ func TestUtxoSerialization(t *testing.T) { utxoEntry.PkScript(), test.entry.PkScript()) continue } - if utxoEntry.BlockHeight() != test.entry.BlockHeight() { + if utxoEntry.BlockChainHeight() != test.entry.BlockChainHeight() { t.Errorf("deserializeUTXOEntry #%d (%s) mismatched "+ "block height: got %d, want %d", i, test.name, - utxoEntry.BlockHeight(), test.entry.BlockHeight()) + utxoEntry.BlockChainHeight(), test.entry.BlockChainHeight()) continue } if utxoEntry.IsBlockReward() != test.entry.IsBlockReward() { diff --git a/blockdag/thresholdstate.go b/blockdag/thresholdstate.go index 7c4358f9a..4b84bb7ff 100644 --- a/blockdag/thresholdstate.go +++ b/blockdag/thresholdstate.go @@ -130,15 +130,15 @@ func (dag *BlockDAG) thresholdState(prevNode *blockNode, checker thresholdCondit // The threshold state for the window that contains the genesis block is // defined by definition. confirmationWindow := checker.MinerConfirmationWindow() - if prevNode == nil || (prevNode.height+1) < confirmationWindow { + if prevNode == nil || (prevNode.chainHeight+1) < confirmationWindow { return ThresholdDefined, nil } // Get the ancestor that is the last block of the previous confirmation // window in order to get its threshold state. This can be done because // the state is the same for all blocks within a given window. - prevNode = prevNode.SelectedAncestor(prevNode.height - - (prevNode.height+1)%confirmationWindow) + prevNode = prevNode.SelectedAncestor(prevNode.chainHeight - + (prevNode.chainHeight+1)%confirmationWindow) // Iterate backwards through each of the previous confirmation windows // to find the most recently cached threshold state. diff --git a/blockdag/utxoset.go b/blockdag/utxoset.go index f1ca7bb97..05f6d9538 100644 --- a/blockdag/utxoset.go +++ b/blockdag/utxoset.go @@ -3,12 +3,20 @@ package blockdag import ( "errors" "fmt" + "math" "sort" "strings" "github.com/daglabs/btcd/wire" ) +const ( + // UnminedChainHeight is the chain-height used for the "block" height field of the + // contextual transaction information provided in a transaction store + // when it has not yet been mined into a block. + UnminedChainHeight = math.MaxUint64 +) + // UTXOEntry houses details about an individual transaction output in a utxo // set such as whether or not it was contained in a block reward tx, the height of // the block that contains the tx, whether or not it is spent, its public key @@ -20,9 +28,9 @@ type UTXOEntry struct { // specifically crafted to result in minimal padding. There will be a // lot of these in memory, so a few extra bytes of padding adds up. - amount uint64 - pkScript []byte // The public key script for the output. - blockHeight uint64 // Height of block containing tx. + amount uint64 + pkScript []byte // The public key script for the output. + blockChainHeight uint64 // Chain-height of block containing tx. // packedFlags contains additional info about output such as whether it // is a block reward, and whether it has been modified @@ -37,9 +45,9 @@ func (entry *UTXOEntry) IsBlockReward() bool { return entry.packedFlags&tfBlockReward == tfBlockReward } -// BlockHeight returns the height of the block containing the output. -func (entry *UTXOEntry) BlockHeight() uint64 { - return entry.blockHeight +// BlockChainHeight returns the chain-height of the block containing the output. +func (entry *UTXOEntry) BlockChainHeight() uint64 { + return entry.blockChainHeight } // Amount returns the amount of the output. @@ -52,6 +60,12 @@ func (entry *UTXOEntry) PkScript() []byte { return entry.pkScript } +// IsUnmined returns true iff this UTXOEntry has still not been mined, +// a.k.a. still in the mempool. +func (entry *UTXOEntry) IsUnmined() bool { + return entry.blockChainHeight == UnminedChainHeight +} + // txoFlags is a bitmask defining additional information and state for a // transaction output in a UTXO set. type txoFlags uint8 @@ -304,11 +318,11 @@ func (d UTXODiff) String() string { } // NewUTXOEntry creates a new utxoEntry representing the given txOut -func NewUTXOEntry(txOut *wire.TxOut, isBlockReward bool, blockHeight uint64) *UTXOEntry { +func NewUTXOEntry(txOut *wire.TxOut, isBlockReward bool, blockChainHeight uint64) *UTXOEntry { entry := &UTXOEntry{ - amount: txOut.Value, - pkScript: txOut.PkScript, - blockHeight: blockHeight, + amount: txOut.Value, + pkScript: txOut.PkScript, + blockChainHeight: blockChainHeight, } if isBlockReward { diff --git a/blockdag/validate.go b/blockdag/validate.go index 7dae024eb..add7c4ecc 100644 --- a/blockdag/validate.go +++ b/blockdag/validate.go @@ -82,15 +82,15 @@ func IsFeeTransaction(tx *util.Tx) bool { // SequenceLockActive determines if a transaction's sequence locks have been // met, meaning that all the inputs of a given transaction have reached a -// height or time sufficient for their relative lock-time maturity. -func SequenceLockActive(sequenceLock *SequenceLock, blockHeight uint64, +// chain-height or time sufficient for their relative lock-time maturity. +func SequenceLockActive(sequenceLock *SequenceLock, blockChainHeight uint64, medianTimePast time.Time) bool { - // If either the seconds, or height relative-lock time has not yet + // If either the seconds, or chain-height relative-lock time has not yet // reached, then the transaction is not yet mature according to its // sequence locks. if sequenceLock.Seconds >= medianTimePast.Unix() || - sequenceLock.BlockHeight >= int64(blockHeight) { + sequenceLock.BlockChainHeight >= int64(blockChainHeight) { return false } @@ -876,8 +876,8 @@ func ensureNoDuplicateTx(block *blockNode, utxoSet UTXOSet, utxo, ok := utxoSet.Get(outpoint) if ok { str := fmt.Sprintf("tried to overwrite transaction %s "+ - "at block height %d that is not fully spent", - outpoint.TxID, utxo.BlockHeight()) + "at block chain-height %d that is not fully spent", + outpoint.TxID, utxo.BlockChainHeight()) return ruleError(ErrOverwriteTx, str) } } @@ -976,18 +976,18 @@ func CheckTransactionInputsAndCalulateFee(tx *util.Tx, txHeight uint64, utxoSet return txFeeInSatoshi, nil } -func validateBlockRewardMaturity(dagParams *dagconfig.Params, entry *UTXOEntry, txHeight uint64, txIn *wire.TxIn) error { +func validateBlockRewardMaturity(dagParams *dagconfig.Params, entry *UTXOEntry, txChainHeight uint64, txIn *wire.TxIn) error { // Ensure the transaction is not spending coins which have not // yet reached the required block reward maturity. if entry.IsBlockReward() { - originHeight := entry.BlockHeight() - blocksSincePrev := txHeight - originHeight + originChainHeight := entry.BlockChainHeight() + blocksSincePrev := txChainHeight - originChainHeight if blocksSincePrev < dagParams.BlockRewardMaturity { str := fmt.Sprintf("tried to spend block reward "+ - "transaction output %s from height %d "+ - "at height %d before required maturity "+ + "transaction output %s from chain-height %d "+ + "at chain-height %d before required maturity "+ "of %d blocks", txIn.PreviousOutPoint, - originHeight, txHeight, + originChainHeight, txChainHeight, dagParams.BlockRewardMaturity) return ruleError(ErrImmatureSpend, str) } diff --git a/blockdag/validate_test.go b/blockdag/validate_test.go index c8c73ec0d..5887afe39 100644 --- a/blockdag/validate_test.go +++ b/blockdag/validate_test.go @@ -22,41 +22,41 @@ import ( func TestSequenceLocksActive(t *testing.T) { seqLock := func(h int64, s int64) *SequenceLock { return &SequenceLock{ - Seconds: s, - BlockHeight: h, + Seconds: s, + BlockChainHeight: h, } } tests := []struct { - seqLock *SequenceLock - blockHeight uint64 - mtp time.Time + seqLock *SequenceLock + blockChainHeight uint64 + mtp time.Time want bool }{ // Block based sequence lock with equal block height. - {seqLock: seqLock(1000, -1), blockHeight: 1001, mtp: time.Unix(9, 0), want: true}, + {seqLock: seqLock(1000, -1), blockChainHeight: 1001, mtp: time.Unix(9, 0), want: true}, // Time based sequence lock with mtp past the absolute time. - {seqLock: seqLock(-1, 30), blockHeight: 2, mtp: time.Unix(31, 0), want: true}, + {seqLock: seqLock(-1, 30), blockChainHeight: 2, mtp: time.Unix(31, 0), want: true}, // Block based sequence lock with current height below seq lock block height. - {seqLock: seqLock(1000, -1), blockHeight: 90, mtp: time.Unix(9, 0), want: false}, + {seqLock: seqLock(1000, -1), blockChainHeight: 90, mtp: time.Unix(9, 0), want: false}, // Time based sequence lock with current time before lock time. - {seqLock: seqLock(-1, 30), blockHeight: 2, mtp: time.Unix(29, 0), want: false}, + {seqLock: seqLock(-1, 30), blockChainHeight: 2, mtp: time.Unix(29, 0), want: false}, // Block based sequence lock at the same height, so shouldn't yet be active. - {seqLock: seqLock(1000, -1), blockHeight: 1000, mtp: time.Unix(9, 0), want: false}, + {seqLock: seqLock(1000, -1), blockChainHeight: 1000, mtp: time.Unix(9, 0), want: false}, // Time based sequence lock with current time equal to lock time, so shouldn't yet be active. - {seqLock: seqLock(-1, 30), blockHeight: 2, mtp: time.Unix(30, 0), want: false}, + {seqLock: seqLock(-1, 30), blockChainHeight: 2, mtp: time.Unix(30, 0), want: false}, } t.Logf("Running %d sequence locks tests", len(tests)) for i, test := range tests { got := SequenceLockActive(test.seqLock, - test.blockHeight, test.mtp) + test.blockChainHeight, test.mtp) if got != test.want { t.Fatalf("SequenceLockActive #%d got %v want %v", i, got, test.want) diff --git a/mempool/estimatefee.go b/mempool/estimatefee.go index 7a3e3c27d..32fab192b 100644 --- a/mempool/estimatefee.go +++ b/mempool/estimatefee.go @@ -16,8 +16,8 @@ import ( "strings" "sync" + "github.com/daglabs/btcd/blockdag" "github.com/daglabs/btcd/dagconfig/daghash" - "github.com/daglabs/btcd/mining" "github.com/daglabs/btcd/util" ) @@ -189,7 +189,7 @@ func NewFeeEstimator(maxRollback, minRegisteredBlocks uint32) *FeeEstimator { return &FeeEstimator{ maxRollback: maxRollback, minRegisteredBlocks: minRegisteredBlocks, - lastKnownHeight: mining.UnminedHeight, + lastKnownHeight: blockdag.UnminedChainHeight, binSize: estimateFeeBinSize, maxReplacements: estimateFeeMaxReplacements, observed: make(map[daghash.TxID]*observedTransaction), @@ -204,7 +204,7 @@ func (ef *FeeEstimator) ObserveTransaction(t *TxDesc) { // If we haven't seen a block yet we don't know when this one arrived, // so we ignore it. - if ef.lastKnownHeight == mining.UnminedHeight { + if ef.lastKnownHeight == blockdag.UnminedChainHeight { return } @@ -216,7 +216,7 @@ func (ef *FeeEstimator) ObserveTransaction(t *TxDesc) { txID: txID, feeRate: NewSatoshiPerByte(util.Amount(t.Fee), size), observed: t.Height, - mined: mining.UnminedHeight, + mined: blockdag.UnminedChainHeight, } } } @@ -230,7 +230,7 @@ func (ef *FeeEstimator) RegisterBlock(block *util.Block) error { ef.cached = nil height := block.Height() - if height != ef.lastKnownHeight+1 && ef.lastKnownHeight != mining.UnminedHeight { + if height != ef.lastKnownHeight+1 && ef.lastKnownHeight != blockdag.UnminedChainHeight { return fmt.Errorf("intermediate block not recorded; current height is %d; new height is %d", ef.lastKnownHeight, height) } @@ -270,7 +270,7 @@ func (ef *FeeEstimator) RegisterBlock(block *util.Block) error { // This shouldn't happen if the fee estimator works correctly, // but return an error if it does. - if o.mined != mining.UnminedHeight { + if o.mined != blockdag.UnminedChainHeight { log.Error("Estimate fee: transaction ", txID.String(), " has already been mined") return errors.New("Transaction has already been mined") } @@ -309,7 +309,7 @@ func (ef *FeeEstimator) RegisterBlock(block *util.Block) error { // Go through the mempool for txs that have been in too long. for hash, o := range ef.observed { - if o.mined == mining.UnminedHeight && height-o.observed >= estimateFeeDepth { + if o.mined == blockdag.UnminedChainHeight && height-o.observed >= estimateFeeDepth { delete(ef.observed, hash) } } @@ -407,7 +407,7 @@ func (ef *FeeEstimator) rollback() { prev := bin[counter] if prev.mined == ef.lastKnownHeight { - prev.mined = mining.UnminedHeight + prev.mined = blockdag.UnminedChainHeight bin[counter] = o @@ -433,7 +433,7 @@ func (ef *FeeEstimator) rollback() { prev := ef.bin[i][j] if prev.mined == ef.lastKnownHeight { - prev.mined = mining.UnminedHeight + prev.mined = blockdag.UnminedChainHeight newBin := append(ef.bin[i][0:j], ef.bin[i][j+1:l]...) // TODO This line should prevent an unintentional memory diff --git a/mempool/estimatefee_test.go b/mempool/estimatefee_test.go index 4e556f19b..f5bccf13c 100644 --- a/mempool/estimatefee_test.go +++ b/mempool/estimatefee_test.go @@ -9,6 +9,7 @@ import ( "math/rand" "testing" + "github.com/daglabs/btcd/blockdag" "github.com/daglabs/btcd/dagconfig/daghash" "github.com/daglabs/btcd/mining" "github.com/daglabs/btcd/util" @@ -291,7 +292,7 @@ func (eft *estimateFeeTester) round(txHistory [][]*TxDesc, mempool := make(map[*observedTransaction]*TxDesc) for _, h := range txHistory { for _, t := range h { - if o, exists := eft.ef.observed[*t.Tx.ID()]; exists && o.mined == mining.UnminedHeight { + if o, exists := eft.ef.observed[*t.Tx.ID()]; exists && o.mined == blockdag.UnminedChainHeight { mempool[o] = t } } diff --git a/mempool/mempool.go b/mempool/mempool.go index d8d2ee075..78c01d834 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -515,12 +515,12 @@ func (mp *TxPool) removeTransaction(tx *util.Tx, removeRedeemers bool, restoreIn if restoreInputs { if prevTxDesc, exists := mp.pool[txIn.PreviousOutPoint.TxID]; exists { prevOut := prevTxDesc.Tx.MsgTx().TxOut[txIn.PreviousOutPoint.Index] - entry := blockdag.NewUTXOEntry(prevOut, false, mining.UnminedHeight) + entry := blockdag.NewUTXOEntry(prevOut, false, blockdag.UnminedChainHeight) diff.AddEntry(txIn.PreviousOutPoint, entry) } if prevTxDesc, exists := mp.depends[txIn.PreviousOutPoint.TxID]; exists { prevOut := prevTxDesc.Tx.MsgTx().TxOut[txIn.PreviousOutPoint.Index] - entry := blockdag.NewUTXOEntry(prevOut, false, mining.UnminedHeight) + entry := blockdag.NewUTXOEntry(prevOut, false, blockdag.UnminedChainHeight) diff.AddEntry(txIn.PreviousOutPoint, entry) } } @@ -639,7 +639,7 @@ func (mp *TxPool) addTransaction(tx *util.Tx, height uint64, fee uint64, parents for _, txIn := range tx.MsgTx().TxIn { mp.outpoints[txIn.PreviousOutPoint] = tx } - mp.mpUTXOSet.AddTx(tx.MsgTx(), mining.UnminedHeight) + mp.mpUTXOSet.AddTx(tx.MsgTx(), blockdag.UnminedChainHeight) atomic.StoreInt64(&mp.lastUpdated, time.Now().Unix()) // Add unconfirmed address index entries associated with the transaction diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index 3afb5c3d6..82f4d0674 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -76,8 +76,8 @@ func calcSequenceLock(tx *util.Tx, utxoSet blockdag.UTXOSet) (*blockdag.SequenceLock, error) { return &blockdag.SequenceLock{ - Seconds: -1, - BlockHeight: -1, + Seconds: -1, + BlockChainHeight: -1, }, nil } @@ -685,8 +685,8 @@ func TestProcessTransaction(t *testing.T) { view blockdag.UTXOSet) (*blockdag.SequenceLock, error) { return &blockdag.SequenceLock{ - Seconds: math.MaxInt64, - BlockHeight: math.MaxInt32, + Seconds: math.MaxInt64, + BlockChainHeight: math.MaxInt64, }, nil } tx, err = harness.createTx(spendableOuts[2], 0, 1) diff --git a/mining/policy.go b/mining/policy.go index 87d7535b0..eb828a473 100644 --- a/mining/policy.go +++ b/mining/policy.go @@ -10,13 +10,6 @@ import ( "github.com/daglabs/btcd/wire" ) -const ( - // UnminedHeight is the height used for the "block" height field of the - // contextual transaction information provided in a transaction store - // when it has not yet been mined into a block. - UnminedHeight = 0x7fffffff -) - // Policy houses the policy (configuration parameters) which is used to control // the generation of block templates. See the documentation for // NewBlockTemplate for more details on each of these parameters are used. @@ -66,11 +59,11 @@ func calcInputValueAge(tx *wire.MsgTx, utxoSet blockdag.UTXOSet, nextBlockHeight // Their input age should computed as zero since their // parent hasn't made it into a block yet. var inputAge uint64 - originHeight := entry.BlockHeight() - if originHeight == UnminedHeight { + originChainHeight := entry.BlockChainHeight() + if entry.IsUnmined() { inputAge = 0 } else { - inputAge = nextBlockHeight - originHeight + inputAge = nextBlockHeight - originChainHeight } // Sum the input value times age. diff --git a/server/rpc/rpcserver.go b/server/rpc/rpcserver.go index af21ef49a..567ce3a58 100644 --- a/server/rpc/rpcserver.go +++ b/server/rpc/rpcserver.go @@ -2718,7 +2718,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte } bestBlockHash = s.cfg.DAG.HighestTipHash().String() - confirmations = 1 + s.cfg.DAG.Height() - entry.BlockHeight() //TODO: (Ori) This is probably wrong. Done only for compilation + confirmations = 1 + s.cfg.DAG.ChainHeight() - entry.BlockChainHeight() //TODO: (Ori) This is probably wrong. Done only for compilation value = entry.Amount() pkScript = entry.PkScript() isCoinbase = entry.IsBlockReward()