mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-02-22 19:45:36 +00:00
Compare commits
91 Commits
v0.8.0-dev
...
v0.8.1-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16e434aa91 | ||
|
|
0e91b44fc6 | ||
|
|
f7fa823f17 | ||
|
|
546ea83123 | ||
|
|
f9c2137344 | ||
|
|
0fa13357c3 | ||
|
|
5b2fae0457 | ||
|
|
3bad9ec1eb | ||
|
|
45d9b63572 | ||
|
|
afc634d871 | ||
|
|
2334f8b4eb | ||
|
|
d65f382c80 | ||
|
|
2096a28d1c | ||
|
|
96d9e5800f | ||
|
|
8264369c81 | ||
|
|
bb2d7f72ac | ||
|
|
c1505b4748 | ||
|
|
dec9ef5f75 | ||
|
|
5211727206 | ||
|
|
fafe1d534f | ||
|
|
c56a5336f3 | ||
|
|
b3a3121725 | ||
|
|
950dd0cc8d | ||
|
|
bb244706ea | ||
|
|
ed386bbc8f | ||
|
|
75d21d39cc | ||
|
|
3f92ddd827 | ||
|
|
8500acd86b | ||
|
|
5b037950d8 | ||
|
|
184911f76e | ||
|
|
7479f5f5e8 | ||
|
|
891095563e | ||
|
|
60c24d8dea | ||
|
|
d4993c1d06 | ||
|
|
c785ca0e52 | ||
|
|
9eb5c4a0ed | ||
|
|
9d5d1b02dc | ||
|
|
213be67c47 | ||
|
|
14d7ab5fc6 | ||
|
|
7224d58940 | ||
|
|
b2188f5993 | ||
|
|
dbd15aecf5 | ||
|
|
66f5a5bd7d | ||
|
|
c994200878 | ||
|
|
7050ebeac9 | ||
|
|
f2df48139f | ||
|
|
48d8137604 | ||
|
|
310cf0bb9b | ||
|
|
b6c47fdd21 | ||
|
|
e6a2b7366f | ||
|
|
d8f72e2b27 | ||
|
|
08749deaeb | ||
|
|
83a88d9989 | ||
|
|
151910c27a | ||
|
|
fca8ed57bd | ||
|
|
56679818be | ||
|
|
f07f2edad2 | ||
|
|
2dcfe90850 | ||
|
|
dc80a39c54 | ||
|
|
34be898491 | ||
|
|
f4a2fbf64f | ||
|
|
a0c6076ccc | ||
|
|
fddce00d08 | ||
|
|
ae682d59f7 | ||
|
|
347f3de15c | ||
|
|
a34091991a | ||
|
|
efe1986a56 | ||
|
|
3ab507b66f | ||
|
|
afbad73c0b | ||
|
|
a1fa17d872 | ||
|
|
b50421beee | ||
|
|
aeded07815 | ||
|
|
7d14f24b84 | ||
|
|
c52b8100c6 | ||
|
|
f52cddc25c | ||
|
|
fc5e39f6cc | ||
|
|
8ccf381fc7 | ||
|
|
f320887bff | ||
|
|
eef5e3768c | ||
|
|
7a7821e1c8 | ||
|
|
37fbdcb453 | ||
|
|
135ffbd4f2 | ||
|
|
4736213ba4 | ||
|
|
8290fadd3a | ||
|
|
23c1ea6c31 | ||
|
|
31c5264430 | ||
|
|
32da4440ba | ||
|
|
e7a61c7edf | ||
|
|
6db337c8c5 | ||
|
|
2282e36196 | ||
|
|
72b5832f30 |
@@ -46,7 +46,7 @@ func StartApp() error {
|
||||
// initializes logging and configures it accordingly.
|
||||
cfg, err := config.LoadConfig()
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return err
|
||||
}
|
||||
defer panics.HandlePanic(log, "MAIN", nil)
|
||||
|
||||
@@ -17,9 +17,9 @@ func DomainBlockToMsgBlock(domainBlock *externalapi.DomainBlock) *MsgBlock {
|
||||
}
|
||||
}
|
||||
|
||||
// DomainBlockHeaderToBlockHeader converts an externalapi.DomainBlockHeader to BlockHeader
|
||||
func DomainBlockHeaderToBlockHeader(domainBlockHeader *externalapi.DomainBlockHeader) *BlockHeader {
|
||||
return &BlockHeader{
|
||||
// DomainBlockHeaderToBlockHeader converts an externalapi.DomainBlockHeader to MsgBlockHeader
|
||||
func DomainBlockHeaderToBlockHeader(domainBlockHeader *externalapi.DomainBlockHeader) *MsgBlockHeader {
|
||||
return &MsgBlockHeader{
|
||||
Version: domainBlockHeader.Version,
|
||||
ParentHashes: domainBlockHeader.ParentHashes,
|
||||
HashMerkleRoot: &domainBlockHeader.HashMerkleRoot,
|
||||
@@ -44,8 +44,8 @@ func MsgBlockToDomainBlock(msgBlock *MsgBlock) *externalapi.DomainBlock {
|
||||
}
|
||||
}
|
||||
|
||||
// BlockHeaderToDomainBlockHeader converts a BlockHeader to externalapi.DomainBlockHeader
|
||||
func BlockHeaderToDomainBlockHeader(blockHeader *BlockHeader) *externalapi.DomainBlockHeader {
|
||||
// BlockHeaderToDomainBlockHeader converts a MsgBlockHeader to externalapi.DomainBlockHeader
|
||||
func BlockHeaderToDomainBlockHeader(blockHeader *MsgBlockHeader) *externalapi.DomainBlockHeader {
|
||||
return &externalapi.DomainBlockHeader{
|
||||
Version: blockHeader.Version,
|
||||
ParentHashes: blockHeader.ParentHashes,
|
||||
@@ -77,7 +77,7 @@ func DomainTransactionToMsgTx(domainTransaction *externalapi.DomainTransaction)
|
||||
LockTime: domainTransaction.LockTime,
|
||||
SubnetworkID: domainTransaction.SubnetworkID,
|
||||
Gas: domainTransaction.Gas,
|
||||
PayloadHash: &domainTransaction.PayloadHash,
|
||||
PayloadHash: domainTransaction.PayloadHash,
|
||||
Payload: domainTransaction.Payload,
|
||||
}
|
||||
}
|
||||
@@ -114,6 +114,12 @@ func MsgTxToDomainTransaction(msgTx *MsgTx) *externalapi.DomainTransaction {
|
||||
for _, txOut := range msgTx.TxOut {
|
||||
transactionOutputs = append(transactionOutputs, txOutToDomainTransactionOutput(txOut))
|
||||
}
|
||||
|
||||
payload := make([]byte, 0)
|
||||
if msgTx.Payload != nil {
|
||||
payload = msgTx.Payload
|
||||
}
|
||||
|
||||
return &externalapi.DomainTransaction{
|
||||
Version: msgTx.Version,
|
||||
Inputs: transactionInputs,
|
||||
@@ -121,8 +127,8 @@ func MsgTxToDomainTransaction(msgTx *MsgTx) *externalapi.DomainTransaction {
|
||||
LockTime: msgTx.LockTime,
|
||||
SubnetworkID: msgTx.SubnetworkID,
|
||||
Gas: msgTx.Gas,
|
||||
PayloadHash: *msgTx.PayloadHash,
|
||||
Payload: msgTx.Payload,
|
||||
PayloadHash: msgTx.PayloadHash,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ const (
|
||||
CmdVerAck
|
||||
CmdRequestAddresses
|
||||
CmdAddresses
|
||||
CmdRequestIBDBlocks
|
||||
CmdRequestHeaders
|
||||
CmdBlock
|
||||
CmdTx
|
||||
CmdPing
|
||||
@@ -48,10 +48,15 @@ const (
|
||||
CmdInvTransaction
|
||||
CmdRequestTransactions
|
||||
CmdIBDBlock
|
||||
CmdRequestNextIBDBlocks
|
||||
CmdDoneIBDBlocks
|
||||
CmdDoneHeaders
|
||||
CmdTransactionNotFound
|
||||
CmdReject
|
||||
CmdHeader
|
||||
CmdRequestNextHeaders
|
||||
CmdRequestIBDRootUTXOSetAndBlock
|
||||
CmdIBDRootUTXOSetAndBlock
|
||||
CmdRequestIBDBlocks
|
||||
CmdIBDRootNotFound
|
||||
|
||||
// rpc
|
||||
CmdGetCurrentNetworkRequestMessage
|
||||
@@ -107,28 +112,33 @@ const (
|
||||
|
||||
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
|
||||
var ProtocolMessageCommandToString = map[MessageCommand]string{
|
||||
CmdVersion: "Version",
|
||||
CmdVerAck: "VerAck",
|
||||
CmdRequestAddresses: "RequestAddresses",
|
||||
CmdAddresses: "Addresses",
|
||||
CmdRequestIBDBlocks: "RequestBlocks",
|
||||
CmdBlock: "Block",
|
||||
CmdTx: "Tx",
|
||||
CmdPing: "Ping",
|
||||
CmdPong: "Pong",
|
||||
CmdRequestBlockLocator: "RequestBlockLocator",
|
||||
CmdBlockLocator: "BlockLocator",
|
||||
CmdSelectedTip: "SelectedTip",
|
||||
CmdRequestSelectedTip: "RequestSelectedTip",
|
||||
CmdInvRelayBlock: "InvRelayBlock",
|
||||
CmdRequestRelayBlocks: "RequestRelayBlocks",
|
||||
CmdInvTransaction: "InvTransaction",
|
||||
CmdRequestTransactions: "RequestTransactions",
|
||||
CmdIBDBlock: "IBDBlock",
|
||||
CmdRequestNextIBDBlocks: "RequestNextIBDBlocks",
|
||||
CmdDoneIBDBlocks: "DoneIBDBlocks",
|
||||
CmdTransactionNotFound: "TransactionNotFound",
|
||||
CmdReject: "Reject",
|
||||
CmdVersion: "Version",
|
||||
CmdVerAck: "VerAck",
|
||||
CmdRequestAddresses: "RequestAddresses",
|
||||
CmdAddresses: "Addresses",
|
||||
CmdRequestHeaders: "RequestHeaders",
|
||||
CmdBlock: "Block",
|
||||
CmdTx: "Tx",
|
||||
CmdPing: "Ping",
|
||||
CmdPong: "Pong",
|
||||
CmdRequestBlockLocator: "RequestBlockLocator",
|
||||
CmdBlockLocator: "BlockLocator",
|
||||
CmdSelectedTip: "SelectedTip",
|
||||
CmdRequestSelectedTip: "RequestSelectedTip",
|
||||
CmdInvRelayBlock: "InvRelayBlock",
|
||||
CmdRequestRelayBlocks: "RequestRelayBlocks",
|
||||
CmdInvTransaction: "InvTransaction",
|
||||
CmdRequestTransactions: "RequestTransactions",
|
||||
CmdIBDBlock: "IBDBlock",
|
||||
CmdDoneHeaders: "DoneHeaders",
|
||||
CmdTransactionNotFound: "TransactionNotFound",
|
||||
CmdReject: "Reject",
|
||||
CmdHeader: "Header",
|
||||
CmdRequestNextHeaders: "RequestNextHeaders",
|
||||
CmdRequestIBDRootUTXOSetAndBlock: "RequestPruningUTXOSetAndBlock",
|
||||
CmdIBDRootUTXOSetAndBlock: "IBDRootUTXOSetAndBlock",
|
||||
CmdRequestIBDBlocks: "RequestIBDBlocks",
|
||||
CmdIBDRootNotFound: "IBDRootNotFound",
|
||||
}
|
||||
|
||||
// RPCMessageCommandToString maps all MessageCommands to their string representation
|
||||
|
||||
22
app/appmessage/p2p_ibdrootnotfound.go
Normal file
22
app/appmessage/p2p_ibdrootnotfound.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package appmessage
|
||||
|
||||
// MsgIBDRootNotFound implements the Message interface and represents a kaspa
|
||||
// IBDRootNotFound message. It is used to notify the IBD root that was requested
|
||||
// by other peer was not found.
|
||||
//
|
||||
// This message has no payload.
|
||||
type MsgIBDRootNotFound struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgIBDRootNotFound) Command() MessageCommand {
|
||||
return CmdIBDRootNotFound
|
||||
}
|
||||
|
||||
// NewMsgIBDRootNotFound returns a new kaspa IBDRootNotFound message that conforms to the
|
||||
// Message interface.
|
||||
func NewMsgIBDRootNotFound() *MsgDoneHeaders {
|
||||
return &MsgDoneHeaders{}
|
||||
}
|
||||
@@ -4,59 +4,15 @@
|
||||
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MaxAddressesPerMsg is the maximum number of addresses that can be in a single
|
||||
// kaspa Addresses message (MsgAddresses).
|
||||
const MaxAddressesPerMsg = 1000
|
||||
|
||||
// MsgAddresses implements the Message interface and represents a kaspa
|
||||
// Addresses message. It is used to provide a list of known active peers on the
|
||||
// network. An active peer is considered one that has transmitted a message
|
||||
// within the last 3 hours. Nodes which have not transmitted in that time
|
||||
// frame should be forgotten. Each message is limited to a maximum number of
|
||||
// addresses, which is currently 1000. As a result, multiple messages must
|
||||
// be used to relay the full list.
|
||||
//
|
||||
// Use the AddAddress function to build up the list of known addresses when
|
||||
// sending an Addresses message to another peer.
|
||||
// Addresses message.
|
||||
type MsgAddresses struct {
|
||||
baseMessage
|
||||
IncludeAllSubnetworks bool
|
||||
SubnetworkID *externalapi.DomainSubnetworkID
|
||||
AddrList []*NetAddress
|
||||
}
|
||||
|
||||
// AddAddress adds a known active peer to the message.
|
||||
func (msg *MsgAddresses) AddAddress(na *NetAddress) error {
|
||||
if len(msg.AddrList)+1 > MaxAddressesPerMsg {
|
||||
str := fmt.Sprintf("too many addresses in message [max %d]",
|
||||
MaxAddressesPerMsg)
|
||||
return messageError("MsgAddresses.AddAddress", str)
|
||||
}
|
||||
|
||||
msg.AddrList = append(msg.AddrList, na)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddAddresses adds multiple known active peers to the message.
|
||||
func (msg *MsgAddresses) AddAddresses(netAddrs ...*NetAddress) error {
|
||||
for _, na := range netAddrs {
|
||||
err := msg.AddAddress(na)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearAddresses removes all addresses from the message.
|
||||
func (msg *MsgAddresses) ClearAddresses() {
|
||||
msg.AddrList = []*NetAddress{}
|
||||
AddressList []*NetAddress
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
@@ -67,10 +23,8 @@ func (msg *MsgAddresses) Command() MessageCommand {
|
||||
|
||||
// NewMsgAddresses returns a new kaspa Addresses message that conforms to the
|
||||
// Message interface. See MsgAddresses for details.
|
||||
func NewMsgAddresses(includeAllSubnetworks bool, subnetworkID *externalapi.DomainSubnetworkID) *MsgAddresses {
|
||||
func NewMsgAddresses(addressList []*NetAddress) *MsgAddresses {
|
||||
return &MsgAddresses{
|
||||
IncludeAllSubnetworks: includeAllSubnetworks,
|
||||
SubnetworkID: subnetworkID,
|
||||
AddrList: make([]*NetAddress, 0, MaxAddressesPerMsg),
|
||||
AddressList: addressList,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// TestAddresses tests the MsgAddresses API.
|
||||
func TestAddresses(t *testing.T) {
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := MessageCommand(3)
|
||||
msg := NewMsgAddresses(false, nil)
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgAddresses: wrong command - got %v want %v",
|
||||
cmd, wantCmd)
|
||||
}
|
||||
|
||||
// Ensure NetAddresses are added properly.
|
||||
tcpAddr := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 16111}
|
||||
na := NewNetAddress(tcpAddr, SFNodeNetwork)
|
||||
err := msg.AddAddress(na)
|
||||
if err != nil {
|
||||
t.Errorf("AddAddress: %v", err)
|
||||
}
|
||||
if msg.AddrList[0] != na {
|
||||
t.Errorf("AddAddress: wrong address added - got %v, want %v",
|
||||
spew.Sprint(msg.AddrList[0]), spew.Sprint(na))
|
||||
}
|
||||
|
||||
// Ensure the address list is cleared properly.
|
||||
msg.ClearAddresses()
|
||||
if len(msg.AddrList) != 0 {
|
||||
t.Errorf("ClearAddresses: address list is not empty - "+
|
||||
"got %v [%v], want %v", len(msg.AddrList),
|
||||
spew.Sprint(msg.AddrList[0]), 0)
|
||||
}
|
||||
|
||||
// Ensure adding more than the max allowed addresses per message returns
|
||||
// error.
|
||||
for i := 0; i < MaxAddressesPerMsg+1; i++ {
|
||||
err = msg.AddAddress(na)
|
||||
}
|
||||
if err == nil {
|
||||
t.Errorf("AddAddress: expected error on too many addresses " +
|
||||
"not received")
|
||||
}
|
||||
err = msg.AddAddresses(na)
|
||||
if err == nil {
|
||||
t.Errorf("AddAddresses: expected error on too many addresses " +
|
||||
"not received")
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ type TxLoc struct {
|
||||
// response to a getdata message (MsgGetData) for a given block hash.
|
||||
type MsgBlock struct {
|
||||
baseMessage
|
||||
Header BlockHeader
|
||||
Header MsgBlockHeader
|
||||
Transactions []*MsgTx
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ func (msg *MsgBlock) ConvertToPartial(subnetworkID *externalapi.DomainSubnetwork
|
||||
|
||||
// NewMsgBlock returns a new kaspa block message that conforms to the
|
||||
// Message interface. See MsgBlock for details.
|
||||
func NewMsgBlock(blockHeader *BlockHeader) *MsgBlock {
|
||||
func NewMsgBlock(blockHeader *MsgBlockHeader) *MsgBlock {
|
||||
return &MsgBlock{
|
||||
Header: *blockHeader,
|
||||
Transactions: make([]*MsgTx, 0, defaultTransactionAlloc),
|
||||
|
||||
@@ -129,7 +129,7 @@ func TestConvertToPartial(t *testing.T) {
|
||||
|
||||
// blockOne is the first block in the mainnet block DAG.
|
||||
var blockOne = MsgBlock{
|
||||
Header: BlockHeader{
|
||||
Header: MsgBlockHeader{
|
||||
Version: 1,
|
||||
ParentHashes: []*externalapi.DomainHash{mainnetGenesisHash, simnetGenesisHash},
|
||||
HashMerkleRoot: mainnetGenesisMerkleRoot,
|
||||
|
||||
@@ -31,9 +31,11 @@ const MaxNumParentBlocks = 255
|
||||
// BaseBlockHeaderPayload + up to MaxNumParentBlocks hashes of parent blocks
|
||||
const MaxBlockHeaderPayload = BaseBlockHeaderPayload + (MaxNumParentBlocks * externalapi.DomainHashSize)
|
||||
|
||||
// BlockHeader defines information about a block and is used in the kaspa
|
||||
// MsgBlockHeader defines information about a block and is used in the kaspa
|
||||
// block (MsgBlock) and headers (MsgHeader) messages.
|
||||
type BlockHeader struct {
|
||||
type MsgBlockHeader struct {
|
||||
baseMessage
|
||||
|
||||
// Version of the block. This is not the same as the protocol version.
|
||||
Version int32
|
||||
|
||||
@@ -61,7 +63,7 @@ type BlockHeader struct {
|
||||
}
|
||||
|
||||
// NumParentBlocks return the number of entries in ParentHashes
|
||||
func (h *BlockHeader) NumParentBlocks() byte {
|
||||
func (h *MsgBlockHeader) NumParentBlocks() byte {
|
||||
numParents := len(h.ParentHashes)
|
||||
if numParents > math.MaxUint8 {
|
||||
panic(errors.Errorf("number of parents is %d, which is more than one byte can fit", numParents))
|
||||
@@ -70,24 +72,30 @@ func (h *BlockHeader) NumParentBlocks() byte {
|
||||
}
|
||||
|
||||
// BlockHash computes the block identifier hash for the given block header.
|
||||
func (h *BlockHeader) BlockHash() *externalapi.DomainHash {
|
||||
func (h *MsgBlockHeader) BlockHash() *externalapi.DomainHash {
|
||||
return consensusserialization.HeaderHash(BlockHeaderToDomainBlockHeader(h))
|
||||
}
|
||||
|
||||
// IsGenesis returns true iff this block is a genesis block
|
||||
func (h *BlockHeader) IsGenesis() bool {
|
||||
func (h *MsgBlockHeader) IsGenesis() bool {
|
||||
return h.NumParentBlocks() == 0
|
||||
}
|
||||
|
||||
// NewBlockHeader returns a new BlockHeader using the provided version, previous
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (h *MsgBlockHeader) Command() MessageCommand {
|
||||
return CmdHeader
|
||||
}
|
||||
|
||||
// NewBlockHeader returns a new MsgBlockHeader using the provided version, previous
|
||||
// block hash, hash merkle root, accepted ID merkle root, difficulty bits, and nonce used to generate the
|
||||
// block with defaults or calclulated values for the remaining fields.
|
||||
func NewBlockHeader(version int32, parentHashes []*externalapi.DomainHash, hashMerkleRoot *externalapi.DomainHash,
|
||||
acceptedIDMerkleRoot *externalapi.DomainHash, utxoCommitment *externalapi.DomainHash, bits uint32, nonce uint64) *BlockHeader {
|
||||
acceptedIDMerkleRoot *externalapi.DomainHash, utxoCommitment *externalapi.DomainHash, bits uint32, nonce uint64) *MsgBlockHeader {
|
||||
|
||||
// Limit the timestamp to one millisecond precision since the protocol
|
||||
// doesn't support better.
|
||||
return &BlockHeader{
|
||||
return &MsgBlockHeader{
|
||||
Version: version,
|
||||
ParentHashes: parentHashes,
|
||||
HashMerkleRoot: hashMerkleRoot,
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/random"
|
||||
)
|
||||
|
||||
// TestBlockHeader tests the BlockHeader API.
|
||||
// TestBlockHeader tests the MsgBlockHeader API.
|
||||
func TestBlockHeader(t *testing.T) {
|
||||
nonce, err := random.Uint64()
|
||||
if err != nil {
|
||||
@@ -52,7 +52,7 @@ func TestIsGenesis(t *testing.T) {
|
||||
bits := uint32(0x1d00ffff)
|
||||
timestamp := mstime.UnixMilliseconds(0x495fab29000)
|
||||
|
||||
baseBlockHdr := &BlockHeader{
|
||||
baseBlockHdr := &MsgBlockHeader{
|
||||
Version: 1,
|
||||
ParentHashes: []*externalapi.DomainHash{mainnetGenesisHash, simnetGenesisHash},
|
||||
HashMerkleRoot: mainnetGenesisMerkleRoot,
|
||||
@@ -60,7 +60,7 @@ func TestIsGenesis(t *testing.T) {
|
||||
Bits: bits,
|
||||
Nonce: nonce,
|
||||
}
|
||||
genesisBlockHdr := &BlockHeader{
|
||||
genesisBlockHdr := &MsgBlockHeader{
|
||||
Version: 1,
|
||||
ParentHashes: []*externalapi.DomainHash{},
|
||||
HashMerkleRoot: mainnetGenesisMerkleRoot,
|
||||
@@ -70,8 +70,8 @@ func TestIsGenesis(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
in *BlockHeader // Block header to encode
|
||||
isGenesis bool // Expected result for call of .IsGenesis
|
||||
in *MsgBlockHeader // Block header to encode
|
||||
isGenesis bool // Expected result for call of .IsGenesis
|
||||
}{
|
||||
{genesisBlockHdr, true},
|
||||
{baseBlockHdr, false},
|
||||
@@ -81,7 +81,7 @@ func TestIsGenesis(t *testing.T) {
|
||||
for i, test := range tests {
|
||||
isGenesis := test.in.IsGenesis()
|
||||
if isGenesis != test.isGenesis {
|
||||
t.Errorf("BlockHeader.IsGenesis: #%d got: %t, want: %t",
|
||||
t.Errorf("MsgBlockHeader.IsGenesis: #%d got: %t, want: %t",
|
||||
i, isGenesis, test.isGenesis)
|
||||
}
|
||||
}
|
||||
22
app/appmessage/p2p_msgdoneheaders.go
Normal file
22
app/appmessage/p2p_msgdoneheaders.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package appmessage
|
||||
|
||||
// MsgDoneHeaders implements the Message interface and represents a kaspa
|
||||
// DoneHeaders message. It is used to notify the IBD syncing peer that the
|
||||
// syncer sent all the requested headers.
|
||||
//
|
||||
// This message has no payload.
|
||||
type MsgDoneHeaders struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgDoneHeaders) Command() MessageCommand {
|
||||
return CmdDoneHeaders
|
||||
}
|
||||
|
||||
// NewMsgDoneHeaders returns a new kaspa DoneIBDBlocks message that conforms to the
|
||||
// Message interface.
|
||||
func NewMsgDoneHeaders() *MsgDoneHeaders {
|
||||
return &MsgDoneHeaders{}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
// MsgDoneIBDBlocks implements the Message interface and represents a kaspa
|
||||
// DoneIBDBlocks message. It is used to notify the IBD syncing peer that the
|
||||
// syncer sent all the requested blocks.
|
||||
//
|
||||
// This message has no payload.
|
||||
type MsgDoneIBDBlocks struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgDoneIBDBlocks) Command() MessageCommand {
|
||||
return CmdDoneIBDBlocks
|
||||
}
|
||||
|
||||
// NewMsgDoneIBDBlocks returns a new kaspa DoneIBDBlocks message that conforms to the
|
||||
// Message interface.
|
||||
func NewMsgDoneIBDBlocks() *MsgDoneIBDBlocks {
|
||||
return &MsgDoneIBDBlocks{}
|
||||
}
|
||||
23
app/appmessage/p2p_msgibdrootutxosetandblock.go
Normal file
23
app/appmessage/p2p_msgibdrootutxosetandblock.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package appmessage
|
||||
|
||||
// MsgIBDRootUTXOSetAndBlock implements the Message interface and represents a kaspa
|
||||
// IBDRootUTXOSetAndBlock message. It is used to answer RequestIBDRootUTXOSetAndBlock messages.
|
||||
type MsgIBDRootUTXOSetAndBlock struct {
|
||||
baseMessage
|
||||
UTXOSet []byte
|
||||
Block *MsgBlock
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgIBDRootUTXOSetAndBlock) Command() MessageCommand {
|
||||
return CmdIBDRootUTXOSetAndBlock
|
||||
}
|
||||
|
||||
// NewMsgIBDRootUTXOSetAndBlock returns a new MsgIBDRootUTXOSetAndBlock.
|
||||
func NewMsgIBDRootUTXOSetAndBlock(utxoSet []byte, block *MsgBlock) *MsgIBDRootUTXOSetAndBlock {
|
||||
return &MsgIBDRootUTXOSetAndBlock{
|
||||
UTXOSet: utxoSet,
|
||||
Block: block,
|
||||
}
|
||||
}
|
||||
34
app/appmessage/p2p_msgrequestheaders.go
Normal file
34
app/appmessage/p2p_msgrequestheaders.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MsgRequestHeaders implements the Message interface and represents a kaspa
|
||||
// RequestHeaders message. It is used to request a list of blocks starting after the
|
||||
// low hash and until the high hash.
|
||||
type MsgRequestHeaders struct {
|
||||
baseMessage
|
||||
LowHash *externalapi.DomainHash
|
||||
HighHash *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgRequestHeaders) Command() MessageCommand {
|
||||
return CmdRequestHeaders
|
||||
}
|
||||
|
||||
// NewMsgRequstHeaders returns a new kaspa RequestHeaders message that conforms to the
|
||||
// Message interface using the passed parameters and defaults for the remaining
|
||||
// fields.
|
||||
func NewMsgRequstHeaders(lowHash, highHash *externalapi.DomainHash) *MsgRequestHeaders {
|
||||
return &MsgRequestHeaders{
|
||||
LowHash: lowHash,
|
||||
HighHash: highHash,
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
)
|
||||
|
||||
// TestRequstIBDBlocks tests the MsgRequestIBDBlocks API.
|
||||
// TestRequstIBDBlocks tests the MsgRequestHeaders API.
|
||||
func TestRequstIBDBlocks(t *testing.T) {
|
||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||
lowHash, err := hashes.FromString(hashStr)
|
||||
@@ -25,16 +25,16 @@ func TestRequstIBDBlocks(t *testing.T) {
|
||||
}
|
||||
|
||||
// Ensure we get the same data back out.
|
||||
msg := NewMsgRequstIBDBlocks(lowHash, highHash)
|
||||
msg := NewMsgRequstHeaders(lowHash, highHash)
|
||||
if *msg.HighHash != *highHash {
|
||||
t.Errorf("NewMsgRequstIBDBlocks: wrong high hash - got %v, want %v",
|
||||
t.Errorf("NewMsgRequstHeaders: wrong high hash - got %v, want %v",
|
||||
msg.HighHash, highHash)
|
||||
}
|
||||
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := MessageCommand(4)
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgRequstIBDBlocks: wrong command - got %v want %v",
|
||||
t.Errorf("NewMsgRequstHeaders: wrong command - got %v want %v",
|
||||
cmd, wantCmd)
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,19 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MaxRequestIBDBlocksHashes is the maximum number of hashes that can
|
||||
// be in a single RequestIBDBlocks message.
|
||||
const MaxRequestIBDBlocksHashes = MaxInvPerMsg
|
||||
|
||||
// MsgRequestIBDBlocks implements the Message interface and represents a kaspa
|
||||
// RequestIBDBlocks message. It is used to request a list of blocks starting after the
|
||||
// low hash and until the high hash.
|
||||
// RequestIBDBlocks message. It is used to request blocks as part of the IBD
|
||||
// protocol.
|
||||
type MsgRequestIBDBlocks struct {
|
||||
baseMessage
|
||||
LowHash *externalapi.DomainHash
|
||||
HighHash *externalapi.DomainHash
|
||||
Hashes []*externalapi.DomainHash
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
@@ -23,12 +22,9 @@ func (msg *MsgRequestIBDBlocks) Command() MessageCommand {
|
||||
return CmdRequestIBDBlocks
|
||||
}
|
||||
|
||||
// NewMsgRequstIBDBlocks returns a new kaspa RequestIBDBlocks message that conforms to the
|
||||
// Message interface using the passed parameters and defaults for the remaining
|
||||
// fields.
|
||||
func NewMsgRequstIBDBlocks(lowHash, highHash *externalapi.DomainHash) *MsgRequestIBDBlocks {
|
||||
// NewMsgRequestIBDBlocks returns a new MsgRequestIBDBlocks.
|
||||
func NewMsgRequestIBDBlocks(hashes []*externalapi.DomainHash) *MsgRequestIBDBlocks {
|
||||
return &MsgRequestIBDBlocks{
|
||||
LowHash: lowHash,
|
||||
HighHash: highHash,
|
||||
Hashes: hashes,
|
||||
}
|
||||
}
|
||||
|
||||
26
app/appmessage/p2p_msgrequestibdrootutxosetandblock.go
Normal file
26
app/appmessage/p2p_msgrequestibdrootutxosetandblock.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MsgRequestIBDRootUTXOSetAndBlock implements the Message interface and represents a kaspa
|
||||
// RequestIBDRootUTXOSetAndBlock message. It is used to request the UTXO set and block body
|
||||
// of the IBD root block.
|
||||
type MsgRequestIBDRootUTXOSetAndBlock struct {
|
||||
baseMessage
|
||||
IBDRoot *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgRequestIBDRootUTXOSetAndBlock) Command() MessageCommand {
|
||||
return CmdRequestIBDRootUTXOSetAndBlock
|
||||
}
|
||||
|
||||
// NewMsgRequestIBDRootUTXOSetAndBlock returns a new MsgRequestIBDRootUTXOSetAndBlock.
|
||||
func NewMsgRequestIBDRootUTXOSetAndBlock(ibdRoot *externalapi.DomainHash) *MsgRequestIBDRootUTXOSetAndBlock {
|
||||
return &MsgRequestIBDRootUTXOSetAndBlock{
|
||||
IBDRoot: ibdRoot,
|
||||
}
|
||||
}
|
||||
22
app/appmessage/p2p_msgrequestnextheaders.go
Normal file
22
app/appmessage/p2p_msgrequestnextheaders.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package appmessage
|
||||
|
||||
// MsgRequestNextHeaders implements the Message interface and represents a kaspa
|
||||
// RequestNextHeaders message. It is used to notify the IBD syncer peer to send
|
||||
// more headers.
|
||||
//
|
||||
// This message has no payload.
|
||||
type MsgRequestNextHeaders struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgRequestNextHeaders) Command() MessageCommand {
|
||||
return CmdRequestNextHeaders
|
||||
}
|
||||
|
||||
// NewMsgRequestNextHeaders returns a new kaspa RequestNextHeaders message that conforms to the
|
||||
// Message interface.
|
||||
func NewMsgRequestNextHeaders() *MsgRequestNextHeaders {
|
||||
return &MsgRequestNextHeaders{}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
// MsgRequestNextIBDBlocks implements the Message interface and represents a kaspa
|
||||
// RequestNextIBDBlocks message. It is used to notify the IBD syncer peer to send
|
||||
// more blocks.
|
||||
//
|
||||
// This message has no payload.
|
||||
type MsgRequestNextIBDBlocks struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgRequestNextIBDBlocks) Command() MessageCommand {
|
||||
return CmdRequestNextIBDBlocks
|
||||
}
|
||||
|
||||
// NewMsgRequestNextIBDBlocks returns a new kaspa RequestNextIBDBlocks message that conforms to the
|
||||
// Message interface.
|
||||
func NewMsgRequestNextIBDBlocks() *MsgRequestNextIBDBlocks {
|
||||
return &MsgRequestNextIBDBlocks{}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MsgRequestRelayBlocksHashes is the maximum number of hashes that can
|
||||
// MaxRequestRelayBlocksHashes is the maximum number of hashes that can
|
||||
// be in a single RequestRelayBlocks message.
|
||||
const MsgRequestRelayBlocksHashes = MaxInvPerMsg
|
||||
const MaxRequestRelayBlocksHashes = MaxInvPerMsg
|
||||
|
||||
// MsgRequestRelayBlocks implements the Message interface and represents a kaspa
|
||||
// RequestRelayBlocks message. It is used to request blocks as part of the block
|
||||
|
||||
@@ -6,9 +6,10 @@ package appmessage
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
@@ -19,38 +20,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// TxVersion is the current latest supported transaction version.
|
||||
TxVersion = 1
|
||||
|
||||
// MaxTxInSequenceNum is the maximum sequence number the sequence field
|
||||
// of a transaction input can be.
|
||||
MaxTxInSequenceNum uint64 = math.MaxUint64
|
||||
|
||||
// MaxPrevOutIndex is the maximum index the index field of a previous
|
||||
// outpoint can be.
|
||||
MaxPrevOutIndex uint32 = 0xffffffff
|
||||
|
||||
// SequenceLockTimeDisabled is a flag that if set on a transaction
|
||||
// input's sequence number, the sequence number will not be interpreted
|
||||
// as a relative locktime.
|
||||
SequenceLockTimeDisabled = 1 << 31
|
||||
|
||||
// SequenceLockTimeIsSeconds is a flag that if set on a transaction
|
||||
// input's sequence number, the relative locktime has units of 512
|
||||
// seconds.
|
||||
SequenceLockTimeIsSeconds = 1 << 22
|
||||
|
||||
// SequenceLockTimeMask is a mask that extracts the relative locktime
|
||||
// when masked against the transaction input sequence number.
|
||||
SequenceLockTimeMask = 0x0000ffff
|
||||
|
||||
// SequenceLockTimeGranularity is the defined time based granularity
|
||||
// for milliseconds-based relative time locks. When converting from milliseconds
|
||||
// to a sequence number, the value is right shifted by this amount,
|
||||
// therefore the granularity of relative time locks in 524288 or 2^19
|
||||
// seconds. Enforced relative lock times are multiples of 524288 milliseconds.
|
||||
SequenceLockTimeGranularity = 19
|
||||
|
||||
// defaultTxInOutAlloc is the default size used for the backing array for
|
||||
// transaction inputs and outputs. The array will dynamically grow as needed,
|
||||
// but this figure is intended to provide enough space for the number of
|
||||
@@ -130,7 +103,7 @@ func NewTxIn(prevOut *Outpoint, signatureScript []byte) *TxIn {
|
||||
return &TxIn{
|
||||
PreviousOutpoint: *prevOut,
|
||||
SignatureScript: signatureScript,
|
||||
Sequence: MaxTxInSequenceNum,
|
||||
Sequence: constants.MaxTxInSequenceNum,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +136,7 @@ type MsgTx struct {
|
||||
LockTime uint64
|
||||
SubnetworkID externalapi.DomainSubnetworkID
|
||||
Gas uint64
|
||||
PayloadHash *externalapi.DomainHash
|
||||
PayloadHash externalapi.DomainHash
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
@@ -310,9 +283,9 @@ func newMsgTx(version int32, txIn []*TxIn, txOut []*TxOut, subnetworkID *externa
|
||||
txOut = make([]*TxOut, 0, defaultTxInOutAlloc)
|
||||
}
|
||||
|
||||
var payloadHash *externalapi.DomainHash
|
||||
var payloadHash externalapi.DomainHash
|
||||
if *subnetworkID != subnetworks.SubnetworkIDNative {
|
||||
payloadHash = hashes.HashData(payload)
|
||||
payloadHash = *hashes.HashData(payload)
|
||||
}
|
||||
|
||||
return &MsgTx{
|
||||
|
||||
@@ -58,6 +58,7 @@ type BlockVerboseData struct {
|
||||
Difficulty float64
|
||||
ParentHashes []string
|
||||
SelectedParentHash string
|
||||
BlueScore uint64
|
||||
}
|
||||
|
||||
// TransactionVerboseData holds verbose data about a transaction
|
||||
|
||||
@@ -20,7 +20,8 @@ func NewGetPeerAddressesRequestMessage() *GetPeerAddressesRequestMessage {
|
||||
// its respective RPC message
|
||||
type GetPeerAddressesResponseMessage struct {
|
||||
baseMessage
|
||||
Addresses []*GetPeerAddressesKnownAddressMessage
|
||||
Addresses []*GetPeerAddressesKnownAddressMessage
|
||||
BannedAddresses []*GetPeerAddressesKnownAddressMessage
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
@@ -31,9 +32,10 @@ func (msg *GetPeerAddressesResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetPeerAddressesResponseMessage returns a instance of the message
|
||||
func NewGetPeerAddressesResponseMessage(addresses []*GetPeerAddressesKnownAddressMessage) *GetPeerAddressesResponseMessage {
|
||||
func NewGetPeerAddressesResponseMessage(addresses []*GetPeerAddressesKnownAddressMessage, bannedAddresses []*GetPeerAddressesKnownAddressMessage) *GetPeerAddressesResponseMessage {
|
||||
return &GetPeerAddressesResponseMessage{
|
||||
Addresses: addresses,
|
||||
Addresses: addresses,
|
||||
BannedAddresses: bannedAddresses,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,11 +70,6 @@ func (a *ComponentManager) Stop() {
|
||||
log.Errorf("Error stopping the net adapter: %+v", err)
|
||||
}
|
||||
|
||||
err = a.addressManager.Stop()
|
||||
if err != nil {
|
||||
log.Errorf("Error stopping address manager: %s", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -92,10 +87,12 @@ func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addressManager, err := addressmanager.New(cfg, db)
|
||||
|
||||
addressManager, err := addressmanager.New(addressmanager.NewConfig(cfg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
connectionManager, err := connmanager.New(cfg, netAdapter, addressManager)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -141,14 +138,14 @@ func (a *ComponentManager) maybeSeedFromDNS() {
|
||||
// Kaspad uses a lookup of the dns seeder here. Since seeder returns
|
||||
// IPs of nodes and not its own IP, we can not know real IP of
|
||||
// source. So we'll take first returned address as source.
|
||||
a.addressManager.AddAddresses(addresses, addresses[0], nil)
|
||||
a.addressManager.AddAddresses(addresses...)
|
||||
})
|
||||
}
|
||||
|
||||
if a.cfg.GRPCSeed != "" {
|
||||
dnsseed.SeedFromGRPC(a.cfg.NetParams(), a.cfg.GRPCSeed, appmessage.SFNodeNetwork, false, nil,
|
||||
func(addresses []*appmessage.NetAddress) {
|
||||
a.addressManager.AddAddresses(addresses, addresses[0], nil)
|
||||
a.addressManager.AddAddresses(addresses...)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ var (
|
||||
// LogBlock logs a new block blue score as an information message
|
||||
// to show progress to the user. In order to prevent spam, it limits logging to
|
||||
// one message every 10 seconds with duration and totals included.
|
||||
func LogBlock(block *externalapi.DomainBlock) error {
|
||||
func LogBlock(block *externalapi.DomainBlock) {
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
|
||||
@@ -32,7 +32,7 @@ func LogBlock(block *externalapi.DomainBlock) error {
|
||||
now := mstime.Now()
|
||||
duration := now.Sub(lastBlockLogTime)
|
||||
if duration < time.Second*10 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// Truncate the duration to 10s of milliseconds.
|
||||
@@ -55,5 +55,4 @@ func LogBlock(block *externalapi.DomainBlock) error {
|
||||
receivedLogBlocks = 0
|
||||
receivedLogTx = 0
|
||||
lastBlockLogTime = now
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package flowcontext
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/blocklogger"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/pkg/errors"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
@@ -14,12 +17,22 @@ import (
|
||||
// relays newly unorphaned transactions and possibly rebroadcast
|
||||
// manually added transactions when not in IBD.
|
||||
func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock) error {
|
||||
f.Domain().MiningManager().HandleNewBlockTransactions(block.Transactions)
|
||||
unorphanedBlocks, err := f.UnorphanBlocks(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.onBlockAddedToDAGHandler != nil {
|
||||
err := f.onBlockAddedToDAGHandler(block)
|
||||
if err != nil {
|
||||
return err
|
||||
newBlocks := append([]*externalapi.DomainBlock{block}, unorphanedBlocks...)
|
||||
for _, newBlock := range newBlocks {
|
||||
blocklogger.LogBlock(block)
|
||||
|
||||
_ = f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions)
|
||||
|
||||
if f.onBlockAddedToDAGHandler != nil {
|
||||
err := f.onBlockAddedToDAGHandler(newBlock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +83,10 @@ func (f *FlowContext) SharedRequestedBlocks() *blockrelay.SharedRequestedBlocks
|
||||
func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
|
||||
err := f.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
log.Infof("Validation failed for block %s: %s", consensusserialization.BlockHash(block), err)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = f.OnNewBlock(block)
|
||||
|
||||
@@ -51,6 +51,9 @@ type FlowContext struct {
|
||||
|
||||
peers map[id.ID]*peerpkg.Peer
|
||||
peersMutex sync.RWMutex
|
||||
|
||||
orphans map[externalapi.DomainHash]*externalapi.DomainBlock
|
||||
orphansMutex sync.RWMutex
|
||||
}
|
||||
|
||||
// New returns a new instance of FlowContext.
|
||||
@@ -67,6 +70,7 @@ func New(cfg *config.Config, domain domain.Domain, addressManager *addressmanage
|
||||
sharedRequestedBlocks: blockrelay.NewSharedRequestedBlocks(),
|
||||
peers: make(map[id.ID]*peerpkg.Peer),
|
||||
transactionsToRebroadcast: make(map[externalapi.DomainTransactionID]*externalapi.DomainTransaction),
|
||||
orphans: make(map[externalapi.DomainHash]*externalapi.DomainBlock),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package flowcontext
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -19,7 +20,16 @@ func (f *FlowContext) StartIBDIfRequired() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
peer, err := f.selectPeerForIBD()
|
||||
syncInfo, err := f.domain.Consensus().GetSyncInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if syncInfo.State == externalapi.SyncStateRelay {
|
||||
return nil
|
||||
}
|
||||
|
||||
peer, err := f.selectPeerForIBD(syncInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -42,18 +52,30 @@ func (f *FlowContext) IsInIBD() bool {
|
||||
|
||||
// selectPeerForIBD returns the first peer whose selected tip
|
||||
// hash is not in our DAG
|
||||
func (f *FlowContext) selectPeerForIBD() (*peerpkg.Peer, error) {
|
||||
func (f *FlowContext) selectPeerForIBD(syncInfo *externalapi.SyncInfo) (*peerpkg.Peer, error) {
|
||||
f.peersMutex.RLock()
|
||||
defer f.peersMutex.RUnlock()
|
||||
|
||||
for _, peer := range f.peers {
|
||||
peerSelectedTipHash := peer.SelectedTipHash()
|
||||
|
||||
if f.IsOrphan(peerSelectedTipHash) {
|
||||
continue
|
||||
}
|
||||
|
||||
blockInfo, err := f.domain.Consensus().GetBlockInfo(peerSelectedTipHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !blockInfo.Exists {
|
||||
return peer, nil
|
||||
if syncInfo.State == externalapi.SyncStateHeadersFirst {
|
||||
if !blockInfo.Exists {
|
||||
return peer, nil
|
||||
}
|
||||
} else {
|
||||
if blockInfo.Exists && blockInfo.BlockStatus == externalapi.StatusHeaderOnly &&
|
||||
blockInfo.IsBlockInHeaderPruningPointFuture {
|
||||
return peer, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
130
app/protocol/flowcontext/orphans.go
Normal file
130
app/protocol/flowcontext/orphans.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package flowcontext
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// AddOrphan adds the block to the orphan set
|
||||
func (f *FlowContext) AddOrphan(orphanBlock *externalapi.DomainBlock) {
|
||||
f.orphansMutex.Lock()
|
||||
defer f.orphansMutex.Unlock()
|
||||
|
||||
orphanHash := consensusserialization.BlockHash(orphanBlock)
|
||||
f.orphans[*orphanHash] = orphanBlock
|
||||
|
||||
log.Infof("Received a block with missing parents, adding to orphan pool: %s", orphanHash)
|
||||
}
|
||||
|
||||
// IsOrphan returns whether the given blockHash belongs to an orphan block
|
||||
func (f *FlowContext) IsOrphan(blockHash *externalapi.DomainHash) bool {
|
||||
f.orphansMutex.RLock()
|
||||
defer f.orphansMutex.RUnlock()
|
||||
|
||||
_, ok := f.orphans[*blockHash]
|
||||
return ok
|
||||
}
|
||||
|
||||
// UnorphanBlocks removes the block from the orphan set, and remove all of the blocks that are not orphans anymore.
|
||||
func (f *FlowContext) UnorphanBlocks(rootBlock *externalapi.DomainBlock) ([]*externalapi.DomainBlock, error) {
|
||||
f.orphansMutex.Lock()
|
||||
defer f.orphansMutex.Unlock()
|
||||
|
||||
// Find all the children of rootBlock among the orphans
|
||||
// and add them to the process queue
|
||||
rootBlockHash := consensusserialization.BlockHash(rootBlock)
|
||||
processQueue := f.addChildOrphansToProcessQueue(rootBlockHash, []externalapi.DomainHash{})
|
||||
|
||||
var unorphanedBlocks []*externalapi.DomainBlock
|
||||
for len(processQueue) > 0 {
|
||||
var orphanHash externalapi.DomainHash
|
||||
orphanHash, processQueue = processQueue[0], processQueue[1:]
|
||||
orphanBlock := f.orphans[orphanHash]
|
||||
|
||||
log.Tracef("Considering to unorphan block %s with parents %s",
|
||||
orphanHash, orphanBlock.Header.ParentHashes)
|
||||
|
||||
canBeUnorphaned := true
|
||||
for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes {
|
||||
orphanBlockParentInfo, err := f.domain.Consensus().GetBlockInfo(orphanBlockParentHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !orphanBlockParentInfo.Exists {
|
||||
log.Tracef("Cannot unorphan block %s. It's missing at "+
|
||||
"least the following parent: %s", orphanHash, orphanBlockParentHash)
|
||||
|
||||
canBeUnorphaned = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if canBeUnorphaned {
|
||||
err := f.unorphanBlock(orphanHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unorphanedBlocks = append(unorphanedBlocks, orphanBlock)
|
||||
processQueue = f.addChildOrphansToProcessQueue(&orphanHash, processQueue)
|
||||
}
|
||||
}
|
||||
|
||||
return unorphanedBlocks, nil
|
||||
}
|
||||
|
||||
// addChildOrphansToProcessQueue finds all child orphans of `blockHash`
|
||||
// and adds them to the given `processQueue` if they don't already exist
|
||||
// inside of it
|
||||
// Note that this method does not modify the given `processQueue`
|
||||
func (f *FlowContext) addChildOrphansToProcessQueue(blockHash *externalapi.DomainHash,
|
||||
processQueue []externalapi.DomainHash) []externalapi.DomainHash {
|
||||
|
||||
blockChildren := f.findChildOrphansOfBlock(blockHash)
|
||||
for _, blockChild := range blockChildren {
|
||||
exists := false
|
||||
for _, queueOrphan := range processQueue {
|
||||
if queueOrphan == blockChild {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
processQueue = append(processQueue, blockChild)
|
||||
}
|
||||
}
|
||||
return processQueue
|
||||
}
|
||||
|
||||
func (f *FlowContext) findChildOrphansOfBlock(blockHash *externalapi.DomainHash) []externalapi.DomainHash {
|
||||
var childOrphans []externalapi.DomainHash
|
||||
for orphanHash, orphanBlock := range f.orphans {
|
||||
for _, orphanBlockParentHash := range orphanBlock.Header.ParentHashes {
|
||||
if *orphanBlockParentHash == *blockHash {
|
||||
childOrphans = append(childOrphans, orphanHash)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return childOrphans
|
||||
}
|
||||
|
||||
func (f *FlowContext) unorphanBlock(orphanHash externalapi.DomainHash) error {
|
||||
orphanBlock, ok := f.orphans[orphanHash]
|
||||
if !ok {
|
||||
return errors.Errorf("attempted to unorphan a non-orphan block %s", orphanHash)
|
||||
}
|
||||
delete(f.orphans, orphanHash)
|
||||
|
||||
err := f.domain.Consensus().ValidateAndInsertBlock(orphanBlock)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
log.Infof("Validation failed for orphan block %s: %s", orphanHash, err)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Unorphaned block %s", orphanHash)
|
||||
return nil
|
||||
}
|
||||
@@ -20,10 +20,6 @@ type ReceiveAddressesContext interface {
|
||||
func ReceiveAddresses(context ReceiveAddressesContext, incomingRoute *router.Route, outgoingRoute *router.Route,
|
||||
peer *peerpkg.Peer) error {
|
||||
|
||||
if !context.AddressManager().NeedMoreAddresses() {
|
||||
return nil
|
||||
}
|
||||
|
||||
subnetworkID := peer.SubnetworkID()
|
||||
msgGetAddresses := appmessage.NewMsgRequestAddresses(false, subnetworkID)
|
||||
err := outgoingRoute.Enqueue(msgGetAddresses)
|
||||
@@ -37,21 +33,10 @@ func ReceiveAddresses(context ReceiveAddressesContext, incomingRoute *router.Rou
|
||||
}
|
||||
|
||||
msgAddresses := message.(*appmessage.MsgAddresses)
|
||||
if len(msgAddresses.AddrList) > addressmanager.GetAddressesMax {
|
||||
if len(msgAddresses.AddressList) > addressmanager.GetAddressesMax {
|
||||
return protocolerrors.Errorf(true, "address count exceeded %d", addressmanager.GetAddressesMax)
|
||||
}
|
||||
|
||||
if msgAddresses.IncludeAllSubnetworks {
|
||||
return protocolerrors.Errorf(true, "got unexpected "+
|
||||
"IncludeAllSubnetworks=true in [%s] command", msgAddresses.Command())
|
||||
}
|
||||
if msgAddresses.SubnetworkID != nil && *msgAddresses.SubnetworkID != *context.Config().SubnetworkID {
|
||||
return protocolerrors.Errorf(false, "only full nodes and %s subnetwork IDs "+
|
||||
"are allowed in [%s] command, but got subnetwork ID %s",
|
||||
context.Config().SubnetworkID, msgAddresses.Command(), msgAddresses.SubnetworkID)
|
||||
}
|
||||
|
||||
sourceAddress := peer.Connection().NetAddress()
|
||||
context.AddressManager().AddAddresses(msgAddresses.AddrList, sourceAddress, msgAddresses.SubnetworkID)
|
||||
context.AddressManager().AddAddresses(msgAddresses.AddressList...)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package addressexchange
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"math/rand"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// SendAddressesContext is the interface for the context needed for the SendAddresses flow.
|
||||
@@ -14,21 +16,25 @@ type SendAddressesContext interface {
|
||||
|
||||
// SendAddresses sends addresses to a peer that requests it.
|
||||
func SendAddresses(context SendAddressesContext, incomingRoute *router.Route, outgoingRoute *router.Route) error {
|
||||
message, err := incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
message, err := incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgGetAddresses := message.(*appmessage.MsgRequestAddresses)
|
||||
addresses := context.AddressManager().AddressCache(msgGetAddresses.IncludeAllSubnetworks,
|
||||
msgGetAddresses.SubnetworkID)
|
||||
msgAddresses := appmessage.NewMsgAddresses(msgGetAddresses.IncludeAllSubnetworks, msgGetAddresses.SubnetworkID)
|
||||
err = msgAddresses.AddAddresses(shuffleAddresses(addresses)...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, ok := message.(*appmessage.MsgRequestAddresses)
|
||||
if !ok {
|
||||
return protocolerrors.Errorf(true, "unexpected message. "+
|
||||
"Expected: %s, got: %s", appmessage.CmdRequestAddresses, message.Command())
|
||||
}
|
||||
addresses := context.AddressManager().Addresses()
|
||||
msgAddresses := appmessage.NewMsgAddresses(shuffleAddresses(addresses))
|
||||
|
||||
return outgoingRoute.Enqueue(msgAddresses)
|
||||
err = outgoingRoute.Enqueue(msgAddresses)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// shuffleAddresses randomizes the given addresses sent if there are more than the maximum allowed in one message.
|
||||
|
||||
@@ -2,7 +2,6 @@ package blockrelay
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/blocklogger"
|
||||
"github.com/kaspanet/kaspad/app/protocol/common"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
@@ -26,6 +25,8 @@ type RelayInvsContext interface {
|
||||
StartIBDIfRequired() error
|
||||
IsInIBD() bool
|
||||
Broadcast(message appmessage.Message) error
|
||||
AddOrphan(orphanBlock *externalapi.DomainBlock)
|
||||
IsOrphan(blockHash *externalapi.DomainHash) bool
|
||||
}
|
||||
|
||||
type handleRelayInvsFlow struct {
|
||||
@@ -71,6 +72,10 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
continue
|
||||
}
|
||||
|
||||
if flow.IsOrphan(inv.Hash) {
|
||||
continue
|
||||
}
|
||||
|
||||
err = flow.StartIBDIfRequired()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -93,7 +98,6 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error) {
|
||||
|
||||
if len(flow.invsQueue) > 0 {
|
||||
var inv *appmessage.MsgInvRelayBlock
|
||||
inv, flow.invsQueue = flow.invsQueue[0], flow.invsQueue[1:]
|
||||
@@ -114,7 +118,7 @@ func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error)
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) requestBlocks(requestQueue *hashesQueueSet) error {
|
||||
numHashesToRequest := mathUtil.MinInt(appmessage.MsgRequestRelayBlocksHashes, requestQueue.len())
|
||||
numHashesToRequest := mathUtil.MinInt(appmessage.MaxRequestRelayBlocksHashes, requestQueue.len())
|
||||
hashesToRequest := requestQueue.dequeue(numHashesToRequest)
|
||||
|
||||
pendingBlocks := map[externalapi.DomainHash]struct{}{}
|
||||
@@ -180,9 +184,7 @@ func (flow *handleRelayInvsFlow) requestBlocks(requestQueue *hashesQueueSet) err
|
||||
// readMsgBlock returns the next msgBlock in msgChan, and populates invsQueue with any inv messages that meanwhile arrive.
|
||||
//
|
||||
// Note: this function assumes msgChan can contain only appmessage.MsgInvRelayBlock and appmessage.MsgBlock messages.
|
||||
func (flow *handleRelayInvsFlow) readMsgBlock() (
|
||||
msgBlock *appmessage.MsgBlock, err error) {
|
||||
|
||||
func (flow *handleRelayInvsFlow) readMsgBlock() (msgBlock *appmessage.MsgBlock, err error) {
|
||||
for {
|
||||
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
@@ -222,6 +224,10 @@ func (flow *handleRelayInvsFlow) processAndRelayBlock(requestQueue *hashesQueueS
|
||||
return err
|
||||
}
|
||||
selectedTipBlueScore, err := blocks.ExtractBlueScore(virtualSelectedParent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if blueScore > selectedTipBlueScore+maxOrphanBlueScoreDiff {
|
||||
log.Infof("Orphan block %s has blue score %d and the selected tip blue score is "+
|
||||
"%d. Ignoring orphans with a blue score difference from the selected tip greater than %d",
|
||||
@@ -229,6 +235,9 @@ func (flow *handleRelayInvsFlow) processAndRelayBlock(requestQueue *hashesQueueS
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add the orphan to the orphan pool
|
||||
flow.AddOrphan(block)
|
||||
|
||||
// Request the parents for the orphan block from the peer that sent it.
|
||||
for _, missingAncestor := range missingParentsError.MissingParentHashes {
|
||||
requestQueue.enqueueIfNotExists(missingAncestor)
|
||||
@@ -240,15 +249,13 @@ func (flow *handleRelayInvsFlow) processAndRelayBlock(requestQueue *hashesQueueS
|
||||
return protocolerrors.Wrapf(true, err, "got invalid block %s from relay", blockHash)
|
||||
}
|
||||
|
||||
err = blocklogger.LogBlock(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = flow.Broadcast(appmessage.NewMsgInvBlock(blockHash))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Accepted block %s via relay", blockHash)
|
||||
|
||||
err = flow.StartIBDIfRequired()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"github.com/kaspanet/kaspad/util/locks"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -38,10 +36,12 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
|
||||
) (*peerpkg.Peer, error) {
|
||||
|
||||
// For HandleHandshake to finish, we need to get from the other node
|
||||
// a version and verack messages, so we increase the wait group by 2
|
||||
// and block HandleHandshake with wg.Wait().
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
// a version and verack messages, so we set doneCount to 2, decrease it
|
||||
// when sending and receiving the version, and close the doneChan when
|
||||
// it's 0. Then we wait for on select for a tick from doneChan or from
|
||||
// errChan.
|
||||
doneCount := int32(2)
|
||||
doneChan := make(chan struct{})
|
||||
|
||||
isStopping := uint32(0)
|
||||
errChan := make(chan error)
|
||||
@@ -56,7 +56,9 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
|
||||
return
|
||||
}
|
||||
peerAddress = address
|
||||
wg.Done()
|
||||
if atomic.AddInt32(&doneCount, -1) == 0 {
|
||||
close(doneChan)
|
||||
}
|
||||
})
|
||||
|
||||
spawn("HandleHandshake-SendVersion", func() {
|
||||
@@ -65,7 +67,9 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
|
||||
handleError(err, "SendVersion", &isStopping, errChan)
|
||||
return
|
||||
}
|
||||
wg.Done()
|
||||
if atomic.AddInt32(&doneCount, -1) == 0 {
|
||||
close(doneChan)
|
||||
}
|
||||
})
|
||||
|
||||
select {
|
||||
@@ -74,7 +78,7 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
case <-locks.ReceiveFromChanWhenDone(func() { wg.Wait() }):
|
||||
case <-doneChan:
|
||||
}
|
||||
|
||||
err := context.AddToPeers(peer)
|
||||
@@ -86,9 +90,7 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
|
||||
}
|
||||
|
||||
if peerAddress != nil {
|
||||
subnetworkID := peer.SubnetworkID()
|
||||
context.AddressManager().AddAddress(peerAddress, peerAddress, subnetworkID)
|
||||
context.AddressManager().Good(peerAddress, subnetworkID)
|
||||
context.AddressManager().AddAddresses(peerAddress)
|
||||
}
|
||||
|
||||
err = context.StartIBDIfRequired()
|
||||
|
||||
@@ -56,7 +56,7 @@ func (flow *sendVersionFlow) start() error {
|
||||
subnetworkID := flow.Config().SubnetworkID
|
||||
|
||||
// Version message.
|
||||
localAddress := flow.AddressManager().GetBestLocalAddress(flow.peer.Connection().NetAddress())
|
||||
localAddress := flow.AddressManager().BestLocalAddress(flow.peer.Connection().NetAddress())
|
||||
msg := appmessage.NewMsgVersion(localAddress, flow.NetAdapter().ID(),
|
||||
flow.Config().ActiveNetParams.Name, selectedTipHash, subnetworkID)
|
||||
msg.AddUserAgent(userAgentName, userAgentVersion, flow.Config().UserAgentComments...)
|
||||
|
||||
51
app/protocol/flows/ibd/handle_ibd_block_requests.go
Normal file
51
app/protocol/flows/ibd/handle_ibd_block_requests.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package ibd
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// HandleIBDBlockRequestsContext is the interface for the context needed for the HandleIBDBlockRequests flow.
|
||||
type HandleIBDBlockRequestsContext interface {
|
||||
Domain() domain.Domain
|
||||
}
|
||||
|
||||
// HandleIBDBlockRequests listens to appmessage.MsgRequestRelayBlocks messages and sends
|
||||
// their corresponding blocks to the requesting peer.
|
||||
func HandleIBDBlockRequests(context HandleIBDBlockRequestsContext, incomingRoute *router.Route,
|
||||
outgoingRoute *router.Route) error {
|
||||
|
||||
for {
|
||||
message, err := incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msgRequestIBDBlocks := message.(*appmessage.MsgRequestIBDBlocks)
|
||||
for _, hash := range msgRequestIBDBlocks.Hashes {
|
||||
// Fetch the block from the database.
|
||||
blockInfo, err := context.Domain().Consensus().GetBlockInfo(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !blockInfo.Exists {
|
||||
return protocolerrors.Errorf(true, "block %s not found", hash)
|
||||
}
|
||||
block, err := context.Domain().Consensus().GetBlock(hash)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to fetch requested block hash %s", hash)
|
||||
}
|
||||
|
||||
// TODO (Partial nodes): Convert block to partial block if needed
|
||||
|
||||
blockMessage := appmessage.DomainBlockToMsgBlock(block)
|
||||
ibdBlockMessage := appmessage.NewMsgIBDBlock(blockMessage)
|
||||
err = outgoingRoute.Enqueue(ibdBlockMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,9 @@ import (
|
||||
)
|
||||
|
||||
const ibdBatchSize = router.DefaultMaxMessages
|
||||
const maxHeaders = appmessage.MaxInvPerMsg
|
||||
|
||||
// RequestIBDBlocksContext is the interface for the context needed for the HandleRequestIBDBlocks flow.
|
||||
// RequestIBDBlocksContext is the interface for the context needed for the HandleRequestHeaders flow.
|
||||
type RequestIBDBlocksContext interface {
|
||||
Domain() domain.Domain
|
||||
}
|
||||
@@ -21,8 +22,8 @@ type handleRequestBlocksFlow struct {
|
||||
incomingRoute, outgoingRoute *router.Route
|
||||
}
|
||||
|
||||
// HandleRequestIBDBlocks handles getBlocks messages
|
||||
func HandleRequestIBDBlocks(context RequestIBDBlocksContext, incomingRoute *router.Route, outgoingRoute *router.Route) error {
|
||||
// HandleRequestHeaders handles RequestHeaders messages
|
||||
func HandleRequestHeaders(context RequestIBDBlocksContext, incomingRoute *router.Route, outgoingRoute *router.Route) error {
|
||||
flow := &handleRequestBlocksFlow{
|
||||
RequestIBDBlocksContext: context,
|
||||
incomingRoute: incomingRoute,
|
||||
@@ -33,24 +34,24 @@ func HandleRequestIBDBlocks(context RequestIBDBlocksContext, incomingRoute *rout
|
||||
|
||||
func (flow *handleRequestBlocksFlow) start() error {
|
||||
for {
|
||||
lowHash, highHash, err := receiveRequestIBDBlocks(flow.incomingRoute)
|
||||
lowHash, highHash, err := receiveRequestHeaders(flow.incomingRoute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgIBDBlocks, err := flow.buildMsgIBDBlocks(lowHash, highHash)
|
||||
msgHeaders, err := flow.buildMsgBlockHeaders(lowHash, highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for offset := 0; offset < len(msgIBDBlocks); offset += ibdBatchSize {
|
||||
for offset := 0; offset < len(msgHeaders); offset += ibdBatchSize {
|
||||
end := offset + ibdBatchSize
|
||||
if end > len(msgIBDBlocks) {
|
||||
end = len(msgIBDBlocks)
|
||||
if end > len(msgHeaders) {
|
||||
end = len(msgHeaders)
|
||||
}
|
||||
|
||||
blocksToSend := msgIBDBlocks[offset:end]
|
||||
err = flow.sendMsgIBDBlocks(blocksToSend)
|
||||
blocksToSend := msgHeaders[offset:end]
|
||||
err = flow.sendHeaders(blocksToSend)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -66,57 +67,57 @@ func (flow *handleRequestBlocksFlow) start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := message.(*appmessage.MsgRequestNextIBDBlocks); !ok {
|
||||
if _, ok := message.(*appmessage.MsgRequestNextHeaders); !ok {
|
||||
return protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdRequestNextIBDBlocks, message.Command())
|
||||
"expected: %s, got: %s", appmessage.CmdRequestNextHeaders, message.Command())
|
||||
}
|
||||
}
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgDoneIBDBlocks())
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func receiveRequestIBDBlocks(incomingRoute *router.Route) (lowHash *externalapi.DomainHash,
|
||||
func receiveRequestHeaders(incomingRoute *router.Route) (lowHash *externalapi.DomainHash,
|
||||
highHash *externalapi.DomainHash, err error) {
|
||||
|
||||
message, err := incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
msgRequestIBDBlocks := message.(*appmessage.MsgRequestIBDBlocks)
|
||||
msgRequestIBDBlocks := message.(*appmessage.MsgRequestHeaders)
|
||||
|
||||
return msgRequestIBDBlocks.LowHash, msgRequestIBDBlocks.HighHash, nil
|
||||
}
|
||||
|
||||
func (flow *handleRequestBlocksFlow) buildMsgIBDBlocks(lowHash *externalapi.DomainHash,
|
||||
highHash *externalapi.DomainHash) ([]*appmessage.MsgIBDBlock, error) {
|
||||
func (flow *handleRequestBlocksFlow) buildMsgBlockHeaders(lowHash *externalapi.DomainHash,
|
||||
highHash *externalapi.DomainHash) ([]*appmessage.MsgBlockHeader, error) {
|
||||
|
||||
blockHashes, err := flow.Domain().Consensus().GetHashesBetween(lowHash, highHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
const maxHashesInMsgIBDBlocks = appmessage.MaxInvPerMsg
|
||||
if len(blockHashes) > maxHashesInMsgIBDBlocks {
|
||||
blockHashes = blockHashes[:maxHashesInMsgIBDBlocks]
|
||||
|
||||
if len(blockHashes) > maxHeaders {
|
||||
blockHashes = blockHashes[:maxHeaders]
|
||||
}
|
||||
|
||||
msgIBDBlocks := make([]*appmessage.MsgIBDBlock, len(blockHashes))
|
||||
msgBlockHeaders := make([]*appmessage.MsgBlockHeader, len(blockHashes))
|
||||
for i, blockHash := range blockHashes {
|
||||
block, err := flow.Domain().Consensus().GetBlock(blockHash)
|
||||
header, err := flow.Domain().Consensus().GetBlockHeader(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgIBDBlocks[i] = appmessage.NewMsgIBDBlock(appmessage.DomainBlockToMsgBlock(block))
|
||||
msgBlockHeaders[i] = appmessage.DomainBlockHeaderToBlockHeader(header)
|
||||
}
|
||||
|
||||
return msgIBDBlocks, nil
|
||||
return msgBlockHeaders, nil
|
||||
}
|
||||
|
||||
func (flow *handleRequestBlocksFlow) sendMsgIBDBlocks(msgIBDBlocks []*appmessage.MsgIBDBlock) error {
|
||||
for _, msgIBDBlock := range msgIBDBlocks {
|
||||
err := flow.outgoingRoute.Enqueue(msgIBDBlock)
|
||||
func (flow *handleRequestBlocksFlow) sendHeaders(headers []*appmessage.MsgBlockHeader) error {
|
||||
for _, msgBlockHeader := range headers {
|
||||
err := flow.outgoingRoute.Enqueue(msgBlockHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
15
app/protocol/flows/ibd/handle_request_headers_test.go
Normal file
15
app/protocol/flows/ibd/handle_request_headers_test.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package ibd
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMaxHeaders(t *testing.T) {
|
||||
testutils.ForAllNets(t, false, func(t *testing.T, params *dagconfig.Params) {
|
||||
if params.FinalityDepth() > maxHeaders {
|
||||
t.Errorf("FinalityDepth() in %s should be lower or equal to appmessage.MaxInvPerMsg", params.Name)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package ibd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleRequestIBDRootUTXOSetAndBlockContext is the interface for the context needed for the HandleRequestIBDRootUTXOSetAndBlock flow.
|
||||
type HandleRequestIBDRootUTXOSetAndBlockContext interface {
|
||||
Domain() domain.Domain
|
||||
}
|
||||
|
||||
type handleRequestIBDRootUTXOSetAndBlockFlow struct {
|
||||
HandleRequestIBDRootUTXOSetAndBlockContext
|
||||
incomingRoute, outgoingRoute *router.Route
|
||||
}
|
||||
|
||||
// HandleRequestIBDRootUTXOSetAndBlock listens to appmessage.MsgRequestIBDRootUTXOSetAndBlock messages and sends
|
||||
// the IBD root UTXO set and block body.
|
||||
func HandleRequestIBDRootUTXOSetAndBlock(context HandleRequestIBDRootUTXOSetAndBlockContext, incomingRoute,
|
||||
outgoingRoute *router.Route) error {
|
||||
flow := &handleRequestIBDRootUTXOSetAndBlockFlow{
|
||||
HandleRequestIBDRootUTXOSetAndBlockContext: context,
|
||||
incomingRoute: incomingRoute,
|
||||
outgoingRoute: outgoingRoute,
|
||||
}
|
||||
|
||||
return flow.start()
|
||||
}
|
||||
|
||||
func (flow *handleRequestIBDRootUTXOSetAndBlockFlow) start() error {
|
||||
for {
|
||||
message, err := flow.incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msgRequestIBDRootUTXOSetAndBlock := message.(*appmessage.MsgRequestIBDRootUTXOSetAndBlock)
|
||||
|
||||
utxoSet, err := flow.Domain().Consensus().GetPruningPointUTXOSet(msgRequestIBDRootUTXOSetAndBlock.IBDRoot)
|
||||
if err != nil {
|
||||
if errors.Is(err, ruleerrors.ErrWrongPruningPointHash) {
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgIBDRootNotFound())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
block, err := flow.Domain().Consensus().GetBlock(msgRequestIBDRootUTXOSetAndBlock.IBDRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgIBDRootUTXOSetAndBlock(utxoSet,
|
||||
appmessage.DomainBlockToMsgBlock(block)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package ibd
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/blocklogger"
|
||||
"github.com/kaspanet/kaspad/app/protocol/common"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
||||
@@ -53,8 +52,53 @@ func (flow *handleIBDFlow) start() error {
|
||||
|
||||
func (flow *handleIBDFlow) runIBD() error {
|
||||
flow.peer.WaitForIBDStart()
|
||||
defer flow.FinishIBD()
|
||||
err := flow.ibdLoop()
|
||||
if err != nil {
|
||||
finishIBDErr := flow.FinishIBD()
|
||||
if finishIBDErr != nil {
|
||||
return finishIBDErr
|
||||
}
|
||||
return err
|
||||
}
|
||||
return flow.FinishIBD()
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) ibdLoop() error {
|
||||
for {
|
||||
syncInfo, err := flow.Domain().Consensus().GetSyncInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch syncInfo.State {
|
||||
case externalapi.SyncStateHeadersFirst:
|
||||
err := flow.syncHeaders()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case externalapi.SyncStateMissingUTXOSet:
|
||||
found, err := flow.fetchMissingUTXOSet(syncInfo.IBDRootUTXOBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
case externalapi.SyncStateMissingBlockBodies:
|
||||
err := flow.syncMissingBlockBodies()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case externalapi.SyncStateRelay:
|
||||
return nil
|
||||
default:
|
||||
return errors.Errorf("unexpected state %s", syncInfo.State)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) syncHeaders() error {
|
||||
peerSelectedTipHash := flow.peer.SelectedTipHash()
|
||||
log.Debugf("Trying to find highest shared chain block with peer %s with selected tip %s", flow.peer, peerSelectedTipHash)
|
||||
highestSharedBlockHash, err := flow.findHighestSharedBlockHash(peerSelectedTipHash)
|
||||
@@ -64,7 +108,108 @@ func (flow *handleIBDFlow) runIBD() error {
|
||||
|
||||
log.Debugf("Found highest shared chain block %s with peer %s", highestSharedBlockHash, flow.peer)
|
||||
|
||||
return flow.downloadBlocks(highestSharedBlockHash, peerSelectedTipHash)
|
||||
return flow.downloadHeaders(highestSharedBlockHash, peerSelectedTipHash)
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) syncMissingBlockBodies() error {
|
||||
hashes, err := flow.Domain().Consensus().GetMissingBlockBodyHashes(flow.peer.SelectedTipHash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for offset := 0; offset < len(hashes); offset += appmessage.MaxRequestIBDBlocksHashes {
|
||||
var hashesToRequest []*externalapi.DomainHash
|
||||
if offset+appmessage.MaxRequestIBDBlocksHashes < len(hashes) {
|
||||
hashesToRequest = hashes[offset : offset+appmessage.MaxRequestIBDBlocksHashes]
|
||||
} else {
|
||||
hashesToRequest = hashes[offset:]
|
||||
}
|
||||
|
||||
err := flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestIBDBlocks(hashesToRequest))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, expectedHash := range hashesToRequest {
|
||||
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgIBDBlock, ok := message.(*appmessage.MsgIBDBlock)
|
||||
if !ok {
|
||||
return protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdIBDBlock, message.Command())
|
||||
}
|
||||
|
||||
block := appmessage.MsgBlockToDomainBlock(msgIBDBlock.MsgBlock)
|
||||
blockHash := consensusserialization.BlockHash(block)
|
||||
if *expectedHash != *blockHash {
|
||||
return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash)
|
||||
}
|
||||
|
||||
err = flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
return protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "invalid block %s", blockHash)
|
||||
}
|
||||
err = flow.OnNewBlock(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) fetchMissingUTXOSet(ibdRootHash *externalapi.DomainHash) (bool, error) {
|
||||
err := flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestIBDRootUTXOSetAndBlock(ibdRootHash))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
utxoSet, block, found, err := flow.receiveIBDRootUTXOSetAndBlock()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !found {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
err = flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
blockHash := consensusserialization.BlockHash(block)
|
||||
return false, protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "got invalid block %s during IBD", blockHash)
|
||||
}
|
||||
|
||||
err = flow.Domain().Consensus().SetPruningPointUTXOSet(utxoSet)
|
||||
if err != nil {
|
||||
return false, protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "error with IBD root UTXO set")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) receiveIBDRootUTXOSetAndBlock() ([]byte, *externalapi.DomainBlock, bool, error) {
|
||||
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
switch message := message.(type) {
|
||||
case *appmessage.MsgIBDRootUTXOSetAndBlock:
|
||||
return message.UTXOSet,
|
||||
appmessage.MsgBlockToDomainBlock(message.Block), true, nil
|
||||
case *appmessage.MsgIBDRootNotFound:
|
||||
return nil, nil, false, nil
|
||||
default:
|
||||
return nil, nil, false,
|
||||
protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s or %s, got: %s",
|
||||
appmessage.CmdIBDRootUTXOSetAndBlock, appmessage.CmdIBDRootNotFound, message.Command(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) findHighestSharedBlockHash(peerSelectedTipHash *externalapi.DomainHash) (lowHash *externalapi.DomainHash,
|
||||
@@ -123,17 +268,17 @@ func (flow *handleIBDFlow) receiveBlockLocator() (blockLocatorHashes []*external
|
||||
return msgBlockLocator.BlockLocatorHashes, nil
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) downloadBlocks(highestSharedBlockHash *externalapi.DomainHash,
|
||||
func (flow *handleIBDFlow) downloadHeaders(highestSharedBlockHash *externalapi.DomainHash,
|
||||
peerSelectedTipHash *externalapi.DomainHash) error {
|
||||
|
||||
err := flow.sendGetBlocks(highestSharedBlockHash, peerSelectedTipHash)
|
||||
err := flow.sendRequestHeaders(highestSharedBlockHash, peerSelectedTipHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blocksReceived := 0
|
||||
for {
|
||||
msgIBDBlock, doneIBD, err := flow.receiveIBDBlock()
|
||||
msgBlockHeader, doneIBD, err := flow.receiveHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -142,14 +287,14 @@ func (flow *handleIBDFlow) downloadBlocks(highestSharedBlockHash *externalapi.Do
|
||||
return nil
|
||||
}
|
||||
|
||||
err = flow.processIBDBlock(msgIBDBlock)
|
||||
err = flow.processHeader(msgBlockHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blocksReceived++
|
||||
if blocksReceived%ibdBatchSize == 0 {
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestNextIBDBlocks())
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestNextHeaders())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -157,57 +302,54 @@ func (flow *handleIBDFlow) downloadBlocks(highestSharedBlockHash *externalapi.Do
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) sendGetBlocks(highestSharedBlockHash *externalapi.DomainHash,
|
||||
func (flow *handleIBDFlow) sendRequestHeaders(highestSharedBlockHash *externalapi.DomainHash,
|
||||
peerSelectedTipHash *externalapi.DomainHash) error {
|
||||
|
||||
msgGetBlockInvs := appmessage.NewMsgRequstIBDBlocks(highestSharedBlockHash, peerSelectedTipHash)
|
||||
msgGetBlockInvs := appmessage.NewMsgRequstHeaders(highestSharedBlockHash, peerSelectedTipHash)
|
||||
return flow.outgoingRoute.Enqueue(msgGetBlockInvs)
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) receiveIBDBlock() (msgIBDBlock *appmessage.MsgIBDBlock, doneIBD bool, err error) {
|
||||
func (flow *handleIBDFlow) receiveHeader() (msgIBDBlock *appmessage.MsgBlockHeader, doneIBD bool, err error) {
|
||||
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
switch message := message.(type) {
|
||||
case *appmessage.MsgIBDBlock:
|
||||
case *appmessage.MsgBlockHeader:
|
||||
return message, false, nil
|
||||
case *appmessage.MsgDoneIBDBlocks:
|
||||
case *appmessage.MsgDoneHeaders:
|
||||
return nil, true, nil
|
||||
default:
|
||||
return nil, false,
|
||||
protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdIBDBlock, message.Command())
|
||||
"expected: %s or %s, got: %s", appmessage.CmdHeader, appmessage.CmdDoneHeaders, message.Command())
|
||||
}
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) processIBDBlock(msgIBDBlock *appmessage.MsgIBDBlock) error {
|
||||
block := appmessage.MsgBlockToDomainBlock(msgIBDBlock.MsgBlock)
|
||||
func (flow *handleIBDFlow) processHeader(msgBlockHeader *appmessage.MsgBlockHeader) error {
|
||||
header := appmessage.BlockHeaderToDomainBlockHeader(msgBlockHeader)
|
||||
block := &externalapi.DomainBlock{
|
||||
Header: header,
|
||||
Transactions: nil,
|
||||
}
|
||||
|
||||
blockHash := consensusserialization.BlockHash(block)
|
||||
blockInfo, err := flow.Domain().Consensus().GetBlockInfo(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if blockInfo.Exists {
|
||||
log.Debugf("IBD block %s is already in the DAG. Skipping...", blockHash)
|
||||
log.Debugf("Block header %s is already in the DAG. Skipping...", blockHash)
|
||||
return nil
|
||||
}
|
||||
err = flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
if !errors.As(err, &ruleerrors.RuleError{}) {
|
||||
return errors.Wrapf(err, "failed to process block %s during IBD", blockHash)
|
||||
return errors.Wrapf(err, "failed to process header %s during IBD", blockHash)
|
||||
}
|
||||
log.Infof("Rejected block %s from %s during IBD: %s", blockHash, flow.peer, err)
|
||||
log.Infof("Rejected block header %s from %s during IBD: %s", blockHash, flow.peer, err)
|
||||
|
||||
return protocolerrors.Wrapf(true, err, "got invalid block %s during IBD", blockHash)
|
||||
}
|
||||
err = flow.OnNewBlock(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = blocklogger.LogBlock(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
)
|
||||
|
||||
// maxProtocolVersion version is the maximum supported protocol
|
||||
// version this kaspad node supports
|
||||
const maxProtocolVersion = 1
|
||||
|
||||
// Peer holds data about a peer.
|
||||
type Peer struct {
|
||||
connection *netadapter.NetConnection
|
||||
@@ -112,9 +116,9 @@ func (p *Peer) IsOutbound() bool {
|
||||
func (p *Peer) UpdateFieldsFromMsgVersion(msg *appmessage.MsgVersion) {
|
||||
// Negotiate the protocol version.
|
||||
p.advertisedProtocolVerion = msg.ProtocolVersion
|
||||
p.protocolVersion = mathUtil.MinUint32(p.protocolVersion, p.advertisedProtocolVerion)
|
||||
p.protocolVersion = mathUtil.MinUint32(maxProtocolVersion, p.advertisedProtocolVerion)
|
||||
log.Debugf("Negotiated protocol version %d for peer %s",
|
||||
p.protocolVersion, p.ID())
|
||||
p.protocolVersion, p)
|
||||
|
||||
// Set the supported services for the peer to what the remote peer
|
||||
// advertised.
|
||||
|
||||
@@ -50,15 +50,9 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net
|
||||
return
|
||||
}
|
||||
|
||||
spawn("Manager.routerInitializer-netConnection.DequeueInvalidMessage", func() {
|
||||
for {
|
||||
isOpen, err := netConnection.DequeueInvalidMessage()
|
||||
if !isOpen {
|
||||
return
|
||||
}
|
||||
if atomic.AddUint32(&isStopping, 1) == 1 {
|
||||
errChan <- protocolerrors.Wrap(true, err, "received bad message")
|
||||
}
|
||||
netConnection.SetOnInvalidMessageHandler(func(err error) {
|
||||
if atomic.AddUint32(&isStopping, 1) == 1 {
|
||||
errChan <- protocolerrors.Wrap(true, err, "received bad message")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -124,7 +118,7 @@ func (m *Manager) registerAddressFlows(router *routerpkg.Router, isStopping *uin
|
||||
outgoingRoute := router.OutgoingRoute()
|
||||
|
||||
return []*flow{
|
||||
m.registerOneTimeFlow("SendAddresses", router, []appmessage.MessageCommand{appmessage.CmdRequestAddresses}, isStopping, errChan,
|
||||
m.registerFlow("SendAddresses", router, []appmessage.MessageCommand{appmessage.CmdRequestAddresses}, isStopping, errChan,
|
||||
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return addressexchange.SendAddresses(m.context, incomingRoute, outgoingRoute)
|
||||
},
|
||||
@@ -180,33 +174,52 @@ func (m *Manager) registerIBDFlows(router *routerpkg.Router, isStopping *uint32,
|
||||
|
||||
return []*flow{
|
||||
m.registerFlow("HandleIBD", router, []appmessage.MessageCommand{appmessage.CmdBlockLocator, appmessage.CmdIBDBlock,
|
||||
appmessage.CmdDoneIBDBlocks}, isStopping, errChan,
|
||||
appmessage.CmdDoneHeaders, appmessage.CmdIBDRootNotFound, appmessage.CmdIBDRootUTXOSetAndBlock, appmessage.CmdHeader},
|
||||
isStopping, errChan,
|
||||
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return ibd.HandleIBD(m.context, incomingRoute, outgoingRoute, peer)
|
||||
},
|
||||
),
|
||||
|
||||
m.registerFlow("RequestSelectedTip", router, []appmessage.MessageCommand{appmessage.CmdSelectedTip}, isStopping, errChan,
|
||||
m.registerFlow("RequestSelectedTip", router,
|
||||
[]appmessage.MessageCommand{appmessage.CmdSelectedTip}, isStopping, errChan,
|
||||
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return selectedtip.RequestSelectedTip(m.context, incomingRoute, outgoingRoute, peer)
|
||||
},
|
||||
),
|
||||
|
||||
m.registerFlow("HandleRequestSelectedTip", router, []appmessage.MessageCommand{appmessage.CmdRequestSelectedTip}, isStopping, errChan,
|
||||
m.registerFlow("HandleRequestSelectedTip", router,
|
||||
[]appmessage.MessageCommand{appmessage.CmdRequestSelectedTip}, isStopping, errChan,
|
||||
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return selectedtip.HandleRequestSelectedTip(m.context, incomingRoute, outgoingRoute)
|
||||
},
|
||||
),
|
||||
|
||||
m.registerFlow("HandleRequestBlockLocator", router, []appmessage.MessageCommand{appmessage.CmdRequestBlockLocator}, isStopping, errChan,
|
||||
m.registerFlow("HandleRequestBlockLocator", router,
|
||||
[]appmessage.MessageCommand{appmessage.CmdRequestBlockLocator}, isStopping, errChan,
|
||||
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return ibd.HandleRequestBlockLocator(m.context, incomingRoute, outgoingRoute)
|
||||
},
|
||||
),
|
||||
|
||||
m.registerFlow("HandleRequestIBDBlocks", router, []appmessage.MessageCommand{appmessage.CmdRequestIBDBlocks, appmessage.CmdRequestNextIBDBlocks}, isStopping, errChan,
|
||||
m.registerFlow("HandleRequestHeaders", router,
|
||||
[]appmessage.MessageCommand{appmessage.CmdRequestHeaders, appmessage.CmdRequestNextHeaders}, isStopping, errChan,
|
||||
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return ibd.HandleRequestIBDBlocks(m.context, incomingRoute, outgoingRoute)
|
||||
return ibd.HandleRequestHeaders(m.context, incomingRoute, outgoingRoute)
|
||||
},
|
||||
),
|
||||
|
||||
m.registerFlow("HandleRequestIBDRootUTXOSetAndBlock", router,
|
||||
[]appmessage.MessageCommand{appmessage.CmdRequestIBDRootUTXOSetAndBlock}, isStopping, errChan,
|
||||
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return ibd.HandleRequestIBDRootUTXOSetAndBlock(m.context, incomingRoute, outgoingRoute)
|
||||
},
|
||||
),
|
||||
|
||||
m.registerFlow("HandleIBDBlockRequests", router,
|
||||
[]appmessage.MessageCommand{appmessage.CmdRequestIBDBlocks}, isStopping, errChan,
|
||||
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return ibd.HandleIBDBlockRequests(m.context, incomingRoute, outgoingRoute)
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package protocolerrors
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ProtocolError is an error that signifies a violation
|
||||
// of the peer-to-peer protocol
|
||||
@@ -50,3 +53,14 @@ func Wrapf(shouldBan bool, err error, format string, args ...interface{}) error
|
||||
Cause: errors.Wrapf(err, format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertToBanningProtocolErrorIfRuleError converts the given error to
|
||||
// a banning protocol error if it's a rule error, and otherwise keep it
|
||||
// as is.
|
||||
func ConvertToBanningProtocolErrorIfRuleError(err error, format string, args ...interface{}) error {
|
||||
if !errors.As(err, &ruleerrors.RuleError{}) {
|
||||
return err
|
||||
}
|
||||
|
||||
return Wrapf(true, err, format, args...)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ var handlers = map[appmessage.MessageCommand]handler{
|
||||
appmessage.CmdResolveFinalityConflictRequestMessage: rpchandlers.HandleResolveFinalityConflict,
|
||||
appmessage.CmdNotifyFinalityConflictsRequestMessage: rpchandlers.HandleNotifyFinalityConflicts,
|
||||
appmessage.CmdGetMempoolEntriesRequestMessage: rpchandlers.HandleGetMempoolEntries,
|
||||
appmessage.CmdShutDownRequestMessage: rpchandlers.HandleGetMempoolEntries,
|
||||
appmessage.CmdShutDownRequestMessage: rpchandlers.HandleShutDown,
|
||||
appmessage.CmdGetHeadersRequestMessage: rpchandlers.HandleGetHeaders,
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package rpccontext
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blocks"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
@@ -25,6 +26,11 @@ func (ctx *Context) BuildBlockVerboseData(block *externalapi.DomainBlock, includ
|
||||
hash := consensusserialization.BlockHash(block)
|
||||
blockHeader := block.Header
|
||||
|
||||
blueScore, err := blocks.ExtractBlueScore(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := &appmessage.BlockVerboseData{
|
||||
Hash: hash.String(),
|
||||
Version: blockHeader.Version,
|
||||
@@ -37,6 +43,7 @@ func (ctx *Context) BuildBlockVerboseData(block *externalapi.DomainBlock, includ
|
||||
Time: blockHeader.TimeInMilliseconds,
|
||||
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
|
||||
Difficulty: ctx.GetDifficultyRatio(blockHeader.Bits, ctx.Config.ActiveNetParams),
|
||||
BlueScore: blueScore,
|
||||
}
|
||||
|
||||
txIDs := make([]string, len(block.Transactions))
|
||||
|
||||
@@ -8,6 +8,10 @@ import (
|
||||
|
||||
// HandleGetBlockCount handles the respectively named RPC command
|
||||
func HandleGetBlockCount(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
response := appmessage.NewGetBlockCountResponseMessage(0) // TODO
|
||||
syncInfo, err := context.Domain.Consensus().GetSyncInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response := appmessage.NewGetBlockCountResponseMessage(syncInfo.BlockCount)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -27,7 +27,11 @@ func HandleGetBlockTemplate(context *rpccontext.Context, _ *router.Router, reque
|
||||
|
||||
coinbaseData := &externalapi.DomainCoinbaseData{ScriptPublicKey: scriptPublicKey}
|
||||
|
||||
templateBlock := context.Domain.MiningManager().GetBlockTemplate(coinbaseData)
|
||||
templateBlock, err := context.Domain.MiningManager().GetBlockTemplate(coinbaseData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgBlock := appmessage.DomainBlockToMsgBlock(templateBlock)
|
||||
|
||||
return appmessage.DomainBlockToMsgBlock(templateBlock), nil
|
||||
return appmessage.NewGetBlockTemplateResponseMessage(msgBlock), nil
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ const (
|
||||
|
||||
// HandleGetBlocks handles the respectively named RPC command
|
||||
func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
|
||||
return nil, nil
|
||||
response := &appmessage.GetBlocksResponseMessage{}
|
||||
response.Error = appmessage.RPCErrorf("not implemented")
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -14,5 +14,7 @@ const (
|
||||
|
||||
// HandleGetChainFromBlock handles the respectively named RPC command
|
||||
func HandleGetChainFromBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
return nil, nil
|
||||
response := &appmessage.GetChainFromBlockResponseMessage{}
|
||||
response.Error = appmessage.RPCErrorf("not implemented")
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -8,5 +8,7 @@ import (
|
||||
|
||||
// HandleGetHeaders handles the respectively named RPC command
|
||||
func HandleGetHeaders(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
return nil, nil
|
||||
response := &appmessage.GetHeadersResponseMessage{}
|
||||
response.Error = appmessage.RPCErrorf("not implemented")
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -8,5 +8,7 @@ import (
|
||||
|
||||
// HandleGetMempoolEntries handles the respectively named RPC command
|
||||
func HandleGetMempoolEntries(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
return nil, nil
|
||||
response := &appmessage.GetMempoolEntriesResponseMessage{}
|
||||
response.Error = appmessage.RPCErrorf("not implemented")
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@ import (
|
||||
|
||||
// HandleGetMempoolEntry handles the respectively named RPC command
|
||||
func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
return nil, nil
|
||||
return &appmessage.GetMempoolEntryResponseMessage{}, nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package rpchandlers
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
@@ -8,14 +11,20 @@ import (
|
||||
|
||||
// HandleGetPeerAddresses handles the respectively named RPC command
|
||||
func HandleGetPeerAddresses(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
peersState, err := context.AddressManager.PeersStateForSerialization()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
netAddresses := context.AddressManager.Addresses()
|
||||
addressMessages := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(netAddresses))
|
||||
for i, netAddress := range netAddresses {
|
||||
addressWithPort := net.JoinHostPort(netAddress.IP.String(), strconv.FormatUint(uint64(netAddress.Port), 10))
|
||||
addressMessages[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: addressWithPort}
|
||||
}
|
||||
addresses := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(peersState.Addresses))
|
||||
for i, address := range peersState.Addresses {
|
||||
addresses[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: string(address.Address)}
|
||||
|
||||
bannedAddresses := context.AddressManager.BannedAddresses()
|
||||
bannedAddressMessages := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(bannedAddresses))
|
||||
for i, netAddress := range bannedAddresses {
|
||||
addressWithPort := net.JoinHostPort(netAddress.IP.String(), strconv.FormatUint(uint64(netAddress.Port), 10))
|
||||
bannedAddressMessages[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: addressWithPort}
|
||||
}
|
||||
response := appmessage.NewGetPeerAddressesResponseMessage(addresses)
|
||||
|
||||
response := appmessage.NewGetPeerAddressesResponseMessage(addressMessages, bannedAddressMessages)
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -8,5 +8,7 @@ import (
|
||||
|
||||
// HandleGetSubnetwork handles the respectively named RPC command
|
||||
func HandleGetSubnetwork(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
return nil, nil
|
||||
response := &appmessage.GetSubnetworkResponseMessage{}
|
||||
response.Error = appmessage.RPCErrorf("not implemented")
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -8,5 +8,7 @@ import (
|
||||
|
||||
// HandleResolveFinalityConflict handles the respectively named RPC command
|
||||
func HandleResolveFinalityConflict(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
return nil, nil
|
||||
response := &appmessage.ResolveFinalityConflictResponseMessage{}
|
||||
response.Error = appmessage.RPCErrorf("not implemented")
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request ap
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
log.Infof("Accepted domainBlock %s via submitBlock", consensusserialization.BlockHash(domainBlock))
|
||||
log.Infof("Accepted block %s via submitBlock", consensusserialization.BlockHash(domainBlock))
|
||||
|
||||
response := appmessage.NewSubmitBlockResponseMessage()
|
||||
return response, nil
|
||||
|
||||
@@ -6,7 +6,8 @@ RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
WORKDIR /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
RUN apk add --no-cache curl git openssh binutils gcc musl-dev
|
||||
RUN go get -u golang.org/x/lint/golint
|
||||
RUN go get -u golang.org/x/lint/golint \
|
||||
honnef.co/go/tools/cmd/staticcheck
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
@@ -20,6 +21,7 @@ WORKDIR /go/src/github.com/kaspanet/kaspad/cmd/kaspactl
|
||||
RUN GOFMT_RESULT=`go fmt ./...`; echo $GOFMT_RESULT; test -z "$GOFMT_RESULT"
|
||||
RUN go vet ./...
|
||||
RUN golint -set_exit_status ./...
|
||||
RUN staticcheck -checks SA4006 ./...
|
||||
RUN GOOS=linux go build -a -installsuffix cgo -o kaspactl .
|
||||
|
||||
# --- multistage docker build: stage #2: runtime image
|
||||
|
||||
@@ -6,7 +6,8 @@ RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
WORKDIR /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
RUN apk add --no-cache curl git openssh binutils gcc musl-dev
|
||||
RUN go get -u golang.org/x/lint/golint
|
||||
RUN go get -u golang.org/x/lint/golint \
|
||||
honnef.co/go/tools/cmd/staticcheck
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
@@ -20,6 +21,7 @@ WORKDIR /go/src/github.com/kaspanet/kaspad/cmd/kaspaminer
|
||||
RUN GOFMT_RESULT=`go fmt ./...`; echo $GOFMT_RESULT; test -z "$GOFMT_RESULT"
|
||||
RUN go vet ./...
|
||||
RUN golint -set_exit_status ./...
|
||||
RUN staticcheck -checks SA4006 ./...
|
||||
RUN GOOS=linux go build -a -installsuffix cgo -o kaspaminer .
|
||||
|
||||
# --- multistage docker build: stage #2: runtime image
|
||||
|
||||
@@ -10,7 +10,8 @@ RUN go get -u golang.org/x/lint/golint \
|
||||
github.com/kisielk/errcheck \
|
||||
github.com/opennota/check/cmd/aligncheck \
|
||||
github.com/opennota/check/cmd/structcheck \
|
||||
github.com/opennota/check/cmd/varcheck
|
||||
github.com/opennota/check/cmd/varcheck \
|
||||
honnef.co/go/tools/cmd/staticcheck
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
@@ -25,6 +26,7 @@ RUN golint -set_exit_status ./...
|
||||
# RUN aligncheck ./...
|
||||
# RUN structcheck -e ./...
|
||||
# RUN varcheck -e ./...
|
||||
RUN staticcheck -checks "SA4006,SA4008,SA4009,SA4010,SA5003,SA1004,SA1014,SA1021,SA1023,SA1024,SA1025,SA1026,SA1027,SA1028,SA2000,SA2001,SA2003,SA4000,SA4001,SA4003,SA4004,SA4011,SA4012,SA4013,SA4014,SA4015,SA4016,SA4017,SA4018,SA4019,SA4020,SA4021,SA4022,SA4023,SA5000,SA5002,SA5004,SA5005,SA5007,SA5008,SA5009,SA5010,SA5011,SA5012,SA6001,SA6002,SA9001,SA9002,SA9003,SA9004,SA9005,SA9006,ST1019" ./...
|
||||
RUN GOOS=linux go build -a -installsuffix cgo -o kaspad .
|
||||
|
||||
# Remove the line below and uncomment the line after it for testing with coverage
|
||||
|
||||
@@ -1,44 +1,47 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// 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
|
||||
ValidateTransactionAndPopulateWithConsensusData(transaction *externalapi.DomainTransaction) error
|
||||
|
||||
GetBlock(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error)
|
||||
GetBlockHeader(blockHash *externalapi.DomainHash) (*externalapi.DomainBlockHeader, error)
|
||||
GetBlockInfo(blockHash *externalapi.DomainHash) (*externalapi.BlockInfo, error)
|
||||
|
||||
GetHashesBetween(lowHash, highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
|
||||
GetMissingBlockBodyHashes(highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
|
||||
GetPruningPointUTXOSet() ([]byte, error)
|
||||
SetPruningPointUTXOSet(serializedUTXOSet []byte) error
|
||||
GetVirtualSelectedParent() (*externalapi.DomainBlock, error)
|
||||
CreateBlockLocator(lowHash, highHash *externalapi.DomainHash) (externalapi.BlockLocator, error)
|
||||
FindNextBlockLocatorBoundaries(blockLocator externalapi.BlockLocator) (lowHash, highHash *externalapi.DomainHash, err error)
|
||||
GetSyncInfo() (*externalapi.SyncInfo, error)
|
||||
}
|
||||
|
||||
type consensus struct {
|
||||
lock *sync.Mutex
|
||||
databaseContext model.DBReader
|
||||
|
||||
blockProcessor model.BlockProcessor
|
||||
blockBuilder model.BlockBuilder
|
||||
consensusStateManager model.ConsensusStateManager
|
||||
transactionValidator model.TransactionValidator
|
||||
syncManager model.SyncManager
|
||||
pastMedianTimeManager model.PastMedianTimeManager
|
||||
blockValidator model.BlockValidator
|
||||
coinbaseManager model.CoinbaseManager
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
dagTraversalManager model.DAGTraversalManager
|
||||
difficultyManager model.DifficultyManager
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
headerTipsManager model.HeaderTipsManager
|
||||
mergeDepthManager model.MergeDepthManager
|
||||
pruningManager model.PruningManager
|
||||
reachabilityManager model.ReachabilityManager
|
||||
|
||||
blockStore model.BlockStore
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
pruningStore model.PruningStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
blockStore model.BlockStore
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
pruningStore model.PruningStore
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
blockRelationStore model.BlockRelationStore
|
||||
blockStatusStore model.BlockStatusStore
|
||||
consensusStateStore model.ConsensusStateStore
|
||||
headerTipsStore model.HeaderTipsStore
|
||||
multisetStore model.MultisetStore
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
utxoDiffStore model.UTXODiffStore
|
||||
}
|
||||
|
||||
// BuildBlock builds a block over the current state, with the transactions
|
||||
@@ -46,18 +49,27 @@ type consensus struct {
|
||||
func (s *consensus) BuildBlock(coinbaseData *externalapi.DomainCoinbaseData,
|
||||
transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error) {
|
||||
|
||||
return s.blockProcessor.BuildBlock(coinbaseData, transactions)
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.blockBuilder.BuildBlock(coinbaseData, transactions)
|
||||
}
|
||||
|
||||
// ValidateAndInsertBlock validates the given block and, if valid, applies it
|
||||
// to the current state
|
||||
func (s *consensus) ValidateAndInsertBlock(block *externalapi.DomainBlock) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.blockProcessor.ValidateAndInsertBlock(block)
|
||||
}
|
||||
|
||||
// ValidateTransactionAndPopulateWithConsensusData validates the given transaction
|
||||
// and populates it with any missing consensus data
|
||||
func (s *consensus) ValidateTransactionAndPopulateWithConsensusData(transaction *externalapi.DomainTransaction) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
err := s.transactionValidator.ValidateTransactionInIsolation(transaction)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -68,11 +80,7 @@ func (s *consensus) ValidateTransactionAndPopulateWithConsensusData(transaction
|
||||
return err
|
||||
}
|
||||
|
||||
virtualGHOSTDAGData, err := s.ghostdagDataStore.Get(s.databaseContext, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
virtualSelectedParentMedianTime, err := s.pastMedianTimeManager.PastMedianTime(virtualGHOSTDAGData.SelectedParent)
|
||||
virtualSelectedParentMedianTime, err := s.pastMedianTimeManager.PastMedianTime(model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -82,14 +90,23 @@ func (s *consensus) ValidateTransactionAndPopulateWithConsensusData(transaction
|
||||
}
|
||||
|
||||
func (s *consensus) GetBlock(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.blockStore.Block(s.databaseContext, blockHash)
|
||||
}
|
||||
|
||||
func (s *consensus) GetBlockHeader(blockHash *externalapi.DomainHash) (*externalapi.DomainBlockHeader, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.blockHeaderStore.BlockHeader(s.databaseContext, blockHash)
|
||||
}
|
||||
|
||||
func (s *consensus) GetBlockInfo(blockHash *externalapi.DomainHash) (*externalapi.BlockInfo, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
blockInfo := &externalapi.BlockInfo{}
|
||||
|
||||
exists, err := s.blockStatusStore.Exists(s.databaseContext, blockHash)
|
||||
@@ -107,6 +124,11 @@ func (s *consensus) GetBlockInfo(blockHash *externalapi.DomainHash) (*externalap
|
||||
}
|
||||
blockInfo.BlockStatus = blockStatus
|
||||
|
||||
// If the status is invalid, then we don't have the necessary reachability data to check if it's in PruningPoint.Future.
|
||||
if blockStatus == externalapi.StatusInvalid {
|
||||
return blockInfo, nil
|
||||
}
|
||||
|
||||
isBlockInHeaderPruningPointFuture, err := s.syncManager.IsBlockInHeaderPruningPointFuture(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -117,37 +139,76 @@ func (s *consensus) GetBlockInfo(blockHash *externalapi.DomainHash) (*externalap
|
||||
}
|
||||
|
||||
func (s *consensus) GetHashesBetween(lowHash, highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.syncManager.GetHashesBetween(lowHash, highHash)
|
||||
}
|
||||
|
||||
func (s *consensus) GetMissingBlockBodyHashes(highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.syncManager.GetMissingBlockBodyHashes(highHash)
|
||||
}
|
||||
|
||||
func (s *consensus) GetPruningPointUTXOSet() ([]byte, error) {
|
||||
return s.pruningStore.PruningPointSerializedUTXOSet(s.databaseContext)
|
||||
func (s *consensus) GetPruningPointUTXOSet(expectedPruningPointHash *externalapi.DomainHash) ([]byte, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
pruningPointHash, err := s.pruningStore.PruningPoint(s.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if *expectedPruningPointHash != *pruningPointHash {
|
||||
return nil, errors.Wrapf(ruleerrors.ErrWrongPruningPointHash, "expected pruning point %s but got %s",
|
||||
expectedPruningPointHash,
|
||||
pruningPointHash)
|
||||
}
|
||||
|
||||
serializedUTXOSet, err := s.pruningStore.PruningPointSerializedUTXOSet(s.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return serializedUTXOSet, nil
|
||||
}
|
||||
|
||||
func (s *consensus) SetPruningPointUTXOSet(serializedUTXOSet []byte) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.consensusStateManager.SetPruningPointUTXOSet(serializedUTXOSet)
|
||||
}
|
||||
|
||||
func (s *consensus) GetVirtualSelectedParent() (*externalapi.DomainBlock, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
virtualGHOSTDAGData, err := s.ghostdagDataStore.Get(s.databaseContext, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.GetBlock(virtualGHOSTDAGData.SelectedParent)
|
||||
return s.blockStore.Block(s.databaseContext, virtualGHOSTDAGData.SelectedParent)
|
||||
}
|
||||
|
||||
func (s *consensus) CreateBlockLocator(lowHash, highHash *externalapi.DomainHash) (externalapi.BlockLocator, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.syncManager.CreateBlockLocator(lowHash, highHash)
|
||||
}
|
||||
|
||||
func (s *consensus) FindNextBlockLocatorBoundaries(blockLocator externalapi.BlockLocator) (lowHash, highHash *externalapi.DomainHash, err error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.syncManager.FindNextBlockLocatorBoundaries(blockLocator)
|
||||
}
|
||||
|
||||
func (s *consensus) GetSyncInfo() (*externalapi.SyncInfo, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.syncManager.GetSyncInfo()
|
||||
}
|
||||
|
||||
76
domain/consensus/consensus_test.go
Normal file
76
domain/consensus/consensus_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestConsensus_GetBlockInfo(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
|
||||
factory := NewFactory()
|
||||
consensus, teardown, err := factory.NewTestConsensus(params, "TestConsensus_GetBlockInfo")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown()
|
||||
|
||||
invalidBlock, _, err := consensus.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
invalidBlock.Header.TimeInMilliseconds = 0
|
||||
err = consensus.ValidateAndInsertBlock(invalidBlock)
|
||||
if !errors.Is(err, ruleerrors.ErrTimeTooOld) {
|
||||
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrTimeTooOld, err)
|
||||
}
|
||||
|
||||
info, err := consensus.GetBlockInfo(consensusserialization.BlockHash(invalidBlock))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get block info: %v", err)
|
||||
}
|
||||
|
||||
if !info.Exists {
|
||||
t.Fatal("The block is missing")
|
||||
}
|
||||
if info.BlockStatus != externalapi.StatusInvalid {
|
||||
t.Fatalf("Expected block status: %s, instead got: %s", externalapi.StatusInvalid, info.BlockStatus)
|
||||
}
|
||||
if info.IsBlockInHeaderPruningPointFuture != false {
|
||||
t.Fatalf("Expected IsBlockInHeaderPruningPointFuture=false, instead found: %t", info.IsBlockInHeaderPruningPointFuture)
|
||||
}
|
||||
|
||||
emptyCoinbase := externalapi.DomainCoinbaseData{}
|
||||
validBlock, err := consensus.BuildBlock(&emptyCoinbase, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("consensus.BuildBlock with an empty coinbase shouldn't fail: %v", err)
|
||||
}
|
||||
|
||||
err = consensus.ValidateAndInsertBlock(validBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("consensus.ValidateAndInsertBlock with a block straight from consensus.BuildBlock should not fail: %v", err)
|
||||
}
|
||||
|
||||
info, err = consensus.GetBlockInfo(consensusserialization.BlockHash(validBlock))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get block info: %v", err)
|
||||
}
|
||||
|
||||
if !info.Exists {
|
||||
t.Fatal("The block is missing")
|
||||
}
|
||||
if info.BlockStatus != externalapi.StatusValid {
|
||||
t.Fatalf("Expected block status: %s, instead got: %s", externalapi.StatusValid, info.BlockStatus)
|
||||
}
|
||||
if info.IsBlockInHeaderPruningPointFuture != true {
|
||||
t.Fatalf("Expected IsBlockInHeaderPruningPointFuture=true, instead found: %t", info.IsBlockInHeaderPruningPointFuture)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
@@ -10,8 +10,9 @@ func bluesAnticoneSizesToDBBluesAnticoneSizes(bluesAnticoneSizes map[externalapi
|
||||
dbBluesAnticoneSizes := make([]*DbBluesAnticoneSizes, len(bluesAnticoneSizes))
|
||||
i := 0
|
||||
for hash, anticoneSize := range bluesAnticoneSizes {
|
||||
hashCopy := hash
|
||||
dbBluesAnticoneSizes[i] = &DbBluesAnticoneSizes{
|
||||
BlueHash: DomainHashToDbHash(&hash),
|
||||
BlueHash: DomainHashToDbHash(&hashCopy),
|
||||
AnticoneSize: uint32(anticoneSize),
|
||||
}
|
||||
i++
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package serialization
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestBlueAnticoneSizesSize tests that no data can be loss when converting model.KType to the corresponding type in
|
||||
// DbBluesAnticoneSizes
|
||||
func TestKType(t *testing.T) {
|
||||
k := model.KType(0)
|
||||
k--
|
||||
|
||||
if k < model.KType(0) {
|
||||
t.Fatalf("KType must be unsigned")
|
||||
}
|
||||
|
||||
// Setting maxKType to maximum value of KType.
|
||||
// As we verify above that KType is unsigned we can be sure that maxKType is indeed the maximum value of KType.
|
||||
maxKType := ^model.KType(0)
|
||||
dbBluesAnticoneSizes := DbBluesAnticoneSizes{
|
||||
AnticoneSize: uint32(maxKType),
|
||||
}
|
||||
if model.KType(dbBluesAnticoneSizes.AnticoneSize) != maxKType {
|
||||
t.Fatalf("convert from uint32 to KType losses data")
|
||||
}
|
||||
}
|
||||
@@ -1535,6 +1535,194 @@ func (x *DbHeaderTips) GetTips() []*DbHash {
|
||||
return nil
|
||||
}
|
||||
|
||||
type DbTips struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Tips []*DbHash `protobuf:"bytes,1,rep,name=tips,proto3" json:"tips,omitempty"`
|
||||
}
|
||||
|
||||
func (x *DbTips) Reset() {
|
||||
*x = DbTips{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_dbobjects_proto_msgTypes[26]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *DbTips) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DbTips) ProtoMessage() {}
|
||||
|
||||
func (x *DbTips) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_dbobjects_proto_msgTypes[26]
|
||||
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 DbTips.ProtoReflect.Descriptor instead.
|
||||
func (*DbTips) Descriptor() ([]byte, []int) {
|
||||
return file_dbobjects_proto_rawDescGZIP(), []int{26}
|
||||
}
|
||||
|
||||
func (x *DbTips) GetTips() []*DbHash {
|
||||
if x != nil {
|
||||
return x.Tips
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DbVirtualDiffParents struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
VirtualDiffParents []*DbHash `protobuf:"bytes,1,rep,name=virtualDiffParents,proto3" json:"virtualDiffParents,omitempty"`
|
||||
}
|
||||
|
||||
func (x *DbVirtualDiffParents) Reset() {
|
||||
*x = DbVirtualDiffParents{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_dbobjects_proto_msgTypes[27]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *DbVirtualDiffParents) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DbVirtualDiffParents) ProtoMessage() {}
|
||||
|
||||
func (x *DbVirtualDiffParents) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_dbobjects_proto_msgTypes[27]
|
||||
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 DbVirtualDiffParents.ProtoReflect.Descriptor instead.
|
||||
func (*DbVirtualDiffParents) Descriptor() ([]byte, []int) {
|
||||
return file_dbobjects_proto_rawDescGZIP(), []int{27}
|
||||
}
|
||||
|
||||
func (x *DbVirtualDiffParents) GetVirtualDiffParents() []*DbHash {
|
||||
if x != nil {
|
||||
return x.VirtualDiffParents
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DbBlockCount struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Count uint64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
|
||||
}
|
||||
|
||||
func (x *DbBlockCount) Reset() {
|
||||
*x = DbBlockCount{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_dbobjects_proto_msgTypes[28]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *DbBlockCount) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DbBlockCount) ProtoMessage() {}
|
||||
|
||||
func (x *DbBlockCount) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_dbobjects_proto_msgTypes[28]
|
||||
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 DbBlockCount.ProtoReflect.Descriptor instead.
|
||||
func (*DbBlockCount) Descriptor() ([]byte, []int) {
|
||||
return file_dbobjects_proto_rawDescGZIP(), []int{28}
|
||||
}
|
||||
|
||||
func (x *DbBlockCount) GetCount() uint64 {
|
||||
if x != nil {
|
||||
return x.Count
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type DbBlockHeaderCount struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Count uint64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
|
||||
}
|
||||
|
||||
func (x *DbBlockHeaderCount) Reset() {
|
||||
*x = DbBlockHeaderCount{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_dbobjects_proto_msgTypes[29]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *DbBlockHeaderCount) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DbBlockHeaderCount) ProtoMessage() {}
|
||||
|
||||
func (x *DbBlockHeaderCount) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_dbobjects_proto_msgTypes[29]
|
||||
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 DbBlockHeaderCount.ProtoReflect.Descriptor instead.
|
||||
func (*DbBlockHeaderCount) Descriptor() ([]byte, []int) {
|
||||
return file_dbobjects_proto_rawDescGZIP(), []int{29}
|
||||
}
|
||||
|
||||
func (x *DbBlockHeaderCount) GetCount() uint64 {
|
||||
if x != nil {
|
||||
return x.Count
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_dbobjects_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_dbobjects_proto_rawDesc = []byte{
|
||||
@@ -1754,11 +1942,25 @@ var file_dbobjects_proto_rawDesc = []byte{
|
||||
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,
|
||||
0x6e, 0x2e, 0x44, 0x62, 0x48, 0x61, 0x73, 0x68, 0x52, 0x04, 0x74, 0x69, 0x70, 0x73, 0x22, 0x33,
|
||||
0x0a, 0x06, 0x44, 0x62, 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, 0x22, 0x5d, 0x0a, 0x14, 0x44, 0x62, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c,
|
||||
0x44, 0x69, 0x66, 0x66, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x45, 0x0a, 0x12, 0x76,
|
||||
0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74,
|
||||
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, 0x12,
|
||||
0x76, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, 0x50, 0x61, 0x72, 0x65, 0x6e,
|
||||
0x74, 0x73, 0x22, 0x24, 0x0a, 0x0c, 0x44, 0x62, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x2a, 0x0a, 0x12, 0x44, 0x62, 0x42, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 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 (
|
||||
@@ -1773,7 +1975,7 @@ func file_dbobjects_proto_rawDescGZIP() []byte {
|
||||
return file_dbobjects_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_dbobjects_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
|
||||
var file_dbobjects_proto_msgTypes = make([]protoimpl.MessageInfo, 30)
|
||||
var file_dbobjects_proto_goTypes = []interface{}{
|
||||
(*DbBlock)(nil), // 0: serialization.DbBlock
|
||||
(*DbBlockHeader)(nil), // 1: serialization.DbBlockHeader
|
||||
@@ -1801,6 +2003,10 @@ var file_dbobjects_proto_goTypes = []interface{}{
|
||||
(*DbUtxoDiff)(nil), // 23: serialization.DbUtxoDiff
|
||||
(*DbPruningPointUTXOSetBytes)(nil), // 24: serialization.DbPruningPointUTXOSetBytes
|
||||
(*DbHeaderTips)(nil), // 25: serialization.DbHeaderTips
|
||||
(*DbTips)(nil), // 26: serialization.DbTips
|
||||
(*DbVirtualDiffParents)(nil), // 27: serialization.DbVirtualDiffParents
|
||||
(*DbBlockCount)(nil), // 28: serialization.DbBlockCount
|
||||
(*DbBlockHeaderCount)(nil), // 29: serialization.DbBlockHeaderCount
|
||||
}
|
||||
var file_dbobjects_proto_depIdxs = []int32{
|
||||
1, // 0: serialization.DbBlock.header:type_name -> serialization.DbBlockHeader
|
||||
@@ -1836,11 +2042,13 @@ var file_dbobjects_proto_depIdxs = []int32{
|
||||
18, // 30: serialization.DbUtxoDiff.toAdd:type_name -> serialization.DbUtxoCollectionItem
|
||||
18, // 31: serialization.DbUtxoDiff.toRemove:type_name -> serialization.DbUtxoCollectionItem
|
||||
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
|
||||
2, // 33: serialization.DbTips.tips:type_name -> serialization.DbHash
|
||||
2, // 34: serialization.DbVirtualDiffParents.virtualDiffParents:type_name -> serialization.DbHash
|
||||
35, // [35:35] is the sub-list for method output_type
|
||||
35, // [35:35] is the sub-list for method input_type
|
||||
35, // [35:35] is the sub-list for extension type_name
|
||||
35, // [35:35] is the sub-list for extension extendee
|
||||
0, // [0:35] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_dbobjects_proto_init() }
|
||||
@@ -2161,6 +2369,54 @@ func file_dbobjects_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_dbobjects_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*DbTips); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_dbobjects_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*DbVirtualDiffParents); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_dbobjects_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*DbBlockCount); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_dbobjects_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*DbBlockHeaderCount); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
@@ -2168,7 +2424,7 @@ func file_dbobjects_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_dbobjects_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 26,
|
||||
NumMessages: 30,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -141,4 +141,20 @@ message DbPruningPointUTXOSetBytes {
|
||||
|
||||
message DbHeaderTips {
|
||||
repeated DbHash tips = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message DbTips {
|
||||
repeated DbHash tips = 1;
|
||||
}
|
||||
|
||||
message DbVirtualDiffParents {
|
||||
repeated DbHash virtualDiffParents = 1;
|
||||
}
|
||||
|
||||
message DbBlockCount {
|
||||
uint64 count = 1;
|
||||
}
|
||||
|
||||
message DbBlockHeaderCount {
|
||||
uint64 count = 1;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ func HeaderTipsToDBHeaderTips(tips []*externalapi.DomainHash) *DbHeaderTips {
|
||||
}
|
||||
}
|
||||
|
||||
// DBHeaderTipsTOHeaderTips converts DbHeaderTips to a slice of hashes
|
||||
func DBHeaderTipsTOHeaderTips(dbHeaderTips *DbHeaderTips) ([]*externalapi.DomainHash, error) {
|
||||
// DBHeaderTipsToHeaderTips converts DbHeaderTips to a slice of hashes
|
||||
func DBHeaderTipsToHeaderTips(dbHeaderTips *DbHeaderTips) ([]*externalapi.DomainHash, error) {
|
||||
return DbHashesToDomainHashes(dbHeaderTips.Tips)
|
||||
}
|
||||
|
||||
17
domain/consensus/database/serialization/tips.go
Normal file
17
domain/consensus/database/serialization/tips.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package serialization
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// TipsToDBTips converts a slice of hashes to DbTips
|
||||
func TipsToDBTips(tips []*externalapi.DomainHash) *DbTips {
|
||||
return &DbTips{
|
||||
Tips: DomainHashesToDbHashes(tips),
|
||||
}
|
||||
}
|
||||
|
||||
// DBTipsToTips converts DbTips to a slice of hashes
|
||||
func DBTipsToTips(dbTips *DbTips) ([]*externalapi.DomainHash, error) {
|
||||
return DbHashesToDomainHashes(dbTips.Tips)
|
||||
}
|
||||
@@ -8,8 +8,9 @@ func utxoCollectionToDBUTXOCollection(utxoCollection model.UTXOCollection) []*Db
|
||||
items := make([]*DbUtxoCollectionItem, len(utxoCollection))
|
||||
i := 0
|
||||
for outpoint, entry := range utxoCollection {
|
||||
outpointCopy := outpoint
|
||||
items[i] = &DbUtxoCollectionItem{
|
||||
Outpoint: DomainOutpointToDbOutpoint(&outpoint),
|
||||
Outpoint: DomainOutpointToDbOutpoint(&outpointCopy),
|
||||
UtxoEntry: UTXOEntryToDBUTXOEntry(entry),
|
||||
}
|
||||
i++
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package serialization
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// VirtualDiffParentsToDBHeaderVirtualDiffParents converts a slice of hashes to DbVirtualDiffParents
|
||||
func VirtualDiffParentsToDBHeaderVirtualDiffParents(tips []*externalapi.DomainHash) *DbVirtualDiffParents {
|
||||
return &DbVirtualDiffParents{
|
||||
VirtualDiffParents: DomainHashesToDbHashes(tips),
|
||||
}
|
||||
}
|
||||
|
||||
// DBVirtualDiffParentsToVirtualDiffParents converts DbHeaderTips to a slice of hashes
|
||||
func DBVirtualDiffParentsToVirtualDiffParents(dbVirtualDiffParents *DbVirtualDiffParents) ([]*externalapi.DomainHash, error) {
|
||||
return DbHashesToDomainHashes(dbVirtualDiffParents.VirtualDiffParents)
|
||||
}
|
||||
@@ -26,7 +26,7 @@ func (d *dbTransaction) Commit() error {
|
||||
}
|
||||
|
||||
func (d *dbTransaction) RollbackUnlessClosed() error {
|
||||
return d.RollbackUnlessClosed()
|
||||
return d.transaction.RollbackUnlessClosed()
|
||||
}
|
||||
|
||||
func newDBTransaction(transaction database.Transaction) model.DBTransaction {
|
||||
|
||||
@@ -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/consensus/utils/dbkeys"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -14,19 +15,21 @@ var bucket = dbkeys.MakeBucket([]byte("acceptance-data"))
|
||||
type acceptanceDataStore struct {
|
||||
staging map[externalapi.DomainHash]model.AcceptanceData
|
||||
toDelete map[externalapi.DomainHash]struct{}
|
||||
cache *lrucache.LRUCache
|
||||
}
|
||||
|
||||
// New instantiates a new AcceptanceDataStore
|
||||
func New() model.AcceptanceDataStore {
|
||||
func New(cacheSize int) model.AcceptanceDataStore {
|
||||
return &acceptanceDataStore{
|
||||
staging: make(map[externalapi.DomainHash]model.AcceptanceData),
|
||||
toDelete: make(map[externalapi.DomainHash]struct{}),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Stage stages the given acceptanceData for the given blockHash
|
||||
func (ads *acceptanceDataStore) Stage(blockHash *externalapi.DomainHash, acceptanceData model.AcceptanceData) {
|
||||
ads.staging[*blockHash] = acceptanceData
|
||||
ads.staging[*blockHash] = acceptanceData.Clone()
|
||||
}
|
||||
|
||||
func (ads *acceptanceDataStore) IsStaged() bool {
|
||||
@@ -48,6 +51,7 @@ func (ads *acceptanceDataStore) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ads.cache.Add(&hash, acceptanceData)
|
||||
}
|
||||
|
||||
for hash := range ads.toDelete {
|
||||
@@ -55,6 +59,7 @@ func (ads *acceptanceDataStore) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ads.cache.Remove(&hash)
|
||||
}
|
||||
|
||||
ads.Discard()
|
||||
@@ -64,7 +69,11 @@ func (ads *acceptanceDataStore) Commit(dbTx model.DBTransaction) error {
|
||||
// Get gets the acceptanceData associated with the given blockHash
|
||||
func (ads *acceptanceDataStore) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (model.AcceptanceData, error) {
|
||||
if acceptanceData, ok := ads.staging[*blockHash]; ok {
|
||||
return acceptanceData, nil
|
||||
return acceptanceData.Clone(), nil
|
||||
}
|
||||
|
||||
if acceptanceData, ok := ads.cache.Get(blockHash); ok {
|
||||
return acceptanceData.(model.AcceptanceData).Clone(), nil
|
||||
}
|
||||
|
||||
acceptanceDataBytes, err := dbContext.Get(ads.hashAsKey(blockHash))
|
||||
@@ -72,7 +81,12 @@ func (ads *acceptanceDataStore) Get(dbContext model.DBReader, blockHash *externa
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ads.deserializeAcceptanceData(acceptanceDataBytes)
|
||||
acceptanceData, err := ads.deserializeAcceptanceData(acceptanceDataBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ads.cache.Add(blockHash, acceptanceData)
|
||||
return acceptanceData.Clone(), nil
|
||||
}
|
||||
|
||||
// Delete deletes the acceptanceData associated with the given blockHash
|
||||
|
||||
@@ -6,82 +6,134 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
|
||||
)
|
||||
|
||||
var bucket = dbkeys.MakeBucket([]byte("block-headers"))
|
||||
var countKey = dbkeys.MakeBucket().Key([]byte("block-headers-count"))
|
||||
|
||||
// blockHeaderStore represents a store of blocks
|
||||
type blockHeaderStore struct {
|
||||
staging map[externalapi.DomainHash]*externalapi.DomainBlockHeader
|
||||
toDelete map[externalapi.DomainHash]struct{}
|
||||
cache *lrucache.LRUCache
|
||||
count uint64
|
||||
}
|
||||
|
||||
// New instantiates a new BlockHeaderStore
|
||||
func New() model.BlockHeaderStore {
|
||||
return &blockHeaderStore{
|
||||
func New(dbContext model.DBReader, cacheSize int) (model.BlockHeaderStore, error) {
|
||||
blockHeaderStore := &blockHeaderStore{
|
||||
staging: make(map[externalapi.DomainHash]*externalapi.DomainBlockHeader),
|
||||
toDelete: make(map[externalapi.DomainHash]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Stage stages the given block header for the given blockHash
|
||||
func (bms *blockHeaderStore) Stage(blockHash *externalapi.DomainHash, blockHeader *externalapi.DomainBlockHeader) {
|
||||
bms.staging[*blockHash] = blockHeader
|
||||
}
|
||||
|
||||
func (bms *blockHeaderStore) IsStaged() bool {
|
||||
return len(bms.staging) != 0 || len(bms.toDelete) != 0
|
||||
}
|
||||
|
||||
func (bms *blockHeaderStore) Discard() {
|
||||
bms.staging = make(map[externalapi.DomainHash]*externalapi.DomainBlockHeader)
|
||||
bms.toDelete = make(map[externalapi.DomainHash]struct{})
|
||||
}
|
||||
|
||||
func (bms *blockHeaderStore) Commit(dbTx model.DBTransaction) error {
|
||||
for hash, header := range bms.staging {
|
||||
headerBytes, err := bms.serializeHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(bms.hashAsKey(&hash), headerBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
|
||||
for hash := range bms.toDelete {
|
||||
err := dbTx.Delete(bms.hashAsKey(&hash))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
bms.Discard()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BlockHeader gets the block header associated with the given blockHash
|
||||
func (bms *blockHeaderStore) BlockHeader(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainBlockHeader, error) {
|
||||
if header, ok := bms.staging[*blockHash]; ok {
|
||||
return header, nil
|
||||
}
|
||||
|
||||
headerBytes, err := dbContext.Get(bms.hashAsKey(blockHash))
|
||||
err := blockHeaderStore.initializeCount(dbContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bms.deserializeHeader(headerBytes)
|
||||
return blockHeaderStore, nil
|
||||
}
|
||||
|
||||
func (bhs *blockHeaderStore) initializeCount(dbContext model.DBReader) error {
|
||||
count := uint64(0)
|
||||
hasCountBytes, err := dbContext.Has(countKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hasCountBytes {
|
||||
countBytes, err := dbContext.Get(countKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count, err = bhs.deserializeHeaderCount(countBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
bhs.count = count
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stage stages the given block header for the given blockHash
|
||||
func (bhs *blockHeaderStore) Stage(blockHash *externalapi.DomainHash, blockHeader *externalapi.DomainBlockHeader) {
|
||||
bhs.staging[*blockHash] = blockHeader.Clone()
|
||||
}
|
||||
|
||||
func (bhs *blockHeaderStore) IsStaged() bool {
|
||||
return len(bhs.staging) != 0 || len(bhs.toDelete) != 0
|
||||
}
|
||||
|
||||
func (bhs *blockHeaderStore) Discard() {
|
||||
bhs.staging = make(map[externalapi.DomainHash]*externalapi.DomainBlockHeader)
|
||||
bhs.toDelete = make(map[externalapi.DomainHash]struct{})
|
||||
}
|
||||
|
||||
func (bhs *blockHeaderStore) Commit(dbTx model.DBTransaction) error {
|
||||
for hash, header := range bhs.staging {
|
||||
headerBytes, err := bhs.serializeHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(bhs.hashAsKey(&hash), headerBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bhs.cache.Add(&hash, header)
|
||||
}
|
||||
|
||||
for hash := range bhs.toDelete {
|
||||
err := dbTx.Delete(bhs.hashAsKey(&hash))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bhs.cache.Remove(&hash)
|
||||
}
|
||||
|
||||
err := bhs.commitCount(dbTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bhs.Discard()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BlockHeader gets the block header associated with the given blockHash
|
||||
func (bhs *blockHeaderStore) BlockHeader(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainBlockHeader, error) {
|
||||
if header, ok := bhs.staging[*blockHash]; ok {
|
||||
return header.Clone(), nil
|
||||
}
|
||||
|
||||
if header, ok := bhs.cache.Get(blockHash); ok {
|
||||
return header.(*externalapi.DomainBlockHeader).Clone(), nil
|
||||
}
|
||||
|
||||
headerBytes, err := dbContext.Get(bhs.hashAsKey(blockHash))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
header, err := bhs.deserializeHeader(headerBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bhs.cache.Add(blockHash, header)
|
||||
return header.Clone(), nil
|
||||
}
|
||||
|
||||
// HasBlock returns whether a block header with a given hash exists in the store.
|
||||
func (bms *blockHeaderStore) HasBlockHeader(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
|
||||
if _, ok := bms.staging[*blockHash]; ok {
|
||||
func (bhs *blockHeaderStore) HasBlockHeader(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
|
||||
if _, ok := bhs.staging[*blockHash]; ok {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
exists, err := dbContext.Has(bms.hashAsKey(blockHash))
|
||||
if bhs.cache.Has(blockHash) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
exists, err := dbContext.Has(bhs.hashAsKey(blockHash))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -90,11 +142,11 @@ func (bms *blockHeaderStore) HasBlockHeader(dbContext model.DBReader, blockHash
|
||||
}
|
||||
|
||||
// BlockHeaders gets the block headers associated with the given blockHashes
|
||||
func (bms *blockHeaderStore) BlockHeaders(dbContext model.DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlockHeader, error) {
|
||||
func (bhs *blockHeaderStore) BlockHeaders(dbContext model.DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlockHeader, error) {
|
||||
headers := make([]*externalapi.DomainBlockHeader, len(blockHashes))
|
||||
for i, hash := range blockHashes {
|
||||
var err error
|
||||
headers[i], err = bms.BlockHeader(dbContext, hash)
|
||||
headers[i], err = bhs.BlockHeader(dbContext, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -103,24 +155,24 @@ func (bms *blockHeaderStore) BlockHeaders(dbContext model.DBReader, blockHashes
|
||||
}
|
||||
|
||||
// Delete deletes the block associated with the given blockHash
|
||||
func (bms *blockHeaderStore) Delete(blockHash *externalapi.DomainHash) {
|
||||
if _, ok := bms.staging[*blockHash]; ok {
|
||||
delete(bms.staging, *blockHash)
|
||||
func (bhs *blockHeaderStore) Delete(blockHash *externalapi.DomainHash) {
|
||||
if _, ok := bhs.staging[*blockHash]; ok {
|
||||
delete(bhs.staging, *blockHash)
|
||||
return
|
||||
}
|
||||
bms.toDelete[*blockHash] = struct{}{}
|
||||
bhs.toDelete[*blockHash] = struct{}{}
|
||||
}
|
||||
|
||||
func (bms *blockHeaderStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
|
||||
func (bhs *blockHeaderStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
|
||||
return bucket.Key(hash[:])
|
||||
}
|
||||
|
||||
func (bms *blockHeaderStore) serializeHeader(header *externalapi.DomainBlockHeader) ([]byte, error) {
|
||||
func (bhs *blockHeaderStore) serializeHeader(header *externalapi.DomainBlockHeader) ([]byte, error) {
|
||||
dbBlockHeader := serialization.DomainBlockHeaderToDbBlockHeader(header)
|
||||
return proto.Marshal(dbBlockHeader)
|
||||
}
|
||||
|
||||
func (bms *blockHeaderStore) deserializeHeader(headerBytes []byte) (*externalapi.DomainBlockHeader, error) {
|
||||
func (bhs *blockHeaderStore) deserializeHeader(headerBytes []byte) (*externalapi.DomainBlockHeader, error) {
|
||||
dbBlockHeader := &serialization.DbBlockHeader{}
|
||||
err := proto.Unmarshal(headerBytes, dbBlockHeader)
|
||||
if err != nil {
|
||||
@@ -128,3 +180,35 @@ func (bms *blockHeaderStore) deserializeHeader(headerBytes []byte) (*externalapi
|
||||
}
|
||||
return serialization.DbBlockHeaderToDomainBlockHeader(dbBlockHeader)
|
||||
}
|
||||
|
||||
func (bhs *blockHeaderStore) Count() uint64 {
|
||||
return bhs.count + uint64(len(bhs.staging)) - uint64(len(bhs.toDelete))
|
||||
}
|
||||
|
||||
func (bhs *blockHeaderStore) deserializeHeaderCount(countBytes []byte) (uint64, error) {
|
||||
dbBlockHeaderCount := &serialization.DbBlockHeaderCount{}
|
||||
err := proto.Unmarshal(countBytes, dbBlockHeaderCount)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dbBlockHeaderCount.Count, nil
|
||||
}
|
||||
|
||||
func (bhs *blockHeaderStore) commitCount(dbTx model.DBTransaction) error {
|
||||
count := bhs.Count()
|
||||
countBytes, err := bhs.serializeHeaderCount(count)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(countKey, countBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bhs.count = count
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bhs *blockHeaderStore) serializeHeaderCount(count uint64) ([]byte, error) {
|
||||
dbBlockHeaderCount := &serialization.DbBlockHeaderCount{Count: count}
|
||||
return proto.Marshal(dbBlockHeaderCount)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
|
||||
)
|
||||
|
||||
var bucket = dbkeys.MakeBucket([]byte("block-relations"))
|
||||
@@ -13,17 +14,19 @@ var bucket = dbkeys.MakeBucket([]byte("block-relations"))
|
||||
// blockRelationStore represents a store of BlockRelations
|
||||
type blockRelationStore struct {
|
||||
staging map[externalapi.DomainHash]*model.BlockRelations
|
||||
cache *lrucache.LRUCache
|
||||
}
|
||||
|
||||
// New instantiates a new BlockRelationStore
|
||||
func New() model.BlockRelationStore {
|
||||
func New(cacheSize int) model.BlockRelationStore {
|
||||
return &blockRelationStore{
|
||||
staging: make(map[externalapi.DomainHash]*model.BlockRelations),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
func (brs *blockRelationStore) StageBlockRelation(blockHash *externalapi.DomainHash, blockRelations *model.BlockRelations) {
|
||||
brs.staging[*blockHash] = blockRelations
|
||||
brs.staging[*blockHash] = blockRelations.Clone()
|
||||
}
|
||||
|
||||
func (brs *blockRelationStore) IsStaged() bool {
|
||||
@@ -44,6 +47,7 @@ func (brs *blockRelationStore) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
brs.cache.Add(&hash, blockRelations)
|
||||
}
|
||||
|
||||
brs.Discard()
|
||||
@@ -52,7 +56,11 @@ func (brs *blockRelationStore) Commit(dbTx model.DBTransaction) error {
|
||||
|
||||
func (brs *blockRelationStore) BlockRelation(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*model.BlockRelations, error) {
|
||||
if blockRelations, ok := brs.staging[*blockHash]; ok {
|
||||
return blockRelations, nil
|
||||
return blockRelations.Clone(), nil
|
||||
}
|
||||
|
||||
if blockRelations, ok := brs.cache.Get(blockHash); ok {
|
||||
return blockRelations.(*model.BlockRelations).Clone(), nil
|
||||
}
|
||||
|
||||
blockRelationsBytes, err := dbContext.Get(brs.hashAsKey(blockHash))
|
||||
@@ -60,7 +68,12 @@ func (brs *blockRelationStore) BlockRelation(dbContext model.DBReader, blockHash
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return brs.deserializeBlockRelations(blockRelationsBytes)
|
||||
blockRelations, err := brs.deserializeBlockRelations(blockRelationsBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
brs.cache.Add(blockHash, blockRelations)
|
||||
return blockRelations.Clone(), nil
|
||||
}
|
||||
|
||||
func (brs *blockRelationStore) Has(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
|
||||
@@ -68,6 +81,10 @@ func (brs *blockRelationStore) Has(dbContext model.DBReader, blockHash *external
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if brs.cache.Has(blockHash) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return dbContext.Has(brs.hashAsKey(blockHash))
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
|
||||
)
|
||||
|
||||
var bucket = dbkeys.MakeBucket([]byte("block-statuses"))
|
||||
@@ -13,18 +14,20 @@ var bucket = dbkeys.MakeBucket([]byte("block-statuses"))
|
||||
// blockStatusStore represents a store of BlockStatuses
|
||||
type blockStatusStore struct {
|
||||
staging map[externalapi.DomainHash]externalapi.BlockStatus
|
||||
cache *lrucache.LRUCache
|
||||
}
|
||||
|
||||
// New instantiates a new BlockStatusStore
|
||||
func New() model.BlockStatusStore {
|
||||
func New(cacheSize int) model.BlockStatusStore {
|
||||
return &blockStatusStore{
|
||||
staging: make(map[externalapi.DomainHash]externalapi.BlockStatus),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Stage stages the given blockStatus for the given blockHash
|
||||
func (bss *blockStatusStore) Stage(blockHash *externalapi.DomainHash, blockStatus externalapi.BlockStatus) {
|
||||
bss.staging[*blockHash] = blockStatus
|
||||
bss.staging[*blockHash] = blockStatus.Clone()
|
||||
}
|
||||
|
||||
func (bss *blockStatusStore) IsStaged() bool {
|
||||
@@ -45,6 +48,7 @@ func (bss *blockStatusStore) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bss.cache.Add(&hash, status)
|
||||
}
|
||||
|
||||
bss.Discard()
|
||||
@@ -57,12 +61,21 @@ func (bss *blockStatusStore) Get(dbContext model.DBReader, blockHash *externalap
|
||||
return status, nil
|
||||
}
|
||||
|
||||
if status, ok := bss.cache.Get(blockHash); ok {
|
||||
return status.(externalapi.BlockStatus), nil
|
||||
}
|
||||
|
||||
statusBytes, err := dbContext.Get(bss.hashAsKey(blockHash))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return bss.deserializeHeader(statusBytes)
|
||||
status, err := bss.deserializeBlockStatus(statusBytes)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
bss.cache.Add(blockHash, status)
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// Exists returns true if the blockStatus for the given blockHash exists
|
||||
@@ -71,6 +84,10 @@ func (bss *blockStatusStore) Exists(dbContext model.DBReader, blockHash *externa
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if bss.cache.Has(blockHash) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
exists, err := dbContext.Has(bss.hashAsKey(blockHash))
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -84,7 +101,7 @@ func (bss *blockStatusStore) serializeBlockStatus(status externalapi.BlockStatus
|
||||
return proto.Marshal(dbBlockStatus)
|
||||
}
|
||||
|
||||
func (bss *blockStatusStore) deserializeHeader(statusBytes []byte) (externalapi.BlockStatus, error) {
|
||||
func (bss *blockStatusStore) deserializeBlockStatus(statusBytes []byte) (externalapi.BlockStatus, error) {
|
||||
dbBlockStatus := &serialization.DbBlockStatus{}
|
||||
err := proto.Unmarshal(statusBytes, dbBlockStatus)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,82 +6,134 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
|
||||
)
|
||||
|
||||
var bucket = dbkeys.MakeBucket([]byte("blocks"))
|
||||
var countKey = dbkeys.MakeBucket().Key([]byte("blocks-count"))
|
||||
|
||||
// blockStore represents a store of blocks
|
||||
type blockStore struct {
|
||||
staging map[externalapi.DomainHash]*externalapi.DomainBlock
|
||||
toDelete map[externalapi.DomainHash]struct{}
|
||||
cache *lrucache.LRUCache
|
||||
count uint64
|
||||
}
|
||||
|
||||
// New instantiates a new BlockStore
|
||||
func New() model.BlockStore {
|
||||
return &blockStore{
|
||||
func New(dbContext model.DBReader, cacheSize int) (model.BlockStore, error) {
|
||||
blockStore := &blockStore{
|
||||
staging: make(map[externalapi.DomainHash]*externalapi.DomainBlock),
|
||||
toDelete: make(map[externalapi.DomainHash]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Stage stages the given block for the given blockHash
|
||||
func (bms *blockStore) Stage(blockHash *externalapi.DomainHash, block *externalapi.DomainBlock) {
|
||||
bms.staging[*blockHash] = block
|
||||
}
|
||||
|
||||
func (bms *blockStore) IsStaged() bool {
|
||||
return len(bms.staging) != 0 || len(bms.toDelete) != 0
|
||||
}
|
||||
|
||||
func (bms *blockStore) Discard() {
|
||||
bms.staging = make(map[externalapi.DomainHash]*externalapi.DomainBlock)
|
||||
bms.toDelete = make(map[externalapi.DomainHash]struct{})
|
||||
}
|
||||
|
||||
func (bms *blockStore) Commit(dbTx model.DBTransaction) error {
|
||||
for hash, block := range bms.staging {
|
||||
blockBytes, err := bms.serializeBlock(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(bms.hashAsKey(&hash), blockBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
|
||||
for hash := range bms.toDelete {
|
||||
err := dbTx.Delete(bms.hashAsKey(&hash))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
bms.Discard()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Block gets the block associated with the given blockHash
|
||||
func (bms *blockStore) Block(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) {
|
||||
if block, ok := bms.staging[*blockHash]; ok {
|
||||
return block, nil
|
||||
}
|
||||
|
||||
blockBytes, err := dbContext.Get(bms.hashAsKey(blockHash))
|
||||
err := blockStore.initializeCount(dbContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bms.deserializeBlock(blockBytes)
|
||||
return blockStore, nil
|
||||
}
|
||||
|
||||
func (bs *blockStore) initializeCount(dbContext model.DBReader) error {
|
||||
count := uint64(0)
|
||||
hasCountBytes, err := dbContext.Has(countKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hasCountBytes {
|
||||
countBytes, err := dbContext.Get(countKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count, err = bs.deserializeBlockCount(countBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
bs.count = count
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stage stages the given block for the given blockHash
|
||||
func (bs *blockStore) Stage(blockHash *externalapi.DomainHash, block *externalapi.DomainBlock) {
|
||||
bs.staging[*blockHash] = block.Clone()
|
||||
}
|
||||
|
||||
func (bs *blockStore) IsStaged() bool {
|
||||
return len(bs.staging) != 0 || len(bs.toDelete) != 0
|
||||
}
|
||||
|
||||
func (bs *blockStore) Discard() {
|
||||
bs.staging = make(map[externalapi.DomainHash]*externalapi.DomainBlock)
|
||||
bs.toDelete = make(map[externalapi.DomainHash]struct{})
|
||||
}
|
||||
|
||||
func (bs *blockStore) Commit(dbTx model.DBTransaction) error {
|
||||
for hash, block := range bs.staging {
|
||||
blockBytes, err := bs.serializeBlock(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(bs.hashAsKey(&hash), blockBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bs.cache.Add(&hash, block)
|
||||
}
|
||||
|
||||
for hash := range bs.toDelete {
|
||||
err := dbTx.Delete(bs.hashAsKey(&hash))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bs.cache.Remove(&hash)
|
||||
}
|
||||
|
||||
err := bs.commitCount(dbTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bs.Discard()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Block gets the block associated with the given blockHash
|
||||
func (bs *blockStore) Block(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) {
|
||||
if block, ok := bs.staging[*blockHash]; ok {
|
||||
return block.Clone(), nil
|
||||
}
|
||||
|
||||
if block, ok := bs.cache.Get(blockHash); ok {
|
||||
return block.(*externalapi.DomainBlock).Clone(), nil
|
||||
}
|
||||
|
||||
blockBytes, err := dbContext.Get(bs.hashAsKey(blockHash))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := bs.deserializeBlock(blockBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bs.cache.Add(blockHash, block)
|
||||
return block.Clone(), nil
|
||||
}
|
||||
|
||||
// HasBlock returns whether a block with a given hash exists in the store.
|
||||
func (bms *blockStore) HasBlock(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
|
||||
if _, ok := bms.staging[*blockHash]; ok {
|
||||
func (bs *blockStore) HasBlock(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
|
||||
if _, ok := bs.staging[*blockHash]; ok {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
exists, err := dbContext.Has(bms.hashAsKey(blockHash))
|
||||
if bs.cache.Has(blockHash) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
exists, err := dbContext.Has(bs.hashAsKey(blockHash))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -90,11 +142,11 @@ func (bms *blockStore) HasBlock(dbContext model.DBReader, blockHash *externalapi
|
||||
}
|
||||
|
||||
// Blocks gets the blocks associated with the given blockHashes
|
||||
func (bms *blockStore) Blocks(dbContext model.DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlock, error) {
|
||||
func (bs *blockStore) Blocks(dbContext model.DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlock, error) {
|
||||
blocks := make([]*externalapi.DomainBlock, len(blockHashes))
|
||||
for i, hash := range blockHashes {
|
||||
var err error
|
||||
blocks[i], err = bms.Block(dbContext, hash)
|
||||
blocks[i], err = bs.Block(dbContext, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -103,20 +155,20 @@ func (bms *blockStore) Blocks(dbContext model.DBReader, blockHashes []*externala
|
||||
}
|
||||
|
||||
// Delete deletes the block associated with the given blockHash
|
||||
func (bms *blockStore) Delete(blockHash *externalapi.DomainHash) {
|
||||
if _, ok := bms.staging[*blockHash]; ok {
|
||||
delete(bms.staging, *blockHash)
|
||||
func (bs *blockStore) Delete(blockHash *externalapi.DomainHash) {
|
||||
if _, ok := bs.staging[*blockHash]; ok {
|
||||
delete(bs.staging, *blockHash)
|
||||
return
|
||||
}
|
||||
bms.toDelete[*blockHash] = struct{}{}
|
||||
bs.toDelete[*blockHash] = struct{}{}
|
||||
}
|
||||
|
||||
func (bms *blockStore) serializeBlock(block *externalapi.DomainBlock) ([]byte, error) {
|
||||
func (bs *blockStore) serializeBlock(block *externalapi.DomainBlock) ([]byte, error) {
|
||||
dbBlock := serialization.DomainBlockToDbBlock(block)
|
||||
return proto.Marshal(dbBlock)
|
||||
}
|
||||
|
||||
func (bms *blockStore) deserializeBlock(blockBytes []byte) (*externalapi.DomainBlock, error) {
|
||||
func (bs *blockStore) deserializeBlock(blockBytes []byte) (*externalapi.DomainBlock, error) {
|
||||
dbBlock := &serialization.DbBlock{}
|
||||
err := proto.Unmarshal(blockBytes, dbBlock)
|
||||
if err != nil {
|
||||
@@ -125,6 +177,38 @@ func (bms *blockStore) deserializeBlock(blockBytes []byte) (*externalapi.DomainB
|
||||
return serialization.DbBlockToDomainBlock(dbBlock)
|
||||
}
|
||||
|
||||
func (bms *blockStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
|
||||
func (bs *blockStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
|
||||
return bucket.Key(hash[:])
|
||||
}
|
||||
|
||||
func (bs *blockStore) Count() uint64 {
|
||||
return bs.count + uint64(len(bs.staging)) - uint64(len(bs.toDelete))
|
||||
}
|
||||
|
||||
func (bs *blockStore) deserializeBlockCount(countBytes []byte) (uint64, error) {
|
||||
dbBlockCount := &serialization.DbBlockCount{}
|
||||
err := proto.Unmarshal(countBytes, dbBlockCount)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dbBlockCount.Count, nil
|
||||
}
|
||||
|
||||
func (bs *blockStore) commitCount(dbTx model.DBTransaction) error {
|
||||
count := bs.Count()
|
||||
countBytes, err := bs.serializeBlockCount(count)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(countKey, countBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bs.count = count
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bs *blockStore) serializeBlockCount(count uint64) ([]byte, error) {
|
||||
dbBlockCount := &serialization.DbBlockCount{Count: count}
|
||||
return proto.Marshal(dbBlockCount)
|
||||
}
|
||||
|
||||
@@ -7,10 +7,13 @@ import (
|
||||
|
||||
// consensusStateStore represents a store for the current consensus state
|
||||
type consensusStateStore struct {
|
||||
stagedTips []*externalapi.DomainHash
|
||||
stagedVirtualDiffParents []*externalapi.DomainHash
|
||||
stagedVirtualUTXODiff *model.UTXODiff
|
||||
stagedVirtualUTXOSet model.UTXOCollection
|
||||
tipsStaging []*externalapi.DomainHash
|
||||
virtualDiffParentsStaging []*externalapi.DomainHash
|
||||
virtualUTXODiffStaging *model.UTXODiff
|
||||
virtualUTXOSetStaging model.UTXOCollection
|
||||
|
||||
tipsCache []*externalapi.DomainHash
|
||||
virtualDiffParentsCache []*externalapi.DomainHash
|
||||
}
|
||||
|
||||
// New instantiates a new ConsensusStateStore
|
||||
@@ -18,40 +21,40 @@ func New() model.ConsensusStateStore {
|
||||
return &consensusStateStore{}
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) Discard() {
|
||||
c.stagedTips = nil
|
||||
c.stagedVirtualUTXODiff = nil
|
||||
c.stagedVirtualDiffParents = nil
|
||||
c.stagedVirtualUTXOSet = nil
|
||||
func (css *consensusStateStore) Discard() {
|
||||
css.tipsStaging = nil
|
||||
css.virtualUTXODiffStaging = nil
|
||||
css.virtualDiffParentsStaging = nil
|
||||
css.virtualUTXOSetStaging = nil
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) Commit(dbTx model.DBTransaction) error {
|
||||
err := c.commitTips(dbTx)
|
||||
func (css *consensusStateStore) Commit(dbTx model.DBTransaction) error {
|
||||
err := css.commitTips(dbTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.commitVirtualDiffParents(dbTx)
|
||||
err = css.commitVirtualDiffParents(dbTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.commitVirtualUTXODiff(dbTx)
|
||||
err = css.commitVirtualUTXODiff(dbTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.commitVirtualUTXOSet(dbTx)
|
||||
err = css.commitVirtualUTXOSet(dbTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Discard()
|
||||
css.Discard()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) IsStaged() bool {
|
||||
return c.stagedTips != nil ||
|
||||
c.stagedVirtualDiffParents != nil ||
|
||||
c.stagedVirtualUTXODiff != nil
|
||||
func (css *consensusStateStore) IsStaged() bool {
|
||||
return css.tipsStaging != nil ||
|
||||
css.virtualDiffParentsStaging != nil ||
|
||||
css.virtualUTXODiffStaging != nil
|
||||
}
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
package consensusstatestore
|
||||
|
||||
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"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
)
|
||||
|
||||
var tipsKey = dbkeys.MakeBucket().Key([]byte("tips"))
|
||||
|
||||
func (c *consensusStateStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
|
||||
if c.stagedTips != nil {
|
||||
return c.stagedTips, nil
|
||||
func (css *consensusStateStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
|
||||
if css.tipsStaging != nil {
|
||||
return externalapi.CloneHashes(css.tipsStaging), nil
|
||||
}
|
||||
|
||||
if css.tipsCache != nil {
|
||||
return externalapi.CloneHashes(css.tipsCache), nil
|
||||
}
|
||||
|
||||
tipsBytes, err := dbContext.Get(tipsKey)
|
||||
@@ -19,20 +24,51 @@ func (c *consensusStateStore) Tips(dbContext model.DBReader) ([]*externalapi.Dom
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hashes.DeserializeHashSlice(tipsBytes)
|
||||
tips, err := css.deserializeTips(tipsBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
css.tipsCache = tips
|
||||
return externalapi.CloneHashes(tips), nil
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) StageTips(tipHashes []*externalapi.DomainHash) {
|
||||
c.stagedTips = tipHashes
|
||||
func (css *consensusStateStore) StageTips(tipHashes []*externalapi.DomainHash) {
|
||||
css.tipsStaging = externalapi.CloneHashes(tipHashes)
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) commitTips(dbTx model.DBTransaction) error {
|
||||
tipsBytes := hashes.SerializeHashSlice(c.stagedTips)
|
||||
func (css *consensusStateStore) commitTips(dbTx model.DBTransaction) error {
|
||||
if css.tipsStaging == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := dbTx.Put(tipsKey, tipsBytes)
|
||||
tipsBytes, err := css.serializeTips(css.tipsStaging)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(tipsKey, tipsBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
css.tipsCache = css.tipsStaging
|
||||
|
||||
// Note: we don't discard the staging here since that's
|
||||
// being done at the end of Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (css *consensusStateStore) serializeTips(tips []*externalapi.DomainHash) ([]byte, error) {
|
||||
dbTips := serialization.TipsToDBTips(tips)
|
||||
return proto.Marshal(dbTips)
|
||||
}
|
||||
|
||||
func (css *consensusStateStore) deserializeTips(tipsBytes []byte) ([]*externalapi.DomainHash,
|
||||
error) {
|
||||
|
||||
dbTips := &serialization.DbTips{}
|
||||
err := proto.Unmarshal(tipsBytes, dbTips)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return serialization.DBTipsToTips(dbTips)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -18,25 +19,21 @@ func utxoKey(outpoint *externalapi.DomainOutpoint) (model.DBKey, error) {
|
||||
return utxoSetBucket.Key(serializedOutpoint), nil
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) StageVirtualUTXODiff(virtualUTXODiff *model.UTXODiff) error {
|
||||
if c.stagedVirtualUTXOSet != nil {
|
||||
func (css *consensusStateStore) StageVirtualUTXODiff(virtualUTXODiff *model.UTXODiff) error {
|
||||
if css.virtualUTXOSetStaging != nil {
|
||||
return errors.New("cannot stage virtual UTXO diff while virtual UTXO set is staged")
|
||||
}
|
||||
|
||||
c.stagedVirtualUTXODiff = virtualUTXODiff
|
||||
css.virtualUTXODiffStaging = virtualUTXODiff.Clone()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) commitVirtualUTXODiff(dbTx model.DBTransaction) error {
|
||||
if c.stagedVirtualUTXOSet != nil {
|
||||
return errors.New("cannot commit virtual UTXO diff while virtual UTXO set is staged")
|
||||
}
|
||||
|
||||
if c.stagedVirtualUTXODiff == nil {
|
||||
func (css *consensusStateStore) commitVirtualUTXODiff(dbTx model.DBTransaction) error {
|
||||
if css.virtualUTXODiffStaging == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for toRemoveOutpoint := range c.stagedVirtualUTXODiff.ToRemove {
|
||||
for toRemoveOutpoint := range css.virtualUTXODiffStaging.ToRemove {
|
||||
dbKey, err := utxoKey(&toRemoveOutpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -47,7 +44,7 @@ func (c *consensusStateStore) commitVirtualUTXODiff(dbTx model.DBTransaction) er
|
||||
}
|
||||
}
|
||||
|
||||
for toAddOutpoint, toAddEntry := range c.stagedVirtualUTXODiff.ToAdd {
|
||||
for toAddOutpoint, toAddEntry := range css.virtualUTXODiffStaging.ToAdd {
|
||||
dbKey, err := utxoKey(&toAddOutpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -62,15 +59,17 @@ func (c *consensusStateStore) commitVirtualUTXODiff(dbTx model.DBTransaction) er
|
||||
}
|
||||
}
|
||||
|
||||
// Note: we don't discard the staging here since that's
|
||||
// being done at the end of Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) commitVirtualUTXOSet(dbTx model.DBTransaction) error {
|
||||
if c.stagedVirtualUTXODiff != nil {
|
||||
return errors.New("cannot commit virtual UTXO set while virtual UTXO diff is staged")
|
||||
func (css *consensusStateStore) commitVirtualUTXOSet(dbTx model.DBTransaction) error {
|
||||
if css.virtualUTXOSetStaging == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for outpoint, utxoEntry := range c.stagedVirtualUTXOSet {
|
||||
for outpoint, utxoEntry := range css.virtualUTXOSetStaging {
|
||||
dbKey, err := utxoKey(&outpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -85,28 +84,30 @@ func (c *consensusStateStore) commitVirtualUTXOSet(dbTx model.DBTransaction) err
|
||||
}
|
||||
}
|
||||
|
||||
// Note: we don't discard the staging here since that's
|
||||
// being done at the end of Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) UTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (
|
||||
func (css *consensusStateStore) UTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (
|
||||
*externalapi.UTXOEntry, error) {
|
||||
|
||||
if c.stagedVirtualUTXOSet != nil {
|
||||
return c.utxoByOutpointFromStagedVirtualUTXOSet(outpoint)
|
||||
if css.virtualUTXOSetStaging != nil {
|
||||
return css.utxoByOutpointFromStagedVirtualUTXOSet(outpoint)
|
||||
}
|
||||
|
||||
return c.utxoByOutpointFromStagedVirtualUTXODiff(dbContext, outpoint)
|
||||
return css.utxoByOutpointFromStagedVirtualUTXODiff(dbContext, outpoint)
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) utxoByOutpointFromStagedVirtualUTXODiff(dbContext model.DBReader,
|
||||
func (css *consensusStateStore) utxoByOutpointFromStagedVirtualUTXODiff(dbContext model.DBReader,
|
||||
outpoint *externalapi.DomainOutpoint) (
|
||||
*externalapi.UTXOEntry, error) {
|
||||
|
||||
if c.stagedVirtualUTXODiff != nil {
|
||||
if _, ok := c.stagedVirtualUTXODiff.ToRemove[*outpoint]; ok {
|
||||
if css.virtualUTXODiffStaging != nil {
|
||||
if _, ok := css.virtualUTXODiffStaging.ToRemove[*outpoint]; ok {
|
||||
return nil, errors.Errorf("outpoint was not found")
|
||||
}
|
||||
if utxoEntry, ok := c.stagedVirtualUTXODiff.ToAdd[*outpoint]; ok {
|
||||
if utxoEntry, ok := css.virtualUTXODiffStaging.ToAdd[*outpoint]; ok {
|
||||
return utxoEntry, nil
|
||||
}
|
||||
}
|
||||
@@ -124,30 +125,33 @@ func (c *consensusStateStore) utxoByOutpointFromStagedVirtualUTXODiff(dbContext
|
||||
return deserializeUTXOEntry(serializedUTXOEntry)
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) utxoByOutpointFromStagedVirtualUTXOSet(outpoint *externalapi.DomainOutpoint) (
|
||||
func (css *consensusStateStore) utxoByOutpointFromStagedVirtualUTXOSet(outpoint *externalapi.DomainOutpoint) (
|
||||
*externalapi.UTXOEntry, error) {
|
||||
if utxoEntry, ok := c.stagedVirtualUTXOSet[*outpoint]; ok {
|
||||
if utxoEntry, ok := css.virtualUTXOSetStaging[*outpoint]; ok {
|
||||
return utxoEntry, nil
|
||||
}
|
||||
|
||||
return nil, errors.Errorf("outpoint was not found")
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) HasUTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (bool, error) {
|
||||
if c.stagedVirtualUTXOSet != nil {
|
||||
return c.hasUTXOByOutpointFromStagedVirtualUTXOSet(outpoint), nil
|
||||
func (css *consensusStateStore) HasUTXOByOutpoint(dbContext model.DBReader, outpoint *externalapi.DomainOutpoint) (bool, error) {
|
||||
if css.virtualUTXOSetStaging != nil {
|
||||
return css.hasUTXOByOutpointFromStagedVirtualUTXOSet(outpoint), nil
|
||||
}
|
||||
|
||||
return c.hasUTXOByOutpointFromStagedVirtualUTXODiff(dbContext, outpoint)
|
||||
return css.hasUTXOByOutpointFromStagedVirtualUTXODiff(dbContext, outpoint)
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXODiff(dbContext model.DBReader,
|
||||
func (css *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXODiff(dbContext model.DBReader,
|
||||
outpoint *externalapi.DomainOutpoint) (bool, error) {
|
||||
if _, ok := c.stagedVirtualUTXODiff.ToRemove[*outpoint]; ok {
|
||||
return false, nil
|
||||
}
|
||||
if _, ok := c.stagedVirtualUTXODiff.ToAdd[*outpoint]; ok {
|
||||
return true, nil
|
||||
|
||||
if css.virtualUTXODiffStaging != nil {
|
||||
if _, ok := css.virtualUTXODiffStaging.ToRemove[*outpoint]; ok {
|
||||
return false, nil
|
||||
}
|
||||
if _, ok := css.virtualUTXODiffStaging.ToAdd[*outpoint]; ok {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
key, err := utxoKey(outpoint)
|
||||
@@ -158,25 +162,30 @@ func (c *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXODiff(dbConte
|
||||
return dbContext.Has(key)
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXOSet(outpoint *externalapi.DomainOutpoint) bool {
|
||||
_, ok := c.stagedVirtualUTXOSet[*outpoint]
|
||||
func (css *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXOSet(outpoint *externalapi.DomainOutpoint) bool {
|
||||
_, ok := css.virtualUTXOSetStaging[*outpoint]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) VirtualUTXOSetIterator(dbContext model.DBReader) (model.ReadOnlyUTXOSetIterator, error) {
|
||||
func (css *consensusStateStore) VirtualUTXOSetIterator(dbContext model.DBReader) (model.ReadOnlyUTXOSetIterator, error) {
|
||||
cursor, err := dbContext.Cursor(utxoSetBucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newUTXOSetIterator(cursor), nil
|
||||
mainIterator := newCursorUTXOSetIterator(cursor)
|
||||
if css.virtualUTXODiffStaging != nil {
|
||||
return utxo.IteratorWithDiff(mainIterator, css.virtualUTXODiffStaging)
|
||||
}
|
||||
|
||||
return mainIterator, nil
|
||||
}
|
||||
|
||||
type utxoSetIterator struct {
|
||||
cursor model.DBCursor
|
||||
}
|
||||
|
||||
func newUTXOSetIterator(cursor model.DBCursor) model.ReadOnlyUTXOSetIterator {
|
||||
func newCursorUTXOSetIterator(cursor model.DBCursor) model.ReadOnlyUTXOSetIterator {
|
||||
return &utxoSetIterator{cursor: cursor}
|
||||
}
|
||||
|
||||
@@ -208,22 +217,22 @@ func (u utxoSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry
|
||||
return outpoint, utxoEntry, nil
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) StageVirtualUTXOSet(virtualUTXOSetIterator model.ReadOnlyUTXOSetIterator) error {
|
||||
if c.stagedVirtualUTXODiff != nil {
|
||||
func (css *consensusStateStore) StageVirtualUTXOSet(virtualUTXOSetIterator model.ReadOnlyUTXOSetIterator) error {
|
||||
if css.virtualUTXODiffStaging != nil {
|
||||
return errors.New("cannot stage virtual UTXO set while virtual UTXO diff is staged")
|
||||
}
|
||||
|
||||
c.stagedVirtualUTXOSet = make(model.UTXOCollection)
|
||||
css.virtualUTXOSetStaging = make(model.UTXOCollection)
|
||||
for virtualUTXOSetIterator.Next() {
|
||||
outpoint, entry, err := virtualUTXOSetIterator.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, exists := c.stagedVirtualUTXOSet[*outpoint]; exists {
|
||||
if _, exists := css.virtualUTXOSetStaging[*outpoint]; exists {
|
||||
return errors.Errorf("outpoint %s is found more than once in the given iterator", outpoint)
|
||||
}
|
||||
c.stagedVirtualUTXOSet[*outpoint] = entry
|
||||
css.virtualUTXOSetStaging[*outpoint] = entry
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
package consensusstatestore
|
||||
|
||||
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"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
)
|
||||
|
||||
var virtualDiffParentsKey = dbkeys.MakeBucket().Key([]byte("virtual-diff-parents"))
|
||||
|
||||
func (c *consensusStateStore) VirtualDiffParents(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
|
||||
if c.stagedVirtualDiffParents != nil {
|
||||
return c.stagedVirtualDiffParents, nil
|
||||
func (css *consensusStateStore) VirtualDiffParents(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
|
||||
if css.virtualDiffParentsStaging != nil {
|
||||
return externalapi.CloneHashes(css.virtualDiffParentsStaging), nil
|
||||
}
|
||||
|
||||
if css.virtualDiffParentsCache != nil {
|
||||
return externalapi.CloneHashes(css.virtualDiffParentsCache), nil
|
||||
}
|
||||
|
||||
virtualDiffParentsBytes, err := dbContext.Get(virtualDiffParentsKey)
|
||||
@@ -19,20 +24,51 @@ func (c *consensusStateStore) VirtualDiffParents(dbContext model.DBReader) ([]*e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hashes.DeserializeHashSlice(virtualDiffParentsBytes)
|
||||
virtualDiffParents, err := css.deserializeVirtualDiffParents(virtualDiffParentsBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
css.virtualDiffParentsCache = virtualDiffParents
|
||||
return externalapi.CloneHashes(virtualDiffParents), nil
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) StageVirtualDiffParents(tipHashes []*externalapi.DomainHash) {
|
||||
c.stagedVirtualDiffParents = tipHashes
|
||||
func (css *consensusStateStore) StageVirtualDiffParents(tipHashes []*externalapi.DomainHash) {
|
||||
css.virtualDiffParentsStaging = externalapi.CloneHashes(tipHashes)
|
||||
}
|
||||
|
||||
func (c *consensusStateStore) commitVirtualDiffParents(dbTx model.DBTransaction) error {
|
||||
virtualDiffParentsBytes := hashes.SerializeHashSlice(c.stagedVirtualDiffParents)
|
||||
func (css *consensusStateStore) commitVirtualDiffParents(dbTx model.DBTransaction) error {
|
||||
if css.virtualDiffParentsStaging == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := dbTx.Put(virtualDiffParentsKey, virtualDiffParentsBytes)
|
||||
virtualDiffParentsBytes, err := css.serializeVirtualDiffParents(css.virtualDiffParentsStaging)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(virtualDiffParentsKey, virtualDiffParentsBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
css.virtualDiffParentsCache = css.virtualDiffParentsStaging
|
||||
|
||||
// Note: we don't discard the staging here since that's
|
||||
// being done at the end of Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (css *consensusStateStore) serializeVirtualDiffParents(virtualDiffParentsBytes []*externalapi.DomainHash) ([]byte, error) {
|
||||
virtualDiffParents := serialization.VirtualDiffParentsToDBHeaderVirtualDiffParents(virtualDiffParentsBytes)
|
||||
return proto.Marshal(virtualDiffParents)
|
||||
}
|
||||
|
||||
func (css *consensusStateStore) deserializeVirtualDiffParents(virtualDiffParentsBytes []byte) ([]*externalapi.DomainHash,
|
||||
error) {
|
||||
|
||||
dbVirtualDiffParents := &serialization.DbVirtualDiffParents{}
|
||||
err := proto.Unmarshal(virtualDiffParentsBytes, dbVirtualDiffParents)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return serialization.DBVirtualDiffParentsToVirtualDiffParents(dbVirtualDiffParents)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
|
||||
)
|
||||
|
||||
var bucket = dbkeys.MakeBucket([]byte("block-ghostdag-data"))
|
||||
@@ -13,18 +14,20 @@ var bucket = dbkeys.MakeBucket([]byte("block-ghostdag-data"))
|
||||
// ghostdagDataStore represents a store of BlockGHOSTDAGData
|
||||
type ghostdagDataStore struct {
|
||||
staging map[externalapi.DomainHash]*model.BlockGHOSTDAGData
|
||||
cache *lrucache.LRUCache
|
||||
}
|
||||
|
||||
// New instantiates a new GHOSTDAGDataStore
|
||||
func New() model.GHOSTDAGDataStore {
|
||||
func New(cacheSize int) model.GHOSTDAGDataStore {
|
||||
return &ghostdagDataStore{
|
||||
staging: make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Stage stages the given blockGHOSTDAGData for the given blockHash
|
||||
func (gds *ghostdagDataStore) Stage(blockHash *externalapi.DomainHash, blockGHOSTDAGData *model.BlockGHOSTDAGData) {
|
||||
gds.staging[*blockHash] = blockGHOSTDAGData
|
||||
gds.staging[*blockHash] = blockGHOSTDAGData.Clone()
|
||||
}
|
||||
|
||||
func (gds *ghostdagDataStore) IsStaged() bool {
|
||||
@@ -41,11 +44,11 @@ func (gds *ghostdagDataStore) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = dbTx.Put(gds.hashAsKey(&hash), blockGhostdagDataBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gds.cache.Add(&hash, blockGHOSTDAGData)
|
||||
}
|
||||
|
||||
gds.Discard()
|
||||
@@ -55,7 +58,11 @@ func (gds *ghostdagDataStore) Commit(dbTx model.DBTransaction) error {
|
||||
// Get gets the blockGHOSTDAGData associated with the given blockHash
|
||||
func (gds *ghostdagDataStore) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*model.BlockGHOSTDAGData, error) {
|
||||
if blockGHOSTDAGData, ok := gds.staging[*blockHash]; ok {
|
||||
return blockGHOSTDAGData, nil
|
||||
return blockGHOSTDAGData.Clone(), nil
|
||||
}
|
||||
|
||||
if blockGHOSTDAGData, ok := gds.cache.Get(blockHash); ok {
|
||||
return blockGHOSTDAGData.(*model.BlockGHOSTDAGData).Clone(), nil
|
||||
}
|
||||
|
||||
blockGHOSTDAGDataBytes, err := dbContext.Get(gds.hashAsKey(blockHash))
|
||||
@@ -63,7 +70,12 @@ func (gds *ghostdagDataStore) Get(dbContext model.DBReader, blockHash *externala
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gds.deserializeBlockGHOSTDAGData(blockGHOSTDAGDataBytes)
|
||||
blockGHOSTDAGData, err := gds.deserializeBlockGHOSTDAGData(blockGHOSTDAGDataBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gds.cache.Add(blockHash, blockGHOSTDAGData)
|
||||
return blockGHOSTDAGData.Clone(), nil
|
||||
}
|
||||
|
||||
func (gds *ghostdagDataStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
|
||||
|
||||
@@ -12,50 +12,64 @@ var headerTipsKey = dbkeys.MakeBucket().Key([]byte("header-tips"))
|
||||
|
||||
type headerTipsStore struct {
|
||||
staging []*externalapi.DomainHash
|
||||
cache []*externalapi.DomainHash
|
||||
}
|
||||
|
||||
func (h *headerTipsStore) HasTips(dbContext model.DBReader) (bool, error) {
|
||||
if h.staging != nil {
|
||||
return len(h.staging) > 0, nil
|
||||
// New instantiates a new HeaderTipsStore
|
||||
func New() model.HeaderTipsStore {
|
||||
return &headerTipsStore{}
|
||||
}
|
||||
|
||||
func (hts *headerTipsStore) HasTips(dbContext model.DBReader) (bool, error) {
|
||||
if len(hts.staging) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if len(hts.cache) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return dbContext.Has(headerTipsKey)
|
||||
}
|
||||
|
||||
func (h *headerTipsStore) Discard() {
|
||||
h.staging = nil
|
||||
func (hts *headerTipsStore) Discard() {
|
||||
hts.staging = nil
|
||||
}
|
||||
|
||||
func (h *headerTipsStore) Commit(dbTx model.DBTransaction) error {
|
||||
if h.staging == nil {
|
||||
func (hts *headerTipsStore) Commit(dbTx model.DBTransaction) error {
|
||||
if hts.staging == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
tipsBytes, err := h.serializeTips(h.staging)
|
||||
tipsBytes, err := hts.serializeTips(hts.staging)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = dbTx.Put(headerTipsKey, tipsBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hts.cache = hts.staging
|
||||
|
||||
h.Discard()
|
||||
hts.Discard()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *headerTipsStore) Stage(tips []*externalapi.DomainHash) {
|
||||
h.staging = tips
|
||||
func (hts *headerTipsStore) Stage(tips []*externalapi.DomainHash) {
|
||||
hts.staging = externalapi.CloneHashes(tips)
|
||||
}
|
||||
|
||||
func (h *headerTipsStore) IsStaged() bool {
|
||||
return h.staging != nil
|
||||
func (hts *headerTipsStore) IsStaged() bool {
|
||||
return hts.staging != nil
|
||||
}
|
||||
|
||||
func (h *headerTipsStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
|
||||
if h.staging != nil {
|
||||
return h.staging, nil
|
||||
func (hts *headerTipsStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
|
||||
if hts.staging != nil {
|
||||
return externalapi.CloneHashes(hts.staging), nil
|
||||
}
|
||||
|
||||
if hts.cache != nil {
|
||||
return externalapi.CloneHashes(hts.cache), nil
|
||||
}
|
||||
|
||||
tipsBytes, err := dbContext.Get(headerTipsKey)
|
||||
@@ -63,25 +77,25 @@ func (h *headerTipsStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainH
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return h.deserializeTips(tipsBytes)
|
||||
tips, err := hts.deserializeTips(tipsBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hts.cache = tips
|
||||
return externalapi.CloneHashes(tips), nil
|
||||
}
|
||||
|
||||
func (h *headerTipsStore) serializeTips(tips []*externalapi.DomainHash) ([]byte, error) {
|
||||
func (hts *headerTipsStore) serializeTips(tips []*externalapi.DomainHash) ([]byte, error) {
|
||||
dbTips := serialization.HeaderTipsToDBHeaderTips(tips)
|
||||
return proto.Marshal(dbTips)
|
||||
}
|
||||
|
||||
func (h *headerTipsStore) deserializeTips(tipsBytes []byte) ([]*externalapi.DomainHash, error) {
|
||||
func (hts *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{}
|
||||
return serialization.DBHeaderTipsToHeaderTips(dbTips)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
|
||||
)
|
||||
|
||||
var bucket = dbkeys.MakeBucket([]byte("multisets"))
|
||||
@@ -14,19 +15,21 @@ var bucket = dbkeys.MakeBucket([]byte("multisets"))
|
||||
type multisetStore struct {
|
||||
staging map[externalapi.DomainHash]model.Multiset
|
||||
toDelete map[externalapi.DomainHash]struct{}
|
||||
cache *lrucache.LRUCache
|
||||
}
|
||||
|
||||
// New instantiates a new MultisetStore
|
||||
func New() model.MultisetStore {
|
||||
func New(cacheSize int) model.MultisetStore {
|
||||
return &multisetStore{
|
||||
staging: make(map[externalapi.DomainHash]model.Multiset),
|
||||
toDelete: make(map[externalapi.DomainHash]struct{}),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Stage stages the given multiset for the given blockHash
|
||||
func (ms *multisetStore) Stage(blockHash *externalapi.DomainHash, multiset model.Multiset) {
|
||||
ms.staging[*blockHash] = multiset
|
||||
ms.staging[*blockHash] = multiset.Clone()
|
||||
}
|
||||
|
||||
func (ms *multisetStore) IsStaged() bool {
|
||||
@@ -44,11 +47,11 @@ func (ms *multisetStore) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = dbTx.Put(ms.hashAsKey(&hash), multisetBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ms.cache.Add(&hash, multiset)
|
||||
}
|
||||
|
||||
for hash := range ms.toDelete {
|
||||
@@ -56,6 +59,7 @@ func (ms *multisetStore) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ms.cache.Remove(&hash)
|
||||
}
|
||||
|
||||
ms.Discard()
|
||||
@@ -65,7 +69,11 @@ func (ms *multisetStore) Commit(dbTx model.DBTransaction) error {
|
||||
// Get gets the multiset associated with the given blockHash
|
||||
func (ms *multisetStore) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (model.Multiset, error) {
|
||||
if multiset, ok := ms.staging[*blockHash]; ok {
|
||||
return multiset, nil
|
||||
return multiset.Clone(), nil
|
||||
}
|
||||
|
||||
if multiset, ok := ms.cache.Get(blockHash); ok {
|
||||
return multiset.(model.Multiset).Clone(), nil
|
||||
}
|
||||
|
||||
multisetBytes, err := dbContext.Get(ms.hashAsKey(blockHash))
|
||||
@@ -73,7 +81,12 @@ func (ms *multisetStore) Get(dbContext model.DBReader, blockHash *externalapi.Do
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ms.deserializeMultiset(multisetBytes)
|
||||
multiset, err := ms.deserializeMultiset(multisetBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ms.cache.Add(blockHash, multiset)
|
||||
return multiset.Clone(), nil
|
||||
}
|
||||
|
||||
// Delete deletes the multiset associated with the given blockHash
|
||||
|
||||
@@ -15,19 +15,17 @@ var pruningSerializedUTXOSetkey = dbkeys.MakeBucket().Key([]byte("pruning-utxo-s
|
||||
type pruningStore struct {
|
||||
pruningPointStaging *externalapi.DomainHash
|
||||
serializedUTXOSetStaging []byte
|
||||
pruningPointCache *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// New instantiates a new PruningStore
|
||||
func New() model.PruningStore {
|
||||
return &pruningStore{
|
||||
pruningPointStaging: nil,
|
||||
serializedUTXOSetStaging: nil,
|
||||
}
|
||||
return &pruningStore{}
|
||||
}
|
||||
|
||||
// Stage stages the pruning state
|
||||
func (ps *pruningStore) Stage(pruningPointBlockHash *externalapi.DomainHash, pruningPointUTXOSetBytes []byte) {
|
||||
ps.pruningPointStaging = pruningPointBlockHash
|
||||
ps.pruningPointStaging = pruningPointBlockHash.Clone()
|
||||
ps.serializedUTXOSetStaging = pruningPointUTXOSetBytes
|
||||
}
|
||||
|
||||
@@ -50,6 +48,7 @@ func (ps *pruningStore) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ps.pruningPointCache = ps.pruningPointStaging
|
||||
}
|
||||
|
||||
if ps.serializedUTXOSetStaging != nil {
|
||||
@@ -57,7 +56,6 @@ func (ps *pruningStore) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = dbTx.Put(pruningSerializedUTXOSetkey, utxoSetBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -74,16 +72,21 @@ func (ps *pruningStore) PruningPoint(dbContext model.DBReader) (*externalapi.Dom
|
||||
return ps.pruningPointStaging, nil
|
||||
}
|
||||
|
||||
blockHashBytes, err := dbContext.Get(pruningBlockHashKey)
|
||||
if ps.pruningPointCache != nil {
|
||||
return ps.pruningPointCache, nil
|
||||
}
|
||||
|
||||
pruningPointBytes, err := dbContext.Get(pruningBlockHashKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockHash, err := ps.deserializePruningPoint(blockHashBytes)
|
||||
pruningPoint, err := ps.deserializePruningPoint(pruningPointBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return blockHash, nil
|
||||
ps.pruningPointCache = pruningPoint
|
||||
return pruningPoint, nil
|
||||
}
|
||||
|
||||
// PruningPointSerializedUTXOSet returns the serialized UTXO set of the current pruning point
|
||||
@@ -91,7 +94,17 @@ func (ps *pruningStore) PruningPointSerializedUTXOSet(dbContext model.DBReader)
|
||||
if ps.serializedUTXOSetStaging != nil {
|
||||
return ps.serializedUTXOSetStaging, nil
|
||||
}
|
||||
return dbContext.Get(pruningSerializedUTXOSetkey)
|
||||
|
||||
dbPruningPointUTXOSetBytes, err := dbContext.Get(pruningSerializedUTXOSetkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pruningPointUTXOSet, err := ps.deserializeUTXOSetBytes(dbPruningPointUTXOSetBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pruningPointUTXOSet, nil
|
||||
}
|
||||
|
||||
func (ps *pruningStore) serializePruningPoint(pruningPoint *externalapi.DomainHash) ([]byte, error) {
|
||||
@@ -127,5 +140,9 @@ func (ps *pruningStore) HasPruningPoint(dbContext model.DBReader) (bool, error)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if ps.pruningPointCache != nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return dbContext.Has(pruningBlockHashKey)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
|
||||
)
|
||||
|
||||
var reachabilityDataBucket = dbkeys.MakeBucket([]byte("reachability-data"))
|
||||
@@ -15,19 +16,22 @@ var reachabilityReindexRootKey = dbkeys.MakeBucket().Key([]byte("reachability-re
|
||||
type reachabilityDataStore struct {
|
||||
reachabilityDataStaging map[externalapi.DomainHash]*model.ReachabilityData
|
||||
reachabilityReindexRootStaging *externalapi.DomainHash
|
||||
reachabilityDataCache *lrucache.LRUCache
|
||||
reachabilityReindexRootCache *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// New instantiates a new ReachabilityDataStore
|
||||
func New() model.ReachabilityDataStore {
|
||||
func New(cacheSize int) model.ReachabilityDataStore {
|
||||
return &reachabilityDataStore{
|
||||
reachabilityDataStaging: make(map[externalapi.DomainHash]*model.ReachabilityData),
|
||||
reachabilityReindexRootStaging: nil,
|
||||
reachabilityDataStaging: make(map[externalapi.DomainHash]*model.ReachabilityData),
|
||||
reachabilityDataCache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
// StageReachabilityData stages the given reachabilityData for the given blockHash
|
||||
func (rds *reachabilityDataStore) StageReachabilityData(blockHash *externalapi.DomainHash, reachabilityData *model.ReachabilityData) {
|
||||
rds.reachabilityDataStaging[*blockHash] = reachabilityData
|
||||
func (rds *reachabilityDataStore) StageReachabilityData(blockHash *externalapi.DomainHash,
|
||||
reachabilityData *model.ReachabilityData) {
|
||||
rds.reachabilityDataStaging[*blockHash] = reachabilityData.Clone()
|
||||
}
|
||||
|
||||
// StageReachabilityReindexRoot stages the given reachabilityReindexRoot
|
||||
@@ -50,22 +54,22 @@ func (rds *reachabilityDataStore) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = dbTx.Put(reachabilityReindexRootKey, reachabilityReindexRootBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rds.reachabilityReindexRootCache = rds.reachabilityReindexRootStaging
|
||||
}
|
||||
for hash, reachabilityData := range rds.reachabilityDataStaging {
|
||||
reachabilityDataBytes, err := rds.serializeReachabilityData(reachabilityData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = dbTx.Put(rds.reachabilityDataBlockHashAsKey(&hash), reachabilityDataBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rds.reachabilityDataCache.Add(&hash, reachabilityData)
|
||||
}
|
||||
|
||||
rds.Discard()
|
||||
@@ -77,7 +81,11 @@ func (rds *reachabilityDataStore) ReachabilityData(dbContext model.DBReader,
|
||||
blockHash *externalapi.DomainHash) (*model.ReachabilityData, error) {
|
||||
|
||||
if reachabilityData, ok := rds.reachabilityDataStaging[*blockHash]; ok {
|
||||
return reachabilityData, nil
|
||||
return reachabilityData.Clone(), nil
|
||||
}
|
||||
|
||||
if reachabilityData, ok := rds.reachabilityDataCache.Get(blockHash); ok {
|
||||
return reachabilityData.(*model.ReachabilityData).Clone(), nil
|
||||
}
|
||||
|
||||
reachabilityDataBytes, err := dbContext.Get(rds.reachabilityDataBlockHashAsKey(blockHash))
|
||||
@@ -85,7 +93,12 @@ func (rds *reachabilityDataStore) ReachabilityData(dbContext model.DBReader,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rds.deserializeReachabilityData(reachabilityDataBytes)
|
||||
reachabilityData, err := rds.deserializeReachabilityData(reachabilityDataBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rds.reachabilityDataCache.Add(blockHash, reachabilityData)
|
||||
return reachabilityData.Clone(), nil
|
||||
}
|
||||
|
||||
func (rds *reachabilityDataStore) HasReachabilityData(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
|
||||
@@ -93,6 +106,10 @@ func (rds *reachabilityDataStore) HasReachabilityData(dbContext model.DBReader,
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if rds.reachabilityDataCache.Has(blockHash) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return dbContext.Has(rds.reachabilityDataBlockHashAsKey(blockHash))
|
||||
}
|
||||
|
||||
@@ -101,6 +118,11 @@ func (rds *reachabilityDataStore) ReachabilityReindexRoot(dbContext model.DBRead
|
||||
if rds.reachabilityReindexRootStaging != nil {
|
||||
return rds.reachabilityReindexRootStaging, nil
|
||||
}
|
||||
|
||||
if rds.reachabilityReindexRootCache != nil {
|
||||
return rds.reachabilityReindexRootCache, nil
|
||||
}
|
||||
|
||||
reachabilityReindexRootBytes, err := dbContext.Get(reachabilityReindexRootKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -110,6 +132,7 @@ func (rds *reachabilityDataStore) ReachabilityReindexRoot(dbContext model.DBRead
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rds.reachabilityReindexRootCache = reachabilityReindexRoot
|
||||
return reachabilityReindexRoot, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var utxoDiffBucket = dbkeys.MakeBucket([]byte("utxo-diffs"))
|
||||
@@ -16,21 +18,28 @@ type utxoDiffStore struct {
|
||||
utxoDiffStaging map[externalapi.DomainHash]*model.UTXODiff
|
||||
utxoDiffChildStaging map[externalapi.DomainHash]*externalapi.DomainHash
|
||||
toDelete map[externalapi.DomainHash]struct{}
|
||||
utxoDiffCache *lrucache.LRUCache
|
||||
utxoDiffChildCache *lrucache.LRUCache
|
||||
}
|
||||
|
||||
// New instantiates a new UTXODiffStore
|
||||
func New() model.UTXODiffStore {
|
||||
func New(cacheSize int) model.UTXODiffStore {
|
||||
return &utxoDiffStore{
|
||||
utxoDiffStaging: make(map[externalapi.DomainHash]*model.UTXODiff),
|
||||
utxoDiffChildStaging: make(map[externalapi.DomainHash]*externalapi.DomainHash),
|
||||
toDelete: make(map[externalapi.DomainHash]struct{}),
|
||||
utxoDiffCache: lrucache.New(cacheSize),
|
||||
utxoDiffChildCache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Stage stages the given utxoDiff for the given blockHash
|
||||
func (uds *utxoDiffStore) Stage(blockHash *externalapi.DomainHash, utxoDiff *model.UTXODiff, utxoDiffChild *externalapi.DomainHash) {
|
||||
uds.utxoDiffStaging[*blockHash] = utxoDiff
|
||||
uds.utxoDiffChildStaging[*blockHash] = utxoDiffChild
|
||||
uds.utxoDiffStaging[*blockHash] = utxoDiff.Clone()
|
||||
|
||||
if utxoDiffChild != nil {
|
||||
uds.utxoDiffChildStaging[*blockHash] = utxoDiffChild.Clone()
|
||||
}
|
||||
}
|
||||
|
||||
func (uds *utxoDiffStore) IsStaged() bool {
|
||||
@@ -57,22 +66,26 @@ func (uds *utxoDiffStore) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = dbTx.Put(uds.utxoDiffHashAsKey(&hash), utxoDiffBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uds.utxoDiffCache.Add(&hash, utxoDiff)
|
||||
}
|
||||
for hash, utxoDiffChild := range uds.utxoDiffChildStaging {
|
||||
if utxoDiffChild == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
utxoDiffChildBytes, err := uds.serializeUTXODiffChild(utxoDiffChild)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = dbTx.Put(uds.utxoDiffHashAsKey(&hash), utxoDiffChildBytes)
|
||||
err = dbTx.Put(uds.utxoDiffChildHashAsKey(&hash), utxoDiffChildBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uds.utxoDiffChildCache.Add(&hash, utxoDiffChild)
|
||||
}
|
||||
|
||||
for hash := range uds.toDelete {
|
||||
@@ -80,11 +93,13 @@ func (uds *utxoDiffStore) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uds.utxoDiffCache.Remove(&hash)
|
||||
|
||||
err = dbTx.Delete(uds.utxoDiffChildHashAsKey(&hash))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uds.utxoDiffChildCache.Remove(&hash)
|
||||
}
|
||||
|
||||
uds.Discard()
|
||||
@@ -94,7 +109,11 @@ func (uds *utxoDiffStore) Commit(dbTx model.DBTransaction) error {
|
||||
// UTXODiff gets the utxoDiff associated with the given blockHash
|
||||
func (uds *utxoDiffStore) UTXODiff(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*model.UTXODiff, error) {
|
||||
if utxoDiff, ok := uds.utxoDiffStaging[*blockHash]; ok {
|
||||
return utxoDiff, nil
|
||||
return utxoDiff.Clone(), nil
|
||||
}
|
||||
|
||||
if utxoDiff, ok := uds.utxoDiffCache.Get(blockHash); ok {
|
||||
return utxoDiff.(*model.UTXODiff).Clone(), nil
|
||||
}
|
||||
|
||||
utxoDiffBytes, err := dbContext.Get(uds.utxoDiffHashAsKey(blockHash))
|
||||
@@ -102,13 +121,22 @@ func (uds *utxoDiffStore) UTXODiff(dbContext model.DBReader, blockHash *external
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return uds.deserializeUTXODiff(utxoDiffBytes)
|
||||
utxoDiff, err := uds.deserializeUTXODiff(utxoDiffBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uds.utxoDiffCache.Add(blockHash, utxoDiff)
|
||||
return utxoDiff.Clone(), nil
|
||||
}
|
||||
|
||||
// UTXODiffChild gets the utxoDiff child associated with the given blockHash
|
||||
func (uds *utxoDiffStore) UTXODiffChild(dbContext model.DBReader, blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
|
||||
if utxoDiffChild, ok := uds.utxoDiffChildStaging[*blockHash]; ok {
|
||||
return utxoDiffChild, nil
|
||||
return utxoDiffChild.Clone(), nil
|
||||
}
|
||||
|
||||
if utxoDiffChild, ok := uds.utxoDiffChildCache.Get(blockHash); ok {
|
||||
return utxoDiffChild.(*externalapi.DomainHash).Clone(), nil
|
||||
}
|
||||
|
||||
utxoDiffChildBytes, err := dbContext.Get(uds.utxoDiffChildHashAsKey(blockHash))
|
||||
@@ -120,7 +148,8 @@ func (uds *utxoDiffStore) UTXODiffChild(dbContext model.DBReader, blockHash *ext
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return utxoDiffChild, nil
|
||||
uds.utxoDiffChildCache.Add(blockHash, utxoDiffChild)
|
||||
return utxoDiffChild.Clone(), nil
|
||||
}
|
||||
|
||||
// HasUTXODiffChild returns true if the given blockHash has a UTXODiffChild
|
||||
@@ -129,6 +158,10 @@ func (uds *utxoDiffStore) HasUTXODiffChild(dbContext model.DBReader, blockHash *
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if uds.utxoDiffChildCache.Has(blockHash) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return dbContext.Has(uds.utxoDiffChildHashAsKey(blockHash))
|
||||
}
|
||||
|
||||
@@ -155,28 +188,37 @@ func (uds *utxoDiffStore) utxoDiffChildHashAsKey(hash *externalapi.DomainHash) m
|
||||
}
|
||||
|
||||
func (uds *utxoDiffStore) serializeUTXODiff(utxoDiff *model.UTXODiff) ([]byte, error) {
|
||||
return proto.Marshal(serialization.UTXODiffToDBUTXODiff(utxoDiff))
|
||||
dbUtxoDiff := serialization.UTXODiffToDBUTXODiff(utxoDiff)
|
||||
bytes, err := proto.Marshal(dbUtxoDiff)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func (uds *utxoDiffStore) deserializeUTXODiff(utxoDiffBytes []byte) (*model.UTXODiff, error) {
|
||||
dbUTXODiff := &serialization.DbUtxoDiff{}
|
||||
err := proto.Unmarshal(utxoDiffBytes, dbUTXODiff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return serialization.DBUTXODiffToUTXODiff(dbUTXODiff)
|
||||
}
|
||||
|
||||
func (uds *utxoDiffStore) serializeUTXODiffChild(utxoDiffChild *externalapi.DomainHash) ([]byte, error) {
|
||||
return proto.Marshal(serialization.DomainHashToDbHash(utxoDiffChild))
|
||||
bytes, err := proto.Marshal(serialization.DomainHashToDbHash(utxoDiffChild))
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func (uds *utxoDiffStore) deserializeUTXODiffChild(utxoDiffChildBytes []byte) (*externalapi.DomainHash, error) {
|
||||
dbHash := &serialization.DbHash{}
|
||||
err := proto.Unmarshal(utxoDiffChildBytes, dbHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return serialization.DbHashToDomainHash(dbHash)
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtraversalmanager"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
|
||||
|
||||
consensusdatabase "github.com/kaspanet/kaspad/domain/consensus/database"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/acceptancedatastore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/blockheaderstore"
|
||||
@@ -16,12 +24,13 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/utxodiffstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/blockbuilder"
|
||||
"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"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/difficultymanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/headertipsmanager"
|
||||
@@ -37,34 +46,48 @@ import (
|
||||
|
||||
// Factory instantiates new Consensuses
|
||||
type Factory interface {
|
||||
NewConsensus(dagParams *dagconfig.Params, db infrastructuredatabase.Database) (Consensus, error)
|
||||
NewConsensus(dagParams *dagconfig.Params, db infrastructuredatabase.Database) (externalapi.Consensus, error)
|
||||
NewTestConsensus(dagParams *dagconfig.Params, testName string) (tc testapi.TestConsensus, teardown func(), err error)
|
||||
NewTestConsensusWithDataDir(dagParams *dagconfig.Params, dataDir string) (
|
||||
tc testapi.TestConsensus, teardown func(), err error)
|
||||
}
|
||||
|
||||
type factory struct{}
|
||||
|
||||
// NewConsensus instantiates a new Consensus
|
||||
func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredatabase.Database) (Consensus, error) {
|
||||
// Data Structures
|
||||
acceptanceDataStore := acceptancedatastore.New()
|
||||
blockStore := blockstore.New()
|
||||
blockHeaderStore := blockheaderstore.New()
|
||||
blockRelationStore := blockrelationstore.New()
|
||||
blockStatusStore := blockstatusstore.New()
|
||||
multisetStore := multisetstore.New()
|
||||
pruningStore := pruningstore.New()
|
||||
reachabilityDataStore := reachabilitydatastore.New()
|
||||
utxoDiffStore := utxodiffstore.New()
|
||||
consensusStateStore := consensusstatestore.New()
|
||||
ghostdagDataStore := ghostdagdatastore.New()
|
||||
headerTipsStore := headertipsstore.New()
|
||||
// NewFactory creates a new Consensus factory
|
||||
func NewFactory() Factory {
|
||||
return &factory{}
|
||||
}
|
||||
|
||||
// NewConsensus instantiates a new Consensus
|
||||
func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredatabase.Database) (externalapi.Consensus, error) {
|
||||
dbManager := consensusdatabase.New(db)
|
||||
|
||||
// Data Structures
|
||||
storeCacheSize := 200
|
||||
acceptanceDataStore := acceptancedatastore.New(storeCacheSize)
|
||||
blockStore, err := blockstore.New(dbManager, storeCacheSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockHeaderStore, err := blockheaderstore.New(dbManager, storeCacheSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockRelationStore := blockrelationstore.New(storeCacheSize)
|
||||
blockStatusStore := blockstatusstore.New(storeCacheSize)
|
||||
multisetStore := multisetstore.New(storeCacheSize)
|
||||
pruningStore := pruningstore.New()
|
||||
reachabilityDataStore := reachabilitydatastore.New(storeCacheSize)
|
||||
utxoDiffStore := utxodiffstore.New(storeCacheSize)
|
||||
consensusStateStore := consensusstatestore.New()
|
||||
ghostdagDataStore := ghostdagdatastore.New(storeCacheSize)
|
||||
headerTipsStore := headertipsstore.New()
|
||||
|
||||
// Processes
|
||||
reachabilityManager := reachabilitymanager.New(
|
||||
dbManager,
|
||||
ghostdagDataStore,
|
||||
blockRelationStore,
|
||||
reachabilityDataStore)
|
||||
dagTopologyManager := dagtopologymanager.New(
|
||||
dbManager,
|
||||
@@ -84,8 +107,10 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
dagParams.TimestampDeviationTolerance,
|
||||
dbManager,
|
||||
dagTraversalManager,
|
||||
blockHeaderStore)
|
||||
blockHeaderStore,
|
||||
ghostdagDataStore)
|
||||
transactionValidator := transactionvalidator.New(dagParams.BlockCoinbaseMaturity,
|
||||
dagParams.EnableNonNativeSubnetworks,
|
||||
dbManager,
|
||||
pastMedianTimeManager,
|
||||
ghostdagDataStore)
|
||||
@@ -104,7 +129,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
ghostdagDataStore,
|
||||
acceptanceDataStore)
|
||||
headerTipsManager := headertipsmanager.New(dbManager, dagTopologyManager, ghostdagManager, headerTipsStore)
|
||||
genesisHash := (*externalapi.DomainHash)(dagParams.GenesisHash)
|
||||
genesisHash := dagParams.GenesisHash
|
||||
mergeDepthManager := mergedepthmanager.New(
|
||||
dagParams.FinalityDepth(),
|
||||
dbManager,
|
||||
@@ -113,7 +138,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
ghostdagDataStore)
|
||||
blockValidator := blockvalidator.New(
|
||||
dagParams.PowMax,
|
||||
false,
|
||||
dagParams.SkipProofOfWork,
|
||||
genesisHash,
|
||||
dagParams.EnableNonNativeSubnetworks,
|
||||
dagParams.DisableDifficultyAdjustment,
|
||||
@@ -138,6 +163,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
dbManager,
|
||||
dagParams.FinalityDepth(),
|
||||
dagParams.PruningDepth(),
|
||||
genesisHash,
|
||||
ghostdagManager,
|
||||
dagTopologyManager,
|
||||
dagTraversalManager,
|
||||
@@ -191,7 +217,21 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
ghostdagDataStore,
|
||||
blockStatusStore,
|
||||
blockHeaderStore,
|
||||
headerTipsStore)
|
||||
headerTipsStore,
|
||||
blockStore)
|
||||
|
||||
blockBuilder := blockbuilder.New(
|
||||
dbManager,
|
||||
difficultyManager,
|
||||
pastMedianTimeManager,
|
||||
coinbaseManager,
|
||||
consensusStateManager,
|
||||
ghostdagManager,
|
||||
acceptanceDataStore,
|
||||
blockRelationStore,
|
||||
multisetStore,
|
||||
ghostdagDataStore,
|
||||
)
|
||||
|
||||
blockProcessor := blockprocessor.New(
|
||||
dagParams,
|
||||
@@ -222,19 +262,38 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
headerTipsStore)
|
||||
|
||||
c := &consensus{
|
||||
lock: &sync.Mutex{},
|
||||
databaseContext: dbManager,
|
||||
|
||||
blockProcessor: blockProcessor,
|
||||
blockBuilder: blockBuilder,
|
||||
consensusStateManager: consensusStateManager,
|
||||
transactionValidator: transactionValidator,
|
||||
syncManager: syncManager,
|
||||
pastMedianTimeManager: pastMedianTimeManager,
|
||||
blockValidator: blockValidator,
|
||||
coinbaseManager: coinbaseManager,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
dagTraversalManager: dagTraversalManager,
|
||||
difficultyManager: difficultyManager,
|
||||
ghostdagManager: ghostdagManager,
|
||||
headerTipsManager: headerTipsManager,
|
||||
mergeDepthManager: mergeDepthManager,
|
||||
pruningManager: pruningManager,
|
||||
reachabilityManager: reachabilityManager,
|
||||
|
||||
blockStore: blockStore,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
pruningStore: pruningStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blockStore: blockStore,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
pruningStore: pruningStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
blockStatusStore: blockStatusStore,
|
||||
blockRelationStore: blockRelationStore,
|
||||
consensusStateStore: consensusStateStore,
|
||||
headerTipsStore: headerTipsStore,
|
||||
multisetStore: multisetStore,
|
||||
reachabilityDataStore: reachabilityDataStore,
|
||||
utxoDiffStore: utxoDiffStore,
|
||||
}
|
||||
|
||||
genesisInfo, err := c.GetBlockInfo(genesisHash)
|
||||
@@ -252,7 +311,44 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewFactory creates a new Consensus factory
|
||||
func NewFactory() Factory {
|
||||
return &factory{}
|
||||
func (f *factory) NewTestConsensus(dagParams *dagconfig.Params, testName string) (
|
||||
tc testapi.TestConsensus, teardown func(), err error) {
|
||||
|
||||
dataDir, err := ioutil.TempDir("", testName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return f.NewTestConsensusWithDataDir(dagParams, dataDir)
|
||||
}
|
||||
|
||||
func (f *factory) NewTestConsensusWithDataDir(dagParams *dagconfig.Params, dataDir string) (
|
||||
tc testapi.TestConsensus, teardown func(), err error) {
|
||||
|
||||
db, err := ldb.NewLevelDB(dataDir)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
consensusAsInterface, err := f.NewConsensus(dagParams, db)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
consensusAsImplementation := consensusAsInterface.(*consensus)
|
||||
|
||||
testConsensusStateManager := consensusstatemanager.NewTestConsensusStateManager(consensusAsImplementation.consensusStateManager)
|
||||
|
||||
tstConsensus := &testConsensus{
|
||||
consensus: consensusAsImplementation,
|
||||
testConsensusStateManager: testConsensusStateManager,
|
||||
testReachabilityManager: reachabilitymanager.NewTestReachabilityManager(consensusAsImplementation.
|
||||
reachabilityManager),
|
||||
}
|
||||
tstConsensus.testBlockBuilder = blockbuilder.NewTestBlockBuilder(consensusAsImplementation.blockBuilder, tstConsensus)
|
||||
teardown = func() {
|
||||
db.Close()
|
||||
os.RemoveAll(dataDir)
|
||||
}
|
||||
|
||||
return tstConsensus, teardown, nil
|
||||
}
|
||||
|
||||
420
domain/consensus/finality_test.go
Normal file
420
domain/consensus/finality_test.go
Normal file
@@ -0,0 +1,420 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFinality(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
// Set finalityInterval to 50 blocks, so that test runs quickly
|
||||
params.FinalityDuration = 50 * params.TargetTimePerBlock
|
||||
|
||||
factory := NewFactory()
|
||||
consensus, teardown, err := factory.NewTestConsensus(params, "TestFinality")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown()
|
||||
|
||||
buildAndInsertBlock := func(parentHashes []*externalapi.DomainHash) (*externalapi.DomainBlock, error) {
|
||||
block, _, err := consensus.BuildBlockWithParents(parentHashes, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = consensus.ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// Build a chain of `finalityInterval - 1` blocks
|
||||
finalityInterval := params.FinalityDepth()
|
||||
var mainChainTip *externalapi.DomainBlock
|
||||
mainChainTipHash := params.GenesisHash
|
||||
|
||||
for i := uint64(0); i < finalityInterval-1; i++ {
|
||||
mainChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{mainChainTipHash})
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed to process Block #%d: %v", i, err)
|
||||
}
|
||||
mainChainTipHash = consensusserialization.BlockHash(mainChainTip)
|
||||
|
||||
blockInfo, err := consensus.GetBlockInfo(mainChainTipHash)
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Block #%d failed to get info: %v", i, err)
|
||||
}
|
||||
if blockInfo.BlockStatus != externalapi.StatusValid {
|
||||
t.Fatalf("Block #%d in main chain expected to have status '%s', but got '%s'",
|
||||
i, externalapi.StatusValid, blockInfo.BlockStatus)
|
||||
}
|
||||
}
|
||||
|
||||
// Mine another chain of `finality-Interval - 2` blocks
|
||||
var sideChainTip *externalapi.DomainBlock
|
||||
sideChainTipHash := params.GenesisHash
|
||||
for i := uint64(0); i < finalityInterval-2; i++ {
|
||||
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
|
||||
}
|
||||
sideChainTipHash = consensusserialization.BlockHash(sideChainTip)
|
||||
|
||||
blockInfo, err := consensus.GetBlockInfo(sideChainTipHash)
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Block #%d failed to get info: %v", i, err)
|
||||
} else if !blockInfo.Exists {
|
||||
t.Fatalf("TestFinality: Failed getting block info, doesn't exists")
|
||||
}
|
||||
if blockInfo.BlockStatus != externalapi.StatusUTXOPendingVerification {
|
||||
t.Fatalf("Block #%d in side chain expected to have status '%s', but got '%s'",
|
||||
i, externalapi.StatusUTXOPendingVerification, blockInfo.BlockStatus)
|
||||
}
|
||||
}
|
||||
|
||||
// Add two more blocks in the side-chain until it becomes the selected chain
|
||||
for i := uint64(0); i < 2; i++ {
|
||||
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
|
||||
}
|
||||
sideChainTipHash = consensusserialization.BlockHash(sideChainTip)
|
||||
}
|
||||
|
||||
// Make sure that now the sideChainTip is valid and selectedTip
|
||||
blockInfo, err := consensus.GetBlockInfo(sideChainTipHash)
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed to get block info: %v", err)
|
||||
} else if !blockInfo.Exists {
|
||||
t.Fatalf("TestFinality: Failed getting block info, doesn't exists")
|
||||
}
|
||||
if blockInfo.BlockStatus != externalapi.StatusValid {
|
||||
t.Fatalf("TestFinality: Overtaking block in side-chain expected to have status '%s', but got '%s'",
|
||||
externalapi.StatusValid, blockInfo.BlockStatus)
|
||||
}
|
||||
selectedTip, err := consensus.GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed getting virtual selectedParent: %v", err)
|
||||
}
|
||||
if *consensusserialization.BlockHash(selectedTip) != *sideChainTipHash {
|
||||
t.Fatalf("Overtaking block in side-chain is not selectedTip")
|
||||
}
|
||||
|
||||
// Add two more blocks to main chain, to move finality point to first non-genesis block in mainChain
|
||||
for i := uint64(0); i < 2; i++ {
|
||||
mainChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{mainChainTipHash})
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
|
||||
}
|
||||
mainChainTipHash = consensusserialization.BlockHash(mainChainTip)
|
||||
}
|
||||
|
||||
virtualFinality, err := consensus.ConsensusStateManager().VirtualFinalityPoint()
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed getting the virtual's finality point: %v", err)
|
||||
}
|
||||
|
||||
if *virtualFinality == *params.GenesisHash {
|
||||
t.Fatalf("virtual's finalityPoint is still genesis after adding finalityInterval + 1 blocks to the main chain")
|
||||
}
|
||||
|
||||
// TODO: Make sure that a finality conflict notification is sent
|
||||
// Add two more blocks to the side chain, so that it violates finality and gets status UTXOPendingVerification even
|
||||
// though it is the block with the highest blue score.
|
||||
for i := uint64(0); i < 2; i++ {
|
||||
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
|
||||
}
|
||||
sideChainTipHash = consensusserialization.BlockHash(sideChainTip)
|
||||
}
|
||||
|
||||
// Check that sideChainTip hash higher blue score than the selected parent
|
||||
selectedTip, err = consensus.GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed getting virtual selectedParent: %v", err)
|
||||
}
|
||||
selectedTipGhostDagData, err := consensus.GHOSTDAGDataStore().Get(consensus.DatabaseContext(), consensusserialization.BlockHash(selectedTip))
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed getting the ghost dag data of the selected tip: %v", err)
|
||||
}
|
||||
|
||||
sideChainTipGhostDagData, err := consensus.GHOSTDAGDataStore().Get(consensus.DatabaseContext(), sideChainTipHash)
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed getting the ghost dag data of the sidechain tip: %v", err)
|
||||
}
|
||||
|
||||
if selectedTipGhostDagData.BlueScore > sideChainTipGhostDagData.BlueScore {
|
||||
t.Fatalf("sideChainTip is not the bluest tip when it is expected to be")
|
||||
}
|
||||
|
||||
// Blocks violating finality should have a UTXOPendingVerification status
|
||||
blockInfo, err = consensus.GetBlockInfo(sideChainTipHash)
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed to get block info: %v", err)
|
||||
} else if !blockInfo.Exists {
|
||||
t.Fatalf("TestFinality: Failed getting block info, doesn't exists")
|
||||
}
|
||||
if blockInfo.BlockStatus != externalapi.StatusUTXOPendingVerification {
|
||||
t.Fatalf("TestFinality: Finality violating block expected to have status '%s', but got '%s'",
|
||||
externalapi.StatusUTXOPendingVerification, blockInfo.BlockStatus)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestBoundedMergeDepth(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
// Set finalityInterval to 50 blocks, so that test runs quickly
|
||||
params.FinalityDuration = 50 * params.TargetTimePerBlock
|
||||
finalityInterval := int(params.FinalityDepth())
|
||||
|
||||
if int(params.K) >= finalityInterval {
|
||||
t.Fatal("K must be smaller than finality duration for this test to run")
|
||||
}
|
||||
|
||||
checkViolatingMergeDepth := func(consensus testapi.TestConsensus, parents []*externalapi.DomainHash) (*externalapi.DomainBlock, bool) {
|
||||
block, _, err := consensus.BuildBlockWithParents(parents, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: BuildBlockWithParents failed: %v", err)
|
||||
return nil, false // fo some reason go doesn't recognize that t.Fatalf never returns
|
||||
}
|
||||
|
||||
err = consensus.ValidateAndInsertBlock(block)
|
||||
if err == nil {
|
||||
return block, false
|
||||
} else if errors.Is(err, ruleerrors.ErrViolatingBoundedMergeDepth) {
|
||||
return block, true
|
||||
} else {
|
||||
t.Fatalf("TestBoundedMergeDepth: expected err: %v, found err: %v", ruleerrors.ErrViolatingBoundedMergeDepth, err)
|
||||
return nil, false // fo some reason go doesn't recognize that t.Fatalf never returns
|
||||
}
|
||||
}
|
||||
|
||||
processBlock := func(consensus testapi.TestConsensus, block *externalapi.DomainBlock, name string) {
|
||||
err := consensus.ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: %s got unexpected error from ProcessBlock: %+v", name, err)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
buildAndInsertBlock := func(consensus testapi.TestConsensus, parentHashes []*externalapi.DomainHash) *externalapi.DomainBlock {
|
||||
block, _, err := consensus.BuildBlockWithParents(parentHashes, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Failed building block: %v", err)
|
||||
}
|
||||
err = consensus.ValidateAndInsertBlock(block)
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Failed Inserting block to consensus: %v", err)
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
getStatus := func(consensus testapi.TestConsensus, block *externalapi.DomainBlock) externalapi.BlockStatus {
|
||||
blockInfo, err := consensus.GetBlockInfo(consensusserialization.BlockHash(block))
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Failed to get block info: %v", err)
|
||||
} else if !blockInfo.Exists {
|
||||
t.Fatalf("TestBoundedMergeDepth: Failed to get block info, block doesn't exists")
|
||||
}
|
||||
return blockInfo.BlockStatus
|
||||
}
|
||||
|
||||
factory := NewFactory()
|
||||
consensusBuild, teardownFunc1, err := factory.NewTestConsensus(params, "BoundedMergeTestBuild")
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Error setting up consensus: %+v", err)
|
||||
}
|
||||
|
||||
consensusReal, teardownFunc2, err := factory.NewTestConsensus(params, "BoundedMergeTestReal")
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardownFunc2()
|
||||
|
||||
// Create a block on top on genesis
|
||||
block1 := buildAndInsertBlock(consensusBuild, []*externalapi.DomainHash{params.GenesisHash})
|
||||
|
||||
// Create a chain
|
||||
selectedChain := make([]*externalapi.DomainBlock, 0, finalityInterval+1)
|
||||
parent := consensusserialization.BlockHash(block1)
|
||||
// Make sure this is always bigger than `blocksChain2` so it will stay the selected chain
|
||||
for i := 0; i < finalityInterval+2; i++ {
|
||||
block := buildAndInsertBlock(consensusBuild, []*externalapi.DomainHash{parent})
|
||||
selectedChain = append(selectedChain, block)
|
||||
parent = consensusserialization.BlockHash(block)
|
||||
}
|
||||
|
||||
// Create another chain
|
||||
blocksChain2 := make([]*externalapi.DomainBlock, 0, finalityInterval+1)
|
||||
parent = consensusserialization.BlockHash(block1)
|
||||
for i := 0; i < finalityInterval+1; i++ {
|
||||
block := buildAndInsertBlock(consensusBuild, []*externalapi.DomainHash{parent})
|
||||
blocksChain2 = append(blocksChain2, block)
|
||||
parent = consensusserialization.BlockHash(block)
|
||||
}
|
||||
|
||||
// Teardown and assign nil to make sure we use the right DAG from here on.
|
||||
teardownFunc1()
|
||||
consensusBuild = nil
|
||||
|
||||
// Now test against the real DAG
|
||||
// submit block1
|
||||
processBlock(consensusReal, block1, "block1")
|
||||
|
||||
// submit chain1
|
||||
for i, block := range selectedChain {
|
||||
processBlock(consensusReal, block, fmt.Sprintf("selectedChain block No %d", i))
|
||||
}
|
||||
|
||||
// submit chain2
|
||||
for i, block := range blocksChain2 {
|
||||
processBlock(consensusReal, block, fmt.Sprintf("blocksChain2 block No %d", i))
|
||||
}
|
||||
|
||||
// submit a block pointing at tip(chain1) and on first block in chain2 directly
|
||||
mergeDepthViolatingBlockBottom, isViolatingMergeDepth := checkViolatingMergeDepth(consensusReal, []*externalapi.DomainHash{consensusserialization.BlockHash(blocksChain2[0]), consensusserialization.BlockHash(selectedChain[len(selectedChain)-1])})
|
||||
if !isViolatingMergeDepth {
|
||||
t.Fatalf("TestBoundedMergeDepth: Expected mergeDepthViolatingBlockBottom to violate merge depth")
|
||||
}
|
||||
|
||||
// submit a block pointing at tip(chain1) and tip(chain2) should also obviously violate merge depth (this points at first block in chain2 indirectly)
|
||||
mergeDepthViolatingTop, isViolatingMergeDepth := checkViolatingMergeDepth(consensusReal, []*externalapi.DomainHash{consensusserialization.BlockHash(blocksChain2[len(blocksChain2)-1]), consensusserialization.BlockHash(selectedChain[len(selectedChain)-1])})
|
||||
if !isViolatingMergeDepth {
|
||||
t.Fatalf("TestBoundedMergeDepth: Expected mergeDepthViolatingTop to violate merge depth")
|
||||
}
|
||||
|
||||
// the location of the parents in the slices need to be both `-X` so the `selectedChain` one will have higher blueScore (it's a chain longer by 1)
|
||||
kosherizingBlock, isViolatingMergeDepth := checkViolatingMergeDepth(consensusReal, []*externalapi.DomainHash{consensusserialization.BlockHash(blocksChain2[len(blocksChain2)-3]), consensusserialization.BlockHash(selectedChain[len(selectedChain)-3])})
|
||||
kosherizingBlockHash := consensusserialization.BlockHash(kosherizingBlock)
|
||||
if isViolatingMergeDepth {
|
||||
t.Fatalf("TestBoundedMergeDepth: Expected blueKosherizingBlock to not violate merge depth")
|
||||
}
|
||||
|
||||
virtualGhotDagData, err := consensusReal.GHOSTDAGDataStore().Get(consensusReal.DatabaseContext(), model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Failed getting the ghostdag data of the virtual: %v", err)
|
||||
}
|
||||
// Make sure it's actually blue
|
||||
found := false
|
||||
for _, blue := range virtualGhotDagData.MergeSetBlues {
|
||||
if *blue == *kosherizingBlockHash {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("TestBoundedMergeDepth: Expected kosherizingBlock to be blue by the virtual")
|
||||
}
|
||||
|
||||
pointAtBlueKosherizing, isViolatingMergeDepth := checkViolatingMergeDepth(consensusReal, []*externalapi.DomainHash{kosherizingBlockHash, consensusserialization.BlockHash(selectedChain[len(selectedChain)-1])})
|
||||
if isViolatingMergeDepth {
|
||||
|
||||
t.Fatalf("TestBoundedMergeDepth: Expected selectedTip to not violate merge depth")
|
||||
}
|
||||
|
||||
virtualSelectedParent, err := consensusReal.GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Failed getting the virtual selected parent %v", err)
|
||||
}
|
||||
|
||||
if *consensusserialization.BlockHash(virtualSelectedParent) != *consensusserialization.BlockHash(pointAtBlueKosherizing) {
|
||||
t.Fatalf("TestBoundedMergeDepth: Expected %s to be the selectedTip but found %s instead", consensusserialization.BlockHash(pointAtBlueKosherizing), consensusserialization.BlockHash(virtualSelectedParent))
|
||||
}
|
||||
|
||||
// Now let's make the kosherizing block red and try to merge again
|
||||
tip := consensusserialization.BlockHash(selectedChain[len(selectedChain)-1])
|
||||
// we use k-1 because `kosherizingBlock` points at tip-2, so 2+k-1 = k+1 anticone.
|
||||
for i := 0; i < int(params.K)-1; i++ {
|
||||
block := buildAndInsertBlock(consensusReal, []*externalapi.DomainHash{tip})
|
||||
tip = consensusserialization.BlockHash(block)
|
||||
}
|
||||
|
||||
virtualSelectedParent, err = consensusReal.GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Failed getting the virtual selected parent %v", err)
|
||||
}
|
||||
|
||||
if *consensusserialization.BlockHash(virtualSelectedParent) != *tip {
|
||||
t.Fatalf("TestBoundedMergeDepth: Expected %s to be the selectedTip but found %s instead", tip, consensusserialization.BlockHash(virtualSelectedParent))
|
||||
}
|
||||
|
||||
virtualGhotDagData, err = consensusReal.GHOSTDAGDataStore().Get(consensusReal.DatabaseContext(), model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Failed getting the ghostdag data of the virtual: %v", err)
|
||||
}
|
||||
// Make sure it's actually blue
|
||||
found = false
|
||||
for _, blue := range virtualGhotDagData.MergeSetBlues {
|
||||
if *blue == *kosherizingBlockHash {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
t.Fatalf("expected kosherizingBlock to be red by the virtual")
|
||||
}
|
||||
|
||||
pointAtRedKosherizing, isViolatingMergeDepth := checkViolatingMergeDepth(consensusReal, []*externalapi.DomainHash{kosherizingBlockHash, tip})
|
||||
if !isViolatingMergeDepth {
|
||||
t.Fatalf("TestBoundedMergeDepth: Expected selectedTipRedKosherize to violate merge depth")
|
||||
}
|
||||
|
||||
// Now `pointAtBlueKosherizing` itself is actually still blue, so we can still point at that even though we can't point at kosherizing directly anymore
|
||||
transitiveBlueKosherizing, isViolatingMergeDepth := checkViolatingMergeDepth(consensusReal, []*externalapi.DomainHash{consensusserialization.BlockHash(pointAtBlueKosherizing), tip})
|
||||
if isViolatingMergeDepth {
|
||||
t.Fatalf("TestBoundedMergeDepth: Expected transitiveBlueKosherizing to not violate merge depth")
|
||||
}
|
||||
|
||||
virtualSelectedParent, err = consensusReal.GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Failed getting the virtual selected parent %v", err)
|
||||
}
|
||||
|
||||
if *consensusserialization.BlockHash(virtualSelectedParent) != *consensusserialization.BlockHash(transitiveBlueKosherizing) {
|
||||
t.Fatalf("TestBoundedMergeDepth: Expected %s to be the selectedTip but found %s instead", consensusserialization.BlockHash(transitiveBlueKosherizing), consensusserialization.BlockHash(virtualSelectedParent))
|
||||
}
|
||||
|
||||
// Lets validate the status of all the interesting blocks
|
||||
if getStatus(consensusReal, pointAtBlueKosherizing) != externalapi.StatusValid {
|
||||
t.Fatalf("TestBoundedMergeDepth: pointAtBlueKosherizing expected status '%s' but got '%s'", externalapi.StatusValid, getStatus(consensusReal, pointAtBlueKosherizing))
|
||||
}
|
||||
if getStatus(consensusReal, pointAtRedKosherizing) != externalapi.StatusInvalid {
|
||||
t.Fatalf("TestBoundedMergeDepth: pointAtRedKosherizing expected status '%s' but got '%s'", externalapi.StatusInvalid, getStatus(consensusReal, pointAtRedKosherizing))
|
||||
}
|
||||
if getStatus(consensusReal, transitiveBlueKosherizing) != externalapi.StatusValid {
|
||||
t.Fatalf("TestBoundedMergeDepth: transitiveBlueKosherizing expected status '%s' but got '%s'", externalapi.StatusValid, getStatus(consensusReal, transitiveBlueKosherizing))
|
||||
}
|
||||
if getStatus(consensusReal, mergeDepthViolatingBlockBottom) != externalapi.StatusInvalid {
|
||||
t.Fatalf("TestBoundedMergeDepth: mergeDepthViolatingBlockBottom expected status '%s' but got '%s'", externalapi.StatusInvalid, getStatus(consensusReal, mergeDepthViolatingBlockBottom))
|
||||
}
|
||||
if getStatus(consensusReal, mergeDepthViolatingTop) != externalapi.StatusInvalid {
|
||||
t.Fatalf("TestBoundedMergeDepth: mergeDepthViolatingTop expected status '%s' but got '%s'", externalapi.StatusInvalid, getStatus(consensusReal, mergeDepthViolatingTop))
|
||||
}
|
||||
if getStatus(consensusReal, kosherizingBlock) != externalapi.StatusUTXOPendingVerification {
|
||||
t.Fatalf("kosherizingBlock expected status '%s' but got '%s'", externalapi.StatusUTXOPendingVerification, getStatus(consensusReal, kosherizingBlock))
|
||||
}
|
||||
|
||||
for i, b := range blocksChain2 {
|
||||
if getStatus(consensusReal, b) != externalapi.StatusUTXOPendingVerification {
|
||||
t.Fatalf("blocksChain2[%d] expected status '%s' but got '%s'", i, externalapi.StatusUTXOPendingVerification, getStatus(consensusReal, b))
|
||||
}
|
||||
}
|
||||
for i, b := range selectedChain {
|
||||
if getStatus(consensusReal, b) != externalapi.StatusValid {
|
||||
t.Fatalf("selectedChain[%d] expected status '%s' but got '%s'", i, externalapi.StatusValid, getStatus(consensusReal, b))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -6,12 +6,39 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
// It's ordered in the same way as the block merge set blues.
|
||||
type AcceptanceData []*BlockAcceptanceData
|
||||
|
||||
// Clone clones the AcceptanceData
|
||||
func (ad AcceptanceData) Clone() AcceptanceData {
|
||||
if ad == nil {
|
||||
return nil
|
||||
}
|
||||
clone := make(AcceptanceData, len(ad))
|
||||
for i, blockAcceptanceData := range ad {
|
||||
clone[i] = blockAcceptanceData.Clone()
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
// BlockAcceptanceData stores all transactions in a block with an indication
|
||||
// if they were accepted or not by some other block
|
||||
type BlockAcceptanceData struct {
|
||||
TransactionAcceptanceData []*TransactionAcceptanceData
|
||||
}
|
||||
|
||||
// Clone returns a clone of BlockAcceptanceData
|
||||
func (bad *BlockAcceptanceData) Clone() *BlockAcceptanceData {
|
||||
if bad == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
clone := &BlockAcceptanceData{TransactionAcceptanceData: make([]*TransactionAcceptanceData, len(bad.TransactionAcceptanceData))}
|
||||
for i, acceptanceData := range bad.TransactionAcceptanceData {
|
||||
clone.TransactionAcceptanceData[i] = acceptanceData.Clone()
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
// TransactionAcceptanceData stores a transaction together with an indication
|
||||
// if it was accepted or not by some block
|
||||
type TransactionAcceptanceData struct {
|
||||
@@ -19,3 +46,16 @@ type TransactionAcceptanceData struct {
|
||||
Fee uint64
|
||||
IsAccepted bool
|
||||
}
|
||||
|
||||
// Clone returns a clone of TransactionAcceptanceData
|
||||
func (tad *TransactionAcceptanceData) Clone() *TransactionAcceptanceData {
|
||||
if tad == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &TransactionAcceptanceData{
|
||||
Transaction: tad.Transaction.Clone(),
|
||||
Fee: tad.Fee,
|
||||
IsAccepted: tad.IsAccepted,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,3 +7,15 @@ type BlockRelations struct {
|
||||
Parents []*externalapi.DomainHash
|
||||
Children []*externalapi.DomainHash
|
||||
}
|
||||
|
||||
// Clone returns a clone of BlockRelations
|
||||
func (br *BlockRelations) Clone() *BlockRelations {
|
||||
if br == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &BlockRelations{
|
||||
Parents: externalapi.CloneHashes(br.Parents),
|
||||
Children: externalapi.CloneHashes(br.Children),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,23 @@ type DomainBlock struct {
|
||||
Transactions []*DomainTransaction
|
||||
}
|
||||
|
||||
// Clone returns a clone of DomainBlock
|
||||
func (block *DomainBlock) Clone() *DomainBlock {
|
||||
if block == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
transactionClone := make([]*DomainTransaction, len(block.Transactions))
|
||||
for i, tx := range block.Transactions {
|
||||
transactionClone[i] = tx.Clone()
|
||||
}
|
||||
|
||||
return &DomainBlock{
|
||||
Header: block.Header.Clone(),
|
||||
Transactions: transactionClone,
|
||||
}
|
||||
}
|
||||
|
||||
// DomainBlockHeader represents the header part of a Kaspa block
|
||||
type DomainBlockHeader struct {
|
||||
Version int32
|
||||
@@ -17,3 +34,21 @@ type DomainBlockHeader struct {
|
||||
Bits uint32
|
||||
Nonce uint64
|
||||
}
|
||||
|
||||
// Clone returns a clone of DomainBlockHeader
|
||||
func (header *DomainBlockHeader) Clone() *DomainBlockHeader {
|
||||
if header == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &DomainBlockHeader{
|
||||
Version: header.Version,
|
||||
ParentHashes: CloneHashes(header.ParentHashes),
|
||||
HashMerkleRoot: *header.HashMerkleRoot.Clone(),
|
||||
AcceptedIDMerkleRoot: *header.AcceptedIDMerkleRoot.Clone(),
|
||||
UTXOCommitment: *header.UTXOCommitment.Clone(),
|
||||
TimeInMilliseconds: header.TimeInMilliseconds,
|
||||
Bits: header.Bits,
|
||||
Nonce: header.Nonce,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,11 @@ package externalapi
|
||||
// BlockStatus represents the validation state of the block.
|
||||
type BlockStatus byte
|
||||
|
||||
// Clone returns a clone of BlockStatus
|
||||
func (bs BlockStatus) Clone() BlockStatus {
|
||||
return bs
|
||||
}
|
||||
|
||||
const (
|
||||
// StatusInvalid indicates that the block is invalid.
|
||||
StatusInvalid BlockStatus = iota
|
||||
@@ -21,3 +26,15 @@ const (
|
||||
// StatusHeaderOnly indicates that the block transactions are not held (pruned or wasn't added yet)
|
||||
StatusHeaderOnly
|
||||
)
|
||||
|
||||
var blockStatusStrings = map[BlockStatus]string{
|
||||
StatusInvalid: "Invalid",
|
||||
StatusValid: "Valid",
|
||||
StatusUTXOPendingVerification: "UTXOPendingVerification",
|
||||
StatusDisqualifiedFromChain: "DisqualifiedFromChain",
|
||||
StatusHeaderOnly: "HeaderOnly",
|
||||
}
|
||||
|
||||
func (bs BlockStatus) String() string {
|
||||
return blockStatusStrings[bs]
|
||||
}
|
||||
|
||||
21
domain/consensus/model/externalapi/consensus.go
Normal file
21
domain/consensus/model/externalapi/consensus.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package externalapi
|
||||
|
||||
// Consensus maintains the current core state of the node
|
||||
type Consensus interface {
|
||||
BuildBlock(coinbaseData *DomainCoinbaseData, transactions []*DomainTransaction) (*DomainBlock, error)
|
||||
ValidateAndInsertBlock(block *DomainBlock) error
|
||||
ValidateTransactionAndPopulateWithConsensusData(transaction *DomainTransaction) error
|
||||
|
||||
GetBlock(blockHash *DomainHash) (*DomainBlock, error)
|
||||
GetBlockHeader(blockHash *DomainHash) (*DomainBlockHeader, error)
|
||||
GetBlockInfo(blockHash *DomainHash) (*BlockInfo, error)
|
||||
|
||||
GetHashesBetween(lowHash, highHash *DomainHash) ([]*DomainHash, error)
|
||||
GetMissingBlockBodyHashes(highHash *DomainHash) ([]*DomainHash, error)
|
||||
GetPruningPointUTXOSet(expectedPruningPointHash *DomainHash) ([]byte, error)
|
||||
SetPruningPointUTXOSet(serializedUTXOSet []byte) error
|
||||
GetVirtualSelectedParent() (*DomainBlock, error)
|
||||
CreateBlockLocator(lowHash, highHash *DomainHash) (BlockLocator, error)
|
||||
FindNextBlockLocatorBoundaries(blockLocator BlockLocator) (lowHash, highHash *DomainHash, err error)
|
||||
GetSyncInfo() (*SyncInfo, error)
|
||||
}
|
||||
@@ -17,6 +17,25 @@ func (hash DomainHash) String() string {
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
// Clone clones the hash
|
||||
func (hash *DomainHash) Clone() *DomainHash {
|
||||
if hash == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
hashClone := *hash
|
||||
return &hashClone
|
||||
}
|
||||
|
||||
// CloneHashes returns a clone of the given hashes slice
|
||||
func CloneHashes(hashes []*DomainHash) []*DomainHash {
|
||||
clone := make([]*DomainHash, len(hashes))
|
||||
for i, hash := range hashes {
|
||||
clone[i] = hash.Clone()
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
// DomainHashesToStrings returns a slice of strings representing the hashes in the given slice of hashes
|
||||
func DomainHashesToStrings(hashes []*DomainHash) []string {
|
||||
strings := make([]string, len(hashes))
|
||||
|
||||
@@ -15,3 +15,13 @@ func (id DomainSubnetworkID) String() string {
|
||||
}
|
||||
return hex.EncodeToString(id[:])
|
||||
}
|
||||
|
||||
// Clone returns a clone of DomainSubnetworkID
|
||||
func (id *DomainSubnetworkID) Clone() *DomainSubnetworkID {
|
||||
if id == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
idClone := *id
|
||||
return &idClone
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package externalapi
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Each of the following represent one of the possible sync
|
||||
// states of the consensus
|
||||
const (
|
||||
SyncStateNormal SyncState = iota
|
||||
SyncStateRelay SyncState = iota
|
||||
SyncStateMissingGenesis
|
||||
SyncStateHeadersFirst
|
||||
SyncStateMissingUTXOSet
|
||||
@@ -15,8 +17,10 @@ type SyncState uint8
|
||||
|
||||
func (s SyncState) String() string {
|
||||
switch s {
|
||||
case SyncStateNormal:
|
||||
return "SyncStateNormal"
|
||||
case SyncStateRelay:
|
||||
return "SyncStateRelay"
|
||||
case SyncStateMissingGenesis:
|
||||
return "SyncStateMissingGenesis"
|
||||
case SyncStateHeadersFirst:
|
||||
return "SyncStateHeadersFirst"
|
||||
case SyncStateMissingUTXOSet:
|
||||
@@ -25,11 +29,13 @@ func (s SyncState) String() string {
|
||||
return "SyncStateMissingBlockBodies"
|
||||
}
|
||||
|
||||
return "<unknown state>"
|
||||
return fmt.Sprintf("<unknown state (%d)>", s)
|
||||
}
|
||||
|
||||
// SyncInfo holds info about the current sync state of the consensus
|
||||
type SyncInfo struct {
|
||||
State SyncState
|
||||
IBDRootUTXOBlockHash *DomainHash
|
||||
HeaderCount uint64
|
||||
BlockCount uint64
|
||||
}
|
||||
|
||||
@@ -17,6 +17,43 @@ type DomainTransaction struct {
|
||||
|
||||
Fee uint64
|
||||
Mass uint64
|
||||
|
||||
// ID is a field that is used to cache the transaction ID.
|
||||
// Always use consensusserialization.TransactionID instead of accessing this field directly
|
||||
ID *DomainTransactionID
|
||||
}
|
||||
|
||||
// Clone returns a clone of DomainTransaction
|
||||
func (tx *DomainTransaction) Clone() *DomainTransaction {
|
||||
if tx == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
payloadClone := make([]byte, len(tx.Payload))
|
||||
copy(payloadClone, tx.Payload)
|
||||
|
||||
inputsClone := make([]*DomainTransactionInput, len(tx.Inputs))
|
||||
for i, input := range tx.Inputs {
|
||||
inputsClone[i] = input.Clone()
|
||||
}
|
||||
|
||||
outputsClone := make([]*DomainTransactionOutput, len(tx.Outputs))
|
||||
for i, output := range tx.Outputs {
|
||||
outputsClone[i] = output.Clone()
|
||||
}
|
||||
|
||||
return &DomainTransaction{
|
||||
Version: tx.Version,
|
||||
Inputs: inputsClone,
|
||||
Outputs: outputsClone,
|
||||
LockTime: tx.LockTime,
|
||||
SubnetworkID: *tx.SubnetworkID.Clone(),
|
||||
Gas: tx.Gas,
|
||||
PayloadHash: *tx.PayloadHash.Clone(),
|
||||
Payload: payloadClone,
|
||||
Fee: tx.Fee,
|
||||
Mass: tx.Mass,
|
||||
}
|
||||
}
|
||||
|
||||
// DomainTransactionInput represents a Kaspa transaction input
|
||||
@@ -28,12 +65,41 @@ type DomainTransactionInput struct {
|
||||
UTXOEntry *UTXOEntry
|
||||
}
|
||||
|
||||
// Clone returns a clone of DomainTransactionInput
|
||||
func (input *DomainTransactionInput) Clone() *DomainTransactionInput {
|
||||
if input == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
signatureScriptClone := make([]byte, len(input.SignatureScript))
|
||||
copy(signatureScriptClone, input.SignatureScript)
|
||||
|
||||
return &DomainTransactionInput{
|
||||
PreviousOutpoint: *input.PreviousOutpoint.Clone(),
|
||||
SignatureScript: signatureScriptClone,
|
||||
Sequence: input.Sequence,
|
||||
UTXOEntry: input.UTXOEntry.Clone(),
|
||||
}
|
||||
}
|
||||
|
||||
// DomainOutpoint represents a Kaspa transaction outpoint
|
||||
type DomainOutpoint struct {
|
||||
TransactionID DomainTransactionID
|
||||
Index uint32
|
||||
}
|
||||
|
||||
// Clone returns a clone of DomainOutpoint
|
||||
func (op *DomainOutpoint) Clone() *DomainOutpoint {
|
||||
if op == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &DomainOutpoint{
|
||||
TransactionID: *op.TransactionID.Clone(),
|
||||
Index: op.Index,
|
||||
}
|
||||
}
|
||||
|
||||
// String stringifies an outpoint.
|
||||
func (op DomainOutpoint) String() string {
|
||||
return fmt.Sprintf("(%s: %d)", op.TransactionID, op.Index)
|
||||
@@ -53,6 +119,21 @@ type DomainTransactionOutput struct {
|
||||
ScriptPublicKey []byte
|
||||
}
|
||||
|
||||
// Clone returns a clone of DomainTransactionOutput
|
||||
func (output *DomainTransactionOutput) Clone() *DomainTransactionOutput {
|
||||
if output == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
scriptPublicKeyClone := make([]byte, len(output.ScriptPublicKey))
|
||||
copy(scriptPublicKeyClone, output.ScriptPublicKey)
|
||||
|
||||
return &DomainTransactionOutput{
|
||||
Value: output.Value,
|
||||
ScriptPublicKey: scriptPublicKeyClone,
|
||||
}
|
||||
}
|
||||
|
||||
// DomainTransactionID represents the ID of a Kaspa transaction
|
||||
type DomainTransactionID DomainHash
|
||||
|
||||
@@ -60,3 +141,13 @@ type DomainTransactionID DomainHash
|
||||
func (id DomainTransactionID) String() string {
|
||||
return DomainHash(id).String()
|
||||
}
|
||||
|
||||
// Clone returns a clone of DomainTransactionID
|
||||
func (id *DomainTransactionID) Clone() *DomainTransactionID {
|
||||
if id == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
idClone := *id
|
||||
return &idClone
|
||||
}
|
||||
|
||||
@@ -11,6 +11,23 @@ type UTXOEntry struct {
|
||||
IsCoinbase bool
|
||||
}
|
||||
|
||||
// Clone returns a clone of UTXOEntry
|
||||
func (entry *UTXOEntry) Clone() *UTXOEntry {
|
||||
if entry == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
scriptPublicKeyClone := make([]byte, len(entry.ScriptPublicKey))
|
||||
copy(scriptPublicKeyClone, entry.ScriptPublicKey)
|
||||
|
||||
return &UTXOEntry{
|
||||
Amount: entry.Amount,
|
||||
ScriptPublicKey: scriptPublicKeyClone,
|
||||
BlockBlueScore: entry.BlockBlueScore,
|
||||
IsCoinbase: entry.IsCoinbase,
|
||||
}
|
||||
}
|
||||
|
||||
// NewUTXOEntry creates a new utxoEntry representing the given txOut
|
||||
func NewUTXOEntry(amount uint64, scriptPubKey []byte, isCoinbase bool, blockBlueScore uint64) *UTXOEntry {
|
||||
return &UTXOEntry{
|
||||
|
||||
@@ -11,5 +11,25 @@ type BlockGHOSTDAGData struct {
|
||||
BluesAnticoneSizes map[externalapi.DomainHash]KType
|
||||
}
|
||||
|
||||
// Clone returns a clone of BlockGHOSTDAGData
|
||||
func (bgd *BlockGHOSTDAGData) Clone() *BlockGHOSTDAGData {
|
||||
if bgd == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
bluesAnticoneSizesClone := make(map[externalapi.DomainHash]KType, len(bgd.BluesAnticoneSizes))
|
||||
for hash, size := range bgd.BluesAnticoneSizes {
|
||||
bluesAnticoneSizesClone[hash] = size
|
||||
}
|
||||
|
||||
return &BlockGHOSTDAGData{
|
||||
BlueScore: bgd.BlueScore,
|
||||
SelectedParent: bgd.SelectedParent.Clone(),
|
||||
MergeSetBlues: externalapi.CloneHashes(bgd.MergeSetBlues),
|
||||
MergeSetReds: externalapi.CloneHashes(bgd.MergeSetReds),
|
||||
BluesAnticoneSizes: bluesAnticoneSizesClone,
|
||||
}
|
||||
}
|
||||
|
||||
// KType defines the size of GHOSTDAG consensus algorithm K parameter.
|
||||
type KType byte
|
||||
|
||||
@@ -11,4 +11,5 @@ type BlockHeaderStore interface {
|
||||
HasBlockHeader(dbContext DBReader, blockHash *externalapi.DomainHash) (bool, error)
|
||||
BlockHeaders(dbContext DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlockHeader, error)
|
||||
Delete(blockHash *externalapi.DomainHash)
|
||||
Count() uint64
|
||||
}
|
||||
|
||||
@@ -11,4 +11,5 @@ type BlockStore interface {
|
||||
HasBlock(dbContext DBReader, blockHash *externalapi.DomainHash) (bool, error)
|
||||
Blocks(dbContext DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlock, error)
|
||||
Delete(blockHash *externalapi.DomainHash)
|
||||
Count() uint64
|
||||
}
|
||||
|
||||
18
domain/consensus/model/interface_processes_blockbuilder.go
Normal file
18
domain/consensus/model/interface_processes_blockbuilder.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package model
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// BlockBuilder is responsible for creating blocks from the current state
|
||||
type BlockBuilder interface {
|
||||
BuildBlock(coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error)
|
||||
}
|
||||
|
||||
// TestBlockBuilder adds to the main BlockBuilder methods required by tests
|
||||
type TestBlockBuilder interface {
|
||||
BlockBuilder
|
||||
|
||||
// BuildBlockWithParents builds a block with provided parents, coinbaseData and transactions,
|
||||
// and returns the block together with its past UTXO-diff from the virtual.
|
||||
BuildBlockWithParents(parentHashes []*externalapi.DomainHash, coinbaseData *externalapi.DomainCoinbaseData,
|
||||
transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, *UTXODiff, error)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user