mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-03-22 16:13:45 +00:00
Compare commits
6 Commits
someone235
...
v0.12.1-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c10fafc07c | ||
|
|
12781833e9 | ||
|
|
24900352f4 | ||
|
|
55d4e78749 | ||
|
|
8ff78ecdb0 | ||
|
|
3f959be0a3 |
@@ -7,6 +7,8 @@ Kaspad
|
||||
|
||||
Kaspad is the reference full node Kaspa implementation written in Go (golang).
|
||||
|
||||
This project is currently under active development and is in Beta state.
|
||||
|
||||
## What is kaspa
|
||||
|
||||
Kaspa is an attempt at a proof-of-work cryptocurrency with instant confirmations and sub-second block times. It is based on [the PHANTOM protocol](https://eprint.iacr.org/2018/104.pdf), a generalization of Nakamoto consensus.
|
||||
|
||||
@@ -161,8 +161,6 @@ const (
|
||||
CmdNewBlockTemplateNotificationMessage
|
||||
CmdGetMempoolEntriesByAddressesRequestMessage
|
||||
CmdGetMempoolEntriesByAddressesResponseMessage
|
||||
CmdGetCoinSupplyRequestMessage
|
||||
CmdGetCoinSupplyResponseMessage
|
||||
)
|
||||
|
||||
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
|
||||
@@ -296,10 +294,8 @@ var RPCMessageCommandToString = map[MessageCommand]string{
|
||||
CmdNotifyNewBlockTemplateRequestMessage: "NotifyNewBlockTemplateRequest",
|
||||
CmdNotifyNewBlockTemplateResponseMessage: "NotifyNewBlockTemplateResponse",
|
||||
CmdNewBlockTemplateNotificationMessage: "NewBlockTemplateNotification",
|
||||
CmdGetMempoolEntriesByAddressesRequestMessage: "GetMempoolEntriesByAddressesRequest",
|
||||
CmdGetMempoolEntriesByAddressesResponseMessage: "GetMempoolEntriesByAddressesResponse",
|
||||
CmdGetCoinSupplyRequestMessage: "GetCoinSupplyRequest",
|
||||
CmdGetCoinSupplyResponseMessage: "GetCoinSupplyResponse",
|
||||
CmdGetMempoolEntriesByAddressesRequestMessage: "CmdGetMempoolEntriesByAddressesRequest",
|
||||
CmdGetMempoolEntriesByAddressesResponseMessage: "CmdGetMempoolEntriesByAddressesResponse",
|
||||
}
|
||||
|
||||
// Message is an interface that describes a kaspa message. A type that
|
||||
|
||||
@@ -133,8 +133,8 @@ func TestTx(t *testing.T) {
|
||||
|
||||
// TestTxHash tests the ability to generate the hash of a transaction accurately.
|
||||
func TestTxHashAndID(t *testing.T) {
|
||||
txHash1Str := "b06f8b650115b5cf4d59499e10764a9312742930cb43c9b4ff6495d76f332ed7"
|
||||
txID1Str := "e20225c3d065ee41743607ee627db44d01ef396dc9779b05b2caf55bac50e12d"
|
||||
txHash1Str := "93663e597f6c968d32d229002f76408edf30d6a0151ff679fc729812d8cb2acc"
|
||||
txID1Str := "24079c6d2bdf602fc389cc307349054937744a9c8dc0f07c023e6af0e949a4e7"
|
||||
wantTxID1, err := transactionid.FromString(txID1Str)
|
||||
if err != nil {
|
||||
t.Fatalf("NewTxIDFromStr: %v", err)
|
||||
@@ -185,7 +185,7 @@ func TestTxHashAndID(t *testing.T) {
|
||||
spew.Sprint(tx1ID), spew.Sprint(wantTxID1))
|
||||
}
|
||||
|
||||
hash2Str := "fa16a8ce88d52ca1ff45187bbba0d33044e9f5fe309e8d0b22d4812dcf1782b7"
|
||||
hash2Str := "8dafd1bec24527d8e3b443ceb0a3b92fffc0d60026317f890b2faf5e9afc177a"
|
||||
wantHash2, err := externalapi.NewDomainHashFromString(hash2Str)
|
||||
if err != nil {
|
||||
t.Errorf("NewTxIDFromStr: %v", err)
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
// GetCoinSupplyRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetCoinSupplyRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetCoinSupplyRequestMessage) Command() MessageCommand {
|
||||
return CmdGetCoinSupplyRequestMessage
|
||||
}
|
||||
|
||||
// NewGetCoinSupplyRequestMessage returns a instance of the message
|
||||
func NewGetCoinSupplyRequestMessage() *GetCoinSupplyRequestMessage {
|
||||
return &GetCoinSupplyRequestMessage{}
|
||||
}
|
||||
|
||||
// GetCoinSupplyResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetCoinSupplyResponseMessage struct {
|
||||
baseMessage
|
||||
MaxSompi uint64
|
||||
CirculatingSompi uint64
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetCoinSupplyResponseMessage) Command() MessageCommand {
|
||||
return CmdGetCoinSupplyResponseMessage
|
||||
}
|
||||
|
||||
// NewGetCoinSupplyResponseMessage returns a instance of the message
|
||||
func NewGetCoinSupplyResponseMessage(maxSompi uint64, circulatingSompi uint64) *GetCoinSupplyResponseMessage {
|
||||
return &GetCoinSupplyResponseMessage{
|
||||
MaxSompi: maxSompi,
|
||||
CirculatingSompi: circulatingSompi,
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,6 @@ type GetInfoResponseMessage struct {
|
||||
P2PID string
|
||||
MempoolSize uint64
|
||||
ServerVersion string
|
||||
IsUtxoIndexed bool
|
||||
IsSynced bool
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
@@ -35,12 +33,10 @@ func (msg *GetInfoResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetInfoResponseMessage returns a instance of the message
|
||||
func NewGetInfoResponseMessage(p2pID string, mempoolSize uint64, serverVersion string, isUtxoIndexed bool, isSynced bool) *GetInfoResponseMessage {
|
||||
func NewGetInfoResponseMessage(p2pID string, mempoolSize uint64, serverVersion string) *GetInfoResponseMessage {
|
||||
return &GetInfoResponseMessage{
|
||||
P2PID: p2pID,
|
||||
MempoolSize: mempoolSize,
|
||||
ServerVersion: serverVersion,
|
||||
IsUtxoIndexed: isUtxoIndexed,
|
||||
IsSynced: isSynced,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ package appmessage
|
||||
// its respective RPC message
|
||||
type GetMempoolEntriesRequestMessage struct {
|
||||
baseMessage
|
||||
IncludeOrphanPool bool
|
||||
FilterTransactionPool bool
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -14,11 +12,8 @@ func (msg *GetMempoolEntriesRequestMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetMempoolEntriesRequestMessage returns a instance of the message
|
||||
func NewGetMempoolEntriesRequestMessage(includeOrphanPool bool, filterTransactionPool bool) *GetMempoolEntriesRequestMessage {
|
||||
return &GetMempoolEntriesRequestMessage{
|
||||
IncludeOrphanPool: includeOrphanPool,
|
||||
FilterTransactionPool: filterTransactionPool,
|
||||
}
|
||||
func NewGetMempoolEntriesRequestMessage() *GetMempoolEntriesRequestMessage {
|
||||
return &GetMempoolEntriesRequestMessage{}
|
||||
}
|
||||
|
||||
// GetMempoolEntriesResponseMessage is an appmessage corresponding to
|
||||
|
||||
@@ -11,9 +11,7 @@ type MempoolEntryByAddress struct {
|
||||
// its respective RPC message
|
||||
type GetMempoolEntriesByAddressesRequestMessage struct {
|
||||
baseMessage
|
||||
Addresses []string
|
||||
IncludeOrphanPool bool
|
||||
FilterTransactionPool bool
|
||||
Addresses []string
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -22,11 +20,9 @@ func (msg *GetMempoolEntriesByAddressesRequestMessage) Command() MessageCommand
|
||||
}
|
||||
|
||||
// NewGetMempoolEntriesByAddressesRequestMessage returns a instance of the message
|
||||
func NewGetMempoolEntriesByAddressesRequestMessage(addresses []string, includeOrphanPool bool, filterTransactionPool bool) *GetMempoolEntriesByAddressesRequestMessage {
|
||||
func NewGetMempoolEntriesByAddressesRequestMessage(addresses []string) *GetMempoolEntriesByAddressesRequestMessage {
|
||||
return &GetMempoolEntriesByAddressesRequestMessage{
|
||||
Addresses: addresses,
|
||||
IncludeOrphanPool: includeOrphanPool,
|
||||
FilterTransactionPool: filterTransactionPool,
|
||||
Addresses: addresses,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,7 @@ package appmessage
|
||||
// its respective RPC message
|
||||
type GetMempoolEntryRequestMessage struct {
|
||||
baseMessage
|
||||
TxID string
|
||||
IncludeOrphanPool bool
|
||||
FilterTransactionPool bool
|
||||
TxID string
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -15,12 +13,8 @@ func (msg *GetMempoolEntryRequestMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetMempoolEntryRequestMessage returns a instance of the message
|
||||
func NewGetMempoolEntryRequestMessage(txID string, includeOrphanPool bool, filterTransactionPool bool) *GetMempoolEntryRequestMessage {
|
||||
return &GetMempoolEntryRequestMessage{
|
||||
TxID: txID,
|
||||
IncludeOrphanPool: includeOrphanPool,
|
||||
FilterTransactionPool: filterTransactionPool,
|
||||
}
|
||||
func NewGetMempoolEntryRequestMessage(txID string) *GetMempoolEntryRequestMessage {
|
||||
return &GetMempoolEntryRequestMessage{TxID: txID}
|
||||
}
|
||||
|
||||
// GetMempoolEntryResponseMessage is an appmessage corresponding to
|
||||
@@ -36,7 +30,6 @@ type GetMempoolEntryResponseMessage struct {
|
||||
type MempoolEntry struct {
|
||||
Fee uint64
|
||||
Transaction *RPCTransaction
|
||||
IsOrphan bool
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -45,12 +38,11 @@ func (msg *GetMempoolEntryResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetMempoolEntryResponseMessage returns a instance of the message
|
||||
func NewGetMempoolEntryResponseMessage(fee uint64, transaction *RPCTransaction, isOrphan bool) *GetMempoolEntryResponseMessage {
|
||||
func NewGetMempoolEntryResponseMessage(fee uint64, transaction *RPCTransaction) *GetMempoolEntryResponseMessage {
|
||||
return &GetMempoolEntryResponseMessage{
|
||||
Entry: &MempoolEntry{
|
||||
Fee: fee,
|
||||
Transaction: transaction,
|
||||
IsOrphan: isOrphan,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
|
||||
return protocolerrors.Errorf(false, "cannot add header only block")
|
||||
}
|
||||
|
||||
err := f.Domain().Consensus().ValidateAndInsertBlock(block, true)
|
||||
_, err := f.Domain().Consensus().ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
log.Warnf("Validation failed for block %s: %s", consensushashing.BlockHash(block), err)
|
||||
|
||||
@@ -141,7 +141,7 @@ func (f *FlowContext) unorphanBlock(orphanHash externalapi.DomainHash) (bool, er
|
||||
}
|
||||
delete(f.orphans, orphanHash)
|
||||
|
||||
err := f.domain.Consensus().ValidateAndInsertBlock(orphanBlock, true)
|
||||
_, err := f.domain.Consensus().ValidateAndInsertBlock(orphanBlock, true)
|
||||
if err != nil {
|
||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||
log.Warnf("Validation failed for orphan block %s: %s", orphanHash, err)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -33,7 +34,7 @@ func HandleIBDBlockLocator(context HandleIBDBlockLocatorContext, incomingRoute *
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !blockInfo.HasHeader() {
|
||||
if !blockInfo.Exists {
|
||||
return protocolerrors.Errorf(true, "received IBDBlockLocator "+
|
||||
"with an unknown targetHash %s", targetHash)
|
||||
}
|
||||
@@ -46,7 +47,7 @@ func HandleIBDBlockLocator(context HandleIBDBlockLocatorContext, incomingRoute *
|
||||
}
|
||||
|
||||
// The IBD block locator is checking only existing blocks with bodies.
|
||||
if !blockInfo.HasBody() {
|
||||
if !blockInfo.Exists || blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"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"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -27,15 +28,18 @@ func HandleIBDBlockRequests(context HandleIBDBlockRequestsContext, incomingRoute
|
||||
log.Debugf("Got request for %d ibd blocks", len(msgRequestIBDBlocks.Hashes))
|
||||
for i, hash := range msgRequestIBDBlocks.Hashes {
|
||||
// Fetch the block from the database.
|
||||
block, found, err := context.Domain().Consensus().GetBlock(hash)
|
||||
blockInfo, err := context.Domain().Consensus().GetBlockInfo(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !blockInfo.Exists || blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
||||
return protocolerrors.Errorf(true, "block %s not found (v5)", hash)
|
||||
}
|
||||
block, err := context.Domain().Consensus().GetBlock(hash)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to fetch requested block hash %s", hash)
|
||||
}
|
||||
|
||||
if !found {
|
||||
return protocolerrors.Errorf(false, "IBD block %s not found", hash)
|
||||
}
|
||||
|
||||
// TODO (Partial nodes): Convert block to partial block if needed
|
||||
|
||||
blockMessage := appmessage.DomainBlockToMsgBlock(block)
|
||||
|
||||
@@ -119,15 +119,11 @@ func HandlePruningPointAndItsAnticoneRequests(context PruningPointAndItsAnticone
|
||||
}
|
||||
|
||||
for i, blockHash := range pointAndItsAnticone {
|
||||
block, found, err := context.Domain().Consensus().GetBlock(blockHash)
|
||||
block, err := context.Domain().Consensus().GetBlock(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !found {
|
||||
return protocolerrors.Errorf(false, "pruning point anticone block %s not found", blockHash)
|
||||
}
|
||||
|
||||
err = outgoingRoute.Enqueue(appmessage.DomainBlockWithTrustedDataToBlockWithTrustedDataV4(block, trustedDataDAABlockIndexes[*blockHash], trustedDataGHOSTDAGDataIndexes[*blockHash]))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
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"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -28,15 +29,18 @@ func HandleRelayBlockRequests(context RelayBlockRequestsContext, incomingRoute *
|
||||
log.Debugf("Got request for relay blocks with hashes %s", getRelayBlocksMessage.Hashes)
|
||||
for _, hash := range getRelayBlocksMessage.Hashes {
|
||||
// Fetch the block from the database.
|
||||
block, found, err := context.Domain().Consensus().GetBlock(hash)
|
||||
blockInfo, err := context.Domain().Consensus().GetBlockInfo(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !blockInfo.Exists || blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
||||
return protocolerrors.Errorf(true, "block %s not found", hash)
|
||||
}
|
||||
block, err := context.Domain().Consensus().GetBlock(hash)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to fetch requested block hash %s", hash)
|
||||
}
|
||||
|
||||
if !found {
|
||||
return protocolerrors.Errorf(false, "Relay block %s not found", hash)
|
||||
}
|
||||
|
||||
// TODO (Partial nodes): Convert block to partial block if needed
|
||||
|
||||
err = outgoingRoute.Enqueue(appmessage.DomainBlockToMsgBlock(block))
|
||||
|
||||
@@ -211,14 +211,10 @@ func (flow *handleRelayInvsFlow) start() error {
|
||||
continue
|
||||
}
|
||||
virtualHasNewParents = true
|
||||
block, found, err := flow.Domain().Consensus().GetBlock(parent)
|
||||
block, err := flow.Domain().Consensus().GetBlock(parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !found {
|
||||
return protocolerrors.Errorf(false, "Virtual parent %s not found", parent)
|
||||
}
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
log.Debugf("Relaying block %s", blockHash)
|
||||
err = flow.relayBlock(block)
|
||||
@@ -325,7 +321,7 @@ func (flow *handleRelayInvsFlow) readMsgBlock() (msgBlock *appmessage.MsgBlock,
|
||||
|
||||
func (flow *handleRelayInvsFlow) processBlock(block *externalapi.DomainBlock) ([]*externalapi.DomainHash, error) {
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
err := flow.Domain().Consensus().ValidateAndInsertBlock(block, true)
|
||||
_, err := flow.Domain().Consensus().ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
if !errors.As(err, &ruleerrors.RuleError{}) {
|
||||
return nil, errors.Wrapf(err, "failed to process block %s", blockHash)
|
||||
|
||||
@@ -47,9 +47,9 @@ func (flow *handleRequestAnticoneFlow) start() error {
|
||||
|
||||
// GetAnticone is expected to be called by the syncee for getting the anticone of the header selected tip
|
||||
// intersected by past of relayed block, and is thus expected to be bounded by mergeset limit since
|
||||
// we relay blocks only if they enter virtual's mergeset. We add a 2 factor for possible sync gaps.
|
||||
// we relay blocks only if they enter virtual's mergeset. We add 2 for a small margin error.
|
||||
blockHashes, err := flow.Domain().Consensus().GetAnticone(blockHash, contextHash,
|
||||
flow.Config().ActiveNetParams.MergeSetSizeLimit*2)
|
||||
flow.Config().ActiveNetParams.MergeSetSizeLimit+2)
|
||||
if err != nil {
|
||||
return protocolerrors.Wrap(true, err, "Failed querying anticone")
|
||||
}
|
||||
|
||||
@@ -46,25 +46,7 @@ func (flow *handleRequestHeadersFlow) start() error {
|
||||
}
|
||||
log.Debugf("Received requestHeaders with lowHash: %s, highHash: %s", lowHash, highHash)
|
||||
|
||||
consensus := flow.Domain().Consensus()
|
||||
|
||||
lowHashInfo, err := consensus.GetBlockInfo(lowHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !lowHashInfo.HasHeader() {
|
||||
return protocolerrors.Errorf(true, "Block %s does not exist", lowHash)
|
||||
}
|
||||
|
||||
highHashInfo, err := consensus.GetBlockInfo(highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !highHashInfo.HasHeader() {
|
||||
return protocolerrors.Errorf(true, "Block %s does not exist", highHash)
|
||||
}
|
||||
|
||||
isLowSelectedAncestorOfHigh, err := consensus.IsInSelectedParentChainOf(lowHash, highHash)
|
||||
isLowSelectedAncestorOfHigh, err := flow.Domain().Consensus().IsInSelectedParentChainOf(lowHash, highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -80,7 +62,7 @@ func (flow *handleRequestHeadersFlow) start() error {
|
||||
// in order to avoid locking the consensus for too long
|
||||
// maxBlocks MUST be >= MergeSetSizeLimit + 1
|
||||
const maxBlocks = 1 << 10
|
||||
blockHashes, _, err := consensus.GetHashesBetween(lowHash, highHash, maxBlocks)
|
||||
blockHashes, _, err := flow.Domain().Consensus().GetHashesBetween(lowHash, highHash, maxBlocks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -88,7 +70,7 @@ func (flow *handleRequestHeadersFlow) start() error {
|
||||
|
||||
blockHeaders := make([]*appmessage.MsgBlockHeader, len(blockHashes))
|
||||
for i, blockHash := range blockHashes {
|
||||
blockHeader, err := consensus.GetBlockHeader(blockHash)
|
||||
blockHeader, err := flow.Domain().Consensus().GetBlockHeader(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/common"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
@@ -71,17 +70,16 @@ func (flow *handleIBDFlow) runIBDIfNotRunning(block *externalapi.DomainBlock) er
|
||||
}
|
||||
|
||||
isFinishedSuccessfully := false
|
||||
var err error
|
||||
defer func() {
|
||||
flow.UnsetIBDRunning()
|
||||
flow.logIBDFinished(isFinishedSuccessfully, err)
|
||||
flow.logIBDFinished(isFinishedSuccessfully)
|
||||
}()
|
||||
|
||||
relayBlockHash := consensushashing.BlockHash(block)
|
||||
|
||||
log.Infof("IBD started with peer %s and relayBlockHash %s", flow.peer, relayBlockHash)
|
||||
log.Infof("Syncing blocks up to %s", relayBlockHash)
|
||||
log.Infof("Trying to find highest known syncer chain block from peer %s with relay hash %s", flow.peer, relayBlockHash)
|
||||
log.Debugf("IBD started with peer %s and relayBlockHash %s", flow.peer, relayBlockHash)
|
||||
log.Debugf("Syncing blocks up to %s", relayBlockHash)
|
||||
log.Debugf("Trying to find highest known syncer chain block from peer %s with relay hash %s", flow.peer, relayBlockHash)
|
||||
|
||||
syncerHeaderSelectedTipHash, highestKnownSyncerChainHash, err := flow.negotiateMissingSyncerChainSegment()
|
||||
if err != nil {
|
||||
@@ -100,7 +98,7 @@ func (flow *handleIBDFlow) runIBDIfNotRunning(block *externalapi.DomainBlock) er
|
||||
|
||||
if shouldDownloadHeadersProof {
|
||||
log.Infof("Starting IBD with headers proof")
|
||||
err = flow.ibdWithHeadersProof(syncerHeaderSelectedTipHash, relayBlockHash, block.Header.DAAScore())
|
||||
err := flow.ibdWithHeadersProof(syncerHeaderSelectedTipHash, relayBlockHash, block.Header.DAAScore())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -183,10 +181,6 @@ func (flow *handleIBDFlow) negotiateMissingSyncerChainSegment() (*externalapi.Do
|
||||
return nil, nil, err
|
||||
}
|
||||
if info.Exists {
|
||||
if info.BlockStatus == externalapi.StatusInvalid {
|
||||
return nil, nil, protocolerrors.Errorf(true, "Sent invalid chain block %s", syncerChainHash)
|
||||
}
|
||||
|
||||
currentHighestKnownSyncerChainHash = syncerChainHash
|
||||
break
|
||||
}
|
||||
@@ -267,7 +261,7 @@ func (flow *handleIBDFlow) negotiateMissingSyncerChainSegment() (*externalapi.Do
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("Found highest known syncer chain block %s from peer %s",
|
||||
log.Debugf("Found highest known syncer chain block %s from peer %s",
|
||||
highestKnownSyncerChainHash, flow.peer)
|
||||
|
||||
return syncerHeaderSelectedTipHash, highestKnownSyncerChainHash, nil
|
||||
@@ -282,10 +276,10 @@ func (flow *handleIBDFlow) isGenesisVirtualSelectedParent() (bool, error) {
|
||||
return virtualSelectedParent.Equal(flow.Config().NetParams().GenesisHash), nil
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) logIBDFinished(isFinishedSuccessfully bool, err error) {
|
||||
func (flow *handleIBDFlow) logIBDFinished(isFinishedSuccessfully bool) {
|
||||
successString := "successfully"
|
||||
if !isFinishedSuccessfully {
|
||||
successString = fmt.Sprintf("(interrupted: %s)", err)
|
||||
successString = "(interrupted)"
|
||||
}
|
||||
log.Infof("IBD with peer %s finished %s", flow.peer, successString)
|
||||
}
|
||||
@@ -494,7 +488,7 @@ func (flow *handleIBDFlow) processHeader(consensus externalapi.Consensus, msgBlo
|
||||
log.Debugf("Block header %s is already in the DAG. Skipping...", blockHash)
|
||||
return nil
|
||||
}
|
||||
err = consensus.ValidateAndInsertBlock(block, false)
|
||||
_, err = consensus.ValidateAndInsertBlock(block, false)
|
||||
if err != nil {
|
||||
if !errors.As(err, &ruleerrors.RuleError{}) {
|
||||
return errors.Wrapf(err, "failed to process header %s during IBD", blockHash)
|
||||
@@ -624,12 +618,6 @@ func (flow *handleIBDFlow) syncMissingBlockBodies(highHash *externalapi.DomainHa
|
||||
progressReporter := newIBDProgressReporter(lowBlockHeader.DAAScore(), highBlockHeader.DAAScore(), "blocks")
|
||||
highestProcessedDAAScore := lowBlockHeader.DAAScore()
|
||||
|
||||
// If the IBD is small, we want to update the virtual after each block in order to avoid complications and possible bugs.
|
||||
updateVirtual, err := flow.Domain().Consensus().IsNearlySynced()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for offset := 0; offset < len(hashes); offset += ibdBatchSize {
|
||||
var hashesToRequest []*externalapi.DomainHash
|
||||
if offset+ibdBatchSize < len(hashes) {
|
||||
@@ -666,7 +654,7 @@ func (flow *handleIBDFlow) syncMissingBlockBodies(highHash *externalapi.DomainHa
|
||||
return err
|
||||
}
|
||||
|
||||
err = flow.Domain().Consensus().ValidateAndInsertBlock(block, updateVirtual)
|
||||
_, err = flow.Domain().Consensus().ValidateAndInsertBlock(block, false)
|
||||
if err != nil {
|
||||
if errors.Is(err, ruleerrors.ErrDuplicateBlock) {
|
||||
log.Debugf("Skipping IBD Block %s as it has already been added to the DAG", blockHash)
|
||||
@@ -685,15 +673,7 @@ func (flow *handleIBDFlow) syncMissingBlockBodies(highHash *externalapi.DomainHa
|
||||
progressReporter.reportProgress(len(hashesToRequest), highestProcessedDAAScore)
|
||||
}
|
||||
|
||||
// We need to resolve virtual only if it wasn't updated while syncing block bodies
|
||||
if !updateVirtual {
|
||||
err := flow.resolveVirtual(highestProcessedDAAScore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return flow.OnNewBlockTemplate()
|
||||
return flow.resolveVirtual(highestProcessedDAAScore)
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) banIfBlockIsHeaderOnly(block *externalapi.DomainBlock) error {
|
||||
@@ -706,24 +686,37 @@ func (flow *handleIBDFlow) banIfBlockIsHeaderOnly(block *externalapi.DomainBlock
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) resolveVirtual(estimatedVirtualDAAScoreTarget uint64) error {
|
||||
err := flow.Domain().Consensus().ResolveVirtual(func(virtualDAAScoreStart uint64, virtualDAAScore uint64) {
|
||||
var percents int
|
||||
if estimatedVirtualDAAScoreTarget-virtualDAAScoreStart <= 0 {
|
||||
percents = 100
|
||||
} else {
|
||||
percents = int(float64(virtualDAAScore-virtualDAAScoreStart) / float64(estimatedVirtualDAAScoreTarget-virtualDAAScoreStart) * 100)
|
||||
}
|
||||
if percents < 0 {
|
||||
percents = 0
|
||||
} else if percents > 100 {
|
||||
percents = 100
|
||||
}
|
||||
log.Infof("Resolving virtual. Estimated progress: %d%%", percents)
|
||||
})
|
||||
virtualDAAScoreStart, err := flow.Domain().Consensus().GetVirtualDAAScore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Resolved virtual")
|
||||
return nil
|
||||
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)
|
||||
}
|
||||
_, isCompletelyResolved, err := flow.Domain().Consensus().ResolveVirtual()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isCompletelyResolved {
|
||||
log.Infof("Resolved virtual")
|
||||
err = flow.OnNewBlockTemplate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func (flow *handleIBDFlow) ibdWithHeadersProof(
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("IBD with pruning proof from %s was unsuccessful. Deleting the staging consensus. (%s)", flow.peer, err)
|
||||
log.Infof("IBD with pruning proof from %s was unsuccessful. Deleting the staging consensus.", flow.peer)
|
||||
deleteStagingConsensusErr := flow.Domain().DeleteStagingConsensus()
|
||||
if deleteStagingConsensusErr != nil {
|
||||
return deleteStagingConsensusErr
|
||||
@@ -55,12 +55,7 @@ func (flow *handleIBDFlow) shouldSyncAndShouldDownloadHeadersProof(
|
||||
|
||||
var highestSharedBlockFound, isPruningPointInSharedBlockChain bool
|
||||
if highestKnownSyncerChainHash != nil {
|
||||
blockInfo, err := flow.Domain().Consensus().GetBlockInfo(highestKnownSyncerChainHash)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
highestSharedBlockFound = blockInfo.HasBody()
|
||||
highestSharedBlockFound = true
|
||||
pruningPoint, err := flow.Domain().Consensus().PruningPoint()
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
@@ -92,21 +87,21 @@ func (flow *handleIBDFlow) shouldSyncAndShouldDownloadHeadersProof(
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) checkIfHighHashHasMoreBlueWorkThanSelectedTipAndPruningDepthMoreBlueScore(relayBlock *externalapi.DomainBlock) (bool, error) {
|
||||
virtualSelectedParent, err := flow.Domain().Consensus().GetVirtualSelectedParent()
|
||||
headersSelectedTip, err := flow.Domain().Consensus().GetHeadersSelectedTip()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
virtualSelectedTipInfo, err := flow.Domain().Consensus().GetBlockInfo(virtualSelectedParent)
|
||||
headersSelectedTipInfo, err := flow.Domain().Consensus().GetBlockInfo(headersSelectedTip)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if relayBlock.Header.BlueScore() < virtualSelectedTipInfo.BlueScore+flow.Config().NetParams().PruningDepth() {
|
||||
if relayBlock.Header.BlueScore() < headersSelectedTipInfo.BlueScore+flow.Config().NetParams().PruningDepth() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return relayBlock.Header.BlueWork().Cmp(virtualSelectedTipInfo.BlueWork) > 0, nil
|
||||
return relayBlock.Header.BlueWork().Cmp(headersSelectedTipInfo.BlueWork) > 0, nil
|
||||
}
|
||||
|
||||
func (flow *handleIBDFlow) syncAndValidatePruningPointProof() (*externalapi.DomainHash, error) {
|
||||
@@ -285,7 +280,7 @@ func (flow *handleIBDFlow) processBlockWithTrustedData(
|
||||
blockWithTrustedData.GHOSTDAGData = append(blockWithTrustedData.GHOSTDAGData, appmessage.GHOSTDAGHashPairToDomainGHOSTDAGHashPair(data.GHOSTDAGData[index]))
|
||||
}
|
||||
|
||||
err := consensus.ValidateAndInsertBlockWithTrustedData(blockWithTrustedData, false)
|
||||
_, err := consensus.ValidateAndInsertBlockWithTrustedData(blockWithTrustedData, false)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ func (flow *handleRelayedTransactionsFlow) requestInvTransactions(
|
||||
func (flow *handleRelayedTransactionsFlow) isKnownTransaction(txID *externalapi.DomainTransactionID) bool {
|
||||
// Ask the transaction memory pool if the transaction is known
|
||||
// to it in any form (main pool or orphan).
|
||||
if _, _, ok := flow.Domain().MiningManager().GetTransaction(txID, true, true); ok {
|
||||
if _, ok := flow.Domain().MiningManager().GetTransaction(txID); ok {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ func (flow *handleRequestedTransactionsFlow) start() error {
|
||||
}
|
||||
|
||||
for _, transactionID := range msgRequestTransactions.IDs {
|
||||
tx, _, ok := flow.Domain().MiningManager().GetTransaction(transactionID, true, false)
|
||||
tx, ok := flow.Domain().MiningManager().GetTransaction(transactionID)
|
||||
|
||||
if !ok {
|
||||
msgTransactionNotFound := appmessage.NewMsgTransactionNotFound(transactionID)
|
||||
@@ -40,6 +40,7 @@ func (flow *handleRequestedTransactionsFlow) start() error {
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err := flow.outgoingRoute.Enqueue(appmessage.DomainTransactionToMsgTx(tx))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -223,9 +223,18 @@ func (m *Manager) notifyVirtualSelectedParentChainChanged(virtualChangeSet *exte
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualSelectedParentChainChanged")
|
||||
defer onEnd()
|
||||
|
||||
hasListeners, includeAcceptedTransactionIDs := m.context.NotificationManager.HasListenersThatPropagateVirtualSelectedParentChainChanged()
|
||||
listenersThatPropagateSelectedParentChanged :=
|
||||
m.context.NotificationManager.AllListenersThatPropagateVirtualSelectedParentChainChanged()
|
||||
if len(listenersThatPropagateSelectedParentChanged) > 0 {
|
||||
// Generating acceptedTransactionIDs is a heavy operation, so we check if it's needed by any listener.
|
||||
includeAcceptedTransactionIDs := false
|
||||
for _, listener := range listenersThatPropagateSelectedParentChanged {
|
||||
if listener.IncludeAcceptedTransactionIDsInVirtualSelectedParentChainChangedNotifications() {
|
||||
includeAcceptedTransactionIDs = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if hasListeners {
|
||||
notification, err := m.context.ConvertVirtualSelectedParentChainChangesToChainChangedNotificationMessage(
|
||||
virtualChangeSet.VirtualSelectedParentChainChanges, includeAcceptedTransactionIDs)
|
||||
if err != nil {
|
||||
|
||||
@@ -49,7 +49,6 @@ var handlers = map[appmessage.MessageCommand]handler{
|
||||
appmessage.CmdEstimateNetworkHashesPerSecondRequestMessage: rpchandlers.HandleEstimateNetworkHashesPerSecond,
|
||||
appmessage.CmdNotifyVirtualDaaScoreChangedRequestMessage: rpchandlers.HandleNotifyVirtualDaaScoreChanged,
|
||||
appmessage.CmdNotifyNewBlockTemplateRequestMessage: rpchandlers.HandleNotifyNewBlockTemplate,
|
||||
appmessage.CmdGetCoinSupplyRequestMessage: rpchandlers.HandleGetCoinSupply,
|
||||
appmessage.CmdGetMempoolEntriesByAddressesRequestMessage: rpchandlers.HandleGetMempoolEntriesByAddresses,
|
||||
}
|
||||
|
||||
|
||||
@@ -40,39 +40,24 @@ func (ctx *Context) getAndConvertAcceptedTransactionIDs(selectedParentChainChang
|
||||
|
||||
acceptedTransactionIDs := make([]*appmessage.AcceptedTransactionIDs, len(selectedParentChainChanges.Added))
|
||||
|
||||
const chunk = 1000
|
||||
position := 0
|
||||
|
||||
for position < len(selectedParentChainChanges.Added) {
|
||||
var chainBlocksChunk []*externalapi.DomainHash
|
||||
if position+chunk > len(selectedParentChainChanges.Added) {
|
||||
chainBlocksChunk = selectedParentChainChanges.Added[position:]
|
||||
} else {
|
||||
chainBlocksChunk = selectedParentChainChanges.Added[position : position+chunk]
|
||||
}
|
||||
// We use chunks in order to avoid blocking consensus for too long
|
||||
chainBlocksAcceptanceData, err := ctx.Domain.Consensus().GetBlocksAcceptanceData(chainBlocksChunk)
|
||||
for i, addedChainBlock := range selectedParentChainChanges.Added {
|
||||
blockAcceptanceData, err := ctx.Domain.Consensus().GetBlockAcceptanceData(addedChainBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, addedChainBlock := range chainBlocksChunk {
|
||||
chainBlockAcceptanceData := chainBlocksAcceptanceData[i]
|
||||
acceptedTransactionIDs[position+i] = &appmessage.AcceptedTransactionIDs{
|
||||
AcceptingBlockHash: addedChainBlock.String(),
|
||||
AcceptedTransactionIDs: nil,
|
||||
}
|
||||
for _, blockAcceptanceData := range chainBlockAcceptanceData {
|
||||
for _, transactionAcceptanceData := range blockAcceptanceData.TransactionAcceptanceData {
|
||||
if transactionAcceptanceData.IsAccepted {
|
||||
acceptedTransactionIDs[position+i].AcceptedTransactionIDs =
|
||||
append(acceptedTransactionIDs[position+i].AcceptedTransactionIDs,
|
||||
consensushashing.TransactionID(transactionAcceptanceData.Transaction).String())
|
||||
}
|
||||
acceptedTransactionIDs[i] = &appmessage.AcceptedTransactionIDs{
|
||||
AcceptingBlockHash: addedChainBlock.String(),
|
||||
AcceptedTransactionIDs: nil,
|
||||
}
|
||||
for _, blockAcceptanceData := range blockAcceptanceData {
|
||||
for _, transactionAcceptanceData := range blockAcceptanceData.TransactionAcceptanceData {
|
||||
if transactionAcceptanceData.IsAccepted {
|
||||
acceptedTransactionIDs[i].AcceptedTransactionIDs =
|
||||
append(acceptedTransactionIDs[i].AcceptedTransactionIDs,
|
||||
consensushashing.TransactionID(transactionAcceptanceData.Transaction).String())
|
||||
}
|
||||
}
|
||||
}
|
||||
position += chunk
|
||||
}
|
||||
|
||||
return acceptedTransactionIDs, nil
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
@@ -142,29 +141,16 @@ func (nm *NotificationManager) NotifyVirtualSelectedParentChainChanged(
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasListenersThatPropagateVirtualSelectedParentChainChanged returns whether there's any listener that is
|
||||
// subscribed to VirtualSelectedParentChainChanged notifications as well as checks if any such listener requested
|
||||
// to include AcceptedTransactionIDs.
|
||||
func (nm *NotificationManager) HasListenersThatPropagateVirtualSelectedParentChainChanged() (hasListeners, hasListenersThatRequireAcceptedTransactionIDs bool) {
|
||||
|
||||
nm.RLock()
|
||||
defer nm.RUnlock()
|
||||
|
||||
hasListeners = false
|
||||
hasListenersThatRequireAcceptedTransactionIDs = false
|
||||
|
||||
// AllListenersThatPropagateVirtualSelectedParentChainChanged returns true if there's any listener that is
|
||||
// subscribed to VirtualSelectedParentChainChanged notifications.
|
||||
func (nm *NotificationManager) AllListenersThatPropagateVirtualSelectedParentChainChanged() []*NotificationListener {
|
||||
var listenersThatPropagate []*NotificationListener
|
||||
for _, listener := range nm.listeners {
|
||||
if listener.propagateVirtualSelectedParentChainChangedNotifications {
|
||||
hasListeners = true
|
||||
// Generating acceptedTransactionIDs is a heavy operation, so we check if it's needed by any listener.
|
||||
if listener.includeAcceptedTransactionIDsInVirtualSelectedParentChainChangedNotifications {
|
||||
hasListenersThatRequireAcceptedTransactionIDs = true
|
||||
break
|
||||
}
|
||||
listenersThatPropagate = append(listenersThatPropagate, listener)
|
||||
}
|
||||
}
|
||||
|
||||
return hasListeners, hasListenersThatRequireAcceptedTransactionIDs
|
||||
return listenersThatPropagate
|
||||
}
|
||||
|
||||
// NotifyFinalityConflict notifies the notification manager that there's a finality conflict in the DAG
|
||||
@@ -351,11 +337,7 @@ func (nl *NotificationListener) PropagateFinalityConflictResolvedNotifications()
|
||||
// to the remote listener for the given addresses. Subsequent calls instruct the listener to
|
||||
// send UTXOs changed notifications for those addresses along with the old ones. Duplicate addresses
|
||||
// are ignored.
|
||||
func (nm *NotificationManager) PropagateUTXOsChangedNotifications(nl *NotificationListener, addresses []*UTXOsChangedNotificationAddress) {
|
||||
// Apply a write-lock since the internal listener address map is modified
|
||||
nm.Lock()
|
||||
defer nm.Unlock()
|
||||
|
||||
func (nl *NotificationListener) PropagateUTXOsChangedNotifications(addresses []*UTXOsChangedNotificationAddress) {
|
||||
if !nl.propagateUTXOsChangedNotifications {
|
||||
nl.propagateUTXOsChangedNotifications = true
|
||||
nl.propagateUTXOsChangedNotificationAddresses =
|
||||
@@ -370,11 +352,7 @@ func (nm *NotificationManager) PropagateUTXOsChangedNotifications(nl *Notificati
|
||||
// StopPropagatingUTXOsChangedNotifications instructs the listener to stop sending UTXOs
|
||||
// changed notifications to the remote listener for the given addresses. Addresses for which
|
||||
// notifications are not currently sent are ignored.
|
||||
func (nm *NotificationManager) StopPropagatingUTXOsChangedNotifications(nl *NotificationListener, addresses []*UTXOsChangedNotificationAddress) {
|
||||
// Apply a write-lock since the internal listener address map is modified
|
||||
nm.Lock()
|
||||
defer nm.Unlock()
|
||||
|
||||
func (nl *NotificationListener) StopPropagatingUTXOsChangedNotifications(addresses []*UTXOsChangedNotificationAddress) {
|
||||
if !nl.propagateUTXOsChangedNotifications {
|
||||
return
|
||||
}
|
||||
@@ -400,9 +378,9 @@ func (nl *NotificationListener) convertUTXOChangesToUTXOsChangedNotification(
|
||||
notification.Added = append(notification.Added, utxosByAddressesEntries...)
|
||||
}
|
||||
}
|
||||
for scriptPublicKeyString, removedPairs := range utxoChanges.Removed {
|
||||
for scriptPublicKeyString, removedOutpoints := range utxoChanges.Removed {
|
||||
if listenerAddress, ok := nl.propagateUTXOsChangedNotificationAddresses[scriptPublicKeyString]; ok {
|
||||
utxosByAddressesEntries := ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(listenerAddress.Address, removedPairs)
|
||||
utxosByAddressesEntries := convertUTXOOutpointsToUTXOsByAddressesEntries(listenerAddress.Address, removedOutpoints)
|
||||
notification.Removed = append(notification.Removed, utxosByAddressesEntries...)
|
||||
}
|
||||
}
|
||||
@@ -413,8 +391,8 @@ func (nl *NotificationListener) convertUTXOChangesToUTXOsChangedNotification(
|
||||
utxosByAddressesEntries := ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(listenerAddress.Address, addedPairs)
|
||||
notification.Added = append(notification.Added, utxosByAddressesEntries...)
|
||||
}
|
||||
if removedPairs, ok := utxoChanges.Removed[listenerScriptPublicKeyString]; ok {
|
||||
utxosByAddressesEntries := ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(listenerAddress.Address, removedPairs)
|
||||
if removedOutpoints, ok := utxoChanges.Removed[listenerScriptPublicKeyString]; ok {
|
||||
utxosByAddressesEntries := convertUTXOOutpointsToUTXOsByAddressesEntries(listenerAddress.Address, removedOutpoints)
|
||||
notification.Removed = append(notification.Removed, utxosByAddressesEntries...)
|
||||
}
|
||||
}
|
||||
@@ -428,13 +406,13 @@ func (nl *NotificationListener) convertUTXOChangesToUTXOsChangedNotification(
|
||||
utxosByAddressesEntries := ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(addressString, addedPairs)
|
||||
notification.Added = append(notification.Added, utxosByAddressesEntries...)
|
||||
}
|
||||
for scriptPublicKeyString, removedPAirs := range utxoChanges.Removed {
|
||||
for scriptPublicKeyString, removedOutpoints := range utxoChanges.Removed {
|
||||
addressString, err := nl.scriptPubKeyStringToAddressString(scriptPublicKeyString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
utxosByAddressesEntries := ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(addressString, removedPAirs)
|
||||
utxosByAddressesEntries := convertUTXOOutpointsToUTXOsByAddressesEntries(addressString, removedOutpoints)
|
||||
notification.Removed = append(notification.Removed, utxosByAddressesEntries...)
|
||||
}
|
||||
}
|
||||
@@ -443,7 +421,7 @@ func (nl *NotificationListener) convertUTXOChangesToUTXOsChangedNotification(
|
||||
}
|
||||
|
||||
func (nl *NotificationListener) scriptPubKeyStringToAddressString(scriptPublicKeyString utxoindex.ScriptPublicKeyString) (string, error) {
|
||||
scriptPubKey := externalapi.NewScriptPublicKeyFromString(string(scriptPublicKeyString))
|
||||
scriptPubKey := utxoindex.ConvertStringToScriptPublicKey(scriptPublicKeyString)
|
||||
|
||||
// ignore error because it is often returned when the script is of unknown type
|
||||
scriptType, address, err := txscript.ExtractScriptPubKeyAddress(scriptPubKey, nl.params)
|
||||
|
||||
@@ -32,6 +32,22 @@ func ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(address string, pair
|
||||
return utxosByAddressesEntries
|
||||
}
|
||||
|
||||
// convertUTXOOutpointsToUTXOsByAddressesEntries converts
|
||||
// UTXOOutpoints to a slice of UTXOsByAddressesEntry
|
||||
func convertUTXOOutpointsToUTXOsByAddressesEntries(address string, outpoints utxoindex.UTXOOutpoints) []*appmessage.UTXOsByAddressesEntry {
|
||||
utxosByAddressesEntries := make([]*appmessage.UTXOsByAddressesEntry, 0, len(outpoints))
|
||||
for outpoint := range outpoints {
|
||||
utxosByAddressesEntries = append(utxosByAddressesEntries, &appmessage.UTXOsByAddressesEntry{
|
||||
Address: address,
|
||||
Outpoint: &appmessage.RPCOutpoint{
|
||||
TransactionID: outpoint.TransactionID.String(),
|
||||
Index: outpoint.Index,
|
||||
},
|
||||
})
|
||||
}
|
||||
return utxosByAddressesEntries
|
||||
}
|
||||
|
||||
// ConvertAddressStringsToUTXOsChangedNotificationAddresses converts address strings
|
||||
// to UTXOsChangedNotificationAddresses
|
||||
func (ctx *Context) ConvertAddressStringsToUTXOsChangedNotificationAddresses(
|
||||
@@ -47,7 +63,7 @@ func (ctx *Context) ConvertAddressStringsToUTXOsChangedNotificationAddresses(
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Could not create a scriptPublicKey for address '%s': %s", addressString, err)
|
||||
}
|
||||
scriptPublicKeyString := utxoindex.ScriptPublicKeyString(scriptPublicKey.String())
|
||||
scriptPublicKeyString := utxoindex.ConvertScriptPublicKeyToString(scriptPublicKey)
|
||||
addresses[i] = &UTXOsChangedNotificationAddress{
|
||||
Address: addressString,
|
||||
ScriptPublicKeyString: scriptPublicKeyString,
|
||||
|
||||
@@ -122,7 +122,6 @@ func (ctx *Context) PopulateTransactionWithVerboseData(
|
||||
}
|
||||
|
||||
ctx.Domain.Consensus().PopulateMass(domainTransaction)
|
||||
|
||||
transaction.VerboseData = &appmessage.RPCTransactionVerboseData{
|
||||
TransactionID: consensushashing.TransactionID(domainTransaction).String(),
|
||||
Hash: consensushashing.TransactionHash(domainTransaction).String(),
|
||||
|
||||
@@ -9,14 +9,6 @@ import (
|
||||
|
||||
// HandleAddPeer handles the respectively named RPC command
|
||||
func HandleAddPeer(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
if context.Config.SafeRPC {
|
||||
log.Warn("AddPeer RPC command called while node in safe RPC mode -- ignoring.")
|
||||
response := appmessage.NewAddPeerResponseMessage()
|
||||
response.Error =
|
||||
appmessage.RPCErrorf("AddPeer RPC command called while node in safe RPC mode")
|
||||
return response, nil
|
||||
}
|
||||
|
||||
AddPeerRequest := request.(*appmessage.AddPeerRequestMessage)
|
||||
address, err := network.NormalizeAddress(AddPeerRequest.Address, context.Config.ActiveNetParams.DefaultPort)
|
||||
if err != nil {
|
||||
|
||||
@@ -9,14 +9,6 @@ import (
|
||||
|
||||
// HandleBan handles the respectively named RPC command
|
||||
func HandleBan(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
if context.Config.SafeRPC {
|
||||
log.Warn("Ban RPC command called while node in safe RPC mode -- ignoring.")
|
||||
response := appmessage.NewBanResponseMessage()
|
||||
response.Error =
|
||||
appmessage.RPCErrorf("Ban RPC command called while node in safe RPC mode")
|
||||
return response, nil
|
||||
}
|
||||
|
||||
banRequest := request.(*appmessage.BanRequestMessage)
|
||||
ip := net.ParseIP(banRequest.IP)
|
||||
if ip == nil {
|
||||
|
||||
@@ -27,27 +27,6 @@ func HandleEstimateNetworkHashesPerSecond(
|
||||
}
|
||||
}
|
||||
|
||||
if context.Config.SafeRPC {
|
||||
const windowSizeLimit = 10000
|
||||
if windowSize > windowSizeLimit {
|
||||
response := &appmessage.EstimateNetworkHashesPerSecondResponseMessage{}
|
||||
response.Error =
|
||||
appmessage.RPCErrorf(
|
||||
"Requested window size %d is larger than max allowed in RPC safe mode (%d)",
|
||||
windowSize, windowSizeLimit)
|
||||
return response, nil
|
||||
}
|
||||
}
|
||||
|
||||
if uint64(windowSize) > context.Config.ActiveNetParams.PruningDepth() {
|
||||
response := &appmessage.EstimateNetworkHashesPerSecondResponseMessage{}
|
||||
response.Error =
|
||||
appmessage.RPCErrorf(
|
||||
"Requested window size %d is larger than pruning point depth %d",
|
||||
windowSize, context.Config.ActiveNetParams.PruningDepth())
|
||||
return response, nil
|
||||
}
|
||||
|
||||
networkHashesPerSecond, err := context.Domain.Consensus().EstimateNetworkHashesPerSecond(startHash, windowSize)
|
||||
if err != nil {
|
||||
response := &appmessage.EstimateNetworkHashesPerSecondResponseMessage{}
|
||||
|
||||
@@ -37,7 +37,7 @@ func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appm
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !blockInfo.HasHeader() {
|
||||
if !blockInfo.Exists {
|
||||
return &appmessage.GetBlocksResponseMessage{
|
||||
Error: appmessage.RPCErrorf("Could not find lowHash %s", getBlocksRequest.LowHash),
|
||||
}, nil
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package rpchandlers
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleGetCoinSupply handles the respectively named RPC command
|
||||
func HandleGetCoinSupply(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
if !context.Config.UTXOIndex {
|
||||
errorMessage := &appmessage.GetCoinSupplyResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Method unavailable when kaspad is run without --utxoindex")
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
circulatingSompiSupply, err := context.UTXOIndex.GetCirculatingSompiSupply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := appmessage.NewGetCoinSupplyResponseMessage(
|
||||
constants.MaxSompi,
|
||||
circulatingSompiSupply,
|
||||
)
|
||||
|
||||
return response, nil
|
||||
}
|
||||
@@ -9,17 +9,10 @@ import (
|
||||
|
||||
// HandleGetInfo handles the respectively named RPC command
|
||||
func HandleGetInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
isNearlySynced, err := context.Domain.Consensus().IsNearlySynced()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := appmessage.NewGetInfoResponseMessage(
|
||||
context.NetAdapter.ID().String(),
|
||||
uint64(context.Domain.MiningManager().TransactionCount(true, false)),
|
||||
uint64(context.Domain.MiningManager().TransactionCount()),
|
||||
version.Version(),
|
||||
context.Config.UTXOIndex,
|
||||
context.ProtocolManager.Context().HasPeers() && isNearlySynced,
|
||||
)
|
||||
|
||||
return response, nil
|
||||
|
||||
@@ -7,40 +7,19 @@ import (
|
||||
)
|
||||
|
||||
// HandleGetMempoolEntries handles the respectively named RPC command
|
||||
func HandleGetMempoolEntries(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
getMempoolEntriesRequest := request.(*appmessage.GetMempoolEntriesRequestMessage)
|
||||
|
||||
entries := make([]*appmessage.MempoolEntry, 0)
|
||||
|
||||
transactionPoolTransactions, orphanPoolTransactions := context.Domain.MiningManager().AllTransactions(!getMempoolEntriesRequest.FilterTransactionPool, getMempoolEntriesRequest.IncludeOrphanPool)
|
||||
|
||||
if !getMempoolEntriesRequest.FilterTransactionPool {
|
||||
for _, transaction := range transactionPoolTransactions {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
if getMempoolEntriesRequest.IncludeOrphanPool {
|
||||
for _, transaction := range orphanPoolTransactions {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: true,
|
||||
})
|
||||
func HandleGetMempoolEntries(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
transactions := context.Domain.MiningManager().AllTransactions()
|
||||
entries := make([]*appmessage.MempoolEntry, 0, len(transactions))
|
||||
for _, transaction := range transactions {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
})
|
||||
}
|
||||
|
||||
return appmessage.NewGetMempoolEntriesResponseMessage(entries), nil
|
||||
|
||||
@@ -3,8 +3,8 @@ package rpchandlers
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
)
|
||||
@@ -12,20 +12,15 @@ import (
|
||||
// HandleGetMempoolEntriesByAddresses handles the respectively named RPC command
|
||||
func HandleGetMempoolEntriesByAddresses(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
|
||||
transactions := context.Domain.MiningManager().AllTransactions()
|
||||
getMempoolEntriesByAddressesRequest := request.(*appmessage.GetMempoolEntriesByAddressesRequestMessage)
|
||||
|
||||
mempoolEntriesByAddresses := make([]*appmessage.MempoolEntryByAddress, 0)
|
||||
|
||||
sendingInTransactionPool, receivingInTransactionPool, sendingInOrphanPool, receivingInOrphanPool, err := context.Domain.MiningManager().GetTransactionsByAddresses(!getMempoolEntriesByAddressesRequest.FilterTransactionPool, getMempoolEntriesByAddressesRequest.IncludeOrphanPool)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, addressString := range getMempoolEntriesByAddressesRequest.Addresses {
|
||||
|
||||
address, err := util.DecodeAddress(addressString, context.Config.NetParams().Prefix)
|
||||
_, err := util.DecodeAddress(addressString, context.Config.ActiveNetParams.Prefix)
|
||||
if err != nil {
|
||||
errorMessage := &appmessage.GetMempoolEntriesByAddressesResponseMessage{}
|
||||
errorMessage := &appmessage.GetUTXOsByAddressesResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Could not decode address '%s': %s", addressString, err)
|
||||
return errorMessage, nil
|
||||
}
|
||||
@@ -33,88 +28,68 @@ func HandleGetMempoolEntriesByAddresses(context *rpccontext.Context, _ *router.R
|
||||
sending := make([]*appmessage.MempoolEntry, 0)
|
||||
receiving := make([]*appmessage.MempoolEntry, 0)
|
||||
|
||||
scriptPublicKey, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
errorMessage := &appmessage.GetMempoolEntriesByAddressesResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Could not extract scriptPublicKey from address '%s': %s", addressString, err)
|
||||
return errorMessage, nil
|
||||
}
|
||||
for _, transaction := range transactions {
|
||||
|
||||
if !getMempoolEntriesByAddressesRequest.FilterTransactionPool {
|
||||
for i, input := range transaction.Inputs {
|
||||
// TODO: Fix this
|
||||
if input.UTXOEntry == nil {
|
||||
log.Errorf("Couldn't find UTXO entry for input %d in mempool transaction %s. This is a bug and should be fixed.", i, consensushashing.TransactionID(transaction))
|
||||
continue
|
||||
}
|
||||
|
||||
if transaction, found := sendingInTransactionPool[scriptPublicKey.String()]; found {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
_, transactionSendingAddress, err := txscript.ExtractScriptPubKeyAddress(
|
||||
input.UTXOEntry.ScriptPublicKey(),
|
||||
context.Config.ActiveNetParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sending = append(sending, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: false,
|
||||
},
|
||||
)
|
||||
if addressString == transactionSendingAddress.String() {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
sending = append(
|
||||
sending,
|
||||
&appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
},
|
||||
)
|
||||
break //one input is enough
|
||||
}
|
||||
}
|
||||
|
||||
if transaction, found := receivingInTransactionPool[scriptPublicKey.String()]; found {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
for _, output := range transaction.Outputs {
|
||||
_, transactionReceivingAddress, err := txscript.ExtractScriptPubKeyAddress(
|
||||
output.ScriptPublicKey,
|
||||
context.Config.ActiveNetParams,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receiving = append(receiving, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: false,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
if getMempoolEntriesByAddressesRequest.IncludeOrphanPool {
|
||||
|
||||
if transaction, found := sendingInOrphanPool[scriptPublicKey.String()]; found {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if addressString == transactionReceivingAddress.String() {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
receiving = append(
|
||||
receiving,
|
||||
&appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
},
|
||||
)
|
||||
break //one output is enough
|
||||
}
|
||||
}
|
||||
|
||||
sending = append(sending, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: true,
|
||||
},
|
||||
//Only append mempoolEntriesByAddress, if at least 1 mempoolEntry for the address is found.
|
||||
//This mimics the behaviour of GetUtxosByAddresses RPC call.
|
||||
if len(sending) > 0 || len(receiving) > 0 {
|
||||
mempoolEntriesByAddresses = append(
|
||||
mempoolEntriesByAddresses,
|
||||
&appmessage.MempoolEntryByAddress{
|
||||
Address: addressString,
|
||||
Sending: sending,
|
||||
Receiving: receiving,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if transaction, found := receivingInOrphanPool[scriptPublicKey.String()]; found {
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
receiving = append(receiving, &appmessage.MempoolEntry{
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
IsOrphan: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(sending) > 0 || len(receiving) > 0 {
|
||||
mempoolEntriesByAddresses = append(
|
||||
mempoolEntriesByAddresses,
|
||||
&appmessage.MempoolEntryByAddress{
|
||||
Address: address.String(),
|
||||
Sending: sending,
|
||||
Receiving: receiving,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,18 +3,12 @@ package rpchandlers
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleGetMempoolEntry handles the respectively named RPC command
|
||||
func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
|
||||
transaction := &externalapi.DomainTransaction{}
|
||||
var found bool
|
||||
var isOrphan bool
|
||||
|
||||
getMempoolEntryRequest := request.(*appmessage.GetMempoolEntryRequestMessage)
|
||||
|
||||
transactionID, err := transactionid.FromString(getMempoolEntryRequest.TxID)
|
||||
@@ -24,18 +18,17 @@ func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, reques
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
mempoolTransaction, isOrphan, found := context.Domain.MiningManager().GetTransaction(transactionID, !getMempoolEntryRequest.FilterTransactionPool, getMempoolEntryRequest.IncludeOrphanPool)
|
||||
|
||||
if !found {
|
||||
transaction, ok := context.Domain.MiningManager().GetTransaction(transactionID)
|
||||
if !ok {
|
||||
errorMessage := &appmessage.GetMempoolEntryResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Transaction %s was not found", transactionID)
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(mempoolTransaction)
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err = context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return appmessage.NewGetMempoolEntryResponseMessage(transaction.Fee, rpcTransaction, isOrphan), nil
|
||||
|
||||
return appmessage.NewGetMempoolEntryResponseMessage(transaction.Fee, rpcTransaction), nil
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func HandleNotifyUTXOsChanged(context *rpccontext.Context, router *router.Router
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
context.NotificationManager.PropagateUTXOsChangedNotifications(listener, addresses)
|
||||
listener.PropagateUTXOsChangedNotifications(addresses)
|
||||
|
||||
response := appmessage.NewNotifyUTXOsChangedResponseMessage()
|
||||
return response, nil
|
||||
|
||||
@@ -8,14 +8,6 @@ import (
|
||||
|
||||
// HandleResolveFinalityConflict handles the respectively named RPC command
|
||||
func HandleResolveFinalityConflict(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
if context.Config.SafeRPC {
|
||||
log.Warn("ResolveFinalityConflict RPC command called while node in safe RPC mode -- ignoring.")
|
||||
response := &appmessage.ResolveFinalityConflictResponseMessage{}
|
||||
response.Error =
|
||||
appmessage.RPCErrorf("ResolveFinalityConflict RPC command called while node in safe RPC mode")
|
||||
return response, nil
|
||||
}
|
||||
|
||||
response := &appmessage.ResolveFinalityConflictResponseMessage{}
|
||||
response.Error = appmessage.RPCErrorf("not implemented")
|
||||
return response, nil
|
||||
|
||||
@@ -12,14 +12,6 @@ const pauseBeforeShutDown = time.Second
|
||||
|
||||
// HandleShutDown handles the respectively named RPC command
|
||||
func HandleShutDown(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
if context.Config.SafeRPC {
|
||||
log.Warn("ShutDown RPC command called while node in safe RPC mode -- ignoring.")
|
||||
response := appmessage.NewShutDownResponseMessage()
|
||||
response.Error =
|
||||
appmessage.RPCErrorf("ShutDown RPC command called while node in safe RPC mode")
|
||||
return response, nil
|
||||
}
|
||||
|
||||
log.Warn("ShutDown RPC called.")
|
||||
|
||||
// Wait a second before shutting down, to allow time to return the response to the caller
|
||||
|
||||
@@ -26,7 +26,7 @@ func HandleStopNotifyingUTXOsChanged(context *rpccontext.Context, router *router
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
context.NotificationManager.StopPropagatingUTXOsChangedNotifications(listener, addresses)
|
||||
listener.StopPropagatingUTXOsChangedNotifications(addresses)
|
||||
|
||||
response := appmessage.NewStopNotifyingUTXOsChangedResponseMessage()
|
||||
return response, nil
|
||||
|
||||
@@ -9,14 +9,6 @@ import (
|
||||
|
||||
// HandleUnban handles the respectively named RPC command
|
||||
func HandleUnban(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
if context.Config.SafeRPC {
|
||||
log.Warn("Unban RPC command called while node in safe RPC mode -- ignoring.")
|
||||
response := appmessage.NewUnbanResponseMessage()
|
||||
response.Error =
|
||||
appmessage.RPCErrorf("Unban RPC command called while node in safe RPC mode")
|
||||
return response, nil
|
||||
}
|
||||
|
||||
unbanRequest := request.(*appmessage.UnbanRequestMessage)
|
||||
ip := net.ParseIP(unbanRequest.IP)
|
||||
if ip == nil {
|
||||
|
||||
109
changelog.txt
109
changelog.txt
@@ -1,112 +1,3 @@
|
||||
Kaspad v0.12.9 - 2022-10-23
|
||||
===========================
|
||||
|
||||
* Create directory before locking lock file (#2160)
|
||||
|
||||
Kaspad v0.12.8 - 2022-10-23
|
||||
===========================
|
||||
|
||||
* Remove hard fork activation rules (#2152)
|
||||
* Add lock file to kaspawallet (#2154)
|
||||
* Add a new testnet DNS seeder (#2156)
|
||||
* Use utxo diff algo for pruning point move and use acceptance data method only as a fall-back (#2157)
|
||||
* Make more checks if status is invalid even if the block exists (#2158)
|
||||
|
||||
|
||||
Kaspad v0.12.7 - 2022-09-21
|
||||
===========================
|
||||
|
||||
* Security Fix + Hard fork - Full details can be seen here: https://medium.com/@michaelsuttonil/kaspa-security-patch-and-hard-fork-september-2022-12da617b0094
|
||||
|
||||
Kaspad v0.12.6 - 2022-09-09
|
||||
===========================
|
||||
|
||||
* Remove tests from docker files (#2133)
|
||||
|
||||
Wallet new features:
|
||||
* Optionally show serialized transactions on send (#2135)
|
||||
|
||||
Bug fixes:
|
||||
* Update virtual on IBD if nearly synced (#2134)
|
||||
|
||||
Kaspad v0.12.5 - 2022-08-28
|
||||
===========================
|
||||
|
||||
* Add tests for hash writers (#2120)
|
||||
* Replace daglabs's dnsseeder with Wolfie's (#2119)
|
||||
* Change testnet dnsseeder (#2126)
|
||||
* Add RPC timeout parameter to wallet daemon (#2104)
|
||||
|
||||
Wallet new features:
|
||||
* Add UseExistingChangeAddress option to the wallet (#2127)
|
||||
|
||||
Bug fixes:
|
||||
* Call update pruning point if required on resolve virtual or startup (#2129)
|
||||
* Add missing locks to notification listener modifications (#2124)
|
||||
* Calculate pruning point utxo set from acceptance data (#2123)
|
||||
* Fix RPC client memory/goroutine leak (#2122)
|
||||
* Fix a subtle lock sync issue in consensus insert block (#2121)
|
||||
* Mempool: Retrieve stable state of the mempool. Optimze get mempool entries by addresses (#2111)
|
||||
* Kaspawallet.send(): Make separate context for Broadcast, to prolong timeout (#2131)
|
||||
|
||||
|
||||
|
||||
Kaspad v0.12.4 - 2022-07-17
|
||||
===========================
|
||||
|
||||
* Crucial fix for the UTXO difference mechanism (#2114)
|
||||
* Implement multi-layer auto-compound (#2115)
|
||||
|
||||
Kaspad v0.12.3 - 2022-06-29
|
||||
===========================
|
||||
|
||||
* Fixes a few bugs which can lead to node crashes or out-of-memory errors
|
||||
|
||||
Kaspad v0.12.2 - 2022-06-17
|
||||
===========================
|
||||
|
||||
* Clarify wallet message concerning a wallet daemon sync state (#2045)
|
||||
* Change the way the miner executable reports execution errors (closes issue #1677) (#2048)
|
||||
* Fix kaspawallet help messages, clarify sweep command help string (#2067)
|
||||
* Wallet parse/send/create commands improvement (#2024)
|
||||
* Use chunks for `GetBlocksAcceptanceData` calls in order to avoid blocking consensus for too long (#2075)
|
||||
* Unite multiple `GetBlockAcceptanceData` consensus calls to one (#2074)
|
||||
* Update many-small-chains-and-one-big-chain DAG to not fail merge depth limit (#2072)
|
||||
|
||||
RPC API Changes:
|
||||
* RPC: include orphans into mempool entries (#2046)
|
||||
* RPC & UtxoIndex: keep track of, query and test circulating supply. (#2070)
|
||||
|
||||
Bug Fixes:
|
||||
* Fix RPC connections counting (#2026)
|
||||
* Fix UTXO diff child error (#2084)
|
||||
* Fix `not in selected chain` crash (#2082)
|
||||
|
||||
Kaspad v0.12.1 - 2022-05-31
|
||||
===========================
|
||||
|
||||
* Fix utxoindex synchronization bug which resulted in kaspawallet orphan tx errors (#2052, #2056, #2059)
|
||||
* Add a channel mechanism for consensus events to be processed in the order they were produced (#2052, #2056, #2059)
|
||||
* Block template cache improvement (#2023)
|
||||
* Improved staging shard performance (#2034)
|
||||
* Add finality check to ResolveVirtual (#2041)
|
||||
* Update Dockerfile for go 1.18 (#2038)
|
||||
* Remove HF1 activation code (#2042)
|
||||
|
||||
Kaspa wallet:
|
||||
* Various kaspawallet text fixes and log additions (#2032, #2047, #2062)
|
||||
* Wallet address synchronization improvement (#2025)
|
||||
* Add support for `from` address in `kaspawallet send` (#1964)
|
||||
* Make kaspawallet ignore outputs that exist in the mempool (#2053)
|
||||
* Wrap the entire wallet send operation with a lock (#2063)
|
||||
|
||||
RPC API:
|
||||
* Add "GetMempoolEntriesByAddresses" to kaspad RPC (#2022)
|
||||
* Make sure RPCErrors are returned and do not crash the system (#2039)
|
||||
* Add AcceptedTransactionIDs to ChainChanged notification and VirtualSelectedParentChain RPC (#2036, for exchanges to track tx confirmations)
|
||||
* Allow blank address in NotifyUTXOsChanged to get all updates (#2027)
|
||||
* Include isSynced and isUtxoIndexed in GetInfoResponse (#2068)
|
||||
|
||||
Kaspad v0.12.0 - 2022-04-14
|
||||
===========================
|
||||
Breaking changes:
|
||||
|
||||
@@ -37,7 +37,6 @@ var commandTypes = []reflect.Type{
|
||||
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetUtxosByAddressesRequest{}),
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetBalanceByAddressRequest{}),
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetCoinSupplyRequest{}),
|
||||
|
||||
reflect.TypeOf(protowire.KaspadMessage_BanRequest{}),
|
||||
reflect.TypeOf(protowire.KaspadMessage_UnbanRequest{}),
|
||||
|
||||
@@ -6,6 +6,8 @@ RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
WORKDIR /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
RUN apk add --no-cache curl git openssh binutils gcc musl-dev
|
||||
RUN go get -u golang.org/x/lint/golint \
|
||||
honnef.co/go/tools/cmd/staticcheck
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
@@ -16,6 +18,10 @@ COPY . .
|
||||
|
||||
WORKDIR /go/src/github.com/kaspanet/kaspad/cmd/kaspactl
|
||||
|
||||
RUN GOFMT_RESULT=`go fmt ./...`; echo $GOFMT_RESULT; test -z "$GOFMT_RESULT"
|
||||
RUN go vet ./...
|
||||
RUN golint -set_exit_status ./...
|
||||
RUN staticcheck -checks SA4006 ./...
|
||||
RUN GOOS=linux go build -a -installsuffix cgo -o kaspactl .
|
||||
|
||||
# --- multistage docker build: stage #2: runtime image
|
||||
|
||||
@@ -6,6 +6,8 @@ RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
WORKDIR /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
RUN apk add --no-cache curl git openssh binutils gcc musl-dev
|
||||
RUN go get -u golang.org/x/lint/golint \
|
||||
honnef.co/go/tools/cmd/staticcheck
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
@@ -15,6 +17,11 @@ RUN go mod download
|
||||
COPY . .
|
||||
|
||||
WORKDIR /go/src/github.com/kaspanet/kaspad/cmd/kaspaminer
|
||||
|
||||
RUN GOFMT_RESULT=`go fmt ./...`; echo $GOFMT_RESULT; test -z "$GOFMT_RESULT"
|
||||
RUN go vet ./...
|
||||
RUN golint -set_exit_status ./...
|
||||
RUN staticcheck -checks SA4006 ./...
|
||||
RUN GOOS=linux go build -a -installsuffix cgo -o kaspaminer .
|
||||
|
||||
# --- multistage docker build: stage #2: runtime image
|
||||
|
||||
@@ -23,7 +23,8 @@ func main() {
|
||||
|
||||
cfg, err := parseConfig()
|
||||
if err != nil {
|
||||
printErrorAndExit(errors.Errorf("Error parsing command-line arguments: %s", err))
|
||||
fmt.Fprintf(os.Stderr, "Error parsing command-line arguments: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer backendLog.Close()
|
||||
|
||||
@@ -43,7 +44,7 @@ func main() {
|
||||
|
||||
miningAddr, err := util.DecodeAddress(cfg.MiningAddr, cfg.ActiveNetParams.Prefix)
|
||||
if err != nil {
|
||||
printErrorAndExit(errors.Errorf("Error decoding mining address: %s", err))
|
||||
panic(errors.Wrap(err, "error decoding mining address"))
|
||||
}
|
||||
|
||||
doneChan := make(chan struct{})
|
||||
@@ -60,8 +61,3 @@ func main() {
|
||||
case <-interrupt:
|
||||
}
|
||||
}
|
||||
|
||||
func printErrorAndExit(err error) {
|
||||
fmt.Fprintf(os.Stderr, "%+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -46,35 +46,32 @@ type createConfig struct {
|
||||
}
|
||||
|
||||
type balanceConfig struct {
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
Verbose bool `long:"verbose" short:"v" description:"Verbose: show addresses with balance"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type sendConfig 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"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
FromAddresses []string `long:"from-address" short:"a" description:"Specific public address to send Kaspa from. Use multiple times to accept several addresses" required:"false"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
UseExistingChangeAddress bool `long:"use-existing-change-address" short:"u" description:"Will use an existing change address (in case no change address was ever used, it will use a new one)"`
|
||||
Verbose bool `long:"show-serialized" short:"s" description:"Show a list of hex encoded sent transactions"`
|
||||
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"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
FromAddresses []string `long:"from-address" short:"a" description:"Specific public address to send Kaspa from. Use multiple times to accept several addresses" required:"false"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type sweepConfig struct {
|
||||
PrivateKey string `long:"private-key" short:"k" description:"Private key in hex format"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type createUnsignedTransactionConfig struct {
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
FromAddresses []string `long:"from-address" short:"a" description:"Specific public address to send Kaspa from. Use multiple times to accept several addresses" required:"false"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
UseExistingChangeAddress bool `long:"use-existing-change-address" short:"u" description:"Will use an existing change address (in case no change address was ever used, it will use a new one)"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
FromAddresses []string `long:"from-address" short:"a" description:"Specific public address to send Kaspa from. Use multiple times to accept several addresses" required:"false"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
@@ -87,7 +84,7 @@ type signConfig struct {
|
||||
}
|
||||
|
||||
type broadcastConfig struct {
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
Transactions string `long:"transaction" short:"t" description:"The signed transaction to broadcast (encoded in hex)"`
|
||||
TransactionsFile string `long:"transaction-file" short:"F" description:"The file containing the unsigned transaction to sign on (encoded in hex)"`
|
||||
config.NetworkFlags
|
||||
@@ -101,12 +98,12 @@ type parseConfig struct {
|
||||
}
|
||||
|
||||
type showAddressesConfig struct {
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type newAddressConfig struct {
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to"`
|
||||
DaemonAddress string `long:"daemonaddress" short:"d" description:"Wallet daemon server to connect to (default: localhost:8082)"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
@@ -114,8 +111,7 @@ type startDaemonConfig 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"`
|
||||
RPCServer string `long:"rpcserver" short:"s" description:"RPC server to connect to"`
|
||||
Listen string `long:"listen" short:"l" description:"Address to listen on (default: 0.0.0.0:8082)"`
|
||||
Timeout uint32 `long:"wait-timeout" short:"w" description:"Waiting timeout for RPC calls, seconds (default: 30 s)"`
|
||||
Listen string `short:"l" long:"listen" description:"Address to listen on (default: 0.0.0.0:8082)"`
|
||||
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
@@ -144,10 +140,8 @@ func parseCommandLine() (subCommand string, config interface{}) {
|
||||
"Sends a Kaspa transaction to a public address", sendConf)
|
||||
|
||||
sweepConf := &sweepConfig{DaemonAddress: defaultListen}
|
||||
parser.AddCommand(sweepSubCmd, "Sends all funds associated with the given schnorr private key to a new address of the current wallet",
|
||||
"Sends all funds associated with the given schnorr private key to a newly created external (i.e. not a change) address of the "+
|
||||
"keyfile that is under the daemon's contol. Can be used with a private key generated with the genkeypair utilily "+
|
||||
"to send funds to your main wallet.", sweepConf)
|
||||
parser.AddCommand(sweepSubCmd, "Sends all funds associated with the given private key, to a new address of the wallet",
|
||||
"Sends all funds associated with the private key, to a given change address of the wallet", sweepConf)
|
||||
|
||||
createUnsignedTransactionConf := &createUnsignedTransactionConfig{DaemonAddress: defaultListen}
|
||||
parser.AddCommand(createUnsignedTransactionSubCmd, "Create an unsigned Kaspa transaction",
|
||||
@@ -185,6 +179,7 @@ func parseCommandLine() (subCommand string, config interface{}) {
|
||||
parser.AddCommand(startDaemonSubCmd, "Start the wallet daemon", "Start the wallet daemon", startDaemonConf)
|
||||
|
||||
_, err := parser.Parse()
|
||||
|
||||
if err != nil {
|
||||
var flagsErr *flags.Error
|
||||
if ok := errors.As(err, &flagsErr); ok && flagsErr.Type == flags.ErrHelp {
|
||||
|
||||
@@ -3,12 +3,11 @@ package main
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/bip32"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/utils"
|
||||
"github.com/pkg/errors"
|
||||
"os"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
|
||||
)
|
||||
@@ -31,10 +30,6 @@ func create(conf *createConfig) error {
|
||||
fmt.Printf("Extended public key of mnemonic #%d:\n%s\n\n", i+1, extendedPublicKey)
|
||||
}
|
||||
|
||||
fmt.Printf("Notice the above is neither a secret key to your wallet " +
|
||||
"(use \"kaspawallet dump-unencrypted-data\" to see a secret seed phrase) " +
|
||||
"nor a wallet public address (use \"kaspawallet new-address\" to create and see one)\n\n")
|
||||
|
||||
extendedPublicKeys := make([]string, conf.NumPrivateKeys, conf.NumPublicKeys)
|
||||
copy(extendedPublicKeys, signerExtendedPublicKeys)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
@@ -78,11 +73,6 @@ func create(conf *createConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = file.TryLock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = file.Save()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/client"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
|
||||
@@ -22,17 +21,15 @@ func createUnsignedTransaction(conf *createUnsignedTransactionConfig) error {
|
||||
|
||||
sendAmountSompi := uint64(conf.SendAmount * constants.SompiPerKaspa)
|
||||
response, err := daemonClient.CreateUnsignedTransactions(ctx, &pb.CreateUnsignedTransactionsRequest{
|
||||
From: conf.FromAddresses,
|
||||
Address: conf.ToAddress,
|
||||
Amount: sendAmountSompi,
|
||||
UseExistingChangeAddress: conf.UseExistingChangeAddress,
|
||||
From: conf.FromAddresses,
|
||||
Address: conf.ToAddress,
|
||||
Amount: sendAmountSompi,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "Created unsigned transaction")
|
||||
fmt.Println("Created unsigned transaction")
|
||||
fmt.Println(encodeTransactionsToHex(response.UnsignedTransactions))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/server"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -17,7 +16,7 @@ func Connect(address string) (pb.KaspawalletdClient, func(), error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
conn, err := grpc.DialContext(ctx, address, grpc.WithInsecure(), grpc.WithBlock(), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(server.MaxDaemonSendMsgSize)))
|
||||
conn, err := grpc.DialContext(ctx, address, grpc.WithInsecure(), grpc.WithBlock())
|
||||
if err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return nil, nil, errors.New("kaspawallet daemon is not running, start it with `kaspawallet start-daemon`")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.12.3
|
||||
// protoc-gen-go v1.28.0
|
||||
// protoc v3.17.2
|
||||
// source: kaspawalletd.proto
|
||||
|
||||
package pb
|
||||
@@ -189,10 +189,9 @@ type CreateUnsignedTransactionsRequest struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"`
|
||||
From []string `protobuf:"bytes,3,rep,name=from,proto3" json:"from,omitempty"`
|
||||
UseExistingChangeAddress bool `protobuf:"varint,4,opt,name=useExistingChangeAddress,proto3" json:"useExistingChangeAddress,omitempty"`
|
||||
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"`
|
||||
From []string `protobuf:"bytes,3,rep,name=from,proto3" json:"from,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CreateUnsignedTransactionsRequest) Reset() {
|
||||
@@ -248,13 +247,6 @@ func (x *CreateUnsignedTransactionsRequest) GetFrom() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CreateUnsignedTransactionsRequest) GetUseExistingChangeAddress() bool {
|
||||
if x != nil {
|
||||
return x.UseExistingChangeAddress
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type CreateUnsignedTransactionsResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -994,11 +986,10 @@ type SendRequest struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ToAddress string `protobuf:"bytes,1,opt,name=toAddress,proto3" json:"toAddress,omitempty"`
|
||||
Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"`
|
||||
Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
|
||||
From []string `protobuf:"bytes,4,rep,name=from,proto3" json:"from,omitempty"`
|
||||
UseExistingChangeAddress bool `protobuf:"varint,5,opt,name=useExistingChangeAddress,proto3" json:"useExistingChangeAddress,omitempty"`
|
||||
ToAddress string `protobuf:"bytes,1,opt,name=toAddress,proto3" json:"toAddress,omitempty"`
|
||||
Amount uint64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"`
|
||||
Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
|
||||
From []string `protobuf:"bytes,4,rep,name=from,proto3" json:"from,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SendRequest) Reset() {
|
||||
@@ -1061,20 +1052,12 @@ func (x *SendRequest) GetFrom() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SendRequest) GetUseExistingChangeAddress() bool {
|
||||
if x != nil {
|
||||
return x.UseExistingChangeAddress
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type SendResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
TxIDs []string `protobuf:"bytes,1,rep,name=txIDs,proto3" json:"txIDs,omitempty"`
|
||||
SignedTransactions [][]byte `protobuf:"bytes,2,rep,name=signedTransactions,proto3" json:"signedTransactions,omitempty"`
|
||||
TxIDs []string `protobuf:"bytes,1,rep,name=txIDs,proto3" json:"txIDs,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SendResponse) Reset() {
|
||||
@@ -1116,13 +1099,6 @@ func (x *SendResponse) GetTxIDs() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SendResponse) GetSignedTransactions() [][]byte {
|
||||
if x != nil {
|
||||
return x.SignedTransactions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Since SignRequest contains a password - this command should only be used on a trusted or secure connection
|
||||
type SignRequest struct {
|
||||
state protoimpl.MessageState
|
||||
@@ -1248,165 +1224,155 @@ var file_kaspawalletd_proto_rawDesc = []byte{
|
||||
0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
|
||||
0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65,
|
||||
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x70, 0x65, 0x6e,
|
||||
0x64, 0x69, 0x6e, 0x67, 0x22, 0xa5, 0x01, 0x0a, 0x21, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55,
|
||||
0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x66, 0x72, 0x6f, 0x6d, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d,
|
||||
0x12, 0x3a, 0x0a, 0x18, 0x75, 0x73, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x43,
|
||||
0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x18, 0x75, 0x73, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x43,
|
||||
0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x58, 0x0a, 0x22,
|
||||
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x32, 0x0a, 0x14, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c,
|
||||
0x52, 0x14, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x31,
|
||||
0x0a, 0x15, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x22, 0x13, 0x0a, 0x11, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2e, 0x0a, 0x12, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x52, 0x0a, 0x10, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63,
|
||||
0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x73,
|
||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73,
|
||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x74, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x29, 0x0a, 0x11, 0x42, 0x72,
|
||||
0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05,
|
||||
0x74, 0x78, 0x49, 0x44, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77,
|
||||
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x53, 0x68, 0x75, 0x74,
|
||||
0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x46, 0x0a, 0x08,
|
||||
0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69,
|
||||
0x6e, 0x64, 0x65, 0x78, 0x22, 0x9c, 0x01, 0x0a, 0x15, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x42, 0x79,
|
||||
0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x18,
|
||||
0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70,
|
||||
0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6b, 0x61, 0x73,
|
||||
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69,
|
||||
0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x09,
|
||||
0x75, 0x74, 0x78, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x17, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x55,
|
||||
0x74, 0x78, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x75, 0x74, 0x78, 0x6f, 0x45, 0x6e,
|
||||
0x74, 0x72, 0x79, 0x22, 0x55, 0x0a, 0x0f, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62,
|
||||
0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x12, 0x28, 0x0a, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||
0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70,
|
||||
0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x22, 0xb2, 0x01, 0x0a, 0x09, 0x55,
|
||||
0x74, 0x78, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x12, 0x47, 0x0a, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||
0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50,
|
||||
0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
|
||||
0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0d, 0x62, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x44, 0x61, 0x61, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04,
|
||||
0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x61, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12,
|
||||
0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x22,
|
||||
0x3c, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70,
|
||||
0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x62, 0x0a,
|
||||
0x21, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e,
|
||||
0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x3d, 0x0a, 0x07, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65,
|
||||
0x74, 0x64, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x42, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65,
|
||||
0x73, 0x22, 0xaf, 0x01, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
|
||||
0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77,
|
||||
0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77,
|
||||
0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x04, 0x20, 0x03, 0x28,
|
||||
0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x3a, 0x0a, 0x18, 0x75, 0x73, 0x65, 0x45, 0x78,
|
||||
0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x75, 0x73, 0x65, 0x45, 0x78,
|
||||
0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x22, 0x54, 0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||
0x28, 0x09, 0x52, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x73, 0x69, 0x67,
|
||||
0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
|
||||
0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x5d, 0x0a, 0x0b, 0x53, 0x69, 0x67,
|
||||
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x14, 0x75, 0x6e, 0x73, 0x69,
|
||||
0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x14, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64,
|
||||
0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x3e, 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x73, 0x69, 0x67, 0x6e,
|
||||
0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01,
|
||||
0x20, 0x03, 0x28, 0x0c, 0x52, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x32, 0xb3, 0x06, 0x0a, 0x0c, 0x6b, 0x61, 0x73,
|
||||
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x12, 0x51, 0x0a, 0x0a, 0x47, 0x65, 0x74,
|
||||
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77,
|
||||
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
|
||||
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61,
|
||||
0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e,
|
||||
0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x19,
|
||||
0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x12, 0x2e, 0x2e, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58,
|
||||
0x4f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58,
|
||||
0x4f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x81, 0x01, 0x0a,
|
||||
0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2f, 0x2e, 0x6b, 0x61,
|
||||
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
|
||||
0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x6b,
|
||||
0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x43, 0x72, 0x65, 0x61,
|
||||
0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
|
||||
0x12, 0x5a, 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65,
|
||||
0x73, 0x12, 0x22, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64,
|
||||
0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c,
|
||||
0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0a,
|
||||
0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x2e, 0x6b, 0x61, 0x73,
|
||||
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6b, 0x61,
|
||||
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
|
||||
0x4b, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x1d, 0x2e, 0x6b, 0x61,
|
||||
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64,
|
||||
0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6b, 0x61, 0x73,
|
||||
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f,
|
||||
0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x09,
|
||||
0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x1e, 0x2e, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61,
|
||||
0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61,
|
||||
0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x04,
|
||||
0x53, 0x65, 0x6e, 0x64, 0x12, 0x19, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c,
|
||||
0x65, 0x74, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53,
|
||||
0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a,
|
||||
0x04, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x19, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c,
|
||||
0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e,
|
||||
0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x36,
|
||||
0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x73,
|
||||
0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2f, 0x63, 0x6d, 0x64,
|
||||
0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2f, 0x64, 0x61, 0x65,
|
||||
0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x64, 0x69, 0x6e, 0x67, 0x22, 0x69, 0x0a, 0x21, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e,
|
||||
0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66,
|
||||
0x72, 0x6f, 0x6d, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x22,
|
||||
0x58, 0x0a, 0x22, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65,
|
||||
0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x14, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65,
|
||||
0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20,
|
||||
0x03, 0x28, 0x0c, 0x52, 0x14, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x53, 0x68, 0x6f,
|
||||
0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x22, 0x31, 0x0a, 0x15, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x22, 0x13, 0x0a, 0x11, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2e, 0x0a, 0x12, 0x4e, 0x65, 0x77,
|
||||
0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x52, 0x0a, 0x10, 0x42, 0x72, 0x6f,
|
||||
0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a,
|
||||
0x08, 0x69, 0x73, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x08, 0x69, 0x73, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x74, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52,
|
||||
0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x29, 0x0a,
|
||||
0x11, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
|
||||
0x09, 0x52, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x68, 0x75, 0x74,
|
||||
0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x53,
|
||||
0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x46, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x74,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49,
|
||||
0x64, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
|
||||
0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x9c, 0x01, 0x0a, 0x15, 0x55, 0x74, 0x78, 0x6f,
|
||||
0x73, 0x42, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72,
|
||||
0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x6f,
|
||||
0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,
|
||||
0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x4f, 0x75, 0x74,
|
||||
0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12,
|
||||
0x35, 0x0a, 0x09, 0x75, 0x74, 0x78, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74,
|
||||
0x64, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x75, 0x74, 0x78,
|
||||
0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x55, 0x0a, 0x0f, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74,
|
||||
0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62,
|
||||
0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x63,
|
||||
0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x22, 0xb2, 0x01,
|
||||
0x0a, 0x09, 0x55, 0x74, 0x78, 0x6f, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61,
|
||||
0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62,
|
||||
0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6b,
|
||||
0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x63, 0x72, 0x69,
|
||||
0x70, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x0f, 0x73, 0x63, 0x72,
|
||||
0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0d,
|
||||
0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x61, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x04, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x61, 0x61, 0x53, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61,
|
||||
0x73, 0x65, 0x22, 0x3c, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x22, 0x62, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53,
|
||||
0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x07, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61,
|
||||
0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x42, 0x79, 0x41, 0x64, 0x64,
|
||||
0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x69, 0x65, 0x73, 0x22, 0x73, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||
0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73,
|
||||
0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73,
|
||||
0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x04, 0x20,
|
||||
0x03, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x22, 0x24, 0x0a, 0x0c, 0x53, 0x65, 0x6e,
|
||||
0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x78, 0x49,
|
||||
0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x74, 0x78, 0x49, 0x44, 0x73, 0x22,
|
||||
0x5d, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32,
|
||||
0x0a, 0x14, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x14, 0x75, 0x6e,
|
||||
0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x3e,
|
||||
0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e,
|
||||
0x0a, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x12, 0x73, 0x69, 0x67, 0x6e,
|
||||
0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x32, 0xb3,
|
||||
0x06, 0x0a, 0x0c, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x12,
|
||||
0x51, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x2e,
|
||||
0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65, 0x74,
|
||||
0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20,
|
||||
0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47, 0x65,
|
||||
0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x7e, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61,
|
||||
0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x12,
|
||||
0x2e, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47,
|
||||
0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61,
|
||||
0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x2f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x47,
|
||||
0x65, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x61,
|
||||
0x62, 0x6c, 0x65, 0x55, 0x54, 0x58, 0x4f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x81, 0x01, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73,
|
||||
0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x73, 0x12, 0x2f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64,
|
||||
0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x30, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74,
|
||||
0x64, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64,
|
||||
0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x0d, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64,
|
||||
0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77,
|
||||
0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6b, 0x61,
|
||||
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x68, 0x6f, 0x77, 0x41,
|
||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x51, 0x0a, 0x0a, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||
0x12, 0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e,
|
||||
0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x20, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64,
|
||||
0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77,
|
||||
0x6e, 0x12, 0x1d, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64,
|
||||
0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x1e, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e,
|
||||
0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x4e, 0x0a, 0x09, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12,
|
||||
0x1e, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42,
|
||||
0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x1f, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x42,
|
||||
0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x3f, 0x0a, 0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x19, 0x2e, 0x6b, 0x61, 0x73,
|
||||
0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c,
|
||||
0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x04, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x19, 0x2e, 0x6b, 0x61,
|
||||
0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61,
|
||||
0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x00, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70,
|
||||
0x61, 0x64, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c,
|
||||
0x65, 0x74, 0x2f, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -36,7 +36,6 @@ message CreateUnsignedTransactionsRequest {
|
||||
string address = 1;
|
||||
uint64 amount = 2;
|
||||
repeated string from = 3;
|
||||
bool useExistingChangeAddress = 4;
|
||||
}
|
||||
|
||||
message CreateUnsignedTransactionsResponse {
|
||||
@@ -58,8 +57,8 @@ message NewAddressResponse {
|
||||
}
|
||||
|
||||
message BroadcastRequest {
|
||||
bool isDomain = 1;
|
||||
repeated bytes transactions = 2;
|
||||
bool isDomain = 1;
|
||||
repeated bytes transactions = 2;
|
||||
}
|
||||
|
||||
message BroadcastResponse {
|
||||
@@ -108,12 +107,10 @@ message SendRequest{
|
||||
uint64 amount = 2;
|
||||
string password = 3;
|
||||
repeated string from = 4;
|
||||
bool useExistingChangeAddress = 5;
|
||||
}
|
||||
|
||||
message SendResponse{
|
||||
repeated string txIDs = 1;
|
||||
repeated bytes signedTransactions = 2;
|
||||
}
|
||||
|
||||
// Since SignRequest contains a password - this command should only be used on a trusted or secure connection
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.12.3
|
||||
// - protoc v3.17.2
|
||||
// source: kaspawalletd.proto
|
||||
|
||||
package pb
|
||||
|
||||
@@ -10,33 +10,22 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (s *server) changeAddress(useExisting bool, fromAddresses []*walletAddress) (util.Address, *walletAddress, error) {
|
||||
var walletAddr *walletAddress
|
||||
if len(fromAddresses) != 0 && useExisting {
|
||||
walletAddr = fromAddresses[0]
|
||||
} else {
|
||||
internalIndex := uint32(0)
|
||||
if !useExisting {
|
||||
err := s.keysFile.SetLastUsedInternalIndex(s.keysFile.LastUsedInternalIndex() + 1)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = s.keysFile.Save()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
internalIndex = s.keysFile.LastUsedInternalIndex()
|
||||
}
|
||||
|
||||
walletAddr = &walletAddress{
|
||||
index: internalIndex,
|
||||
cosignerIndex: s.keysFile.CosignerIndex,
|
||||
keyChain: libkaspawallet.InternalKeychain,
|
||||
}
|
||||
func (s *server) changeAddress() (util.Address, *walletAddress, error) {
|
||||
err := s.keysFile.SetLastUsedInternalIndex(s.keysFile.LastUsedInternalIndex() + 1)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = s.keysFile.Save()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
walletAddr := &walletAddress{
|
||||
index: s.keysFile.LastUsedInternalIndex(),
|
||||
cosignerIndex: s.keysFile.CosignerIndex,
|
||||
keyChain: libkaspawallet.InternalKeychain,
|
||||
}
|
||||
path := s.walletAddressPath(walletAddr)
|
||||
address, err := libkaspawallet.Address(s.params, s.keysFile.ExtendedPublicKeys, s.keysFile.MinimumSignatures, path, s.keysFile.ECDSA)
|
||||
if err != nil {
|
||||
@@ -50,10 +39,10 @@ func (s *server) ShowAddresses(_ context.Context, request *pb.ShowAddressesReque
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if !s.isSynced() {
|
||||
return nil, errors.Errorf("wallet daemon is not synced yet, %s", s.formatSyncStateReport())
|
||||
return nil, errors.New("server is not synced")
|
||||
}
|
||||
|
||||
addresses := make([]string, s.keysFile.LastUsedExternalIndex())
|
||||
addresses := make([]string, 0)
|
||||
for i := uint32(1); i <= s.keysFile.LastUsedExternalIndex(); i++ {
|
||||
walletAddr := &walletAddress{
|
||||
index: i,
|
||||
@@ -65,7 +54,7 @@ func (s *server) ShowAddresses(_ context.Context, request *pb.ShowAddressesReque
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addresses[i-1] = address.String()
|
||||
addresses = append(addresses, address.String())
|
||||
}
|
||||
|
||||
return &pb.ShowAddressesResponse{Address: addresses}, nil
|
||||
@@ -76,7 +65,7 @@ func (s *server) NewAddress(_ context.Context, request *pb.NewAddressRequest) (*
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if !s.isSynced() {
|
||||
return nil, errors.Errorf("wallet daemon is not synced yet, %s", s.formatSyncStateReport())
|
||||
return nil, errors.New("server is not synced")
|
||||
}
|
||||
|
||||
err := s.keysFile.SetLastUsedExternalIndex(s.keysFile.LastUsedExternalIndex() + 1)
|
||||
|
||||
@@ -3,26 +3,24 @@ package server
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/exp/slices"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TODO: Implement a better fee estimation mechanism
|
||||
const feePerInput = 10000
|
||||
|
||||
func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.CreateUnsignedTransactionsRequest) (
|
||||
*pb.CreateUnsignedTransactionsResponse, error,
|
||||
) {
|
||||
*pb.CreateUnsignedTransactionsResponse, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
unsignedTransactions, err := s.createUnsignedTransactions(request.Address, request.Amount, request.From, request.UseExistingChangeAddress)
|
||||
unsignedTransactions, err := s.createUnsignedTransactions(request.Address, request.Amount, request.From)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -30,19 +28,17 @@ func (s *server) CreateUnsignedTransactions(_ context.Context, request *pb.Creat
|
||||
return &pb.CreateUnsignedTransactionsResponse{UnsignedTransactions: unsignedTransactions}, nil
|
||||
}
|
||||
|
||||
func (s *server) createUnsignedTransactions(address string, amount uint64, fromAddressesString []string, useExistingChangeAddress bool) ([][]byte, error) {
|
||||
func (s *server) createUnsignedTransactions(address string, amount uint64, fromAddressesString []string) ([][]byte, error) {
|
||||
if !s.isSynced() {
|
||||
return nil, errors.Errorf("wallet daemon is not synced yet, %s", s.formatSyncStateReport())
|
||||
return nil, errors.New("server is not synced")
|
||||
}
|
||||
|
||||
// make sure address string is correct before proceeding to a
|
||||
// potentially long UTXO refreshment operation
|
||||
toAddress, err := util.DecodeAddress(address, s.params.Prefix)
|
||||
err := s.refreshUTXOs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.refreshUTXOs()
|
||||
toAddress, err := util.DecodeAddress(address, s.params.Prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -61,7 +57,7 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, fromA
|
||||
return nil, err
|
||||
}
|
||||
|
||||
changeAddress, changeWalletAddress, err := s.changeAddress(useExistingChangeAddress, fromAddresses)
|
||||
changeAddress, changeWalletAddress, err := s.changeAddress()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -91,8 +87,8 @@ func (s *server) createUnsignedTransactions(address string, amount uint64, fromA
|
||||
}
|
||||
|
||||
func (s *server) selectUTXOs(spendAmount uint64, feePerInput uint64, fromAddresses []*walletAddress) (
|
||||
selectedUTXOs []*libkaspawallet.UTXO, changeSompi uint64, err error,
|
||||
) {
|
||||
selectedUTXOs []*libkaspawallet.UTXO, changeSompi uint64, err error) {
|
||||
|
||||
selectedUTXOs = []*libkaspawallet.UTXO{}
|
||||
totalValue := uint64(0)
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ func (s *server) GetExternalSpendableUTXOs(_ context.Context, request *pb.GetExt
|
||||
}
|
||||
|
||||
func (s *server) selectExternalSpendableUTXOs(externalUTXOs *appmessage.GetUTXOsByAddressesResponseMessage, address string) ([]*pb.UtxosByAddressesEntry, error) {
|
||||
|
||||
dagInfo, err := s.rpcClient.GetBlockDAGInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
|
||||
)
|
||||
|
||||
func connectToRPC(params *dagconfig.Params, rpcServer string, timeout uint32) (*rpcclient.RPCClient, error) {
|
||||
func connectToRPC(params *dagconfig.Params, rpcServer string) (*rpcclient.RPCClient, error) {
|
||||
rpcAddress, err := params.NormalizeRPCServerAddress(rpcServer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rpcClient, err := rpcclient.NewRPCClient(rpcAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if timeout != 0 {
|
||||
rpcClient.SetTimeout(time.Duration(timeout) * time.Second)
|
||||
}
|
||||
|
||||
return rpcClient, err
|
||||
return rpcclient.NewRPCClient(rpcAddress)
|
||||
}
|
||||
|
||||
@@ -7,14 +7,10 @@ import (
|
||||
)
|
||||
|
||||
func (s *server) Send(_ context.Context, request *pb.SendRequest) (*pb.SendResponse, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
unsignedTransactions, err := s.createUnsignedTransactions(request.ToAddress, request.Amount, request.From, request.UseExistingChangeAddress)
|
||||
unsignedTransactions, err := s.createUnsignedTransactions(request.ToAddress, request.Amount, request.From)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signedTransactions, err := s.signTransactions(unsignedTransactions, request.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -25,5 +21,5 @@ func (s *server) Send(_ context.Context, request *pb.SendRequest) (*pb.SendRespo
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pb.SendResponse{TxIDs: txIDs, SignedTransactions: signedTransactions}, nil
|
||||
return &pb.SendResponse{TxIDs: txIDs}, nil
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@ package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/txmass"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/profiling"
|
||||
@@ -38,18 +37,10 @@ type server struct {
|
||||
addressSet walletAddressSet
|
||||
txMassCalculator *txmass.Calculator
|
||||
usedOutpoints map[externalapi.DomainOutpoint]time.Time
|
||||
|
||||
isLogFinalProgressLineShown bool
|
||||
maxUsedAddressesForLog uint32
|
||||
maxProcessedAddressesForLog uint32
|
||||
}
|
||||
|
||||
// MaxDaemonSendMsgSize is the max send message size used for the daemon server.
|
||||
// Currently, set to 100MB
|
||||
const MaxDaemonSendMsgSize = 100_000_000
|
||||
|
||||
// Start starts the kaspawalletd server
|
||||
func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath string, profile string, timeout uint32) error {
|
||||
func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath string, profile string) error {
|
||||
initLog(defaultLogFile, defaultErrLogFile)
|
||||
|
||||
defer panics.HandlePanic(log, "MAIN", nil)
|
||||
@@ -61,43 +52,32 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri
|
||||
|
||||
listener, err := net.Listen("tcp", listen)
|
||||
if err != nil {
|
||||
return (errors.Wrapf(err, "Error listening to TCP on %s", listen))
|
||||
return (errors.Wrapf(err, "Error listening to tcp at %s", listen))
|
||||
}
|
||||
log.Infof("Listening to TCP on %s", listen)
|
||||
log.Infof("Listening on %s", listen)
|
||||
|
||||
log.Infof("Connecting to a node at %s...", rpcServer)
|
||||
rpcClient, err := connectToRPC(params, rpcServer, timeout)
|
||||
rpcClient, err := connectToRPC(params, rpcServer)
|
||||
if err != nil {
|
||||
return (errors.Wrapf(err, "Error connecting to RPC server %s", rpcServer))
|
||||
}
|
||||
|
||||
log.Infof("Connected, reading keys file %s...", keysFilePath)
|
||||
keysFile, err := keys.ReadKeysFile(params, keysFilePath)
|
||||
if err != nil {
|
||||
return (errors.Wrapf(err, "Error reading keys file %s", keysFilePath))
|
||||
}
|
||||
|
||||
err = keysFile.TryLock()
|
||||
if err != nil {
|
||||
return err
|
||||
return (errors.Wrapf(err, "Error connecting to RPC server %s", rpcServer))
|
||||
}
|
||||
|
||||
serverInstance := &server{
|
||||
rpcClient: rpcClient,
|
||||
params: params,
|
||||
utxosSortedByAmount: []*walletUTXO{},
|
||||
nextSyncStartIndex: 0,
|
||||
keysFile: keysFile,
|
||||
shutdown: make(chan struct{}),
|
||||
addressSet: make(walletAddressSet),
|
||||
txMassCalculator: txmass.NewCalculator(params.MassPerTxByte, params.MassPerScriptPubKeyByte, params.MassPerSigOp),
|
||||
usedOutpoints: map[externalapi.DomainOutpoint]time.Time{},
|
||||
isLogFinalProgressLineShown: false,
|
||||
maxUsedAddressesForLog: 0,
|
||||
maxProcessedAddressesForLog: 0,
|
||||
rpcClient: rpcClient,
|
||||
params: params,
|
||||
utxosSortedByAmount: []*walletUTXO{},
|
||||
nextSyncStartIndex: 0,
|
||||
keysFile: keysFile,
|
||||
shutdown: make(chan struct{}),
|
||||
addressSet: make(walletAddressSet),
|
||||
txMassCalculator: txmass.NewCalculator(params.MassPerTxByte, params.MassPerScriptPubKeyByte, params.MassPerSigOp),
|
||||
usedOutpoints: map[externalapi.DomainOutpoint]time.Time{},
|
||||
}
|
||||
|
||||
log.Infof("Read, syncing the wallet...")
|
||||
spawn("serverInstance.sync", func() {
|
||||
err := serverInstance.sync()
|
||||
if err != nil {
|
||||
@@ -105,7 +85,7 @@ func Start(params *dagconfig.Params, listen, rpcServer string, keysFilePath stri
|
||||
}
|
||||
})
|
||||
|
||||
grpcServer := grpc.NewServer(grpc.MaxSendMsgSize(MaxDaemonSendMsgSize))
|
||||
grpcServer := grpc.NewServer()
|
||||
pb.RegisterKaspawalletdServer(grpcServer, serverInstance)
|
||||
|
||||
spawn("grpcServer.Serve", func() {
|
||||
|
||||
@@ -27,10 +27,18 @@ func (s *server) maybeAutoCompoundTransaction(transactionBytes []byte, toAddress
|
||||
return nil, err
|
||||
}
|
||||
|
||||
splitTransactions, err := s.maybeSplitAndMergeTransaction(transaction, toAddress, changeAddress, changeWalletAddress)
|
||||
splitTransactions, err := s.maybeSplitTransaction(transaction, changeAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(splitTransactions) > 1 {
|
||||
mergeTransaction, err := s.mergeTransaction(splitTransactions, transaction, toAddress, changeAddress, changeWalletAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
splitTransactions = append(splitTransactions, mergeTransaction)
|
||||
}
|
||||
|
||||
splitTransactionsBytes := make([][]byte, len(splitTransactions))
|
||||
for i, splitTransaction := range splitTransactions {
|
||||
splitTransactionsBytes[i], err = serialization.SerializePartiallySignedTransaction(splitTransaction)
|
||||
@@ -105,8 +113,8 @@ func (s *server) mergeTransaction(
|
||||
return serialization.DeserializePartiallySignedTransaction(mergeTransactionBytes)
|
||||
}
|
||||
|
||||
func (s *server) maybeSplitAndMergeTransaction(transaction *serialization.PartiallySignedTransaction, toAddress util.Address,
|
||||
changeAddress util.Address, changeWalletAddress *walletAddress) ([]*serialization.PartiallySignedTransaction, error) {
|
||||
func (s *server) maybeSplitTransaction(transaction *serialization.PartiallySignedTransaction,
|
||||
changeAddress util.Address) ([]*serialization.PartiallySignedTransaction, error) {
|
||||
|
||||
transactionMass, err := s.estimateMassAfterSignatures(transaction)
|
||||
if err != nil {
|
||||
@@ -133,20 +141,6 @@ func (s *server) maybeSplitAndMergeTransaction(transaction *serialization.Partia
|
||||
}
|
||||
}
|
||||
|
||||
if len(splitTransactions) > 1 {
|
||||
mergeTransaction, err := s.mergeTransaction(splitTransactions, transaction, toAddress, changeAddress, changeWalletAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Recursion will be 2-3 iterations deep even in the rarest` cases, so considered safe..
|
||||
splitMergeTransaction, err := s.maybeSplitAndMergeTransaction(mergeTransaction, toAddress, changeAddress, changeWalletAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
splitTransactions = append(splitTransactions, splitMergeTransaction...)
|
||||
|
||||
}
|
||||
|
||||
return splitTransactions, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ func testEstimateMassIncreaseForSignaturesSetUp(t *testing.T, consensusConfig *c
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
block1, _, err := tc.GetBlock(block1Hash)
|
||||
block1, err := tc.GetBlock(block1Hash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
@@ -57,10 +56,8 @@ func (s *server) sync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
numIndexesToQueryForFarAddresses = 100
|
||||
numIndexesToQueryForRecentAddresses = 1000
|
||||
)
|
||||
const numIndexesToQueryForFarAddresses = 100
|
||||
const numIndexesToQueryForRecentAddresses = 1000
|
||||
|
||||
// addressesToQuery scans the addresses in the given range. Because
|
||||
// each cosigner in a multisig has its own unique path for generating
|
||||
@@ -102,14 +99,10 @@ func (s *server) collectFarAddresses() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *server) maxUsedIndexWithLock() uint32 {
|
||||
func (s *server) maxUsedIndex() uint32 {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
|
||||
return s.maxUsedIndex()
|
||||
}
|
||||
|
||||
func (s *server) maxUsedIndex() uint32 {
|
||||
maxUsedIndex := s.keysFile.LastUsedExternalIndex()
|
||||
if s.keysFile.LastUsedInternalIndex() > maxUsedIndex {
|
||||
maxUsedIndex = s.keysFile.LastUsedInternalIndex()
|
||||
@@ -127,13 +120,10 @@ func (s *server) collectRecentAddresses() error {
|
||||
maxUsedIndex := uint32(0)
|
||||
for ; index < maxUsedIndex+numIndexesToQueryForRecentAddresses; index += numIndexesToQueryForRecentAddresses {
|
||||
err := s.collectAddressesWithLock(index, index+numIndexesToQueryForRecentAddresses)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
maxUsedIndex = s.maxUsedIndexWithLock()
|
||||
|
||||
s.updateSyncingProgressLog(index, maxUsedIndex)
|
||||
maxUsedIndex = s.maxUsedIndex()
|
||||
}
|
||||
|
||||
s.lock.Lock()
|
||||
@@ -173,6 +163,7 @@ func (s *server) collectAddresses(start, end uint32) error {
|
||||
|
||||
func (s *server) updateAddressesAndLastUsedIndexes(requestedAddressSet walletAddressSet,
|
||||
getBalancesByAddressesResponse *appmessage.GetBalancesByAddressesResponseMessage) error {
|
||||
|
||||
lastUsedExternalIndex := s.keysFile.LastUsedExternalIndex()
|
||||
lastUsedInternalIndex := s.keysFile.LastUsedInternalIndex()
|
||||
|
||||
@@ -267,7 +258,7 @@ func (s *server) refreshUTXOs() error {
|
||||
// and not in consensus, and between the calls its spending transaction will be
|
||||
// added to consensus and removed from the mempool, so `getUTXOsByAddressesResponse`
|
||||
// will include an obsolete output.
|
||||
mempoolEntriesByAddresses, err := s.rpcClient.GetMempoolEntriesByAddresses(s.addressSet.strings(), true, true)
|
||||
mempoolEntriesByAddresses, err := s.rpcClient.GetMempoolEntriesByAddresses(s.addressSet.strings())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -281,43 +272,5 @@ func (s *server) refreshUTXOs() error {
|
||||
}
|
||||
|
||||
func (s *server) isSynced() bool {
|
||||
return s.nextSyncStartIndex > s.maxUsedIndex()
|
||||
}
|
||||
|
||||
func (s *server) formatSyncStateReport() string {
|
||||
maxUsedIndex := s.maxUsedIndex()
|
||||
|
||||
if s.nextSyncStartIndex > maxUsedIndex {
|
||||
maxUsedIndex = s.nextSyncStartIndex
|
||||
}
|
||||
|
||||
return fmt.Sprintf("scanned %d out of %d addresses (%.2f%%)",
|
||||
s.nextSyncStartIndex, maxUsedIndex, float64(s.nextSyncStartIndex)*100.0/float64(maxUsedIndex))
|
||||
}
|
||||
|
||||
func (s *server) updateSyncingProgressLog(currProcessedAddresses, currMaxUsedAddresses uint32) {
|
||||
if currMaxUsedAddresses > s.maxUsedAddressesForLog {
|
||||
s.maxUsedAddressesForLog = currMaxUsedAddresses
|
||||
if s.isLogFinalProgressLineShown {
|
||||
log.Infof("An additional set of previously used addresses found, processing...")
|
||||
s.maxProcessedAddressesForLog = 0
|
||||
s.isLogFinalProgressLineShown = false
|
||||
}
|
||||
}
|
||||
|
||||
if currProcessedAddresses > s.maxProcessedAddressesForLog {
|
||||
s.maxProcessedAddressesForLog = currProcessedAddresses
|
||||
}
|
||||
|
||||
if s.maxProcessedAddressesForLog >= s.maxUsedAddressesForLog {
|
||||
if !s.isLogFinalProgressLineShown {
|
||||
log.Infof("Wallet is synced, ready for queries")
|
||||
s.isLogFinalProgressLineShown = true
|
||||
}
|
||||
} else {
|
||||
percentProcessed := float64(s.maxProcessedAddressesForLog) / float64(s.maxUsedAddressesForLog) * 100.0
|
||||
|
||||
log.Infof("%d addresses of %d processed (%.2f%%)...",
|
||||
s.maxProcessedAddressesForLog, s.maxUsedAddressesForLog, percentProcessed)
|
||||
}
|
||||
return s.nextSyncStartIndex > s.keysFile.LastUsedInternalIndex() && s.nextSyncStartIndex > s.keysFile.LastUsedExternalIndex()
|
||||
}
|
||||
|
||||
@@ -62,8 +62,6 @@ func encryptedMnemonicExtendedPublicKeyPairs(params *dagconfig.Params, mnemonics
|
||||
}
|
||||
|
||||
encryptedPrivateKeys = make([]*EncryptedMnemonic, 0, len(mnemonics))
|
||||
extendedPublicKeys = make([]string, 0, len(mnemonics))
|
||||
|
||||
for _, mnemonic := range mnemonics {
|
||||
extendedPublicKey, err := libkaspawallet.MasterPublicKeyFromMnemonic(params, mnemonic, isMultisig)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gofrs/flock"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -401,33 +400,3 @@ func decryptMnemonic(numThreads uint8, encryptedPrivateKey *EncryptedMnemonic, p
|
||||
|
||||
return string(decrypted), nil
|
||||
}
|
||||
|
||||
// flockMap is a map that holds all lock file handlers. This map guarantees that
|
||||
// the associated locked file handler will never get cleaned by the GC, because
|
||||
// once they are cleaned the associated file will be unlocked.
|
||||
var flockMap = make(map[string]*flock.Flock)
|
||||
|
||||
// TryLock tries to acquire an exclusive lock for the file.
|
||||
func (d *File) TryLock() error {
|
||||
if _, ok := flockMap[d.path]; ok {
|
||||
return errors.Errorf("file %s is already locked", d.path)
|
||||
}
|
||||
|
||||
lockFile := flock.New(d.path + ".lock")
|
||||
err := createFileDirectoryIfDoesntExist(lockFile.Path())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flockMap[d.path] = lockFile
|
||||
|
||||
success, err := lockFile.TryLock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !success {
|
||||
return errors.Errorf("%s is locked and cannot be used. Make sure that no other active wallet command is using it.", d.path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func TestMultisig(t *testing.T) {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
block1, _, err := tc.GetBlock(block1Hash)
|
||||
block1, err := tc.GetBlock(block1Hash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
@@ -245,7 +245,7 @@ func TestP2PK(t *testing.T) {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
block1, _, err := tc.GetBlock(block1Hash)
|
||||
block1, err := tc.GetBlock(block1Hash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
@@ -377,17 +377,17 @@ func TestMaxSompi(t *testing.T) {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
fundingBlock2, _, err := tc.GetBlock(fundingBlock2Hash)
|
||||
fundingBlock2, err := tc.GetBlock(fundingBlock2Hash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
|
||||
fundingBlock3, _, err := tc.GetBlock(fundingBlock3Hash)
|
||||
fundingBlock3, err := tc.GetBlock(fundingBlock3Hash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
|
||||
fundingBlock4, _, err := tc.GetBlock(fundingBlock4Hash)
|
||||
fundingBlock4, err := tc.GetBlock(fundingBlock4Hash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
@@ -397,7 +397,7 @@ func TestMaxSompi(t *testing.T) {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
block1, _, err := tc.GetBlock(block1Hash)
|
||||
block1, err := tc.GetBlock(block1Hash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
|
||||
@@ -29,57 +29,55 @@ func parse(conf *parseConfig) error {
|
||||
transactionHex = strings.TrimSpace(string(transactionHexBytes))
|
||||
}
|
||||
|
||||
transactions, err := decodeTransactionsFromHex(transactionHex)
|
||||
transaction, err := hex.DecodeString(transactionHex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, transaction := range transactions {
|
||||
|
||||
partiallySignedTransaction, err := serialization.DeserializePartiallySignedTransaction(transaction)
|
||||
partiallySignedTransaction, err := serialization.DeserializePartiallySignedTransaction(transaction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Transaction ID: \t%s\n", consensushashing.TransactionID(partiallySignedTransaction.Tx))
|
||||
fmt.Println()
|
||||
|
||||
allInputSompi := uint64(0)
|
||||
for index, input := range partiallySignedTransaction.Tx.Inputs {
|
||||
partiallySignedInput := partiallySignedTransaction.PartiallySignedInputs[index]
|
||||
|
||||
if conf.Verbose {
|
||||
fmt.Printf("Input %d: \tOutpoint: %s:%d \tAmount: %.2f Kaspa\n", index, input.PreviousOutpoint.TransactionID,
|
||||
input.PreviousOutpoint.Index, float64(partiallySignedInput.PrevOutput.Value)/float64(constants.SompiPerKaspa))
|
||||
}
|
||||
|
||||
allInputSompi += partiallySignedInput.PrevOutput.Value
|
||||
}
|
||||
if conf.Verbose {
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
allOutputSompi := uint64(0)
|
||||
for index, output := range partiallySignedTransaction.Tx.Outputs {
|
||||
scriptPublicKeyType, scriptPublicKeyAddress, err := txscript.ExtractScriptPubKeyAddress(output.ScriptPublicKey, conf.ActiveNetParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Transaction #%d ID: \t%s\n", i+1, consensushashing.TransactionID(partiallySignedTransaction.Tx))
|
||||
fmt.Println()
|
||||
|
||||
allInputSompi := uint64(0)
|
||||
for index, input := range partiallySignedTransaction.Tx.Inputs {
|
||||
partiallySignedInput := partiallySignedTransaction.PartiallySignedInputs[index]
|
||||
|
||||
if conf.Verbose {
|
||||
fmt.Printf("Input %d: \tOutpoint: %s:%d \tAmount: %.2f Kaspa\n", index, input.PreviousOutpoint.TransactionID,
|
||||
input.PreviousOutpoint.Index, float64(partiallySignedInput.PrevOutput.Value)/float64(constants.SompiPerKaspa))
|
||||
}
|
||||
|
||||
allInputSompi += partiallySignedInput.PrevOutput.Value
|
||||
}
|
||||
if conf.Verbose {
|
||||
fmt.Println()
|
||||
addressString := scriptPublicKeyAddress.EncodeAddress()
|
||||
if scriptPublicKeyType == txscript.NonStandardTy {
|
||||
scriptPublicKeyHex := hex.EncodeToString(output.ScriptPublicKey.Script)
|
||||
addressString = fmt.Sprintf("<Non-standard transaction script public key: %s>", scriptPublicKeyHex)
|
||||
}
|
||||
|
||||
allOutputSompi := uint64(0)
|
||||
for index, output := range partiallySignedTransaction.Tx.Outputs {
|
||||
scriptPublicKeyType, scriptPublicKeyAddress, err := txscript.ExtractScriptPubKeyAddress(output.ScriptPublicKey, conf.ActiveNetParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Output %d: \tRecipient: %s \tAmount: %.2f Kaspa\n",
|
||||
index, addressString, float64(output.Value)/float64(constants.SompiPerKaspa))
|
||||
|
||||
addressString := scriptPublicKeyAddress.EncodeAddress()
|
||||
if scriptPublicKeyType == txscript.NonStandardTy {
|
||||
scriptPublicKeyHex := hex.EncodeToString(output.ScriptPublicKey.Script)
|
||||
addressString = fmt.Sprintf("<Non-standard transaction script public key: %s>", scriptPublicKeyHex)
|
||||
}
|
||||
|
||||
fmt.Printf("Output %d: \tRecipient: %s \tAmount: %.2f Kaspa\n",
|
||||
index, addressString, float64(output.Value)/float64(constants.SompiPerKaspa))
|
||||
|
||||
allOutputSompi += output.Value
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
fmt.Printf("Fee:\t%d Sompi\n\n", allInputSompi-allOutputSompi)
|
||||
allOutputSompi += output.Value
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
fmt.Printf("Fee:\t%d Sompi\n", allInputSompi-allOutputSompi)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/client"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/pb"
|
||||
@@ -37,10 +35,9 @@ func send(conf *sendConfig) error {
|
||||
|
||||
createUnsignedTransactionsResponse, err :=
|
||||
daemonClient.CreateUnsignedTransactions(ctx, &pb.CreateUnsignedTransactionsRequest{
|
||||
From: conf.FromAddresses,
|
||||
Address: conf.ToAddress,
|
||||
Amount: sendAmountSompi,
|
||||
UseExistingChangeAddress: conf.UseExistingChangeAddress,
|
||||
From: conf.FromAddresses,
|
||||
Address: conf.ToAddress,
|
||||
Amount: sendAmountSompi,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -51,10 +48,6 @@ func send(conf *sendConfig) error {
|
||||
}
|
||||
mnemonics, err := keysFile.DecryptMnemonics(conf.Password)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "message authentication failed") {
|
||||
fmt.Fprintf(os.Stderr, "Password decryption failed. Sometimes this is a result of not "+
|
||||
"specifying the same keys file used by the wallet daemon process.\n")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -71,12 +64,7 @@ func send(conf *sendConfig) error {
|
||||
fmt.Printf("Broadcasting %d transactions\n", len(signedTransactions))
|
||||
}
|
||||
|
||||
// Since we waited for user input when getting the password, which could take unbound amount of time -
|
||||
// create a new context for broadcast, to reset the timeout.
|
||||
broadcastCtx, broadcastCancel := context.WithTimeout(context.Background(), daemonTimeout)
|
||||
defer broadcastCancel()
|
||||
|
||||
response, err := daemonClient.Broadcast(broadcastCtx, &pb.BroadcastRequest{Transactions: signedTransactions})
|
||||
response, err := daemonClient.Broadcast(ctx, &pb.BroadcastRequest{Transactions: signedTransactions})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -86,12 +74,5 @@ func send(conf *sendConfig) error {
|
||||
fmt.Printf("\t%s\n", txID)
|
||||
}
|
||||
|
||||
if conf.Verbose {
|
||||
fmt.Println("Serialized Transaction(s) (can be parsed via the `parse` command or resent via `broadcast`): ")
|
||||
for _, signedTx := range signedTransactions {
|
||||
fmt.Printf("\t%x\n\n", signedTx)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -26,8 +26,5 @@ func showAddresses(conf *showAddressesConfig) error {
|
||||
for _, address := range response.Address {
|
||||
fmt.Println(address)
|
||||
}
|
||||
|
||||
fmt.Printf("\nNote: the above are only addresses that were manually created by the 'new-address' command. If you want to see a list of all addresses, including change addresses, " +
|
||||
"that have a positive balance, use the command 'balance -v'\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
|
||||
@@ -67,11 +66,12 @@ func sign(conf *signConfig) error {
|
||||
}
|
||||
|
||||
if areAllTransactionsFullySigned {
|
||||
fmt.Fprintln(os.Stderr, "The transaction is signed and ready to broadcast")
|
||||
fmt.Println("The transaction is signed and ready to broadcast")
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, "Successfully signed transaction")
|
||||
fmt.Println("Successfully signed transaction")
|
||||
}
|
||||
|
||||
fmt.Println("Transaction: ")
|
||||
fmt.Println(encodeTransactionsToHex(updatedPartiallySignedTransactions))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@ package main
|
||||
import "github.com/kaspanet/kaspad/cmd/kaspawallet/daemon/server"
|
||||
|
||||
func startDaemon(conf *startDaemonConfig) error {
|
||||
return server.Start(conf.NetParams(), conf.Listen, conf.RPCServer, conf.KeysFile, conf.Profile, conf.Timeout)
|
||||
return server.Start(conf.NetParams(), conf.Listen, conf.RPCServer, conf.KeysFile, conf.Profile)
|
||||
}
|
||||
|
||||
@@ -10,13 +10,19 @@ RUN apk add --no-cache curl git openssh binutils gcc musl-dev
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
|
||||
RUN go get -u golang.org/x/lint/golint \
|
||||
github.com/kisielk/errcheck \
|
||||
github.com/opennota/check/cmd/aligncheck \
|
||||
github.com/opennota/check/cmd/structcheck \
|
||||
github.com/opennota/check/cmd/varcheck \
|
||||
honnef.co/go/tools/cmd/staticcheck
|
||||
|
||||
# Cache kaspad dependencies
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN go build $FLAGS -o kaspad .
|
||||
RUN ./build_and_test.sh
|
||||
|
||||
# --- multistage docker build: stage #2: runtime image
|
||||
FROM alpine
|
||||
|
||||
@@ -61,24 +61,17 @@ type consensus struct {
|
||||
blocksWithTrustedDataDAAWindowStore model.BlocksWithTrustedDataDAAWindowStore
|
||||
|
||||
consensusEventsChan chan externalapi.ConsensusEvent
|
||||
virtualNotUpdated bool
|
||||
}
|
||||
|
||||
// In order to prevent a situation that the consensus lock is held for too much time, we
|
||||
// release the lock each time we resolve 100 blocks.
|
||||
// Note: `virtualResolveChunk` should be smaller than `params.FinalityDuration` in order to avoid a situation
|
||||
// where UpdatePruningPointByVirtual skips a pruning point.
|
||||
const virtualResolveChunk = 100
|
||||
|
||||
func (s *consensus) ValidateAndInsertBlockWithTrustedData(block *externalapi.BlockWithTrustedData, validateUTXO bool) error {
|
||||
func (s *consensus) ValidateAndInsertBlockWithTrustedData(block *externalapi.BlockWithTrustedData, validateUTXO bool) (*externalapi.VirtualChangeSet, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
_, _, err := s.blockProcessor.ValidateAndInsertBlockWithTrustedData(block, validateUTXO)
|
||||
virtualChangeSet, _, err := s.blockProcessor.ValidateAndInsertBlockWithTrustedData(block, validateUTXO)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
return nil
|
||||
return virtualChangeSet, nil
|
||||
}
|
||||
|
||||
// Init initializes consensus
|
||||
@@ -200,73 +193,21 @@ func (s *consensus) BuildBlockTemplate(coinbaseData *externalapi.DomainCoinbaseD
|
||||
|
||||
// ValidateAndInsertBlock validates the given block and, if valid, applies it
|
||||
// to the current state
|
||||
func (s *consensus) ValidateAndInsertBlock(block *externalapi.DomainBlock, updateVirtual bool) error {
|
||||
if updateVirtual {
|
||||
s.lock.Lock()
|
||||
if s.virtualNotUpdated {
|
||||
// We enter the loop in locked state
|
||||
for {
|
||||
_, isCompletelyResolved, err := s.resolveVirtualChunkNoLock(virtualResolveChunk)
|
||||
if err != nil {
|
||||
s.lock.Unlock()
|
||||
return err
|
||||
}
|
||||
if isCompletelyResolved {
|
||||
// Make sure we enter the block insertion function w/o releasing the lock.
|
||||
// Otherwise, we might actually enter it in `s.virtualNotUpdated == true` state
|
||||
_, err = s.validateAndInsertBlockNoLock(block, updateVirtual)
|
||||
// Finally, unlock for the last iteration and return
|
||||
s.lock.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Unlock to allow other threads to enter consensus
|
||||
s.lock.Unlock()
|
||||
// Lock for the next iteration
|
||||
s.lock.Lock()
|
||||
}
|
||||
}
|
||||
_, err := s.validateAndInsertBlockNoLock(block, updateVirtual)
|
||||
s.lock.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.validateAndInsertBlockWithLock(block, updateVirtual)
|
||||
}
|
||||
|
||||
func (s *consensus) validateAndInsertBlockWithLock(block *externalapi.DomainBlock, updateVirtual bool) error {
|
||||
func (s *consensus) ValidateAndInsertBlock(block *externalapi.DomainBlock, shouldValidateAgainstUTXO bool) (*externalapi.VirtualChangeSet, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
_, err := s.validateAndInsertBlockNoLock(block, updateVirtual)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *consensus) validateAndInsertBlockNoLock(block *externalapi.DomainBlock, updateVirtual bool) (*externalapi.VirtualChangeSet, error) {
|
||||
virtualChangeSet, blockStatus, err := s.blockProcessor.ValidateAndInsertBlock(block, updateVirtual)
|
||||
virtualChangeSet, blockStatus, err := s.blockProcessor.ValidateAndInsertBlock(block, shouldValidateAgainstUTXO)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If block has a body, and yet virtual was not updated -- signify that virtual is in non-updated state
|
||||
if !updateVirtual && blockStatus != externalapi.StatusHeaderOnly {
|
||||
s.virtualNotUpdated = true
|
||||
}
|
||||
|
||||
err = s.sendBlockAddedEvent(block, blockStatus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.sendVirtualChangedEvent(virtualChangeSet, updateVirtual)
|
||||
err = s.sendVirtualChangedEvent(virtualChangeSet, shouldValidateAgainstUTXO)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -289,7 +230,7 @@ func (s *consensus) sendBlockAddedEvent(block *externalapi.DomainBlock, blockSta
|
||||
}
|
||||
|
||||
func (s *consensus) sendVirtualChangedEvent(virtualChangeSet *externalapi.VirtualChangeSet, wasVirtualUpdated bool) error {
|
||||
if !wasVirtualUpdated || s.consensusEventsChan == nil || virtualChangeSet == nil {
|
||||
if !wasVirtualUpdated || s.consensusEventsChan == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -357,7 +298,7 @@ func (s *consensus) ValidateTransactionAndPopulateWithConsensusData(transaction
|
||||
stagingArea, transaction, model.VirtualBlockHash)
|
||||
}
|
||||
|
||||
func (s *consensus) GetBlock(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, bool, error) {
|
||||
func (s *consensus) GetBlock(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
@@ -366,11 +307,11 @@ func (s *consensus) GetBlock(blockHash *externalapi.DomainHash) (*externalapi.Do
|
||||
block, err := s.blockStore.Block(s.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
if errors.Is(err, database.ErrNotFound) {
|
||||
return nil, false, nil
|
||||
return nil, errors.Wrapf(err, "block %s does not exist", blockHash)
|
||||
}
|
||||
return nil, false, err
|
||||
return nil, err
|
||||
}
|
||||
return block, true, nil
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func (s *consensus) GetBlockEvenIfHeaderOnly(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) {
|
||||
@@ -485,30 +426,6 @@ func (s *consensus) GetBlockAcceptanceData(blockHash *externalapi.DomainHash) (e
|
||||
return s.acceptanceDataStore.Get(s.databaseContext, stagingArea, blockHash)
|
||||
}
|
||||
|
||||
func (s *consensus) GetBlocksAcceptanceData(blockHashes []*externalapi.DomainHash) ([]externalapi.AcceptanceData, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
stagingArea := model.NewStagingArea()
|
||||
blocksAcceptanceData := make([]externalapi.AcceptanceData, len(blockHashes))
|
||||
|
||||
for i, blockHash := range blockHashes {
|
||||
err := s.validateBlockHashExists(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
acceptanceData, err := s.acceptanceDataStore.Get(s.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blocksAcceptanceData[i] = acceptanceData
|
||||
}
|
||||
|
||||
return blocksAcceptanceData, nil
|
||||
}
|
||||
|
||||
func (s *consensus) GetHashesBetween(lowHash, highHash *externalapi.DomainHash, maxBlocks uint64) (
|
||||
hashes []*externalapi.DomainHash, actualHighHash *externalapi.DomainHash, err error) {
|
||||
|
||||
@@ -853,16 +770,12 @@ func (s *consensus) GetVirtualSelectedParentChainFromBlock(blockHash *externalap
|
||||
}
|
||||
|
||||
func (s *consensus) validateBlockHashExists(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) error {
|
||||
status, err := s.blockStatusStore.Get(s.databaseContext, stagingArea, blockHash)
|
||||
if database.IsNotFoundError(err) {
|
||||
return errors.Errorf("block %s does not exist", blockHash)
|
||||
}
|
||||
exists, err := s.blockStatusStore.Exists(s.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if status == externalapi.StatusInvalid {
|
||||
return errors.Errorf("block %s is invalid", blockHash)
|
||||
if !exists {
|
||||
return errors.Errorf("block %s does not exist", blockHash)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -924,46 +837,18 @@ func (s *consensus) PopulateMass(transaction *externalapi.DomainTransaction) {
|
||||
s.transactionValidator.PopulateMass(transaction)
|
||||
}
|
||||
|
||||
func (s *consensus) ResolveVirtual(progressReportCallback func(uint64, uint64)) error {
|
||||
virtualDAAScoreStart, err := s.GetVirtualDAAScore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; ; i++ {
|
||||
if i%10 == 0 && progressReportCallback != nil {
|
||||
virtualDAAScore, err := s.GetVirtualDAAScore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
progressReportCallback(virtualDAAScoreStart, virtualDAAScore)
|
||||
}
|
||||
|
||||
_, isCompletelyResolved, err := s.resolveVirtualChunkWithLock(virtualResolveChunk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isCompletelyResolved {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *consensus) resolveVirtualChunkWithLock(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error) {
|
||||
func (s *consensus) ResolveVirtual() (*externalapi.VirtualChangeSet, bool, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.resolveVirtualChunkNoLock(maxBlocksToResolve)
|
||||
}
|
||||
|
||||
func (s *consensus) resolveVirtualChunkNoLock(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error) {
|
||||
virtualChangeSet, isCompletelyResolved, err := s.consensusStateManager.ResolveVirtual(maxBlocksToResolve)
|
||||
// In order to prevent a situation that the consensus lock is held for too much time, we
|
||||
// release the lock each time resolve 100 blocks.
|
||||
// Note: maxBlocksToResolve should be smaller than finality interval in order to avoid a situation
|
||||
// where UpdatePruningPointByVirtual skips a pruning point.
|
||||
virtualChangeSet, isCompletelyResolved, err := s.consensusStateManager.ResolveVirtual(100)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
s.virtualNotUpdated = !isCompletelyResolved
|
||||
|
||||
stagingArea := model.NewStagingArea()
|
||||
err = s.pruningManager.UpdatePruningPointByVirtual(stagingArea)
|
||||
@@ -976,11 +861,6 @@ func (s *consensus) resolveVirtualChunkNoLock(maxBlocksToResolve uint64) (*exter
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
err = s.pruningManager.UpdatePruningPointIfRequired()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
err = s.sendVirtualChangedEvent(virtualChangeSet, true)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestConsensus_GetBlockInfo(t *testing.T) {
|
||||
newHeader := invalidBlock.Header.ToMutable()
|
||||
newHeader.SetTimeInMilliseconds(0)
|
||||
invalidBlock.Header = newHeader.ToImmutable()
|
||||
err = consensus.ValidateAndInsertBlock(invalidBlock, true)
|
||||
_, err = consensus.ValidateAndInsertBlock(invalidBlock, true)
|
||||
if !errors.Is(err, ruleerrors.ErrTimeTooOld) {
|
||||
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrTimeTooOld, err)
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func TestConsensus_GetBlockInfo(t *testing.T) {
|
||||
t.Fatalf("consensus.BuildBlock with an empty coinbase shouldn't fail: %v", err)
|
||||
}
|
||||
|
||||
err = consensus.ValidateAndInsertBlock(validBlock, true)
|
||||
_, err = consensus.ValidateAndInsertBlock(validBlock, true)
|
||||
if err != nil {
|
||||
t.Fatalf("consensus.ValidateAndInsertBlock with a block straight from consensus.BuildBlock should not fail: %v", err)
|
||||
}
|
||||
|
||||
@@ -216,8 +216,6 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
transactionValidator := transactionvalidator.New(config.BlockCoinbaseMaturity,
|
||||
config.EnableNonNativeSubnetworks,
|
||||
config.MaxCoinbasePayloadLength,
|
||||
config.K,
|
||||
config.CoinbasePayloadScriptPublicKeyMaxLength,
|
||||
dbManager,
|
||||
pastMedianTimeManager,
|
||||
ghostdagDataStore,
|
||||
@@ -239,14 +237,12 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
config.GenesisBlock.Header.Bits())
|
||||
coinbaseManager := coinbasemanager.New(
|
||||
dbManager,
|
||||
|
||||
config.SubsidyGenesisReward,
|
||||
config.PreDeflationaryPhaseBaseSubsidy,
|
||||
config.CoinbasePayloadScriptPublicKeyMaxLength,
|
||||
config.GenesisHash,
|
||||
config.DeflationaryPhaseDaaScore,
|
||||
config.DeflationaryPhaseBaseSubsidy,
|
||||
|
||||
dagTraversalManager,
|
||||
ghostdagDataStore,
|
||||
acceptanceDataStore,
|
||||
@@ -519,7 +515,6 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
blocksWithTrustedDataDAAWindowStore: daaWindowStore,
|
||||
|
||||
consensusEventsChan: consensusEventsChan,
|
||||
virtualNotUpdated: true,
|
||||
}
|
||||
|
||||
if isOldReachabilityInitialized {
|
||||
@@ -544,8 +539,6 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// If the virtual moved before shutdown but the pruning point hasn't, we
|
||||
// move it if needed.
|
||||
stagingArea := model.NewStagingArea()
|
||||
err = pruningManager.UpdatePruningPointByVirtual(stagingArea)
|
||||
if err != nil {
|
||||
@@ -557,11 +550,6 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
err = pruningManager.UpdatePruningPointIfRequired()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return c, false, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestFinality(t *testing.T) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = consensus.ValidateAndInsertBlock(block, true)
|
||||
_, err = consensus.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -201,7 +201,7 @@ func TestBoundedMergeDepth(t *testing.T) {
|
||||
return nil, false // fo some reason go doesn't recognize that t.Fatalf never returns
|
||||
}
|
||||
|
||||
err = consensus.ValidateAndInsertBlock(block, true)
|
||||
_, err = consensus.ValidateAndInsertBlock(block, true)
|
||||
if err == nil {
|
||||
return block, false
|
||||
} else if errors.Is(err, ruleerrors.ErrViolatingBoundedMergeDepth) {
|
||||
@@ -213,7 +213,7 @@ func TestBoundedMergeDepth(t *testing.T) {
|
||||
}
|
||||
|
||||
processBlock := func(consensus testapi.TestConsensus, block *externalapi.DomainBlock, name string) {
|
||||
err := consensus.ValidateAndInsertBlock(block, true)
|
||||
_, err := consensus.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: %s got unexpected error from ProcessBlock: %+v", name, err)
|
||||
|
||||
@@ -225,7 +225,7 @@ func TestBoundedMergeDepth(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Failed building block: %+v", err)
|
||||
}
|
||||
err = consensus.ValidateAndInsertBlock(block, true)
|
||||
_, err = consensus.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Failed Inserting block to consensus: %v", err)
|
||||
}
|
||||
@@ -263,12 +263,12 @@ func TestBoundedMergeDepth(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
|
||||
block, _, err := tcSyncer.GetBlock(blocksHash)
|
||||
block, err := tcSyncer.GetBlock(blocksHash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlockHeader: %+v", err)
|
||||
}
|
||||
|
||||
err = tcSyncee.ValidateAndInsertBlock(block, true)
|
||||
_, err = tcSyncee.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlock %d: %+v", i, err)
|
||||
}
|
||||
@@ -556,7 +556,7 @@ func TestFinalityResolveVirtual(t *testing.T) {
|
||||
block.Header = mutableHeader.ToImmutable()
|
||||
}
|
||||
|
||||
err = tcAttacker.ValidateAndInsertBlock(block, true)
|
||||
_, err = tcAttacker.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -583,18 +583,23 @@ func TestFinalityResolveVirtual(t *testing.T) {
|
||||
t.Logf("Side chain tip (%s) blue score %d", sideChainTipHash, sideChainTipGHOSTDAGData.BlueScore())
|
||||
|
||||
for _, block := range sideChain {
|
||||
err := tc.ValidateAndInsertBlock(block, false)
|
||||
_, err := tc.ValidateAndInsertBlock(block, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
err = tc.ResolveVirtual(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for i := 0; ; i++ {
|
||||
_, isCompletelyResolved, err := tc.ResolveVirtual()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
t.Log("Resolved virtual")
|
||||
if isCompletelyResolved {
|
||||
t.Log("Resolved virtual")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
sideChainTipGHOSTDAGData, err = tc.GHOSTDAGDataStore().Get(tc.DatabaseContext(), stagingArea, sideChainTipHash, false)
|
||||
if err != nil {
|
||||
|
||||
@@ -13,16 +13,6 @@ type BlockInfo struct {
|
||||
MergeSetReds []*DomainHash
|
||||
}
|
||||
|
||||
// HasHeader returns whether the block exists and has a valid header
|
||||
func (bi *BlockInfo) HasHeader() bool {
|
||||
return bi.Exists && bi.BlockStatus != StatusInvalid
|
||||
}
|
||||
|
||||
// HasBody returns whether the block exists and has a valid body
|
||||
func (bi *BlockInfo) HasBody() bool {
|
||||
return bi.Exists && bi.BlockStatus != StatusInvalid && bi.BlockStatus != StatusHeaderOnly
|
||||
}
|
||||
|
||||
// Clone returns a clone of BlockInfo
|
||||
func (bi *BlockInfo) Clone() *BlockInfo {
|
||||
return &BlockInfo{
|
||||
|
||||
@@ -5,21 +5,20 @@ type Consensus interface {
|
||||
Init(skipAddingGenesis bool) error
|
||||
BuildBlock(coinbaseData *DomainCoinbaseData, transactions []*DomainTransaction) (*DomainBlock, error)
|
||||
BuildBlockTemplate(coinbaseData *DomainCoinbaseData, transactions []*DomainTransaction) (*DomainBlockTemplate, error)
|
||||
ValidateAndInsertBlock(block *DomainBlock, updateVirtual bool) error
|
||||
ValidateAndInsertBlockWithTrustedData(block *BlockWithTrustedData, validateUTXO bool) error
|
||||
ValidateAndInsertBlock(block *DomainBlock, shouldValidateAgainstUTXO bool) (*VirtualChangeSet, error)
|
||||
ValidateAndInsertBlockWithTrustedData(block *BlockWithTrustedData, validateUTXO bool) (*VirtualChangeSet, error)
|
||||
ValidateTransactionAndPopulateWithConsensusData(transaction *DomainTransaction) error
|
||||
ImportPruningPoints(pruningPoints []BlockHeader) error
|
||||
BuildPruningPointProof() (*PruningPointProof, error)
|
||||
ValidatePruningPointProof(pruningPointProof *PruningPointProof) error
|
||||
ApplyPruningPointProof(pruningPointProof *PruningPointProof) error
|
||||
|
||||
GetBlock(blockHash *DomainHash) (*DomainBlock, bool, error)
|
||||
GetBlock(blockHash *DomainHash) (*DomainBlock, error)
|
||||
GetBlockEvenIfHeaderOnly(blockHash *DomainHash) (*DomainBlock, error)
|
||||
GetBlockHeader(blockHash *DomainHash) (BlockHeader, error)
|
||||
GetBlockInfo(blockHash *DomainHash) (*BlockInfo, error)
|
||||
GetBlockRelations(blockHash *DomainHash) (parents []*DomainHash, children []*DomainHash, err error)
|
||||
GetBlockAcceptanceData(blockHash *DomainHash) (AcceptanceData, error)
|
||||
GetBlocksAcceptanceData(blockHashes []*DomainHash) ([]AcceptanceData, error)
|
||||
|
||||
GetHashesBetween(lowHash, highHash *DomainHash, maxBlocks uint64) (hashes []*DomainHash, actualHighHash *DomainHash, err error)
|
||||
GetAnticone(blockHash, contextHash *DomainHash, maxBlocks uint64) (hashes []*DomainHash, err error)
|
||||
@@ -48,7 +47,7 @@ type Consensus interface {
|
||||
Anticone(blockHash *DomainHash) ([]*DomainHash, error)
|
||||
EstimateNetworkHashesPerSecond(startHash *DomainHash, windowSize int) (uint64, error)
|
||||
PopulateMass(transaction *DomainTransaction)
|
||||
ResolveVirtual(progressReportCallback func(uint64, uint64)) error
|
||||
ResolveVirtual() (*VirtualChangeSet, bool, error)
|
||||
BlockDAAWindowHashes(blockHash *DomainHash) ([]*DomainHash, error)
|
||||
TrustedDataDataDAAHeader(trustedBlockHash, daaBlockHash *DomainHash, daaBlockWindowIndex uint64) (*TrustedDataDataDAAHeader, error)
|
||||
TrustedBlockAssociatedGHOSTDAGDataBlockHashes(blockHash *DomainHash) ([]*DomainHash, error)
|
||||
|
||||
@@ -2,7 +2,6 @@ package externalapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -243,23 +242,6 @@ func (spk *ScriptPublicKey) Equal(other *ScriptPublicKey) bool {
|
||||
return bytes.Equal(spk.Script, other.Script)
|
||||
}
|
||||
|
||||
// String stringifies a ScriptPublicKey.
|
||||
func (spk *ScriptPublicKey) String() string {
|
||||
var versionBytes = make([]byte, 2) // uint16
|
||||
binary.LittleEndian.PutUint16(versionBytes, spk.Version)
|
||||
versionString := string(versionBytes)
|
||||
scriptString := string(spk.Script)
|
||||
return versionString + scriptString
|
||||
}
|
||||
|
||||
// NewScriptPublicKeyFromString converts the given string to a scriptPublicKey
|
||||
func NewScriptPublicKeyFromString(ScriptPublicKeyString string) *ScriptPublicKey {
|
||||
bytes := []byte(ScriptPublicKeyString)
|
||||
version := binary.LittleEndian.Uint16(bytes[:2])
|
||||
script := bytes[2:]
|
||||
return &ScriptPublicKey{Script: script, Version: version}
|
||||
}
|
||||
|
||||
// DomainTransactionOutput represents a Kaspad transaction output
|
||||
type DomainTransactionOutput struct {
|
||||
Value uint64
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package externalapi
|
||||
|
||||
// UTXOEntry houses details about an individual transaction output in a utxo
|
||||
// set such as whether or not it was contained in a coinbase tx, the daa
|
||||
// set such as whether or not it was contained in a coinbase tx, the blue
|
||||
// score of the block that accepts the tx, its public key script, and how
|
||||
// much it pays.
|
||||
type UTXOEntry interface {
|
||||
Amount() uint64 // Utxo amount in Sompis
|
||||
Amount() uint64
|
||||
ScriptPublicKey() *ScriptPublicKey // The public key script for the output.
|
||||
BlockDAAScore() uint64 // Daa score of the block accepting the tx.
|
||||
BlockDAAScore() uint64 // Blue score of the block accepting the tx.
|
||||
IsCoinbase() bool
|
||||
Equal(other UTXOEntry) bool
|
||||
}
|
||||
|
||||
@@ -7,4 +7,5 @@ type MergeDepthManager interface {
|
||||
CheckBoundedMergeDepth(stagingArea *StagingArea, blockHash *externalapi.DomainHash, isBlockWithTrustedData bool) error
|
||||
NonBoundedMergeDepthViolatingBlues(stagingArea *StagingArea, blockHash, mergeDepthRoot *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
|
||||
VirtualMergeDepthRoot(stagingArea *StagingArea) (*externalapi.DomainHash, error)
|
||||
MergeDepthRoot(stagingArea *StagingArea, blockHash *externalapi.DomainHash, isBlockWithTrustedData bool) (*externalapi.DomainHash, error)
|
||||
}
|
||||
|
||||
@@ -47,9 +47,6 @@ type TestConsensus interface {
|
||||
|
||||
AddUTXOInvalidBlock(parentHashes []*externalapi.DomainHash) (*externalapi.DomainHash,
|
||||
*externalapi.VirtualChangeSet, error)
|
||||
UpdatePruningPointByVirtual() error
|
||||
|
||||
ResolveVirtualWithMaxParam(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error)
|
||||
|
||||
MineJSON(r io.Reader, blockType MineJSONBlockType) (tips []*externalapi.DomainHash, err error)
|
||||
ToJSON(w io.Writer) error
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/consensusstatemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/multiset"
|
||||
@@ -164,7 +165,12 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea,
|
||||
|
||||
if reversalData != nil {
|
||||
err = bp.consensusStateManager.ReverseUTXODiffs(blockHash, reversalData)
|
||||
if err != nil {
|
||||
// It's still not known what causes this error, but we can ignore it and not reverse the UTXO diffs
|
||||
// and harm performance in some cases.
|
||||
// TODO: Investigate why this error happens in the first place, and remove the workaround.
|
||||
if errors.Is(err, consensusstatemanager.ErrReverseUTXODiffsUTXODiffChildNotFound) {
|
||||
log.Errorf("Could not reverse UTXO diffs while resolving virtual: %s", err)
|
||||
} else if err != nil {
|
||||
return nil, externalapi.StatusInvalid, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ func TestBlockStatus(t *testing.T) {
|
||||
disqualifiedBlock.Header.PruningPoint(),
|
||||
)
|
||||
|
||||
err = tc.ValidateAndInsertBlock(disqualifiedBlock, true)
|
||||
_, err = tc.ValidateAndInsertBlock(disqualifiedBlock, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlock: %+v", err)
|
||||
}
|
||||
@@ -106,7 +106,7 @@ func TestBlockStatus(t *testing.T) {
|
||||
disqualifiedBlock.Header.PruningPoint(),
|
||||
)
|
||||
|
||||
err = tc.ValidateAndInsertBlock(invalidBlock, true)
|
||||
_, err = tc.ValidateAndInsertBlock(invalidBlock, true)
|
||||
if err == nil {
|
||||
t.Fatalf("block is expected to be invalid")
|
||||
}
|
||||
@@ -139,11 +139,11 @@ func TestValidateAndInsertErrors(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
err = tc.ValidateAndInsertBlock(blockWithStatusInvalid, true)
|
||||
_, err = tc.ValidateAndInsertBlock(blockWithStatusInvalid, true)
|
||||
if err == nil {
|
||||
t.Fatalf("Test ValidateAndInsertBlock: Expected an error, because the block is invalid.")
|
||||
}
|
||||
err = tc.ValidateAndInsertBlock(blockWithStatusInvalid, true)
|
||||
_, err = tc.ValidateAndInsertBlock(blockWithStatusInvalid, true)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrKnownInvalid) {
|
||||
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrKnownInvalid, err)
|
||||
}
|
||||
@@ -155,12 +155,12 @@ func TestValidateAndInsertErrors(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
err = tc.ValidateAndInsertBlock(block, true)
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlock: %+v", err)
|
||||
}
|
||||
// resend the same block.
|
||||
err = tc.ValidateAndInsertBlock(block, true)
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrDuplicateBlock) {
|
||||
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrDuplicateBlock, err)
|
||||
}
|
||||
@@ -173,12 +173,12 @@ func TestValidateAndInsertErrors(t *testing.T) {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
onlyHeader.Transactions = []*externalapi.DomainTransaction{}
|
||||
err = tc.ValidateAndInsertBlock(onlyHeader, true)
|
||||
_, err = tc.ValidateAndInsertBlock(onlyHeader, true)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
// resend the same header.
|
||||
err = tc.ValidateAndInsertBlock(onlyHeader, true)
|
||||
_, err = tc.ValidateAndInsertBlock(onlyHeader, true)
|
||||
if err == nil || !errors.Is(err, ruleerrors.ErrDuplicateBlock) {
|
||||
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrDuplicateBlock, err)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func addBlock(tc testapi.TestConsensus, parentHashes []*externalapi.DomainHash,
|
||||
}
|
||||
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
err = tc.ValidateAndInsertBlock(block, true)
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlock: %+v", err)
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
consensusConfig.K = 0
|
||||
consensusConfig.PruningProofM = 1
|
||||
|
||||
syncConsensuses := func(tcSyncerRef, tcSynceeRef *testapi.TestConsensus, updatePruningPointJustAfterImportingPruningPoint bool) {
|
||||
syncConsensuses := func(tcSyncerRef, tcSynceeRef *testapi.TestConsensus) {
|
||||
tcSyncer, tcSyncee := *tcSyncerRef, *tcSynceeRef
|
||||
pruningPointProof, err := tcSyncer.BuildPruningPointProof()
|
||||
if err != nil {
|
||||
@@ -93,7 +93,7 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, blockHash := range pruningPointAndItsAnticone {
|
||||
block, _, err := tcSyncer.GetBlock(blockHash)
|
||||
block, err := tcSyncer.GetBlock(blockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
@@ -133,7 +133,7 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
err = synceeStaging.ValidateAndInsertBlockWithTrustedData(blockWithTrustedData, false)
|
||||
_, err = synceeStaging.ValidateAndInsertBlockWithTrustedData(blockWithTrustedData, false)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlockWithTrustedData: %+v", err)
|
||||
}
|
||||
@@ -169,7 +169,7 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
t.Fatalf("GetBlockHeader: %+v", err)
|
||||
}
|
||||
|
||||
err = synceeStaging.ValidateAndInsertBlock(&externalapi.DomainBlock{Header: header}, false)
|
||||
_, err = synceeStaging.ValidateAndInsertBlock(&externalapi.DomainBlock{Header: header}, false)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlock %d: %+v", i, err)
|
||||
}
|
||||
@@ -236,13 +236,6 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
t.Fatalf("ValidateAndInsertImportedPruningPoint: %+v", err)
|
||||
}
|
||||
|
||||
if updatePruningPointJustAfterImportingPruningPoint {
|
||||
err = synceeStaging.UpdatePruningPointByVirtual()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
emptyCoinbase := &externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
@@ -268,12 +261,12 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, blocksHash := range missingBlockHashes {
|
||||
block, _, err := tcSyncer.GetBlock(blocksHash)
|
||||
block, err := tcSyncer.GetBlock(blocksHash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
|
||||
err = synceeStaging.ValidateAndInsertBlock(block, true)
|
||||
_, err = synceeStaging.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlock: %+v", err)
|
||||
}
|
||||
@@ -294,12 +287,12 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
}
|
||||
|
||||
tipHash := addBlock(tcSyncer, syncerTips, t)
|
||||
tip, _, err := tcSyncer.GetBlock(tipHash)
|
||||
tip, err := tcSyncer.GetBlock(tipHash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
|
||||
err = synceeStaging.ValidateAndInsertBlock(tip, true)
|
||||
_, err = synceeStaging.ValidateAndInsertBlock(tip, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlock: %+v", err)
|
||||
}
|
||||
@@ -341,12 +334,12 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
tipHash := consensusConfig.GenesisHash
|
||||
for i := 0; i < numSharedBlocks; i++ {
|
||||
tipHash = addBlock(tcSyncer, []*externalapi.DomainHash{tipHash}, t)
|
||||
block, _, err := tcSyncer.GetBlock(tipHash)
|
||||
block, err := tcSyncer.GetBlock(tipHash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
|
||||
err = tcSyncee1.ValidateAndInsertBlock(block, true)
|
||||
_, err = tcSyncee1.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlock: %+v", err)
|
||||
}
|
||||
@@ -393,7 +386,7 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
}
|
||||
|
||||
tcSyncee1Ref := &tcSyncee1
|
||||
syncConsensuses(&tcSyncer, tcSyncee1Ref, false)
|
||||
syncConsensuses(&tcSyncer, tcSyncee1Ref)
|
||||
|
||||
// Test a situation where a consensus with pruned headers syncs another fresh consensus.
|
||||
tcSyncee2, teardownSyncee2, err := factory.NewTestConsensus(consensusConfig, "TestValidateAndInsertPruningPointSyncee2")
|
||||
@@ -402,17 +395,7 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
}
|
||||
defer teardownSyncee2(false)
|
||||
|
||||
syncConsensuses(tcSyncee1Ref, &tcSyncee2, false)
|
||||
|
||||
// Check the regular sync but try to update the pruning point after the pruning point was imported. It tests a situation where the node
|
||||
// was restarted before the virtual was resolved and then it calls UpdatePruningPointByVirtual on init.
|
||||
tcSyncee3, teardownSyncee3, err := factory.NewTestConsensus(consensusConfig, "TestValidateAndInsertPruningPointSyncee3")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tcSyncee1: %+v", err)
|
||||
}
|
||||
defer teardownSyncee3(false)
|
||||
|
||||
syncConsensuses(&tcSyncer, &tcSyncee3, true)
|
||||
syncConsensuses(tcSyncee1Ref, &tcSyncee2)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -478,7 +461,7 @@ func TestGetPruningPointUTXOs(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error building block above genesis: %+v", err)
|
||||
}
|
||||
err = testConsensus.ValidateAndInsertBlock(blockAboveGenesis, true)
|
||||
_, err = testConsensus.ValidateAndInsertBlock(blockAboveGenesis, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Error validating and inserting block above genesis: %+v", err)
|
||||
}
|
||||
@@ -490,7 +473,7 @@ func TestGetPruningPointUTXOs(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error building block with spendable coinbase: %+v", err)
|
||||
}
|
||||
err = testConsensus.ValidateAndInsertBlock(blockWithSpendableCoinbase, true)
|
||||
_, err = testConsensus.ValidateAndInsertBlock(blockWithSpendableCoinbase, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Error validating and inserting block with spendable coinbase: %+v", err)
|
||||
}
|
||||
@@ -529,7 +512,7 @@ func TestGetPruningPointUTXOs(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error building including block: %+v", err)
|
||||
}
|
||||
err = testConsensus.ValidateAndInsertBlock(includingBlock, true)
|
||||
_, err = testConsensus.ValidateAndInsertBlock(includingBlock, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Error validating and inserting including block: %+v", err)
|
||||
}
|
||||
@@ -540,7 +523,7 @@ func TestGetPruningPointUTXOs(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error building block: %+v", err)
|
||||
}
|
||||
err = testConsensus.ValidateAndInsertBlock(block, true)
|
||||
_, err = testConsensus.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Error validating and inserting block: %+v", err)
|
||||
}
|
||||
@@ -636,7 +619,7 @@ func BenchmarkGetPruningPointUTXOs(b *testing.B) {
|
||||
if err != nil {
|
||||
b.Fatalf("Error building block with spendable coinbase: %+v", err)
|
||||
}
|
||||
err = testConsensus.ValidateAndInsertBlock(blockWithSpendableCoinbase, true)
|
||||
_, err = testConsensus.ValidateAndInsertBlock(blockWithSpendableCoinbase, true)
|
||||
if err != nil {
|
||||
b.Fatalf("Error validating and inserting block with spendable coinbase: %+v", err)
|
||||
}
|
||||
@@ -674,7 +657,7 @@ func BenchmarkGetPruningPointUTXOs(b *testing.B) {
|
||||
if err != nil {
|
||||
b.Fatalf("Error building block: %+v", err)
|
||||
}
|
||||
err = testConsensus.ValidateAndInsertBlock(block, true)
|
||||
_, err = testConsensus.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
b.Fatalf("Error validating and inserting block: %+v", err)
|
||||
}
|
||||
@@ -694,7 +677,7 @@ func BenchmarkGetPruningPointUTXOs(b *testing.B) {
|
||||
if err != nil {
|
||||
b.Fatalf("Error building block: %+v", err)
|
||||
}
|
||||
err = testConsensus.ValidateAndInsertBlock(block, true)
|
||||
_, err = testConsensus.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
b.Fatalf("Error validating and inserting block: %+v", err)
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func TestCheckBlockIsNotPruned(t *testing.T) {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
beforePruningBlock, _, err := tc.GetBlock(tipHash)
|
||||
beforePruningBlock, err := tc.GetBlock(tipHash)
|
||||
if err != nil {
|
||||
t.Fatalf("beforePruningBlock: %+v", err)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func TestCheckBlockIsNotPruned(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
err = tc.ValidateAndInsertBlock(beforePruningBlock, true)
|
||||
_, err = tc.ValidateAndInsertBlock(beforePruningBlock, true)
|
||||
if !errors.Is(err, ruleerrors.ErrPrunedBlock) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
@@ -117,7 +117,7 @@ func TestCheckParentBlockBodiesExist(t *testing.T) {
|
||||
}
|
||||
|
||||
// Add only the header of anticonePruningBlock
|
||||
err = tc.ValidateAndInsertBlock(&externalapi.DomainBlock{
|
||||
_, err = tc.ValidateAndInsertBlock(&externalapi.DomainBlock{
|
||||
Header: anticonePruningBlock.Header,
|
||||
Transactions: nil,
|
||||
}, true)
|
||||
@@ -143,7 +143,7 @@ func TestCheckParentBlockBodiesExist(t *testing.T) {
|
||||
|
||||
// Add anticonePruningBlock's body and check that it's valid to point to
|
||||
// a header only block in the past of the pruning point.
|
||||
err = tc.ValidateAndInsertBlock(anticonePruningBlock, true)
|
||||
_, err = tc.ValidateAndInsertBlock(anticonePruningBlock, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlock: %+v", err)
|
||||
}
|
||||
@@ -189,7 +189,7 @@ func TestIsFinalizedTransaction(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting block: %+v", err)
|
||||
}
|
||||
err = tc.ValidateAndInsertBlock(block, true)
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Error Inserting block: %+v", err)
|
||||
}
|
||||
@@ -199,7 +199,7 @@ func TestIsFinalizedTransaction(t *testing.T) {
|
||||
t.Fatalf("Error getting block DAA score : %+v", err)
|
||||
}
|
||||
blockParents := block.Header.DirectParents()
|
||||
parentToSpend, _, err := tc.GetBlock(blockParents[0])
|
||||
parentToSpend, err := tc.GetBlock(blockParents[0])
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting block1: %+v", err)
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ func TestChainedTransactions(t *testing.T) {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
block1, _, err := tc.GetBlock(block1Hash)
|
||||
block1, err := tc.GetBlock(block1Hash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting block1: %+v", err)
|
||||
}
|
||||
@@ -97,7 +97,7 @@ func TestChainedTransactions(t *testing.T) {
|
||||
t.Fatalf("unexpected error %+v", err)
|
||||
}
|
||||
|
||||
block2, _, err := tc.GetBlock(block2Hash)
|
||||
block2, err := tc.GetBlock(block2Hash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting block2: %+v", err)
|
||||
}
|
||||
@@ -169,10 +169,10 @@ var unOrderedParentsBlock = externalapi.DomainBlock{
|
||||
}),
|
||||
}},
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0x7e, 0xe2, 0x10, 0x4e, 0x21, 0x2f, 0x2a, 0xb1,
|
||||
0x7d, 0x22, 0xf5, 0xe8, 0xa0, 0x98, 0xef, 0x53,
|
||||
0x83, 0xae, 0x59, 0x1f, 0x83, 0xf3, 0x78, 0x5d,
|
||||
0x30, 0xae, 0x3e, 0xb3, 0x06, 0x08, 0x6f, 0x79,
|
||||
0x31, 0x33, 0x37, 0x72, 0x5c, 0xde, 0x1c, 0xdf,
|
||||
0xf5, 0x9f, 0xde, 0x16, 0x74, 0xbf, 0x0c, 0x64,
|
||||
0x37, 0x40, 0x49, 0xdf, 0x02, 0x05, 0xca, 0x6d,
|
||||
0x52, 0x23, 0x6f, 0xc2, 0x2b, 0xec, 0xad, 0x42,
|
||||
}),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0x80, 0xf7, 0x00, 0xe3, 0x16, 0x3d, 0x04, 0x95,
|
||||
@@ -202,7 +202,20 @@ var unOrderedParentsBlock = externalapi.DomainBlock{
|
||||
Transactions: []*externalapi.DomainTransaction{
|
||||
{
|
||||
Version: 0,
|
||||
Inputs: nil,
|
||||
Inputs: []*externalapi.DomainTransactionInput{
|
||||
{
|
||||
PreviousOutpoint: externalapi.DomainOutpoint{
|
||||
TransactionID: *externalapi.NewDomainTransactionIDFromByteArray(&[externalapi.DomainHashSize]byte{}),
|
||||
Index: 0xffffffff,
|
||||
},
|
||||
SignatureScript: []byte{
|
||||
0x02, 0x10, 0x27, 0x08, 0xac, 0x29, 0x2f, 0x2f,
|
||||
0xcf, 0x70, 0xb0, 0x7e, 0x0b, 0x2f, 0x50, 0x32,
|
||||
0x53, 0x48, 0x2f, 0x62, 0x74, 0x63, 0x64, 0x2f,
|
||||
},
|
||||
Sequence: math.MaxUint64,
|
||||
},
|
||||
},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{
|
||||
{
|
||||
Value: 0x12a05f200, // 5000000000
|
||||
@@ -433,10 +446,10 @@ var exampleValidBlock = externalapi.DomainBlock{
|
||||
}),
|
||||
}},
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0x46, 0xec, 0xf4, 0x5b, 0xe3, 0xba, 0xca, 0x34,
|
||||
0x9d, 0xfe, 0x8a, 0x78, 0xde, 0xaf, 0x05, 0x3b,
|
||||
0x0a, 0xa6, 0xd5, 0x38, 0x97, 0x4d, 0xa5, 0x0f,
|
||||
0xd6, 0xef, 0xb4, 0xd2, 0x66, 0xbc, 0x8d, 0x21,
|
||||
0x86, 0x8b, 0x73, 0xcd, 0x20, 0x51, 0x23, 0x60,
|
||||
0xea, 0x62, 0x99, 0x9b, 0x87, 0xf6, 0xdd, 0x8d,
|
||||
0xa4, 0x0b, 0xd7, 0xcf, 0xc6, 0x32, 0x38, 0xee,
|
||||
0xd9, 0x68, 0x72, 0x1f, 0xa2, 0x51, 0xe4, 0x28,
|
||||
}),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0x8a, 0xb7, 0xd6, 0x73, 0x1b, 0xe6, 0xc5, 0xd3,
|
||||
@@ -461,7 +474,21 @@ var exampleValidBlock = externalapi.DomainBlock{
|
||||
Transactions: []*externalapi.DomainTransaction{
|
||||
{
|
||||
Version: 0,
|
||||
Inputs: nil,
|
||||
Inputs: []*externalapi.DomainTransactionInput{
|
||||
{
|
||||
PreviousOutpoint: externalapi.DomainOutpoint{
|
||||
TransactionID: *externalapi.NewDomainTransactionIDFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0x9b, 0x22, 0x59, 0x44, 0x66, 0xf0, 0xbe, 0x50,
|
||||
0x7c, 0x1c, 0x8a, 0xf6, 0x06, 0x27, 0xe6, 0x33,
|
||||
0x38, 0x7e, 0xd1, 0xd5, 0x8c, 0x42, 0x59, 0x1a,
|
||||
0x31, 0xac, 0x9a, 0xa6, 0x2e, 0xd5, 0x2b, 0x0f,
|
||||
}),
|
||||
Index: 0xffffffff,
|
||||
},
|
||||
SignatureScript: nil,
|
||||
Sequence: math.MaxUint64,
|
||||
},
|
||||
},
|
||||
Outputs: []*externalapi.DomainTransactionOutput{
|
||||
{
|
||||
Value: 0x12a05f200, // 5000000000
|
||||
@@ -724,10 +751,10 @@ var blockWithWrongTxOrder = externalapi.DomainBlock{
|
||||
}),
|
||||
}},
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0xd5, 0xd2, 0x32, 0xe4, 0xbe, 0x9c, 0x33, 0xbd,
|
||||
0xf1, 0x0a, 0xd2, 0x9d, 0x0c, 0xbd, 0xe5, 0xae,
|
||||
0xcb, 0x1a, 0xf9, 0x5a, 0x3e, 0xfb, 0xf3, 0xc7,
|
||||
0x2b, 0x4d, 0x10, 0xa6, 0xbd, 0x5f, 0x07, 0xe7,
|
||||
0x7b, 0x25, 0x8b, 0xfa, 0xfb, 0x49, 0xe4, 0x94,
|
||||
0x48, 0x2c, 0xf9, 0x74, 0xdd, 0xad, 0x9d, 0x6f,
|
||||
0x98, 0x8f, 0xfb, 0x01, 0x9d, 0x49, 0x29, 0xbe,
|
||||
0x3c, 0xec, 0x90, 0xfe, 0xa5, 0x0c, 0xaf, 0x6b,
|
||||
}),
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
0xa0, 0x69, 0x2d, 0x16, 0xb5, 0xd7, 0xe4, 0xf3,
|
||||
@@ -1025,14 +1052,14 @@ func CheckBlockHashMerkleRoot(t *testing.T, tc testapi.TestConsensus, consensusC
|
||||
blockWithInvalidMerkleRoot := block.Clone()
|
||||
blockWithInvalidMerkleRoot.Transactions[0].Version += 1
|
||||
|
||||
err = tc.ValidateAndInsertBlock(blockWithInvalidMerkleRoot, true)
|
||||
_, err = tc.ValidateAndInsertBlock(blockWithInvalidMerkleRoot, true)
|
||||
if !errors.Is(err, ruleerrors.ErrBadMerkleRoot) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
|
||||
// Check that a block with invalid merkle root is not marked as invalid
|
||||
// and can be re-added with the right transactions.
|
||||
err = tc.ValidateAndInsertBlock(block, true)
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateAndInsertBlock: %+v", err)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestValidateMedianTime(t *testing.T) {
|
||||
newHeader := block.Header.ToMutable()
|
||||
newHeader.SetTimeInMilliseconds(blockTime)
|
||||
block.Header = newHeader.ToImmutable()
|
||||
err = tc.ValidateAndInsertBlock(block, true)
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if !errors.Is(err, expectedErr) {
|
||||
t.Fatalf("expected error %s but got %+v", expectedErr, err)
|
||||
}
|
||||
@@ -127,7 +127,7 @@ func TestCheckParentsIncest(t *testing.T) {
|
||||
Transactions: nil,
|
||||
}
|
||||
|
||||
err = tc.ValidateAndInsertBlock(directParentsRelationBlock, true)
|
||||
_, err = tc.ValidateAndInsertBlock(directParentsRelationBlock, true)
|
||||
if !errors.Is(err, ruleerrors.ErrInvalidParentsRelation) {
|
||||
t.Fatalf("unexpected error %+v", err)
|
||||
}
|
||||
@@ -150,7 +150,7 @@ func TestCheckParentsIncest(t *testing.T) {
|
||||
Transactions: nil,
|
||||
}
|
||||
|
||||
err = tc.ValidateAndInsertBlock(indirectParentsRelationBlock, true)
|
||||
_, err = tc.ValidateAndInsertBlock(indirectParentsRelationBlock, true)
|
||||
if !errors.Is(err, ruleerrors.ErrInvalidParentsRelation) {
|
||||
t.Fatalf("unexpected error %+v", err)
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func CheckBlockVersion(t *testing.T, tc testapi.TestConsensus, consensusConfig *
|
||||
block.Header.PruningPoint(),
|
||||
)
|
||||
|
||||
err = tc.ValidateAndInsertBlock(block, true)
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if !errors.Is(err, ruleerrors.ErrWrongBlockVersion) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
@@ -118,7 +118,7 @@ func CheckBlockTimestampInIsolation(t *testing.T, tc testapi.TestConsensus, cfg
|
||||
block.Header.PruningPoint(),
|
||||
)
|
||||
|
||||
err = tc.ValidateAndInsertBlock(block, true)
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if !errors.Is(err, ruleerrors.ErrTimeTooMuchInTheFuture) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestPOW(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
invalidBlockWrongPOW = solveBlockWithWrongPOW(invalidBlockWrongPOW)
|
||||
err = tc.ValidateAndInsertBlock(invalidBlockWrongPOW, true)
|
||||
_, err = tc.ValidateAndInsertBlock(invalidBlockWrongPOW, true)
|
||||
if !errors.Is(err, ruleerrors.ErrInvalidPoW) {
|
||||
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrInvalidPoW, err)
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func TestPOW(t *testing.T) {
|
||||
abovePowMaxBlock.Header.PruningPoint(),
|
||||
)
|
||||
|
||||
err = tc.ValidateAndInsertBlock(abovePowMaxBlock, true)
|
||||
_, err = tc.ValidateAndInsertBlock(abovePowMaxBlock, true)
|
||||
if !errors.Is(err, ruleerrors.ErrTargetTooHigh) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
@@ -90,7 +90,7 @@ func TestPOW(t *testing.T) {
|
||||
negativeTargetBlock.Header.PruningPoint(),
|
||||
)
|
||||
|
||||
err = tc.ValidateAndInsertBlock(negativeTargetBlock, true)
|
||||
_, err = tc.ValidateAndInsertBlock(negativeTargetBlock, true)
|
||||
if !errors.Is(err, ruleerrors.ErrNegativeTarget) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func TestPOW(t *testing.T) {
|
||||
// Difficulty is too high on mainnet to actually mine.
|
||||
if consensusConfig.Name != "kaspa-mainnet" {
|
||||
mining.SolveBlock(validBlock, random)
|
||||
err = tc.ValidateAndInsertBlock(validBlock, true)
|
||||
_, err = tc.ValidateAndInsertBlock(validBlock, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -161,7 +161,7 @@ func TestCheckParentHeadersExist(t *testing.T) {
|
||||
orphanBlock.Header.PruningPoint(),
|
||||
)
|
||||
|
||||
err = tc.ValidateAndInsertBlock(orphanBlock, true)
|
||||
_, err = tc.ValidateAndInsertBlock(orphanBlock, true)
|
||||
errMissingParents := &ruleerrors.ErrMissingParents{}
|
||||
if !errors.As(err, errMissingParents) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
@@ -193,7 +193,7 @@ func TestCheckParentHeadersExist(t *testing.T) {
|
||||
orphanBlock.Header.PruningPoint(),
|
||||
)
|
||||
|
||||
err = tc.ValidateAndInsertBlock(invalidBlock, true)
|
||||
_, err = tc.ValidateAndInsertBlock(invalidBlock, true)
|
||||
if !errors.Is(err, ruleerrors.ErrTransactionVersionIsUnknown) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
@@ -220,7 +220,7 @@ func TestCheckParentHeadersExist(t *testing.T) {
|
||||
invalidBlockChild.Header.PruningPoint(),
|
||||
)
|
||||
|
||||
err = tc.ValidateAndInsertBlock(invalidBlockChild, true)
|
||||
_, err = tc.ValidateAndInsertBlock(invalidBlockChild, true)
|
||||
if !errors.Is(err, ruleerrors.ErrInvalidAncestorBlock) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
@@ -284,7 +284,7 @@ func TestCheckPruningPointViolation(t *testing.T) {
|
||||
blockWithPruningViolation.Header.PruningPoint(),
|
||||
)
|
||||
|
||||
err = tc.ValidateAndInsertBlock(blockWithPruningViolation, true)
|
||||
_, err = tc.ValidateAndInsertBlock(blockWithPruningViolation, true)
|
||||
if !errors.Is(err, ruleerrors.ErrPruningPointViolation) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package coinbasemanager_test
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExtractCoinbaseDataBlueScoreAndSubsidy(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestBlockStatus")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
scriptPublicKeyVersion uint16
|
||||
}{
|
||||
{
|
||||
name: "below 255",
|
||||
scriptPublicKeyVersion: 100,
|
||||
},
|
||||
{
|
||||
name: "above 255",
|
||||
scriptPublicKeyVersion: 300,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
coinbaseTx, _, err := tc.CoinbaseManager().ExpectedCoinbaseTransaction(model.NewStagingArea(), model.VirtualBlockHash, &externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
Version: test.scriptPublicKeyVersion,
|
||||
},
|
||||
ExtraData: nil,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, cbData, _, err := tc.CoinbaseManager().ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if cbData.ScriptPublicKey.Version != test.scriptPublicKeyVersion {
|
||||
t.Fatalf("test %s post HF expected %d but got %d", test.name, test.scriptPublicKeyVersion, cbData.ScriptPublicKey.Version)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func (c *coinbaseManager) serializeCoinbasePayload(blueScore uint64,
|
||||
binary.LittleEndian.PutUint64(payload[:uint64Len], blueScore)
|
||||
binary.LittleEndian.PutUint64(payload[uint64Len:], subsidy)
|
||||
|
||||
binary.LittleEndian.PutUint16(payload[uint64Len+lengthOfSubsidy:], coinbaseData.ScriptPublicKey.Version)
|
||||
payload[uint64Len+lengthOfSubsidy] = uint8(coinbaseData.ScriptPublicKey.Version)
|
||||
payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey] = uint8(len(coinbaseData.ScriptPublicKey.Script))
|
||||
copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength:], coinbaseData.ScriptPublicKey.Script)
|
||||
copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength+scriptLengthOfScriptPubKey:], coinbaseData.ExtraData)
|
||||
@@ -52,7 +52,7 @@ func ModifyCoinbasePayload(payload []byte, coinbaseData *externalapi.DomainCoinb
|
||||
payload = newPayload
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint16(payload[uint64Len+lengthOfSubsidy:uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey], coinbaseData.ScriptPublicKey.Version)
|
||||
payload[uint64Len+lengthOfSubsidy] = uint8(coinbaseData.ScriptPublicKey.Version)
|
||||
payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey] = uint8(len(coinbaseData.ScriptPublicKey.Script))
|
||||
copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength:], coinbaseData.ScriptPublicKey.Script)
|
||||
copy(payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey+lengthOfScriptPubKeyLength+scriptLengthOfScriptPubKey:], coinbaseData.ExtraData)
|
||||
@@ -73,8 +73,7 @@ func (c *coinbaseManager) ExtractCoinbaseDataBlueScoreAndSubsidy(coinbaseTx *ext
|
||||
blueScore = binary.LittleEndian.Uint64(coinbaseTx.Payload[:uint64Len])
|
||||
subsidy = binary.LittleEndian.Uint64(coinbaseTx.Payload[uint64Len:])
|
||||
|
||||
scriptPubKeyVersion := binary.LittleEndian.Uint16(coinbaseTx.Payload[uint64Len+lengthOfSubsidy : uint64Len+lengthOfSubsidy+uint16Len])
|
||||
|
||||
scriptPubKeyVersion := uint16(coinbaseTx.Payload[uint64Len+lengthOfSubsidy])
|
||||
scriptPubKeyScriptLength := coinbaseTx.Payload[uint64Len+lengthOfSubsidy+lengthOfVersionScriptPubKey]
|
||||
|
||||
if scriptPubKeyScriptLength > c.coinbasePayloadScriptPublicKeyMaxLength {
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestUTXOCommitment(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating block B: %+v", err)
|
||||
}
|
||||
blockB, _, err := consensus.GetBlock(blockBHash)
|
||||
blockB, err := consensus.GetBlock(blockBHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting block B: %+v", err)
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func TestUTXOCommitment(t *testing.T) {
|
||||
}
|
||||
|
||||
func checkBlockUTXOCommitment(t *testing.T, consensus testapi.TestConsensus, blockHash *externalapi.DomainHash, blockName string) {
|
||||
block, _, err := consensus.GetBlock(blockHash)
|
||||
block, err := consensus.GetBlock(blockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting block %s: %+v", blockName, err)
|
||||
}
|
||||
|
||||
@@ -72,8 +72,7 @@ func New(
|
||||
maxBlockParents: maxBlockParents,
|
||||
mergeSetSizeLimit: mergeSetSizeLimit,
|
||||
genesisHash: genesisHash,
|
||||
|
||||
databaseContext: databaseContext,
|
||||
databaseContext: databaseContext,
|
||||
|
||||
ghostdagManager: ghostdagManager,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
|
||||
@@ -9,18 +9,19 @@ import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// tipsInDecreasingGHOSTDAGParentSelectionOrder returns the current DAG tips in decreasing parent selection order.
|
||||
// This means that the first tip in the resulting list would be the GHOSTDAG selected parent, and if removed from the list,
|
||||
// the second tip would be the selected parent, and so on.
|
||||
func (csm *consensusStateManager) tipsInDecreasingGHOSTDAGParentSelectionOrder(stagingArea *model.StagingArea) ([]*externalapi.DomainHash, error) {
|
||||
tips, err := csm.consensusStateStore.Tips(stagingArea, csm.databaseContext)
|
||||
func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "csm.ResolveVirtual")
|
||||
defer onEnd()
|
||||
|
||||
readStagingArea := model.NewStagingArea()
|
||||
tips, err := csm.consensusStateStore.Tips(readStagingArea, csm.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
var sortErr error
|
||||
sort.Slice(tips, func(i, j int) bool {
|
||||
selectedParent, err := csm.ghostdagManager.ChooseSelectedParent(stagingArea, tips[i], tips[j])
|
||||
selectedParent, err := csm.ghostdagManager.ChooseSelectedParent(readStagingArea, tips[i], tips[j])
|
||||
if err != nil {
|
||||
sortErr = err
|
||||
return false
|
||||
@@ -29,22 +30,16 @@ func (csm *consensusStateManager) tipsInDecreasingGHOSTDAGParentSelectionOrder(s
|
||||
return selectedParent.Equal(tips[i])
|
||||
})
|
||||
if sortErr != nil {
|
||||
return nil, sortErr
|
||||
}
|
||||
return tips, nil
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) findNextPendingTip(stagingArea *model.StagingArea) (*externalapi.DomainHash, externalapi.BlockStatus, error) {
|
||||
orderedTips, err := csm.tipsInDecreasingGHOSTDAGParentSelectionOrder(stagingArea)
|
||||
if err != nil {
|
||||
return nil, externalapi.StatusInvalid, err
|
||||
return nil, false, sortErr
|
||||
}
|
||||
|
||||
for _, tip := range orderedTips {
|
||||
var selectedTip *externalapi.DomainHash
|
||||
isCompletelyResolved := true
|
||||
for _, tip := range tips {
|
||||
log.Debugf("Resolving tip %s", tip)
|
||||
isViolatingFinality, shouldNotify, err := csm.isViolatingFinality(stagingArea, tip)
|
||||
isViolatingFinality, shouldNotify, err := csm.isViolatingFinality(readStagingArea, tip)
|
||||
if err != nil {
|
||||
return nil, externalapi.StatusInvalid, err
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if isViolatingFinality {
|
||||
@@ -55,147 +50,60 @@ func (csm *consensusStateManager) findNextPendingTip(stagingArea *model.StagingA
|
||||
continue
|
||||
}
|
||||
|
||||
status, err := csm.blockStatusStore.Get(csm.databaseContext, stagingArea, tip)
|
||||
if err != nil {
|
||||
return nil, externalapi.StatusInvalid, err
|
||||
}
|
||||
if status == externalapi.StatusUTXOValid || status == externalapi.StatusUTXOPendingVerification {
|
||||
return tip, status, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, externalapi.StatusInvalid, nil
|
||||
}
|
||||
|
||||
// getGHOSTDAGLowerTips returns the set of tips which are lower in GHOSTDAG parent selection order than `pendingTip`. i.e.,
|
||||
// they can be added to virtual parents but `pendingTip` will remain the virtual selected parent
|
||||
func (csm *consensusStateManager) getGHOSTDAGLowerTips(stagingArea *model.StagingArea, pendingTip *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
tips, err := csm.consensusStateStore.Tips(stagingArea, csm.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lowerTips := []*externalapi.DomainHash{pendingTip}
|
||||
for _, tip := range tips {
|
||||
if tip.Equal(pendingTip) {
|
||||
continue
|
||||
}
|
||||
selectedParent, err := csm.ghostdagManager.ChooseSelectedParent(stagingArea, tip, pendingTip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if selectedParent.Equal(pendingTip) {
|
||||
lowerTips = append(lowerTips, tip)
|
||||
}
|
||||
}
|
||||
return lowerTips, nil
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (*externalapi.VirtualChangeSet, bool, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "csm.ResolveVirtual")
|
||||
defer onEnd()
|
||||
|
||||
// We use a read-only staging area for some read-only actions, to avoid
|
||||
// confusion with the resolve/updateVirtual staging areas below
|
||||
readStagingArea := model.NewStagingArea()
|
||||
|
||||
pendingTip, pendingTipStatus, err := csm.findNextPendingTip(readStagingArea)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if pendingTip == nil {
|
||||
log.Warnf("None of the DAG tips are valid")
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
previousVirtualSelectedParent, err := csm.virtualSelectedParent(readStagingArea)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if pendingTipStatus == externalapi.StatusUTXOValid && previousVirtualSelectedParent.Equal(pendingTip) {
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
// Resolve a chunk from the pending chain
|
||||
resolveStagingArea := model.NewStagingArea()
|
||||
unverifiedBlocks, err := csm.getUnverifiedChainBlocks(resolveStagingArea, pendingTip)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// Initially set the resolve processing point to the pending tip
|
||||
processingPoint := pendingTip
|
||||
|
||||
// Too many blocks to verify, so we only process a chunk and return
|
||||
if maxBlocksToResolve != 0 && uint64(len(unverifiedBlocks)) > maxBlocksToResolve {
|
||||
processingPointIndex := uint64(len(unverifiedBlocks)) - maxBlocksToResolve
|
||||
processingPoint = unverifiedBlocks[processingPointIndex]
|
||||
isNewVirtualSelectedParent, err := csm.isNewSelectedTip(readStagingArea, processingPoint, previousVirtualSelectedParent)
|
||||
resolveStagingArea := model.NewStagingArea()
|
||||
unverifiedBlocks, err := csm.getUnverifiedChainBlocks(resolveStagingArea, tip)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// We must find a processing point which wins previous virtual selected parent
|
||||
// even if we process more than `maxBlocksToResolve` for that.
|
||||
// Otherwise, internal UTXO diff logic gets all messed up
|
||||
for !isNewVirtualSelectedParent {
|
||||
if processingPointIndex == 0 {
|
||||
return nil, false, errors.Errorf(
|
||||
"Expecting the pending tip %s to overcome the previous selected parent %s", pendingTip, previousVirtualSelectedParent)
|
||||
}
|
||||
processingPointIndex--
|
||||
processingPoint = unverifiedBlocks[processingPointIndex]
|
||||
isNewVirtualSelectedParent, err = csm.isNewSelectedTip(readStagingArea, processingPoint, previousVirtualSelectedParent)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
resolveTip := tip
|
||||
hasMoreUnverifiedThanMax := maxBlocksToResolve != 0 && uint64(len(unverifiedBlocks)) > maxBlocksToResolve
|
||||
if hasMoreUnverifiedThanMax {
|
||||
resolveTip = unverifiedBlocks[uint64(len(unverifiedBlocks))-maxBlocksToResolve]
|
||||
log.Debugf("Has more than %d blocks to resolve. Changing the resolve tip to %s", maxBlocksToResolve, resolveTip)
|
||||
}
|
||||
log.Debugf("Has more than %d blocks to resolve. Setting the resolve processing point to %s", maxBlocksToResolve, processingPoint)
|
||||
}
|
||||
|
||||
processingPointStatus, reversalData, err := csm.resolveBlockStatus(
|
||||
resolveStagingArea, processingPoint, true)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if processingPointStatus == externalapi.StatusUTXOValid {
|
||||
err = staging.CommitAllChanges(csm.databaseContext, resolveStagingArea)
|
||||
blockStatus, reversalData, err := csm.resolveBlockStatus(resolveStagingArea, resolveTip, true)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if reversalData != nil {
|
||||
err = csm.ReverseUTXODiffs(processingPoint, reversalData)
|
||||
if blockStatus == externalapi.StatusUTXOValid {
|
||||
selectedTip = resolveTip
|
||||
isCompletelyResolved = !hasMoreUnverifiedThanMax
|
||||
|
||||
err = staging.CommitAllChanges(csm.databaseContext, resolveStagingArea)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if reversalData != nil {
|
||||
err = csm.ReverseUTXODiffs(resolveTip, reversalData)
|
||||
// It's still not known what causes this error, but we can ignore it and not reverse the UTXO diffs
|
||||
// and harm performance in some cases.
|
||||
// TODO: Investigate why this error happens in the first place, and remove the workaround.
|
||||
if errors.Is(err, ErrReverseUTXODiffsUTXODiffChildNotFound) {
|
||||
log.Errorf("Could not reverse UTXO diffs while resolving virtual: %s", err)
|
||||
} else if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
isActualTip := processingPoint.Equal(pendingTip)
|
||||
isCompletelyResolved := isActualTip && processingPointStatus == externalapi.StatusUTXOValid
|
||||
if selectedTip == nil {
|
||||
log.Warnf("Non of the DAG tips are valid")
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
oldVirtualGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, readStagingArea, model.VirtualBlockHash, false)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
updateVirtualStagingArea := model.NewStagingArea()
|
||||
|
||||
virtualParents := []*externalapi.DomainHash{processingPoint}
|
||||
// If `isCompletelyResolved`, set virtual correctly with all tips which have less blue work than pending
|
||||
if isCompletelyResolved {
|
||||
lowerTips, err := csm.getGHOSTDAGLowerTips(readStagingArea, pendingTip)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
log.Debugf("Picking virtual parents from relevant tips len: %d", len(lowerTips))
|
||||
|
||||
virtualParents, err = csm.pickVirtualParents(readStagingArea, lowerTips)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
log.Debugf("Picked virtual parents: %s", virtualParents)
|
||||
}
|
||||
virtualUTXODiff, err := csm.updateVirtualWithParents(updateVirtualStagingArea, virtualParents)
|
||||
virtualUTXODiff, err := csm.updateVirtualWithParents(updateVirtualStagingArea, []*externalapi.DomainHash{selectedTip})
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@@ -206,12 +114,12 @@ func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (*ex
|
||||
}
|
||||
|
||||
selectedParentChainChanges, err := csm.dagTraversalManager.
|
||||
CalculateChainPath(updateVirtualStagingArea, previousVirtualSelectedParent, processingPoint)
|
||||
CalculateChainPath(readStagingArea, oldVirtualGHOSTDAGData.SelectedParent(), selectedTip)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
virtualParentsOutcome, err := csm.dagTopologyManager.Parents(updateVirtualStagingArea, model.VirtualBlockHash)
|
||||
virtualParents, err := csm.dagTopologyManager.Parents(readStagingArea, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@@ -219,6 +127,6 @@ func (csm *consensusStateManager) ResolveVirtual(maxBlocksToResolve uint64) (*ex
|
||||
return &externalapi.VirtualChangeSet{
|
||||
VirtualSelectedParentChainChanges: selectedParentChainChanges,
|
||||
VirtualUTXODiff: virtualUTXODiff,
|
||||
VirtualParents: virtualParentsOutcome,
|
||||
VirtualParents: virtualParents,
|
||||
}, isCompletelyResolved, nil
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ func (csm *consensusStateManager) resolveSingleBlockStatus(stagingArea *model.St
|
||||
return externalapi.StatusUTXOValid, nil, nil
|
||||
}
|
||||
|
||||
oldSelectedTip, err := csm.virtualSelectedParent(stagingArea)
|
||||
oldSelectedTip, err := csm.selectedTip(stagingArea)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
@@ -298,7 +298,7 @@ func (csm *consensusStateManager) isNewSelectedTip(stagingArea *model.StagingAre
|
||||
return blockHash.Equal(newSelectedTip), nil
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) virtualSelectedParent(stagingArea *model.StagingArea) (*externalapi.DomainHash, error) {
|
||||
func (csm *consensusStateManager) selectedTip(stagingArea *model.StagingArea) (*externalapi.DomainHash, error) {
|
||||
virtualGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, stagingArea, model.VirtualBlockHash, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestDoubleSpends(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating fundingBlock: %+v", err)
|
||||
}
|
||||
fundingBlock, _, err := consensus.GetBlock(fundingBlockHash)
|
||||
fundingBlock, err := consensus.GetBlock(fundingBlockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting fundingBlock: %+v", err)
|
||||
}
|
||||
@@ -195,7 +195,7 @@ func TestTransactionAcceptance(t *testing.T) {
|
||||
}
|
||||
}
|
||||
lastBlockInChain := chainTipHash
|
||||
blockC, _, err := testConsensus.GetBlock(blockHashC)
|
||||
blockC, err := testConsensus.GetBlock(blockHashC)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting blockC: %+v", err)
|
||||
}
|
||||
@@ -269,27 +269,22 @@ func TestTransactionAcceptance(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting acceptance data: %+v", err)
|
||||
}
|
||||
blueChildOfRedBlock, _, err := testConsensus.GetBlock(hashBlueChildOfRedBlock)
|
||||
blueChildOfRedBlock, err := testConsensus.GetBlock(hashBlueChildOfRedBlock)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting blueChildOfRedBlock: %+v", err)
|
||||
}
|
||||
blockE, _, err := testConsensus.GetBlock(blockHashF)
|
||||
blockE, err := testConsensus.GetBlock(blockHashF)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting blockE: %+v", err)
|
||||
}
|
||||
redBlock, _, err := testConsensus.GetBlock(redHash)
|
||||
redBlock, err := testConsensus.GetBlock(redHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting redBlock: %+v", err)
|
||||
}
|
||||
_, found, err := testConsensus.GetBlock(blockHashG)
|
||||
_, err = testConsensus.GetBlock(blockHashG)
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting blockG: %+v", err)
|
||||
t.Fatalf("Error getting blockF: %+v", err)
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("block G is missing")
|
||||
}
|
||||
|
||||
updatedDAAScoreVirtualBlock := consensusConfig.GenesisBlock.Header.DAAScore() + 26
|
||||
//We expect the second transaction in the "blue block" (blueChildOfRedBlock) to be accepted because the merge set is ordered topologically
|
||||
//and the red block is ordered topologically before the "blue block" so the input is known in the UTXOSet.
|
||||
|
||||
@@ -1,444 +0,0 @@
|
||||
package consensusstatemanager_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
)
|
||||
|
||||
func TestAddBlockBetweenResolveVirtualCalls(t *testing.T) {
|
||||
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestAddBlockBetweenResolveVirtualCalls")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
hashes := []*externalapi.DomainHash{consensusConfig.GenesisHash}
|
||||
|
||||
// Create a chain of blocks
|
||||
const initialChainLength = 10
|
||||
previousBlockHash := consensusConfig.GenesisHash
|
||||
for i := 0; i < initialChainLength; i++ {
|
||||
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in initial chain: %+v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Mine a chain with more blocks, to re-organize the DAG
|
||||
const reorgChainLength = initialChainLength + 1
|
||||
previousBlockHash = consensusConfig.GenesisHash
|
||||
for i := 0; i < reorgChainLength; i++ {
|
||||
previousBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
previousBlockHash = consensushashing.BlockHash(previousBlock)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
|
||||
// Do not UTXO validate in order to resolve virtual later
|
||||
err = tc.ValidateAndInsertBlock(previousBlock, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve one step
|
||||
_, _, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
|
||||
emptyCoinbase := &externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
Version: 0,
|
||||
},
|
||||
}
|
||||
|
||||
// Get template based on current resolve state
|
||||
blockTemplate, err := tc.BuildBlockTemplate(emptyCoinbase, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error building block template during virtual resolution of reorg: %+v", err)
|
||||
}
|
||||
|
||||
// Resolve one more step
|
||||
_, isCompletelyResolved, err := tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
|
||||
// Add the mined block (now virtual was modified)
|
||||
err = tc.ValidateAndInsertBlock(blockTemplate.Block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block during virtual resolution of reorg: %+v", err)
|
||||
}
|
||||
hashes = append(hashes, consensushashing.BlockHash(blockTemplate.Block))
|
||||
|
||||
// Complete resolving virtual
|
||||
for !isCompletelyResolved {
|
||||
_, isCompletelyResolved, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddGenesisChildAfterOneResolveVirtualCall(t *testing.T) {
|
||||
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestAddGenesisChildAfterOneResolveVirtualCall")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
hashes := []*externalapi.DomainHash{consensusConfig.GenesisHash}
|
||||
|
||||
// Create a chain of blocks
|
||||
const initialChainLength = 6
|
||||
previousBlockHash := consensusConfig.GenesisHash
|
||||
for i := 0; i < initialChainLength; i++ {
|
||||
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in initial chain: %+v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Mine a chain with more blocks, to re-organize the DAG
|
||||
const reorgChainLength = initialChainLength + 1
|
||||
previousBlockHash = consensusConfig.GenesisHash
|
||||
for i := 0; i < reorgChainLength; i++ {
|
||||
previousBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
previousBlockHash = consensushashing.BlockHash(previousBlock)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
|
||||
// Do not UTXO validate in order to resolve virtual later
|
||||
err = tc.ValidateAndInsertBlock(previousBlock, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve one step
|
||||
_, isCompletelyResolved, err := tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
|
||||
_, _, err = tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error adding block during virtual resolution of reorg: %+v", err)
|
||||
}
|
||||
|
||||
// Complete resolving virtual
|
||||
for !isCompletelyResolved {
|
||||
_, isCompletelyResolved, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddGenesisChildAfterTwoResolveVirtualCalls(t *testing.T) {
|
||||
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestAddGenesisChildAfterTwoResolveVirtualCalls")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
hashes := []*externalapi.DomainHash{consensusConfig.GenesisHash}
|
||||
|
||||
// Create a chain of blocks
|
||||
const initialChainLength = 6
|
||||
previousBlockHash := consensusConfig.GenesisHash
|
||||
for i := 0; i < initialChainLength; i++ {
|
||||
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in initial chain: %+v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Mine a chain with more blocks, to re-organize the DAG
|
||||
const reorgChainLength = initialChainLength + 1
|
||||
previousBlockHash = consensusConfig.GenesisHash
|
||||
for i := 0; i < reorgChainLength; i++ {
|
||||
previousBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
previousBlockHash = consensushashing.BlockHash(previousBlock)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
|
||||
// Do not UTXO validate in order to resolve virtual later
|
||||
err = tc.ValidateAndInsertBlock(previousBlock, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve one step
|
||||
_, _, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
|
||||
// Resolve one more step
|
||||
_, isCompletelyResolved, err := tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
|
||||
_, _, err = tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error adding block during virtual resolution of reorg: %+v", err)
|
||||
}
|
||||
|
||||
// Complete resolving virtual
|
||||
for !isCompletelyResolved {
|
||||
_, isCompletelyResolved, err = tc.ResolveVirtualWithMaxParam(2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
})
|
||||
}
|
||||
|
||||
func TestResolveVirtualBackAndForthReorgs(t *testing.T) {
|
||||
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestAddGenesisChildAfterTwoResolveVirtualCalls")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
hashes := []*externalapi.DomainHash{consensusConfig.GenesisHash}
|
||||
blocks := make(map[externalapi.DomainHash]string)
|
||||
blocks[*consensusConfig.GenesisHash] = "g"
|
||||
blocks[*model.VirtualBlockHash] = "v"
|
||||
printfDebug("%s\n\n", consensusConfig.GenesisHash)
|
||||
|
||||
// Create a chain of blocks
|
||||
const initialChainLength = 6
|
||||
previousBlockHash := consensusConfig.GenesisHash
|
||||
for i := 0; i < initialChainLength; i++ {
|
||||
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
blocks[*previousBlockHash] = fmt.Sprintf("A_%d", i)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
printfDebug("A_%d: %s\n", i, previousBlockHash)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in initial chain: %+v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
printfDebug("\n")
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
|
||||
firstChainTip := previousBlockHash
|
||||
|
||||
// Mine a chain with more blocks, to re-organize the DAG
|
||||
const reorgChainLength = 12 // initialChainLength + 1
|
||||
previousBlockHash = consensusConfig.GenesisHash
|
||||
for i := 0; i < reorgChainLength; i++ {
|
||||
previousBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
previousBlockHash = consensushashing.BlockHash(previousBlock)
|
||||
blocks[*previousBlockHash] = fmt.Sprintf("B_%d", i)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
printfDebug("B_%d: %s\n", i, previousBlockHash)
|
||||
|
||||
// Do not UTXO validate in order to resolve virtual later
|
||||
err = tc.ValidateAndInsertBlock(previousBlock, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in re-org chain: %+v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
printfDebug("\n")
|
||||
|
||||
printUtxoDiffChildren(t, tc, hashes, blocks)
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
|
||||
previousVirtualSelectedParent, err := tc.GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Resolve one step
|
||||
virtualChangeSet, _, err := tc.ResolveVirtualWithMaxParam(3)
|
||||
if err != nil {
|
||||
printUtxoDiffChildren(t, tc, hashes, blocks)
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
|
||||
newVirtualSelectedParent, err := tc.GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Make sure the reported change-set is compatible with actual changes.
|
||||
// Checking this for one call should suffice to avoid possible bugs.
|
||||
reportedPreviousVirtualSelectedParent := virtualChangeSet.VirtualSelectedParentChainChanges.Removed[0]
|
||||
reportedNewVirtualSelectedParent := virtualChangeSet.VirtualSelectedParentChainChanges.
|
||||
Added[len(virtualChangeSet.VirtualSelectedParentChainChanges.Added)-1]
|
||||
|
||||
if !previousVirtualSelectedParent.Equal(reportedPreviousVirtualSelectedParent) {
|
||||
t.Fatalf("The reported changeset is incompatible with actual changes")
|
||||
}
|
||||
if !newVirtualSelectedParent.Equal(reportedNewVirtualSelectedParent) {
|
||||
t.Fatalf("The reported changeset is incompatible with actual changes")
|
||||
}
|
||||
|
||||
// Resolve one more step
|
||||
_, isCompletelyResolved, err := tc.ResolveVirtualWithMaxParam(3)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
|
||||
// Complete resolving virtual
|
||||
for !isCompletelyResolved {
|
||||
_, isCompletelyResolved, err = tc.ResolveVirtualWithMaxParam(3)
|
||||
if err != nil {
|
||||
t.Fatalf("Error resolving virtual in re-org chain: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
printUtxoDiffChildren(t, tc, hashes, blocks)
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
|
||||
// Now get the first chain back to the wining position
|
||||
previousBlockHash = firstChainTip
|
||||
for i := 0; i < reorgChainLength; i++ {
|
||||
previousBlockHash, _, err = tc.AddBlock([]*externalapi.DomainHash{previousBlockHash}, nil, nil)
|
||||
blocks[*previousBlockHash] = fmt.Sprintf("A_%d", initialChainLength+i)
|
||||
hashes = append(hashes, previousBlockHash)
|
||||
printfDebug("A_%d: %s\n", initialChainLength+i, previousBlockHash)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error mining block no. %d in initial chain: %+v", initialChainLength+i, err)
|
||||
}
|
||||
}
|
||||
|
||||
printfDebug("\n")
|
||||
|
||||
printUtxoDiffChildren(t, tc, hashes, blocks)
|
||||
verifyUtxoDiffPaths(t, tc, hashes)
|
||||
})
|
||||
}
|
||||
|
||||
func verifyUtxoDiffPathToRoot(t *testing.T, tc testapi.TestConsensus, stagingArea *model.StagingArea, block, utxoDiffRoot *externalapi.DomainHash) {
|
||||
current := block
|
||||
for !current.Equal(utxoDiffRoot) {
|
||||
hasUTXODiffChild, err := tc.UTXODiffStore().HasUTXODiffChild(tc.DatabaseContext(), stagingArea, current)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while reading utxo diff store: %+v", err)
|
||||
}
|
||||
if !hasUTXODiffChild {
|
||||
t.Fatalf("%s is expected to have a UTXO diff child", current)
|
||||
}
|
||||
current, err = tc.UTXODiffStore().UTXODiffChild(tc.DatabaseContext(), stagingArea, current)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while reading utxo diff store: %+v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyUtxoDiffPaths(t *testing.T, tc testapi.TestConsensus, hashes []*externalapi.DomainHash) {
|
||||
stagingArea := model.NewStagingArea()
|
||||
|
||||
virtualGHOSTDAGData, err := tc.GHOSTDAGDataStore().Get(tc.DatabaseContext(), stagingArea, model.VirtualBlockHash, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
utxoDiffRoot := virtualGHOSTDAGData.SelectedParent()
|
||||
hasUTXODiffChild, err := tc.UTXODiffStore().HasUTXODiffChild(tc.DatabaseContext(), stagingArea, utxoDiffRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while reading utxo diff store: %+v", err)
|
||||
}
|
||||
if hasUTXODiffChild {
|
||||
t.Fatalf("Virtual selected parent is not expected to have an explicit diff child")
|
||||
}
|
||||
_, err = tc.UTXODiffStore().UTXODiff(tc.DatabaseContext(), stagingArea, utxoDiffRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("Virtual selected parent is expected to have a utxo diff: %+v", err)
|
||||
}
|
||||
|
||||
for _, block := range hashes {
|
||||
hasUTXODiffChild, err = tc.UTXODiffStore().HasUTXODiffChild(tc.DatabaseContext(), stagingArea, block)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while reading utxo diff store: %+v", err)
|
||||
}
|
||||
isOnVirtualSelectedChain, err := tc.DAGTopologyManager().IsInSelectedParentChainOf(stagingArea, block, utxoDiffRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// We expect a valid path to root in both cases: (i) block has a diff child, (ii) block is on the virtual selected chain
|
||||
if hasUTXODiffChild || isOnVirtualSelectedChain {
|
||||
verifyUtxoDiffPathToRoot(t, tc, stagingArea, block, utxoDiffRoot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printfDebug(format string, a ...any) {
|
||||
// Uncomment below when debugging the test
|
||||
//fmt.Printf(format, a...)
|
||||
}
|
||||
|
||||
func printUtxoDiffChildren(t *testing.T, tc testapi.TestConsensus, hashes []*externalapi.DomainHash, blocks map[externalapi.DomainHash]string) {
|
||||
printfDebug("\n===============================\nBlock\t\tDiff child\n")
|
||||
stagingArea := model.NewStagingArea()
|
||||
for _, block := range hashes {
|
||||
hasUTXODiffChild, err := tc.UTXODiffStore().HasUTXODiffChild(tc.DatabaseContext(), stagingArea, block)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while reading utxo diff store: %+v", err)
|
||||
}
|
||||
if hasUTXODiffChild {
|
||||
utxoDiffChild, err := tc.UTXODiffStore().UTXODiffChild(tc.DatabaseContext(), stagingArea, block)
|
||||
if err != nil {
|
||||
t.Fatalf("Error while reading utxo diff store: %+v", err)
|
||||
}
|
||||
printfDebug("%s\t\t\t%s\n", blocks[*block], blocks[*utxoDiffChild])
|
||||
} else {
|
||||
printfDebug("%s\n", blocks[*block])
|
||||
}
|
||||
}
|
||||
printfDebug("\n===============================\n")
|
||||
}
|
||||
@@ -3,10 +3,18 @@ package consensusstatemanager
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/staging"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ErrReverseUTXODiffsUTXODiffChildNotFound indicates a UTXO diff child was not found while calling ReverseUTXODiffs.
|
||||
// It's still not known what causes this error, but we can ignore it and not reverse the UTXO diffs
|
||||
// and harm performance in some cases.
|
||||
// TODO: Investigate why this error happens in the first place, and remove the workaround.
|
||||
var ErrReverseUTXODiffsUTXODiffChildNotFound = errors.New("ErrReverseUTXODiffsUTXODiffChildNotFound")
|
||||
|
||||
func (csm *consensusStateManager) ReverseUTXODiffs(tipHash *externalapi.DomainHash,
|
||||
reversalData *model.UTXODiffReversalData) error {
|
||||
|
||||
@@ -49,6 +57,9 @@ func (csm *consensusStateManager) ReverseUTXODiffs(tipHash *externalapi.DomainHa
|
||||
|
||||
currentBlockUTXODiffChild, err := csm.utxoDiffStore.UTXODiffChild(csm.databaseContext, readStagingArea, currentBlock)
|
||||
if err != nil {
|
||||
if database.IsNotFoundError(err) {
|
||||
return errors.Wrapf(ErrReverseUTXODiffsUTXODiffChildNotFound, "UTXO diff child was not found for block %s", currentBlock)
|
||||
}
|
||||
return err
|
||||
}
|
||||
currentBlockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, readStagingArea, currentBlock, false)
|
||||
@@ -56,6 +67,12 @@ func (csm *consensusStateManager) ReverseUTXODiffs(tipHash *externalapi.DomainHa
|
||||
return err
|
||||
}
|
||||
|
||||
// We stop reversing when current's UTXODiffChild is not current's SelectedParent
|
||||
if !currentBlockGHOSTDAGData.SelectedParent().Equal(currentBlockUTXODiffChild) {
|
||||
log.Debugf("Block %s's UTXODiffChild is not it's selected parent - finish reversing", currentBlock)
|
||||
break
|
||||
}
|
||||
|
||||
currentUTXODiff := previousUTXODiff.Reversed()
|
||||
|
||||
// retrieve current utxoDiff for Bi, to be used by next block
|
||||
@@ -69,12 +86,6 @@ func (csm *consensusStateManager) ReverseUTXODiffs(tipHash *externalapi.DomainHa
|
||||
return err
|
||||
}
|
||||
|
||||
// We stop reversing when current's UTXODiffChild is not current's SelectedParent
|
||||
if !currentBlockGHOSTDAGData.SelectedParent().Equal(currentBlockUTXODiffChild) {
|
||||
log.Debugf("Block %s's UTXODiffChild is not it's selected parent - finish reversing", currentBlock)
|
||||
break
|
||||
}
|
||||
|
||||
previousBlock = currentBlock
|
||||
previousBlockGHOSTDAGData = currentBlockGHOSTDAGData
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ func (csm *consensusStateManager) updateSelectedTipUTXODiff(
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "updateSelectedTipUTXODiff")
|
||||
defer onEnd()
|
||||
|
||||
selectedTip, err := csm.virtualSelectedParent(stagingArea)
|
||||
selectedTip, err := csm.selectedTip(stagingArea)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func TestConsensusStateManager_pickVirtualParents(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Failed building a block: %v", err)
|
||||
}
|
||||
err = tc.ValidateAndInsertBlock(block, true)
|
||||
_, err = tc.ValidateAndInsertBlock(block, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed Inserting block to tc: %v", err)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user