[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:
stasatdaglabs 2020-10-27 17:24:15 +02:00 committed by GitHub
parent f62183473c
commit 97b5b0b875
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 466 additions and 49 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,7 @@
package model
// Store is a common interface for data stores
type Store interface {
Discard()
Commit(dbTx DBTxProxy) error
}

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

View File

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

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

View File

@ -0,0 +1,7 @@
package blockprocessor
import (
"github.com/kaspanet/kaspad/infrastructure/logger"
)
var log, _ = logger.Get(logger.SubsystemTags.BDAG)

View File

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

View File

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

View File

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

View File

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

View File

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

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

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