Compare commits

..

1 Commits

Author SHA1 Message Date
msutton
3a323ea1fb Fix protobuf generation for PR #1885 2021-12-29 14:20:31 +02:00
151 changed files with 2678 additions and 4562 deletions

View File

@@ -87,7 +87,6 @@ func (app *kaspadApp) main(startedChan chan<- struct{}) error {
if app.cfg.Profile != "" {
profiling.Start(app.cfg.Profile, log)
}
profiling.TrackHeap(app.cfg.AppDir, log)
// Return now if an interrupt signal was triggered.
if signal.InterruptRequested(interrupt) {

View File

@@ -436,10 +436,10 @@ func RPCBlockToDomainBlock(block *RPCBlock) (*externalapi.DomainBlock, error) {
// BlockWithTrustedDataToDomainBlockWithTrustedData converts *MsgBlockWithTrustedData to *externalapi.BlockWithTrustedData
func BlockWithTrustedDataToDomainBlockWithTrustedData(block *MsgBlockWithTrustedData) *externalapi.BlockWithTrustedData {
daaWindow := make([]*externalapi.TrustedDataDataDAAHeader, len(block.DAAWindow))
daaWindow := make([]*externalapi.TrustedDataDataDAABlock, len(block.DAAWindow))
for i, daaBlock := range block.DAAWindow {
daaWindow[i] = &externalapi.TrustedDataDataDAAHeader{
Header: BlockHeaderToDomainBlockHeader(&daaBlock.Block.Header),
daaWindow[i] = &externalapi.TrustedDataDataDAABlock{
Block: MsgBlockToDomainBlock(daaBlock.Block),
GHOSTDAGData: ghostdagDataToDomainGHOSTDAGData(daaBlock.GHOSTDAGData),
}
}
@@ -454,27 +454,12 @@ func BlockWithTrustedDataToDomainBlockWithTrustedData(block *MsgBlockWithTrusted
return &externalapi.BlockWithTrustedData{
Block: MsgBlockToDomainBlock(block.Block),
DAAScore: block.DAAScore,
DAAWindow: daaWindow,
GHOSTDAGData: ghostdagData,
}
}
// TrustedDataDataDAABlockV4ToTrustedDataDataDAAHeader converts *TrustedDataDAAHeader to *externalapi.TrustedDataDataDAAHeader
func TrustedDataDataDAABlockV4ToTrustedDataDataDAAHeader(daaBlock *TrustedDataDAAHeader) *externalapi.TrustedDataDataDAAHeader {
return &externalapi.TrustedDataDataDAAHeader{
Header: BlockHeaderToDomainBlockHeader(daaBlock.Header),
GHOSTDAGData: ghostdagDataToDomainGHOSTDAGData(daaBlock.GHOSTDAGData),
}
}
// GHOSTDAGHashPairToDomainGHOSTDAGHashPair converts *BlockGHOSTDAGDataHashPair to *externalapi.BlockGHOSTDAGDataHashPair
func GHOSTDAGHashPairToDomainGHOSTDAGHashPair(datum *BlockGHOSTDAGDataHashPair) *externalapi.BlockGHOSTDAGDataHashPair {
return &externalapi.BlockGHOSTDAGDataHashPair{
Hash: datum.Hash,
GHOSTDAGData: ghostdagDataToDomainGHOSTDAGData(datum.GHOSTDAGData),
}
}
func ghostdagDataToDomainGHOSTDAGData(data *BlockGHOSTDAGData) *externalapi.BlockGHOSTDAGData {
bluesAnticoneSizes := make(map[externalapi.DomainHash]externalapi.KType, len(data.BluesAnticoneSizes))
for _, pair := range data.BluesAnticoneSizes {
@@ -515,9 +500,7 @@ func DomainBlockWithTrustedDataToBlockWithTrustedData(block *externalapi.BlockWi
daaWindow := make([]*TrustedDataDataDAABlock, len(block.DAAWindow))
for i, daaBlock := range block.DAAWindow {
daaWindow[i] = &TrustedDataDataDAABlock{
Block: &MsgBlock{
Header: *DomainBlockHeaderToBlockHeader(daaBlock.Header),
},
Block: DomainBlockToMsgBlock(daaBlock.Block),
GHOSTDAGData: domainGHOSTDAGDataGHOSTDAGData(daaBlock.GHOSTDAGData),
}
}
@@ -532,41 +515,7 @@ func DomainBlockWithTrustedDataToBlockWithTrustedData(block *externalapi.BlockWi
return &MsgBlockWithTrustedData{
Block: DomainBlockToMsgBlock(block.Block),
DAAScore: block.Block.Header.DAAScore(),
DAAWindow: daaWindow,
GHOSTDAGData: ghostdagData,
}
}
// DomainBlockWithTrustedDataToBlockWithTrustedDataV4 converts a set of *externalapi.DomainBlock, daa window indices and ghostdag data indices
// to *MsgBlockWithTrustedDataV4
func DomainBlockWithTrustedDataToBlockWithTrustedDataV4(block *externalapi.DomainBlock, daaWindowIndices, ghostdagDataIndices []uint64) *MsgBlockWithTrustedDataV4 {
return &MsgBlockWithTrustedDataV4{
Block: DomainBlockToMsgBlock(block),
DAAWindowIndices: daaWindowIndices,
GHOSTDAGDataIndices: ghostdagDataIndices,
}
}
// DomainTrustedDataToTrustedData converts *externalapi.BlockWithTrustedData to *MsgBlockWithTrustedData
func DomainTrustedDataToTrustedData(domainDAAWindow []*externalapi.TrustedDataDataDAAHeader, domainGHOSTDAGData []*externalapi.BlockGHOSTDAGDataHashPair) *MsgTrustedData {
daaWindow := make([]*TrustedDataDAAHeader, len(domainDAAWindow))
for i, daaBlock := range domainDAAWindow {
daaWindow[i] = &TrustedDataDAAHeader{
Header: DomainBlockHeaderToBlockHeader(daaBlock.Header),
GHOSTDAGData: domainGHOSTDAGDataGHOSTDAGData(daaBlock.GHOSTDAGData),
}
}
ghostdagData := make([]*BlockGHOSTDAGDataHashPair, len(domainGHOSTDAGData))
for i, datum := range domainGHOSTDAGData {
ghostdagData[i] = &BlockGHOSTDAGDataHashPair{
Hash: datum.Hash,
GHOSTDAGData: domainGHOSTDAGDataGHOSTDAGData(datum.GHOSTDAGData),
}
}
return &MsgTrustedData{
DAAScore: block.DAAScore,
DAAWindow: daaWindow,
GHOSTDAGData: ghostdagData,
}

View File

@@ -38,10 +38,6 @@ type RPCError struct {
Message string
}
func (err RPCError) Error() string {
return err.Message
}
// RPCErrorf formats according to a format specifier and returns the string
// as an RPCError.
func RPCErrorf(format string, args ...interface{}) *RPCError {

View File

@@ -66,9 +66,6 @@ const (
CmdPruningPoints
CmdRequestPruningPointProof
CmdPruningPointProof
CmdReady
CmdTrustedData
CmdBlockWithTrustedDataV4
// rpc
CmdGetCurrentNetworkRequestMessage
@@ -150,8 +147,6 @@ const (
CmdNotifyVirtualDaaScoreChangedRequestMessage
CmdNotifyVirtualDaaScoreChangedResponseMessage
CmdVirtualDaaScoreChangedNotificationMessage
CmdGetBalancesByAddressesRequestMessage
CmdGetBalancesByAddressesResponseMessage
)
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
@@ -192,9 +187,6 @@ var ProtocolMessageCommandToString = map[MessageCommand]string{
CmdPruningPoints: "PruningPoints",
CmdRequestPruningPointProof: "RequestPruningPointProof",
CmdPruningPointProof: "PruningPointProof",
CmdReady: "Ready",
CmdTrustedData: "TrustedData",
CmdBlockWithTrustedDataV4: "BlockWithTrustedDataV4",
}
// RPCMessageCommandToString maps all MessageCommands to their string representation
@@ -276,8 +268,6 @@ var RPCMessageCommandToString = map[MessageCommand]string{
CmdNotifyVirtualDaaScoreChangedRequestMessage: "NotifyVirtualDaaScoreChangedRequest",
CmdNotifyVirtualDaaScoreChangedResponseMessage: "NotifyVirtualDaaScoreChangedResponse",
CmdVirtualDaaScoreChangedNotificationMessage: "VirtualDaaScoreChangedNotification",
CmdGetBalancesByAddressesRequestMessage: "GetBalancesByAddressesRequest",
CmdGetBalancesByAddressesResponseMessage: "GetBalancesByAddressesResponse",
}
// Message is an interface that describes a kaspa message. A type that

View File

@@ -18,7 +18,7 @@ import (
// TestBlock tests the MsgBlock API.
func TestBlock(t *testing.T) {
pver := uint32(4)
pver := ProtocolVersion
// Block 1 header.
parents := blockOne.Header.Parents

View File

@@ -1,20 +0,0 @@
package appmessage
// MsgBlockWithTrustedDataV4 represents a kaspa BlockWithTrustedDataV4 message
type MsgBlockWithTrustedDataV4 struct {
baseMessage
Block *MsgBlock
DAAWindowIndices []uint64
GHOSTDAGDataIndices []uint64
}
// Command returns the protocol command string for the message
func (msg *MsgBlockWithTrustedDataV4) Command() MessageCommand {
return CmdBlockWithTrustedDataV4
}
// NewMsgBlockWithTrustedDataV4 returns a new MsgBlockWithTrustedDataV4.
func NewMsgBlockWithTrustedDataV4() *MsgBlockWithTrustedDataV4 {
return &MsgBlockWithTrustedDataV4{}
}

View File

@@ -1,25 +0,0 @@
package appmessage
// MsgTrustedData represents a kaspa TrustedData message
type MsgTrustedData struct {
baseMessage
DAAWindow []*TrustedDataDAAHeader
GHOSTDAGData []*BlockGHOSTDAGDataHashPair
}
// Command returns the protocol command string for the message
func (msg *MsgTrustedData) Command() MessageCommand {
return CmdTrustedData
}
// NewMsgTrustedData returns a new MsgTrustedData.
func NewMsgTrustedData() *MsgTrustedData {
return &MsgTrustedData{}
}
// TrustedDataDAAHeader is an appmessage representation of externalapi.TrustedDataDataDAAHeader
type TrustedDataDAAHeader struct {
Header *MsgBlockHeader
GHOSTDAGData *BlockGHOSTDAGData
}

View File

@@ -22,7 +22,7 @@ import (
// TestTx tests the MsgTx API.
func TestTx(t *testing.T) {
pver := uint32(4)
pver := ProtocolVersion
txIDStr := "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
txID, err := transactionid.FromString(txIDStr)

View File

@@ -82,12 +82,12 @@ func (msg *MsgVersion) Command() MessageCommand {
// Message interface using the passed parameters and defaults for the remaining
// fields.
func NewMsgVersion(addr *NetAddress, id *id.ID, network string,
subnetworkID *externalapi.DomainSubnetworkID, protocolVersion uint32) *MsgVersion {
subnetworkID *externalapi.DomainSubnetworkID) *MsgVersion {
// Limit the timestamp to one millisecond precision since the protocol
// doesn't support better.
return &MsgVersion{
ProtocolVersion: protocolVersion,
ProtocolVersion: ProtocolVersion,
Network: network,
Services: 0,
Timestamp: mstime.Now(),

View File

@@ -15,7 +15,7 @@ import (
// TestVersion tests the MsgVersion API.
func TestVersion(t *testing.T) {
pver := uint32(4)
pver := ProtocolVersion
// Create version message data.
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 16111}
@@ -26,7 +26,7 @@ func TestVersion(t *testing.T) {
}
// Ensure we get the correct data back out.
msg := NewMsgVersion(me, generatedID, "mainnet", nil, 4)
msg := NewMsgVersion(me, generatedID, "mainnet", nil)
if msg.ProtocolVersion != pver {
t.Errorf("NewMsgVersion: wrong protocol version - got %v, want %v",
msg.ProtocolVersion, pver)

View File

@@ -5,9 +5,8 @@
package appmessage
import (
"net"
"github.com/kaspanet/kaspad/util/mstime"
"net"
)
// NetAddress defines information about a peer on the network including the time
@@ -58,7 +57,3 @@ func NewNetAddressTimestamp(
func NewNetAddress(addr *net.TCPAddr) *NetAddress {
return NewNetAddressIPPort(addr.IP, uint16(addr.Port))
}
func (na NetAddress) String() string {
return na.TCPAddress().String()
}

View File

@@ -1,22 +0,0 @@
package appmessage
// MsgReady implements the Message interface and represents a kaspa
// Ready message. It is used to notify that the peer is ready to receive
// messages.
//
// This message has no payload.
type MsgReady struct {
baseMessage
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgReady) Command() MessageCommand {
return CmdReady
}
// NewMsgReady returns a new kaspa Ready message that conforms to the
// Message interface.
func NewMsgReady() *MsgReady {
return &MsgReady{}
}

View File

@@ -11,6 +11,9 @@ import (
)
const (
// ProtocolVersion is the latest protocol version this package supports.
ProtocolVersion uint32 = 3
// DefaultServices describes the default services that are supported by
// the server.
DefaultServices = SFNodeNetwork | SFNodeBloom | SFNodeCF

View File

@@ -1,47 +0,0 @@
package appmessage
// GetBalancesByAddressesRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetBalancesByAddressesRequestMessage struct {
baseMessage
Addresses []string
}
// Command returns the protocol command string for the message
func (msg *GetBalancesByAddressesRequestMessage) Command() MessageCommand {
return CmdGetBalancesByAddressesRequestMessage
}
// NewGetBalancesByAddressesRequest returns a instance of the message
func NewGetBalancesByAddressesRequest(addresses []string) *GetBalancesByAddressesRequestMessage {
return &GetBalancesByAddressesRequestMessage{
Addresses: addresses,
}
}
// BalancesByAddressesEntry represents the balance of some address
type BalancesByAddressesEntry struct {
Address string
Balance uint64
}
// GetBalancesByAddressesResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetBalancesByAddressesResponseMessage struct {
baseMessage
Entries []*BalancesByAddressesEntry
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetBalancesByAddressesResponseMessage) Command() MessageCommand {
return CmdGetBalancesByAddressesResponseMessage
}
// NewGetBalancesByAddressesResponse returns an instance of the message
func NewGetBalancesByAddressesResponse(entries []*BalancesByAddressesEntry) *GetBalancesByAddressesResponseMessage {
return &GetBalancesByAddressesResponseMessage{
Entries: entries,
}
}

View File

@@ -4,8 +4,7 @@ package appmessage
// its respective RPC message
type SubmitBlockRequestMessage struct {
baseMessage
Block *RPCBlock
AllowNonDAABlocks bool
Block *RPCBlock
}
// Command returns the protocol command string for the message
@@ -14,10 +13,9 @@ func (msg *SubmitBlockRequestMessage) Command() MessageCommand {
}
// NewSubmitBlockRequestMessage returns a instance of the message
func NewSubmitBlockRequestMessage(block *RPCBlock, allowNonDAABlocks bool) *SubmitBlockRequestMessage {
func NewSubmitBlockRequestMessage(block *RPCBlock) *SubmitBlockRequestMessage {
return &SubmitBlockRequestMessage{
Block: block,
AllowNonDAABlocks: allowNonDAABlocks,
Block: block,
}
}

View File

@@ -1,8 +1,6 @@
package common
import (
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"time"
"github.com/pkg/errors"
@@ -14,14 +12,3 @@ const DefaultTimeout = 120 * time.Second
// ErrPeerWithSameIDExists signifies that a peer with the same ID already exist.
var ErrPeerWithSameIDExists = errors.New("ready peer with the same ID already exists")
type flowExecuteFunc func(peer *peerpkg.Peer)
// Flow is a a data structure that is used in order to associate a p2p flow to some route in a router.
type Flow struct {
Name string
ExecuteFunc flowExecuteFunc
}
// FlowInitializeFunc is a function that is used in order to initialize a flow
type FlowInitializeFunc func(route *routerpkg.Route, peer *peerpkg.Peer) error

View File

@@ -11,6 +11,7 @@ import (
"github.com/pkg/errors"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
)
// OnNewBlock updates the mempool after a new block arrival, and
@@ -108,7 +109,7 @@ func (f *FlowContext) broadcastTransactionsAfterBlockAdded(
// SharedRequestedBlocks returns a *blockrelay.SharedRequestedBlocks for sharing
// data about requested blocks between different peers.
func (f *FlowContext) SharedRequestedBlocks() *SharedRequestedBlocks {
func (f *FlowContext) SharedRequestedBlocks() *blockrelay.SharedRequestedBlocks {
return f.sharedRequestedBlocks
}

View File

@@ -10,6 +10,8 @@ import (
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
"github.com/kaspanet/kaspad/app/protocol/flows/transactionrelay"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
@@ -50,9 +52,9 @@ type FlowContext struct {
onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler
lastRebroadcastTime time.Time
sharedRequestedTransactions *SharedRequestedTransactions
sharedRequestedTransactions *transactionrelay.SharedRequestedTransactions
sharedRequestedBlocks *SharedRequestedBlocks
sharedRequestedBlocks *blockrelay.SharedRequestedBlocks
ibdPeer *peerpkg.Peer
ibdPeerMutex sync.RWMutex
@@ -80,8 +82,8 @@ func New(cfg *config.Config, domain domain.Domain, addressManager *addressmanage
domain: domain,
addressManager: addressManager,
connectionManager: connectionManager,
sharedRequestedTransactions: NewSharedRequestedTransactions(),
sharedRequestedBlocks: NewSharedRequestedBlocks(),
sharedRequestedTransactions: transactionrelay.NewSharedRequestedTransactions(),
sharedRequestedBlocks: blockrelay.NewSharedRequestedBlocks(),
peers: make(map[id.ID]*peerpkg.Peer),
orphans: make(map[externalapi.DomainHash]*externalapi.DomainBlock),
timeStarted: mstime.Now().UnixMilliseconds(),

View File

@@ -4,6 +4,7 @@ import (
"time"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/flows/transactionrelay"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
)
@@ -29,7 +30,7 @@ func (f *FlowContext) shouldRebroadcastTransactions() bool {
// SharedRequestedTransactions returns a *transactionrelay.SharedRequestedTransactions for sharing
// data about requested transactions between different peers.
func (f *FlowContext) SharedRequestedTransactions() *SharedRequestedTransactions {
func (f *FlowContext) SharedRequestedTransactions() *transactionrelay.SharedRequestedTransactions {
return f.sharedRequestedTransactions
}

View File

@@ -13,21 +13,15 @@ func (flow *handleRelayInvsFlow) sendGetBlockLocator(highHash *externalapi.Domai
}
func (flow *handleRelayInvsFlow) receiveBlockLocator() (blockLocatorHashes []*externalapi.DomainHash, err error) {
for {
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
if err != nil {
return nil, err
}
switch message := message.(type) {
case *appmessage.MsgInvRelayBlock:
flow.invsQueue = append(flow.invsQueue, message)
case *appmessage.MsgBlockLocator:
return message.BlockLocatorHashes, nil
default:
return nil,
protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s, got: %s", appmessage.CmdBlockLocator, message.Command())
}
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return nil, err
}
msgBlockLocator, ok := message.(*appmessage.MsgBlockLocator)
if !ok {
return nil,
protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s, got: %s", appmessage.CmdBlockLocator, message.Command())
}
return msgBlockLocator.BlockLocatorHashes, nil
}

View File

@@ -0,0 +1,95 @@
package blockrelay
import (
"github.com/kaspanet/kaspad/app/appmessage"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"runtime"
"sync/atomic"
)
// PruningPointAndItsAnticoneRequestsContext is the interface for the context needed for the HandlePruningPointAndItsAnticoneRequests flow.
type PruningPointAndItsAnticoneRequestsContext interface {
Domain() domain.Domain
}
var isBusy uint32
// HandlePruningPointAndItsAnticoneRequests listens to appmessage.MsgRequestPruningPointAndItsAnticone messages and sends
// the pruning point and its anticone to the requesting peer.
func HandlePruningPointAndItsAnticoneRequests(context PruningPointAndItsAnticoneRequestsContext, incomingRoute *router.Route,
outgoingRoute *router.Route, peer *peerpkg.Peer) error {
for {
err := func() error {
_, err := incomingRoute.Dequeue()
if err != nil {
return err
}
if !atomic.CompareAndSwapUint32(&isBusy, 0, 1) {
return protocolerrors.Errorf(false, "node is busy with other pruning point anticone requests")
}
defer atomic.StoreUint32(&isBusy, 0)
log.Debugf("Got request for pruning point and its anticone from %s", peer)
pruningPointHeaders, err := context.Domain().Consensus().PruningPointHeaders()
if err != nil {
return err
}
msgPruningPointHeaders := make([]*appmessage.MsgBlockHeader, len(pruningPointHeaders))
for i, header := range pruningPointHeaders {
msgPruningPointHeaders[i] = appmessage.DomainBlockHeaderToBlockHeader(header)
}
err = outgoingRoute.Enqueue(appmessage.NewMsgPruningPoints(msgPruningPointHeaders))
if err != nil {
return err
}
pointAndItsAnticone, err := context.Domain().Consensus().PruningPointAndItsAnticone()
if err != nil {
return err
}
for _, blockHash := range pointAndItsAnticone {
err := sendBlockWithTrustedData(context, outgoingRoute, blockHash)
if err != nil {
return err
}
}
err = outgoingRoute.Enqueue(appmessage.NewMsgDoneBlocksWithTrustedData())
if err != nil {
return err
}
log.Debugf("Sent pruning point and its anticone to %s", peer)
return nil
}()
if err != nil {
return err
}
}
}
func sendBlockWithTrustedData(context PruningPointAndItsAnticoneRequestsContext, outgoingRoute *router.Route, blockHash *externalapi.DomainHash) error {
blockWithTrustedData, err := context.Domain().Consensus().BlockWithTrustedData(blockHash)
if err != nil {
return err
}
err = outgoingRoute.Enqueue(appmessage.DomainBlockWithTrustedDataToBlockWithTrustedData(blockWithTrustedData))
if err != nil {
return err
}
runtime.GC()
return nil
}

View File

@@ -3,7 +3,6 @@ package blockrelay
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/common"
"github.com/kaspanet/kaspad/app/protocol/flowcontext"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/domain"
@@ -27,12 +26,14 @@ type RelayInvsContext interface {
OnNewBlock(block *externalapi.DomainBlock, virtualChangeSet *externalapi.VirtualChangeSet) error
OnVirtualChange(virtualChangeSet *externalapi.VirtualChangeSet) error
OnPruningPointUTXOSetOverride() error
SharedRequestedBlocks() *flowcontext.SharedRequestedBlocks
SharedRequestedBlocks() *SharedRequestedBlocks
Broadcast(message appmessage.Message) error
AddOrphan(orphanBlock *externalapi.DomainBlock)
GetOrphanRoots(orphanHash *externalapi.DomainHash) ([]*externalapi.DomainHash, bool, error)
IsOrphan(blockHash *externalapi.DomainHash) bool
IsIBDRunning() bool
TrySetIBDRunning(ibdPeer *peerpkg.Peer) bool
UnsetIBDRunning()
IsRecoverableError(err error) bool
}
@@ -55,10 +56,7 @@ func HandleRelayInvs(context RelayInvsContext, incomingRoute *router.Route, outg
peer: peer,
invsQueue: make([]*appmessage.MsgInvRelayBlock, 0),
}
err := flow.start()
// Currently, HandleRelayInvs flow is the only place where IBD is triggered, so the channel can be closed now
close(peer.IBDRequestChannel())
return err
return flow.start()
}
func (flow *handleRelayInvsFlow) start() error {
@@ -196,14 +194,14 @@ func (flow *handleRelayInvsFlow) readInv() (*appmessage.MsgInvRelayBlock, error)
}
func (flow *handleRelayInvsFlow) requestBlock(requestHash *externalapi.DomainHash) (*externalapi.DomainBlock, bool, error) {
exists := flow.SharedRequestedBlocks().AddIfNotExists(requestHash)
exists := flow.SharedRequestedBlocks().addIfNotExists(requestHash)
if exists {
return nil, true, nil
}
// In case the function returns earlier than expected, we want to make sure flow.SharedRequestedBlocks() is
// clean from any pending blocks.
defer flow.SharedRequestedBlocks().Remove(requestHash)
defer flow.SharedRequestedBlocks().remove(requestHash)
getRelayBlocksMsg := appmessage.NewMsgRequestRelayBlocks([]*externalapi.DomainHash{requestHash})
err := flow.outgoingRoute.Enqueue(getRelayBlocksMsg)
@@ -307,14 +305,7 @@ func (flow *handleRelayInvsFlow) processOrphan(block *externalapi.DomainBlock) e
// Start IBD unless we already are in IBD
log.Debugf("Block %s is out of orphan resolution range. "+
"Attempting to start IBD against it.", blockHash)
// Send the block to IBD flow via the IBDRequestChannel.
// Note that this is a non-blocking send, since if IBD is already running, there is no need to trigger it
select {
case flow.peer.IBDRequestChannel() <- block:
default:
}
return nil
return flow.runIBDIfNotRunning(block)
}
func (flow *handleRelayInvsFlow) isGenesisVirtualSelectedParent() (bool, error) {

View File

@@ -1,69 +1,22 @@
package blockrelay
import (
"time"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/common"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/pkg/errors"
"time"
)
// IBDContext is the interface for the context needed for the HandleIBD flow.
type IBDContext interface {
Domain() domain.Domain
Config() *config.Config
OnNewBlock(block *externalapi.DomainBlock, virtualChangeSet *externalapi.VirtualChangeSet) error
OnVirtualChange(virtualChangeSet *externalapi.VirtualChangeSet) error
OnPruningPointUTXOSetOverride() error
IsIBDRunning() bool
TrySetIBDRunning(ibdPeer *peerpkg.Peer) bool
UnsetIBDRunning()
IsRecoverableError(err error) bool
}
type handleIBDFlow struct {
IBDContext
incomingRoute, outgoingRoute *router.Route
peer *peerpkg.Peer
}
// HandleIBD handles IBD
func HandleIBD(context IBDContext, incomingRoute *router.Route, outgoingRoute *router.Route,
peer *peerpkg.Peer) error {
flow := &handleIBDFlow{
IBDContext: context,
incomingRoute: incomingRoute,
outgoingRoute: outgoingRoute,
peer: peer,
}
return flow.start()
}
func (flow *handleIBDFlow) start() error {
for {
// Wait for IBD requests triggered by other flows
block, ok := <-flow.peer.IBDRequestChannel()
if !ok {
return nil
}
err := flow.runIBDIfNotRunning(block)
if err != nil {
return err
}
}
}
func (flow *handleIBDFlow) runIBDIfNotRunning(block *externalapi.DomainBlock) error {
func (flow *handleRelayInvsFlow) runIBDIfNotRunning(block *externalapi.DomainBlock) error {
wasIBDNotRunning := flow.TrySetIBDRunning(flow.peer)
if !wasIBDNotRunning {
log.Debugf("IBD is already running")
@@ -97,7 +50,7 @@ func (flow *handleIBDFlow) runIBDIfNotRunning(block *externalapi.DomainBlock) er
if shouldDownloadHeadersProof {
log.Infof("Starting IBD with headers proof")
err := flow.ibdWithHeadersProof(highHash, block.Header.DAAScore())
err := flow.ibdWithHeadersProof(highHash)
if err != nil {
return err
}
@@ -115,7 +68,7 @@ func (flow *handleIBDFlow) runIBDIfNotRunning(block *externalapi.DomainBlock) er
}
}
err = flow.syncPruningPointFutureHeaders(flow.Domain().Consensus(), highestSharedBlockHash, highHash, block.Header.DAAScore())
err = flow.syncPruningPointFutureHeaders(flow.Domain().Consensus(), highestSharedBlockHash, highHash)
if err != nil {
return err
}
@@ -131,16 +84,7 @@ func (flow *handleIBDFlow) runIBDIfNotRunning(block *externalapi.DomainBlock) er
return nil
}
func (flow *handleIBDFlow) isGenesisVirtualSelectedParent() (bool, error) {
virtualSelectedParent, err := flow.Domain().Consensus().GetVirtualSelectedParent()
if err != nil {
return false, err
}
return virtualSelectedParent.Equal(flow.Config().NetParams().GenesisHash), nil
}
func (flow *handleIBDFlow) logIBDFinished(isFinishedSuccessfully bool) {
func (flow *handleRelayInvsFlow) logIBDFinished(isFinishedSuccessfully bool) {
successString := "successfully"
if !isFinishedSuccessfully {
successString = "(interrupted)"
@@ -151,7 +95,7 @@ func (flow *handleIBDFlow) logIBDFinished(isFinishedSuccessfully bool) {
// findHighestSharedBlock attempts to find the highest shared block between the peer
// and this node. This method may fail because the peer and us have conflicting pruning
// points. In that case we return (nil, false, nil) so that we may stop IBD gracefully.
func (flow *handleIBDFlow) findHighestSharedBlockHash(
func (flow *handleRelayInvsFlow) findHighestSharedBlockHash(
targetHash *externalapi.DomainHash) (*externalapi.DomainHash, bool, error) {
log.Debugf("Sending a blockLocator to %s between pruning point and headers selected tip", flow.peer)
@@ -194,7 +138,7 @@ func (flow *handleIBDFlow) findHighestSharedBlockHash(
}
}
func (flow *handleIBDFlow) nextBlockLocator(lowHash, highHash *externalapi.DomainHash) (externalapi.BlockLocator, error) {
func (flow *handleRelayInvsFlow) nextBlockLocator(lowHash, highHash *externalapi.DomainHash) (externalapi.BlockLocator, error) {
log.Debugf("Sending a blockLocator to %s between %s and %s", flow.peer, lowHash, highHash)
blockLocator, err := flow.Domain().Consensus().CreateHeadersSelectedChainBlockLocator(lowHash, highHash)
if err != nil {
@@ -212,7 +156,7 @@ func (flow *handleIBDFlow) nextBlockLocator(lowHash, highHash *externalapi.Domai
return blockLocator, nil
}
func (flow *handleIBDFlow) findHighestHashIndex(
func (flow *handleRelayInvsFlow) findHighestHashIndex(
highestHash *externalapi.DomainHash, blockLocator externalapi.BlockLocator) (int, error) {
highestHashIndex := 0
@@ -237,7 +181,7 @@ func (flow *handleIBDFlow) findHighestHashIndex(
// fetchHighestHash attempts to fetch the highest hash the peer knows amongst the given
// blockLocator. This method may fail because the peer and us have conflicting pruning
// points. In that case we return (nil, false, nil) so that we may stop IBD gracefully.
func (flow *handleIBDFlow) fetchHighestHash(
func (flow *handleRelayInvsFlow) fetchHighestHash(
targetHash *externalapi.DomainHash, blockLocator externalapi.BlockLocator) (*externalapi.DomainHash, bool, error) {
ibdBlockLocatorMessage := appmessage.NewMsgIBDBlockLocator(targetHash, blockLocator)
@@ -245,7 +189,7 @@ func (flow *handleIBDFlow) fetchHighestHash(
if err != nil {
return nil, false, err
}
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return nil, false, err
}
@@ -265,8 +209,8 @@ func (flow *handleIBDFlow) fetchHighestHash(
}
}
func (flow *handleIBDFlow) syncPruningPointFutureHeaders(consensus externalapi.Consensus, highestSharedBlockHash *externalapi.DomainHash,
highHash *externalapi.DomainHash, highBlockDAAScore uint64) error {
func (flow *handleRelayInvsFlow) syncPruningPointFutureHeaders(consensus externalapi.Consensus, highestSharedBlockHash *externalapi.DomainHash,
highHash *externalapi.DomainHash) error {
log.Infof("Downloading headers from %s", flow.peer)
@@ -275,12 +219,6 @@ func (flow *handleIBDFlow) syncPruningPointFutureHeaders(consensus externalapi.C
return err
}
highestSharedBlockHeader, err := consensus.GetBlockHeader(highestSharedBlockHash)
if err != nil {
return err
}
progressReporter := newIBDProgressReporter(highestSharedBlockHeader.DAAScore(), highBlockDAAScore, "block headers")
// Keep a short queue of BlockHeadersMessages so that there's
// never a moment when the node is not validating and inserting
// headers
@@ -329,24 +267,21 @@ func (flow *handleIBDFlow) syncPruningPointFutureHeaders(consensus externalapi.C
return err
}
}
lastReceivedHeader := ibdBlocksMessage.BlockHeaders[len(ibdBlocksMessage.BlockHeaders)-1]
progressReporter.reportProgress(len(ibdBlocksMessage.BlockHeaders), lastReceivedHeader.DAAScore)
case err := <-errChan:
return err
}
}
}
func (flow *handleIBDFlow) sendRequestHeaders(highestSharedBlockHash *externalapi.DomainHash,
func (flow *handleRelayInvsFlow) sendRequestHeaders(highestSharedBlockHash *externalapi.DomainHash,
peerSelectedTipHash *externalapi.DomainHash) error {
msgGetBlockInvs := appmessage.NewMsgRequstHeaders(highestSharedBlockHash, peerSelectedTipHash)
return flow.outgoingRoute.Enqueue(msgGetBlockInvs)
}
func (flow *handleIBDFlow) receiveHeaders() (msgIBDBlock *appmessage.BlockHeadersMessage, doneHeaders bool, err error) {
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
func (flow *handleRelayInvsFlow) receiveHeaders() (msgIBDBlock *appmessage.BlockHeadersMessage, doneHeaders bool, err error) {
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return nil, false, err
}
@@ -365,7 +300,7 @@ func (flow *handleIBDFlow) receiveHeaders() (msgIBDBlock *appmessage.BlockHeader
}
}
func (flow *handleIBDFlow) processHeader(consensus externalapi.Consensus, msgBlockHeader *appmessage.MsgBlockHeader) error {
func (flow *handleRelayInvsFlow) processHeader(consensus externalapi.Consensus, msgBlockHeader *appmessage.MsgBlockHeader) error {
header := appmessage.BlockHeaderToDomainBlockHeader(msgBlockHeader)
block := &externalapi.DomainBlock{
Header: header,
@@ -398,7 +333,7 @@ func (flow *handleIBDFlow) processHeader(consensus externalapi.Consensus, msgBlo
return nil
}
func (flow *handleIBDFlow) validatePruningPointFutureHeaderTimestamps() error {
func (flow *handleRelayInvsFlow) validatePruningPointFutureHeaderTimestamps() error {
headerSelectedTipHash, err := flow.Domain().StagingConsensus().GetHeadersSelectedTip()
if err != nil {
return err
@@ -432,7 +367,7 @@ func (flow *handleIBDFlow) validatePruningPointFutureHeaderTimestamps() error {
return nil
}
func (flow *handleIBDFlow) receiveAndInsertPruningPointUTXOSet(
func (flow *handleRelayInvsFlow) receiveAndInsertPruningPointUTXOSet(
consensus externalapi.Consensus, pruningPointHash *externalapi.DomainHash) (bool, error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "receiveAndInsertPruningPointUTXOSet")
@@ -441,7 +376,7 @@ func (flow *handleIBDFlow) receiveAndInsertPruningPointUTXOSet(
receivedChunkCount := 0
receivedUTXOCount := 0
for {
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return false, err
}
@@ -487,7 +422,7 @@ func (flow *handleIBDFlow) receiveAndInsertPruningPointUTXOSet(
}
}
func (flow *handleIBDFlow) syncMissingBlockBodies(highHash *externalapi.DomainHash) error {
func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.DomainHash) error {
hashes, err := flow.Domain().Consensus().GetMissingBlockBodyHashes(highHash)
if err != nil {
return err
@@ -500,17 +435,6 @@ func (flow *handleIBDFlow) syncMissingBlockBodies(highHash *externalapi.DomainHa
return nil
}
lowBlockHeader, err := flow.Domain().Consensus().GetBlockHeader(hashes[0])
if err != nil {
return err
}
highBlockHeader, err := flow.Domain().Consensus().GetBlockHeader(hashes[len(hashes)-1])
if err != nil {
return err
}
progressReporter := newIBDProgressReporter(lowBlockHeader.DAAScore(), highBlockHeader.DAAScore(), "blocks")
highestProcessedDAAScore := lowBlockHeader.DAAScore()
for offset := 0; offset < len(hashes); offset += ibdBatchSize {
var hashesToRequest []*externalapi.DomainHash
if offset+ibdBatchSize < len(hashes) {
@@ -525,7 +449,7 @@ func (flow *handleIBDFlow) syncMissingBlockBodies(highHash *externalapi.DomainHa
}
for _, expectedHash := range hashesToRequest {
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return err
}
@@ -559,44 +483,16 @@ func (flow *handleIBDFlow) syncMissingBlockBodies(highHash *externalapi.DomainHa
if err != nil {
return err
}
highestProcessedDAAScore = block.Header.DAAScore()
}
progressReporter.reportProgress(len(hashesToRequest), highestProcessedDAAScore)
}
return flow.resolveVirtual(highestProcessedDAAScore)
return flow.resolveVirtual()
}
func (flow *handleIBDFlow) banIfBlockIsHeaderOnly(block *externalapi.DomainBlock) error {
if len(block.Transactions) == 0 {
return protocolerrors.Errorf(true, "sent header of %s block where expected block with body",
consensushashing.BlockHash(block))
}
return nil
}
func (flow *handleIBDFlow) resolveVirtual(estimatedVirtualDAAScoreTarget uint64) error {
virtualDAAScoreStart, err := flow.Domain().Consensus().GetVirtualDAAScore()
if err != nil {
return err
}
func (flow *handleRelayInvsFlow) resolveVirtual() error {
for i := 0; ; i++ {
if i%10 == 0 {
virtualDAAScore, err := flow.Domain().Consensus().GetVirtualDAAScore()
if err != nil {
return err
}
var percents int
if estimatedVirtualDAAScoreTarget-virtualDAAScoreStart <= 0 {
percents = 100
} else {
percents = int(float64(virtualDAAScore-virtualDAAScoreStart) / float64(estimatedVirtualDAAScoreTarget-virtualDAAScoreStart) * 100)
}
log.Infof("Resolving virtual. Estimated progress: %d%%", percents)
log.Infof("Resolving virtual. This may take some time...")
}
virtualChangeSet, isCompletelyResolved, err := flow.Domain().Consensus().ResolveVirtual()
if err != nil {
@@ -614,3 +510,18 @@ func (flow *handleIBDFlow) resolveVirtual(estimatedVirtualDAAScoreTarget uint64)
}
}
}
// dequeueIncomingMessageAndSkipInvs is a convenience method to be used during
// IBD. Inv messages are expected to arrive at any given moment, but should be
// ignored while we're in IBD
func (flow *handleRelayInvsFlow) dequeueIncomingMessageAndSkipInvs(timeout time.Duration) (appmessage.Message, error) {
for {
message, err := flow.incomingRoute.DequeueWithTimeout(timeout)
if err != nil {
return nil, err
}
if _, ok := message.(*appmessage.MsgInvRelayBlock); !ok {
return message, nil
}
}
}

View File

@@ -9,16 +9,15 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/pkg/errors"
"time"
)
func (flow *handleIBDFlow) ibdWithHeadersProof(highHash *externalapi.DomainHash, highBlockDAAScore uint64) error {
func (flow *handleRelayInvsFlow) ibdWithHeadersProof(highHash *externalapi.DomainHash) error {
err := flow.Domain().InitStagingConsensus()
if err != nil {
return err
}
err = flow.downloadHeadersAndPruningUTXOSet(highHash, highBlockDAAScore)
err = flow.downloadHeadersAndPruningUTXOSet(highHash)
if err != nil {
if !flow.IsRecoverableError(err) {
return err
@@ -45,7 +44,7 @@ func (flow *handleIBDFlow) ibdWithHeadersProof(highHash *externalapi.DomainHash,
return nil
}
func (flow *handleIBDFlow) shouldSyncAndShouldDownloadHeadersProof(highBlock *externalapi.DomainBlock,
func (flow *handleRelayInvsFlow) shouldSyncAndShouldDownloadHeadersProof(highBlock *externalapi.DomainBlock,
highestSharedBlockFound bool) (shouldDownload, shouldSync bool, err error) {
if !highestSharedBlockFound {
@@ -64,7 +63,7 @@ func (flow *handleIBDFlow) shouldSyncAndShouldDownloadHeadersProof(highBlock *ex
return false, true, nil
}
func (flow *handleIBDFlow) checkIfHighHashHasMoreBlueWorkThanSelectedTipAndPruningDepthMoreBlueScore(highBlock *externalapi.DomainBlock) (bool, error) {
func (flow *handleRelayInvsFlow) checkIfHighHashHasMoreBlueWorkThanSelectedTipAndPruningDepthMoreBlueScore(highBlock *externalapi.DomainBlock) (bool, error) {
headersSelectedTip, err := flow.Domain().Consensus().GetHeadersSelectedTip()
if err != nil {
return false, err
@@ -82,13 +81,13 @@ func (flow *handleIBDFlow) checkIfHighHashHasMoreBlueWorkThanSelectedTipAndPruni
return highBlock.Header.BlueWork().Cmp(headersSelectedTipInfo.BlueWork) > 0, nil
}
func (flow *handleIBDFlow) syncAndValidatePruningPointProof() (*externalapi.DomainHash, error) {
func (flow *handleRelayInvsFlow) syncAndValidatePruningPointProof() (*externalapi.DomainHash, error) {
log.Infof("Downloading the pruning point proof from %s", flow.peer)
err := flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestPruningPointProof())
if err != nil {
return nil, err
}
message, err := flow.incomingRoute.DequeueWithTimeout(10 * time.Minute)
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return nil, err
}
@@ -114,7 +113,7 @@ func (flow *handleIBDFlow) syncAndValidatePruningPointProof() (*externalapi.Doma
return consensushashing.HeaderHash(pruningPointProof.Headers[0][len(pruningPointProof.Headers[0])-1]), nil
}
func (flow *handleIBDFlow) downloadHeadersAndPruningUTXOSet(highHash *externalapi.DomainHash, highBlockDAAScore uint64) error {
func (flow *handleRelayInvsFlow) downloadHeadersAndPruningUTXOSet(highHash *externalapi.DomainHash) error {
proofPruningPoint, err := flow.syncAndValidatePruningPointProof()
if err != nil {
return err
@@ -131,7 +130,7 @@ func (flow *handleIBDFlow) downloadHeadersAndPruningUTXOSet(highHash *externalap
return protocolerrors.Errorf(true, "the genesis pruning point violates finality")
}
err = flow.syncPruningPointFutureHeaders(flow.Domain().StagingConsensus(), proofPruningPoint, highHash, highBlockDAAScore)
err = flow.syncPruningPointFutureHeaders(flow.Domain().StagingConsensus(), proofPruningPoint, highHash)
if err != nil {
return err
}
@@ -165,7 +164,7 @@ func (flow *handleIBDFlow) downloadHeadersAndPruningUTXOSet(highHash *externalap
return nil
}
func (flow *handleIBDFlow) syncPruningPointsAndPruningPointAnticone(proofPruningPoint *externalapi.DomainHash) error {
func (flow *handleRelayInvsFlow) syncPruningPointsAndPruningPointAnticone(proofPruningPoint *externalapi.DomainHash) error {
log.Infof("Downloading the past pruning points and the pruning point anticone from %s", flow.peer)
err := flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestPruningPointAndItsAnticone())
if err != nil {
@@ -177,17 +176,6 @@ func (flow *handleIBDFlow) syncPruningPointsAndPruningPointAnticone(proofPruning
return err
}
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
if err != nil {
return err
}
msgTrustedData, ok := message.(*appmessage.MsgTrustedData)
if !ok {
return protocolerrors.Errorf(true, "received unexpected message type. "+
"expected: %s, got: %s", appmessage.CmdTrustedData, message.Command())
}
pruningPointWithMetaData, done, err := flow.receiveBlockWithTrustedData()
if err != nil {
return err
@@ -201,7 +189,7 @@ func (flow *handleIBDFlow) syncPruningPointsAndPruningPointAnticone(proofPruning
return protocolerrors.Errorf(true, "first block with trusted data is not the pruning point")
}
err = flow.processBlockWithTrustedData(flow.Domain().StagingConsensus(), pruningPointWithMetaData, msgTrustedData)
err = flow.processBlockWithTrustedData(flow.Domain().StagingConsensus(), pruningPointWithMetaData)
if err != nil {
return err
}
@@ -216,7 +204,7 @@ func (flow *handleIBDFlow) syncPruningPointsAndPruningPointAnticone(proofPruning
break
}
err = flow.processBlockWithTrustedData(flow.Domain().StagingConsensus(), blockWithTrustedData, msgTrustedData)
err = flow.processBlockWithTrustedData(flow.Domain().StagingConsensus(), blockWithTrustedData)
if err != nil {
return err
}
@@ -226,35 +214,21 @@ func (flow *handleIBDFlow) syncPruningPointsAndPruningPointAnticone(proofPruning
return nil
}
func (flow *handleIBDFlow) processBlockWithTrustedData(
consensus externalapi.Consensus, block *appmessage.MsgBlockWithTrustedDataV4, data *appmessage.MsgTrustedData) error {
func (flow *handleRelayInvsFlow) processBlockWithTrustedData(
consensus externalapi.Consensus, block *appmessage.MsgBlockWithTrustedData) error {
blockWithTrustedData := &externalapi.BlockWithTrustedData{
Block: appmessage.MsgBlockToDomainBlock(block.Block),
DAAWindow: make([]*externalapi.TrustedDataDataDAAHeader, 0, len(block.DAAWindowIndices)),
GHOSTDAGData: make([]*externalapi.BlockGHOSTDAGDataHashPair, 0, len(block.GHOSTDAGDataIndices)),
}
for _, index := range block.DAAWindowIndices {
blockWithTrustedData.DAAWindow = append(blockWithTrustedData.DAAWindow, appmessage.TrustedDataDataDAABlockV4ToTrustedDataDataDAAHeader(data.DAAWindow[index]))
}
for _, index := range block.GHOSTDAGDataIndices {
blockWithTrustedData.GHOSTDAGData = append(blockWithTrustedData.GHOSTDAGData, appmessage.GHOSTDAGHashPairToDomainGHOSTDAGHashPair(data.GHOSTDAGData[index]))
}
_, err := consensus.ValidateAndInsertBlockWithTrustedData(blockWithTrustedData, false)
_, err := consensus.ValidateAndInsertBlockWithTrustedData(appmessage.BlockWithTrustedDataToDomainBlockWithTrustedData(block), false)
return err
}
func (flow *handleIBDFlow) receiveBlockWithTrustedData() (*appmessage.MsgBlockWithTrustedDataV4, bool, error) {
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
func (flow *handleRelayInvsFlow) receiveBlockWithTrustedData() (*appmessage.MsgBlockWithTrustedData, bool, error) {
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return nil, false, err
}
switch downCastedMessage := message.(type) {
case *appmessage.MsgBlockWithTrustedDataV4:
case *appmessage.MsgBlockWithTrustedData:
return downCastedMessage, false, nil
case *appmessage.MsgDoneBlocksWithTrustedData:
return nil, true, nil
@@ -268,8 +242,8 @@ func (flow *handleIBDFlow) receiveBlockWithTrustedData() (*appmessage.MsgBlockWi
}
}
func (flow *handleIBDFlow) receivePruningPoints() (*appmessage.MsgPruningPoints, error) {
message, err := flow.incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
func (flow *handleRelayInvsFlow) receivePruningPoints() (*appmessage.MsgPruningPoints, error) {
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
if err != nil {
return nil, err
}
@@ -284,7 +258,7 @@ func (flow *handleIBDFlow) receivePruningPoints() (*appmessage.MsgPruningPoints,
return msgPruningPoints, nil
}
func (flow *handleIBDFlow) validateAndInsertPruningPoints(proofPruningPoint *externalapi.DomainHash) error {
func (flow *handleRelayInvsFlow) validateAndInsertPruningPoints(proofPruningPoint *externalapi.DomainHash) error {
currentPruningPoint, err := flow.Domain().Consensus().PruningPoint()
if err != nil {
return err
@@ -328,7 +302,7 @@ func (flow *handleIBDFlow) validateAndInsertPruningPoints(proofPruningPoint *ext
return nil
}
func (flow *handleIBDFlow) syncPruningPointUTXOSet(consensus externalapi.Consensus,
func (flow *handleRelayInvsFlow) syncPruningPointUTXOSet(consensus externalapi.Consensus,
pruningPoint *externalapi.DomainHash) (bool, error) {
log.Infof("Checking if the suggested pruning point %s is compatible to the node DAG", pruningPoint)
@@ -356,7 +330,7 @@ func (flow *handleIBDFlow) syncPruningPointUTXOSet(consensus externalapi.Consens
return true, nil
}
func (flow *handleIBDFlow) fetchMissingUTXOSet(consensus externalapi.Consensus, pruningPointHash *externalapi.DomainHash) (succeed bool, err error) {
func (flow *handleRelayInvsFlow) fetchMissingUTXOSet(consensus externalapi.Consensus, pruningPointHash *externalapi.DomainHash) (succeed bool, err error) {
defer func() {
err := flow.Domain().StagingConsensus().ClearImportedPruningPointData()
if err != nil {

View File

@@ -1,4 +1,4 @@
package flowcontext
package blockrelay
import (
"sync"
@@ -13,15 +13,13 @@ type SharedRequestedBlocks struct {
sync.Mutex
}
// Remove removes a block from the set.
func (s *SharedRequestedBlocks) Remove(hash *externalapi.DomainHash) {
func (s *SharedRequestedBlocks) remove(hash *externalapi.DomainHash) {
s.Lock()
defer s.Unlock()
delete(s.blocks, *hash)
}
// RemoveSet removes a set of blocks from the set.
func (s *SharedRequestedBlocks) RemoveSet(blockHashes map[externalapi.DomainHash]struct{}) {
func (s *SharedRequestedBlocks) removeSet(blockHashes map[externalapi.DomainHash]struct{}) {
s.Lock()
defer s.Unlock()
for hash := range blockHashes {
@@ -29,8 +27,7 @@ func (s *SharedRequestedBlocks) RemoveSet(blockHashes map[externalapi.DomainHash
}
}
// AddIfNotExists adds a block to the set if it doesn't exist yet.
func (s *SharedRequestedBlocks) AddIfNotExists(hash *externalapi.DomainHash) (exists bool) {
func (s *SharedRequestedBlocks) addIfNotExists(hash *externalapi.DomainHash) (exists bool) {
s.Lock()
defer s.Unlock()
_, ok := s.blocks[*hash]

View File

@@ -28,7 +28,7 @@ type HandleHandshakeContext interface {
HandleError(err error, flowName string, isStopping *uint32, errChan chan<- error)
}
// HandleHandshake sets up the new_handshake protocol - It sends a version message and waits for an incoming
// HandleHandshake sets up the handshake protocol - It sends a version message and waits for an incoming
// version message, as well as a verack for the sent version
func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.NetConnection,
receiveVersionRoute *routerpkg.Route, sendVersionRoute *routerpkg.Route, outgoingRoute *routerpkg.Route,
@@ -98,7 +98,7 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
}
// Handshake is different from other flows, since in it should forward router.ErrRouteClosed to errChan
// Therefore we implement a separate handleError for new_handshake
// Therefore we implement a separate handleError for handshake
func handleError(err error, flowName string, isStopping *uint32, errChan chan error) {
if errors.Is(err, routerpkg.ErrRouteClosed) {
if atomic.AddUint32(isStopping, 1) == 1 {

View File

@@ -7,7 +7,6 @@ import (
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/pkg/errors"
)
var (
@@ -18,9 +17,7 @@ var (
// minAcceptableProtocolVersion is the lowest protocol version that a
// connected peer may support.
minAcceptableProtocolVersion = uint32(4)
maxAcceptableProtocolVersion = uint32(4)
minAcceptableProtocolVersion = appmessage.ProtocolVersion
)
type receiveVersionFlow struct {
@@ -100,12 +97,7 @@ func (flow *receiveVersionFlow) start() (*appmessage.NetAddress, error) {
return nil, protocolerrors.New(false, "incompatible subnetworks")
}
if flow.Config().ProtocolVersion > maxAcceptableProtocolVersion {
return nil, errors.Errorf("%d is a non existing protocol version", flow.Config().ProtocolVersion)
}
maxProtocolVersion := flow.Config().ProtocolVersion
flow.peer.UpdateFieldsFromMsgVersion(msgVersion, maxProtocolVersion)
flow.peer.UpdateFieldsFromMsgVersion(msgVersion)
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgVerAck())
if err != nil {
return nil, err

View File

@@ -7,7 +7,6 @@ import (
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/version"
"github.com/pkg/errors"
)
var (
@@ -57,18 +56,15 @@ func (flow *sendVersionFlow) start() error {
// Version message.
localAddress := flow.AddressManager().BestLocalAddress(flow.peer.Connection().NetAddress())
subnetworkID := flow.Config().SubnetworkID
if flow.Config().ProtocolVersion < minAcceptableProtocolVersion {
return errors.Errorf("configured protocol version %d is obsolete", flow.Config().ProtocolVersion)
}
msg := appmessage.NewMsgVersion(localAddress, flow.NetAdapter().ID(),
flow.Config().ActiveNetParams.Name, subnetworkID, flow.Config().ProtocolVersion)
flow.Config().ActiveNetParams.Name, subnetworkID)
msg.AddUserAgent(userAgentName, userAgentVersion, flow.Config().UserAgentComments...)
// Advertise the services flag
msg.Services = defaultServices
// Advertise our max supported protocol version.
msg.ProtocolVersion = flow.Config().ProtocolVersion
msg.ProtocolVersion = appmessage.ProtocolVersion
// Advertise if inv messages for transactions are desired.
msg.DisableRelayTx = flow.Config().BlocksOnly

View File

@@ -1,9 +0,0 @@
package ready
import (
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/kaspanet/kaspad/util/panics"
)
var log = logger.RegisterSubSystem("PROT")
var spawn = panics.GoroutineWrapperFunc(log)

View File

@@ -1,56 +0,0 @@
package ready
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/common"
"sync/atomic"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/pkg/errors"
)
// HandleReady notify the other peer that peer is ready for messages, and wait for the other peer
// to send a ready message before start running the flows.
func HandleReady(incomingRoute *routerpkg.Route, outgoingRoute *routerpkg.Route,
peer *peerpkg.Peer,
) error {
log.Debugf("Sending ready message to %s", peer)
isStopping := uint32(0)
err := outgoingRoute.Enqueue(appmessage.NewMsgReady())
if err != nil {
return handleError(err, "HandleReady", &isStopping)
}
_, err = incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
if err != nil {
return handleError(err, "HandleReady", &isStopping)
}
log.Debugf("Got ready message from %s", peer)
return nil
}
// Ready is different from other flows, since in it should forward router.ErrRouteClosed to errChan
// Therefore we implement a separate handleError for 'ready'
func handleError(err error, flowName string, isStopping *uint32) error {
if errors.Is(err, routerpkg.ErrRouteClosed) {
if atomic.AddUint32(isStopping, 1) == 1 {
return err
}
return nil
}
if protocolErr := (protocolerrors.ProtocolError{}); errors.As(err, &protocolErr) {
log.Errorf("Ready protocol error from %s: %s", flowName, err)
if atomic.AddUint32(isStopping, 1) == 1 {
return err
}
return nil
}
panic(err)
}

View File

@@ -1,11 +1,11 @@
package testing
import (
"github.com/kaspanet/kaspad/app/protocol/flows/v4/addressexchange"
"testing"
"time"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/flows/addressexchange"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/domain/consensus"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"

View File

@@ -3,7 +3,6 @@ package transactionrelay
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/common"
"github.com/kaspanet/kaspad/app/protocol/flowcontext"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
@@ -19,7 +18,7 @@ import (
type TransactionsRelayContext interface {
NetAdapter() *netadapter.NetAdapter
Domain() domain.Domain
SharedRequestedTransactions() *flowcontext.SharedRequestedTransactions
SharedRequestedTransactions() *SharedRequestedTransactions
OnTransactionAddedToMempool()
EnqueueTransactionIDsForPropagation(transactionIDs []*externalapi.DomainTransactionID) error
}
@@ -69,7 +68,7 @@ func (flow *handleRelayedTransactionsFlow) requestInvTransactions(
if flow.isKnownTransaction(txID) {
continue
}
exists := flow.SharedRequestedTransactions().AddIfNotExists(txID)
exists := flow.SharedRequestedTransactions().addIfNotExists(txID)
if exists {
continue
}
@@ -83,7 +82,7 @@ func (flow *handleRelayedTransactionsFlow) requestInvTransactions(
msgGetTransactions := appmessage.NewMsgRequestTransactions(idsToRequest)
err = flow.outgoingRoute.Enqueue(msgGetTransactions)
if err != nil {
flow.SharedRequestedTransactions().RemoveMany(idsToRequest)
flow.SharedRequestedTransactions().removeMany(idsToRequest)
return nil, err
}
return idsToRequest, nil
@@ -152,7 +151,7 @@ func (flow *handleRelayedTransactionsFlow) readMsgTxOrNotFound() (
func (flow *handleRelayedTransactionsFlow) receiveTransactions(requestedTransactions []*externalapi.DomainTransactionID) error {
// In case the function returns earlier than expected, we want to make sure sharedRequestedTransactions is
// clean from any pending transactions.
defer flow.SharedRequestedTransactions().RemoveMany(requestedTransactions)
defer flow.SharedRequestedTransactions().removeMany(requestedTransactions)
for _, expectedID := range requestedTransactions {
msgTx, msgTxNotFound, err := flow.readMsgTxOrNotFound()
if err != nil {

View File

@@ -2,11 +2,10 @@ package transactionrelay_test
import (
"errors"
"github.com/kaspanet/kaspad/app/protocol/flowcontext"
"github.com/kaspanet/kaspad/app/protocol/flows/v4/transactionrelay"
"strings"
"testing"
"github.com/kaspanet/kaspad/app/protocol/flows/transactionrelay"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/domain/consensus"
@@ -25,7 +24,7 @@ import (
type mocTransactionsRelayContext struct {
netAdapter *netadapter.NetAdapter
domain domain.Domain
sharedRequestedTransactions *flowcontext.SharedRequestedTransactions
sharedRequestedTransactions *transactionrelay.SharedRequestedTransactions
}
func (m *mocTransactionsRelayContext) NetAdapter() *netadapter.NetAdapter {
@@ -36,7 +35,7 @@ func (m *mocTransactionsRelayContext) Domain() domain.Domain {
return m.domain
}
func (m *mocTransactionsRelayContext) SharedRequestedTransactions() *flowcontext.SharedRequestedTransactions {
func (m *mocTransactionsRelayContext) SharedRequestedTransactions() *transactionrelay.SharedRequestedTransactions {
return m.sharedRequestedTransactions
}
@@ -61,7 +60,7 @@ func TestHandleRelayedTransactionsNotFound(t *testing.T) {
}
defer teardown(false)
sharedRequestedTransactions := flowcontext.NewSharedRequestedTransactions()
sharedRequestedTransactions := transactionrelay.NewSharedRequestedTransactions()
adapter, err := netadapter.NewNetAdapter(config.DefaultConfig())
if err != nil {
t.Fatalf("Failed to create a NetAdapter: %v", err)
@@ -154,7 +153,7 @@ func TestOnClosedIncomingRoute(t *testing.T) {
}
defer teardown(false)
sharedRequestedTransactions := flowcontext.NewSharedRequestedTransactions()
sharedRequestedTransactions := transactionrelay.NewSharedRequestedTransactions()
adapter, err := netadapter.NewNetAdapter(config.DefaultConfig())
if err != nil {
t.Fatalf("Failed to creat a NetAdapter : %v", err)

View File

@@ -1,11 +1,10 @@
package transactionrelay_test
import (
"github.com/kaspanet/kaspad/app/protocol/flowcontext"
"github.com/kaspanet/kaspad/app/protocol/flows/v4/transactionrelay"
"testing"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/flows/transactionrelay"
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/domain/consensus"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
@@ -32,7 +31,7 @@ func TestHandleRequestedTransactionsNotFound(t *testing.T) {
}
defer teardown(false)
sharedRequestedTransactions := flowcontext.NewSharedRequestedTransactions()
sharedRequestedTransactions := transactionrelay.NewSharedRequestedTransactions()
adapter, err := netadapter.NewNetAdapter(config.DefaultConfig())
if err != nil {
t.Fatalf("Failed to create a NetAdapter: %v", err)

View File

@@ -1,4 +1,4 @@
package flowcontext
package transactionrelay
import (
"sync"
@@ -13,15 +13,13 @@ type SharedRequestedTransactions struct {
sync.Mutex
}
// Remove removes a transaction from the set.
func (s *SharedRequestedTransactions) Remove(txID *externalapi.DomainTransactionID) {
func (s *SharedRequestedTransactions) remove(txID *externalapi.DomainTransactionID) {
s.Lock()
defer s.Unlock()
delete(s.transactions, *txID)
}
// RemoveMany removes a set of transactions from the set.
func (s *SharedRequestedTransactions) RemoveMany(txIDs []*externalapi.DomainTransactionID) {
func (s *SharedRequestedTransactions) removeMany(txIDs []*externalapi.DomainTransactionID) {
s.Lock()
defer s.Unlock()
for _, txID := range txIDs {
@@ -29,8 +27,7 @@ func (s *SharedRequestedTransactions) RemoveMany(txIDs []*externalapi.DomainTran
}
}
// AddIfNotExists adds a transaction to the set if it doesn't exist yet.
func (s *SharedRequestedTransactions) AddIfNotExists(txID *externalapi.DomainTransactionID) (exists bool) {
func (s *SharedRequestedTransactions) addIfNotExists(txID *externalapi.DomainTransactionID) (exists bool) {
s.Lock()
defer s.Unlock()
_, ok := s.transactions[*txID]

View File

@@ -1,145 +0,0 @@
package blockrelay
import (
"github.com/kaspanet/kaspad/app/appmessage"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/domain"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"sync/atomic"
)
// PruningPointAndItsAnticoneRequestsContext is the interface for the context needed for the HandlePruningPointAndItsAnticoneRequests flow.
type PruningPointAndItsAnticoneRequestsContext interface {
Domain() domain.Domain
Config() *config.Config
}
var isBusy uint32
// HandlePruningPointAndItsAnticoneRequests listens to appmessage.MsgRequestPruningPointAndItsAnticone messages and sends
// the pruning point and its anticone to the requesting peer.
func HandlePruningPointAndItsAnticoneRequests(context PruningPointAndItsAnticoneRequestsContext, incomingRoute *router.Route,
outgoingRoute *router.Route, peer *peerpkg.Peer) error {
for {
err := func() error {
_, err := incomingRoute.Dequeue()
if err != nil {
return err
}
if !atomic.CompareAndSwapUint32(&isBusy, 0, 1) {
return protocolerrors.Errorf(false, "node is busy with other pruning point anticone requests")
}
defer atomic.StoreUint32(&isBusy, 0)
log.Debugf("Got request for pruning point and its anticone from %s", peer)
pruningPointHeaders, err := context.Domain().Consensus().PruningPointHeaders()
if err != nil {
return err
}
msgPruningPointHeaders := make([]*appmessage.MsgBlockHeader, len(pruningPointHeaders))
for i, header := range pruningPointHeaders {
msgPruningPointHeaders[i] = appmessage.DomainBlockHeaderToBlockHeader(header)
}
err = outgoingRoute.Enqueue(appmessage.NewMsgPruningPoints(msgPruningPointHeaders))
if err != nil {
return err
}
pointAndItsAnticone, err := context.Domain().Consensus().PruningPointAndItsAnticone()
if err != nil {
return err
}
windowSize := context.Config().NetParams().DifficultyAdjustmentWindowSize
daaWindowBlocks := make([]*externalapi.TrustedDataDataDAAHeader, 0, windowSize)
daaWindowHashesToIndex := make(map[externalapi.DomainHash]int, windowSize)
trustedDataDAABlockIndexes := make(map[externalapi.DomainHash][]uint64)
ghostdagData := make([]*externalapi.BlockGHOSTDAGDataHashPair, 0)
ghostdagDataHashToIndex := make(map[externalapi.DomainHash]int)
trustedDataGHOSTDAGDataIndexes := make(map[externalapi.DomainHash][]uint64)
for _, blockHash := range pointAndItsAnticone {
blockDAAWindowHashes, err := context.Domain().Consensus().BlockDAAWindowHashes(blockHash)
if err != nil {
return err
}
trustedDataDAABlockIndexes[*blockHash] = make([]uint64, 0, windowSize)
for i, daaBlockHash := range blockDAAWindowHashes {
index, exists := daaWindowHashesToIndex[*daaBlockHash]
if !exists {
trustedDataDataDAAHeader, err := context.Domain().Consensus().TrustedDataDataDAAHeader(blockHash, daaBlockHash, uint64(i))
if err != nil {
return err
}
daaWindowBlocks = append(daaWindowBlocks, trustedDataDataDAAHeader)
index = len(daaWindowBlocks) - 1
daaWindowHashesToIndex[*daaBlockHash] = index
}
trustedDataDAABlockIndexes[*blockHash] = append(trustedDataDAABlockIndexes[*blockHash], uint64(index))
}
ghostdagDataBlockHashes, err := context.Domain().Consensus().TrustedBlockAssociatedGHOSTDAGDataBlockHashes(blockHash)
if err != nil {
return err
}
trustedDataGHOSTDAGDataIndexes[*blockHash] = make([]uint64, 0, context.Config().NetParams().K)
for _, ghostdagDataBlockHash := range ghostdagDataBlockHashes {
index, exists := ghostdagDataHashToIndex[*ghostdagDataBlockHash]
if !exists {
data, err := context.Domain().Consensus().TrustedGHOSTDAGData(ghostdagDataBlockHash)
if err != nil {
return err
}
ghostdagData = append(ghostdagData, &externalapi.BlockGHOSTDAGDataHashPair{
Hash: ghostdagDataBlockHash,
GHOSTDAGData: data,
})
index = len(ghostdagData) - 1
ghostdagDataHashToIndex[*ghostdagDataBlockHash] = index
}
trustedDataGHOSTDAGDataIndexes[*blockHash] = append(trustedDataGHOSTDAGDataIndexes[*blockHash], uint64(index))
}
}
err = outgoingRoute.Enqueue(appmessage.DomainTrustedDataToTrustedData(daaWindowBlocks, ghostdagData))
if err != nil {
return err
}
for _, blockHash := range pointAndItsAnticone {
block, err := context.Domain().Consensus().GetBlock(blockHash)
if err != nil {
return err
}
err = outgoingRoute.Enqueue(appmessage.DomainBlockWithTrustedDataToBlockWithTrustedDataV4(block, trustedDataDAABlockIndexes[*blockHash], trustedDataGHOSTDAGDataIndexes[*blockHash]))
if err != nil {
return err
}
}
err = outgoingRoute.Enqueue(appmessage.NewMsgDoneBlocksWithTrustedData())
if err != nil {
return err
}
log.Debugf("Sent pruning point and its anticone to %s", peer)
return nil
}()
if err != nil {
return err
}
}
}

View File

@@ -1,32 +0,0 @@
package blockrelay
type ibdProgressReporter struct {
lowDAAScore uint64
highDAAScore uint64
objectName string
totalDAAScoreDifference uint64
lastReportedProgressPercent int
processed int
}
func newIBDProgressReporter(lowDAAScore uint64, highDAAScore uint64, objectName string) *ibdProgressReporter {
return &ibdProgressReporter{
lowDAAScore: lowDAAScore,
highDAAScore: highDAAScore,
objectName: objectName,
totalDAAScoreDifference: highDAAScore - lowDAAScore,
lastReportedProgressPercent: 0,
processed: 0,
}
}
func (ipr *ibdProgressReporter) reportProgress(processedDelta int, highestProcessedDAAScore uint64) {
ipr.processed += processedDelta
relativeDAAScore := highestProcessedDAAScore - ipr.lowDAAScore
progressPercent := int((float64(relativeDAAScore) / float64(ipr.totalDAAScoreDifference)) * 100)
if progressPercent > ipr.lastReportedProgressPercent {
log.Infof("IBD: Processed %d %s (%d%%)", ipr.processed, ipr.objectName, progressPercent)
ipr.lastReportedProgressPercent = progressPercent
}
}

View File

@@ -1,194 +0,0 @@
package v4
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/common"
"github.com/kaspanet/kaspad/app/protocol/flowcontext"
"github.com/kaspanet/kaspad/app/protocol/flows/v4/addressexchange"
"github.com/kaspanet/kaspad/app/protocol/flows/v4/blockrelay"
"github.com/kaspanet/kaspad/app/protocol/flows/v4/ping"
"github.com/kaspanet/kaspad/app/protocol/flows/v4/rejects"
"github.com/kaspanet/kaspad/app/protocol/flows/v4/transactionrelay"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
)
type protocolManager interface {
RegisterFlow(name string, router *routerpkg.Router, messageTypes []appmessage.MessageCommand, isStopping *uint32,
errChan chan error, initializeFunc common.FlowInitializeFunc) *common.Flow
RegisterOneTimeFlow(name string, router *routerpkg.Router, messageTypes []appmessage.MessageCommand,
isStopping *uint32, stopChan chan error, initializeFunc common.FlowInitializeFunc) *common.Flow
RegisterFlowWithCapacity(name string, capacity int, router *routerpkg.Router,
messageTypes []appmessage.MessageCommand, isStopping *uint32,
errChan chan error, initializeFunc common.FlowInitializeFunc) *common.Flow
Context() *flowcontext.FlowContext
}
// Register is used in order to register all the protocol flows to the given router.
func Register(m protocolManager, router *routerpkg.Router, errChan chan error, isStopping *uint32) (flows []*common.Flow) {
flows = registerAddressFlows(m, router, isStopping, errChan)
flows = append(flows, registerBlockRelayFlows(m, router, isStopping, errChan)...)
flows = append(flows, registerPingFlows(m, router, isStopping, errChan)...)
flows = append(flows, registerTransactionRelayFlow(m, router, isStopping, errChan)...)
flows = append(flows, registerRejectsFlow(m, router, isStopping, errChan)...)
return flows
}
func registerAddressFlows(m protocolManager, router *routerpkg.Router, isStopping *uint32, errChan chan error) []*common.Flow {
outgoingRoute := router.OutgoingRoute()
return []*common.Flow{
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)
},
),
m.RegisterOneTimeFlow("ReceiveAddresses", router, []appmessage.MessageCommand{appmessage.CmdAddresses}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return addressexchange.ReceiveAddresses(m.Context(), incomingRoute, outgoingRoute, peer)
},
),
}
}
func registerBlockRelayFlows(m protocolManager, router *routerpkg.Router, isStopping *uint32, errChan chan error) []*common.Flow {
outgoingRoute := router.OutgoingRoute()
return []*common.Flow{
m.RegisterOneTimeFlow("SendVirtualSelectedParentInv", router, []appmessage.MessageCommand{},
isStopping, errChan, func(route *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.SendVirtualSelectedParentInv(m.Context(), outgoingRoute, peer)
}),
m.RegisterFlow("HandleRelayInvs", router, []appmessage.MessageCommand{
appmessage.CmdInvRelayBlock, appmessage.CmdBlock, appmessage.CmdBlockLocator,
},
isStopping, errChan, func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRelayInvs(m.Context(), incomingRoute,
outgoingRoute, peer)
},
),
m.RegisterFlow("HandleIBD", router, []appmessage.MessageCommand{
appmessage.CmdDoneHeaders, appmessage.CmdUnexpectedPruningPoint, appmessage.CmdPruningPointUTXOSetChunk,
appmessage.CmdBlockHeaders, appmessage.CmdIBDBlockLocatorHighestHash, appmessage.CmdBlockWithTrustedDataV4,
appmessage.CmdDoneBlocksWithTrustedData, appmessage.CmdIBDBlockLocatorHighestHashNotFound,
appmessage.CmdDonePruningPointUTXOSetChunks, appmessage.CmdIBDBlock, appmessage.CmdPruningPoints,
appmessage.CmdPruningPointProof,
appmessage.CmdTrustedData,
},
isStopping, errChan, func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleIBD(m.Context(), incomingRoute,
outgoingRoute, peer)
},
),
m.RegisterFlow("HandleRelayBlockRequests", router, []appmessage.MessageCommand{appmessage.CmdRequestRelayBlocks}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRelayBlockRequests(m.Context(), incomingRoute, outgoingRoute, peer)
},
),
m.RegisterFlow("HandleRequestBlockLocator", router,
[]appmessage.MessageCommand{appmessage.CmdRequestBlockLocator}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRequestBlockLocator(m.Context(), incomingRoute, outgoingRoute)
},
),
m.RegisterFlow("HandleRequestHeaders", router,
[]appmessage.MessageCommand{appmessage.CmdRequestHeaders, appmessage.CmdRequestNextHeaders}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRequestHeaders(m.Context(), incomingRoute, outgoingRoute, peer)
},
),
m.RegisterFlow("HandleIBDBlockRequests", router,
[]appmessage.MessageCommand{appmessage.CmdRequestIBDBlocks}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleIBDBlockRequests(m.Context(), incomingRoute, outgoingRoute)
},
),
m.RegisterFlow("HandleRequestPruningPointUTXOSet", router,
[]appmessage.MessageCommand{appmessage.CmdRequestPruningPointUTXOSet,
appmessage.CmdRequestNextPruningPointUTXOSetChunk}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRequestPruningPointUTXOSet(m.Context(), incomingRoute, outgoingRoute)
},
),
m.RegisterFlow("HandlePruningPointAndItsAnticoneRequests", router,
[]appmessage.MessageCommand{appmessage.CmdRequestPruningPointAndItsAnticone}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandlePruningPointAndItsAnticoneRequests(m.Context(), incomingRoute, outgoingRoute, peer)
},
),
m.RegisterFlow("HandleIBDBlockLocator", router,
[]appmessage.MessageCommand{appmessage.CmdIBDBlockLocator}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleIBDBlockLocator(m.Context(), incomingRoute, outgoingRoute, peer)
},
),
m.RegisterFlow("HandlePruningPointProofRequests", router,
[]appmessage.MessageCommand{appmessage.CmdRequestPruningPointProof}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandlePruningPointProofRequests(m.Context(), incomingRoute, outgoingRoute, peer)
},
),
}
}
func registerPingFlows(m protocolManager, router *routerpkg.Router, isStopping *uint32, errChan chan error) []*common.Flow {
outgoingRoute := router.OutgoingRoute()
return []*common.Flow{
m.RegisterFlow("ReceivePings", router, []appmessage.MessageCommand{appmessage.CmdPing}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return ping.ReceivePings(m.Context(), incomingRoute, outgoingRoute)
},
),
m.RegisterFlow("SendPings", router, []appmessage.MessageCommand{appmessage.CmdPong}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return ping.SendPings(m.Context(), incomingRoute, outgoingRoute, peer)
},
),
}
}
func registerTransactionRelayFlow(m protocolManager, router *routerpkg.Router, isStopping *uint32, errChan chan error) []*common.Flow {
outgoingRoute := router.OutgoingRoute()
return []*common.Flow{
m.RegisterFlowWithCapacity("HandleRelayedTransactions", 10_000, router,
[]appmessage.MessageCommand{appmessage.CmdInvTransaction, appmessage.CmdTx, appmessage.CmdTransactionNotFound}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return transactionrelay.HandleRelayedTransactions(m.Context(), incomingRoute, outgoingRoute)
},
),
m.RegisterFlow("HandleRequestTransactions", router,
[]appmessage.MessageCommand{appmessage.CmdRequestTransactions}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return transactionrelay.HandleRequestedTransactions(m.Context(), incomingRoute, outgoingRoute)
},
),
}
}
func registerRejectsFlow(m protocolManager, router *routerpkg.Router, isStopping *uint32, errChan chan error) []*common.Flow {
outgoingRoute := router.OutgoingRoute()
return []*common.Flow{
m.RegisterFlow("HandleRejects", router,
[]appmessage.MessageCommand{appmessage.CmdReject}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return rejects.HandleRejects(m.Context(), incomingRoute, outgoingRoute)
},
),
}
}

View File

@@ -2,7 +2,6 @@ package protocol
import (
"fmt"
"github.com/kaspanet/kaspad/app/protocol/common"
"sync"
"sync/atomic"
@@ -72,16 +71,11 @@ func (m *Manager) AddBlock(block *externalapi.DomainBlock) error {
return m.context.AddBlock(block)
}
// Context returns the manager's flow context
func (m *Manager) Context() *flowcontext.FlowContext {
return m.context
}
func (m *Manager) runFlows(flows []*common.Flow, peer *peerpkg.Peer, errChan <-chan error, flowsWaitGroup *sync.WaitGroup) error {
func (m *Manager) runFlows(flows []*flow, peer *peerpkg.Peer, errChan <-chan error, flowsWaitGroup *sync.WaitGroup) error {
flowsWaitGroup.Add(len(flows))
for _, flow := range flows {
executeFunc := flow.ExecuteFunc // extract to new variable so that it's not overwritten
spawn(fmt.Sprintf("flow-%s", flow.Name), func() {
executeFunc := flow.executeFunc // extract to new variable so that it's not overwritten
spawn(fmt.Sprintf("flow-%s", flow.name), func() {
executeFunc(peer)
flowsWaitGroup.Done()
})

View File

@@ -13,6 +13,10 @@ import (
"github.com/kaspanet/kaspad/util/mstime"
)
// maxProtocolVersion version is the maximum supported protocol
// version this kaspad node supports
const maxProtocolVersion = 3
// Peer holds data about a peer.
type Peer struct {
connection *netadapter.NetConnection
@@ -31,8 +35,6 @@ type Peer struct {
lastPingNonce uint64 // The nonce of the last ping we sent
lastPingTime time.Time // Time we sent last ping
lastPingDuration time.Duration // Time for last ping to return
ibdRequestChannel chan *externalapi.DomainBlock // A channel used to communicate IBD requests between flows
}
// New returns a new Peer
@@ -40,7 +42,6 @@ func New(connection *netadapter.NetConnection) *Peer {
return &Peer{
connection: connection,
connectionStarted: time.Now(),
ibdRequestChannel: make(chan *externalapi.DomainBlock),
}
}
@@ -75,11 +76,6 @@ func (p *Peer) AdvertisedProtocolVersion() uint32 {
return p.advertisedProtocolVerion
}
// ProtocolVersion returns the protocol version which is used when communicating with the peer.
func (p *Peer) ProtocolVersion() uint32 {
return p.protocolVersion
}
// TimeConnected returns the time since the connection to this been has been started.
func (p *Peer) TimeConnected() time.Duration {
return time.Since(p.connectionStarted)
@@ -91,7 +87,7 @@ func (p *Peer) IsOutbound() bool {
}
// UpdateFieldsFromMsgVersion updates the peer with the data from the version message.
func (p *Peer) UpdateFieldsFromMsgVersion(msg *appmessage.MsgVersion, maxProtocolVersion uint32) {
func (p *Peer) UpdateFieldsFromMsgVersion(msg *appmessage.MsgVersion) {
// Negotiate the protocol version.
p.advertisedProtocolVerion = msg.ProtocolVersion
p.protocolVersion = mathUtil.MinUint32(maxProtocolVersion, p.advertisedProtocolVerion)
@@ -146,8 +142,3 @@ func (p *Peer) LastPingDuration() time.Duration {
return p.lastPingDuration
}
// IBDRequestChannel returns the channel used in order to communicate an IBD request between peer flows
func (p *Peer) IBDRequestChannel() chan *externalapi.DomainBlock {
return p.ibdRequestChannel
}

View File

@@ -1,14 +1,16 @@
package protocol
import (
"github.com/kaspanet/kaspad/app/protocol/common"
"github.com/kaspanet/kaspad/app/protocol/flows/ready"
v4 "github.com/kaspanet/kaspad/app/protocol/flows/v4"
"sync"
"sync/atomic"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/protocol/flows/addressexchange"
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
"github.com/kaspanet/kaspad/app/protocol/flows/handshake"
"github.com/kaspanet/kaspad/app/protocol/flows/ping"
"github.com/kaspanet/kaspad/app/protocol/flows/rejects"
"github.com/kaspanet/kaspad/app/protocol/flows/transactionrelay"
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
@@ -18,6 +20,14 @@ import (
"github.com/pkg/errors"
)
type flowInitializeFunc func(route *routerpkg.Route, peer *peerpkg.Peer) error
type flowExecuteFunc func(peer *peerpkg.Peer)
type flow struct {
name string
executeFunc flowExecuteFunc
}
func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *netadapter.NetConnection) {
// isStopping flag is raised the moment that the connection associated with this router is disconnected
// errChan is used by the flow goroutines to return to runFlows when an error occurs.
@@ -25,7 +35,8 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net
isStopping := uint32(0)
errChan := make(chan error)
receiveVersionRoute, sendVersionRoute, receiveReadyRoute := registerHandshakeRoutes(router)
flows := m.registerFlows(router, errChan, &isStopping)
receiveVersionRoute, sendVersionRoute := registerHandshakeRoutes(router)
// After flows were registered - spawn a new thread that will wait for connection to finish initializing
// and start receiving messages
@@ -73,21 +84,6 @@ func (m *Manager) routerInitializer(router *routerpkg.Router, netConnection *net
}
defer m.context.RemoveFromPeers(peer)
var flows []*common.Flow
log.Infof("Registering p2p flows for peer %s for protocol version %d", peer, peer.ProtocolVersion())
switch peer.ProtocolVersion() {
case 4:
flows = v4.Register(m, router, errChan, &isStopping)
default:
panic(errors.Errorf("no way to handle protocol version %d", peer.ProtocolVersion()))
}
err = ready.HandleReady(receiveReadyRoute, router.OutgoingRoute(), peer)
if err != nil {
m.handleError(err, netConnection, router.OutgoingRoute())
return
}
removeHandshakeRoutes(router)
flowsWaitGroup := &sync.WaitGroup{}
@@ -134,9 +130,167 @@ func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection
panic(err)
}
// RegisterFlow registers a flow to the given router.
func (m *Manager) RegisterFlow(name string, router *routerpkg.Router, messageTypes []appmessage.MessageCommand, isStopping *uint32,
errChan chan error, initializeFunc common.FlowInitializeFunc) *common.Flow {
func (m *Manager) registerFlows(router *routerpkg.Router, errChan chan error, isStopping *uint32) (flows []*flow) {
flows = m.registerAddressFlows(router, isStopping, errChan)
flows = append(flows, m.registerBlockRelayFlows(router, isStopping, errChan)...)
flows = append(flows, m.registerPingFlows(router, isStopping, errChan)...)
flows = append(flows, m.registerTransactionRelayFlow(router, isStopping, errChan)...)
flows = append(flows, m.registerRejectsFlow(router, isStopping, errChan)...)
return flows
}
func (m *Manager) registerAddressFlows(router *routerpkg.Router, isStopping *uint32, errChan chan error) []*flow {
outgoingRoute := router.OutgoingRoute()
return []*flow{
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)
},
),
m.registerOneTimeFlow("ReceiveAddresses", router, []appmessage.MessageCommand{appmessage.CmdAddresses}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return addressexchange.ReceiveAddresses(m.context, incomingRoute, outgoingRoute, peer)
},
),
}
}
func (m *Manager) registerBlockRelayFlows(router *routerpkg.Router, isStopping *uint32, errChan chan error) []*flow {
outgoingRoute := router.OutgoingRoute()
return []*flow{
m.registerOneTimeFlow("SendVirtualSelectedParentInv", router, []appmessage.MessageCommand{},
isStopping, errChan, func(route *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.SendVirtualSelectedParentInv(m.context, outgoingRoute, peer)
}),
m.registerFlow("HandleRelayInvs", router, []appmessage.MessageCommand{
appmessage.CmdInvRelayBlock, appmessage.CmdBlock, appmessage.CmdBlockLocator,
appmessage.CmdDoneHeaders, appmessage.CmdUnexpectedPruningPoint, appmessage.CmdPruningPointUTXOSetChunk,
appmessage.CmdBlockHeaders, appmessage.CmdIBDBlockLocatorHighestHash, appmessage.CmdBlockWithTrustedData,
appmessage.CmdDoneBlocksWithTrustedData, appmessage.CmdIBDBlockLocatorHighestHashNotFound,
appmessage.CmdDonePruningPointUTXOSetChunks, appmessage.CmdIBDBlock, appmessage.CmdPruningPoints,
appmessage.CmdPruningPointProof,
},
isStopping, errChan, func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRelayInvs(m.context, incomingRoute,
outgoingRoute, peer)
},
),
m.registerFlow("HandleRelayBlockRequests", router, []appmessage.MessageCommand{appmessage.CmdRequestRelayBlocks}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRelayBlockRequests(m.context, incomingRoute, outgoingRoute, peer)
},
),
m.registerFlow("HandleRequestBlockLocator", router,
[]appmessage.MessageCommand{appmessage.CmdRequestBlockLocator}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRequestBlockLocator(m.context, incomingRoute, outgoingRoute)
},
),
m.registerFlow("HandleRequestHeaders", router,
[]appmessage.MessageCommand{appmessage.CmdRequestHeaders, appmessage.CmdRequestNextHeaders}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRequestHeaders(m.context, incomingRoute, outgoingRoute, peer)
},
),
m.registerFlow("HandleIBDBlockRequests", router,
[]appmessage.MessageCommand{appmessage.CmdRequestIBDBlocks}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleIBDBlockRequests(m.context, incomingRoute, outgoingRoute)
},
),
m.registerFlow("HandleRequestPruningPointUTXOSet", router,
[]appmessage.MessageCommand{appmessage.CmdRequestPruningPointUTXOSet,
appmessage.CmdRequestNextPruningPointUTXOSetChunk}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleRequestPruningPointUTXOSet(m.context, incomingRoute, outgoingRoute)
},
),
m.registerFlow("HandlePruningPointAndItsAnticoneRequests", router,
[]appmessage.MessageCommand{appmessage.CmdRequestPruningPointAndItsAnticone}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandlePruningPointAndItsAnticoneRequests(m.context, incomingRoute, outgoingRoute, peer)
},
),
m.registerFlow("HandleIBDBlockLocator", router,
[]appmessage.MessageCommand{appmessage.CmdIBDBlockLocator}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandleIBDBlockLocator(m.context, incomingRoute, outgoingRoute, peer)
},
),
m.registerFlow("HandlePruningPointProofRequests", router,
[]appmessage.MessageCommand{appmessage.CmdRequestPruningPointProof}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return blockrelay.HandlePruningPointProofRequests(m.context, incomingRoute, outgoingRoute, peer)
},
),
}
}
func (m *Manager) registerPingFlows(router *routerpkg.Router, isStopping *uint32, errChan chan error) []*flow {
outgoingRoute := router.OutgoingRoute()
return []*flow{
m.registerFlow("ReceivePings", router, []appmessage.MessageCommand{appmessage.CmdPing}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return ping.ReceivePings(m.context, incomingRoute, outgoingRoute)
},
),
m.registerFlow("SendPings", router, []appmessage.MessageCommand{appmessage.CmdPong}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return ping.SendPings(m.context, incomingRoute, outgoingRoute, peer)
},
),
}
}
func (m *Manager) registerTransactionRelayFlow(router *routerpkg.Router, isStopping *uint32, errChan chan error) []*flow {
outgoingRoute := router.OutgoingRoute()
return []*flow{
m.registerFlowWithCapacity("HandleRelayedTransactions", 10_000, router,
[]appmessage.MessageCommand{appmessage.CmdInvTransaction, appmessage.CmdTx, appmessage.CmdTransactionNotFound}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return transactionrelay.HandleRelayedTransactions(m.context, incomingRoute, outgoingRoute)
},
),
m.registerFlow("HandleRequestTransactions", router,
[]appmessage.MessageCommand{appmessage.CmdRequestTransactions}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return transactionrelay.HandleRequestedTransactions(m.context, incomingRoute, outgoingRoute)
},
),
}
}
func (m *Manager) registerRejectsFlow(router *routerpkg.Router, isStopping *uint32, errChan chan error) []*flow {
outgoingRoute := router.OutgoingRoute()
return []*flow{
m.registerFlow("HandleRejects", router,
[]appmessage.MessageCommand{appmessage.CmdReject}, isStopping, errChan,
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
return rejects.HandleRejects(m.context, incomingRoute, outgoingRoute)
},
),
}
}
func (m *Manager) registerFlow(name string, router *routerpkg.Router, messageTypes []appmessage.MessageCommand, isStopping *uint32,
errChan chan error, initializeFunc flowInitializeFunc) *flow {
route, err := router.AddIncomingRoute(name, messageTypes)
if err != nil {
@@ -146,10 +300,9 @@ func (m *Manager) RegisterFlow(name string, router *routerpkg.Router, messageTyp
return m.registerFlowForRoute(route, name, isStopping, errChan, initializeFunc)
}
// RegisterFlowWithCapacity registers a flow to the given router with a custom capacity.
func (m *Manager) RegisterFlowWithCapacity(name string, capacity int, router *routerpkg.Router,
func (m *Manager) registerFlowWithCapacity(name string, capacity int, router *routerpkg.Router,
messageTypes []appmessage.MessageCommand, isStopping *uint32,
errChan chan error, initializeFunc common.FlowInitializeFunc) *common.Flow {
errChan chan error, initializeFunc flowInitializeFunc) *flow {
route, err := router.AddIncomingRouteWithCapacity(name, capacity, messageTypes)
if err != nil {
@@ -160,11 +313,11 @@ func (m *Manager) RegisterFlowWithCapacity(name string, capacity int, router *ro
}
func (m *Manager) registerFlowForRoute(route *routerpkg.Route, name string, isStopping *uint32,
errChan chan error, initializeFunc common.FlowInitializeFunc) *common.Flow {
errChan chan error, initializeFunc flowInitializeFunc) *flow {
return &common.Flow{
Name: name,
ExecuteFunc: func(peer *peerpkg.Peer) {
return &flow{
name: name,
executeFunc: func(peer *peerpkg.Peer) {
err := initializeFunc(route, peer)
if err != nil {
m.context.HandleError(err, name, isStopping, errChan)
@@ -174,18 +327,17 @@ func (m *Manager) registerFlowForRoute(route *routerpkg.Route, name string, isSt
}
}
// RegisterOneTimeFlow registers a one-time flow (that exits once some operations are done) to the given router.
func (m *Manager) RegisterOneTimeFlow(name string, router *routerpkg.Router, messageTypes []appmessage.MessageCommand,
isStopping *uint32, stopChan chan error, initializeFunc common.FlowInitializeFunc) *common.Flow {
func (m *Manager) registerOneTimeFlow(name string, router *routerpkg.Router, messageTypes []appmessage.MessageCommand,
isStopping *uint32, stopChan chan error, initializeFunc flowInitializeFunc) *flow {
route, err := router.AddIncomingRoute(name, messageTypes)
if err != nil {
panic(err)
}
return &common.Flow{
Name: name,
ExecuteFunc: func(peer *peerpkg.Peer) {
return &flow{
name: name,
executeFunc: func(peer *peerpkg.Peer) {
defer func() {
err := router.RemoveRoute(messageTypes)
if err != nil {
@@ -203,7 +355,7 @@ func (m *Manager) RegisterOneTimeFlow(name string, router *routerpkg.Router, mes
}
func registerHandshakeRoutes(router *routerpkg.Router) (
receiveVersionRoute, sendVersionRoute, receiveReadyRoute *routerpkg.Route) {
receiveVersionRoute *routerpkg.Route, sendVersionRoute *routerpkg.Route) {
receiveVersionRoute, err := router.AddIncomingRoute("recieveVersion - incoming", []appmessage.MessageCommand{appmessage.CmdVersion})
if err != nil {
panic(err)
@@ -214,16 +366,11 @@ func registerHandshakeRoutes(router *routerpkg.Router) (
panic(err)
}
receiveReadyRoute, err = router.AddIncomingRoute("recieveReady - incoming", []appmessage.MessageCommand{appmessage.CmdReady})
if err != nil {
panic(err)
}
return receiveVersionRoute, sendVersionRoute, receiveReadyRoute
return receiveVersionRoute, sendVersionRoute
}
func removeHandshakeRoutes(router *routerpkg.Router) {
err := router.RemoveRoute([]appmessage.MessageCommand{appmessage.CmdVersion, appmessage.CmdVerAck, appmessage.CmdReady})
err := router.RemoveRoute([]appmessage.MessageCommand{appmessage.CmdVersion, appmessage.CmdVerAck})
if err != nil {
panic(err)
}

View File

@@ -38,7 +38,6 @@ var handlers = map[appmessage.MessageCommand]handler{
appmessage.CmdNotifyUTXOsChangedRequestMessage: rpchandlers.HandleNotifyUTXOsChanged,
appmessage.CmdStopNotifyingUTXOsChangedRequestMessage: rpchandlers.HandleStopNotifyingUTXOsChanged,
appmessage.CmdGetUTXOsByAddressesRequestMessage: rpchandlers.HandleGetUTXOsByAddresses,
appmessage.CmdGetBalancesByAddressesRequestMessage: rpchandlers.HandleGetBalancesByAddresses,
appmessage.CmdGetVirtualSelectedParentBlueScoreRequestMessage: rpchandlers.HandleGetVirtualSelectedParentBlueScore,
appmessage.CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage: rpchandlers.HandleNotifyVirtualSelectedParentBlueScoreChanged,
appmessage.CmdBanRequestMessage: rpchandlers.HandleBan,

View File

@@ -6,7 +6,6 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util"
"github.com/pkg/errors"
)
// HandleGetBalanceByAddress handles the respectively named RPC command
@@ -19,39 +18,30 @@ func HandleGetBalanceByAddress(context *rpccontext.Context, _ *router.Router, re
getBalanceByAddressRequest := request.(*appmessage.GetBalanceByAddressRequestMessage)
balance, err := getBalanceByAddress(context, getBalanceByAddressRequest.Address)
var balance uint64 = 0
addressString := getBalanceByAddressRequest.Address
address, err := util.DecodeAddress(addressString, context.Config.ActiveNetParams.Prefix)
if err != nil {
rpcError := &appmessage.RPCError{}
if !errors.As(err, rpcError) {
return nil, err
}
errorMessage := &appmessage.GetUTXOsByAddressesResponseMessage{}
errorMessage.Error = rpcError
errorMessage.Error = appmessage.RPCErrorf("Could decode address '%s': %s", addressString, err)
return errorMessage, nil
}
scriptPublicKey, err := txscript.PayToAddrScript(address)
if err != nil {
errorMessage := &appmessage.GetUTXOsByAddressesResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Could not create a scriptPublicKey for address '%s': %s", addressString, err)
return errorMessage, nil
}
utxoOutpointEntryPairs, err := context.UTXOIndex.UTXOs(scriptPublicKey)
if err != nil {
return nil, err
}
for _, utxoOutpointEntryPair := range utxoOutpointEntryPairs {
balance += utxoOutpointEntryPair.Amount()
}
response := appmessage.NewGetBalanceByAddressResponse(balance)
return response, nil
}
func getBalanceByAddress(context *rpccontext.Context, addressString string) (uint64, error) {
address, err := util.DecodeAddress(addressString, context.Config.ActiveNetParams.Prefix)
if err != nil {
return 0, appmessage.RPCErrorf("Couldn't decode address '%s': %s", addressString, err)
}
scriptPublicKey, err := txscript.PayToAddrScript(address)
if err != nil {
return 0, appmessage.RPCErrorf("Could not create a scriptPublicKey for address '%s': %s", addressString, err)
}
utxoOutpointEntryPairs, err := context.UTXOIndex.UTXOs(scriptPublicKey)
if err != nil {
return 0, err
}
balance := uint64(0)
for _, utxoOutpointEntryPair := range utxoOutpointEntryPairs {
balance += utxoOutpointEntryPair.Amount()
}
return balance, nil
}

View File

@@ -1,41 +0,0 @@
package rpchandlers
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/pkg/errors"
)
// HandleGetBalancesByAddresses handles the respectively named RPC command
func HandleGetBalancesByAddresses(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
if !context.Config.UTXOIndex {
errorMessage := &appmessage.GetBalancesByAddressesResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Method unavailable when kaspad is run without --utxoindex")
return errorMessage, nil
}
getBalancesByAddressesRequest := request.(*appmessage.GetBalancesByAddressesRequestMessage)
allEntries := make([]*appmessage.BalancesByAddressesEntry, len(getBalancesByAddressesRequest.Addresses))
for i, address := range getBalancesByAddressesRequest.Addresses {
balance, err := getBalanceByAddress(context, address)
if err != nil {
rpcError := &appmessage.RPCError{}
if !errors.As(err, rpcError) {
return nil, err
}
errorMessage := &appmessage.GetUTXOsByAddressesResponseMessage{}
errorMessage.Error = rpcError
return errorMessage, nil
}
allEntries[i] = &appmessage.BalancesByAddressesEntry{
Address: address,
Balance: balance,
}
}
response := appmessage.NewGetBalancesByAddressesResponse(allEntries)
return response, nil
}

View File

@@ -34,23 +34,6 @@ func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request ap
}, nil
}
if !submitBlockRequest.AllowNonDAABlocks {
virtualDAAScore, err := context.Domain.Consensus().GetVirtualDAAScore()
if err != nil {
return nil, err
}
// A simple heuristic check which signals that the mined block is out of date
// and should not be accepted unless user explicitly requests
daaWindowSize := uint64(context.Config.NetParams().DifficultyAdjustmentWindowSize)
if virtualDAAScore > daaWindowSize && domainBlock.Header.DAAScore() < virtualDAAScore-daaWindowSize {
return &appmessage.SubmitBlockResponseMessage{
Error: appmessage.RPCErrorf("Block rejected. Reason: block DAA score %d is too far "+
"behind virtual's DAA score %d", domainBlock.Header.DAAScore(), virtualDAAScore),
RejectReason: appmessage.RejectReasonBlockInvalid,
}, nil
}
}
err = context.ProtocolManager.AddBlock(domainBlock)
if err != nil {
isProtocolOrRuleError := errors.As(err, &ruleerrors.RuleError{}) || errors.As(err, &protocolerrors.ProtocolError{})

View File

@@ -1,32 +1,3 @@
Kaspad v0.11.11 - 2022-01-27
===========================
* Fix for rare consensus bug regarding DAA window order. The bug only affected IBD from scratch and only today (#1934)
Kaspad v0.11.10 - 2022-01-27
===========================
* Add monitoring of heap and save heap profile if size is over some limit (#1932)
* Extract IBD management from invs relay flow to a new separated flow (#1930)
* Add --transaction-file options to the `sign` and `broadcast` wallet subcommands (#1927)
* Filter redundant blocks from daa window on IBD (#1925)
* Implement a P2P upgrade mechanism (#1921)
Kaspad v0.11.9 - 2021-12-30
===========================
Breaking changes:
* Implement the new monetary policy. Breaking change effective only in ~4 months (#1892)
Bug fixes:
* Fix two pruning proof IBD crash bugs (#1913)
* Fix UTXO index bug showing wrong wallet balance (#1891)
Non-breaking changes:
* Address search: cleanup repetitively-offline addresses and use randomization weighted by connection failures (#1899, #1916)
* New DNS seeders and removal of offline one (#1901, #1910, #1918)
* Add request balance by address to kaspactl (#1885)
* Wallet: show balance by addresses (#1904)
* Reject outdated non-DAA blocks submitted via RPC (#1914)
* Add a profile option to kaspawallet daemon (#1854)
Kaspad v0.11.8 - 2021-12-13
===========================
Bug fixes:

View File

@@ -12,12 +12,11 @@ var (
)
type configFlags struct {
RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
Timeout uint64 `short:"t" long:"timeout" description:"Timeout for the request (in seconds)"`
RequestJSON string `short:"j" long:"json" description:"The request in JSON format"`
ListCommands bool `short:"l" long:"list-commands" description:"List all commands and exit"`
AllowConnectionToDifferentVersions bool `short:"a" long:"allow-connection-to-different-versions" description:"Allow connections to versions different than kaspactl's version'"`
CommandAndParameters []string
RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
Timeout uint64 `short:"t" long:"timeout" description:"Timeout for the request (in seconds)"`
RequestJSON string `short:"j" long:"json" description:"The request in JSON format"`
ListCommands bool `short:"l" long:"list-commands" description:"List all commands and exit"`
CommandAndParameters []string
config.NetworkFlags
}

View File

@@ -34,18 +34,16 @@ func main() {
}
defer client.Disconnect()
if !cfg.AllowConnectionToDifferentVersions {
kaspadMessage, err := client.Post(&protowire.KaspadMessage{Payload: &protowire.KaspadMessage_GetInfoRequest{GetInfoRequest: &protowire.GetInfoRequestMessage{}}})
if err != nil {
printErrorAndExit(fmt.Sprintf("Cannot post GetInfo message: %s", err))
}
kaspadMessage, err := client.Post(&protowire.KaspadMessage{Payload: &protowire.KaspadMessage_GetInfoRequest{GetInfoRequest: &protowire.GetInfoRequestMessage{}}})
if err != nil {
printErrorAndExit(fmt.Sprintf("Cannot post GetInfo message: %s", err))
}
localVersion := version.Version()
remoteVersion := kaspadMessage.GetGetInfoResponse().ServerVersion
localVersion := version.Version()
remoteVersion := kaspadMessage.GetGetInfoResponse().ServerVersion
if localVersion != remoteVersion {
printErrorAndExit(fmt.Sprintf("Server version mismatch, expect: %s, got: %s", localVersion, remoteVersion))
}
if localVersion != remoteVersion {
printErrorAndExit(fmt.Sprintf("Server version mismatch, expect: %s, got: %s", localVersion, remoteVersion))
}
responseChan := make(chan string)

View File

@@ -6,9 +6,6 @@ import (
"fmt"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/client"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/pkg/errors"
"io/ioutil"
"strings"
)
func broadcast(conf *broadcastConfig) error {
@@ -21,23 +18,7 @@ func broadcast(conf *broadcastConfig) error {
ctx, cancel := context.WithTimeout(context.Background(), daemonTimeout)
defer cancel()
if conf.Transaction == "" && conf.TransactionFile == "" {
return errors.Errorf("Either --transaction or --transaction-file is required")
}
if conf.Transaction != "" && conf.TransactionFile != "" {
return errors.Errorf("Both --transaction and --transaction-file cannot be passed at the same time")
}
transactionHex := conf.Transaction
if conf.TransactionFile != "" {
transactionHexBytes, err := ioutil.ReadFile(conf.TransactionFile)
if err != nil {
return errors.Wrapf(err, "Could not read hex from %s", conf.TransactionFile)
}
transactionHex = strings.TrimSpace(string(transactionHexBytes))
}
transaction, err := hex.DecodeString(transactionHex)
transaction, err := hex.DecodeString(conf.Transaction)
if err != nil {
return err
}

View File

@@ -65,17 +65,15 @@ type createUnsignedTransactionConfig struct {
}
type signConfig struct {
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
Password string `long:"password" short:"p" description:"Wallet password"`
Transaction string `long:"transaction" short:"t" description:"The unsigned transaction to sign on (encoded in hex)"`
TransactionFile string `long:"transaction-file" short:"F" description:"The file containing the unsigned transaction to sign on (encoded in hex)"`
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
Password string `long:"password" short:"p" description:"Wallet password"`
Transaction string `long:"transaction" short:"t" description:"The unsigned transaction to sign on (encoded in hex)" required:"true"`
config.NetworkFlags
}
type broadcastConfig struct {
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
Transaction string `long:"transaction" short:"t" description:"The signed transaction to broadcast (encoded in hex)"`
TransactionFile string `long:"transaction-file" short:"F" description:"The file containing the unsigned transaction to sign on (encoded in hex)"`
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
Transaction string `long:"transaction" short:"t" description:"The signed transaction to broadcast (encoded in hex)" required:"true"`
config.NetworkFlags
}

View File

@@ -2,7 +2,6 @@ package server
import (
"context"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
)
@@ -22,7 +21,7 @@ func (s *server) GetBalance(_ context.Context, _ *pb.GetBalanceRequest) (*pb.Get
maturity := s.params.BlockCoinbaseMaturity
balancesMap := make(balancesMapType, 0)
for _, entry := range s.utxosSortedByAmount {
for _, entry := range s.utxos {
amount := entry.UTXOEntry.Amount()
address := entry.address
balances, ok := balancesMap[address]

View File

@@ -2,7 +2,6 @@ package server
import (
"context"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
@@ -18,7 +17,7 @@ func (s *server) CreateUnsignedTransaction(_ context.Context, request *pb.Create
return nil, errors.New("server is not synced")
}
err := s.refreshUTXOs()
err := s.refreshExistingUTXOs()
if err != nil {
return nil, err
}
@@ -67,7 +66,7 @@ func (s *server) selectUTXOs(spendAmount uint64, feePerInput uint64) (
return nil, 0, err
}
for _, utxo := range s.utxosSortedByAmount {
for _, utxo := range s.utxos {
if !isUTXOSpendable(utxo, dagInfo.VirtualDAAScore, s.params.BlockCoinbaseMaturity) {
continue
}

View File

@@ -2,15 +2,15 @@ package server
import (
"fmt"
"github.com/kaspanet/kaspad/util/profiling"
"net"
"os"
"sync"
"time"
"github.com/kaspanet/kaspad/util/profiling"
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
"github.com/kaspanet/kaspad/infrastructure/os/signal"
@@ -26,12 +26,11 @@ type server struct {
rpcClient *rpcclient.RPCClient
params *dagconfig.Params
lock sync.RWMutex
utxosSortedByAmount []*walletUTXO
nextSyncStartIndex uint32
keysFile *keys.File
shutdown chan struct{}
addressSet walletAddressSet
lock sync.RWMutex
utxos map[externalapi.DomainOutpoint]*walletUTXO
nextSyncStartIndex uint32
keysFile *keys.File
shutdown chan struct{}
}
// Start starts the kaspawalletd server
@@ -62,13 +61,12 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri
}
serverInstance := &server{
rpcClient: rpcClient,
params: params,
utxosSortedByAmount: []*walletUTXO{},
nextSyncStartIndex: 0,
keysFile: keysFile,
shutdown: make(chan struct{}),
addressSet: make(walletAddressSet),
rpcClient: rpcClient,
params: params,
utxos: make(map[externalapi.DomainOutpoint]*walletUTXO),
nextSyncStartIndex: 0,
keysFile: keysFile,
shutdown: make(chan struct{}),
}
spawn("serverInstance.sync", func() {

View File

@@ -1,12 +1,12 @@
package server
import (
"sort"
"time"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/pkg/errors"
)
@@ -27,18 +27,17 @@ func (s *server) sync() error {
defer ticker.Stop()
for range ticker.C {
err := s.collectRecentAddresses()
err := s.collectUTXOsFromRecentAddresses()
if err != nil {
return err
}
err = s.collectFarAddresses()
err = s.collectUTXOsFromFarAddresses()
if err != nil {
return err
}
err = s.refreshExistingUTXOsWithLock()
if err != nil {
return err
}
@@ -75,12 +74,12 @@ func (s *server) addressesToQuery(start, end uint32) (walletAddressSet, error) {
return addresses, nil
}
// collectFarAddresses collects numIndexesToQuery addresses
// collectUTXOsFromFarAddresses collects numIndexesToQuery UTXOs
// from the last point it stopped in the previous call.
func (s *server) collectFarAddresses() error {
func (s *server) collectUTXOsFromFarAddresses() error {
s.lock.Lock()
defer s.lock.Unlock()
err := s.collectAddresses(s.nextSyncStartIndex, s.nextSyncStartIndex+numIndexesToQuery)
err := s.collectUTXOs(s.nextSyncStartIndex, s.nextSyncStartIndex+numIndexesToQuery)
if err != nil {
return err
}
@@ -101,14 +100,14 @@ func (s *server) maxUsedIndex() uint32 {
return maxUsedIndex
}
// collectRecentAddresses collects addresses from used addresses until
// collectUTXOsFromRecentAddresses collects UTXOs from used addresses until
// the address with the index of the last used address + 1000.
// collectRecentAddresses scans addresses in batches of numIndexesToQuery,
// collectUTXOsFromRecentAddresses scans addresses in batches of numIndexesToQuery,
// and releases the lock between scans.
func (s *server) collectRecentAddresses() error {
func (s *server) collectUTXOsFromRecentAddresses() error {
maxUsedIndex := s.maxUsedIndex()
for i := uint32(0); i < maxUsedIndex+1000; i += numIndexesToQuery {
err := s.collectAddressesWithLock(i, i+numIndexesToQuery)
err := s.collectUTXOsWithLock(i, i+numIndexesToQuery)
if err != nil {
return err
}
@@ -117,25 +116,30 @@ func (s *server) collectRecentAddresses() error {
return nil
}
func (s *server) collectAddressesWithLock(start, end uint32) error {
func (s *server) collectUTXOsWithLock(start, end uint32) error {
s.lock.Lock()
defer s.lock.Unlock()
return s.collectAddresses(start, end)
return s.collectUTXOs(start, end)
}
func (s *server) collectAddresses(start, end uint32) error {
func (s *server) collectUTXOs(start, end uint32) error {
addressSet, err := s.addressesToQuery(start, end)
if err != nil {
return err
}
getBalancesByAddressesResponse, err := s.rpcClient.GetBalancesByAddresses(addressSet.strings())
getUTXOsByAddressesResponse, err := s.rpcClient.GetUTXOsByAddresses(addressSet.strings())
if err != nil {
return err
}
err = s.updateAddressesAndLastUsedIndexes(addressSet, getBalancesByAddressesResponse)
err = s.updateLastUsedIndexes(addressSet, getUTXOsByAddressesResponse)
if err != nil {
return err
}
err = s.updateUTXOs(addressSet, getUTXOsByAddressesResponse)
if err != nil {
return err
}
@@ -143,28 +147,35 @@ func (s *server) collectAddresses(start, end uint32) error {
return nil
}
func (s *server) updateAddressesAndLastUsedIndexes(requestedAddressSet walletAddressSet,
getBalancesByAddressesResponse *appmessage.GetBalancesByAddressesResponseMessage) error {
func (s *server) updateUTXOs(addressSet walletAddressSet,
getUTXOsByAddressesResponse *appmessage.GetUTXOsByAddressesResponseMessage) error {
for _, entry := range getUTXOsByAddressesResponse.Entries {
err := s.addEntryToUTXOSet(entry, addressSet)
if err != nil {
return err
}
}
return nil
}
func (s *server) updateLastUsedIndexes(addressSet walletAddressSet,
getUTXOsByAddressesResponse *appmessage.GetUTXOsByAddressesResponseMessage) error {
lastUsedExternalIndex := s.keysFile.LastUsedExternalIndex()
lastUsedInternalIndex := s.keysFile.LastUsedInternalIndex()
for _, entry := range getBalancesByAddressesResponse.Entries {
walletAddress, ok := requestedAddressSet[entry.Address]
for _, entry := range getUTXOsByAddressesResponse.Entries {
walletAddress, ok := addressSet[entry.Address]
if !ok {
return errors.Errorf("Got result from address %s even though it wasn't requested", entry.Address)
}
if entry.Balance == 0 {
continue
}
if walletAddress.cosignerIndex != s.keysFile.CosignerIndex {
continue
}
s.addressSet[entry.Address] = walletAddress
if walletAddress.keyChain == libkaspawallet.ExternalKeychain {
if walletAddress.index > lastUsedExternalIndex {
lastUsedExternalIndex = walletAddress.index
@@ -189,49 +200,58 @@ func (s *server) refreshExistingUTXOsWithLock() error {
s.lock.Lock()
defer s.lock.Unlock()
return s.refreshUTXOs()
return s.refreshExistingUTXOs()
}
// updateUTXOSet clears the current UTXO set, and re-fills it with the given entries
func (s *server) updateUTXOSet(entries []*appmessage.UTXOsByAddressesEntry) error {
utxos := make([]*walletUTXO, len(entries))
for i, entry := range entries {
outpoint, err := appmessage.RPCOutpointToDomainOutpoint(entry.Outpoint)
if err != nil {
return err
}
utxoEntry, err := appmessage.RPCUTXOEntryToUTXOEntry(entry.UTXOEntry)
if err != nil {
return err
}
address, ok := s.addressSet[entry.Address]
if !ok {
return errors.Errorf("Got result from address %s even though it wasn't requested", entry.Address)
}
utxos[i] = &walletUTXO{
Outpoint: outpoint,
UTXOEntry: utxoEntry,
address: address,
}
}
sort.Slice(utxos, func(i, j int) bool { return utxos[i].UTXOEntry.Amount() > utxos[j].UTXOEntry.Amount() })
s.utxosSortedByAmount = utxos
return nil
}
func (s *server) refreshUTXOs() error {
getUTXOsByAddressesResponse, err := s.rpcClient.GetUTXOsByAddresses(s.addressSet.strings())
func (s *server) addEntryToUTXOSet(entry *appmessage.UTXOsByAddressesEntry, addressSet walletAddressSet) error {
outpoint, err := appmessage.RPCOutpointToDomainOutpoint(entry.Outpoint)
if err != nil {
return err
}
return s.updateUTXOSet(getUTXOsByAddressesResponse.Entries)
utxoEntry, err := appmessage.RPCUTXOEntryToUTXOEntry(entry.UTXOEntry)
if err != nil {
return err
}
address, ok := addressSet[entry.Address]
if !ok {
return errors.Errorf("Got result from address %s even though it wasn't requested", entry.Address)
}
s.utxos[*outpoint] = &walletUTXO{
Outpoint: outpoint,
UTXOEntry: utxoEntry,
address: address,
}
return nil
}
func (s *server) refreshExistingUTXOs() error {
addressSet := make(walletAddressSet, len(s.utxos))
for _, utxo := range s.utxos {
addressString, err := s.walletAddressString(utxo.address)
if err != nil {
return err
}
addressSet[addressString] = utxo.address
}
getUTXOsByAddressesResponse, err := s.rpcClient.GetUTXOsByAddresses(addressSet.strings())
if err != nil {
return err
}
s.utxos = make(map[externalapi.DomainOutpoint]*walletUTXO, len(getUTXOsByAddressesResponse.Entries))
for _, entry := range getUTXOsByAddressesResponse.Entries {
err := s.addEntryToUTXOSet(entry, addressSet)
if err != nil {
return err
}
}
return nil
}
func (s *server) isSynced() bool {

View File

@@ -5,9 +5,6 @@ import (
"fmt"
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/pkg/errors"
"io/ioutil"
"strings"
)
func sign(conf *signConfig) error {
@@ -16,23 +13,7 @@ func sign(conf *signConfig) error {
return err
}
if conf.Transaction == "" && conf.TransactionFile == "" {
return errors.Errorf("Either --transaction or --transaction-file is required")
}
if conf.Transaction != "" && conf.TransactionFile != "" {
return errors.Errorf("Both --transaction and --transaction-file cannot be passed at the same time")
}
transactionHex := conf.Transaction
if conf.TransactionFile != "" {
transactionHexBytes, err := ioutil.ReadFile(conf.TransactionFile)
if err != nil {
return errors.Wrapf(err, "Could not read hex from %s", conf.TransactionFile)
}
transactionHex = strings.TrimSpace(string(transactionHexBytes))
}
partiallySignedTransaction, err := hex.DecodeString(transactionHex)
partiallySignedTransaction, err := hex.DecodeString(conf.Transaction)
if err != nil {
return err
}

View File

@@ -39,22 +39,21 @@ type consensus struct {
finalityManager model.FinalityManager
pruningProofManager model.PruningProofManager
acceptanceDataStore model.AcceptanceDataStore
blockStore model.BlockStore
blockHeaderStore model.BlockHeaderStore
pruningStore model.PruningStore
ghostdagDataStores []model.GHOSTDAGDataStore
blockRelationStores []model.BlockRelationStore
blockStatusStore model.BlockStatusStore
consensusStateStore model.ConsensusStateStore
headersSelectedTipStore model.HeaderSelectedTipStore
multisetStore model.MultisetStore
reachabilityDataStores []model.ReachabilityDataStore
utxoDiffStore model.UTXODiffStore
finalityStore model.FinalityStore
headersSelectedChainStore model.HeadersSelectedChainStore
daaBlocksStore model.DAABlocksStore
blocksWithTrustedDataDAAWindowStore model.BlocksWithTrustedDataDAAWindowStore
acceptanceDataStore model.AcceptanceDataStore
blockStore model.BlockStore
blockHeaderStore model.BlockHeaderStore
pruningStore model.PruningStore
ghostdagDataStores []model.GHOSTDAGDataStore
blockRelationStores []model.BlockRelationStore
blockStatusStore model.BlockStatusStore
consensusStateStore model.ConsensusStateStore
headersSelectedTipStore model.HeaderSelectedTipStore
multisetStore model.MultisetStore
reachabilityDataStores []model.ReachabilityDataStore
utxoDiffStore model.UTXODiffStore
finalityStore model.FinalityStore
headersSelectedChainStore model.HeadersSelectedChainStore
daaBlocksStore model.DAABlocksStore
}
func (s *consensus) ValidateAndInsertBlockWithTrustedData(block *externalapi.BlockWithTrustedData, validateUTXO bool) (*externalapi.VirtualChangeSet, error) {
@@ -120,6 +119,7 @@ func (s *consensus) Init(skipAddingGenesis bool) error {
if !skipAddingGenesis && s.blockStore.Count(stagingArea) == 0 {
genesisWithTrustedData := &externalapi.BlockWithTrustedData{
Block: s.genesisBlock,
DAAScore: 0,
DAAWindow: nil,
GHOSTDAGData: []*externalapi.BlockGHOSTDAGDataHashPair{
{
@@ -144,6 +144,13 @@ func (s *consensus) PruningPointAndItsAnticone() ([]*externalapi.DomainHash, err
return s.pruningManager.PruningPointAndItsAnticone()
}
func (s *consensus) BlockWithTrustedData(blockHash *externalapi.DomainHash) (*externalapi.BlockWithTrustedData, error) {
s.lock.Lock()
defer s.lock.Unlock()
return s.pruningManager.BlockWithTrustedData(model.NewStagingArea(), blockHash)
}
// BuildBlock builds a block over the current state, with the transactions
// selected by the given transactionSelector
func (s *consensus) BuildBlock(coinbaseData *externalapi.DomainCoinbaseData,
@@ -182,12 +189,7 @@ func (s *consensus) ValidateTransactionAndPopulateWithConsensusData(transaction
return err
}
virtualPastMedianTime, err := s.pastMedianTimeManager.PastMedianTime(stagingArea, model.VirtualBlockHash)
if err != nil {
return err
}
err = s.transactionValidator.ValidateTransactionInContextIgnoringUTXO(stagingArea, transaction, model.VirtualBlockHash, virtualPastMedianTime)
err = s.transactionValidator.ValidateTransactionInContextIgnoringUTXO(stagingArea, transaction, model.VirtualBlockHash)
if err != nil {
return err
}
@@ -738,89 +740,20 @@ func (s *consensus) ValidatePruningPointProof(pruningPointProof *externalapi.Pru
s.lock.Lock()
defer s.lock.Unlock()
log.Infof("Validating the pruning point proof")
err := s.pruningProofManager.ValidatePruningPointProof(pruningPointProof)
if err != nil {
return err
}
log.Infof("Done validating the pruning point proof")
return nil
return s.pruningProofManager.ValidatePruningPointProof(pruningPointProof)
}
func (s *consensus) ApplyPruningPointProof(pruningPointProof *externalapi.PruningPointProof) error {
s.lock.Lock()
defer s.lock.Unlock()
log.Infof("Applying the pruning point proof")
err := s.pruningProofManager.ApplyPruningPointProof(pruningPointProof)
stagingArea := model.NewStagingArea()
err := s.pruningProofManager.ApplyPruningPointProof(stagingArea, pruningPointProof)
if err != nil {
return err
}
err = staging.CommitAllChanges(s.databaseContext, stagingArea)
if err != nil {
return err
}
log.Infof("Done applying the pruning point proof")
return nil
}
func (s *consensus) BlockDAAWindowHashes(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
s.lock.Lock()
defer s.lock.Unlock()
stagingArea := model.NewStagingArea()
return s.dagTraversalManager.DAABlockWindow(stagingArea, blockHash)
}
func (s *consensus) TrustedDataDataDAAHeader(trustedBlockHash, daaBlockHash *externalapi.DomainHash, daaBlockWindowIndex uint64) (*externalapi.TrustedDataDataDAAHeader, error) {
s.lock.Lock()
defer s.lock.Unlock()
stagingArea := model.NewStagingArea()
header, err := s.blockHeaderStore.BlockHeader(s.databaseContext, stagingArea, daaBlockHash)
if err != nil {
return nil, err
}
ghostdagData, err := s.ghostdagDataStores[0].Get(s.databaseContext, stagingArea, daaBlockHash, false)
isNotFoundError := database.IsNotFoundError(err)
if !isNotFoundError && err != nil {
return nil, err
}
if !isNotFoundError {
return &externalapi.TrustedDataDataDAAHeader{
Header: header,
GHOSTDAGData: ghostdagData,
}, nil
}
ghostdagDataHashPair, err := s.blocksWithTrustedDataDAAWindowStore.DAAWindowBlock(s.databaseContext, stagingArea, trustedBlockHash, daaBlockWindowIndex)
if err != nil {
return nil, err
}
return &externalapi.TrustedDataDataDAAHeader{
Header: header,
GHOSTDAGData: ghostdagDataHashPair.GHOSTDAGData,
}, nil
}
func (s *consensus) TrustedBlockAssociatedGHOSTDAGDataBlockHashes(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
s.lock.Lock()
defer s.lock.Unlock()
return s.pruningManager.TrustedBlockAssociatedGHOSTDAGDataBlockHashes(model.NewStagingArea(), blockHash)
}
func (s *consensus) TrustedGHOSTDAGData(blockHash *externalapi.DomainHash) (*externalapi.BlockGHOSTDAGData, error) {
s.lock.Lock()
defer s.lock.Unlock()
stagingArea := model.NewStagingArea()
ghostdagData, err := s.ghostdagDataStores[0].Get(s.databaseContext, stagingArea, blockHash, false)
isNotFoundError := database.IsNotFoundError(err)
if isNotFoundError || ghostdagData.SelectedParent().Equal(model.VirtualGenesisBlockHash) {
return s.ghostdagDataStores[0].Get(s.databaseContext, stagingArea, blockHash, true)
}
return ghostdagData, nil
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/processes/blockparentbuilder"
parentssanager "github.com/kaspanet/kaspad/domain/consensus/processes/parentsmanager"
"github.com/kaspanet/kaspad/domain/consensus/processes/pruningproofmanager"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"io/ioutil"
"os"
"sync"
@@ -157,7 +158,7 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
dagTraversalManager := dagTraversalManagers[0]
// Processes
parentsManager := parentssanager.New(config.GenesisHash, config.MaxBlockLevel)
parentsManager := parentssanager.New(config.GenesisHash)
blockParentBuilder := blockparentbuilder.New(
dbManager,
blockHeaderStore,
@@ -167,7 +168,6 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
pruningStore,
config.GenesisHash,
config.MaxBlockLevel,
)
pastMedianTimeManager := f.pastMedianTimeConsructor(
config.TimestampDeviationTolerance,
@@ -203,11 +203,14 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
coinbaseManager := coinbasemanager.New(
dbManager,
config.SubsidyGenesisReward,
config.PreDeflationaryPhaseBaseSubsidy,
config.MinSubsidy,
config.MaxSubsidy,
config.SubsidyPastRewardMultiplier,
config.SubsidyMergeSetRewardMultiplier,
config.CoinbasePayloadScriptPublicKeyMaxLength,
config.GenesisHash,
config.DeflationaryPhaseDaaScore,
config.DeflationaryPhaseBaseSubsidy,
config.FixedSubsidySwitchPruningPointInterval,
config.FixedSubsidySwitchHashRateThreshold,
dagTraversalManager,
ghostdagDataStore,
acceptanceDataStore,
@@ -304,7 +307,6 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
config.TimestampDeviationTolerance,
config.TargetTimePerBlock,
config.IgnoreHeaderMass,
config.MaxBlockLevel,
dbManager,
difficultyManager,
@@ -371,7 +373,6 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
blockProcessor := blockprocessor.New(
genesisHash,
config.TargetTimePerBlock,
config.MaxBlockLevel,
dbManager,
consensusStateManager,
pruningManager,
@@ -419,7 +420,6 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
genesisHash,
config.K,
config.PruningProofM,
config.MaxBlockLevel,
)
c := &consensus{
@@ -448,22 +448,21 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
finalityManager: finalityManager,
pruningProofManager: pruningProofManager,
acceptanceDataStore: acceptanceDataStore,
blockStore: blockStore,
blockHeaderStore: blockHeaderStore,
pruningStore: pruningStore,
ghostdagDataStores: ghostdagDataStores,
blockStatusStore: blockStatusStore,
blockRelationStores: blockRelationStores,
consensusStateStore: consensusStateStore,
headersSelectedTipStore: headersSelectedTipStore,
multisetStore: multisetStore,
reachabilityDataStores: reachabilityDataStores,
utxoDiffStore: utxoDiffStore,
finalityStore: finalityStore,
headersSelectedChainStore: headersSelectedChainStore,
daaBlocksStore: daaBlocksStore,
blocksWithTrustedDataDAAWindowStore: daaWindowStore,
acceptanceDataStore: acceptanceDataStore,
blockStore: blockStore,
blockHeaderStore: blockHeaderStore,
pruningStore: pruningStore,
ghostdagDataStores: ghostdagDataStores,
blockStatusStore: blockStatusStore,
blockRelationStores: blockRelationStores,
consensusStateStore: consensusStateStore,
headersSelectedTipStore: headersSelectedTipStore,
multisetStore: multisetStore,
reachabilityDataStores: reachabilityDataStores,
utxoDiffStore: utxoDiffStore,
finalityStore: finalityStore,
headersSelectedChainStore: headersSelectedChainStore,
daaBlocksStore: daaBlocksStore,
}
err = c.Init(config.SkipAddingGenesis)
@@ -571,16 +570,16 @@ func dagStores(config *Config,
pruningWindowSizePlusFinalityDepthForCache, pruningWindowSizeForCaches int,
preallocateCaches bool) ([]model.BlockRelationStore, []model.ReachabilityDataStore, []model.GHOSTDAGDataStore) {
blockRelationStores := make([]model.BlockRelationStore, config.MaxBlockLevel+1)
reachabilityDataStores := make([]model.ReachabilityDataStore, config.MaxBlockLevel+1)
ghostdagDataStores := make([]model.GHOSTDAGDataStore, config.MaxBlockLevel+1)
blockRelationStores := make([]model.BlockRelationStore, constants.MaxBlockLevel+1)
reachabilityDataStores := make([]model.ReachabilityDataStore, constants.MaxBlockLevel+1)
ghostdagDataStores := make([]model.GHOSTDAGDataStore, constants.MaxBlockLevel+1)
ghostdagDataCacheSize := pruningWindowSizeForCaches * 2
if ghostdagDataCacheSize < config.DifficultyAdjustmentWindowSize {
ghostdagDataCacheSize = config.DifficultyAdjustmentWindowSize
}
for i := 0; i <= config.MaxBlockLevel; i++ {
for i := 0; i <= constants.MaxBlockLevel; i++ {
prefixBucket := prefixBucket.Bucket([]byte{byte(i)})
if i == 0 {
blockRelationStores[i] = blockrelationstore.New(prefixBucket, pruningWindowSizePlusFinalityDepthForCache, preallocateCaches)
@@ -609,12 +608,12 @@ func (f *factory) dagProcesses(config *Config,
[]model.DAGTraversalManager,
) {
reachabilityManagers := make([]model.ReachabilityManager, config.MaxBlockLevel+1)
dagTopologyManagers := make([]model.DAGTopologyManager, config.MaxBlockLevel+1)
ghostdagManagers := make([]model.GHOSTDAGManager, config.MaxBlockLevel+1)
dagTraversalManagers := make([]model.DAGTraversalManager, config.MaxBlockLevel+1)
reachabilityManagers := make([]model.ReachabilityManager, constants.MaxBlockLevel+1)
dagTopologyManagers := make([]model.DAGTopologyManager, constants.MaxBlockLevel+1)
ghostdagManagers := make([]model.GHOSTDAGManager, constants.MaxBlockLevel+1)
dagTraversalManagers := make([]model.DAGTraversalManager, constants.MaxBlockLevel+1)
for i := 0; i <= config.MaxBlockLevel; i++ {
for i := 0; i <= constants.MaxBlockLevel; i++ {
reachabilityManagers[i] = reachabilitymanager.New(
dbManager,
ghostdagDataStores[i],
@@ -641,8 +640,7 @@ func (f *factory) dagProcesses(config *Config,
reachabilityDataStores[i],
ghostdagManagers[i],
daaWindowStore,
config.GenesisHash,
config.DifficultyAdjustmentWindowSize)
config.GenesisHash)
}
return reachabilityManagers, dagTopologyManagers, ghostdagManagers, dagTraversalManagers

View File

@@ -69,7 +69,7 @@ type BaseBlockHeader interface {
BlueScore() uint64
BlueWork() *big.Int
PruningPoint() *DomainHash
BlockLevel(maxBlockLevel int) int
BlockLevel() int
Equal(other BaseBlockHeader) bool
}

View File

@@ -6,13 +6,17 @@ package externalapi
// anticone on a pruned-headers node.
type BlockWithTrustedData struct {
Block *DomainBlock
DAAWindow []*TrustedDataDataDAAHeader
DAAScore uint64
DAAWindow []*TrustedDataDataDAABlock
GHOSTDAGData []*BlockGHOSTDAGDataHashPair
}
// TrustedDataDataDAAHeader is a block that belongs to BlockWithTrustedData.DAAWindow
type TrustedDataDataDAAHeader struct {
Header BlockHeader
// TrustedDataDataDAABlock is a block that belongs to BlockWithTrustedData.DAAWindow
// TODO: Currently each trusted data block contains the entire set of blocks in its
// DAA window. There's a lot of duplications between DAA windows of trusted blocks.
// This duplication should be optimized out.
type TrustedDataDataDAABlock struct {
Block *DomainBlock
GHOSTDAGData *BlockGHOSTDAGData
}

View File

@@ -26,6 +26,7 @@ type Consensus interface {
PruningPoint() (*DomainHash, error)
PruningPointHeaders() ([]BlockHeader, error)
PruningPointAndItsAnticone() ([]*DomainHash, error)
BlockWithTrustedData(blockHash *DomainHash) (*BlockWithTrustedData, error)
ClearImportedPruningPointData() error
AppendImportedPruningPointUTXOs(outpointAndUTXOEntryPairs []*OutpointAndUTXOEntryPair) error
ValidateAndInsertImportedPruningPoint(newPruningPoint *DomainHash) error
@@ -46,8 +47,4 @@ type Consensus interface {
EstimateNetworkHashesPerSecond(startHash *DomainHash, windowSize int) (uint64, error)
PopulateMass(transaction *DomainTransaction)
ResolveVirtual() (*VirtualChangeSet, bool, error)
BlockDAAWindowHashes(blockHash *DomainHash) ([]*DomainHash, error)
TrustedDataDataDAAHeader(trustedBlockHash, daaBlockHash *DomainHash, daaBlockWindowIndex uint64) (*TrustedDataDataDAAHeader, error)
TrustedBlockAssociatedGHOSTDAGDataBlockHashes(blockHash *DomainHash) ([]*DomainHash, error)
TrustedGHOSTDAGData(blockHash *DomainHash) (*BlockGHOSTDAGData, error)
}

View File

@@ -7,6 +7,6 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
type CoinbaseManager interface {
ExpectedCoinbaseTransaction(stagingArea *StagingArea, blockHash *externalapi.DomainHash,
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error)
CalcBlockSubsidy(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (uint64, error)
CalcBlockSubsidy(blockHash *externalapi.DomainHash) (uint64, error)
ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx *externalapi.DomainTransaction) (blueScore uint64, coinbaseData *externalapi.DomainCoinbaseData, subsidy uint64, err error)
}

View File

@@ -13,7 +13,7 @@ type DAGTraversalManager interface {
AnticoneFromBlocks(stagingArea *StagingArea, tips []*externalapi.DomainHash, blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
AnticoneFromVirtualPOV(stagingArea *StagingArea, blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
BlockWindow(stagingArea *StagingArea, highHash *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error)
DAABlockWindow(stagingArea *StagingArea, highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
BlockWindowWithGHOSTDAGData(stagingArea *StagingArea, highHash *externalapi.DomainHash, windowSize int) ([]*externalapi.BlockGHOSTDAGDataHashPair, error)
NewDownHeap(stagingArea *StagingArea) BlockHeap
NewUpHeap(stagingArea *StagingArea) BlockHeap
CalculateChainPath(stagingArea *StagingArea, fromBlockHash, toBlockHash *externalapi.DomainHash) (

View File

@@ -14,5 +14,5 @@ type PruningManager interface {
PruneAllBlocksBelow(stagingArea *StagingArea, pruningPointHash *externalapi.DomainHash) error
PruningPointAndItsAnticone() ([]*externalapi.DomainHash, error)
ExpectedHeaderPruningPoint(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error)
TrustedBlockAssociatedGHOSTDAGDataBlockHashes(stagingArea *StagingArea, blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
BlockWithTrustedData(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.BlockWithTrustedData, error)
}

View File

@@ -6,5 +6,5 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
type PruningProofManager interface {
BuildPruningPointProof(stagingArea *StagingArea) (*externalapi.PruningPointProof, error)
ValidatePruningPointProof(pruningPointProof *externalapi.PruningPointProof) error
ApplyPruningPointProof(pruningPointProof *externalapi.PruningPointProof) error
ApplyPruningPointProof(stagingArea *StagingArea, pruningPointProof *externalapi.PruningPointProof) error
}

View File

@@ -9,7 +9,7 @@ import (
type TransactionValidator interface {
ValidateTransactionInIsolation(transaction *externalapi.DomainTransaction) error
ValidateTransactionInContextIgnoringUTXO(stagingArea *StagingArea, tx *externalapi.DomainTransaction,
povBlockHash *externalapi.DomainHash, povBlockPastMedianTime int64) error
povBlockHash *externalapi.DomainHash) error
ValidateTransactionInContextAndPopulateFee(stagingArea *StagingArea,
tx *externalapi.DomainTransaction, povBlockHash *externalapi.DomainHash) error
PopulateMass(transaction *externalapi.DomainTransaction)

View File

@@ -166,12 +166,7 @@ func (bb *blockBuilder) validateTransaction(
return err
}
virtualPastMedianTime, err := bb.pastMedianTimeManager.PastMedianTime(stagingArea, model.VirtualBlockHash)
if err != nil {
return err
}
err = bb.transactionValidator.ValidateTransactionInContextIgnoringUTXO(stagingArea, transaction, model.VirtualBlockHash, virtualPastMedianTime)
err = bb.transactionValidator.ValidateTransactionInContextIgnoringUTXO(stagingArea, transaction, model.VirtualBlockHash)
if err != nil {
return err
}

View File

@@ -16,8 +16,7 @@ type blockParentBuilder struct {
reachabilityDataStore model.ReachabilityDataStore
pruningStore model.PruningStore
genesisHash *externalapi.DomainHash
maxBlockLevel int
genesisHash *externalapi.DomainHash
}
// New creates a new instance of a BlockParentBuilder
@@ -31,7 +30,6 @@ func New(
pruningStore model.PruningStore,
genesisHash *externalapi.DomainHash,
maxBlockLevel int,
) model.BlockParentBuilder {
return &blockParentBuilder{
databaseContext: databaseContext,
@@ -42,7 +40,6 @@ func New(
reachabilityDataStore: reachabilityDataStore,
pruningStore: pruningStore,
genesisHash: genesisHash,
maxBlockLevel: maxBlockLevel,
}
}
@@ -105,7 +102,7 @@ func (bpb *blockParentBuilder) BuildParents(stagingArea *model.StagingArea,
// all the block levels they occupy
for _, directParentHeader := range directParentHeaders {
directParentHash := consensushashing.HeaderHash(directParentHeader)
blockLevel := directParentHeader.BlockLevel(bpb.maxBlockLevel)
blockLevel := directParentHeader.BlockLevel()
for i := 0; i <= blockLevel; i++ {
if _, exists := candidatesByLevelToReferenceBlocksMap[i]; !exists {
candidatesByLevelToReferenceBlocksMap[i] = make(map[externalapi.DomainHash][]*externalapi.DomainHash)

View File

@@ -14,7 +14,6 @@ import (
type blockProcessor struct {
genesisHash *externalapi.DomainHash
targetTimePerBlock time.Duration
maxBlockLevel int
databaseContext model.DBManager
blockLogger *blocklogger.BlockLogger
@@ -53,7 +52,6 @@ type blockProcessor struct {
func New(
genesisHash *externalapi.DomainHash,
targetTimePerBlock time.Duration,
maxBlockLevel int,
databaseContext model.DBManager,
consensusStateManager model.ConsensusStateManager,
@@ -88,7 +86,6 @@ func New(
return &blockProcessor{
genesisHash: genesisHash,
targetTimePerBlock: targetTimePerBlock,
maxBlockLevel: maxBlockLevel,
databaseContext: databaseContext,
blockLogger: blocklogger.NewBlockLogger(),
pruningManager: pruningManager,

View File

@@ -259,7 +259,7 @@ func (bp *blockProcessor) updateReachabilityReindexRoot(stagingArea *model.Stagi
return err
}
headersSelectedTipHeaderBlockLevel := headersSelectedTipHeader.BlockLevel(bp.maxBlockLevel)
headersSelectedTipHeaderBlockLevel := headersSelectedTipHeader.BlockLevel()
for blockLevel := 0; blockLevel <= headersSelectedTipHeaderBlockLevel; blockLevel++ {
err := bp.reachabilityManagers[blockLevel].UpdateReindexRoot(stagingArea, headersSelectedTip)
if err != nil {

View File

@@ -12,12 +12,13 @@ func (bp *blockProcessor) validateAndInsertBlockWithTrustedData(stagingArea *mod
blockHash := consensushashing.BlockHash(block.Block)
for i, daaBlock := range block.DAAWindow {
hash := consensushashing.HeaderHash(daaBlock.Header)
hash := consensushashing.BlockHash(daaBlock.Block)
bp.blocksWithTrustedDataDAAWindowStore.Stage(stagingArea, blockHash, uint64(i), &externalapi.BlockGHOSTDAGDataHashPair{
Hash: hash,
GHOSTDAGData: daaBlock.GHOSTDAGData,
})
bp.blockHeaderStore.Stage(stagingArea, hash, daaBlock.Header)
bp.blockStore.Stage(stagingArea, hash, daaBlock.Block)
bp.blockHeaderStore.Stage(stagingArea, hash, daaBlock.Block.Header)
}
blockReplacedGHOSTDAGData, err := bp.ghostdagDataWithoutPrunedBlocks(stagingArea, block.GHOSTDAGData[0].GHOSTDAGData)
@@ -30,7 +31,7 @@ func (bp *blockProcessor) validateAndInsertBlockWithTrustedData(stagingArea *mod
bp.ghostdagDataStore.Stage(stagingArea, pair.Hash, pair.GHOSTDAGData, true)
}
bp.daaBlocksStore.StageDAAScore(stagingArea, blockHash, block.Block.Header.DAAScore())
bp.daaBlocksStore.StageDAAScore(stagingArea, blockHash, block.DAAScore)
return bp.validateAndInsertBlock(stagingArea, block.Block, false, validateUTXO, true)
}

View File

@@ -93,44 +93,9 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
}
for _, blockHash := range pruningPointAndItsAnticone {
block, err := tcSyncer.GetBlock(blockHash)
blockWithTrustedData, err := tcSyncer.BlockWithTrustedData(blockHash)
if err != nil {
t.Fatalf("GetBlock: %+v", err)
}
blockDAAWindowHashes, err := tcSyncer.BlockDAAWindowHashes(blockHash)
if err != nil {
t.Fatalf("BlockDAAWindowHashes: %+v", err)
}
ghostdagDataBlockHashes, err := tcSyncer.TrustedBlockAssociatedGHOSTDAGDataBlockHashes(blockHash)
if err != nil {
t.Fatalf("TrustedBlockAssociatedGHOSTDAGDataBlockHashes: %+v", err)
}
blockWithTrustedData := &externalapi.BlockWithTrustedData{
Block: block,
DAAWindow: make([]*externalapi.TrustedDataDataDAAHeader, 0, len(blockDAAWindowHashes)),
GHOSTDAGData: make([]*externalapi.BlockGHOSTDAGDataHashPair, 0, len(ghostdagDataBlockHashes)),
}
for i, daaBlockHash := range blockDAAWindowHashes {
trustedDataDataDAAHeader, err := tcSyncer.TrustedDataDataDAAHeader(blockHash, daaBlockHash, uint64(i))
if err != nil {
t.Fatalf("TrustedDataDataDAAHeader: %+v", err)
}
blockWithTrustedData.DAAWindow = append(blockWithTrustedData.DAAWindow, trustedDataDataDAAHeader)
}
for _, ghostdagDataBlockHash := range ghostdagDataBlockHashes {
data, err := tcSyncer.TrustedGHOSTDAGData(ghostdagDataBlockHash)
if err != nil {
t.Fatalf("TrustedGHOSTDAGData: %+v", err)
}
blockWithTrustedData.GHOSTDAGData = append(blockWithTrustedData.GHOSTDAGData, &externalapi.BlockGHOSTDAGDataHashPair{
Hash: ghostdagDataBlockHash,
GHOSTDAGData: data,
})
return
}
_, err = synceeStaging.ValidateAndInsertBlockWithTrustedData(blockWithTrustedData, false)

View File

@@ -136,12 +136,8 @@ func (v *blockValidator) checkBlockTransactions(
}
// Ensure all transactions in the block are finalized.
pastMedianTime, err := v.pastMedianTimeManager.PastMedianTime(stagingArea, blockHash)
if err != nil {
return err
}
for _, tx := range block.Transactions {
if err = v.transactionValidator.ValidateTransactionInContextIgnoringUTXO(stagingArea, tx, blockHash, pastMedianTime); err != nil {
if err = v.transactionValidator.ValidateTransactionInContextIgnoringUTXO(stagingArea, tx, blockHash); err != nil {
return err
}
}
@@ -182,7 +178,7 @@ func (v *blockValidator) checkCoinbaseSubsidy(
return err
}
expectedSubsidy, err := v.coinbaseManager.CalcBlockSubsidy(stagingArea, blockHash)
expectedSubsidy, err := v.coinbaseManager.CalcBlockSubsidy(blockHash)
if err != nil {
return err
}

View File

@@ -62,7 +62,7 @@ func (v *blockValidator) ValidateHeaderInContext(stagingArea *model.StagingArea,
return err
}
if !hasReachabilityData {
blockLevel := header.BlockLevel(v.maxBlockLevel)
blockLevel := header.BlockLevel()
for i := 0; i <= blockLevel; i++ {
err = v.reachabilityManagers[i].AddBlock(stagingArea, blockHash)
if err != nil {

View File

@@ -23,7 +23,6 @@ type blockValidator struct {
timestampDeviationTolerance int
targetTimePerBlock time.Duration
ignoreHeaderMass bool
maxBlockLevel int
databaseContext model.DBReader
difficultyManager model.DifficultyManager
@@ -61,7 +60,6 @@ func New(powMax *big.Int,
timestampDeviationTolerance int,
targetTimePerBlock time.Duration,
ignoreHeaderMass bool,
maxBlockLevel int,
databaseContext model.DBReader,
@@ -99,7 +97,6 @@ func New(powMax *big.Int,
mergeSetSizeLimit: mergeSetSizeLimit,
maxBlockParents: maxBlockParents,
ignoreHeaderMass: ignoreHeaderMass,
maxBlockLevel: maxBlockLevel,
timestampDeviationTolerance: timestampDeviationTolerance,
targetTimePerBlock: targetTimePerBlock,

View File

@@ -69,7 +69,7 @@ func (v *blockValidator) setParents(stagingArea *model.StagingArea,
header externalapi.BlockHeader,
isBlockWithTrustedData bool) error {
for level := 0; level <= header.BlockLevel(v.maxBlockLevel); level++ {
for level := 0; level <= header.BlockLevel(); level++ {
var parents []*externalapi.DomainHash
for _, parent := range v.parentsManager.ParentsAtLevel(header, level) {
_, err := v.ghostdagDataStores[level].Get(v.databaseContext, stagingArea, parent, false)
@@ -118,7 +118,7 @@ func (v *blockValidator) validateDifficulty(stagingArea *model.StagingArea,
return err
}
blockLevel := header.BlockLevel(v.maxBlockLevel)
blockLevel := header.BlockLevel()
for i := 1; i <= blockLevel; i++ {
err = v.ghostdagManagers[i].GHOSTDAG(stagingArea, blockHash)
if err != nil {

View File

@@ -0,0 +1,86 @@
package coinbasemanager
import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"math/big"
)
func (c *coinbaseManager) isBlockRewardFixed(stagingArea *model.StagingArea, blockPruningPoint *externalapi.DomainHash) (bool, error) {
blockPruningPointIndex, found, err := c.findPruningPointIndex(stagingArea, blockPruningPoint)
if err != nil {
return false, err
}
// The given `pruningPointBlock` may only not be found under one circumstance:
// we're currently in the process of building the next pruning point. As such,
// we must manually set highIndex to currentIndex + 1 because the next pruning
// point is not yet stored in the database
highPruningPointIndex := blockPruningPointIndex
highPruningPointHash := blockPruningPoint
if !found {
currentPruningPointIndex, err := c.pruningStore.CurrentPruningPointIndex(c.databaseContext, stagingArea)
if err != nil {
return false, err
}
highPruningPointIndex = currentPruningPointIndex + 1
}
for {
if highPruningPointIndex <= c.fixedSubsidySwitchPruningPointInterval {
break
}
lowPruningPointIndex := highPruningPointIndex - c.fixedSubsidySwitchPruningPointInterval
lowPruningPointHash, err := c.pruningStore.PruningPointByIndex(c.databaseContext, stagingArea, lowPruningPointIndex)
if err != nil {
return false, err
}
highPruningPointHeader, err := c.blockHeaderStore.BlockHeader(c.databaseContext, stagingArea, highPruningPointHash)
if err != nil {
return false, err
}
lowPruningPointHeader, err := c.blockHeaderStore.BlockHeader(c.databaseContext, stagingArea, lowPruningPointHash)
if err != nil {
return false, err
}
blueWorkDifference := new(big.Int).Sub(highPruningPointHeader.BlueWork(), lowPruningPointHeader.BlueWork())
blueScoreDifference := new(big.Int).SetUint64(highPruningPointHeader.BlueScore() - lowPruningPointHeader.BlueScore())
estimatedAverageHashRate := new(big.Int).Div(blueWorkDifference, blueScoreDifference)
if estimatedAverageHashRate.Cmp(c.fixedSubsidySwitchHashRateThreshold) >= 0 {
return true, nil
}
highPruningPointIndex--
highPruningPointHash, err = c.pruningStore.PruningPointByIndex(c.databaseContext, stagingArea, highPruningPointIndex)
if err != nil {
return false, err
}
}
return false, nil
}
func (c *coinbaseManager) findPruningPointIndex(stagingArea *model.StagingArea, pruningPointHash *externalapi.DomainHash) (uint64, bool, error) {
currentPruningPointHash, err := c.pruningStore.PruningPoint(c.databaseContext, stagingArea)
if err != nil {
return 0, false, err
}
currentPruningPointIndex, err := c.pruningStore.CurrentPruningPointIndex(c.databaseContext, stagingArea)
if err != nil {
return 0, false, err
}
for !currentPruningPointHash.Equal(pruningPointHash) && currentPruningPointIndex > 0 {
currentPruningPointIndex--
currentPruningPointHash, err = c.pruningStore.PruningPointByIndex(c.databaseContext, stagingArea, currentPruningPointIndex)
if err != nil {
return 0, false, err
}
}
if currentPruningPointIndex == 0 && !currentPruningPointHash.Equal(pruningPointHash) {
return 0, false, nil
}
return currentPruningPointIndex, true, nil
}

View File

@@ -0,0 +1,90 @@
package coinbasemanager_test
import (
"github.com/kaspanet/kaspad/domain/consensus"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
"github.com/kaspanet/kaspad/util/difficulty"
"testing"
"time"
)
func TestBlockRewardSwitch(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
// Set the pruning depth to 10
consensusConfig.MergeSetSizeLimit = 1
consensusConfig.K = 1
consensusConfig.FinalityDuration = 1 * time.Second
consensusConfig.TargetTimePerBlock = 1 * time.Second
// Disable difficulty adjustment so that we could reason about blue work
consensusConfig.DisableDifficultyAdjustment = true
// Disable pruning so that we could have access to all the blocks
consensusConfig.IsArchival = true
// Set the interval to 10
consensusConfig.FixedSubsidySwitchPruningPointInterval = 10
// Set the hash rate difference such that the switch would trigger exactly
// on the `FixedSubsidySwitchPruningPointInterval + 1`th pruning point
workToAcceptGenesis := difficulty.CalcWork(consensusConfig.GenesisBlock.Header.Bits())
consensusConfig.FixedSubsidySwitchHashRateThreshold = workToAcceptGenesis
// Set the min, max, and post-switch subsidies to values that would make it
// easy to tell whether the switch happened
consensusConfig.MinSubsidy = 2 * constants.SompiPerKaspa
consensusConfig.MaxSubsidy = 2 * constants.SompiPerKaspa
consensusConfig.SubsidyGenesisReward = 1 * constants.SompiPerKaspa
factory := consensus.NewFactory()
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestBlockRewardSwitch")
if err != nil {
t.Fatalf("Error setting up consensus: %+v", err)
}
defer teardown(false)
// Make the pruning point move FixedSubsidySwitchPruningPointInterval times
tipHash := consensusConfig.GenesisHash
for i := uint64(0); i < consensusConfig.PruningDepth()+consensusConfig.FixedSubsidySwitchPruningPointInterval; i++ {
addedBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil)
if err != nil {
t.Fatalf("AddBlock: %+v", err)
}
tipHash = addedBlockHash
}
// We expect to see `FixedSubsidySwitchPruningPointInterval` pruning points + the genesis
pruningPointHeaders, err := tc.PruningPointHeaders()
if err != nil {
t.Fatalf("PruningPointHeaders: %+v", pruningPointHeaders)
}
expectedPruningPointHeaderAmount := consensusConfig.FixedSubsidySwitchPruningPointInterval + 1
if uint64(len(pruningPointHeaders)) != expectedPruningPointHeaderAmount {
t.Fatalf("Unexpected amount of pruning point headers. "+
"Want: %d, got: %d", expectedPruningPointHeaderAmount, len(pruningPointHeaders))
}
// Make sure that all the headers thus far had a non-fixed subsidies
// Note that we skip the genesis, since that always has the post-switch
// value
for _, pruningPointHeader := range pruningPointHeaders[1:] {
pruningPointHash := consensushashing.HeaderHash(pruningPointHeader)
pruningPoint, err := tc.GetBlock(pruningPointHash)
if err != nil {
t.Fatalf("GetBlock: %+v", err)
}
pruningPointCoinbase := pruningPoint.Transactions[transactionhelper.CoinbaseTransactionIndex]
_, _, subsidy, err := tc.CoinbaseManager().ExtractCoinbaseDataBlueScoreAndSubsidy(pruningPointCoinbase)
if err != nil {
t.Fatalf("ExtractCoinbaseDataBlueScoreAndSubsidy: %+v", err)
}
if subsidy != consensusConfig.MinSubsidy {
t.Fatalf("Subsidy has unexpected value. Want: %d, got: %d", consensusConfig.MinSubsidy, subsidy)
}
}
})
}

View File

@@ -1,6 +1,9 @@
package coinbasemanager
import (
"encoding/binary"
"math/big"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
@@ -9,16 +12,18 @@ import (
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
"github.com/kaspanet/kaspad/infrastructure/db/database"
"github.com/pkg/errors"
"math"
)
type coinbaseManager struct {
subsidyGenesisReward uint64
preDeflationaryPhaseBaseSubsidy uint64
minSubsidy uint64
maxSubsidy uint64
subsidyPastRewardMultiplier *big.Rat
subsidyMergeSetRewardMultiplier *big.Rat
coinbasePayloadScriptPublicKeyMaxLength uint8
genesisHash *externalapi.DomainHash
deflationaryPhaseDaaScore uint64
deflationaryPhaseBaseSubsidy uint64
fixedSubsidySwitchPruningPointInterval uint64
fixedSubsidySwitchHashRateThreshold *big.Int
databaseContext model.DBReader
dagTraversalManager model.DAGTraversalManager
@@ -79,7 +84,7 @@ func (c *coinbaseManager) ExpectedCoinbaseTransaction(stagingArea *model.Staging
txOuts = append(txOuts, txOut)
}
subsidy, err := c.CalcBlockSubsidy(stagingArea, blockHash)
subsidy, err := c.CalcBlockSubsidy(blockHash)
if err != nil {
return nil, err
}
@@ -177,68 +182,97 @@ func acceptanceDataFromArrayToMap(acceptanceData externalapi.AcceptanceData) map
// should have. This is mainly used for determining how much the coinbase for
// newly generated blocks awards as well as validating the coinbase for blocks
// has the expected value.
func (c *coinbaseManager) CalcBlockSubsidy(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (uint64, error) {
//
// Further details: https://hashdag.medium.com/kaspa-launch-plan-9a63f4d754a6
func (c *coinbaseManager) CalcBlockSubsidy(blockHash *externalapi.DomainHash) (uint64, error) {
if blockHash.Equal(c.genesisHash) {
return c.subsidyGenesisReward, nil
}
blockDaaScore, err := c.daaBlocksStore.DAAScore(c.databaseContext, stagingArea, blockHash)
return c.maxSubsidy, nil
}
func (c *coinbaseManager) calculateAveragePastSubsidy(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (*big.Rat, error) {
const subsidyPastWindowSize = 100
blockWindow, err := c.dagTraversalManager.BlockWindow(stagingArea, blockHash, subsidyPastWindowSize)
if err != nil {
return nil, err
}
if len(blockWindow) == 0 {
return new(big.Rat).SetFrac64(int64(c.subsidyGenesisReward), 1), nil
}
pastBlocks, err := c.blockStore.Blocks(c.databaseContext, stagingArea, blockWindow)
if err != nil {
return nil, err
}
pastBlockSubsidySum := int64(0)
for _, pastBlock := range pastBlocks {
coinbaseTransaction := pastBlock.Transactions[transactionhelper.CoinbaseTransactionIndex]
_, _, pastBlockSubsidy, err := c.ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTransaction)
if err != nil {
return nil, err
}
pastBlockSubsidySum += int64(pastBlockSubsidy)
}
return big.NewRat(pastBlockSubsidySum, int64(len(blockWindow))), nil
}
func (c *coinbaseManager) calculateMergeSetSubsidySum(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (*big.Rat, error) {
ghostdagData, err := c.ghostdagDataStore.Get(c.databaseContext, stagingArea, blockHash, true)
if !database.IsNotFoundError(err) && err != nil {
return nil, err
}
// If there's ghostdag data with trusted data we prefer it because we need the original merge set non-pruned merge set.
if database.IsNotFoundError(err) {
ghostdagData, err = c.ghostdagDataStore.Get(c.databaseContext, stagingArea, blockHash, false)
if err != nil {
return nil, err
}
}
mergeSet := append(ghostdagData.MergeSetBlues(), ghostdagData.MergeSetReds()...)
mergeSetBlocks, err := c.blockStore.Blocks(c.databaseContext, stagingArea, mergeSet)
if err != nil {
return nil, err
}
mergeSetSubsidySum := int64(0)
for _, mergeSetBlock := range mergeSetBlocks {
coinbaseTransaction := mergeSetBlock.Transactions[transactionhelper.CoinbaseTransactionIndex]
_, _, mergeSetBlockSubsidy, err := c.ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTransaction)
if err != nil {
return nil, err
}
mergeSetSubsidySum += int64(mergeSetBlockSubsidy)
}
return big.NewRat(mergeSetSubsidySum, 1), nil
}
func (c *coinbaseManager) calculateSubsidyRandomVariable(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (int64, error) {
ghostdagData, err := c.ghostdagDataStore.Get(c.databaseContext, stagingArea, blockHash, false)
if err != nil {
return 0, err
}
if blockDaaScore < c.deflationaryPhaseDaaScore {
return c.preDeflationaryPhaseBaseSubsidy, nil
selectedParentHash := ghostdagData.SelectedParent()
if selectedParentHash == nil {
return 0, nil
}
blockSubsidy := c.calcDeflationaryPeriodBlockSubsidy(blockDaaScore)
return blockSubsidy, nil
}
const binomialSteps = 10
binomialSum := int64(0)
func (c *coinbaseManager) calcDeflationaryPeriodBlockSubsidy(blockDaaScore uint64) uint64 {
// We define a year as 365.25 days and a month as 365.25 / 12 = 30.4375
// secondsPerMonth = 30.4375 * 24 * 60 * 60
const secondsPerMonth = 2629800
// Note that this calculation implicitly assumes that block per second = 1 (by assuming daa score diff is in second units).
monthsSinceDeflationaryPhaseStarted := (blockDaaScore - c.deflationaryPhaseDaaScore) / secondsPerMonth
// Return the pre-calculated value from subsidy-per-month table
return c.getDeflationaryPeriodBlockSubsidyFromTable(monthsSinceDeflationaryPhaseStarted)
}
/*
This table was pre-calculated by calling `calcDeflationaryPeriodBlockSubsidyFloatCalc` for all months until reaching 0 subsidy.
To regenerate this table, run `TestBuildSubsidyTable` in coinbasemanager_test.go (note the `deflationaryPhaseBaseSubsidy` therein)
*/
var subsidyByDeflationaryMonthTable = []uint64{
44000000000, 41530469757, 39199543598, 36999442271, 34922823143, 32962755691, 31112698372, 29366476791, 27718263097, 26162556530, 24694165062, 23308188075, 22000000000, 20765234878, 19599771799, 18499721135, 17461411571, 16481377845, 15556349186, 14683238395, 13859131548, 13081278265, 12347082531, 11654094037, 11000000000,
10382617439, 9799885899, 9249860567, 8730705785, 8240688922, 7778174593, 7341619197, 6929565774, 6540639132, 6173541265, 5827047018, 5500000000, 5191308719, 4899942949, 4624930283, 4365352892, 4120344461, 3889087296, 3670809598, 3464782887, 3270319566, 3086770632, 2913523509, 2750000000, 2595654359,
2449971474, 2312465141, 2182676446, 2060172230, 1944543648, 1835404799, 1732391443, 1635159783, 1543385316, 1456761754, 1375000000, 1297827179, 1224985737, 1156232570, 1091338223, 1030086115, 972271824, 917702399, 866195721, 817579891, 771692658, 728380877, 687500000, 648913589, 612492868,
578116285, 545669111, 515043057, 486135912, 458851199, 433097860, 408789945, 385846329, 364190438, 343750000, 324456794, 306246434, 289058142, 272834555, 257521528, 243067956, 229425599, 216548930, 204394972, 192923164, 182095219, 171875000, 162228397, 153123217, 144529071,
136417277, 128760764, 121533978, 114712799, 108274465, 102197486, 96461582, 91047609, 85937500, 81114198, 76561608, 72264535, 68208638, 64380382, 60766989, 57356399, 54137232, 51098743, 48230791, 45523804, 42968750, 40557099, 38280804, 36132267, 34104319,
32190191, 30383494, 28678199, 27068616, 25549371, 24115395, 22761902, 21484375, 20278549, 19140402, 18066133, 17052159, 16095095, 15191747, 14339099, 13534308, 12774685, 12057697, 11380951, 10742187, 10139274, 9570201, 9033066, 8526079, 8047547,
7595873, 7169549, 6767154, 6387342, 6028848, 5690475, 5371093, 5069637, 4785100, 4516533, 4263039, 4023773, 3797936, 3584774, 3383577, 3193671, 3014424, 2845237, 2685546, 2534818, 2392550, 2258266, 2131519, 2011886, 1898968,
1792387, 1691788, 1596835, 1507212, 1422618, 1342773, 1267409, 1196275, 1129133, 1065759, 1005943, 949484, 896193, 845894, 798417, 753606, 711309, 671386, 633704, 598137, 564566, 532879, 502971, 474742, 448096,
422947, 399208, 376803, 355654, 335693, 316852, 299068, 282283, 266439, 251485, 237371, 224048, 211473, 199604, 188401, 177827, 167846, 158426, 149534, 141141, 133219, 125742, 118685, 112024, 105736,
99802, 94200, 88913, 83923, 79213, 74767, 70570, 66609, 62871, 59342, 56012, 52868, 49901, 47100, 44456, 41961, 39606, 37383, 35285, 33304, 31435, 29671, 28006, 26434, 24950,
23550, 22228, 20980, 19803, 18691, 17642, 16652, 15717, 14835, 14003, 13217, 12475, 11775, 11114, 10490, 9901, 9345, 8821, 8326, 7858, 7417, 7001, 6608, 6237, 5887,
5557, 5245, 4950, 4672, 4410, 4163, 3929, 3708, 3500, 3304, 3118, 2943, 2778, 2622, 2475, 2336, 2205, 2081, 1964, 1854, 1750, 1652, 1559, 1471, 1389,
1311, 1237, 1168, 1102, 1040, 982, 927, 875, 826, 779, 735, 694, 655, 618, 584, 551, 520, 491, 463, 437, 413, 389, 367, 347, 327,
309, 292, 275, 260, 245, 231, 218, 206, 194, 183, 173, 163, 154, 146, 137, 130, 122, 115, 109, 103, 97, 91, 86, 81, 77,
73, 68, 65, 61, 57, 54, 51, 48, 45, 43, 40, 38, 36, 34, 32, 30, 28, 27, 25, 24, 22, 21, 20, 19, 18,
17, 16, 15, 14, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4,
4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0,
}
func (c *coinbaseManager) getDeflationaryPeriodBlockSubsidyFromTable(month uint64) uint64 {
if month >= uint64(len(subsidyByDeflationaryMonthTable)) {
month = uint64(len(subsidyByDeflationaryMonthTable) - 1)
// The first two bytes of a hash are a good deterministic source
// of randomness, so we use that instead of any rand implementation
firstTwoBytes := binary.LittleEndian.Uint16(selectedParentHash.ByteSlice()[:2])
for i := 0; i < binomialSteps; i++ {
step := firstTwoBytes & 1
firstTwoBytes >>= 1
binomialSum += int64(step)
}
return subsidyByDeflationaryMonthTable[month]
}
func (c *coinbaseManager) calcDeflationaryPeriodBlockSubsidyFloatCalc(month uint64) uint64 {
baseSubsidy := c.deflationaryPhaseBaseSubsidy
subsidy := float64(baseSubsidy) / math.Pow(2, float64(month)/12)
return uint64(subsidy)
return binomialSum - (binomialSteps / 2), nil
}
func (c *coinbaseManager) calcMergedBlockReward(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
@@ -278,11 +312,14 @@ func New(
databaseContext model.DBReader,
subsidyGenesisReward uint64,
preDeflationaryPhaseBaseSubsidy uint64,
minSubsidy uint64,
maxSubsidy uint64,
subsidyPastRewardMultiplier *big.Rat,
subsidyMergeSetRewardMultiplier *big.Rat,
coinbasePayloadScriptPublicKeyMaxLength uint8,
genesisHash *externalapi.DomainHash,
deflationaryPhaseDaaScore uint64,
deflationaryPhaseBaseSubsidy uint64,
fixedSubsidySwitchPruningPointInterval uint64,
fixedSubsidySwitchHashRateThreshold *big.Int,
dagTraversalManager model.DAGTraversalManager,
ghostdagDataStore model.GHOSTDAGDataStore,
@@ -296,11 +333,14 @@ func New(
databaseContext: databaseContext,
subsidyGenesisReward: subsidyGenesisReward,
preDeflationaryPhaseBaseSubsidy: preDeflationaryPhaseBaseSubsidy,
minSubsidy: minSubsidy,
maxSubsidy: maxSubsidy,
subsidyPastRewardMultiplier: subsidyPastRewardMultiplier,
subsidyMergeSetRewardMultiplier: subsidyMergeSetRewardMultiplier,
coinbasePayloadScriptPublicKeyMaxLength: coinbasePayloadScriptPublicKeyMaxLength,
genesisHash: genesisHash,
deflationaryPhaseDaaScore: deflationaryPhaseDaaScore,
deflationaryPhaseBaseSubsidy: deflationaryPhaseBaseSubsidy,
fixedSubsidySwitchPruningPointInterval: fixedSubsidySwitchPruningPointInterval,
fixedSubsidySwitchHashRateThreshold: fixedSubsidySwitchHashRateThreshold,
dagTraversalManager: dagTraversalManager,
ghostdagDataStore: ghostdagDataStore,

View File

@@ -1,126 +0,0 @@
package coinbasemanager
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
"github.com/kaspanet/kaspad/domain/dagconfig"
"strconv"
"testing"
)
func TestCalcDeflationaryPeriodBlockSubsidy(t *testing.T) {
const secondsPerMonth = 2629800
const secondsPerHalving = secondsPerMonth * 12
const deflationaryPhaseDaaScore = secondsPerMonth * 6
const deflationaryPhaseBaseSubsidy = 440 * constants.SompiPerKaspa
coinbaseManagerInterface := New(
nil,
0,
0,
0,
&externalapi.DomainHash{},
deflationaryPhaseDaaScore,
deflationaryPhaseBaseSubsidy,
nil,
nil,
nil,
nil,
nil,
nil,
nil)
coinbaseManagerInstance := coinbaseManagerInterface.(*coinbaseManager)
tests := []struct {
name string
blockDaaScore uint64
expectedBlockSubsidy uint64
}{
{
name: "start of deflationary phase",
blockDaaScore: deflationaryPhaseDaaScore,
expectedBlockSubsidy: deflationaryPhaseBaseSubsidy,
},
{
name: "after one halving",
blockDaaScore: deflationaryPhaseDaaScore + secondsPerHalving,
expectedBlockSubsidy: deflationaryPhaseBaseSubsidy / 2,
},
{
name: "after two halvings",
blockDaaScore: deflationaryPhaseDaaScore + secondsPerHalving*2,
expectedBlockSubsidy: deflationaryPhaseBaseSubsidy / 4,
},
{
name: "after five halvings",
blockDaaScore: deflationaryPhaseDaaScore + secondsPerHalving*5,
expectedBlockSubsidy: deflationaryPhaseBaseSubsidy / 32,
},
{
name: "after 32 halvings",
blockDaaScore: deflationaryPhaseDaaScore + secondsPerHalving*32,
expectedBlockSubsidy: deflationaryPhaseBaseSubsidy / 4294967296,
},
{
name: "just before subsidy depleted",
blockDaaScore: deflationaryPhaseDaaScore + secondsPerHalving*35,
expectedBlockSubsidy: 1,
},
{
name: "after subsidy depleted",
blockDaaScore: deflationaryPhaseDaaScore + secondsPerHalving*36,
expectedBlockSubsidy: 0,
},
}
for _, test := range tests {
blockSubsidy := coinbaseManagerInstance.calcDeflationaryPeriodBlockSubsidy(test.blockDaaScore)
if blockSubsidy != test.expectedBlockSubsidy {
t.Errorf("TestCalcDeflationaryPeriodBlockSubsidy: test '%s' failed. Want: %d, got: %d",
test.name, test.expectedBlockSubsidy, blockSubsidy)
}
}
}
func TestBuildSubsidyTable(t *testing.T) {
deflationaryPhaseBaseSubsidy := dagconfig.MainnetParams.DeflationaryPhaseBaseSubsidy
if deflationaryPhaseBaseSubsidy != 440*constants.SompiPerKaspa {
t.Errorf("TestBuildSubsidyTable: table generation function was not updated to reflect "+
"the new base subsidy %d. Please fix the constant above and replace subsidyByDeflationaryMonthTable "+
"in coinbasemanager.go with the printed table", deflationaryPhaseBaseSubsidy)
}
coinbaseManagerInterface := New(
nil,
0,
0,
0,
&externalapi.DomainHash{},
0,
deflationaryPhaseBaseSubsidy,
nil,
nil,
nil,
nil,
nil,
nil,
nil)
coinbaseManagerInstance := coinbaseManagerInterface.(*coinbaseManager)
var subsidyTable []uint64
for M := uint64(0); ; M++ {
subsidy := coinbaseManagerInstance.calcDeflationaryPeriodBlockSubsidyFloatCalc(M)
subsidyTable = append(subsidyTable, subsidy)
if subsidy == 0 {
break
}
}
tableStr := "\n{\t"
for i := 0; i < len(subsidyTable); i++ {
tableStr += strconv.FormatUint(subsidyTable[i], 10) + ", "
if (i+1)%25 == 0 {
tableStr += "\n\t"
}
}
tableStr += "\n}"
t.Logf(tableStr)
}

View File

@@ -11,13 +11,12 @@ import (
type dagTraversalManager struct {
databaseContext model.DBReader
dagTopologyManager model.DAGTopologyManager
ghostdagManager model.GHOSTDAGManager
ghostdagDataStore model.GHOSTDAGDataStore
reachabilityDataStore model.ReachabilityDataStore
daaWindowStore model.BlocksWithTrustedDataDAAWindowStore
genesisHash *externalapi.DomainHash
difficultyAdjustmentWindowSize int
dagTopologyManager model.DAGTopologyManager
ghostdagManager model.GHOSTDAGManager
ghostdagDataStore model.GHOSTDAGDataStore
reachabilityDataStore model.ReachabilityDataStore
daaWindowStore model.BlocksWithTrustedDataDAAWindowStore
genesisHash *externalapi.DomainHash
}
// New instantiates a new DAGTraversalManager
@@ -28,8 +27,7 @@ func New(
reachabilityDataStore model.ReachabilityDataStore,
ghostdagManager model.GHOSTDAGManager,
daaWindowStore model.BlocksWithTrustedDataDAAWindowStore,
genesisHash *externalapi.DomainHash,
difficultyAdjustmentWindowSize int) model.DAGTraversalManager {
genesisHash *externalapi.DomainHash) model.DAGTraversalManager {
return &dagTraversalManager{
databaseContext: databaseContext,
dagTopologyManager: dagTopologyManager,
@@ -38,8 +36,7 @@ func New(
ghostdagManager: ghostdagManager,
daaWindowStore: daaWindowStore,
genesisHash: genesisHash,
difficultyAdjustmentWindowSize: difficultyAdjustmentWindowSize,
genesisHash: genesisHash,
}
}

View File

@@ -6,10 +6,6 @@ import (
"github.com/kaspanet/kaspad/infrastructure/db/database"
)
func (dtm *dagTraversalManager) DAABlockWindow(stagingArea *model.StagingArea, highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
return dtm.BlockWindow(stagingArea, highHash, dtm.difficultyAdjustmentWindowSize)
}
// BlockWindow returns a blockWindow of the given size that contains the
// blocks in the past of highHash, the sorting is unspecified.
// If the number of blocks in the past of startingNode is less then windowSize,
@@ -28,6 +24,17 @@ func (dtm *dagTraversalManager) BlockWindow(stagingArea *model.StagingArea, high
return window, nil
}
func (dtm *dagTraversalManager) BlockWindowWithGHOSTDAGData(stagingArea *model.StagingArea,
highHash *externalapi.DomainHash, windowSize int) ([]*externalapi.BlockGHOSTDAGDataHashPair, error) {
windowHeap, err := dtm.calculateBlockWindowHeap(stagingArea, highHash, windowSize)
if err != nil {
return nil, err
}
return windowHeap.impl.slice, nil
}
func (dtm *dagTraversalManager) calculateBlockWindowHeap(stagingArea *model.StagingArea,
highHash *externalapi.DomainHash, windowSize int) (*sizedUpBlockHeap, error) {

View File

@@ -1,18 +1,18 @@
package difficultymanager
import (
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/util/difficulty"
"math"
"math/big"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/util/difficulty"
)
type difficultyBlock struct {
timeInMilliseconds int64
Bits uint32
hash *externalapi.DomainHash
blueWork *big.Int
}
type blockWindow []difficultyBlock
@@ -27,8 +27,6 @@ func (dm *difficultyManager) getDifficultyBlock(
return difficultyBlock{
timeInMilliseconds: header.TimeInMilliseconds(),
Bits: header.Bits(),
hash: blockHash,
blueWork: header.BlueWork(),
}, nil
}
@@ -55,32 +53,19 @@ func (dm *difficultyManager) blockWindow(stagingArea *model.StagingArea, startin
return window, windowHashes, nil
}
func ghostdagLess(blockA *difficultyBlock, blockB *difficultyBlock) bool {
switch blockA.blueWork.Cmp(blockB.blueWork) {
case -1:
return true
case 1:
return false
case 0:
return blockA.hash.Less(blockB.hash)
default:
panic("big.Int.Cmp is defined to always return -1/1/0 and nothing else")
}
}
func (window blockWindow) minMaxTimestamps() (min, max int64, minIndex int) {
func (window blockWindow) minMaxTimestamps() (min, max int64, minIndex, maxIndex int) {
min = math.MaxInt64
minIndex = 0
minIndex = math.MaxInt64
max = 0
maxIndex = 0
for i, block := range window {
// If timestamps are equal we ghostdag compare in order to reach consensus on `minIndex`
if block.timeInMilliseconds < min ||
(block.timeInMilliseconds == min && ghostdagLess(&block, &window[minIndex])) {
if block.timeInMilliseconds < min {
min = block.timeInMilliseconds
minIndex = i
}
if block.timeInMilliseconds > max {
max = block.timeInMilliseconds
maxIndex = i
}
}
return

View File

@@ -115,10 +115,9 @@ func (dm *difficultyManager) requiredDifficultyFromTargetsWindow(targetsWindow b
if len(targetsWindow) < 2 || len(targetsWindow) < dm.difficultyAdjustmentWindowSize {
return dm.genesisBits, nil
}
windowMinTimestamp, windowMaxTimeStamp, windowMinIndex := targetsWindow.minMaxTimestamps()
windowMinTimestamp, windowMaxTimeStamp, windowsMinIndex, _ := targetsWindow.minMaxTimestamps()
// Remove the last block from the window so to calculate the average target of dag.difficultyAdjustmentWindowSize blocks
targetsWindow.remove(windowMinIndex)
targetsWindow.remove(windowsMinIndex)
// Calculate new target difficulty as:
// averageWindowTarget * (windowMinTimestamp / (targetTimePerBlock * windowSize))

Some files were not shown because too many files have changed in this diff Show More