[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:
Svarog 2019-05-02 16:50:55 +03:00 committed by stasatdaglabs
parent 6163d3b4ec
commit 33036278ac
15 changed files with 163 additions and 143 deletions

View File

@ -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

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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() {

View File

@ -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.

View File

@ -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 {

View File

@ -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)
}

View File

@ -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)

View File

@ -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

View File

@ -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
}
}

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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()