mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
[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
This commit is contained in:
parent
6163d3b4ec
commit
33036278ac
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user