mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-09-13 04:50:11 +00:00

* [NOD-1223] Delete unused files/packages. * [NOD-1223] Move signal and limits to the os package. * [NOD-1223] Put database and dbaccess into the db package. * [NOD-1223] Fold the logs package into the logger package. * [NOD-1223] Rename domainmessage to appmessage. * [NOD-1223] Rename to/from DomainMessage to AppMessage. * [NOD-1223] Move appmessage to the app packge. * [NOD-1223] Move protocol to the app packge. * [NOD-1223] Move the network package to the infrastructure packge. * [NOD-1223] Rename cmd to executables. * [NOD-1223] Fix go.doc in the logger package.
144 lines
5.8 KiB
Go
144 lines
5.8 KiB
Go
package blockdag
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/kaspanet/kaspad/app/appmessage"
|
|
"github.com/kaspanet/kaspad/util"
|
|
)
|
|
|
|
// SequenceLock represents the converted relative lock-time in seconds, and
|
|
// absolute block-blue-score 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
|
|
// 'BlockBlueScore' has been reached.
|
|
type SequenceLock struct {
|
|
Milliseconds int64
|
|
BlockBlueScore int64
|
|
}
|
|
|
|
// CalcSequenceLock computes a relative lock-time SequenceLock for the passed
|
|
// transaction using the passed UTXOSet to obtain the past median time
|
|
// for blocks in which the referenced inputs of the transactions were included
|
|
// within. The generated SequenceLock lock can be used in conjunction with a
|
|
// block height, and adjusted median block time to determine if all the inputs
|
|
// referenced within a transaction have reached sufficient maturity allowing
|
|
// the candidate transaction to be included in a block.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (dag *BlockDAG) CalcSequenceLock(tx *util.Tx, utxoSet UTXOSet) (*SequenceLock, error) {
|
|
dag.dagLock.RLock()
|
|
defer dag.dagLock.RUnlock()
|
|
|
|
return dag.calcSequenceLock(dag.selectedTip(), utxoSet, tx)
|
|
}
|
|
|
|
// CalcSequenceLockNoLock is lock free version of CalcSequenceLockWithLock
|
|
// This function is unsafe for concurrent access.
|
|
func (dag *BlockDAG) CalcSequenceLockNoLock(tx *util.Tx, utxoSet UTXOSet) (*SequenceLock, error) {
|
|
return dag.calcSequenceLock(dag.selectedTip(), utxoSet, tx)
|
|
}
|
|
|
|
// calcSequenceLock computes the relative lock-times for the passed
|
|
// transaction. See the exported version, CalcSequenceLock for further details.
|
|
//
|
|
// This function MUST be called with the DAG state lock held (for writes).
|
|
func (dag *BlockDAG) calcSequenceLock(node *blockNode, utxoSet UTXOSet, tx *util.Tx) (*SequenceLock, error) {
|
|
// 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{Milliseconds: -1, BlockBlueScore: -1}
|
|
|
|
// Sequence locks don't apply to coinbase transactions Therefore, we
|
|
// return sequence lock values of -1 indicating that this transaction
|
|
// can be included within a block at any given height or time.
|
|
if tx.IsCoinBase() {
|
|
return sequenceLock, nil
|
|
}
|
|
|
|
mTx := tx.MsgTx()
|
|
for txInIndex, txIn := range mTx.TxIn {
|
|
entry, ok := utxoSet.Get(txIn.PreviousOutpoint)
|
|
if !ok {
|
|
str := fmt.Sprintf("output %s referenced from "+
|
|
"transaction %s input %d either does not exist or "+
|
|
"has already been spent", txIn.PreviousOutpoint,
|
|
tx.ID(), txInIndex)
|
|
return sequenceLock, ruleError(ErrMissingTxOut, str)
|
|
}
|
|
|
|
// If the input blue score is set to the mempool blue score, then we
|
|
// assume the transaction makes it into the next block when
|
|
// evaluating its sequence blocks.
|
|
inputBlueScore := entry.BlockBlueScore()
|
|
if entry.IsUnaccepted() {
|
|
inputBlueScore = dag.virtual.blueScore
|
|
}
|
|
|
|
// Given a sequence number, we apply the relative time lock
|
|
// mask in order to obtain the time lock delta required before
|
|
// this input can be spent.
|
|
sequenceNum := txIn.Sequence
|
|
relativeLock := int64(sequenceNum & appmessage.SequenceLockTimeMask)
|
|
|
|
switch {
|
|
// Relative time locks are disabled for this input, so we can
|
|
// skip any further calculation.
|
|
case sequenceNum&appmessage.SequenceLockTimeDisabled == appmessage.SequenceLockTimeDisabled:
|
|
continue
|
|
case sequenceNum&appmessage.SequenceLockTimeIsSeconds == appmessage.SequenceLockTimeIsSeconds:
|
|
// This input requires a relative time lock expressed
|
|
// in seconds before it can be spent. Therefore, we
|
|
// need to query for the block prior to the one in
|
|
// which this input was accepted within so we can
|
|
// compute the past median time for the block prior to
|
|
// the one which accepted this referenced output.
|
|
blockNode := node
|
|
for blockNode.selectedParent.blueScore > inputBlueScore {
|
|
blockNode = blockNode.selectedParent
|
|
}
|
|
medianTime := blockNode.PastMedianTime(dag)
|
|
|
|
// Time based relative time-locks have a time granularity of
|
|
// appmessage.SequenceLockTimeGranularity, so we shift left by this
|
|
// amount to convert to the proper relative time-lock. We also
|
|
// subtract one from the relative lock to maintain the original
|
|
// lockTime semantics.
|
|
timeLockMilliseconds := (relativeLock << appmessage.SequenceLockTimeGranularity) - 1
|
|
timeLock := medianTime.UnixMilliseconds() + timeLockMilliseconds
|
|
if timeLock > sequenceLock.Milliseconds {
|
|
sequenceLock.Milliseconds = timeLock
|
|
}
|
|
default:
|
|
// The relative lock-time for this input is expressed
|
|
// in blocks so we calculate the relative offset from
|
|
// the input's blue score as its converted absolute
|
|
// lock-time. We subtract one from the relative lock in
|
|
// order to maintain the original lockTime semantics.
|
|
blockBlueScore := int64(inputBlueScore) + relativeLock - 1
|
|
if blockBlueScore > sequenceLock.BlockBlueScore {
|
|
sequenceLock.BlockBlueScore = blockBlueScore
|
|
}
|
|
}
|
|
}
|
|
|
|
return sequenceLock, nil
|
|
}
|
|
|
|
// LockTimeToSequence converts the passed relative locktime to a sequence
|
|
// number.
|
|
func LockTimeToSequence(isMilliseconds bool, locktime uint64) uint64 {
|
|
// If we're expressing the relative lock time in blocks, then the
|
|
// corresponding sequence number is simply the desired input age.
|
|
if !isMilliseconds {
|
|
return locktime
|
|
}
|
|
|
|
// Set the 22nd bit which indicates the lock time is in milliseconds, then
|
|
// shift the locktime over by 19 since the time granularity is in
|
|
// 524288-millisecond intervals (2^19). This results in a max lock-time of
|
|
// 34,359,214,080 seconds, or 1.1 years.
|
|
return appmessage.SequenceLockTimeIsSeconds |
|
|
locktime>>appmessage.SequenceLockTimeGranularity
|
|
}
|