mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-03-08 01:51:39 +00:00
[NOD-1496] Implement headers only verification (#987)
* [NOD-1496] Implement headers only verification * [NOD-1496] Add checkParentsExist * [NOD-1496] Stage block statuses in block processor * [NOD-1496] Rename AddBlock->AddHeaderTip * [NOD-1496] Return early from validateAndInsertBlock on header only and put ValidateProofOfWorkAndDifficulty inside validateBlock
This commit is contained in:
@@ -8,7 +8,7 @@ import (
|
||||
// Consensus maintains the current core state of the node
|
||||
type Consensus interface {
|
||||
BuildBlock(coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error)
|
||||
ValidateAndInsertBlock(block *externalapi.DomainBlock) error
|
||||
ValidateAndInsertBlock(block *externalapi.DomainBlock, headerOnly bool) error
|
||||
ValidateTransactionAndPopulateWithConsensusData(transaction *externalapi.DomainTransaction) error
|
||||
|
||||
GetBlock(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error)
|
||||
@@ -50,8 +50,8 @@ func (s *consensus) BuildBlock(coinbaseData *externalapi.DomainCoinbaseData,
|
||||
|
||||
// ValidateAndInsertBlock validates the given block and, if valid, applies it
|
||||
// to the current state
|
||||
func (s *consensus) ValidateAndInsertBlock(block *externalapi.DomainBlock) error {
|
||||
return s.blockProcessor.ValidateAndInsertBlock(block)
|
||||
func (s *consensus) ValidateAndInsertBlock(block *externalapi.DomainBlock, headerOnly bool) error {
|
||||
return s.blockProcessor.ValidateAndInsertBlock(block, headerOnly)
|
||||
}
|
||||
|
||||
// ValidateTransactionAndPopulateWithConsensusData validates the given transaction
|
||||
|
||||
17
domain/consensus/database/serialization/header_tips.go
Normal file
17
domain/consensus/database/serialization/header_tips.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package serialization
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// HeaderTipsToDBHeaderTips converts a slice of hashes to DbHeaderTips
|
||||
func HeaderTipsToDBHeaderTips(tips []*externalapi.DomainHash) *DbHeaderTips {
|
||||
return &DbHeaderTips{
|
||||
Tips: DomainHashesToDbHashes(tips),
|
||||
}
|
||||
}
|
||||
|
||||
// DBHeaderTipsTOHeaderTips converts DbHeaderTips to a slice of hashes
|
||||
func DBHeaderTipsTOHeaderTips(dbHeaderTips *DbHeaderTips) ([]*externalapi.DomainHash, error) {
|
||||
return DbHashesToDomainHashes(dbHeaderTips.Tips)
|
||||
}
|
||||
@@ -1441,7 +1441,7 @@ func (x *DbUtxoDiff) GetToRemove() []*DbUtxoCollectionItem {
|
||||
return nil
|
||||
}
|
||||
|
||||
type PruningPointUTXOSetBytes struct {
|
||||
type DbPruningPointUTXOSetBytes struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
@@ -1449,8 +1449,8 @@ type PruningPointUTXOSetBytes struct {
|
||||
Bytes []byte `protobuf:"bytes,1,opt,name=bytes,proto3" json:"bytes,omitempty"`
|
||||
}
|
||||
|
||||
func (x *PruningPointUTXOSetBytes) Reset() {
|
||||
*x = PruningPointUTXOSetBytes{}
|
||||
func (x *DbPruningPointUTXOSetBytes) Reset() {
|
||||
*x = DbPruningPointUTXOSetBytes{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_messages_proto_msgTypes[24]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -1458,13 +1458,13 @@ func (x *PruningPointUTXOSetBytes) Reset() {
|
||||
}
|
||||
}
|
||||
|
||||
func (x *PruningPointUTXOSetBytes) String() string {
|
||||
func (x *DbPruningPointUTXOSetBytes) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PruningPointUTXOSetBytes) ProtoMessage() {}
|
||||
func (*DbPruningPointUTXOSetBytes) ProtoMessage() {}
|
||||
|
||||
func (x *PruningPointUTXOSetBytes) ProtoReflect() protoreflect.Message {
|
||||
func (x *DbPruningPointUTXOSetBytes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[24]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
@@ -1476,18 +1476,65 @@ func (x *PruningPointUTXOSetBytes) ProtoReflect() protoreflect.Message {
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PruningPointUTXOSetBytes.ProtoReflect.Descriptor instead.
|
||||
func (*PruningPointUTXOSetBytes) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use DbPruningPointUTXOSetBytes.ProtoReflect.Descriptor instead.
|
||||
func (*DbPruningPointUTXOSetBytes) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{24}
|
||||
}
|
||||
|
||||
func (x *PruningPointUTXOSetBytes) GetBytes() []byte {
|
||||
func (x *DbPruningPointUTXOSetBytes) GetBytes() []byte {
|
||||
if x != nil {
|
||||
return x.Bytes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DbHeaderTips struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Tips []*DbHash `protobuf:"bytes,1,rep,name=tips,proto3" json:"tips,omitempty"`
|
||||
}
|
||||
|
||||
func (x *DbHeaderTips) Reset() {
|
||||
*x = DbHeaderTips{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_messages_proto_msgTypes[25]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *DbHeaderTips) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DbHeaderTips) ProtoMessage() {}
|
||||
|
||||
func (x *DbHeaderTips) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_messages_proto_msgTypes[25]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use DbHeaderTips.ProtoReflect.Descriptor instead.
|
||||
func (*DbHeaderTips) Descriptor() ([]byte, []int) {
|
||||
return file_messages_proto_rawDescGZIP(), []int{25}
|
||||
}
|
||||
|
||||
func (x *DbHeaderTips) GetTips() []*DbHash {
|
||||
if x != nil {
|
||||
return x.Tips
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_messages_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_messages_proto_rawDesc = []byte{
|
||||
@@ -1700,14 +1747,18 @@ var file_messages_proto_rawDesc = []byte{
|
||||
0x08, 0x74, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x23, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
|
||||
0x44, 0x62, 0x55, 0x74, 0x78, 0x6f, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x49, 0x74, 0x65, 0x6d, 0x52, 0x08, 0x74, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x22, 0x30,
|
||||
0x0a, 0x18, 0x50, 0x72, 0x75, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x55, 0x54,
|
||||
0x58, 0x4f, 0x53, 0x65, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79,
|
||||
0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73,
|
||||
0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b,
|
||||
0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2f, 0x73,
|
||||
0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x33,
|
||||
0x49, 0x74, 0x65, 0x6d, 0x52, 0x08, 0x74, 0x6f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x22, 0x32,
|
||||
0x0a, 0x1a, 0x44, 0x62, 0x50, 0x72, 0x75, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x69, 0x6e, 0x74,
|
||||
0x55, 0x54, 0x58, 0x4f, 0x53, 0x65, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74,
|
||||
0x65, 0x73, 0x22, 0x39, 0x0a, 0x0c, 0x44, 0x62, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x54, 0x69,
|
||||
0x70, 0x73, 0x12, 0x29, 0x0a, 0x04, 0x74, 0x69, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x2e, 0x44, 0x62, 0x48, 0x61, 0x73, 0x68, 0x52, 0x04, 0x74, 0x69, 0x70, 0x73, 0x42, 0x2a, 0x5a,
|
||||
0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2f, 0x73, 0x65, 0x72, 0x69,
|
||||
0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -1722,7 +1773,7 @@ func file_messages_proto_rawDescGZIP() []byte {
|
||||
return file_messages_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
|
||||
var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
|
||||
var file_messages_proto_goTypes = []interface{}{
|
||||
(*DbBlock)(nil), // 0: serialization.DbBlock
|
||||
(*DbBlockHeader)(nil), // 1: serialization.DbBlockHeader
|
||||
@@ -1748,7 +1799,8 @@ var file_messages_proto_goTypes = []interface{}{
|
||||
(*DbReachabilityTreeNode)(nil), // 21: serialization.DbReachabilityTreeNode
|
||||
(*DbReachabilityInterval)(nil), // 22: serialization.DbReachabilityInterval
|
||||
(*DbUtxoDiff)(nil), // 23: serialization.DbUtxoDiff
|
||||
(*PruningPointUTXOSetBytes)(nil), // 24: serialization.PruningPointUTXOSetBytes
|
||||
(*DbPruningPointUTXOSetBytes)(nil), // 24: serialization.DbPruningPointUTXOSetBytes
|
||||
(*DbHeaderTips)(nil), // 25: serialization.DbHeaderTips
|
||||
}
|
||||
var file_messages_proto_depIdxs = []int32{
|
||||
1, // 0: serialization.DbBlock.header:type_name -> serialization.DbBlockHeader
|
||||
@@ -1783,11 +1835,12 @@ var file_messages_proto_depIdxs = []int32{
|
||||
22, // 29: serialization.DbReachabilityTreeNode.interval:type_name -> serialization.DbReachabilityInterval
|
||||
18, // 30: serialization.DbUtxoDiff.toAdd:type_name -> serialization.DbUtxoCollectionItem
|
||||
18, // 31: serialization.DbUtxoDiff.toRemove:type_name -> serialization.DbUtxoCollectionItem
|
||||
32, // [32:32] is the sub-list for method output_type
|
||||
32, // [32:32] is the sub-list for method input_type
|
||||
32, // [32:32] is the sub-list for extension type_name
|
||||
32, // [32:32] is the sub-list for extension extendee
|
||||
0, // [0:32] is the sub-list for field type_name
|
||||
2, // 32: serialization.DbHeaderTips.tips:type_name -> serialization.DbHash
|
||||
33, // [33:33] is the sub-list for method output_type
|
||||
33, // [33:33] is the sub-list for method input_type
|
||||
33, // [33:33] is the sub-list for extension type_name
|
||||
33, // [33:33] is the sub-list for extension extendee
|
||||
0, // [0:33] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_messages_proto_init() }
|
||||
@@ -2085,7 +2138,19 @@ func file_messages_proto_init() {
|
||||
}
|
||||
}
|
||||
file_messages_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*PruningPointUTXOSetBytes); i {
|
||||
switch v := v.(*DbPruningPointUTXOSetBytes); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_messages_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*DbHeaderTips); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@@ -2103,7 +2168,7 @@ func file_messages_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_messages_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 25,
|
||||
NumMessages: 26,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -135,6 +135,10 @@ message DbUtxoDiff {
|
||||
repeated DbUtxoCollectionItem toRemove = 2;
|
||||
}
|
||||
|
||||
message PruningPointUTXOSetBytes {
|
||||
message DbPruningPointUTXOSetBytes {
|
||||
bytes bytes = 1;
|
||||
}
|
||||
|
||||
message DbHeaderTips {
|
||||
repeated DbHash tips = 1;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package headertipsstore
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
|
||||
)
|
||||
|
||||
var headerTipsKey = dbkeys.MakeBucket().Key([]byte("header-tips"))
|
||||
|
||||
type headerTipsStore struct {
|
||||
staging []*externalapi.DomainHash
|
||||
}
|
||||
|
||||
func (h headerTipsStore) Discard() {
|
||||
h.staging = nil
|
||||
}
|
||||
|
||||
func (h headerTipsStore) Commit(dbTx model.DBTransaction) error {
|
||||
tipsBytes, err := h.serializeTips(h.staging)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dbTx.Put(headerTipsKey, tipsBytes)
|
||||
}
|
||||
|
||||
func (h headerTipsStore) Stage(tips []*externalapi.DomainHash) {
|
||||
h.staging = tips
|
||||
}
|
||||
|
||||
func (h headerTipsStore) IsStaged() bool {
|
||||
return h.staging != nil
|
||||
}
|
||||
|
||||
func (h headerTipsStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
|
||||
if h.staging != nil {
|
||||
return h.staging, nil
|
||||
}
|
||||
|
||||
tipsBytes, err := dbContext.Get(headerTipsKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return h.deserializeTips(tipsBytes)
|
||||
}
|
||||
|
||||
func (h headerTipsStore) serializeTips(tips []*externalapi.DomainHash) ([]byte, error) {
|
||||
dbTips := serialization.HeaderTipsToDBHeaderTips(tips)
|
||||
return proto.Marshal(dbTips)
|
||||
}
|
||||
|
||||
func (h headerTipsStore) deserializeTips(tipsBytes []byte) ([]*externalapi.DomainHash, error) {
|
||||
dbTips := &serialization.DbHeaderTips{}
|
||||
err := proto.Unmarshal(tipsBytes, dbTips)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return serialization.DBHeaderTipsTOHeaderTips(dbTips)
|
||||
}
|
||||
|
||||
// New instantiates a new HeaderTipsStore
|
||||
func New() model.HeaderTipsStore {
|
||||
return &headerTipsStore{}
|
||||
}
|
||||
@@ -106,11 +106,11 @@ func (ps *pruningStore) deserializePruningPoint(pruningPointBytes []byte) (*exte
|
||||
}
|
||||
|
||||
func (ps *pruningStore) serializeUTXOSetBytes(pruningPointUTXOSetBytes []byte) ([]byte, error) {
|
||||
return proto.Marshal(&serialization.PruningPointUTXOSetBytes{Bytes: pruningPointUTXOSetBytes})
|
||||
return proto.Marshal(&serialization.DbPruningPointUTXOSetBytes{Bytes: pruningPointUTXOSetBytes})
|
||||
}
|
||||
|
||||
func (ps *pruningStore) deserializeUTXOSetBytes(dbPruningPointUTXOSetBytes []byte) ([]byte, error) {
|
||||
dbPruningPointUTXOSet := &serialization.PruningPointUTXOSetBytes{}
|
||||
dbPruningPointUTXOSet := &serialization.DbPruningPointUTXOSetBytes{}
|
||||
err := proto.Unmarshal(dbPruningPointUTXOSetBytes, dbPruningPointUTXOSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/blockstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/consensusstatestore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/ghostdagdatastore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/headertipsstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/multisetstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/pruningstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/reachabilitydatastore"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtraversalmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/difficultymanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/headertipsmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/mergedepthmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/pastmediantimemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/pruningmanager"
|
||||
@@ -54,6 +56,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
utxoDiffStore := utxodiffstore.New()
|
||||
consensusStateStore := consensusstatestore.New()
|
||||
ghostdagDataStore := ghostdagdatastore.New()
|
||||
headerTipsStore := headertipsstore.New()
|
||||
|
||||
dbManager := consensusdatabase.New(db)
|
||||
|
||||
@@ -97,6 +100,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
coinbaseManager := coinbasemanager.New(
|
||||
ghostdagDataStore,
|
||||
acceptanceDataStore)
|
||||
headerTipsManager := headertipsmanager.New(dbManager, dagTopologyManager, headerTipsStore)
|
||||
genesisHash := externalapi.DomainHash(*dagParams.GenesisHash)
|
||||
mergeDepthManager := mergedepthmanager.New(
|
||||
dagParams.FinalityDepth(),
|
||||
@@ -124,6 +128,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
blockStore,
|
||||
ghostdagDataStore,
|
||||
blockHeaderStore,
|
||||
blockStatusStore,
|
||||
)
|
||||
consensusStateManager, err := consensusstatemanager.New(
|
||||
dbManager,
|
||||
@@ -162,6 +167,8 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
pastMedianTimeManager,
|
||||
ghostdagManager,
|
||||
coinbaseManager,
|
||||
headerTipsManager,
|
||||
|
||||
acceptanceDataStore,
|
||||
blockStore,
|
||||
blockStatusStore,
|
||||
@@ -172,7 +179,8 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
pruningStore,
|
||||
reachabilityDataStore,
|
||||
utxoDiffStore,
|
||||
blockHeaderStore)
|
||||
blockHeaderStore,
|
||||
headerTipsStore)
|
||||
|
||||
syncManager := syncmanager.New(dagTraversalManager)
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ package externalapi
|
||||
type DomainBlock struct {
|
||||
Header *DomainBlockHeader
|
||||
Transactions []*DomainTransaction
|
||||
|
||||
Hash *DomainHash
|
||||
}
|
||||
|
||||
// DomainBlockHeader represents the header part of a Kaspa block
|
||||
|
||||
@@ -13,10 +13,6 @@ const (
|
||||
// StatusValidateFailed indicates that the block has failed validation.
|
||||
StatusValidateFailed
|
||||
|
||||
// StatusInvalidAncestor indicates that one of the block's ancestors has
|
||||
// has failed validation, thus the block is also invalid.
|
||||
StatusInvalidAncestor
|
||||
|
||||
// StatusUTXOPendingVerification indicates that the block is pending verification against its past UTXO-Set, either
|
||||
// because it was not yet verified since the block was never in the selected parent chain, or if the
|
||||
// block violates finality.
|
||||
@@ -24,4 +20,7 @@ const (
|
||||
|
||||
// StatusDisqualifiedFromChain indicates that the block is not eligible to be a selected parent.
|
||||
StatusDisqualifiedFromChain
|
||||
|
||||
// StatusHeaderOnly indicates that the block transactions are not held (pruned or wasn't added yet)
|
||||
StatusHeaderOnly
|
||||
)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package model
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// HeaderTipsStore represents a store of the header tips
|
||||
type HeaderTipsStore interface {
|
||||
Store
|
||||
Stage(tips []*externalapi.DomainHash)
|
||||
IsStaged() bool
|
||||
Tips(dbContext DBReader) ([]*externalapi.DomainHash, error)
|
||||
}
|
||||
@@ -6,5 +6,5 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
// and creating blocks from the current state
|
||||
type BlockProcessor interface {
|
||||
BuildBlock(coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error)
|
||||
ValidateAndInsertBlock(block *externalapi.DomainBlock) error
|
||||
ValidateAndInsertBlock(block *externalapi.DomainBlock, headerOnly bool) error
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package model
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// HeaderTipsManager manages the state of the header tips
|
||||
type HeaderTipsManager interface {
|
||||
AddHeaderTip(hash *externalapi.DomainHash) error
|
||||
}
|
||||
@@ -22,6 +22,7 @@ type blockProcessor struct {
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
pastMedianTimeManager model.PastMedianTimeManager
|
||||
coinbaseManager model.CoinbaseManager
|
||||
headerTipsManager model.HeaderTipsManager
|
||||
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
blockStore model.BlockStore
|
||||
@@ -34,6 +35,7 @@ type blockProcessor struct {
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
utxoDiffStore model.UTXODiffStore
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
headerTipsStore model.HeaderTipsStore
|
||||
|
||||
stores []model.Store
|
||||
}
|
||||
@@ -51,6 +53,8 @@ func New(
|
||||
pastMedianTimeManager model.PastMedianTimeManager,
|
||||
ghostdagManager model.GHOSTDAGManager,
|
||||
coinbaseManager model.CoinbaseManager,
|
||||
headerTipsManager model.HeaderTipsManager,
|
||||
|
||||
acceptanceDataStore model.AcceptanceDataStore,
|
||||
blockStore model.BlockStore,
|
||||
blockStatusStore model.BlockStatusStore,
|
||||
@@ -61,7 +65,8 @@ func New(
|
||||
pruningStore model.PruningStore,
|
||||
reachabilityDataStore model.ReachabilityDataStore,
|
||||
utxoDiffStore model.UTXODiffStore,
|
||||
blockHeaderStore model.BlockHeaderStore) model.BlockProcessor {
|
||||
blockHeaderStore model.BlockHeaderStore,
|
||||
headerTipsStore model.HeaderTipsStore) model.BlockProcessor {
|
||||
|
||||
return &blockProcessor{
|
||||
dagParams: dagParams,
|
||||
@@ -74,6 +79,7 @@ func New(
|
||||
pastMedianTimeManager: pastMedianTimeManager,
|
||||
ghostdagManager: ghostdagManager,
|
||||
coinbaseManager: coinbaseManager,
|
||||
headerTipsManager: headerTipsManager,
|
||||
|
||||
consensusStateManager: consensusStateManager,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
@@ -87,6 +93,7 @@ func New(
|
||||
reachabilityDataStore: reachabilityDataStore,
|
||||
utxoDiffStore: utxoDiffStore,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
headerTipsStore: headerTipsStore,
|
||||
|
||||
stores: []model.Store{
|
||||
consensusStateStore,
|
||||
@@ -101,6 +108,7 @@ func New(
|
||||
reachabilityDataStore,
|
||||
utxoDiffStore,
|
||||
blockHeaderStore,
|
||||
headerTipsStore,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -118,9 +126,9 @@ func (bp *blockProcessor) BuildBlock(coinbaseData *externalapi.DomainCoinbaseDat
|
||||
|
||||
// ValidateAndInsertBlock validates the given block and, if valid, applies it
|
||||
// to the current state
|
||||
func (bp *blockProcessor) ValidateAndInsertBlock(block *externalapi.DomainBlock) error {
|
||||
func (bp *blockProcessor) ValidateAndInsertBlock(block *externalapi.DomainBlock, headerOnly bool) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "ValidateAndInsertBlock")
|
||||
defer onEnd()
|
||||
|
||||
return bp.validateAndInsertBlock(block)
|
||||
return bp.validateAndInsertBlock(block, headerOnly)
|
||||
}
|
||||
|
||||
@@ -26,12 +26,10 @@ func (bp *blockProcessor) buildBlock(coinbaseData *externalapi.DomainCoinbaseDat
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headerHash := hashserialization.HeaderHash(header)
|
||||
|
||||
return &externalapi.DomainBlock{
|
||||
Header: header,
|
||||
Transactions: transactionsWithCoinbase,
|
||||
Hash: headerHash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,50 @@ package blockprocessor
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashserialization"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock) error {
|
||||
err := bp.validateBlock(block)
|
||||
func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock, headerOnly bool) error {
|
||||
hash := hashserialization.HeaderHash(block.Header)
|
||||
if headerOnly && len(block.Transactions) != 0 {
|
||||
return errors.Errorf("block %s contains transactions while validating in header only mode", hash)
|
||||
}
|
||||
|
||||
err := bp.checkBlockStatus(hash, headerOnly)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bp.validateBlock(block, headerOnly)
|
||||
if err != nil {
|
||||
bp.discardAllChanges()
|
||||
return err
|
||||
}
|
||||
|
||||
hasHeader, err := bp.hasHeader(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !hasHeader {
|
||||
err = bp.reachabilityManager.AddBlock(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bp.headerTipsManager.AddHeaderTip(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if headerOnly {
|
||||
bp.blockStatusStore.Stage(hash, externalapi.StatusHeaderOnly)
|
||||
} else {
|
||||
bp.blockStatusStore.Stage(hash, externalapi.StatusUTXOPendingVerification)
|
||||
}
|
||||
|
||||
// Block validations passed, save whatever DAG data was
|
||||
// collected so far
|
||||
err = bp.commitAllChanges()
|
||||
@@ -20,8 +54,12 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock)
|
||||
return err
|
||||
}
|
||||
|
||||
if headerOnly {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Attempt to add the block to the virtual
|
||||
err = bp.consensusStateManager.AddBlockToVirtual(block.Hash)
|
||||
err = bp.consensusStateManager.AddBlockToVirtual(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -29,22 +67,55 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock)
|
||||
return bp.commitAllChanges()
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock) error {
|
||||
// If either in-isolation or proof-of-work validations fail, simply
|
||||
func (bp *blockProcessor) checkBlockStatus(hash *externalapi.DomainHash, headerOnly bool) error {
|
||||
exists, err := bp.blockStatusStore.Exists(bp.databaseContext, hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
status, err := bp.blockStatusStore.Get(bp.databaseContext, hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if status == externalapi.StatusInvalid {
|
||||
return errors.Wrapf(ruleerrors.ErrKnownInvalid, "block %s is a known invalid block", hash)
|
||||
}
|
||||
|
||||
if headerOnly || status != externalapi.StatusHeaderOnly {
|
||||
return errors.Wrapf(ruleerrors.ErrDuplicateBlock, "block %s already exists", hash)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock, headerOnly bool) error {
|
||||
// If any validation until (included) proof-of-work fails, simply
|
||||
// return an error without writing anything in the database.
|
||||
// This is to prevent spamming attacks.
|
||||
err := bp.validateBlockInIsolationAndProofOfWork(block)
|
||||
err := bp.validatePreProofOfWork(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockHash := hashserialization.HeaderHash(block.Header)
|
||||
err = bp.blockValidator.ValidateProofOfWorkAndDifficulty(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If in-context validations fail, discard all changes and store the
|
||||
// block with StatusInvalid.
|
||||
err = bp.validateInContext(block)
|
||||
err = bp.validatePostProofOfWork(block, headerOnly)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
bp.discardAllChanges()
|
||||
bp.blockStatusStore.Stage(block.Hash, externalapi.StatusInvalid)
|
||||
hash := hashserialization.HeaderHash(block.Header)
|
||||
bp.blockStatusStore.Stage(hash, externalapi.StatusInvalid)
|
||||
commitErr := bp.commitAllChanges()
|
||||
if commitErr != nil {
|
||||
return commitErr
|
||||
@@ -55,42 +126,75 @@ func (bp *blockProcessor) validateBlock(block *externalapi.DomainBlock) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validateBlockInIsolationAndProofOfWork(block *externalapi.DomainBlock) error {
|
||||
err := bp.blockValidator.ValidateHeaderInIsolation(block.Hash)
|
||||
func (bp *blockProcessor) validatePreProofOfWork(block *externalapi.DomainBlock) error {
|
||||
blockHash := hashserialization.HeaderHash(block.Header)
|
||||
|
||||
hasHeader, err := bp.hasHeader(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = bp.blockValidator.ValidateBodyInIsolation(block.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
if hasHeader {
|
||||
return nil
|
||||
}
|
||||
err = bp.blockValidator.ValidateProofOfWorkAndDifficulty(block.Hash)
|
||||
|
||||
err = bp.blockValidator.ValidateHeaderInIsolation(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validateInContext(block *externalapi.DomainBlock) error {
|
||||
err := bp.dagTopologyManager.SetParents(block.Hash, block.Header.ParentHashes)
|
||||
func (bp *blockProcessor) validatePostProofOfWork(block *externalapi.DomainBlock, headerOnly bool) error {
|
||||
blockHash := hashserialization.HeaderHash(block.Header)
|
||||
|
||||
if !headerOnly {
|
||||
bp.blockStore.Stage(blockHash, block)
|
||||
err := bp.blockValidator.ValidateBodyInIsolation(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
hasHeader, err := bp.hasHeader(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bp.blockStore.Stage(block.Hash, block)
|
||||
bp.blockHeaderStore.Stage(block.Hash, block.Header)
|
||||
if !hasHeader {
|
||||
bp.blockHeaderStore.Stage(blockHash, block.Header)
|
||||
|
||||
err = bp.blockValidator.ValidateHeaderInContext(block.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = bp.blockValidator.ValidateBodyInContext(block.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
err := bp.dagTopologyManager.SetParents(blockHash, block.Header.ParentHashes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = bp.blockValidator.ValidateHeaderInContext(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) hasHeader(blockHash *externalapi.DomainHash) (bool, error) {
|
||||
exists, err := bp.blockStatusStore.Exists(bp.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
status, err := bp.blockStatusStore.Get(bp.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return status == externalapi.StatusHeaderOnly, nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) discardAllChanges() {
|
||||
for _, store := range bp.stores {
|
||||
store.Discard()
|
||||
|
||||
@@ -26,11 +26,18 @@ func (v *blockValidator) ValidateHeaderInContext(blockHash *externalapi.DomainHa
|
||||
return err
|
||||
}
|
||||
|
||||
err = v.ghostdagManager.GHOSTDAG(blockHash)
|
||||
status, err := v.blockStatusStore.Get(v.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if status != externalapi.StatusHeaderOnly {
|
||||
err = v.ghostdagManager.GHOSTDAG(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = v.checkMergeSizeLimit(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -31,6 +31,7 @@ type blockValidator struct {
|
||||
blockStore model.BlockStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
}
|
||||
|
||||
// New instantiates a new BlockValidator
|
||||
@@ -52,7 +53,8 @@ func New(powMax *big.Int,
|
||||
|
||||
blockStore model.BlockStore,
|
||||
ghostdagDataStore model.GHOSTDAGDataStore,
|
||||
blockHeaderStore model.BlockHeaderStore) model.BlockValidator {
|
||||
blockHeaderStore model.BlockHeaderStore,
|
||||
blockStatusStore model.BlockStatusStore) model.BlockValidator {
|
||||
|
||||
return &blockValidator{
|
||||
powMax: powMax,
|
||||
@@ -73,5 +75,6 @@ func New(powMax *big.Int,
|
||||
blockStore: blockStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@ func (v *blockValidator) ValidateProofOfWorkAndDifficulty(blockHash *externalapi
|
||||
return err
|
||||
}
|
||||
|
||||
err = v.checkParentsExist(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = v.validateDifficulty(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -86,7 +91,7 @@ func (v *blockValidator) checkProofOfWork(header *externalapi.DomainBlockHeader)
|
||||
|
||||
func (v *blockValidator) checkParentsExist(header *externalapi.DomainBlockHeader) error {
|
||||
for _, parent := range header.ParentHashes {
|
||||
exists, err := v.blockStore.HasBlock(v.databaseContext, parent)
|
||||
exists, err := v.blockHeaderStore.HasBlockHeader(v.databaseContext, parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package headertipsmanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
type headerTipsManager struct {
|
||||
databaseContext model.DBReader
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
headerTipsStore model.HeaderTipsStore
|
||||
}
|
||||
|
||||
// New instantiates a new HeaderTipsManager
|
||||
func New(databaseContext model.DBReader,
|
||||
dagTopologyManager model.DAGTopologyManager,
|
||||
headerTipsStore model.HeaderTipsStore) model.HeaderTipsManager {
|
||||
return &headerTipsManager{
|
||||
databaseContext: databaseContext,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
headerTipsStore: headerTipsStore,
|
||||
}
|
||||
}
|
||||
|
||||
func (h headerTipsManager) AddHeaderTip(hash *externalapi.DomainHash) error {
|
||||
tips, err := h.headerTipsStore.Tips(h.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newTips := make([]*externalapi.DomainHash, 0, len(tips)+1)
|
||||
for _, tip := range tips {
|
||||
isAncestorOf, err := h.dagTopologyManager.IsAncestorOf(tip, hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isAncestorOf {
|
||||
newTips = append(newTips, tip)
|
||||
}
|
||||
}
|
||||
|
||||
newTips = append(newTips, hash)
|
||||
h.headerTipsStore.Stage(newTips)
|
||||
return nil
|
||||
}
|
||||
@@ -227,6 +227,8 @@ var (
|
||||
|
||||
// ErrMissingParent indicates one of the block parents is not found.
|
||||
ErrMissingParent = newRuleError("ErrMissingParent")
|
||||
|
||||
ErrKnownInvalid = newRuleError("ErrKnownInvalid")
|
||||
)
|
||||
|
||||
// RuleError identifies a rule violation. It is used to indicate that
|
||||
|
||||
Reference in New Issue
Block a user