mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-05 13:46:42 +00:00
[NOD-1416] Implement BlockProcessor. (#969)
* [NOD-1416] Add entry/exit logs to all the functions. * [NOD-1416] Build some scaffolding inside BlockProcessor. * [NOD-1416] Implement selectParentsForNewBlock. * [NOD-1416] Implement validateBlock. * [NOD-1476] Fix merge errors. * [NOD-1416] Move buildBlock and validateAndInsertBlock to separate files. * [NOD-1416] Begin implementing buildBlock. * [NOD-1416] Implement newBlockDifficulty. * [NOD-1416] Add skeletons for the rest of the buildBlock functions. * [NOD-1416] Implement newBlockUTXOCommitment. * [NOD-1416] Implement newBlockAcceptedIDMerkleRoot. * [NOD-1416] Implement newBlockHashMerkleRoot. * [NOD-1416] Fix bad function call. * [NOD-1416] Implement validateHeaderAndProofOfWork and validateBody. * [NOD-1416] Use ValidateProofOfWorkAndDifficulty. * [NOD-1416] Finish validateAndInsertBlock. * [NOD-1416] Implement newBlockHashMerkleRoot. * [NOD-1416] Implement newBlockAcceptedIDMerkleRoot. * [NOD-1416] Fix a comment. * [NOD-1416] Implement newBlockCoinbaseTransaction. * [NOD-1416] Add VirtualBlockHash. * [NOD-1416] Add ParentHashes and SelectedParent to VirtualData(). * [NOD-1416] Make go vet happy. * [NOD-1416] Implement discardAllChanges. * [NOD-1416] Implement commitAllChanges. * [NOD-1416] Fix factory. * [NOD-1416] Make go vet happy. * [NOD-1416] Format factory. * [NOD-1416] Pass transactionsWithCoinbase to buildHeader. * [NOD-1416] Call VirtualData() from buildHeader. * [NOD-1416] Fix a typo. * [NOD-1416] Fix in-out-of-context/header-body confusion. * [NOD-1416] Extract LogAndMeasureExecutionTime. * [NOD-1416] Add a comment about LogAndMeasureExecutionTime. * [NOD-1416] Simplify discardAllChanges and commitAllChanges. * [NOD-1416] If in-context validations fail, discard all changes and store the block with StatusInvalid. * [NOD-1416] Add a comment above Store. * [NOD-1416] Use errors.As instead of errors.Is.
This commit is contained in:
parent
f62183473c
commit
97b5b0b875
@ -21,3 +21,8 @@ func (dtc *DomainTxContext) StoreBlockRelation(blockHash *externalapi.DomainHash
|
||||
func NewDomainTxContext(dbTx *dbaccess.TxContext) *DomainTxContext {
|
||||
return &DomainTxContext{dbTx: dbTx}
|
||||
}
|
||||
|
||||
// Commit commits this database transaction
|
||||
func (dtc *DomainTxContext) Commit() error {
|
||||
return dtc.dbTx.Commit()
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ func New() model.AcceptanceDataStore {
|
||||
}
|
||||
|
||||
// Stage stages the given acceptanceData for the given blockHash
|
||||
func (ads *acceptanceDataStore) Stage(blockHash *externalapi.DomainHash, acceptanceData *model.BlockAcceptanceData) {
|
||||
func (ads *acceptanceDataStore) Stage(blockHash *externalapi.DomainHash, acceptanceData []*model.BlockAcceptanceData) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ func (ads *acceptanceDataStore) Commit(dbTx model.DBTxProxy) error {
|
||||
}
|
||||
|
||||
// Get gets the acceptanceData associated with the given blockHash
|
||||
func (ads *acceptanceDataStore) Get(dbContext model.DBContextProxy, blockHash *externalapi.DomainHash) (*model.BlockAcceptanceData, error) {
|
||||
func (ads *acceptanceDataStore) Get(dbContext model.DBContextProxy, blockHash *externalapi.DomainHash) ([]*model.BlockAcceptanceData, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/blockprocessor"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/blockvalidator"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/coinbasemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/consensusstatemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtopologymanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtraversalmanager"
|
||||
@ -96,8 +97,10 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, databaseContext *dba
|
||||
domainDBContext,
|
||||
pastMedianTimeManager,
|
||||
ghostdagDataStore)
|
||||
|
||||
genesisHash := externalapi.DomainHash([32]byte(*dagParams.GenesisHash))
|
||||
coinbaseManager := coinbasemanager.New(
|
||||
ghostdagDataStore,
|
||||
acceptanceDataStore)
|
||||
genesisHash := externalapi.DomainHash(*dagParams.GenesisHash)
|
||||
blockValidator := blockvalidator.New(
|
||||
dagParams.PowMax,
|
||||
false,
|
||||
@ -130,10 +133,17 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, databaseContext *dba
|
||||
difficultyManager,
|
||||
pastMedianTimeManager,
|
||||
ghostdagManager,
|
||||
coinbaseManager,
|
||||
acceptanceDataStore,
|
||||
blockStore,
|
||||
blockStatusStore,
|
||||
blockRelationStore)
|
||||
blockRelationStore,
|
||||
multisetStore,
|
||||
ghostdagDataStore,
|
||||
consensusStateStore,
|
||||
pruningStore,
|
||||
reachabilityDataStore,
|
||||
utxoDiffStore)
|
||||
|
||||
return &consensus{
|
||||
consensusStateManager: consensusStateManager,
|
||||
|
@ -11,7 +11,7 @@ type BlockAcceptanceData struct {
|
||||
// TransactionAcceptanceData stores a transaction together with an indication
|
||||
// if it was accepted or not by some block
|
||||
type TransactionAcceptanceData struct {
|
||||
Tx *externalapi.DomainTransaction
|
||||
Fee uint64
|
||||
IsAccepted bool
|
||||
Transaction *externalapi.DomainTransaction
|
||||
Fee uint64
|
||||
IsAccepted bool
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ package model
|
||||
type BlockStatus byte
|
||||
|
||||
const (
|
||||
// StatusDataStored indicates that the block's payload is stored on disk.
|
||||
StatusDataStored BlockStatus = iota
|
||||
// StatusInvalid indicates that the block is invalid.
|
||||
StatusInvalid BlockStatus = iota
|
||||
|
||||
// StatusValid indicates that the block has been fully validated.
|
||||
StatusValid
|
||||
|
@ -3,6 +3,6 @@ package externalapi
|
||||
// DomainCoinbaseData contains data by which a coinbase transaction
|
||||
// is built
|
||||
type DomainCoinbaseData struct {
|
||||
scriptPublicKey []byte
|
||||
extraData []byte
|
||||
ScriptPublicKey []byte
|
||||
ExtraData []byte
|
||||
}
|
||||
|
@ -4,10 +4,9 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// AcceptanceDataStore represents a store of AcceptanceData
|
||||
type AcceptanceDataStore interface {
|
||||
Stage(blockHash *externalapi.DomainHash, acceptanceData *BlockAcceptanceData)
|
||||
Store
|
||||
Stage(blockHash *externalapi.DomainHash, acceptanceData []*BlockAcceptanceData)
|
||||
IsStaged() bool
|
||||
Discard()
|
||||
Commit(dbTx DBTxProxy) error
|
||||
Get(dbContext DBContextProxy, blockHash *externalapi.DomainHash) (*BlockAcceptanceData, error)
|
||||
Get(dbContext DBContextProxy, blockHash *externalapi.DomainHash) ([]*BlockAcceptanceData, error)
|
||||
Delete(dbTx DBTxProxy, blockHash *externalapi.DomainHash) error
|
||||
}
|
||||
|
@ -4,10 +4,9 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// BlockStore represents a store of blocks
|
||||
type BlockStore interface {
|
||||
Store
|
||||
Stage(blockHash *externalapi.DomainHash, block *externalapi.DomainBlock)
|
||||
IsStaged() bool
|
||||
Discard()
|
||||
Commit(dbTx DBTxProxy) error
|
||||
Block(dbContext DBContextProxy, blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error)
|
||||
HasBlock(dbContext DBContextProxy, blockHash *externalapi.DomainHash) (bool, error)
|
||||
Blocks(dbContext DBContextProxy, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlock, error)
|
||||
|
@ -4,11 +4,10 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// BlockRelationStore represents a store of BlockRelations
|
||||
type BlockRelationStore interface {
|
||||
Store
|
||||
StageBlockRelation(blockHash *externalapi.DomainHash, parentHashes []*externalapi.DomainHash)
|
||||
StageTips(tipHashess []*externalapi.DomainHash)
|
||||
IsAnythingStaged() bool
|
||||
Discard()
|
||||
Commit(dbTx DBTxProxy) error
|
||||
BlockRelation(dbContext DBContextProxy, blockHash *externalapi.DomainHash) (*BlockRelations, error)
|
||||
Tips(dbContext DBContextProxy) ([]*externalapi.DomainHash, error)
|
||||
}
|
||||
|
@ -4,10 +4,9 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// BlockStatusStore represents a store of BlockStatuses
|
||||
type BlockStatusStore interface {
|
||||
Store
|
||||
Stage(blockHash *externalapi.DomainHash, blockStatus BlockStatus)
|
||||
IsStaged() bool
|
||||
Discard()
|
||||
Commit(dbTx DBTxProxy) error
|
||||
Get(dbContext DBContextProxy, blockHash *externalapi.DomainHash) (BlockStatus, error)
|
||||
Exists(dbContext DBContextProxy, blockHash *externalapi.DomainHash) (bool, error)
|
||||
}
|
||||
|
@ -4,9 +4,8 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// ConsensusStateStore represents a store for the current consensus state
|
||||
type ConsensusStateStore interface {
|
||||
Store
|
||||
Stage(consensusStateChanges *ConsensusStateChanges)
|
||||
IsStaged() bool
|
||||
Discard()
|
||||
Commit(dbTx DBTxProxy) error
|
||||
UTXOByOutpoint(dbContext DBContextProxy, outpoint *externalapi.DomainOutpoint) (*externalapi.UTXOEntry, error)
|
||||
}
|
||||
|
@ -4,9 +4,8 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// GHOSTDAGDataStore represents a store of BlockGHOSTDAGData
|
||||
type GHOSTDAGDataStore interface {
|
||||
Store
|
||||
Stage(blockHash *externalapi.DomainHash, blockGHOSTDAGData *BlockGHOSTDAGData)
|
||||
IsStaged() bool
|
||||
Discard()
|
||||
Commit(dbTx DBTxProxy) error
|
||||
Get(dbContext DBContextProxy, blockHash *externalapi.DomainHash) (*BlockGHOSTDAGData, error)
|
||||
}
|
||||
|
@ -4,10 +4,9 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// MultisetStore represents a store of Multisets
|
||||
type MultisetStore interface {
|
||||
Store
|
||||
Stage(blockHash *externalapi.DomainHash, multiset Multiset)
|
||||
IsStaged() bool
|
||||
Discard()
|
||||
Commit(dbTx DBTxProxy) error
|
||||
Get(dbContext DBContextProxy, blockHash *externalapi.DomainHash) (Multiset, error)
|
||||
Delete(dbTx DBTxProxy, blockHash *externalapi.DomainHash) error
|
||||
}
|
||||
|
@ -4,10 +4,9 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// PruningStore represents a store for the current pruning state
|
||||
type PruningStore interface {
|
||||
Store
|
||||
Stage(pruningPointBlockHash *externalapi.DomainHash, pruningPointUTXOSet ReadOnlyUTXOSet)
|
||||
IsStaged() bool
|
||||
Discard()
|
||||
Commit(dbTx DBTxProxy) error
|
||||
PruningPoint(dbContext DBContextProxy) (*externalapi.DomainHash, error)
|
||||
PruningPointSerializedUTXOSet(dbContext DBContextProxy) ([]byte, error)
|
||||
}
|
||||
|
@ -4,11 +4,10 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// ReachabilityDataStore represents a store of ReachabilityData
|
||||
type ReachabilityDataStore interface {
|
||||
Store
|
||||
StageReachabilityData(blockHash *externalapi.DomainHash, reachabilityData *ReachabilityData)
|
||||
StageReachabilityReindexRoot(reachabilityReindexRoot *externalapi.DomainHash)
|
||||
IsAnythingStaged() bool
|
||||
Discard()
|
||||
Commit(dbTx DBTxProxy) error
|
||||
ReachabilityData(dbContext DBContextProxy, blockHash *externalapi.DomainHash) (*ReachabilityData, error)
|
||||
ReachabilityReindexRoot(dbContext DBContextProxy) (*externalapi.DomainHash, error)
|
||||
}
|
||||
|
@ -4,10 +4,9 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// UTXODiffStore represents a store of UTXODiffs
|
||||
type UTXODiffStore interface {
|
||||
Store
|
||||
Stage(blockHash *externalapi.DomainHash, utxoDiff *UTXODiff, utxoDiffChild *externalapi.DomainHash)
|
||||
IsStaged() bool
|
||||
Discard()
|
||||
Commit(dbTx DBTxProxy) error
|
||||
UTXODiff(dbContext DBContextProxy, blockHash *externalapi.DomainHash) (*UTXODiff, error)
|
||||
UTXODiffChild(dbContext DBContextProxy, blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error)
|
||||
Delete(dbTx DBTxProxy, blockHash *externalapi.DomainHash) error
|
||||
|
@ -0,0 +1,10 @@
|
||||
package model
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// CoinbaseManager exposes methods for handling blocks'
|
||||
// coinbase transactions
|
||||
type CoinbaseManager interface {
|
||||
ExpectedCoinbaseTransaction(blockHash *externalapi.DomainHash,
|
||||
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error)
|
||||
}
|
@ -6,5 +6,5 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
type ConsensusStateManager interface {
|
||||
AddBlockToVirtual(blockHash *externalapi.DomainHash) error
|
||||
PopulateTransactionWithUTXOEntries(transaction *externalapi.DomainTransaction) error
|
||||
VirtualData() (medianTime int64, blueScore uint64, err error)
|
||||
VirtualData() (virtualData *VirtualData, err error)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package model
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// DAGTraversalManager exposes methods for travering blocks
|
||||
// DAGTraversalManager exposes methods for traversing blocks
|
||||
// in the DAG
|
||||
type DAGTraversalManager interface {
|
||||
HighestChainBlockBelowBlueScore(highHash *externalapi.DomainHash, blueScore uint64) (*externalapi.DomainHash, error)
|
||||
|
7
domain/consensus/model/store.go
Normal file
7
domain/consensus/model/store.go
Normal file
@ -0,0 +1,7 @@
|
||||
package model
|
||||
|
||||
// Store is a common interface for data stores
|
||||
type Store interface {
|
||||
Discard()
|
||||
Commit(dbTx DBTxProxy) error
|
||||
}
|
19
domain/consensus/model/virtual.go
Normal file
19
domain/consensus/model/virtual.go
Normal file
@ -0,0 +1,19 @@
|
||||
package model
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// VirtualBlockHash is a marker hash for the virtual block
|
||||
var VirtualBlockHash = &externalapi.DomainHash{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
}
|
||||
|
||||
// VirtualData is data about the current virtual block
|
||||
type VirtualData struct {
|
||||
PastMedianTime int64
|
||||
BlueScore uint64
|
||||
ParentHashes []*externalapi.DomainHash
|
||||
SelectedParent *externalapi.DomainHash
|
||||
}
|
@ -5,6 +5,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
)
|
||||
|
||||
// blockProcessor is responsible for processing incoming blocks
|
||||
@ -21,10 +22,20 @@ type blockProcessor struct {
|
||||
difficultyManager model.DifficultyManager
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
pastMedianTimeManager model.PastMedianTimeManager
|
||||
coinbaseManager model.CoinbaseManager
|
||||
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
blockStore model.BlockStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
blockRelationStore model.BlockRelationStore
|
||||
multisetStore model.MultisetStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
consensusStateStore model.ConsensusStateStore
|
||||
pruningStore model.PruningStore
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
utxoDiffStore model.UTXODiffStore
|
||||
|
||||
stores []model.Store
|
||||
}
|
||||
|
||||
// New instantiates a new BlockProcessor
|
||||
@ -39,10 +50,17 @@ func New(
|
||||
difficultyManager model.DifficultyManager,
|
||||
pastMedianTimeManager model.PastMedianTimeManager,
|
||||
ghostdagManager model.GHOSTDAGManager,
|
||||
coinbaseManager model.CoinbaseManager,
|
||||
acceptanceDataStore model.AcceptanceDataStore,
|
||||
blockStore model.BlockStore,
|
||||
blockStatusStore model.BlockStatusStore,
|
||||
blockRelationStore model.BlockRelationStore) model.BlockProcessor {
|
||||
blockRelationStore model.BlockRelationStore,
|
||||
multisetStore model.MultisetStore,
|
||||
ghostdagDataStore model.GHOSTDAGDataStore,
|
||||
consensusStateStore model.ConsensusStateStore,
|
||||
pruningStore model.PruningStore,
|
||||
reachabilityDataStore model.ReachabilityDataStore,
|
||||
utxoDiffStore model.UTXODiffStore) model.BlockProcessor {
|
||||
|
||||
return &blockProcessor{
|
||||
dagParams: dagParams,
|
||||
@ -54,25 +72,52 @@ func New(
|
||||
difficultyManager: difficultyManager,
|
||||
pastMedianTimeManager: pastMedianTimeManager,
|
||||
ghostdagManager: ghostdagManager,
|
||||
coinbaseManager: coinbaseManager,
|
||||
|
||||
consensusStateManager: consensusStateManager,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blockStore: blockStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
blockRelationStore: blockRelationStore,
|
||||
multisetStore: multisetStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
consensusStateStore: consensusStateStore,
|
||||
pruningStore: pruningStore,
|
||||
reachabilityDataStore: reachabilityDataStore,
|
||||
utxoDiffStore: utxoDiffStore,
|
||||
|
||||
stores: []model.Store{
|
||||
consensusStateStore,
|
||||
acceptanceDataStore,
|
||||
blockStore,
|
||||
blockStatusStore,
|
||||
blockRelationStore,
|
||||
multisetStore,
|
||||
ghostdagDataStore,
|
||||
consensusStateStore,
|
||||
pruningStore,
|
||||
reachabilityDataStore,
|
||||
utxoDiffStore,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BuildBlock builds a block over the current state, with the transactions
|
||||
// selected by the given transactionSelector
|
||||
// BuildBlock builds a block over the current state, with the given
|
||||
// coinbaseData and the given transactions
|
||||
func (bp *blockProcessor) BuildBlock(coinbaseData *externalapi.DomainCoinbaseData,
|
||||
transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error) {
|
||||
|
||||
return nil, nil
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "BuildBlock")
|
||||
defer onEnd()
|
||||
|
||||
return bp.buildBlock(coinbaseData, transactions)
|
||||
}
|
||||
|
||||
// ValidateAndInsertBlock validates the given block and, if valid, applies it
|
||||
// to the current state
|
||||
func (bp *blockProcessor) ValidateAndInsertBlock(block *externalapi.DomainBlock) error {
|
||||
return nil
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidateAndInsertBlock")
|
||||
defer onEnd()
|
||||
|
||||
return bp.validateAndInsertBlock(block)
|
||||
}
|
||||
|
142
domain/consensus/processes/blockprocessor/buildblock.go
Normal file
142
domain/consensus/processes/blockprocessor/buildblock.go
Normal file
@ -0,0 +1,142 @@
|
||||
package blockprocessor
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const blockVersion = 1
|
||||
|
||||
func (bp *blockProcessor) buildBlock(coinbaseData *externalapi.DomainCoinbaseData,
|
||||
transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error) {
|
||||
|
||||
coinbase, err := bp.newBlockCoinbaseTransaction(coinbaseData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactionsWithCoinbase := append([]*externalapi.DomainTransaction{coinbase}, transactions...)
|
||||
|
||||
header, err := bp.buildHeader(transactionsWithCoinbase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headerHash := hashserialization.HeaderHash(header)
|
||||
|
||||
return &externalapi.DomainBlock{
|
||||
Header: header,
|
||||
Transactions: transactionsWithCoinbase,
|
||||
Hash: headerHash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) newBlockCoinbaseTransaction(
|
||||
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error) {
|
||||
|
||||
return bp.coinbaseManager.ExpectedCoinbaseTransaction(model.VirtualBlockHash, coinbaseData)
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) buildHeader(transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlockHeader, error) {
|
||||
virtualData, err := bp.consensusStateManager.VirtualData()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentHashes, err := bp.newBlockParentHashes(virtualData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
timeInMilliseconds, err := bp.newBlockTime(virtualData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bits, err := bp.newBlockDifficulty(virtualData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashMerkleRoot := bp.newBlockHashMerkleRoot(transactions)
|
||||
acceptedIDMerkleRoot, err := bp.newBlockAcceptedIDMerkleRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
utxoCommitment, err := bp.newBlockUTXOCommitment()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &externalapi.DomainBlockHeader{
|
||||
Version: blockVersion,
|
||||
ParentHashes: parentHashes,
|
||||
HashMerkleRoot: *hashMerkleRoot,
|
||||
AcceptedIDMerkleRoot: *acceptedIDMerkleRoot,
|
||||
UTXOCommitment: *utxoCommitment,
|
||||
TimeInMilliseconds: timeInMilliseconds,
|
||||
Bits: bits,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) newBlockParentHashes(virtualData *model.VirtualData) ([]*externalapi.DomainHash, error) {
|
||||
return virtualData.ParentHashes, nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) newBlockTime(virtualData *model.VirtualData) (int64, error) {
|
||||
// The timestamp for the block must not be before the median timestamp
|
||||
// of the last several blocks. Thus, choose the maximum between the
|
||||
// current time and one second after the past median time. The current
|
||||
// timestamp is truncated to a millisecond boundary before comparison since a
|
||||
// block timestamp does not supported a precision greater than one
|
||||
// millisecond.
|
||||
newTimestamp := mstime.Now().UnixMilliseconds() + 1
|
||||
minTimestamp, err := bp.pastMedianTimeManager.PastMedianTime(virtualData.SelectedParent)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if newTimestamp < minTimestamp {
|
||||
newTimestamp = minTimestamp
|
||||
}
|
||||
return newTimestamp, nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) newBlockDifficulty(virtualData *model.VirtualData) (uint32, error) {
|
||||
return bp.difficultyManager.RequiredDifficulty(virtualData.SelectedParent)
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) newBlockHashMerkleRoot(transactions []*externalapi.DomainTransaction) *externalapi.DomainHash {
|
||||
return merkle.CalculateHashMerkleRoot(transactions)
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) newBlockAcceptedIDMerkleRoot() (*externalapi.DomainHash, error) {
|
||||
newBlockAcceptanceData, err := bp.acceptanceDataStore.Get(bp.databaseContext, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var acceptedTransactions []*externalapi.DomainTransaction
|
||||
for _, blockAcceptanceData := range newBlockAcceptanceData {
|
||||
for _, transactionAcceptance := range blockAcceptanceData.TransactionAcceptanceData {
|
||||
if !transactionAcceptance.IsAccepted {
|
||||
continue
|
||||
}
|
||||
acceptedTransactions = append(acceptedTransactions, transactionAcceptance.Transaction)
|
||||
}
|
||||
}
|
||||
sort.Slice(acceptedTransactions, func(i, j int) bool {
|
||||
acceptedTransactionIID := hashserialization.TransactionID(acceptedTransactions[i])
|
||||
acceptedTransactionJID := hashserialization.TransactionID(acceptedTransactions[j])
|
||||
return transactionid.Less(acceptedTransactionIID, acceptedTransactionJID)
|
||||
})
|
||||
|
||||
return merkle.CalculateIDMerkleRoot(acceptedTransactions), nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) newBlockUTXOCommitment() (*externalapi.DomainHash, error) {
|
||||
newBlockMultiset, err := bp.multisetStore.Get(bp.databaseContext, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newBlockUTXOCommitment := newBlockMultiset.Hash()
|
||||
return newBlockUTXOCommitment, nil
|
||||
}
|
7
domain/consensus/processes/blockprocessor/log.go
Normal file
7
domain/consensus/processes/blockprocessor/log.go
Normal file
@ -0,0 +1,7 @@
|
||||
package blockprocessor
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.BDAG)
|
@ -0,0 +1,108 @@
|
||||
package blockprocessor
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock) error {
|
||||
err := bp.validateBlock(block)
|
||||
if err != nil {
|
||||
bp.discardAllChanges()
|
||||
return err
|
||||
}
|
||||
|
||||
// Block validations passed, save whatever DAG data was
|
||||
// collected so far
|
||||
err = bp.commitAllChanges()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt to add the block to the virtual
|
||||
err = bp.consensusStateManager.AddBlockToVirtual(block.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bp.commitAllChanges()
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock) error {
|
||||
// If either in-isolation or proof-of-work validations fail, simply
|
||||
// return an error without writing anything in the database.
|
||||
// This is to prevent spamming attacks.
|
||||
err := bp.validateBlockInIsolationAndProofOfWork(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If in-context validations fail, discard all changes and store the
|
||||
// block with StatusInvalid.
|
||||
err = bp.validateInContext(block)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
bp.discardAllChanges()
|
||||
bp.blockStatusStore.Stage(block.Hash, model.StatusInvalid)
|
||||
commitErr := bp.commitAllChanges()
|
||||
if commitErr != nil {
|
||||
return commitErr
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validateBlockInIsolationAndProofOfWork(block *externalapi.DomainBlock) error {
|
||||
err := bp.blockValidator.ValidateHeaderInIsolation(block.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = bp.blockValidator.ValidateBodyInIsolation(block.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = bp.blockValidator.ValidateProofOfWorkAndDifficulty(block.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validateInContext(block *externalapi.DomainBlock) error {
|
||||
bp.blockStore.Stage(block.Hash, block)
|
||||
err := bp.blockValidator.ValidateHeaderInContext(block.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = bp.blockValidator.ValidateBodyInContext(block.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) discardAllChanges() {
|
||||
for _, store := range bp.stores {
|
||||
store.Discard()
|
||||
}
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) commitAllChanges() error {
|
||||
dbTx, err := bp.databaseContext.NewTx()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, store := range bp.stores {
|
||||
err = store.Commit(dbTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return dbTx.Commit()
|
||||
}
|
@ -134,7 +134,7 @@ func (v *blockValidator) checkTransactionsInIsolation(block *externalapi.DomainB
|
||||
}
|
||||
|
||||
func (v *blockValidator) checkBlockHashMerkleRoot(block *externalapi.DomainBlock) error {
|
||||
calculatedHashMerkleRoot := merkle.CalcHashMerkleRoot(block.Transactions)
|
||||
calculatedHashMerkleRoot := merkle.CalculateHashMerkleRoot(block.Transactions)
|
||||
if block.Header.HashMerkleRoot != *calculatedHashMerkleRoot {
|
||||
return errors.Wrapf(ruleerrors.ErrBadMerkleRoot, "block hash merkle root is invalid - block "+
|
||||
"header indicates %s, but calculated value is %s",
|
||||
|
@ -0,0 +1,28 @@
|
||||
package coinbasemanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
type coinbaseManager struct {
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
}
|
||||
|
||||
// New instantiates a new CoinbaseManager
|
||||
func New(
|
||||
ghostdagDataStore model.GHOSTDAGDataStore,
|
||||
acceptanceDataStore model.AcceptanceDataStore) model.CoinbaseManager {
|
||||
|
||||
return &coinbaseManager{
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
}
|
||||
}
|
||||
|
||||
func (c coinbaseManager) ExpectedCoinbaseTransaction(blockHash *externalapi.DomainHash,
|
||||
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error) {
|
||||
|
||||
panic("implement me")
|
||||
}
|
@ -68,9 +68,9 @@ func (csm *consensusStateManager) AddBlockToVirtual(blockHash *externalapi.Domai
|
||||
return nil
|
||||
}
|
||||
|
||||
// VirtualData returns the medianTime and blueScore of the current virtual block
|
||||
func (csm *consensusStateManager) VirtualData() (medianTime int64, blueScore uint64, err error) {
|
||||
return 0, 0, nil
|
||||
// VirtualData returns data on the current virtual block
|
||||
func (csm *consensusStateManager) VirtualData() (virtualData *model.VirtualData, err error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
// PopulateTransactionWithUTXOEntries populates the transaction UTXO entries with data from the virtual.
|
||||
|
@ -42,9 +42,9 @@ func hashMerkleBranches(left, right *externalapi.DomainHash) *externalapi.Domain
|
||||
return &hash
|
||||
}
|
||||
|
||||
// CalcHashMerkleRoot calculates the merkle root of a tree consisted of the given transaction hashes.
|
||||
// CalculateHashMerkleRoot calculates the merkle root of a tree consisted of the given transaction hashes.
|
||||
// See `merkleRoot` for more info.
|
||||
func CalcHashMerkleRoot(transactions []*externalapi.DomainTransaction) *externalapi.DomainHash {
|
||||
func CalculateHashMerkleRoot(transactions []*externalapi.DomainTransaction) *externalapi.DomainHash {
|
||||
txHashes := make([]*externalapi.DomainHash, len(transactions))
|
||||
for i, tx := range transactions {
|
||||
txHashes[i] = hashserialization.TransactionHash(tx)
|
||||
@ -52,9 +52,9 @@ func CalcHashMerkleRoot(transactions []*externalapi.DomainTransaction) *external
|
||||
return merkleRoot(txHashes)
|
||||
}
|
||||
|
||||
// CalcIDMerkleRoot calculates the merkle root of a tree consisted of the given transaction IDs.
|
||||
// CalculateIDMerkleRoot calculates the merkle root of a tree consisted of the given transaction IDs.
|
||||
// See `merkleRoot` for more info.
|
||||
func CalcIDMerkleRoot(transactions []*externalapi.DomainTransaction) *externalapi.DomainHash {
|
||||
func CalculateIDMerkleRoot(transactions []*externalapi.DomainTransaction) *externalapi.DomainHash {
|
||||
txIDs := make([]*externalapi.DomainHash, len(transactions))
|
||||
for i, tx := range transactions {
|
||||
txIDs[i] = (*externalapi.DomainHash)(hashserialization.TransactionID(tx))
|
||||
|
29
domain/consensus/utils/transactionid/compare.go
Normal file
29
domain/consensus/utils/transactionid/compare.go
Normal file
@ -0,0 +1,29 @@
|
||||
package transactionid
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// cmp compares two transaction IDs and returns:
|
||||
//
|
||||
// -1 if a < b
|
||||
// 0 if a == b
|
||||
// +1 if a > b
|
||||
//
|
||||
func cmp(a, b *externalapi.DomainTransactionID) int {
|
||||
// We compare the transaction IDs backwards because Hash is stored as a little endian byte array.
|
||||
for i := externalapi.DomainHashSize - 1; i >= 0; i-- {
|
||||
switch {
|
||||
case a[i] < b[i]:
|
||||
return -1
|
||||
case a[i] > b[i]:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Less returns true iff transaction ID a is less than hash b
|
||||
func Less(a, b *externalapi.DomainTransactionID) bool {
|
||||
return cmp(a, b) < 0
|
||||
}
|
17
infrastructure/logger/utils.go
Normal file
17
infrastructure/logger/utils.go
Normal file
@ -0,0 +1,17 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// LogAndMeasureExecutionTime logs that `functionName` has
|
||||
// started. The user is expected to defer `onEnd`, which
|
||||
// will then log that the function has ended, as well as
|
||||
// the time duration the function had ran.
|
||||
func LogAndMeasureExecutionTime(log *Logger, functionName string) (onEnd func()) {
|
||||
start := time.Now()
|
||||
log.Debugf("%s start", functionName)
|
||||
return func() {
|
||||
log.Debugf("%s end. Took: %s", functionName, time.Since(start))
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user