mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-03-22 16:13:45 +00:00
Compare commits
51 Commits
v0.10.0-al
...
v0.10.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
691756be6e | ||
|
|
63453ddf6b | ||
|
|
f70c1d5ae5 | ||
|
|
5ef24ae37e | ||
|
|
83e631548f | ||
|
|
0adfb2dfbb | ||
|
|
4461f56ca3 | ||
|
|
a18f2f8802 | ||
|
|
04dc1947ff | ||
|
|
36c56f73bf | ||
|
|
b76ca41109 | ||
|
|
50fd86e287 | ||
|
|
79cf0d64d0 | ||
|
|
b405ea50e5 | ||
|
|
ccfe8a45dd | ||
|
|
9dd8136e4b | ||
|
|
eb1703b948 | ||
|
|
a6da3251d0 | ||
|
|
bf198948c4 | ||
|
|
dfd8b3423d | ||
|
|
28bfc0fb9c | ||
|
|
83beae4463 | ||
|
|
a6ebe83198 | ||
|
|
acdc59b565 | ||
|
|
15811b0bcb | ||
|
|
a8a7e3dd9b | ||
|
|
3f193e9219 | ||
|
|
dfa24d8353 | ||
|
|
3c3ad1425d | ||
|
|
9bb8123391 | ||
|
|
347dd8fc4b | ||
|
|
d2cccd2829 | ||
|
|
7186f83095 | ||
|
|
5c394c2951 | ||
|
|
a786cdc15e | ||
|
|
6dd3d4a9e7 | ||
|
|
73b36f12f0 | ||
|
|
a795a9e619 | ||
|
|
0be1bba408 | ||
|
|
6afc06ce58 | ||
|
|
d01a213f3d | ||
|
|
7ad8ce521c | ||
|
|
86ba80a091 | ||
|
|
088e2114c2 | ||
|
|
2854d91688 | ||
|
|
af10b59181 | ||
|
|
c5b0394bbc | ||
|
|
9266d179a9 | ||
|
|
321792778e | ||
|
|
70f3fa9893 | ||
|
|
4e18031483 |
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-16.04, macos-10.15 ]
|
||||
os: [ ubuntu-16.04, macos-10.15, windows-2019 ]
|
||||
name: Testing on on ${{ matrix.os }}
|
||||
steps:
|
||||
|
||||
|
||||
25
app/app.go
25
app/app.go
@@ -85,12 +85,6 @@ func (app *kaspadApp) main(startedChan chan<- struct{}) error {
|
||||
profiling.Start(app.cfg.Profile, log)
|
||||
}
|
||||
|
||||
// Perform upgrades to kaspad as new versions require it.
|
||||
if err := doUpgrades(); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Return now if an interrupt signal was triggered.
|
||||
if signal.InterruptRequested(interrupt) {
|
||||
return nil
|
||||
@@ -163,12 +157,6 @@ func (app *kaspadApp) main(startedChan chan<- struct{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// doUpgrades performs upgrades to kaspad as new versions require it.
|
||||
// currently it's a placeholder we got from kaspad upstream, that does nothing
|
||||
func doUpgrades() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// dbPath returns the path to the block database given a database type.
|
||||
func databasePath(cfg *config.Config) string {
|
||||
return filepath.Join(cfg.AppDir, "data")
|
||||
@@ -181,6 +169,17 @@ func removeDatabase(cfg *config.Config) error {
|
||||
|
||||
func openDB(cfg *config.Config) (database.Database, error) {
|
||||
dbPath := databasePath(cfg)
|
||||
|
||||
err := checkDatabaseVersion(dbPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Infof("Loading database from '%s'", dbPath)
|
||||
return ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
|
||||
db, err := ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package appmessage
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
@@ -294,3 +295,70 @@ func DomainOutpointAndUTXOEntryPairsToOutpointAndUTXOEntryPairs(
|
||||
}
|
||||
return domainOutpointAndUTXOEntryPairs
|
||||
}
|
||||
|
||||
// DomainBlockToRPCBlock converts DomainBlocks to RPCBlocks
|
||||
func DomainBlockToRPCBlock(block *externalapi.DomainBlock) *RPCBlock {
|
||||
header := &RPCBlockHeader{
|
||||
Version: uint32(block.Header.Version()),
|
||||
ParentHashes: hashes.ToStrings(block.Header.ParentHashes()),
|
||||
HashMerkleRoot: block.Header.HashMerkleRoot().String(),
|
||||
AcceptedIDMerkleRoot: block.Header.AcceptedIDMerkleRoot().String(),
|
||||
UTXOCommitment: block.Header.UTXOCommitment().String(),
|
||||
Timestamp: block.Header.TimeInMilliseconds(),
|
||||
Bits: block.Header.Bits(),
|
||||
Nonce: block.Header.Nonce(),
|
||||
}
|
||||
transactions := make([]*RPCTransaction, len(block.Transactions))
|
||||
for i, transaction := range block.Transactions {
|
||||
transactions[i] = DomainTransactionToRPCTransaction(transaction)
|
||||
}
|
||||
return &RPCBlock{
|
||||
Header: header,
|
||||
Transactions: transactions,
|
||||
}
|
||||
}
|
||||
|
||||
// RPCBlockToDomainBlock converts `block` into a DomainBlock
|
||||
func RPCBlockToDomainBlock(block *RPCBlock) (*externalapi.DomainBlock, error) {
|
||||
parentHashes := make([]*externalapi.DomainHash, len(block.Header.ParentHashes))
|
||||
for i, parentHash := range block.Header.ParentHashes {
|
||||
domainParentHashes, err := externalapi.NewDomainHashFromString(parentHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentHashes[i] = domainParentHashes
|
||||
}
|
||||
hashMerkleRoot, err := externalapi.NewDomainHashFromString(block.Header.HashMerkleRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acceptedIDMerkleRoot, err := externalapi.NewDomainHashFromString(block.Header.AcceptedIDMerkleRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
utxoCommitment, err := externalapi.NewDomainHashFromString(block.Header.UTXOCommitment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header := blockheader.NewImmutableBlockHeader(
|
||||
uint16(block.Header.Version),
|
||||
parentHashes,
|
||||
hashMerkleRoot,
|
||||
acceptedIDMerkleRoot,
|
||||
utxoCommitment,
|
||||
block.Header.Timestamp,
|
||||
block.Header.Bits,
|
||||
block.Header.Nonce)
|
||||
transactions := make([]*externalapi.DomainTransaction, len(block.Transactions))
|
||||
for i, transaction := range block.Transactions {
|
||||
domainTransaction, err := RPCTransactionToDomainTransaction(transaction)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions[i] = domainTransaction
|
||||
}
|
||||
return &externalapi.DomainBlock{
|
||||
Header: header,
|
||||
Transactions: transactions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -137,6 +137,9 @@ const (
|
||||
CmdPruningPointUTXOSetOverrideNotificationMessage
|
||||
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage
|
||||
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage
|
||||
CmdNotifyVirtualDaaScoreChangedRequestMessage
|
||||
CmdNotifyVirtualDaaScoreChangedResponseMessage
|
||||
CmdVirtualDaaScoreChangedNotificationMessage
|
||||
)
|
||||
|
||||
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
|
||||
@@ -248,6 +251,9 @@ var RPCMessageCommandToString = map[MessageCommand]string{
|
||||
CmdPruningPointUTXOSetOverrideNotificationMessage: "PruningPointUTXOSetOverrideNotification",
|
||||
CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage: "StopNotifyingPruningPointUTXOSetOverrideRequest",
|
||||
CmdStopNotifyingPruningPointUTXOSetOverrideResponseMessage: "StopNotifyingPruningPointUTXOSetOverrideResponse",
|
||||
CmdNotifyVirtualDaaScoreChangedRequestMessage: "NotifyVirtualDaaScoreChangedRequest",
|
||||
CmdNotifyVirtualDaaScoreChangedResponseMessage: "NotifyVirtualDaaScoreChangedResponse",
|
||||
CmdVirtualDaaScoreChangedNotificationMessage: "VirtualDaaScoreChangedNotification",
|
||||
}
|
||||
|
||||
// Message is an interface that describes a kaspa message. A type that
|
||||
|
||||
@@ -25,7 +25,7 @@ func NewGetBlockRequestMessage(hash string, includeTransactionVerboseData bool)
|
||||
// its respective RPC message
|
||||
type GetBlockResponseMessage struct {
|
||||
baseMessage
|
||||
BlockVerboseData *BlockVerboseData
|
||||
Block *RPCBlock
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
@@ -39,70 +39,3 @@ func (msg *GetBlockResponseMessage) Command() MessageCommand {
|
||||
func NewGetBlockResponseMessage() *GetBlockResponseMessage {
|
||||
return &GetBlockResponseMessage{}
|
||||
}
|
||||
|
||||
// BlockVerboseData holds verbose data about a block
|
||||
type BlockVerboseData struct {
|
||||
Hash string
|
||||
Version uint16
|
||||
VersionHex string
|
||||
HashMerkleRoot string
|
||||
AcceptedIDMerkleRoot string
|
||||
UTXOCommitment string
|
||||
TxIDs []string
|
||||
TransactionVerboseData []*TransactionVerboseData
|
||||
Time int64
|
||||
Nonce uint64
|
||||
Bits string
|
||||
Difficulty float64
|
||||
ParentHashes []string
|
||||
ChildrenHashes []string
|
||||
SelectedParentHash string
|
||||
BlueScore uint64
|
||||
IsHeaderOnly bool
|
||||
}
|
||||
|
||||
// TransactionVerboseData holds verbose data about a transaction
|
||||
type TransactionVerboseData struct {
|
||||
TxID string
|
||||
Hash string
|
||||
Size uint64
|
||||
Version uint16
|
||||
LockTime uint64
|
||||
SubnetworkID string
|
||||
Gas uint64
|
||||
Payload string
|
||||
TransactionVerboseInputs []*TransactionVerboseInput
|
||||
TransactionVerboseOutputs []*TransactionVerboseOutput
|
||||
BlockHash string
|
||||
Time uint64
|
||||
BlockTime uint64
|
||||
}
|
||||
|
||||
// TransactionVerboseInput holds data about a transaction input
|
||||
type TransactionVerboseInput struct {
|
||||
TxID string
|
||||
OutputIndex uint32
|
||||
ScriptSig *ScriptSig
|
||||
Sequence uint64
|
||||
}
|
||||
|
||||
// ScriptSig holds data about a script signature
|
||||
type ScriptSig struct {
|
||||
Asm string
|
||||
Hex string
|
||||
}
|
||||
|
||||
// TransactionVerboseOutput holds data about a transaction output
|
||||
type TransactionVerboseOutput struct {
|
||||
Value uint64
|
||||
Index uint32
|
||||
ScriptPubKey *ScriptPubKeyResult
|
||||
}
|
||||
|
||||
// ScriptPubKeyResult holds data about a script public key
|
||||
type ScriptPubKeyResult struct {
|
||||
Hex string
|
||||
Type string
|
||||
Address string
|
||||
Version uint16
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ type GetBlockDAGInfoResponseMessage struct {
|
||||
Difficulty float64
|
||||
PastMedianTime int64
|
||||
PruningPointHash string
|
||||
VirtualDAAScore uint64
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func NewGetBlockTemplateRequestMessage(payAddress string) *GetBlockTemplateReque
|
||||
// its respective RPC message
|
||||
type GetBlockTemplateResponseMessage struct {
|
||||
baseMessage
|
||||
MsgBlock *MsgBlock
|
||||
Block *RPCBlock
|
||||
IsSynced bool
|
||||
|
||||
Error *RPCError
|
||||
@@ -35,9 +35,9 @@ func (msg *GetBlockTemplateResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlockTemplateResponseMessage returns a instance of the message
|
||||
func NewGetBlockTemplateResponseMessage(msgBlock *MsgBlock, isSynced bool) *GetBlockTemplateResponseMessage {
|
||||
func NewGetBlockTemplateResponseMessage(block *RPCBlock, isSynced bool) *GetBlockTemplateResponseMessage {
|
||||
return &GetBlockTemplateResponseMessage{
|
||||
MsgBlock: msgBlock,
|
||||
Block: block,
|
||||
IsSynced: isSynced,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ package appmessage
|
||||
type GetBlocksRequestMessage struct {
|
||||
baseMessage
|
||||
LowHash string
|
||||
IncludeBlockVerboseData bool
|
||||
IncludeBlocks bool
|
||||
IncludeTransactionVerboseData bool
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ func (msg *GetBlocksRequestMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlocksRequestMessage returns a instance of the message
|
||||
func NewGetBlocksRequestMessage(lowHash string, includeBlockVerboseData bool,
|
||||
func NewGetBlocksRequestMessage(lowHash string, includeBlocks bool,
|
||||
includeTransactionVerboseData bool) *GetBlocksRequestMessage {
|
||||
return &GetBlocksRequestMessage{
|
||||
LowHash: lowHash,
|
||||
IncludeBlockVerboseData: includeBlockVerboseData,
|
||||
IncludeBlocks: includeBlocks,
|
||||
IncludeTransactionVerboseData: includeTransactionVerboseData,
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,8 @@ func NewGetBlocksRequestMessage(lowHash string, includeBlockVerboseData bool,
|
||||
// its respective RPC message
|
||||
type GetBlocksResponseMessage struct {
|
||||
baseMessage
|
||||
BlockHashes []string
|
||||
BlockVerboseData []*BlockVerboseData
|
||||
BlockHashes []string
|
||||
Blocks []*RPCBlock
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
@@ -40,11 +40,6 @@ func (msg *GetBlocksResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlocksResponseMessage returns a instance of the message
|
||||
func NewGetBlocksResponseMessage(blockHashes []string, blockHexes []string,
|
||||
blockVerboseData []*BlockVerboseData) *GetBlocksResponseMessage {
|
||||
|
||||
return &GetBlocksResponseMessage{
|
||||
BlockHashes: blockHashes,
|
||||
BlockVerboseData: blockVerboseData,
|
||||
}
|
||||
func NewGetBlocksResponseMessage() *GetBlocksResponseMessage {
|
||||
return &GetBlocksResponseMessage{}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ func (msg *GetInfoRequestMessage) Command() MessageCommand {
|
||||
return CmdGetInfoRequestMessage
|
||||
}
|
||||
|
||||
// NewGeInfoRequestMessage returns a instance of the message
|
||||
func NewGeInfoRequestMessage() *GetInfoRequestMessage {
|
||||
// NewGetInfoRequestMessage returns a instance of the message
|
||||
func NewGetInfoRequestMessage() *GetInfoRequestMessage {
|
||||
return &GetInfoRequestMessage{}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ type GetMempoolEntryResponseMessage struct {
|
||||
|
||||
// MempoolEntry represents a transaction in the mempool.
|
||||
type MempoolEntry struct {
|
||||
Fee uint64
|
||||
TransactionVerboseData *TransactionVerboseData
|
||||
Fee uint64
|
||||
Transaction *RPCTransaction
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -38,11 +38,11 @@ func (msg *GetMempoolEntryResponseMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetMempoolEntryResponseMessage returns a instance of the message
|
||||
func NewGetMempoolEntryResponseMessage(fee uint64, transactionVerboseData *TransactionVerboseData) *GetMempoolEntryResponseMessage {
|
||||
func NewGetMempoolEntryResponseMessage(fee uint64, transaction *RPCTransaction) *GetMempoolEntryResponseMessage {
|
||||
return &GetMempoolEntryResponseMessage{
|
||||
Entry: &MempoolEntry{
|
||||
Fee: fee,
|
||||
TransactionVerboseData: transactionVerboseData,
|
||||
Fee: fee,
|
||||
Transaction: transaction,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,7 @@ func NewNotifyBlockAddedResponseMessage() *NotifyBlockAddedResponseMessage {
|
||||
// its respective RPC message
|
||||
type BlockAddedNotificationMessage struct {
|
||||
baseMessage
|
||||
Block *MsgBlock
|
||||
BlockVerboseData *BlockVerboseData
|
||||
Block *RPCBlock
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -47,9 +46,8 @@ func (msg *BlockAddedNotificationMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewBlockAddedNotificationMessage returns a instance of the message
|
||||
func NewBlockAddedNotificationMessage(block *MsgBlock, blockVerboseData *BlockVerboseData) *BlockAddedNotificationMessage {
|
||||
func NewBlockAddedNotificationMessage(block *RPCBlock) *BlockAddedNotificationMessage {
|
||||
return &BlockAddedNotificationMessage{
|
||||
Block: block,
|
||||
BlockVerboseData: blockVerboseData,
|
||||
Block: block,
|
||||
}
|
||||
}
|
||||
|
||||
55
app/appmessage/rpc_notify_virtual_daa_score_changed.go
Normal file
55
app/appmessage/rpc_notify_virtual_daa_score_changed.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package appmessage
|
||||
|
||||
// NotifyVirtualDaaScoreChangedRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyVirtualDaaScoreChangedRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyVirtualDaaScoreChangedRequestMessage) Command() MessageCommand {
|
||||
return CmdNotifyVirtualDaaScoreChangedRequestMessage
|
||||
}
|
||||
|
||||
// NewNotifyVirtualDaaScoreChangedRequestMessage returns a instance of the message
|
||||
func NewNotifyVirtualDaaScoreChangedRequestMessage() *NotifyVirtualDaaScoreChangedRequestMessage {
|
||||
return &NotifyVirtualDaaScoreChangedRequestMessage{}
|
||||
}
|
||||
|
||||
// NotifyVirtualDaaScoreChangedResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type NotifyVirtualDaaScoreChangedResponseMessage struct {
|
||||
baseMessage
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *NotifyVirtualDaaScoreChangedResponseMessage) Command() MessageCommand {
|
||||
return CmdNotifyVirtualDaaScoreChangedResponseMessage
|
||||
}
|
||||
|
||||
// NewNotifyVirtualDaaScoreChangedResponseMessage returns a instance of the message
|
||||
func NewNotifyVirtualDaaScoreChangedResponseMessage() *NotifyVirtualDaaScoreChangedResponseMessage {
|
||||
return &NotifyVirtualDaaScoreChangedResponseMessage{}
|
||||
}
|
||||
|
||||
// VirtualDaaScoreChangedNotificationMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type VirtualDaaScoreChangedNotificationMessage struct {
|
||||
baseMessage
|
||||
VirtualDaaScore uint64
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *VirtualDaaScoreChangedNotificationMessage) Command() MessageCommand {
|
||||
return CmdVirtualDaaScoreChangedNotificationMessage
|
||||
}
|
||||
|
||||
// NewVirtualDaaScoreChangedNotificationMessage returns a instance of the message
|
||||
func NewVirtualDaaScoreChangedNotificationMessage(
|
||||
virtualDaaScore uint64) *VirtualDaaScoreChangedNotificationMessage {
|
||||
|
||||
return &VirtualDaaScoreChangedNotificationMessage{
|
||||
VirtualDaaScore: virtualDaaScore,
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ package appmessage
|
||||
// its respective RPC message
|
||||
type SubmitBlockRequestMessage struct {
|
||||
baseMessage
|
||||
Block *MsgBlock
|
||||
Block *RPCBlock
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -13,7 +13,7 @@ func (msg *SubmitBlockRequestMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewSubmitBlockRequestMessage returns a instance of the message
|
||||
func NewSubmitBlockRequestMessage(block *MsgBlock) *SubmitBlockRequestMessage {
|
||||
func NewSubmitBlockRequestMessage(block *RPCBlock) *SubmitBlockRequestMessage {
|
||||
return &SubmitBlockRequestMessage{
|
||||
Block: block,
|
||||
}
|
||||
@@ -57,3 +57,35 @@ func (msg *SubmitBlockResponseMessage) Command() MessageCommand {
|
||||
func NewSubmitBlockResponseMessage() *SubmitBlockResponseMessage {
|
||||
return &SubmitBlockResponseMessage{}
|
||||
}
|
||||
|
||||
// RPCBlock is a kaspad block representation meant to be
|
||||
// used over RPC
|
||||
type RPCBlock struct {
|
||||
Header *RPCBlockHeader
|
||||
Transactions []*RPCTransaction
|
||||
VerboseData *RPCBlockVerboseData
|
||||
}
|
||||
|
||||
// RPCBlockHeader is a kaspad block header representation meant to be
|
||||
// used over RPC
|
||||
type RPCBlockHeader struct {
|
||||
Version uint32
|
||||
ParentHashes []string
|
||||
HashMerkleRoot string
|
||||
AcceptedIDMerkleRoot string
|
||||
UTXOCommitment string
|
||||
Timestamp int64
|
||||
Bits uint32
|
||||
Nonce uint64
|
||||
}
|
||||
|
||||
// RPCBlockVerboseData holds verbose data about a block
|
||||
type RPCBlockVerboseData struct {
|
||||
Hash string
|
||||
Difficulty float64
|
||||
SelectedParentHash string
|
||||
TransactionIDs []string
|
||||
IsHeaderOnly bool
|
||||
BlueScore uint64
|
||||
ChildrenHashes []string
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ type RPCTransaction struct {
|
||||
SubnetworkID string
|
||||
Gas uint64
|
||||
Payload string
|
||||
VerboseData *RPCTransactionVerboseData
|
||||
}
|
||||
|
||||
// RPCTransactionInput is a kaspad transaction input representation
|
||||
@@ -58,6 +59,7 @@ type RPCTransactionInput struct {
|
||||
PreviousOutpoint *RPCOutpoint
|
||||
SignatureScript string
|
||||
Sequence uint64
|
||||
VerboseData *RPCTransactionInputVerboseData
|
||||
}
|
||||
|
||||
// RPCScriptPublicKey is a kaspad ScriptPublicKey representation
|
||||
@@ -71,6 +73,7 @@ type RPCScriptPublicKey struct {
|
||||
type RPCTransactionOutput struct {
|
||||
Amount uint64
|
||||
ScriptPublicKey *RPCScriptPublicKey
|
||||
VerboseData *RPCTransactionOutputVerboseData
|
||||
}
|
||||
|
||||
// RPCOutpoint is a kaspad outpoint representation meant to be used
|
||||
@@ -88,3 +91,22 @@ type RPCUTXOEntry struct {
|
||||
BlockDAAScore uint64
|
||||
IsCoinbase bool
|
||||
}
|
||||
|
||||
// RPCTransactionVerboseData holds verbose data about a transaction
|
||||
type RPCTransactionVerboseData struct {
|
||||
TransactionID string
|
||||
Hash string
|
||||
Size uint64
|
||||
BlockHash string
|
||||
BlockTime uint64
|
||||
}
|
||||
|
||||
// RPCTransactionInputVerboseData holds data about a transaction input
|
||||
type RPCTransactionInputVerboseData struct {
|
||||
}
|
||||
|
||||
// RPCTransactionOutputVerboseData holds data about a transaction output
|
||||
type RPCTransactionOutputVerboseData struct {
|
||||
ScriptPublicKeyType string
|
||||
ScriptPublicKeyAddress string
|
||||
}
|
||||
|
||||
@@ -4,23 +4,19 @@ import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/utxoindex"
|
||||
|
||||
infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol"
|
||||
"github.com/kaspanet/kaspad/app/rpc"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/utxoindex"
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/dnsseed"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/id"
|
||||
"github.com/kaspanet/kaspad/util/panics"
|
||||
)
|
||||
|
||||
@@ -82,7 +78,13 @@ func (a *ComponentManager) Stop() {
|
||||
func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database, interrupt chan<- struct{}) (
|
||||
*ComponentManager, error) {
|
||||
|
||||
domain, err := domain.New(cfg.ActiveNetParams, db, cfg.IsArchivalNode)
|
||||
consensusConfig := consensus.Config{
|
||||
Params: *cfg.ActiveNetParams,
|
||||
IsArchival: cfg.IsArchivalNode,
|
||||
EnableSanityCheckPruningUTXOSet: cfg.EnableSanityCheckPruningUTXOSet,
|
||||
}
|
||||
|
||||
domain, err := domain.New(&consensusConfig, db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
57
app/db_version.go
Normal file
57
app/db_version.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const currentDatabaseVersion = 1
|
||||
|
||||
func checkDatabaseVersion(dbPath string) (err error) {
|
||||
versionFileName := versionFilePath(dbPath)
|
||||
|
||||
versionBytes, err := os.ReadFile(versionFileName)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) { // If version file doesn't exist, we assume that the database is new
|
||||
return createDatabaseVersionFile(dbPath, versionFileName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
databaseVersion, err := strconv.Atoi(string(versionBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if databaseVersion != currentDatabaseVersion {
|
||||
// TODO: Once there's more then one database version, it might make sense to add upgrade logic at this point
|
||||
return errors.Errorf("Invalid database version %d. Expected version: %d", databaseVersion, currentDatabaseVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createDatabaseVersionFile(dbPath string, versionFileName string) error {
|
||||
err := os.MkdirAll(dbPath, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
versionFile, err := os.Create(versionFileName)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer versionFile.Close()
|
||||
|
||||
versionString := strconv.Itoa(currentDatabaseVersion)
|
||||
_, err = versionFile.Write([]byte(versionString))
|
||||
return err
|
||||
}
|
||||
|
||||
func versionFilePath(dbPath string) string {
|
||||
dbVersionFileName := path.Join(dbPath, "version")
|
||||
return dbVersionFileName
|
||||
}
|
||||
@@ -37,12 +37,14 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
|
||||
newBlockInsertionResults = append(newBlockInsertionResults, unorphaningResult.blockInsertionResult)
|
||||
}
|
||||
|
||||
allAcceptedTransactions := make([]*externalapi.DomainTransaction, 0)
|
||||
for i, newBlock := range newBlocks {
|
||||
log.Debugf("OnNewBlock: passing block %s transactions to mining manager", hash)
|
||||
_, err = f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions)
|
||||
acceptedTransactions, err := f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allAcceptedTransactions = append(allAcceptedTransactions, acceptedTransactions...)
|
||||
|
||||
if f.onBlockAddedToDAGHandler != nil {
|
||||
log.Debugf("OnNewBlock: calling f.onBlockAddedToDAGHandler for block %s", hash)
|
||||
@@ -54,7 +56,7 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return f.broadcastTransactionsAfterBlockAdded(newBlocks, allAcceptedTransactions)
|
||||
}
|
||||
|
||||
// OnPruningPointUTXOSetOverride calls the handler function whenever the UTXO set
|
||||
@@ -67,9 +69,9 @@ func (f *FlowContext) OnPruningPointUTXOSetOverride() error {
|
||||
}
|
||||
|
||||
func (f *FlowContext) broadcastTransactionsAfterBlockAdded(
|
||||
block *externalapi.DomainBlock, transactionsAcceptedToMempool []*externalapi.DomainTransaction) error {
|
||||
addedBlocks []*externalapi.DomainBlock, transactionsAcceptedToMempool []*externalapi.DomainTransaction) error {
|
||||
|
||||
f.updateTransactionsToRebroadcast(block)
|
||||
f.updateTransactionsToRebroadcast(addedBlocks)
|
||||
|
||||
// Don't relay transactions when in IBD.
|
||||
if f.IsIBDRunning() {
|
||||
|
||||
@@ -25,14 +25,17 @@ func (f *FlowContext) AddTransaction(tx *externalapi.DomainTransaction) error {
|
||||
return f.Broadcast(inv)
|
||||
}
|
||||
|
||||
func (f *FlowContext) updateTransactionsToRebroadcast(block *externalapi.DomainBlock) {
|
||||
func (f *FlowContext) updateTransactionsToRebroadcast(addedBlocks []*externalapi.DomainBlock) {
|
||||
f.transactionsToRebroadcastLock.Lock()
|
||||
defer f.transactionsToRebroadcastLock.Unlock()
|
||||
// Note: if the block is red, its transactions won't be rebroadcasted
|
||||
// anymore, although they are not included in the UTXO set.
|
||||
// This is probably ok, since red blocks are quite rare.
|
||||
for _, tx := range block.Transactions {
|
||||
delete(f.transactionsToRebroadcast, *consensushashing.TransactionID(tx))
|
||||
|
||||
for _, block := range addedBlocks {
|
||||
// Note: if a transaction is included in the DAG but not accepted,
|
||||
// it won't be rebroadcast anymore, although it is not included in
|
||||
// the UTXO set
|
||||
for _, tx := range block.Transactions {
|
||||
delete(f.transactionsToRebroadcast, *consensushashing.TransactionID(tx))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
@@ -129,7 +130,11 @@ type fakeRelayInvsContext struct {
|
||||
rwLock sync.RWMutex
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) GetBlockChildren(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
func (f *fakeRelayInvsContext) GetBlockEvenIfHeaderOnly(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) GetBlockRelations(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, *externalapi.DomainHash, []*externalapi.DomainHash, error) {
|
||||
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
|
||||
}
|
||||
|
||||
@@ -1496,7 +1501,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
for _, test := range tests {
|
||||
|
||||
// This is done to avoid race condition
|
||||
@@ -1511,7 +1516,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
errChan := make(chan error)
|
||||
context := &fakeRelayInvsContext{
|
||||
testName: test.name,
|
||||
params: params,
|
||||
params: &consensusConfig.Params,
|
||||
finishedIBD: make(chan struct{}),
|
||||
|
||||
trySetIBDRunningResponse: true,
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/addressexchange"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type fakeReceiveAddressesContext struct{}
|
||||
@@ -19,7 +20,7 @@ func (f fakeReceiveAddressesContext) AddressManager() *addressmanager.AddressMan
|
||||
}
|
||||
|
||||
func TestReceiveAddressesErrors(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
incomingRoute := router.NewRoute()
|
||||
outgoingRoute := router.NewRoute()
|
||||
peer := peerpkg.New(nil)
|
||||
|
||||
@@ -64,17 +64,22 @@ func (m *Manager) NotifyBlockAddedToDAG(block *externalapi.DomainBlock, blockIns
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.notifyVirtualDaaScoreChanged()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.notifyVirtualSelectedParentChainChanged(blockInsertionResult)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgBlock := appmessage.DomainBlockToMsgBlock(block)
|
||||
blockVerboseData, err := m.context.BuildBlockVerboseData(block.Header, block, false)
|
||||
rpcBlock := appmessage.DomainBlockToRPCBlock(block)
|
||||
err = m.context.PopulateBlockWithVerboseData(rpcBlock, block.Header, block, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(msgBlock, blockVerboseData)
|
||||
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(rpcBlock)
|
||||
return m.context.NotificationManager.NotifyBlockAdded(blockAddedNotification)
|
||||
}
|
||||
|
||||
@@ -153,6 +158,19 @@ func (m *Manager) notifyVirtualSelectedParentBlueScoreChanged() error {
|
||||
return m.context.NotificationManager.NotifyVirtualSelectedParentBlueScoreChanged(notification)
|
||||
}
|
||||
|
||||
func (m *Manager) notifyVirtualDaaScoreChanged() error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualDaaScoreChanged")
|
||||
defer onEnd()
|
||||
|
||||
virtualInfo, err := m.context.Domain.Consensus().GetVirtualInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notification := appmessage.NewVirtualDaaScoreChangedNotificationMessage(virtualInfo.DAAScore)
|
||||
return m.context.NotificationManager.NotifyVirtualDaaScoreChanged(notification)
|
||||
}
|
||||
|
||||
func (m *Manager) notifyVirtualSelectedParentChainChanged(blockInsertionResult *externalapi.BlockInsertionResult) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualSelectedParentChainChanged")
|
||||
defer onEnd()
|
||||
|
||||
@@ -44,6 +44,7 @@ var handlers = map[appmessage.MessageCommand]handler{
|
||||
appmessage.CmdGetInfoRequestMessage: rpchandlers.HandleGetInfo,
|
||||
appmessage.CmdNotifyPruningPointUTXOSetOverrideRequestMessage: rpchandlers.HandleNotifyPruningPointUTXOSetOverrideRequest,
|
||||
appmessage.CmdStopNotifyingPruningPointUTXOSetOverrideRequestMessage: rpchandlers.HandleStopNotifyingPruningPointUTXOSetOverrideRequest,
|
||||
appmessage.CmdNotifyVirtualDaaScoreChangedRequestMessage: rpchandlers.HandleNotifyVirtualDaaScoreChanged,
|
||||
}
|
||||
|
||||
func (m *Manager) routerInitializer(router *router.Router, netConnection *netadapter.NetConnection) {
|
||||
|
||||
@@ -30,6 +30,7 @@ type NotificationListener struct {
|
||||
propagateFinalityConflictResolvedNotifications bool
|
||||
propagateUTXOsChangedNotifications bool
|
||||
propagateVirtualSelectedParentBlueScoreChangedNotifications bool
|
||||
propagateVirtualDaaScoreChangedNotifications bool
|
||||
propagatePruningPointUTXOSetOverrideNotifications bool
|
||||
|
||||
propagateUTXOsChangedNotificationAddresses map[utxoindex.ScriptPublicKeyString]*UTXOsChangedNotificationAddress
|
||||
@@ -181,6 +182,25 @@ func (nm *NotificationManager) NotifyVirtualSelectedParentBlueScoreChanged(
|
||||
return nil
|
||||
}
|
||||
|
||||
// NotifyVirtualDaaScoreChanged notifies the notification manager that the DAG's
|
||||
// virtual DAA score has changed
|
||||
func (nm *NotificationManager) NotifyVirtualDaaScoreChanged(
|
||||
notification *appmessage.VirtualDaaScoreChangedNotificationMessage) error {
|
||||
|
||||
nm.RLock()
|
||||
defer nm.RUnlock()
|
||||
|
||||
for router, listener := range nm.listeners {
|
||||
if listener.propagateVirtualDaaScoreChangedNotifications {
|
||||
err := router.OutgoingRoute().Enqueue(notification)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NotifyPruningPointUTXOSetOverride notifies the notification manager that the UTXO index
|
||||
// reset due to pruning point change via IBD.
|
||||
func (nm *NotificationManager) NotifyPruningPointUTXOSetOverride() error {
|
||||
@@ -308,6 +328,12 @@ func (nl *NotificationListener) PropagateVirtualSelectedParentBlueScoreChangedNo
|
||||
nl.propagateVirtualSelectedParentBlueScoreChangedNotifications = true
|
||||
}
|
||||
|
||||
// PropagateVirtualDaaScoreChangedNotifications instructs the listener to send
|
||||
// virtual DAA score notifications to the remote listener
|
||||
func (nl *NotificationListener) PropagateVirtualDaaScoreChangedNotifications() {
|
||||
nl.propagateVirtualDaaScoreChangedNotifications = true
|
||||
}
|
||||
|
||||
// PropagatePruningPointUTXOSetOverrideNotifications instructs the listener to send pruning point UTXO set override notifications
|
||||
// to the remote listener.
|
||||
func (nl *NotificationListener) PropagatePruningPointUTXOSetOverrideNotifications() {
|
||||
|
||||
@@ -2,14 +2,10 @@ package rpccontext
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
difficultyPackage "github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
@@ -26,79 +22,6 @@ import (
|
||||
// ErrBuildBlockVerboseDataInvalidBlock indicates that a block that was given to BuildBlockVerboseData is invalid.
|
||||
var ErrBuildBlockVerboseDataInvalidBlock = errors.New("ErrBuildBlockVerboseDataInvalidBlock")
|
||||
|
||||
// BuildBlockVerboseData builds a BlockVerboseData from the given blockHeader.
|
||||
// A block may optionally also be given if it's available in the calling context.
|
||||
func (ctx *Context) BuildBlockVerboseData(blockHeader externalapi.BlockHeader, block *externalapi.DomainBlock,
|
||||
includeTransactionVerboseData bool) (*appmessage.BlockVerboseData, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "BuildBlockVerboseData")
|
||||
defer onEnd()
|
||||
|
||||
hash := consensushashing.HeaderHash(blockHeader)
|
||||
|
||||
blockInfo, err := ctx.Domain.Consensus().GetBlockInfo(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if blockInfo.BlockStatus == externalapi.StatusInvalid {
|
||||
return nil, errors.Wrap(ErrBuildBlockVerboseDataInvalidBlock, "cannot build verbose data for "+
|
||||
"invalid block")
|
||||
}
|
||||
|
||||
childrenHashes, err := ctx.Domain.Consensus().GetBlockChildren(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := &appmessage.BlockVerboseData{
|
||||
Hash: hash.String(),
|
||||
Version: blockHeader.Version(),
|
||||
VersionHex: fmt.Sprintf("%08x", blockHeader.Version()),
|
||||
HashMerkleRoot: blockHeader.HashMerkleRoot().String(),
|
||||
AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot().String(),
|
||||
UTXOCommitment: blockHeader.UTXOCommitment().String(),
|
||||
ParentHashes: hashes.ToStrings(blockHeader.ParentHashes()),
|
||||
ChildrenHashes: hashes.ToStrings(childrenHashes),
|
||||
Nonce: blockHeader.Nonce(),
|
||||
Time: blockHeader.TimeInMilliseconds(),
|
||||
Bits: strconv.FormatInt(int64(blockHeader.Bits()), 16),
|
||||
Difficulty: ctx.GetDifficultyRatio(blockHeader.Bits(), ctx.Config.ActiveNetParams),
|
||||
BlueScore: blockInfo.BlueScore,
|
||||
IsHeaderOnly: blockInfo.BlockStatus == externalapi.StatusHeaderOnly,
|
||||
}
|
||||
|
||||
if blockInfo.BlockStatus != externalapi.StatusHeaderOnly {
|
||||
if block == nil {
|
||||
block, err = ctx.Domain.Consensus().GetBlock(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
txIDs := make([]string, len(block.Transactions))
|
||||
for i, tx := range block.Transactions {
|
||||
txIDs[i] = consensushashing.TransactionID(tx).String()
|
||||
}
|
||||
result.TxIDs = txIDs
|
||||
|
||||
if includeTransactionVerboseData {
|
||||
transactionVerboseData := make([]*appmessage.TransactionVerboseData, len(block.Transactions))
|
||||
for i, tx := range block.Transactions {
|
||||
txID := consensushashing.TransactionID(tx).String()
|
||||
data, err := ctx.BuildTransactionVerboseData(tx, txID, blockHeader, hash.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactionVerboseData[i] = data
|
||||
}
|
||||
result.TransactionVerboseData = transactionVerboseData
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetDifficultyRatio returns the proof-of-work difficulty as a multiple of the
|
||||
// minimum difficulty using the passed bits field from the header of a block.
|
||||
func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) float64 {
|
||||
@@ -106,7 +29,7 @@ func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) fl
|
||||
// converted back to a number. Note this is not the same as the proof of
|
||||
// work limit directly because the block difficulty is encoded in a block
|
||||
// with the compact form which loses precision.
|
||||
target := difficulty.CompactToBig(bits)
|
||||
target := difficultyPackage.CompactToBig(bits)
|
||||
|
||||
difficulty := new(big.Rat).SetFrac(params.PowMax, target)
|
||||
diff, _ := difficulty.Float64()
|
||||
@@ -117,100 +40,128 @@ func (ctx *Context) GetDifficultyRatio(bits uint32, params *dagconfig.Params) fl
|
||||
return diff
|
||||
}
|
||||
|
||||
// BuildTransactionVerboseData builds a TransactionVerboseData from
|
||||
// the given parameters
|
||||
func (ctx *Context) BuildTransactionVerboseData(tx *externalapi.DomainTransaction, txID string,
|
||||
blockHeader externalapi.BlockHeader, blockHash string) (
|
||||
*appmessage.TransactionVerboseData, error) {
|
||||
// PopulateBlockWithVerboseData populates the given `block` with verbose
|
||||
// data from `domainBlockHeader` and optionally from `domainBlock`
|
||||
func (ctx *Context) PopulateBlockWithVerboseData(block *appmessage.RPCBlock, domainBlockHeader externalapi.BlockHeader,
|
||||
domainBlock *externalapi.DomainBlock, includeTransactionVerboseData bool) error {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "BuildTransactionVerboseData")
|
||||
defer onEnd()
|
||||
blockHash := consensushashing.HeaderHash(domainBlockHeader)
|
||||
|
||||
txReply := &appmessage.TransactionVerboseData{
|
||||
TxID: txID,
|
||||
Hash: consensushashing.TransactionHash(tx).String(),
|
||||
Size: estimatedsize.TransactionEstimatedSerializedSize(tx),
|
||||
TransactionVerboseInputs: ctx.buildTransactionVerboseInputs(tx),
|
||||
TransactionVerboseOutputs: ctx.buildTransactionVerboseOutputs(tx, nil),
|
||||
Version: tx.Version,
|
||||
LockTime: tx.LockTime,
|
||||
SubnetworkID: tx.SubnetworkID.String(),
|
||||
Gas: tx.Gas,
|
||||
Payload: hex.EncodeToString(tx.Payload),
|
||||
blockInfo, err := ctx.Domain.Consensus().GetBlockInfo(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if blockHeader != nil {
|
||||
txReply.Time = uint64(blockHeader.TimeInMilliseconds())
|
||||
txReply.BlockTime = uint64(blockHeader.TimeInMilliseconds())
|
||||
txReply.BlockHash = blockHash
|
||||
if blockInfo.BlockStatus == externalapi.StatusInvalid {
|
||||
return errors.Wrap(ErrBuildBlockVerboseDataInvalidBlock, "cannot build verbose data for "+
|
||||
"invalid block")
|
||||
}
|
||||
|
||||
return txReply, nil
|
||||
}
|
||||
_, selectedParentHash, childrenHashes, err := ctx.Domain.Consensus().GetBlockRelations(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (ctx *Context) buildTransactionVerboseInputs(tx *externalapi.DomainTransaction) []*appmessage.TransactionVerboseInput {
|
||||
inputs := make([]*appmessage.TransactionVerboseInput, len(tx.Inputs))
|
||||
for i, transactionInput := range tx.Inputs {
|
||||
// The disassembled string will contain [error] inline
|
||||
// if the script doesn't fully parse, so ignore the
|
||||
// error here.
|
||||
disbuf, _ := txscript.DisasmString(constants.MaxScriptPublicKeyVersion, transactionInput.SignatureScript)
|
||||
block.VerboseData = &appmessage.RPCBlockVerboseData{
|
||||
Hash: blockHash.String(),
|
||||
Difficulty: ctx.GetDifficultyRatio(domainBlockHeader.Bits(), ctx.Config.ActiveNetParams),
|
||||
ChildrenHashes: hashes.ToStrings(childrenHashes),
|
||||
IsHeaderOnly: blockInfo.BlockStatus == externalapi.StatusHeaderOnly,
|
||||
BlueScore: blockInfo.BlueScore,
|
||||
}
|
||||
// selectedParentHash will be nil in the genesis block
|
||||
if selectedParentHash != nil {
|
||||
block.VerboseData.SelectedParentHash = selectedParentHash.String()
|
||||
}
|
||||
|
||||
input := &appmessage.TransactionVerboseInput{}
|
||||
input.TxID = transactionInput.PreviousOutpoint.TransactionID.String()
|
||||
input.OutputIndex = transactionInput.PreviousOutpoint.Index
|
||||
input.Sequence = transactionInput.Sequence
|
||||
input.ScriptSig = &appmessage.ScriptSig{
|
||||
Asm: disbuf,
|
||||
Hex: hex.EncodeToString(transactionInput.SignatureScript),
|
||||
if blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the block if we didn't receive it previously
|
||||
if domainBlock == nil {
|
||||
domainBlock, err = ctx.Domain.Consensus().GetBlockEvenIfHeaderOnly(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inputs[i] = input
|
||||
}
|
||||
|
||||
return inputs
|
||||
}
|
||||
transactionIDs := make([]string, len(domainBlock.Transactions))
|
||||
for i, transaction := range domainBlock.Transactions {
|
||||
transactionIDs[i] = consensushashing.TransactionID(transaction).String()
|
||||
}
|
||||
block.VerboseData.TransactionIDs = transactionIDs
|
||||
|
||||
// buildTransactionVerboseOutputs returns a slice of JSON objects for the outputs of the passed
|
||||
// transaction.
|
||||
func (ctx *Context) buildTransactionVerboseOutputs(tx *externalapi.DomainTransaction, filterAddrMap map[string]struct{}) []*appmessage.TransactionVerboseOutput {
|
||||
outputs := make([]*appmessage.TransactionVerboseOutput, len(tx.Outputs))
|
||||
for i, transactionOutput := range tx.Outputs {
|
||||
|
||||
// Ignore the error here since an error means the script
|
||||
// couldn't parse and there is no additional information about
|
||||
// it anyways.
|
||||
scriptClass, addr, _ := txscript.ExtractScriptPubKeyAddress(
|
||||
transactionOutput.ScriptPublicKey, ctx.Config.ActiveNetParams)
|
||||
|
||||
// Encode the addresses while checking if the address passes the
|
||||
// filter when needed.
|
||||
passesFilter := len(filterAddrMap) == 0
|
||||
var encodedAddr string
|
||||
if addr != nil {
|
||||
encodedAddr = addr.EncodeAddress()
|
||||
|
||||
// If the filter doesn't already pass, make it pass if
|
||||
// the address exists in the filter.
|
||||
if _, exists := filterAddrMap[encodedAddr]; exists {
|
||||
passesFilter = true
|
||||
if includeTransactionVerboseData {
|
||||
for _, transaction := range block.Transactions {
|
||||
err := ctx.PopulateTransactionWithVerboseData(transaction, domainBlockHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !passesFilter {
|
||||
continue
|
||||
}
|
||||
|
||||
output := &appmessage.TransactionVerboseOutput{}
|
||||
output.Index = uint32(i)
|
||||
output.Value = transactionOutput.Value
|
||||
output.ScriptPubKey = &appmessage.ScriptPubKeyResult{
|
||||
Version: transactionOutput.ScriptPublicKey.Version,
|
||||
Address: encodedAddr,
|
||||
Hex: hex.EncodeToString(transactionOutput.ScriptPublicKey.Script),
|
||||
Type: scriptClass.String(),
|
||||
}
|
||||
outputs[i] = output
|
||||
}
|
||||
|
||||
return outputs
|
||||
return nil
|
||||
}
|
||||
|
||||
// PopulateTransactionWithVerboseData populates the given `transaction` with
|
||||
// verbose data from `domainTransaction`
|
||||
func (ctx *Context) PopulateTransactionWithVerboseData(
|
||||
transaction *appmessage.RPCTransaction, domainBlockHeader externalapi.BlockHeader) error {
|
||||
|
||||
domainTransaction, err := appmessage.RPCTransactionToDomainTransaction(transaction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transaction.VerboseData = &appmessage.RPCTransactionVerboseData{
|
||||
TransactionID: consensushashing.TransactionID(domainTransaction).String(),
|
||||
Hash: consensushashing.TransactionHash(domainTransaction).String(),
|
||||
Size: estimatedsize.TransactionEstimatedSerializedSize(domainTransaction),
|
||||
}
|
||||
if domainBlockHeader != nil {
|
||||
transaction.VerboseData.BlockHash = consensushashing.HeaderHash(domainBlockHeader).String()
|
||||
transaction.VerboseData.BlockTime = uint64(domainBlockHeader.TimeInMilliseconds())
|
||||
}
|
||||
for _, input := range transaction.Inputs {
|
||||
ctx.populateTransactionInputWithVerboseData(input)
|
||||
}
|
||||
for _, output := range transaction.Outputs {
|
||||
err := ctx.populateTransactionOutputWithVerboseData(output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *Context) populateTransactionInputWithVerboseData(transactionInput *appmessage.RPCTransactionInput) {
|
||||
transactionInput.VerboseData = &appmessage.RPCTransactionInputVerboseData{}
|
||||
}
|
||||
|
||||
func (ctx *Context) populateTransactionOutputWithVerboseData(transactionOutput *appmessage.RPCTransactionOutput) error {
|
||||
scriptPublicKey, err := hex.DecodeString(transactionOutput.ScriptPublicKey.Script)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
domainScriptPublicKey := &externalapi.ScriptPublicKey{
|
||||
Script: scriptPublicKey,
|
||||
Version: transactionOutput.ScriptPublicKey.Version,
|
||||
}
|
||||
|
||||
// Ignore the error here since an error means the script
|
||||
// couldn't be parsed and there's no additional information about
|
||||
// it anyways
|
||||
scriptPublicKeyType, scriptPublicKeyAddress, _ := txscript.ExtractScriptPubKeyAddress(
|
||||
domainScriptPublicKey, ctx.Config.ActiveNetParams)
|
||||
|
||||
var encodedScriptPublicKeyAddress string
|
||||
if scriptPublicKeyAddress != nil {
|
||||
encodedScriptPublicKeyAddress = scriptPublicKeyAddress.EncodeAddress()
|
||||
}
|
||||
transactionOutput.VerboseData = &appmessage.RPCTransactionOutputVerboseData{
|
||||
ScriptPublicKeyType: scriptPublicKeyType.String(),
|
||||
ScriptPublicKeyAddress: encodedScriptPublicKeyAddress,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
header, err := context.Domain.Consensus().GetBlockHeader(hash)
|
||||
block, err := context.Domain.Consensus().GetBlockEvenIfHeaderOnly(hash)
|
||||
if err != nil {
|
||||
errorMessage := &appmessage.GetBlockResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Block %s not found", hash)
|
||||
@@ -29,7 +29,13 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
|
||||
|
||||
response := appmessage.NewGetBlockResponseMessage()
|
||||
|
||||
blockVerboseData, err := context.BuildBlockVerboseData(header, nil, getBlockRequest.IncludeTransactionVerboseData)
|
||||
if getBlockRequest.IncludeTransactionVerboseData {
|
||||
response.Block = appmessage.DomainBlockToRPCBlock(block)
|
||||
} else {
|
||||
response.Block = appmessage.DomainBlockToRPCBlock(&externalapi.DomainBlock{Header: block.Header})
|
||||
}
|
||||
|
||||
err = context.PopulateBlockWithVerboseData(response.Block, block.Header, block, getBlockRequest.IncludeTransactionVerboseData)
|
||||
if err != nil {
|
||||
if errors.Is(err, rpccontext.ErrBuildBlockVerboseDataInvalidBlock) {
|
||||
errorMessage := &appmessage.GetBlockResponseMessage{}
|
||||
@@ -39,7 +45,5 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response.BlockVerboseData = blockVerboseData
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ func HandleGetBlockDAGInfo(context *rpccontext.Context, _ *router.Router, _ appm
|
||||
response.VirtualParentHashes = hashes.ToStrings(virtualInfo.ParentHashes)
|
||||
response.Difficulty = context.GetDifficultyRatio(virtualInfo.Bits, context.Config.ActiveNetParams)
|
||||
response.PastMedianTime = virtualInfo.PastMedianTime
|
||||
response.VirtualDAAScore = virtualInfo.DAAScore
|
||||
|
||||
pruningPoint, err := context.Domain.Consensus().PruningPoint()
|
||||
if err != nil {
|
||||
|
||||
@@ -31,12 +31,12 @@ func HandleGetBlockTemplate(context *rpccontext.Context, _ *router.Router, reque
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgBlock := appmessage.DomainBlockToMsgBlock(templateBlock)
|
||||
rpcBlock := appmessage.DomainBlockToRPCBlock(templateBlock)
|
||||
|
||||
isSynced, err := context.ProtocolManager.ShouldMine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return appmessage.NewGetBlockTemplateResponseMessage(msgBlock, isSynced), nil
|
||||
return appmessage.NewGetBlockTemplateResponseMessage(rpcBlock, isSynced), nil
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ const (
|
||||
func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
getBlocksRequest := request.(*appmessage.GetBlocksRequestMessage)
|
||||
|
||||
// Validate that user didn't set IncludeTransactionVerboseData without setting IncludeBlockVerboseData
|
||||
if !getBlocksRequest.IncludeBlockVerboseData && getBlocksRequest.IncludeTransactionVerboseData {
|
||||
// Validate that user didn't set IncludeTransactionVerboseData without setting IncludeBlocks
|
||||
if !getBlocksRequest.IncludeBlocks && getBlocksRequest.IncludeTransactionVerboseData {
|
||||
return &appmessage.GetBlocksResponseMessage{
|
||||
Error: appmessage.RPCErrorf(
|
||||
"If includeTransactionVerboseData is set, then includeBlockVerboseData must be set as well"),
|
||||
@@ -81,26 +81,28 @@ func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appm
|
||||
}
|
||||
|
||||
// Prepare the response
|
||||
response := &appmessage.GetBlocksResponseMessage{
|
||||
BlockHashes: hashes.ToStrings(blockHashes),
|
||||
}
|
||||
|
||||
// Retrieve all block data in case BlockVerboseData was requested
|
||||
if getBlocksRequest.IncludeBlockVerboseData {
|
||||
response.BlockVerboseData = make([]*appmessage.BlockVerboseData, len(blockHashes))
|
||||
response := appmessage.NewGetBlocksResponseMessage()
|
||||
response.BlockHashes = hashes.ToStrings(blockHashes)
|
||||
if getBlocksRequest.IncludeBlocks {
|
||||
rpcBlocks := make([]*appmessage.RPCBlock, len(blockHashes))
|
||||
for i, blockHash := range blockHashes {
|
||||
blockHeader, err := context.Domain.Consensus().GetBlockHeader(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockVerboseData, err := context.BuildBlockVerboseData(blockHeader, nil,
|
||||
getBlocksRequest.IncludeTransactionVerboseData)
|
||||
block, err := context.Domain.Consensus().GetBlockEvenIfHeaderOnly(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response.BlockVerboseData[i] = blockVerboseData
|
||||
if getBlocksRequest.IncludeTransactionVerboseData {
|
||||
rpcBlocks[i] = appmessage.DomainBlockToRPCBlock(block)
|
||||
} else {
|
||||
rpcBlocks[i] = appmessage.DomainBlockToRPCBlock(&externalapi.DomainBlock{Header: block.Header})
|
||||
}
|
||||
err = context.PopulateBlockWithVerboseData(rpcBlocks[i], block.Header, nil, getBlocksRequest.IncludeTransactionVerboseData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
response.Blocks = rpcBlocks
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/domain/miningmanager"
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
)
|
||||
@@ -28,18 +27,18 @@ func (d fakeDomain) Consensus() externalapi.Consensus { return d }
|
||||
func (d fakeDomain) MiningManager() miningmanager.MiningManager { return nil }
|
||||
|
||||
func TestHandleGetBlocks(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
stagingArea := model.NewStagingArea()
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestHandleGetBlocks")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestHandleGetBlocks")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
fakeContext := rpccontext.Context{
|
||||
Config: &config.Config{Flags: &config.Flags{NetworkFlags: config.NetworkFlags{ActiveNetParams: params}}},
|
||||
Config: &config.Config{Flags: &config.Flags{NetworkFlags: config.NetworkFlags{ActiveNetParams: &consensusConfig.Params}}},
|
||||
Domain: fakeDomain{tc},
|
||||
}
|
||||
|
||||
@@ -81,7 +80,7 @@ func TestHandleGetBlocks(t *testing.T) {
|
||||
// \ | /
|
||||
// etc.
|
||||
expectedOrder := make([]*externalapi.DomainHash, 0, 40)
|
||||
mergingBlock := params.GenesisHash
|
||||
mergingBlock := consensusConfig.GenesisHash
|
||||
for i := 0; i < 10; i++ {
|
||||
splitBlocks := make([]*externalapi.DomainHash, 0, 3)
|
||||
for j := 0; j < 3; j++ {
|
||||
@@ -134,13 +133,13 @@ func TestHandleGetBlocks(t *testing.T) {
|
||||
virtualSelectedParent, actualBlocks.BlockHashes)
|
||||
}
|
||||
|
||||
expectedOrder = append([]*externalapi.DomainHash{params.GenesisHash}, expectedOrder...)
|
||||
expectedOrder = append([]*externalapi.DomainHash{consensusConfig.GenesisHash}, expectedOrder...)
|
||||
actualOrder := getBlocks(nil)
|
||||
if !reflect.DeepEqual(actualOrder.BlockHashes, hashes.ToStrings(expectedOrder)) {
|
||||
t.Fatalf("TestHandleGetBlocks \nexpected: %v \nactual:\n%v", expectedOrder, actualOrder.BlockHashes)
|
||||
}
|
||||
|
||||
requestAllExplictly := getBlocks(params.GenesisHash)
|
||||
requestAllExplictly := getBlocks(consensusConfig.GenesisHash)
|
||||
if !reflect.DeepEqual(requestAllExplictly.BlockHashes, hashes.ToStrings(expectedOrder)) {
|
||||
t.Fatalf("TestHandleGetBlocks \nexpected: \n%v\n. actual:\n%v", expectedOrder, requestAllExplictly.BlockHashes)
|
||||
}
|
||||
|
||||
@@ -3,25 +3,22 @@ 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/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleGetMempoolEntries handles the respectively named RPC command
|
||||
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 _, tx := range transactions {
|
||||
transactionVerboseData, err := context.BuildTransactionVerboseData(
|
||||
tx, consensushashing.TransactionID(tx).String(), nil, "")
|
||||
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: tx.Fee,
|
||||
TransactionVerboseData: transactionVerboseData,
|
||||
Fee: transaction.Fee,
|
||||
Transaction: rpcTransaction,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -24,12 +24,11 @@ func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, reques
|
||||
errorMessage.Error = appmessage.RPCErrorf("Transaction %s was not found", transactionID)
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
transactionVerboseData, err := context.BuildTransactionVerboseData(
|
||||
transaction, getMempoolEntryRequest.TxID, nil, "")
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
||||
err = context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return appmessage.NewGetMempoolEntryResponseMessage(transaction.Fee, transactionVerboseData), nil
|
||||
return appmessage.NewGetMempoolEntryResponseMessage(transaction.Fee, rpcTransaction), nil
|
||||
}
|
||||
|
||||
19
app/rpc/rpchandlers/notify_virtual_daa_score_changed.go
Normal file
19
app/rpc/rpchandlers/notify_virtual_daa_score_changed.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package rpchandlers
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleNotifyVirtualDaaScoreChanged handles the respectively named RPC command
|
||||
func HandleNotifyVirtualDaaScoreChanged(context *rpccontext.Context, router *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
listener, err := context.NotificationManager.Listener(router)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listener.PropagateVirtualDaaScoreChangedNotifications()
|
||||
|
||||
response := appmessage.NewNotifyVirtualDaaScoreChangedResponseMessage()
|
||||
return response, nil
|
||||
}
|
||||
@@ -14,9 +14,6 @@ import (
|
||||
func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
submitBlockRequest := request.(*appmessage.SubmitBlockRequestMessage)
|
||||
|
||||
msgBlock := submitBlockRequest.Block
|
||||
domainBlock := appmessage.MsgBlockToDomainBlock(msgBlock)
|
||||
|
||||
if context.ProtocolManager.IsIBDRunning() {
|
||||
return &appmessage.SubmitBlockResponseMessage{
|
||||
Error: appmessage.RPCErrorf("Block not submitted - IBD is running"),
|
||||
@@ -24,7 +21,15 @@ func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request ap
|
||||
}, nil
|
||||
}
|
||||
|
||||
err := context.ProtocolManager.AddBlock(domainBlock)
|
||||
domainBlock, err := appmessage.RPCBlockToDomainBlock(submitBlockRequest.Block)
|
||||
if err != nil {
|
||||
return &appmessage.SubmitBlockResponseMessage{
|
||||
Error: appmessage.RPCErrorf("Could not parse block: %s", err),
|
||||
RejectReason: appmessage.RejectReasonBlockInvalid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = context.ProtocolManager.AddBlock(domainBlock)
|
||||
if err != nil {
|
||||
isProtocolOrRuleError := errors.As(err, &ruleerrors.RuleError{}) || errors.As(err, &protocolerrors.ProtocolError{})
|
||||
if !isProtocolOrRuleError {
|
||||
|
||||
105
changelog.txt
105
changelog.txt
@@ -1,11 +1,100 @@
|
||||
Kaspad v0.10.3 - 2021-06-06
|
||||
===========================
|
||||
Non-breaking changes:
|
||||
* Implement NotifyVirtualDaaScoreChanged (#1737)
|
||||
|
||||
Kaspad v0.10.2 - 2021-05-18
|
||||
===========================
|
||||
Non-breaking changes:
|
||||
* Fix getBlock and getBlocks RPC commands to return blocks and transactions properly (#1716)
|
||||
* serializeAddress should always serialize as IPv6, since it assumes the IP size is 16 bytes (#1720)
|
||||
* Add VirtualDaaScore to GetBlockDagInfo (#1719)
|
||||
* Fix calcTxSequenceLockFromReferencedUTXOEntries for loop break condition (#1723)
|
||||
* Fix overflow when checking coinbase maturity and don't ban peers that send transactions with immature spend (#1722)
|
||||
|
||||
Kaspad v0.10.1 - 2021-05-11
|
||||
===========================
|
||||
* Calculate virtual's acceptance data and multiset after importing a new pruning point (#1700)
|
||||
|
||||
Kaspad v0.10.0 - 2021-04-26
|
||||
===========================
|
||||
Major changes include:
|
||||
* Implementing a signature hashing scheme similar to BIP-143
|
||||
* Replacing HASH160 with BLAKE2B
|
||||
* Replacing ECMH with MuHash
|
||||
* Removing RIPEMD160 and SHA1 from the codebase entirely
|
||||
* Making P2PKH transactions non-standard
|
||||
* Vastly enhancing the CLI wallet
|
||||
* Restructuring kaspad's app/home directory
|
||||
* Modifying block and transaction types in the RPC to be easier to consume clientside
|
||||
|
||||
A partial list of the more-important commits is as follows:
|
||||
* Fix data race in GetBlockChildren (#1579)
|
||||
* Remove payload hash (#1583)
|
||||
* Add the mempool size to getInfo RPC command (#1584)
|
||||
* Change the difficulty to be calculated based on the same block instead of its selected parent (#1591)
|
||||
* Adjust the difficulty in the first difficultyAdjustmentWindowSize blocks (#1592)
|
||||
* Adding DAA score (#1596)
|
||||
* Use DAA score where needed (#1602)
|
||||
* Remove the Services field from NetAddress. (#1610)
|
||||
* Fix getBlocks to not add the anticone when some blocks were filtered by GetHashesBetween (#1611)
|
||||
* Restructure the default ~/.kaspad directory layout (#1613)
|
||||
* Replace the HomeDir flag with a AppDir flag (#1615)
|
||||
* Implement BIP-143-like sighash (#1598)
|
||||
* Change --datadir to --appdir and remove symmetrical connection in stability tests (#1617)
|
||||
* Use BLAKE2B instead of HASH160, and get rid of any usage of RIPEMD160 and SHA1 (#1618)
|
||||
* Replace ECMH with Muhash (#1624)
|
||||
* Add support for multiple staging areas (#1633)
|
||||
* Make sure the ghostdagDataStore cache is at least DifficultyAdjustmentBlockWindow sized (#1635)
|
||||
* Resolve each block status in it's own staging area (#1634)
|
||||
* Add mass limit to mempool (#1627)
|
||||
* In RPC, use RPCTransactions and RPCBlocks instead of TransactionMessages and BlockMessages (#1609)
|
||||
* Use go-secp256k1 v0.0.5 (#1640)
|
||||
* Add a show-address subcommand to kaspawallet (#1653)
|
||||
* Replace p2pkh with p2pk (#1650)
|
||||
* Implement importing private keys into the wallet (#1655)
|
||||
* Add dump unencrypted data sub command to the wallet (#1661)
|
||||
* Add ECDSA support (#1657)
|
||||
* Add OpCheckMultiSigECDSA (#1663)
|
||||
* Add ECDSA support to the wallet (#1664)
|
||||
* Make moving the pruning point faster (#1660)
|
||||
* Implement new mechanism for updating UTXO Diffs (#1671)
|
||||
|
||||
Kaspad v0.9.2 - 2021-03-31
|
||||
===========================
|
||||
* Increase the route capacity of InvTransaction messages. (#1603) (#1637)
|
||||
|
||||
Kaspad v0.9.1 - 2021-03-14
|
||||
===========================
|
||||
* Testnet network reset
|
||||
|
||||
Kaspad v0.9.0 - 2021-03-04
|
||||
===========================
|
||||
|
||||
* Merge big subdags in pick virtual parents (#1574)
|
||||
* Write in the reject message the tx rejection reason (#1573)
|
||||
* Add nil checks for protowire (#1570)
|
||||
* Increase getBlocks limit to 1000 (#1572)
|
||||
* Return RPC error if getBlock's lowHash doesn't exist (#1569)
|
||||
* Add default dns-seeder to testnet (#1568)
|
||||
* Fix utxoindex deserialization (#1566)
|
||||
* Add pruning point hash to GetBlockDagInfo response (#1565)
|
||||
* Use EmitUnpopulated so that kaspactl prints all fields, even the default ones (#1561)
|
||||
* Stop logging an error whenever an RPC/P2P connection is canceled (#1562)
|
||||
* Cleanup the logger and make it asynchronous (#1524)
|
||||
* Close all iterators (#1542)
|
||||
* Add childrenHashes to GetBlock/s RPC commands (#1560)
|
||||
* Add ScriptPublicKey.Version to RPC (#1559)
|
||||
* Fix the target block rate to create less bursty mining (#1554)
|
||||
|
||||
Kaspad v0.8.10 - 2021-02-25
|
||||
===========================
|
||||
|
||||
[*] Fix bug where invalid mempool transactions were not removed (#1551)
|
||||
[*] Add RPC reconnection to the miner (#1552)
|
||||
[*] Remove virtual diff parents - only selectedTip is virtualDiffParent now (#1550)
|
||||
[*] Fix UTXO index (#1548)
|
||||
[*] Prevent fast failing (#1545)
|
||||
[*] Increase the sleep time in kaspaminer when the node is not synced (#1544)
|
||||
[*] Disallow header only blocks on RPC, relay and when requesting IBD full blocks (#1537)
|
||||
[*] Make templateManager hold a DomainBlock and isSynced bool instead of a GetBlockTemplateResponseMessage (#1538)
|
||||
* Fix bug where invalid mempool transactions were not removed (#1551)
|
||||
* Add RPC reconnection to the miner (#1552)
|
||||
* Remove virtual diff parents - only selectedTip is virtualDiffParent now (#1550)
|
||||
* Fix UTXO index (#1548)
|
||||
* Prevent fast failing (#1545)
|
||||
* Increase the sleep time in kaspaminer when the node is not synced (#1544)
|
||||
* Disallow header only blocks on RPC, relay and when requesting IBD full blocks (#1537)
|
||||
* Make templateManager hold a DomainBlock and isSynced bool instead of a GetBlockTemplateResponseMessage (#1538)
|
||||
|
||||
9
cmd/genkeypair/README.md
Normal file
9
cmd/genkeypair/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
genkeypair
|
||||
========
|
||||
|
||||
A tool for generating private-key-address pairs.
|
||||
|
||||
Note: This tool prints unencrypted private keys and is not recommended for day
|
||||
to day use, and is intended mainly for tests.
|
||||
|
||||
In order to manage your funds it's recommended to use [kaspawallet](../kaspawallet)
|
||||
26
cmd/genkeypair/config.go
Normal file
26
cmd/genkeypair/config.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/jessevdk/go-flags"
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
)
|
||||
|
||||
type configFlags struct {
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
func parseConfig() (*configFlags, error) {
|
||||
cfg := &configFlags{}
|
||||
parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag)
|
||||
_, err := parser.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = cfg.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
27
cmd/genkeypair/main.go
Normal file
27
cmd/genkeypair/main.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg, err := parseConfig()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
privateKey, publicKey, err := libkaspawallet.CreateKeyPair(false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
addr, err := util.NewAddressPublicKey(publicKey, cfg.NetParams().Prefix)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Private key: %x\n", privateKey)
|
||||
fmt.Printf("Address: %s\n", addr)
|
||||
}
|
||||
@@ -5,72 +5,32 @@ import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
|
||||
"github.com/pkg/errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const minerTimeout = 10 * time.Second
|
||||
|
||||
type minerClient struct {
|
||||
isReconnecting uint32
|
||||
clientLock sync.RWMutex
|
||||
rpcClient *rpcclient.RPCClient
|
||||
*rpcclient.RPCClient
|
||||
|
||||
cfg *configFlags
|
||||
blockAddedNotificationChan chan struct{}
|
||||
}
|
||||
|
||||
func (mc *minerClient) safeRPCClient() *rpcclient.RPCClient {
|
||||
mc.clientLock.RLock()
|
||||
defer mc.clientLock.RUnlock()
|
||||
return mc.rpcClient
|
||||
}
|
||||
|
||||
func (mc *minerClient) reconnect() {
|
||||
swapped := atomic.CompareAndSwapUint32(&mc.isReconnecting, 0, 1)
|
||||
if !swapped {
|
||||
return
|
||||
}
|
||||
|
||||
defer atomic.StoreUint32(&mc.isReconnecting, 0)
|
||||
|
||||
mc.clientLock.Lock()
|
||||
defer mc.clientLock.Unlock()
|
||||
|
||||
retryDuration := time.Second
|
||||
const maxRetryDuration = time.Minute
|
||||
log.Infof("Reconnecting RPC connection")
|
||||
for {
|
||||
err := mc.connect()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if retryDuration < time.Minute {
|
||||
retryDuration *= 2
|
||||
} else {
|
||||
retryDuration = maxRetryDuration
|
||||
}
|
||||
|
||||
log.Errorf("Got error '%s' while reconnecting. Trying again in %s", err, retryDuration)
|
||||
time.Sleep(retryDuration)
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *minerClient) connect() error {
|
||||
rpcAddress, err := mc.cfg.NetParams().NormalizeRPCServerAddress(mc.cfg.RPCServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mc.rpcClient, err = rpcclient.NewRPCClient(rpcAddress)
|
||||
rpcClient, err := rpcclient.NewRPCClient(rpcAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mc.rpcClient.SetTimeout(minerTimeout)
|
||||
mc.rpcClient.SetLogger(backendLog, logger.LevelTrace)
|
||||
mc.RPCClient = rpcClient
|
||||
mc.SetTimeout(minerTimeout)
|
||||
mc.SetLogger(backendLog, logger.LevelTrace)
|
||||
|
||||
err = mc.rpcClient.RegisterForBlockAddedNotifications(func(_ *appmessage.BlockAddedNotificationMessage) {
|
||||
err = mc.RegisterForBlockAddedNotifications(func(_ *appmessage.BlockAddedNotificationMessage) {
|
||||
select {
|
||||
case mc.blockAddedNotificationChan <- struct{}{}:
|
||||
default:
|
||||
|
||||
@@ -40,7 +40,7 @@ func main() {
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "error connecting to the RPC server"))
|
||||
}
|
||||
defer client.safeRPCClient().Disconnect()
|
||||
defer client.Disconnect()
|
||||
|
||||
miningAddr, err := util.DecodeAddress(cfg.MiningAddr, cfg.ActiveNetParams.Prefix)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,18 +6,14 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspaminer/templatemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspaminer/templatemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -114,13 +110,17 @@ func logHashRate() {
|
||||
|
||||
func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error {
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
log.Infof("Submitting block %s to %s", blockHash, client.safeRPCClient().Address())
|
||||
log.Infof("Submitting block %s to %s", blockHash, client.Address())
|
||||
|
||||
rejectReason, err := client.safeRPCClient().SubmitBlock(block)
|
||||
rejectReason, err := client.SubmitBlock(block)
|
||||
if err != nil {
|
||||
if nativeerrors.Is(err, router.ErrTimeout) {
|
||||
log.Warnf("Got timeout while submitting block %s to %s: %s", blockHash, client.safeRPCClient().Address(), err)
|
||||
client.reconnect()
|
||||
log.Warnf("Got timeout while submitting block %s to %s: %s", blockHash, client.Address(), err)
|
||||
return client.Reconnect()
|
||||
}
|
||||
if nativeerrors.Is(err, router.ErrRouteClosed) {
|
||||
log.Debugf("Got route is closed while requesting block template from %s. "+
|
||||
"The client is most likely reconnecting", client.Address())
|
||||
return nil
|
||||
}
|
||||
if rejectReason == appmessage.RejectReasonIsInIBD {
|
||||
@@ -129,7 +129,7 @@ func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error
|
||||
time.Sleep(waitTime)
|
||||
return nil
|
||||
}
|
||||
return errors.Wrapf(err, "Error submitting block %s to %s", blockHash, client.safeRPCClient().Address())
|
||||
return errors.Wrapf(err, "Error submitting block %s to %s", blockHash, client.Address())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -188,17 +188,29 @@ func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
|
||||
func templatesLoop(client *minerClient, miningAddr util.Address, errChan chan error) {
|
||||
getBlockTemplate := func() {
|
||||
template, err := client.safeRPCClient().GetBlockTemplate(miningAddr.String())
|
||||
template, err := client.GetBlockTemplate(miningAddr.String())
|
||||
if nativeerrors.Is(err, router.ErrTimeout) {
|
||||
log.Warnf("Got timeout while requesting block template from %s: %s", client.safeRPCClient().Address(), err)
|
||||
client.reconnect()
|
||||
log.Warnf("Got timeout while requesting block template from %s: %s", client.Address(), err)
|
||||
reconnectErr := client.Reconnect()
|
||||
if reconnectErr != nil {
|
||||
errChan <- reconnectErr
|
||||
}
|
||||
return
|
||||
}
|
||||
if nativeerrors.Is(err, router.ErrRouteClosed) {
|
||||
log.Debugf("Got route is closed while requesting block template from %s. "+
|
||||
"The client is most likely reconnecting", client.Address())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
errChan <- errors.Wrapf(err, "Error getting block template from %s", client.safeRPCClient().Address())
|
||||
errChan <- errors.Wrapf(err, "Error getting block template from %s", client.Address())
|
||||
return
|
||||
}
|
||||
err = templatemanager.Set(template)
|
||||
if err != nil {
|
||||
errChan <- errors.Wrapf(err, "Error setting block template from %s", client.Address())
|
||||
return
|
||||
}
|
||||
templatemanager.Set(template)
|
||||
}
|
||||
|
||||
getBlockTemplate()
|
||||
|
||||
@@ -23,10 +23,14 @@ func Get() (*externalapi.DomainBlock, bool) {
|
||||
}
|
||||
|
||||
// Set sets the current template to work on
|
||||
func Set(template *appmessage.GetBlockTemplateResponseMessage) {
|
||||
block := appmessage.MsgBlockToDomainBlock(template.MsgBlock)
|
||||
func Set(template *appmessage.GetBlockTemplateResponseMessage) error {
|
||||
block, err := appmessage.RPCBlockToDomainBlock(template.Block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
currentTemplate = block
|
||||
isSynced = template.IsSynced
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,29 +2,40 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
)
|
||||
|
||||
func balance(conf *balanceConfig) error {
|
||||
client, err := rpcclient.NewRPCClient(conf.RPCServer)
|
||||
client, err := connectToRPC(conf.NetParams(), conf.RPCServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
getUTXOsByAddressesResponse, err := client.GetUTXOsByAddresses([]string{conf.Address})
|
||||
|
||||
keysFile, err := keys.ReadKeysFile(conf.NetParams(), conf.KeysFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
virtualSelectedParentBlueScoreResponse, err := client.GetVirtualSelectedParentBlueScore()
|
||||
|
||||
addr, err := libkaspawallet.Address(conf.NetParams(), keysFile.PublicKeys, keysFile.MinimumSignatures, keysFile.ECDSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
getUTXOsByAddressesResponse, err := client.GetUTXOsByAddresses([]string{addr.String()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blockDAGInfo, err := client.GetBlockDAGInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
virtualSelectedParentBlueScore := virtualSelectedParentBlueScoreResponse.BlueScore
|
||||
|
||||
var availableBalance, pendingBalance uint64
|
||||
for _, entry := range getUTXOsByAddressesResponse.Entries {
|
||||
if isUTXOSpendable(entry, virtualSelectedParentBlueScore, conf.ActiveNetParams.BlockCoinbaseMaturity) {
|
||||
if isUTXOSpendable(entry, blockDAGInfo.VirtualDAAScore, conf.ActiveNetParams.BlockCoinbaseMaturity) {
|
||||
availableBalance += entry.UTXOEntry.Amount
|
||||
} else {
|
||||
pendingBalance += entry.UTXOEntry.Amount
|
||||
34
cmd/kaspawallet/broadcast.go
Normal file
34
cmd/kaspawallet/broadcast.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
)
|
||||
|
||||
func broadcast(conf *broadcastConfig) error {
|
||||
client, err := connectToRPC(conf.NetParams(), conf.RPCServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
psTxBytes, err := hex.DecodeString(conf.Transaction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx, err := libkaspawallet.ExtractTransaction(psTxBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transactionID, err := sendTransaction(client, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Transaction was sent successfully")
|
||||
fmt.Printf("Transaction ID: \t%s\n", transactionID)
|
||||
|
||||
return nil
|
||||
}
|
||||
31
cmd/kaspawallet/common.go
Normal file
31
cmd/kaspawallet/common.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
|
||||
"os"
|
||||
)
|
||||
|
||||
func isUTXOSpendable(entry *appmessage.UTXOsByAddressesEntry, virtualDAAScore uint64, coinbaseMaturity uint64) bool {
|
||||
if !entry.UTXOEntry.IsCoinbase {
|
||||
return true
|
||||
}
|
||||
blockBlueScore := entry.UTXOEntry.BlockDAAScore
|
||||
return blockBlueScore+coinbaseMaturity < virtualDAAScore
|
||||
}
|
||||
|
||||
func printErrorAndExit(err error) {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func connectToRPC(params *dagconfig.Params, rpcServer string) (*rpcclient.RPCClient, error) {
|
||||
rpcAddress, err := params.NormalizeRPCServerAddress(rpcServer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rpcclient.NewRPCClient(rpcAddress)
|
||||
}
|
||||
198
cmd/kaspawallet/config.go
Normal file
198
cmd/kaspawallet/config.go
Normal file
@@ -0,0 +1,198 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"github.com/pkg/errors"
|
||||
"os"
|
||||
|
||||
"github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
const (
|
||||
createSubCmd = "create"
|
||||
balanceSubCmd = "balance"
|
||||
sendSubCmd = "send"
|
||||
createUnsignedTransactionSubCmd = "create-unsigned-transaction"
|
||||
signSubCmd = "sign"
|
||||
broadcastSubCmd = "broadcast"
|
||||
showAddressSubCmd = "show-address"
|
||||
dumpUnencryptedDataSubCmd = "dump-unencrypted-data"
|
||||
)
|
||||
|
||||
type configFlags struct {
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type createConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
MinimumSignatures uint32 `long:"min-signatures" short:"m" description:"Minimum required signatures" default:"1"`
|
||||
NumPrivateKeys uint32 `long:"num-private-keys" short:"k" description:"Number of private keys" default:"1"`
|
||||
NumPublicKeys uint32 `long:"num-public-keys" short:"n" description:"Total number of keys" default:"1"`
|
||||
ECDSA bool `long:"ecdsa" description:"Create an ECDSA wallet"`
|
||||
Import bool `long:"import" short:"i" description:"Import private keys (as opposed to generating them)"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type balanceConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
RPCServer string `long:"rpcserver" short:"s" description:"RPC server to connect to"`
|
||||
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))"`
|
||||
RPCServer string `long:"rpcserver" short:"s" description:"RPC server to connect to"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type createUnsignedTransactionConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
RPCServer string `long:"rpcserver" short:"s" description:"RPC server to connect to"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type signConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
Transaction string `long:"transaction" short:"t" description:"The unsigned transaction to sign on (encoded in hex)" required:"true"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type broadcastConfig struct {
|
||||
RPCServer string `long:"rpcserver" short:"s" description:"RPC server to connect to"`
|
||||
Transaction string `long:"transaction" short:"t" description:"The signed transaction to broadcast (encoded in hex)" required:"true"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type showAddressConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type dumpUnencryptedDataConfig struct {
|
||||
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
func parseCommandLine() (subCommand string, config interface{}) {
|
||||
cfg := &configFlags{}
|
||||
parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag)
|
||||
|
||||
createConf := &createConfig{}
|
||||
parser.AddCommand(createSubCmd, "Creates a new wallet",
|
||||
"Creates a private key and 3 public addresses, one for each of MainNet, TestNet and DevNet", createConf)
|
||||
|
||||
balanceConf := &balanceConfig{}
|
||||
parser.AddCommand(balanceSubCmd, "Shows the balance of a public address",
|
||||
"Shows the balance for a public address in Kaspa", balanceConf)
|
||||
|
||||
sendConf := &sendConfig{}
|
||||
parser.AddCommand(sendSubCmd, "Sends a Kaspa transaction to a public address",
|
||||
"Sends a Kaspa transaction to a public address", sendConf)
|
||||
|
||||
createUnsignedTransactionConf := &createUnsignedTransactionConfig{}
|
||||
parser.AddCommand(createUnsignedTransactionSubCmd, "Create an unsigned Kaspa transaction",
|
||||
"Create an unsigned Kaspa transaction", createUnsignedTransactionConf)
|
||||
|
||||
signConf := &signConfig{}
|
||||
parser.AddCommand(signSubCmd, "Sign the given partially signed transaction",
|
||||
"Sign the given partially signed transaction", signConf)
|
||||
|
||||
broadcastConf := &broadcastConfig{}
|
||||
parser.AddCommand(broadcastSubCmd, "Broadcast the given transaction",
|
||||
"Broadcast the given transaction", broadcastConf)
|
||||
|
||||
showAddressConf := &showAddressConfig{}
|
||||
parser.AddCommand(showAddressSubCmd, "Shows the public address of the current wallet",
|
||||
"Shows the public address of the current wallet", showAddressConf)
|
||||
|
||||
dumpUnencryptedDataConf := &dumpUnencryptedDataConfig{}
|
||||
parser.AddCommand(dumpUnencryptedDataSubCmd, "Prints the unencrypted wallet data",
|
||||
"Prints the unencrypted wallet data including its private keys. Anyone that sees it can access "+
|
||||
"the funds. Use only on safe environment.", dumpUnencryptedDataConf)
|
||||
|
||||
_, err := parser.Parse()
|
||||
|
||||
if err != nil {
|
||||
var flagsErr *flags.Error
|
||||
if ok := errors.As(err, &flagsErr); ok && flagsErr.Type == flags.ErrHelp {
|
||||
os.Exit(0)
|
||||
} else {
|
||||
os.Exit(1)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
switch parser.Command.Active.Name {
|
||||
case createSubCmd:
|
||||
combineNetworkFlags(&createConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := createConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = createConf
|
||||
case balanceSubCmd:
|
||||
combineNetworkFlags(&balanceConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := balanceConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = balanceConf
|
||||
case sendSubCmd:
|
||||
combineNetworkFlags(&sendConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := sendConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = sendConf
|
||||
case createUnsignedTransactionSubCmd:
|
||||
combineNetworkFlags(&createUnsignedTransactionConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := createUnsignedTransactionConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = createUnsignedTransactionConf
|
||||
case signSubCmd:
|
||||
combineNetworkFlags(&signConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := signConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = signConf
|
||||
case broadcastSubCmd:
|
||||
combineNetworkFlags(&broadcastConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := broadcastConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = broadcastConf
|
||||
case showAddressSubCmd:
|
||||
combineNetworkFlags(&showAddressConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := showAddressConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = showAddressConf
|
||||
case dumpUnencryptedDataSubCmd:
|
||||
combineNetworkFlags(&dumpUnencryptedDataConf.NetworkFlags, &cfg.NetworkFlags)
|
||||
err := dumpUnencryptedDataConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = dumpUnencryptedDataConf
|
||||
}
|
||||
|
||||
return parser.Command.Active.Name, config
|
||||
}
|
||||
|
||||
func combineNetworkFlags(dst, src *config.NetworkFlags) {
|
||||
dst.Testnet = dst.Testnet || src.Testnet
|
||||
dst.Simnet = dst.Simnet || src.Simnet
|
||||
dst.Devnet = dst.Devnet || src.Devnet
|
||||
if dst.OverrideDAGParamsFile == "" {
|
||||
dst.OverrideDAGParamsFile = src.OverrideDAGParamsFile
|
||||
}
|
||||
}
|
||||
71
cmd/kaspawallet/create.go
Normal file
71
cmd/kaspawallet/create.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func create(conf *createConfig) error {
|
||||
var encryptedPrivateKeys []*keys.EncryptedPrivateKey
|
||||
var publicKeys [][]byte
|
||||
var err error
|
||||
if !conf.Import {
|
||||
encryptedPrivateKeys, publicKeys, err = keys.CreateKeyPairs(conf.NumPrivateKeys, conf.ECDSA)
|
||||
} else {
|
||||
encryptedPrivateKeys, publicKeys, err = keys.ImportKeyPairs(conf.NumPrivateKeys)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, publicKey := range publicKeys {
|
||||
fmt.Printf("Public key of private key #%d:\n%x\n\n", i+1, publicKey)
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
for i := conf.NumPrivateKeys; i < conf.NumPublicKeys; i++ {
|
||||
fmt.Printf("Enter public key #%d here:\n", i+1)
|
||||
line, isPrefix, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
if isPrefix {
|
||||
return errors.Errorf("Public key is too long")
|
||||
}
|
||||
|
||||
publicKey, err := hex.DecodeString(string(line))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
publicKeys = append(publicKeys, publicKey)
|
||||
}
|
||||
|
||||
err = keys.WriteKeysFile(
|
||||
conf.NetParams(), conf.KeysFile, encryptedPrivateKeys, publicKeys, conf.MinimumSignatures, conf.ECDSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keysFile, err := keys.ReadKeysFile(conf.NetParams(), conf.KeysFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addr, err := libkaspawallet.Address(conf.NetParams(), keysFile.PublicKeys, keysFile.MinimumSignatures, keysFile.ECDSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("The wallet address is:\n%s\n", addr)
|
||||
return nil
|
||||
}
|
||||
62
cmd/kaspawallet/create_unsigned_tx.go
Normal file
62
cmd/kaspawallet/create_unsigned_tx.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
)
|
||||
|
||||
func createUnsignedTransaction(conf *createUnsignedTransactionConfig) error {
|
||||
keysFile, err := keys.ReadKeysFile(conf.NetParams(), conf.KeysFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
toAddress, err := util.DecodeAddress(conf.ToAddress, conf.NetParams().Prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fromAddress, err := libkaspawallet.Address(conf.NetParams(), keysFile.PublicKeys, keysFile.MinimumSignatures, keysFile.ECDSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := connectToRPC(conf.NetParams(), conf.RPCServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
utxos, err := fetchSpendableUTXOs(conf.NetParams(), client, fromAddress.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sendAmountSompi := uint64(conf.SendAmount * util.SompiPerKaspa)
|
||||
|
||||
const feePerInput = 1000
|
||||
selectedUTXOs, changeSompi, err := selectUTXOs(utxos, sendAmountSompi, feePerInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
psTx, err := libkaspawallet.CreateUnsignedTransaction(keysFile.PublicKeys,
|
||||
keysFile.MinimumSignatures,
|
||||
keysFile.ECDSA,
|
||||
[]*libkaspawallet.Payment{{
|
||||
Address: toAddress,
|
||||
Amount: sendAmountSompi,
|
||||
}, {
|
||||
Address: fromAddress,
|
||||
Amount: changeSompi,
|
||||
}}, selectedUTXOs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Created unsigned transaction")
|
||||
fmt.Println(hex.EncodeToString(psTx))
|
||||
return nil
|
||||
}
|
||||
70
cmd/kaspawallet/dump_unencrypted_data.go
Normal file
70
cmd/kaspawallet/dump_unencrypted_data.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func dumpUnencryptedData(conf *dumpUnencryptedDataConfig) error {
|
||||
err := confirmDump()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keysFile, err := keys.ReadKeysFile(conf.NetParams(), conf.KeysFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privateKeys, err := keysFile.DecryptPrivateKeys()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privateKeysPublicKeys := make(map[string]struct{})
|
||||
for i, privateKey := range privateKeys {
|
||||
fmt.Printf("Private key #%d:\n%x\n\n", i+1, privateKey)
|
||||
publicKey, err := libkaspawallet.PublicKeyFromPrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privateKeysPublicKeys[string(publicKey)] = struct{}{}
|
||||
}
|
||||
|
||||
i := 1
|
||||
for _, publicKey := range keysFile.PublicKeys {
|
||||
if _, exists := privateKeysPublicKeys[string(publicKey)]; exists {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("Public key #%d:\n%x\n\n", i, publicKey)
|
||||
i++
|
||||
}
|
||||
|
||||
fmt.Printf("Minimum number of signatures: %d\n", keysFile.MinimumSignatures)
|
||||
return nil
|
||||
}
|
||||
|
||||
func confirmDump() error {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Printf("This operation will print your unencrypted keys on the screen. Anyone that sees this information " +
|
||||
"will be able to steal your funds. Are you sure you want to proceed (y/N)? ")
|
||||
line, isPrefix, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
if isPrefix || string(line) != "y" {
|
||||
return errors.Errorf("Dump aborted by user")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
110
cmd/kaspawallet/keys/create.go
Normal file
110
cmd/kaspawallet/keys/create.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/pkg/errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// CreateKeyPairs generates `numKeys` number of key pairs.
|
||||
func CreateKeyPairs(numKeys uint32, ecdsa bool) (encryptedPrivateKeys []*EncryptedPrivateKey, publicKeys [][]byte, err error) {
|
||||
return createKeyPairsFromFunction(numKeys, func(_ uint32) ([]byte, []byte, error) {
|
||||
return libkaspawallet.CreateKeyPair(ecdsa)
|
||||
})
|
||||
}
|
||||
|
||||
// ImportKeyPairs imports a `numKeys` of private keys and generates key pairs out of them.
|
||||
func ImportKeyPairs(numKeys uint32) (encryptedPrivateKeys []*EncryptedPrivateKey, publicKeys [][]byte, err error) {
|
||||
return createKeyPairsFromFunction(numKeys, func(keyIndex uint32) ([]byte, []byte, error) {
|
||||
fmt.Printf("Enter private key #%d here:\n", keyIndex+1)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
line, isPrefix, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if isPrefix {
|
||||
return nil, nil, errors.Errorf("Private key is too long")
|
||||
}
|
||||
privateKey, err := hex.DecodeString(string(line))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
publicKey, err := libkaspawallet.PublicKeyFromPrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return privateKey, publicKey, nil
|
||||
})
|
||||
}
|
||||
|
||||
func createKeyPairsFromFunction(numKeys uint32, keyPairFunction func(keyIndex uint32) ([]byte, []byte, error)) (
|
||||
encryptedPrivateKeys []*EncryptedPrivateKey, publicKeys [][]byte, err error) {
|
||||
|
||||
password := getPassword("Enter password for the key file:")
|
||||
confirmPassword := getPassword("Confirm password:")
|
||||
|
||||
if subtle.ConstantTimeCompare(password, confirmPassword) != 1 {
|
||||
return nil, nil, errors.New("Passwords are not identical")
|
||||
}
|
||||
|
||||
encryptedPrivateKeys = make([]*EncryptedPrivateKey, 0, numKeys)
|
||||
for i := uint32(0); i < numKeys; i++ {
|
||||
privateKey, publicKey, err := keyPairFunction(i)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
publicKeys = append(publicKeys, publicKey)
|
||||
|
||||
encryptedPrivateKey, err := encryptPrivateKey(privateKey, password)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
encryptedPrivateKeys = append(encryptedPrivateKeys, encryptedPrivateKey)
|
||||
}
|
||||
|
||||
return encryptedPrivateKeys, publicKeys, nil
|
||||
}
|
||||
|
||||
func generateSalt() ([]byte, error) {
|
||||
salt := make([]byte, 16)
|
||||
_, err := rand.Read(salt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return salt, nil
|
||||
}
|
||||
|
||||
func encryptPrivateKey(privateKey []byte, password []byte) (*EncryptedPrivateKey, error) {
|
||||
salt, err := generateSalt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aead, err := getAEAD(password, salt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Select a random nonce, and leave capacity for the ciphertext.
|
||||
nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(privateKey)+aead.Overhead())
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Encrypt the message and append the ciphertext to the nonce.
|
||||
cipher := aead.Seal(nonce, nonce, privateKey, nil)
|
||||
|
||||
return &EncryptedPrivateKey{
|
||||
cipher: cipher,
|
||||
salt: salt,
|
||||
}, nil
|
||||
}
|
||||
41
cmd/kaspawallet/keys/get_password.go
Normal file
41
cmd/kaspawallet/keys/get_password.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.org/x/term"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// getPassword was adapted from https://gist.github.com/jlinoff/e8e26b4ffa38d379c7f1891fd174a6d0#file-getpassword2-go
|
||||
func getPassword(prompt string) []byte {
|
||||
// Get the initial state of the terminal.
|
||||
initialTermState, e1 := term.GetState(int(syscall.Stdin))
|
||||
if e1 != nil {
|
||||
panic(e1)
|
||||
}
|
||||
|
||||
// Restore it in the event of an interrupt.
|
||||
// CITATION: Konstantin Shaposhnikov - https://groups.google.com/forum/#!topic/golang-nuts/kTVAbtee9UA
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, os.Interrupt, os.Kill)
|
||||
go func() {
|
||||
<-c
|
||||
_ = term.Restore(int(syscall.Stdin), initialTermState)
|
||||
os.Exit(1)
|
||||
}()
|
||||
|
||||
// Now get the password.
|
||||
fmt.Print(prompt)
|
||||
p, err := term.ReadPassword(int(syscall.Stdin))
|
||||
fmt.Println()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Stop looking for ^C on the channel.
|
||||
signal.Stop(c)
|
||||
|
||||
return p
|
||||
}
|
||||
257
cmd/kaspawallet/keys/keys.go
Normal file
257
cmd/kaspawallet/keys/keys.go
Normal file
@@ -0,0 +1,257 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/cipher"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/argon2"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultAppDir = util.AppDir("kaspawallet", false)
|
||||
)
|
||||
|
||||
func defaultKeysFile(netParams *dagconfig.Params) string {
|
||||
return filepath.Join(defaultAppDir, netParams.Name, "keys.json")
|
||||
}
|
||||
|
||||
type encryptedPrivateKeyJSON struct {
|
||||
Cipher string `json:"cipher"`
|
||||
Salt string `json:"salt"`
|
||||
}
|
||||
|
||||
type keysFileJSON struct {
|
||||
EncryptedPrivateKeys []*encryptedPrivateKeyJSON `json:"encryptedPrivateKeys"`
|
||||
PublicKeys []string `json:"publicKeys"`
|
||||
MinimumSignatures uint32 `json:"minimumSignatures"`
|
||||
ECDSA bool `json:"ecdsa"`
|
||||
}
|
||||
|
||||
// EncryptedPrivateKey represents an encrypted private key
|
||||
type EncryptedPrivateKey struct {
|
||||
cipher []byte
|
||||
salt []byte
|
||||
}
|
||||
|
||||
// Data holds all the data related to the wallet keys
|
||||
type Data struct {
|
||||
encryptedPrivateKeys []*EncryptedPrivateKey
|
||||
PublicKeys [][]byte
|
||||
MinimumSignatures uint32
|
||||
ECDSA bool
|
||||
}
|
||||
|
||||
func (d *Data) toJSON() *keysFileJSON {
|
||||
encryptedPrivateKeysJSON := make([]*encryptedPrivateKeyJSON, len(d.encryptedPrivateKeys))
|
||||
for i, encryptedPrivateKey := range d.encryptedPrivateKeys {
|
||||
encryptedPrivateKeysJSON[i] = &encryptedPrivateKeyJSON{
|
||||
Cipher: hex.EncodeToString(encryptedPrivateKey.cipher),
|
||||
Salt: hex.EncodeToString(encryptedPrivateKey.salt),
|
||||
}
|
||||
}
|
||||
|
||||
publicKeysHex := make([]string, len(d.PublicKeys))
|
||||
for i, publicKey := range d.PublicKeys {
|
||||
publicKeysHex[i] = hex.EncodeToString(publicKey)
|
||||
}
|
||||
|
||||
return &keysFileJSON{
|
||||
EncryptedPrivateKeys: encryptedPrivateKeysJSON,
|
||||
PublicKeys: publicKeysHex,
|
||||
MinimumSignatures: d.MinimumSignatures,
|
||||
ECDSA: d.ECDSA,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Data) fromJSON(fileJSON *keysFileJSON) error {
|
||||
d.MinimumSignatures = fileJSON.MinimumSignatures
|
||||
d.ECDSA = fileJSON.ECDSA
|
||||
|
||||
d.encryptedPrivateKeys = make([]*EncryptedPrivateKey, len(fileJSON.EncryptedPrivateKeys))
|
||||
for i, encryptedPrivateKeyJSON := range fileJSON.EncryptedPrivateKeys {
|
||||
cipher, err := hex.DecodeString(encryptedPrivateKeyJSON.Cipher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
salt, err := hex.DecodeString(encryptedPrivateKeyJSON.Salt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.encryptedPrivateKeys[i] = &EncryptedPrivateKey{
|
||||
cipher: cipher,
|
||||
salt: salt,
|
||||
}
|
||||
}
|
||||
|
||||
d.PublicKeys = make([][]byte, len(fileJSON.PublicKeys))
|
||||
for i, publicKey := range fileJSON.PublicKeys {
|
||||
var err error
|
||||
d.PublicKeys[i], err = hex.DecodeString(publicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecryptPrivateKeys asks the user to enter the password for the private keys and
|
||||
// returns the decrypted private keys.
|
||||
func (d *Data) DecryptPrivateKeys() ([][]byte, error) {
|
||||
password := getPassword("Password:")
|
||||
privateKeys := make([][]byte, len(d.encryptedPrivateKeys))
|
||||
for i, encryptedPrivateKey := range d.encryptedPrivateKeys {
|
||||
var err error
|
||||
privateKeys[i], err = decryptPrivateKey(encryptedPrivateKey, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return privateKeys, nil
|
||||
}
|
||||
|
||||
// ReadKeysFile returns the data related to the keys file
|
||||
func ReadKeysFile(netParams *dagconfig.Params, path string) (*Data, error) {
|
||||
if path == "" {
|
||||
path = defaultKeysFile(netParams)
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(file)
|
||||
decoder.DisallowUnknownFields()
|
||||
decodedFile := &keysFileJSON{}
|
||||
err = decoder.Decode(&decodedFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keysFile := &Data{}
|
||||
err = keysFile.fromJSON(decodedFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return keysFile, nil
|
||||
}
|
||||
|
||||
func createFileDirectoryIfDoesntExist(path string) error {
|
||||
dir := filepath.Dir(path)
|
||||
exists, err := pathExists(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
return os.MkdirAll(dir, 0700)
|
||||
}
|
||||
|
||||
func pathExists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
// WriteKeysFile writes a keys file with the given data
|
||||
func WriteKeysFile(netParams *dagconfig.Params, path string, encryptedPrivateKeys []*EncryptedPrivateKey,
|
||||
publicKeys [][]byte, minimumSignatures uint32, ecdsa bool) error {
|
||||
|
||||
if path == "" {
|
||||
path = defaultKeysFile(netParams)
|
||||
}
|
||||
|
||||
exists, err := pathExists(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exists {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Printf("The file %s already exists. Are you sure you want to override it (type 'y' to approve)? ", path)
|
||||
line, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if string(line) != "y" {
|
||||
return errors.Errorf("aborted keys file creation")
|
||||
}
|
||||
}
|
||||
|
||||
err = createFileDirectoryIfDoesntExist(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
keysFile := &Data{
|
||||
encryptedPrivateKeys: encryptedPrivateKeys,
|
||||
PublicKeys: publicKeys,
|
||||
MinimumSignatures: minimumSignatures,
|
||||
ECDSA: ecdsa,
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(file)
|
||||
err = encoder.Encode(keysFile.toJSON())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Wrote the keys into %s\n", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAEAD(password, salt []byte) (cipher.AEAD, error) {
|
||||
key := argon2.IDKey(password, salt, 1, 64*1024, uint8(runtime.NumCPU()), 32)
|
||||
return chacha20poly1305.NewX(key)
|
||||
}
|
||||
|
||||
func decryptPrivateKey(encryptedPrivateKey *EncryptedPrivateKey, password []byte) ([]byte, error) {
|
||||
aead, err := getAEAD(password, encryptedPrivateKey.salt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(encryptedPrivateKey.cipher) < aead.NonceSize() {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
|
||||
// Split nonce and ciphertext.
|
||||
nonce, ciphertext := encryptedPrivateKey.cipher[:aead.NonceSize()], encryptedPrivateKey.cipher[aead.NonceSize():]
|
||||
|
||||
// Decrypt the message and check it wasn't tampered with.
|
||||
return aead.Open(nil, nonce, ciphertext, nil)
|
||||
}
|
||||
93
cmd/kaspawallet/libkaspawallet/keypair.go
Normal file
93
cmd/kaspawallet/libkaspawallet/keypair.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package libkaspawallet
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/go-secp256k1"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// CreateKeyPair generates a private-public key pair
|
||||
func CreateKeyPair(ecdsa bool) ([]byte, []byte, error) {
|
||||
if ecdsa {
|
||||
return createKeyPairECDSA()
|
||||
}
|
||||
|
||||
return createKeyPair()
|
||||
}
|
||||
|
||||
func createKeyPair() ([]byte, []byte, error) {
|
||||
keyPair, err := secp256k1.GenerateSchnorrKeyPair()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to generate private key")
|
||||
}
|
||||
publicKey, err := keyPair.SchnorrPublicKey()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to generate public key")
|
||||
}
|
||||
publicKeySerialized, err := publicKey.Serialize()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to serialize public key")
|
||||
}
|
||||
|
||||
return keyPair.SerializePrivateKey()[:], publicKeySerialized[:], nil
|
||||
}
|
||||
|
||||
func createKeyPairECDSA() ([]byte, []byte, error) {
|
||||
keyPair, err := secp256k1.GenerateECDSAPrivateKey()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to generate private key")
|
||||
}
|
||||
publicKey, err := keyPair.ECDSAPublicKey()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to generate public key")
|
||||
}
|
||||
publicKeySerialized, err := publicKey.Serialize()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to serialize public key")
|
||||
}
|
||||
|
||||
return keyPair.Serialize()[:], publicKeySerialized[:], nil
|
||||
}
|
||||
|
||||
// PublicKeyFromPrivateKey returns the public key associated with a private key
|
||||
func PublicKeyFromPrivateKey(privateKeyBytes []byte) ([]byte, error) {
|
||||
keyPair, err := secp256k1.DeserializeSchnorrPrivateKeyFromSlice(privateKeyBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to deserialize private key")
|
||||
}
|
||||
|
||||
publicKey, err := keyPair.SchnorrPublicKey()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to generate public key")
|
||||
}
|
||||
|
||||
publicKeySerialized, err := publicKey.Serialize()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to serialize public key")
|
||||
}
|
||||
|
||||
return publicKeySerialized[:], nil
|
||||
}
|
||||
|
||||
// Address returns the address associated with the given public keys and minimum signatures parameters.
|
||||
func Address(params *dagconfig.Params, pubKeys [][]byte, minimumSignatures uint32, ecdsa bool) (util.Address, error) {
|
||||
sortPublicKeys(pubKeys)
|
||||
if uint32(len(pubKeys)) < minimumSignatures {
|
||||
return nil, errors.Errorf("The minimum amount of signatures (%d) is greater than the amount of "+
|
||||
"provided public keys (%d)", minimumSignatures, len(pubKeys))
|
||||
}
|
||||
if len(pubKeys) == 1 {
|
||||
if ecdsa {
|
||||
return util.NewAddressPublicKeyECDSA(pubKeys[0][:], params.Prefix)
|
||||
}
|
||||
return util.NewAddressPublicKey(pubKeys[0][:], params.Prefix)
|
||||
}
|
||||
|
||||
redeemScript, err := multiSigRedeemScript(pubKeys, minimumSignatures, ecdsa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return util.NewAddressScriptHash(redeemScript, params.Prefix)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
//go:generate protoc --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative wallet.proto
|
||||
|
||||
package protoserialization
|
||||
@@ -0,0 +1,915 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.12.3
|
||||
// source: wallet.proto
|
||||
|
||||
package protoserialization
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type PartiallySignedTransaction struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Tx *TransactionMessage `protobuf:"bytes,1,opt,name=tx,proto3" json:"tx,omitempty"`
|
||||
PartiallySignedInputs []*PartiallySignedInput `protobuf:"bytes,2,rep,name=partiallySignedInputs,proto3" json:"partiallySignedInputs,omitempty"`
|
||||
}
|
||||
|
||||
func (x *PartiallySignedTransaction) Reset() {
|
||||
*x = PartiallySignedTransaction{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_wallet_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *PartiallySignedTransaction) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PartiallySignedTransaction) ProtoMessage() {}
|
||||
|
||||
func (x *PartiallySignedTransaction) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_wallet_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PartiallySignedTransaction.ProtoReflect.Descriptor instead.
|
||||
func (*PartiallySignedTransaction) Descriptor() ([]byte, []int) {
|
||||
return file_wallet_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *PartiallySignedTransaction) GetTx() *TransactionMessage {
|
||||
if x != nil {
|
||||
return x.Tx
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PartiallySignedTransaction) GetPartiallySignedInputs() []*PartiallySignedInput {
|
||||
if x != nil {
|
||||
return x.PartiallySignedInputs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PartiallySignedInput struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
RedeemScript []byte `protobuf:"bytes,1,opt,name=redeemScript,proto3" json:"redeemScript,omitempty"`
|
||||
PrevOutput *TransactionOutput `protobuf:"bytes,2,opt,name=prevOutput,proto3" json:"prevOutput,omitempty"`
|
||||
MinimumSignatures uint32 `protobuf:"varint,3,opt,name=minimumSignatures,proto3" json:"minimumSignatures,omitempty"`
|
||||
PubKeySignaturePairs []*PubKeySignaturePair `protobuf:"bytes,4,rep,name=pubKeySignaturePairs,proto3" json:"pubKeySignaturePairs,omitempty"`
|
||||
}
|
||||
|
||||
func (x *PartiallySignedInput) Reset() {
|
||||
*x = PartiallySignedInput{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_wallet_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *PartiallySignedInput) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PartiallySignedInput) ProtoMessage() {}
|
||||
|
||||
func (x *PartiallySignedInput) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_wallet_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PartiallySignedInput.ProtoReflect.Descriptor instead.
|
||||
func (*PartiallySignedInput) Descriptor() ([]byte, []int) {
|
||||
return file_wallet_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *PartiallySignedInput) GetRedeemScript() []byte {
|
||||
if x != nil {
|
||||
return x.RedeemScript
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PartiallySignedInput) GetPrevOutput() *TransactionOutput {
|
||||
if x != nil {
|
||||
return x.PrevOutput
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PartiallySignedInput) GetMinimumSignatures() uint32 {
|
||||
if x != nil {
|
||||
return x.MinimumSignatures
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *PartiallySignedInput) GetPubKeySignaturePairs() []*PubKeySignaturePair {
|
||||
if x != nil {
|
||||
return x.PubKeySignaturePairs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PubKeySignaturePair struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
PubKey []byte `protobuf:"bytes,1,opt,name=pubKey,proto3" json:"pubKey,omitempty"`
|
||||
Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"`
|
||||
}
|
||||
|
||||
func (x *PubKeySignaturePair) Reset() {
|
||||
*x = PubKeySignaturePair{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_wallet_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *PubKeySignaturePair) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PubKeySignaturePair) ProtoMessage() {}
|
||||
|
||||
func (x *PubKeySignaturePair) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_wallet_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PubKeySignaturePair.ProtoReflect.Descriptor instead.
|
||||
func (*PubKeySignaturePair) Descriptor() ([]byte, []int) {
|
||||
return file_wallet_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *PubKeySignaturePair) GetPubKey() []byte {
|
||||
if x != nil {
|
||||
return x.PubKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PubKeySignaturePair) GetSignature() []byte {
|
||||
if x != nil {
|
||||
return x.Signature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SubnetworkId struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Bytes []byte `protobuf:"bytes,1,opt,name=bytes,proto3" json:"bytes,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SubnetworkId) Reset() {
|
||||
*x = SubnetworkId{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_wallet_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SubnetworkId) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SubnetworkId) ProtoMessage() {}
|
||||
|
||||
func (x *SubnetworkId) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_wallet_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SubnetworkId.ProtoReflect.Descriptor instead.
|
||||
func (*SubnetworkId) Descriptor() ([]byte, []int) {
|
||||
return file_wallet_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *SubnetworkId) GetBytes() []byte {
|
||||
if x != nil {
|
||||
return x.Bytes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TransactionMessage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
|
||||
Inputs []*TransactionInput `protobuf:"bytes,2,rep,name=inputs,proto3" json:"inputs,omitempty"`
|
||||
Outputs []*TransactionOutput `protobuf:"bytes,3,rep,name=outputs,proto3" json:"outputs,omitempty"`
|
||||
LockTime uint64 `protobuf:"varint,4,opt,name=lockTime,proto3" json:"lockTime,omitempty"`
|
||||
SubnetworkId *SubnetworkId `protobuf:"bytes,5,opt,name=subnetworkId,proto3" json:"subnetworkId,omitempty"`
|
||||
Gas uint64 `protobuf:"varint,6,opt,name=gas,proto3" json:"gas,omitempty"`
|
||||
Payload []byte `protobuf:"bytes,8,opt,name=payload,proto3" json:"payload,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TransactionMessage) Reset() {
|
||||
*x = TransactionMessage{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_wallet_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TransactionMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TransactionMessage) ProtoMessage() {}
|
||||
|
||||
func (x *TransactionMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_wallet_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TransactionMessage.ProtoReflect.Descriptor instead.
|
||||
func (*TransactionMessage) Descriptor() ([]byte, []int) {
|
||||
return file_wallet_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *TransactionMessage) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TransactionMessage) GetInputs() []*TransactionInput {
|
||||
if x != nil {
|
||||
return x.Inputs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TransactionMessage) GetOutputs() []*TransactionOutput {
|
||||
if x != nil {
|
||||
return x.Outputs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TransactionMessage) GetLockTime() uint64 {
|
||||
if x != nil {
|
||||
return x.LockTime
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TransactionMessage) GetSubnetworkId() *SubnetworkId {
|
||||
if x != nil {
|
||||
return x.SubnetworkId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TransactionMessage) GetGas() uint64 {
|
||||
if x != nil {
|
||||
return x.Gas
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TransactionMessage) GetPayload() []byte {
|
||||
if x != nil {
|
||||
return x.Payload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TransactionInput struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
PreviousOutpoint *Outpoint `protobuf:"bytes,1,opt,name=previousOutpoint,proto3" json:"previousOutpoint,omitempty"`
|
||||
SignatureScript []byte `protobuf:"bytes,2,opt,name=signatureScript,proto3" json:"signatureScript,omitempty"`
|
||||
Sequence uint64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TransactionInput) Reset() {
|
||||
*x = TransactionInput{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_wallet_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TransactionInput) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TransactionInput) ProtoMessage() {}
|
||||
|
||||
func (x *TransactionInput) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_wallet_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TransactionInput.ProtoReflect.Descriptor instead.
|
||||
func (*TransactionInput) Descriptor() ([]byte, []int) {
|
||||
return file_wallet_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *TransactionInput) GetPreviousOutpoint() *Outpoint {
|
||||
if x != nil {
|
||||
return x.PreviousOutpoint
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TransactionInput) GetSignatureScript() []byte {
|
||||
if x != nil {
|
||||
return x.SignatureScript
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TransactionInput) GetSequence() uint64 {
|
||||
if x != nil {
|
||||
return x.Sequence
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Outpoint struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
TransactionId *TransactionId `protobuf:"bytes,1,opt,name=transactionId,proto3" json:"transactionId,omitempty"`
|
||||
Index uint32 `protobuf:"varint,2,opt,name=index,proto3" json:"index,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Outpoint) Reset() {
|
||||
*x = Outpoint{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_wallet_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Outpoint) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Outpoint) ProtoMessage() {}
|
||||
|
||||
func (x *Outpoint) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_wallet_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Outpoint.ProtoReflect.Descriptor instead.
|
||||
func (*Outpoint) Descriptor() ([]byte, []int) {
|
||||
return file_wallet_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *Outpoint) GetTransactionId() *TransactionId {
|
||||
if x != nil {
|
||||
return x.TransactionId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Outpoint) GetIndex() uint32 {
|
||||
if x != nil {
|
||||
return x.Index
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type TransactionId struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Bytes []byte `protobuf:"bytes,1,opt,name=bytes,proto3" json:"bytes,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TransactionId) Reset() {
|
||||
*x = TransactionId{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_wallet_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TransactionId) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TransactionId) ProtoMessage() {}
|
||||
|
||||
func (x *TransactionId) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_wallet_proto_msgTypes[7]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TransactionId.ProtoReflect.Descriptor instead.
|
||||
func (*TransactionId) Descriptor() ([]byte, []int) {
|
||||
return file_wallet_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *TransactionId) GetBytes() []byte {
|
||||
if x != nil {
|
||||
return x.Bytes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ScriptPublicKey struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Script []byte `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"`
|
||||
Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ScriptPublicKey) Reset() {
|
||||
*x = ScriptPublicKey{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_wallet_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ScriptPublicKey) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ScriptPublicKey) ProtoMessage() {}
|
||||
|
||||
func (x *ScriptPublicKey) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_wallet_proto_msgTypes[8]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ScriptPublicKey.ProtoReflect.Descriptor instead.
|
||||
func (*ScriptPublicKey) Descriptor() ([]byte, []int) {
|
||||
return file_wallet_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *ScriptPublicKey) GetScript() []byte {
|
||||
if x != nil {
|
||||
return x.Script
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ScriptPublicKey) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type TransactionOutput struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
|
||||
ScriptPublicKey *ScriptPublicKey `protobuf:"bytes,2,opt,name=scriptPublicKey,proto3" json:"scriptPublicKey,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TransactionOutput) Reset() {
|
||||
*x = TransactionOutput{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_wallet_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TransactionOutput) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TransactionOutput) ProtoMessage() {}
|
||||
|
||||
func (x *TransactionOutput) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_wallet_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TransactionOutput.ProtoReflect.Descriptor instead.
|
||||
func (*TransactionOutput) Descriptor() ([]byte, []int) {
|
||||
return file_wallet_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *TransactionOutput) GetValue() uint64 {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TransactionOutput) GetScriptPublicKey() *ScriptPublicKey {
|
||||
if x != nil {
|
||||
return x.ScriptPublicKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_wallet_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_wallet_proto_rawDesc = []byte{
|
||||
0x0a, 0x0c, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x22, 0xb4, 0x01, 0x0a, 0x1a, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x79,
|
||||
0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x36, 0x0a, 0x02, 0x74, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65,
|
||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x02, 0x74, 0x78, 0x12, 0x5e, 0x0a, 0x15, 0x70, 0x61, 0x72,
|
||||
0x74, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x70, 0x75,
|
||||
0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x61,
|
||||
0x72, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x70,
|
||||
0x75, 0x74, 0x52, 0x15, 0x70, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x53, 0x69, 0x67,
|
||||
0x6e, 0x65, 0x64, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x22, 0x8c, 0x02, 0x0a, 0x14, 0x50, 0x61,
|
||||
0x72, 0x74, 0x69, 0x61, 0x6c, 0x6c, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x49, 0x6e, 0x70,
|
||||
0x75, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x53, 0x63, 0x72, 0x69,
|
||||
0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d,
|
||||
0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x45, 0x0a, 0x0a, 0x70, 0x72, 0x65, 0x76, 0x4f, 0x75,
|
||||
0x74, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
|
||||
0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75,
|
||||
0x74, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x76, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x2c, 0x0a,
|
||||
0x11, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
|
||||
0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75,
|
||||
0x6d, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x5b, 0x0a, 0x14, 0x70,
|
||||
0x75, 0x62, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x50, 0x61,
|
||||
0x69, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50,
|
||||
0x75, 0x62, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x50, 0x61,
|
||||
0x69, 0x72, 0x52, 0x14, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74,
|
||||
0x75, 0x72, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x22, 0x4b, 0x0a, 0x13, 0x50, 0x75, 0x62, 0x4b,
|
||||
0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||
0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61,
|
||||
0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e,
|
||||
0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x24, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77,
|
||||
0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, 0xbb, 0x02, 0x0a, 0x12,
|
||||
0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 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, 0x3c, 0x0a, 0x06,
|
||||
0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x70,
|
||||
0x75, 0x74, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x3f, 0x0a, 0x07, 0x6f, 0x75,
|
||||
0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70,
|
||||
0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65,
|
||||
0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x2e, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x52,
|
||||
0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x10, 0x0a,
|
||||
0x03, 0x67, 0x61, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x67, 0x61, 0x73, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xa2, 0x01, 0x0a, 0x10, 0x54, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x48,
|
||||
0x0a, 0x10, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69,
|
||||
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4f, 0x75,
|
||||
0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x10, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73,
|
||||
0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e,
|
||||
0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x63, 0x72, 0x69,
|
||||
0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x69,
|
||||
0x0a, 0x08, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x0d, 0x74, 0x72,
|
||||
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69,
|
||||
0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x49, 0x64, 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, 0x25, 0x0a, 0x0d, 0x54, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79,
|
||||
0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73,
|
||||
0x22, 0x43, 0x0a, 0x0f, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||
0x4b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76,
|
||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65,
|
||||
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x78, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
|
||||
0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||
0x12, 0x4d, 0x0a, 0x0f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||
0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x42,
|
||||
0x5c, 0x5a, 0x5a, 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, 0x6c, 0x69,
|
||||
0x62, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2f, 0x73, 0x65, 0x72,
|
||||
0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_wallet_proto_rawDescOnce sync.Once
|
||||
file_wallet_proto_rawDescData = file_wallet_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_wallet_proto_rawDescGZIP() []byte {
|
||||
file_wallet_proto_rawDescOnce.Do(func() {
|
||||
file_wallet_proto_rawDescData = protoimpl.X.CompressGZIP(file_wallet_proto_rawDescData)
|
||||
})
|
||||
return file_wallet_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_wallet_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||
var file_wallet_proto_goTypes = []interface{}{
|
||||
(*PartiallySignedTransaction)(nil), // 0: protoserialization.PartiallySignedTransaction
|
||||
(*PartiallySignedInput)(nil), // 1: protoserialization.PartiallySignedInput
|
||||
(*PubKeySignaturePair)(nil), // 2: protoserialization.PubKeySignaturePair
|
||||
(*SubnetworkId)(nil), // 3: protoserialization.SubnetworkId
|
||||
(*TransactionMessage)(nil), // 4: protoserialization.TransactionMessage
|
||||
(*TransactionInput)(nil), // 5: protoserialization.TransactionInput
|
||||
(*Outpoint)(nil), // 6: protoserialization.Outpoint
|
||||
(*TransactionId)(nil), // 7: protoserialization.TransactionId
|
||||
(*ScriptPublicKey)(nil), // 8: protoserialization.ScriptPublicKey
|
||||
(*TransactionOutput)(nil), // 9: protoserialization.TransactionOutput
|
||||
}
|
||||
var file_wallet_proto_depIdxs = []int32{
|
||||
4, // 0: protoserialization.PartiallySignedTransaction.tx:type_name -> protoserialization.TransactionMessage
|
||||
1, // 1: protoserialization.PartiallySignedTransaction.partiallySignedInputs:type_name -> protoserialization.PartiallySignedInput
|
||||
9, // 2: protoserialization.PartiallySignedInput.prevOutput:type_name -> protoserialization.TransactionOutput
|
||||
2, // 3: protoserialization.PartiallySignedInput.pubKeySignaturePairs:type_name -> protoserialization.PubKeySignaturePair
|
||||
5, // 4: protoserialization.TransactionMessage.inputs:type_name -> protoserialization.TransactionInput
|
||||
9, // 5: protoserialization.TransactionMessage.outputs:type_name -> protoserialization.TransactionOutput
|
||||
3, // 6: protoserialization.TransactionMessage.subnetworkId:type_name -> protoserialization.SubnetworkId
|
||||
6, // 7: protoserialization.TransactionInput.previousOutpoint:type_name -> protoserialization.Outpoint
|
||||
7, // 8: protoserialization.Outpoint.transactionId:type_name -> protoserialization.TransactionId
|
||||
8, // 9: protoserialization.TransactionOutput.scriptPublicKey:type_name -> protoserialization.ScriptPublicKey
|
||||
10, // [10:10] is the sub-list for method output_type
|
||||
10, // [10:10] is the sub-list for method input_type
|
||||
10, // [10:10] is the sub-list for extension type_name
|
||||
10, // [10:10] is the sub-list for extension extendee
|
||||
0, // [0:10] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_wallet_proto_init() }
|
||||
func file_wallet_proto_init() {
|
||||
if File_wallet_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_wallet_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*PartiallySignedTransaction); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_wallet_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*PartiallySignedInput); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_wallet_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*PubKeySignaturePair); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_wallet_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SubnetworkId); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_wallet_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TransactionMessage); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_wallet_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TransactionInput); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_wallet_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Outpoint); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_wallet_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TransactionId); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_wallet_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ScriptPublicKey); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_wallet_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TransactionOutput); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_wallet_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 10,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_wallet_proto_goTypes,
|
||||
DependencyIndexes: file_wallet_proto_depIdxs,
|
||||
MessageInfos: file_wallet_proto_msgTypes,
|
||||
}.Build()
|
||||
File_wallet_proto = out.File
|
||||
file_wallet_proto_rawDesc = nil
|
||||
file_wallet_proto_goTypes = nil
|
||||
file_wallet_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
syntax = "proto3";
|
||||
package protoserialization;
|
||||
|
||||
option go_package = "github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/serialization/protoserialization";
|
||||
|
||||
message PartiallySignedTransaction{
|
||||
TransactionMessage tx = 1;
|
||||
repeated PartiallySignedInput partiallySignedInputs = 2;
|
||||
}
|
||||
|
||||
message PartiallySignedInput{
|
||||
bytes redeemScript = 1;
|
||||
TransactionOutput prevOutput = 2;
|
||||
uint32 minimumSignatures = 3;
|
||||
repeated PubKeySignaturePair pubKeySignaturePairs = 4;
|
||||
}
|
||||
|
||||
message PubKeySignaturePair{
|
||||
bytes pubKey = 1;
|
||||
bytes signature = 2;
|
||||
}
|
||||
|
||||
message SubnetworkId{
|
||||
bytes bytes = 1;
|
||||
}
|
||||
|
||||
message TransactionMessage{
|
||||
uint32 version = 1;
|
||||
repeated TransactionInput inputs = 2;
|
||||
repeated TransactionOutput outputs = 3;
|
||||
uint64 lockTime = 4;
|
||||
SubnetworkId subnetworkId = 5;
|
||||
uint64 gas = 6;
|
||||
bytes payload = 8;
|
||||
}
|
||||
|
||||
message TransactionInput{
|
||||
Outpoint previousOutpoint = 1;
|
||||
bytes signatureScript = 2;
|
||||
uint64 sequence = 3;
|
||||
}
|
||||
|
||||
message Outpoint{
|
||||
TransactionId transactionId = 1;
|
||||
uint32 index = 2;
|
||||
}
|
||||
|
||||
message TransactionId{
|
||||
bytes bytes = 1;
|
||||
}
|
||||
message ScriptPublicKey {
|
||||
bytes script = 1;
|
||||
uint32 version = 2;
|
||||
}
|
||||
|
||||
message TransactionOutput{
|
||||
uint64 value = 1;
|
||||
ScriptPublicKey scriptPublicKey = 2;
|
||||
}
|
||||
273
cmd/kaspawallet/libkaspawallet/serialization/serialization.go
Normal file
273
cmd/kaspawallet/libkaspawallet/serialization/serialization.go
Normal file
@@ -0,0 +1,273 @@
|
||||
package serialization
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/serialization/protoserialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"github.com/pkg/errors"
|
||||
"math"
|
||||
)
|
||||
|
||||
// PartiallySignedTransaction is a type that is intended
|
||||
// to be transferred between multiple parties so each
|
||||
// party will be able to sign the transaction before
|
||||
// it's fully signed.
|
||||
type PartiallySignedTransaction struct {
|
||||
Tx *externalapi.DomainTransaction
|
||||
PartiallySignedInputs []*PartiallySignedInput
|
||||
}
|
||||
|
||||
// PartiallySignedInput represents an input signed
|
||||
// only by some of the relevant parties.
|
||||
type PartiallySignedInput struct {
|
||||
RedeeemScript []byte
|
||||
PrevOutput *externalapi.DomainTransactionOutput
|
||||
MinimumSignatures uint32
|
||||
PubKeySignaturePairs []*PubKeySignaturePair
|
||||
}
|
||||
|
||||
// PubKeySignaturePair is a pair of public key and (potentially) its associated signature
|
||||
type PubKeySignaturePair struct {
|
||||
PubKey []byte
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
// DeserializePartiallySignedTransaction deserializes a byte slice into PartiallySignedTransaction.
|
||||
func DeserializePartiallySignedTransaction(serializedPartiallySignedTransaction []byte) (*PartiallySignedTransaction, error) {
|
||||
protoPartiallySignedTransaction := &protoserialization.PartiallySignedTransaction{}
|
||||
err := proto.Unmarshal(serializedPartiallySignedTransaction, protoPartiallySignedTransaction)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return partiallySignedTransactionFromProto(protoPartiallySignedTransaction)
|
||||
}
|
||||
|
||||
// SerializePartiallySignedTransaction serializes a PartiallySignedTransaction.
|
||||
func SerializePartiallySignedTransaction(partiallySignedTransaction *PartiallySignedTransaction) ([]byte, error) {
|
||||
return proto.Marshal(partiallySignedTransactionToProto(partiallySignedTransaction))
|
||||
}
|
||||
|
||||
func partiallySignedTransactionFromProto(protoPartiallySignedTransaction *protoserialization.PartiallySignedTransaction) (*PartiallySignedTransaction, error) {
|
||||
tx, err := transactionFromProto(protoPartiallySignedTransaction.Tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
inputs := make([]*PartiallySignedInput, len(protoPartiallySignedTransaction.PartiallySignedInputs))
|
||||
for i, protoInput := range protoPartiallySignedTransaction.PartiallySignedInputs {
|
||||
inputs[i], err = partiallySignedInputFromProto(protoInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &PartiallySignedTransaction{
|
||||
Tx: tx,
|
||||
PartiallySignedInputs: inputs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func partiallySignedTransactionToProto(partiallySignedTransaction *PartiallySignedTransaction) *protoserialization.PartiallySignedTransaction {
|
||||
protoInputs := make([]*protoserialization.PartiallySignedInput, len(partiallySignedTransaction.PartiallySignedInputs))
|
||||
for i, input := range partiallySignedTransaction.PartiallySignedInputs {
|
||||
protoInputs[i] = partiallySignedInputToProto(input)
|
||||
}
|
||||
|
||||
return &protoserialization.PartiallySignedTransaction{
|
||||
Tx: transactionToProto(partiallySignedTransaction.Tx),
|
||||
PartiallySignedInputs: protoInputs,
|
||||
}
|
||||
}
|
||||
|
||||
func partiallySignedInputFromProto(protoPartiallySignedInput *protoserialization.PartiallySignedInput) (*PartiallySignedInput, error) {
|
||||
output, err := transactionOutputFromProto(protoPartiallySignedInput.PrevOutput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubKeySignaturePairs := make([]*PubKeySignaturePair, len(protoPartiallySignedInput.PubKeySignaturePairs))
|
||||
for i, protoPair := range protoPartiallySignedInput.PubKeySignaturePairs {
|
||||
pubKeySignaturePairs[i] = pubKeySignaturePairFromProto(protoPair)
|
||||
}
|
||||
|
||||
return &PartiallySignedInput{
|
||||
RedeeemScript: protoPartiallySignedInput.RedeemScript,
|
||||
PrevOutput: output,
|
||||
MinimumSignatures: protoPartiallySignedInput.MinimumSignatures,
|
||||
PubKeySignaturePairs: pubKeySignaturePairs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func partiallySignedInputToProto(partiallySignedInput *PartiallySignedInput) *protoserialization.PartiallySignedInput {
|
||||
protoPairs := make([]*protoserialization.PubKeySignaturePair, len(partiallySignedInput.PubKeySignaturePairs))
|
||||
for i, pair := range partiallySignedInput.PubKeySignaturePairs {
|
||||
protoPairs[i] = pubKeySignaturePairToProto(pair)
|
||||
}
|
||||
|
||||
return &protoserialization.PartiallySignedInput{
|
||||
RedeemScript: partiallySignedInput.RedeeemScript,
|
||||
PrevOutput: transactionOutputToProto(partiallySignedInput.PrevOutput),
|
||||
MinimumSignatures: partiallySignedInput.MinimumSignatures,
|
||||
PubKeySignaturePairs: protoPairs,
|
||||
}
|
||||
}
|
||||
|
||||
func pubKeySignaturePairFromProto(protoPubKeySignaturePair *protoserialization.PubKeySignaturePair) *PubKeySignaturePair {
|
||||
return &PubKeySignaturePair{
|
||||
PubKey: protoPubKeySignaturePair.PubKey,
|
||||
Signature: protoPubKeySignaturePair.Signature,
|
||||
}
|
||||
}
|
||||
|
||||
func pubKeySignaturePairToProto(pubKeySignaturePair *PubKeySignaturePair) *protoserialization.PubKeySignaturePair {
|
||||
return &protoserialization.PubKeySignaturePair{
|
||||
PubKey: pubKeySignaturePair.PubKey,
|
||||
Signature: pubKeySignaturePair.Signature,
|
||||
}
|
||||
}
|
||||
|
||||
func transactionFromProto(protoTransaction *protoserialization.TransactionMessage) (*externalapi.DomainTransaction, error) {
|
||||
if protoTransaction.Version > math.MaxUint16 {
|
||||
return nil, errors.Errorf("protoTransaction.Version is %d and is too big to be a uint16", protoTransaction.Version)
|
||||
}
|
||||
|
||||
inputs := make([]*externalapi.DomainTransactionInput, len(protoTransaction.Inputs))
|
||||
for i, protoInput := range protoTransaction.Inputs {
|
||||
var err error
|
||||
inputs[i], err = transactionInputFromProto(protoInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
outputs := make([]*externalapi.DomainTransactionOutput, len(protoTransaction.Outputs))
|
||||
for i, protoOutput := range protoTransaction.Outputs {
|
||||
var err error
|
||||
outputs[i], err = transactionOutputFromProto(protoOutput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
subnetworkID, err := subnetworks.FromBytes(protoTransaction.SubnetworkId.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &externalapi.DomainTransaction{
|
||||
Version: uint16(protoTransaction.Version),
|
||||
Inputs: inputs,
|
||||
Outputs: outputs,
|
||||
LockTime: protoTransaction.LockTime,
|
||||
SubnetworkID: *subnetworkID,
|
||||
Gas: protoTransaction.Gas,
|
||||
Payload: protoTransaction.Payload,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func transactionToProto(tx *externalapi.DomainTransaction) *protoserialization.TransactionMessage {
|
||||
protoInputs := make([]*protoserialization.TransactionInput, len(tx.Inputs))
|
||||
for i, input := range tx.Inputs {
|
||||
protoInputs[i] = transactionInputToProto(input)
|
||||
}
|
||||
|
||||
protoOutputs := make([]*protoserialization.TransactionOutput, len(tx.Outputs))
|
||||
for i, output := range tx.Outputs {
|
||||
protoOutputs[i] = transactionOutputToProto(output)
|
||||
}
|
||||
|
||||
return &protoserialization.TransactionMessage{
|
||||
Version: uint32(tx.Version),
|
||||
Inputs: protoInputs,
|
||||
Outputs: protoOutputs,
|
||||
LockTime: tx.LockTime,
|
||||
SubnetworkId: &protoserialization.SubnetworkId{Bytes: tx.SubnetworkID[:]},
|
||||
Gas: tx.Gas,
|
||||
Payload: tx.Payload,
|
||||
}
|
||||
}
|
||||
|
||||
func transactionInputFromProto(protoInput *protoserialization.TransactionInput) (*externalapi.DomainTransactionInput, error) {
|
||||
outpoint, err := outpointFromProto(protoInput.PreviousOutpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &externalapi.DomainTransactionInput{
|
||||
PreviousOutpoint: *outpoint,
|
||||
SignatureScript: protoInput.SignatureScript,
|
||||
Sequence: protoInput.Sequence,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func transactionInputToProto(input *externalapi.DomainTransactionInput) *protoserialization.TransactionInput {
|
||||
return &protoserialization.TransactionInput{
|
||||
PreviousOutpoint: outpointToProto(&input.PreviousOutpoint),
|
||||
SignatureScript: input.SignatureScript,
|
||||
Sequence: input.Sequence,
|
||||
}
|
||||
}
|
||||
|
||||
func outpointFromProto(protoOutpoint *protoserialization.Outpoint) (*externalapi.DomainOutpoint, error) {
|
||||
txID, err := transactionIDFromProto(protoOutpoint.TransactionId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &externalapi.DomainOutpoint{
|
||||
TransactionID: *txID,
|
||||
Index: protoOutpoint.Index,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func outpointToProto(outpoint *externalapi.DomainOutpoint) *protoserialization.Outpoint {
|
||||
return &protoserialization.Outpoint{
|
||||
TransactionId: &protoserialization.TransactionId{Bytes: outpoint.TransactionID.ByteSlice()},
|
||||
Index: outpoint.Index,
|
||||
}
|
||||
}
|
||||
|
||||
func transactionIDFromProto(protoTxID *protoserialization.TransactionId) (*externalapi.DomainTransactionID, error) {
|
||||
if protoTxID == nil {
|
||||
return nil, errors.Errorf("protoTxID is nil")
|
||||
}
|
||||
|
||||
return externalapi.NewDomainTransactionIDFromByteSlice(protoTxID.Bytes)
|
||||
}
|
||||
|
||||
func transactionOutputFromProto(protoOutput *protoserialization.TransactionOutput) (*externalapi.DomainTransactionOutput, error) {
|
||||
scriptPublicKey, err := scriptPublicKeyFromProto(protoOutput.ScriptPublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &externalapi.DomainTransactionOutput{
|
||||
Value: protoOutput.Value,
|
||||
ScriptPublicKey: scriptPublicKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func transactionOutputToProto(output *externalapi.DomainTransactionOutput) *protoserialization.TransactionOutput {
|
||||
return &protoserialization.TransactionOutput{
|
||||
Value: output.Value,
|
||||
ScriptPublicKey: scriptPublicKeyToProto(output.ScriptPublicKey),
|
||||
}
|
||||
}
|
||||
|
||||
func scriptPublicKeyFromProto(protoScriptPublicKey *protoserialization.ScriptPublicKey) (*externalapi.ScriptPublicKey, error) {
|
||||
if protoScriptPublicKey.Version > math.MaxUint16 {
|
||||
return nil, errors.Errorf("protoOutput.ScriptPublicKey.Version is %d and is too big to be a uint16", protoScriptPublicKey.Version)
|
||||
}
|
||||
return &externalapi.ScriptPublicKey{
|
||||
Script: protoScriptPublicKey.Script,
|
||||
Version: uint16(protoScriptPublicKey.Version),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func scriptPublicKeyToProto(scriptPublicKey *externalapi.ScriptPublicKey) *protoserialization.ScriptPublicKey {
|
||||
return &protoserialization.ScriptPublicKey{
|
||||
Script: scriptPublicKey.Script,
|
||||
Version: uint32(scriptPublicKey.Version),
|
||||
}
|
||||
}
|
||||
146
cmd/kaspawallet/libkaspawallet/sign.go
Normal file
146
cmd/kaspawallet/libkaspawallet/sign.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package libkaspawallet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/kaspanet/go-secp256k1"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/serialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type signer interface {
|
||||
rawTxInSignature(tx *externalapi.DomainTransaction, idx int, hashType consensushashing.SigHashType,
|
||||
sighashReusedValues *consensushashing.SighashReusedValues) ([]byte, error)
|
||||
serializedPublicKey() ([]byte, error)
|
||||
}
|
||||
|
||||
type schnorrSigner secp256k1.SchnorrKeyPair
|
||||
|
||||
func (s *schnorrSigner) rawTxInSignature(tx *externalapi.DomainTransaction, idx int, hashType consensushashing.SigHashType,
|
||||
sighashReusedValues *consensushashing.SighashReusedValues) ([]byte, error) {
|
||||
return txscript.RawTxInSignature(tx, idx, hashType, (*secp256k1.SchnorrKeyPair)(s), sighashReusedValues)
|
||||
}
|
||||
|
||||
func (s *schnorrSigner) serializedPublicKey() ([]byte, error) {
|
||||
publicKey, err := (*secp256k1.SchnorrKeyPair)(s).SchnorrPublicKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serializedPublicKey, err := publicKey.Serialize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return serializedPublicKey[:], nil
|
||||
}
|
||||
|
||||
type ecdsaSigner secp256k1.ECDSAPrivateKey
|
||||
|
||||
func (e *ecdsaSigner) rawTxInSignature(tx *externalapi.DomainTransaction, idx int, hashType consensushashing.SigHashType,
|
||||
sighashReusedValues *consensushashing.SighashReusedValues) ([]byte, error) {
|
||||
return txscript.RawTxInSignatureECDSA(tx, idx, hashType, (*secp256k1.ECDSAPrivateKey)(e), sighashReusedValues)
|
||||
}
|
||||
|
||||
func (e *ecdsaSigner) serializedPublicKey() ([]byte, error) {
|
||||
publicKey, err := (*secp256k1.ECDSAPrivateKey)(e).ECDSAPublicKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serializedPublicKey, err := publicKey.Serialize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return serializedPublicKey[:], nil
|
||||
}
|
||||
|
||||
func deserializeECDSAPrivateKey(privateKey []byte, ecdsa bool) (signer, error) {
|
||||
if ecdsa {
|
||||
keyPair, err := secp256k1.DeserializeECDSAPrivateKeyFromSlice(privateKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error deserializing private key")
|
||||
}
|
||||
|
||||
return (*ecdsaSigner)(keyPair), nil
|
||||
}
|
||||
|
||||
keyPair, err := secp256k1.DeserializeSchnorrPrivateKeyFromSlice(privateKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error deserializing private key")
|
||||
}
|
||||
|
||||
return (*schnorrSigner)(keyPair), nil
|
||||
}
|
||||
|
||||
// Sign signs the transaction with the given private keys
|
||||
func Sign(privateKeys [][]byte, serializedPSTx []byte, ecdsa bool) ([]byte, error) {
|
||||
keyPairs := make([]signer, len(privateKeys))
|
||||
for i, privateKey := range privateKeys {
|
||||
var err error
|
||||
keyPairs[i], err = deserializeECDSAPrivateKey(privateKey, ecdsa)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Error deserializing private key")
|
||||
}
|
||||
}
|
||||
|
||||
partiallySignedTransaction, err := serialization.DeserializePartiallySignedTransaction(serializedPSTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, keyPair := range keyPairs {
|
||||
err = sign(keyPair, partiallySignedTransaction)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return serialization.SerializePartiallySignedTransaction(partiallySignedTransaction)
|
||||
}
|
||||
|
||||
func sign(keyPair signer, psTx *serialization.PartiallySignedTransaction) error {
|
||||
if isTransactionFullySigned(psTx) {
|
||||
return nil
|
||||
}
|
||||
|
||||
serializedPublicKey, err := keyPair.serializedPublicKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sighashReusedValues := &consensushashing.SighashReusedValues{}
|
||||
for i, partiallySignedInput := range psTx.PartiallySignedInputs {
|
||||
prevOut := partiallySignedInput.PrevOutput
|
||||
psTx.Tx.Inputs[i].UTXOEntry = utxo.NewUTXOEntry(
|
||||
prevOut.Value,
|
||||
prevOut.ScriptPublicKey,
|
||||
false, // This is a fake value, because it's irrelevant for the signature
|
||||
0, // This is a fake value, because it's irrelevant for the signature
|
||||
)
|
||||
}
|
||||
|
||||
signed := false
|
||||
for i, partiallySignedInput := range psTx.PartiallySignedInputs {
|
||||
for _, pair := range partiallySignedInput.PubKeySignaturePairs {
|
||||
if bytes.Equal(pair.PubKey, serializedPublicKey[:]) {
|
||||
pair.Signature, err = keyPair.rawTxInSignature(psTx.Tx, i, consensushashing.SigHashAll, sighashReusedValues)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !signed {
|
||||
return errors.Errorf("Public key doesn't match any of the transaction public keys")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
206
cmd/kaspawallet/libkaspawallet/transaction.go
Normal file
206
cmd/kaspawallet/libkaspawallet/transaction.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package libkaspawallet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/serialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/pkg/errors"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Payment contains a recipient payment details
|
||||
type Payment struct {
|
||||
Address util.Address
|
||||
Amount uint64
|
||||
}
|
||||
|
||||
func sortPublicKeys(publicKeys [][]byte) {
|
||||
sort.Slice(publicKeys, func(i, j int) bool {
|
||||
return bytes.Compare(publicKeys[i], publicKeys[j]) < 0
|
||||
})
|
||||
}
|
||||
|
||||
// CreateUnsignedTransaction creates an unsigned transaction
|
||||
func CreateUnsignedTransaction(
|
||||
pubKeys [][]byte,
|
||||
minimumSignatures uint32,
|
||||
ecdsa bool,
|
||||
payments []*Payment,
|
||||
selectedUTXOs []*externalapi.OutpointAndUTXOEntryPair) ([]byte, error) {
|
||||
|
||||
sortPublicKeys(pubKeys)
|
||||
unsignedTransaction, err := createUnsignedTransaction(pubKeys, minimumSignatures, ecdsa, payments, selectedUTXOs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return serialization.SerializePartiallySignedTransaction(unsignedTransaction)
|
||||
}
|
||||
|
||||
func multiSigRedeemScript(pubKeys [][]byte, minimumSignatures uint32, ecdsa bool) ([]byte, error) {
|
||||
scriptBuilder := txscript.NewScriptBuilder()
|
||||
scriptBuilder.AddInt64(int64(minimumSignatures))
|
||||
for _, key := range pubKeys {
|
||||
scriptBuilder.AddData(key)
|
||||
}
|
||||
scriptBuilder.AddInt64(int64(len(pubKeys)))
|
||||
|
||||
if ecdsa {
|
||||
scriptBuilder.AddOp(txscript.OpCheckMultiSigECDSA)
|
||||
} else {
|
||||
scriptBuilder.AddOp(txscript.OpCheckMultiSig)
|
||||
}
|
||||
|
||||
return scriptBuilder.Script()
|
||||
}
|
||||
|
||||
func createUnsignedTransaction(
|
||||
pubKeys [][]byte,
|
||||
minimumSignatures uint32,
|
||||
ecdsa bool,
|
||||
payments []*Payment,
|
||||
selectedUTXOs []*externalapi.OutpointAndUTXOEntryPair) (*serialization.PartiallySignedTransaction, error) {
|
||||
|
||||
var redeemScript []byte
|
||||
if len(pubKeys) > 1 {
|
||||
var err error
|
||||
redeemScript, err = multiSigRedeemScript(pubKeys, minimumSignatures, ecdsa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
inputs := make([]*externalapi.DomainTransactionInput, len(selectedUTXOs))
|
||||
partiallySignedInputs := make([]*serialization.PartiallySignedInput, len(selectedUTXOs))
|
||||
for i, utxo := range selectedUTXOs {
|
||||
emptyPubKeySignaturePairs := make([]*serialization.PubKeySignaturePair, len(pubKeys))
|
||||
for i, pubKey := range pubKeys {
|
||||
emptyPubKeySignaturePairs[i] = &serialization.PubKeySignaturePair{
|
||||
PubKey: pubKey,
|
||||
}
|
||||
}
|
||||
|
||||
inputs[i] = &externalapi.DomainTransactionInput{PreviousOutpoint: *utxo.Outpoint}
|
||||
partiallySignedInputs[i] = &serialization.PartiallySignedInput{
|
||||
RedeeemScript: redeemScript,
|
||||
PrevOutput: &externalapi.DomainTransactionOutput{
|
||||
Value: utxo.UTXOEntry.Amount(),
|
||||
ScriptPublicKey: utxo.UTXOEntry.ScriptPublicKey(),
|
||||
},
|
||||
MinimumSignatures: minimumSignatures,
|
||||
PubKeySignaturePairs: emptyPubKeySignaturePairs,
|
||||
}
|
||||
}
|
||||
|
||||
outputs := make([]*externalapi.DomainTransactionOutput, len(payments))
|
||||
for i, payment := range payments {
|
||||
scriptPublicKey, err := txscript.PayToAddrScript(payment.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputs[i] = &externalapi.DomainTransactionOutput{
|
||||
Value: payment.Amount,
|
||||
ScriptPublicKey: scriptPublicKey,
|
||||
}
|
||||
}
|
||||
|
||||
domainTransaction := &externalapi.DomainTransaction{
|
||||
Version: constants.MaxTransactionVersion,
|
||||
Inputs: inputs,
|
||||
Outputs: outputs,
|
||||
LockTime: 0,
|
||||
SubnetworkID: subnetworks.SubnetworkIDNative,
|
||||
Gas: 0,
|
||||
Payload: nil,
|
||||
}
|
||||
|
||||
return &serialization.PartiallySignedTransaction{
|
||||
Tx: domainTransaction,
|
||||
PartiallySignedInputs: partiallySignedInputs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsTransactionFullySigned returns whether the transaction is fully signed and ready to broadcast.
|
||||
func IsTransactionFullySigned(psTxBytes []byte) (bool, error) {
|
||||
partiallySignedTransaction, err := serialization.DeserializePartiallySignedTransaction(psTxBytes)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return isTransactionFullySigned(partiallySignedTransaction), nil
|
||||
}
|
||||
|
||||
func isTransactionFullySigned(psTx *serialization.PartiallySignedTransaction) bool {
|
||||
for _, input := range psTx.PartiallySignedInputs {
|
||||
numSignatures := 0
|
||||
for _, pair := range input.PubKeySignaturePairs {
|
||||
if pair.Signature != nil {
|
||||
numSignatures++
|
||||
}
|
||||
}
|
||||
if uint32(numSignatures) < input.MinimumSignatures {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ExtractTransaction extracts a domain transaction from partially signed transaction after all of the
|
||||
// relevant parties have signed it.
|
||||
func ExtractTransaction(psTxBytes []byte) (*externalapi.DomainTransaction, error) {
|
||||
partiallySignedTransaction, err := serialization.DeserializePartiallySignedTransaction(psTxBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return extractTransaction(partiallySignedTransaction)
|
||||
}
|
||||
|
||||
func extractTransaction(psTx *serialization.PartiallySignedTransaction) (*externalapi.DomainTransaction, error) {
|
||||
for i, input := range psTx.PartiallySignedInputs {
|
||||
isMultisig := input.RedeeemScript != nil
|
||||
scriptBuilder := txscript.NewScriptBuilder()
|
||||
if isMultisig {
|
||||
signatureCount := 0
|
||||
for _, pair := range input.PubKeySignaturePairs {
|
||||
if pair.Signature != nil {
|
||||
scriptBuilder.AddData(pair.Signature)
|
||||
signatureCount++
|
||||
}
|
||||
}
|
||||
if uint32(signatureCount) < input.MinimumSignatures {
|
||||
return nil, errors.Errorf("missing %d signatures", input.MinimumSignatures-uint32(signatureCount))
|
||||
}
|
||||
|
||||
scriptBuilder.AddData(input.RedeeemScript)
|
||||
sigScript, err := scriptBuilder.Script()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
psTx.Tx.Inputs[i].SignatureScript = sigScript
|
||||
} else {
|
||||
if len(input.PubKeySignaturePairs) > 1 {
|
||||
return nil, errors.Errorf("Cannot sign on P2PK when len(input.PubKeySignaturePairs) > 1")
|
||||
}
|
||||
|
||||
if input.PubKeySignaturePairs[0].Signature == nil {
|
||||
return nil, errors.Errorf("missing signature")
|
||||
}
|
||||
|
||||
sigScript, err := txscript.NewScriptBuilder().
|
||||
AddData(input.PubKeySignaturePairs[0].Signature).
|
||||
Script()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
psTx.Tx.Inputs[i].SignatureScript = sigScript
|
||||
}
|
||||
}
|
||||
return psTx.Tx, nil
|
||||
}
|
||||
290
cmd/kaspawallet/libkaspawallet/transaction_test.go
Normal file
290
cmd/kaspawallet/libkaspawallet/transaction_test.go
Normal file
@@ -0,0 +1,290 @@
|
||||
package libkaspawallet_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func forSchnorrAndECDSA(t *testing.T, testFunc func(t *testing.T, ecdsa bool)) {
|
||||
t.Run("schnorr", func(t *testing.T) {
|
||||
testFunc(t, false)
|
||||
})
|
||||
|
||||
t.Run("ecdsa", func(t *testing.T) {
|
||||
testFunc(t, true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMultisig(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
forSchnorrAndECDSA(t, func(t *testing.T, ecdsa bool) {
|
||||
consensusConfig.BlockCoinbaseMaturity = 0
|
||||
tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestMultisig")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
const numKeys = 3
|
||||
privateKeys := make([][]byte, numKeys)
|
||||
publicKeys := make([][]byte, numKeys)
|
||||
for i := 0; i < numKeys; i++ {
|
||||
privateKeys[i], publicKeys[i], err = libkaspawallet.CreateKeyPair(ecdsa)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateKeyPair: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
const minimumSignatures = 2
|
||||
address, err := libkaspawallet.Address(&consensusConfig.Params, publicKeys, minimumSignatures, ecdsa)
|
||||
if err != nil {
|
||||
t.Fatalf("Address: %+v", err)
|
||||
}
|
||||
|
||||
if _, ok := address.(*util.AddressScriptHash); !ok {
|
||||
t.Fatalf("The address is of unexpected type")
|
||||
}
|
||||
|
||||
scriptPublicKey, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
t.Fatalf("PayToAddrScript: %+v", err)
|
||||
}
|
||||
|
||||
coinbaseData := &externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: scriptPublicKey,
|
||||
ExtraData: nil,
|
||||
}
|
||||
|
||||
fundingBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, coinbaseData, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
block1Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlockHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
block1, err := tc.GetBlock(block1Hash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
|
||||
block1Tx := block1.Transactions[0]
|
||||
block1TxOut := block1Tx.Outputs[0]
|
||||
selectedUTXOs := []*externalapi.OutpointAndUTXOEntryPair{{
|
||||
Outpoint: &externalapi.DomainOutpoint{
|
||||
TransactionID: *consensushashing.TransactionID(block1.Transactions[0]),
|
||||
Index: 0,
|
||||
},
|
||||
UTXOEntry: utxo.NewUTXOEntry(block1TxOut.Value, block1TxOut.ScriptPublicKey, true, 0),
|
||||
}}
|
||||
|
||||
unsignedTransaction, err := libkaspawallet.CreateUnsignedTransaction(publicKeys, minimumSignatures, ecdsa,
|
||||
[]*libkaspawallet.Payment{{
|
||||
Address: address,
|
||||
Amount: 10,
|
||||
}}, selectedUTXOs)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateUnsignedTransaction: %+v", err)
|
||||
}
|
||||
|
||||
isFullySigned, err := libkaspawallet.IsTransactionFullySigned(unsignedTransaction)
|
||||
if err != nil {
|
||||
t.Fatalf("IsTransactionFullySigned: %+v", err)
|
||||
}
|
||||
|
||||
if isFullySigned {
|
||||
t.Fatalf("Transaction is not expected to be signed")
|
||||
}
|
||||
|
||||
_, err = libkaspawallet.ExtractTransaction(unsignedTransaction)
|
||||
if err == nil || !strings.Contains(err.Error(), fmt.Sprintf("missing %d signatures", minimumSignatures)) {
|
||||
t.Fatal("Unexpectedly succeed to extract a valid transaction out of unsigned transaction")
|
||||
}
|
||||
|
||||
signedTxStep1, err := libkaspawallet.Sign(privateKeys[:1], unsignedTransaction, ecdsa)
|
||||
if err != nil {
|
||||
t.Fatalf("IsTransactionFullySigned: %+v", err)
|
||||
}
|
||||
|
||||
isFullySigned, err = libkaspawallet.IsTransactionFullySigned(signedTxStep1)
|
||||
if err != nil {
|
||||
t.Fatalf("IsTransactionFullySigned: %+v", err)
|
||||
}
|
||||
|
||||
if isFullySigned {
|
||||
t.Fatalf("Transaction is not expected to be fully signed")
|
||||
}
|
||||
|
||||
signedTxStep2, err := libkaspawallet.Sign(privateKeys[1:2], signedTxStep1, ecdsa)
|
||||
if err != nil {
|
||||
t.Fatalf("Sign: %+v", err)
|
||||
}
|
||||
|
||||
extractedSignedTxStep2, err := libkaspawallet.ExtractTransaction(signedTxStep2)
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractTransaction: %+v", err)
|
||||
}
|
||||
|
||||
signedTxOneStep, err := libkaspawallet.Sign(privateKeys[:2], unsignedTransaction, ecdsa)
|
||||
if err != nil {
|
||||
t.Fatalf("Sign: %+v", err)
|
||||
}
|
||||
|
||||
extractedSignedTxOneStep, err := libkaspawallet.ExtractTransaction(signedTxOneStep)
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractTransaction: %+v", err)
|
||||
}
|
||||
|
||||
// We check IDs instead of comparing the actual transactions because the actual transactions have different
|
||||
// signature scripts due to non deterministic signature scheme.
|
||||
if !consensushashing.TransactionID(extractedSignedTxStep2).Equal(consensushashing.TransactionID(extractedSignedTxOneStep)) {
|
||||
t.Fatalf("Expected extractedSignedTxOneStep and extractedSignedTxStep2 IDs to be equal")
|
||||
}
|
||||
|
||||
_, insertionResult, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, []*externalapi.DomainTransaction{extractedSignedTxStep2})
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
addedUTXO := &externalapi.DomainOutpoint{
|
||||
TransactionID: *consensushashing.TransactionID(extractedSignedTxStep2),
|
||||
Index: 0,
|
||||
}
|
||||
if !insertionResult.VirtualUTXODiff.ToAdd().Contains(addedUTXO) {
|
||||
t.Fatalf("Transaction wasn't accepted in the DAG")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestP2PK(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
forSchnorrAndECDSA(t, func(t *testing.T, ecdsa bool) {
|
||||
consensusConfig.BlockCoinbaseMaturity = 0
|
||||
tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestMultisig")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
const numKeys = 1
|
||||
privateKeys := make([][]byte, numKeys)
|
||||
publicKeys := make([][]byte, numKeys)
|
||||
for i := 0; i < numKeys; i++ {
|
||||
privateKeys[i], publicKeys[i], err = libkaspawallet.CreateKeyPair(ecdsa)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateKeyPair: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
const minimumSignatures = 1
|
||||
address, err := libkaspawallet.Address(&consensusConfig.Params, publicKeys, minimumSignatures, ecdsa)
|
||||
if err != nil {
|
||||
t.Fatalf("Address: %+v", err)
|
||||
}
|
||||
|
||||
if ecdsa {
|
||||
if _, ok := address.(*util.AddressPublicKeyECDSA); !ok {
|
||||
t.Fatalf("The address is of unexpected type")
|
||||
}
|
||||
} else {
|
||||
if _, ok := address.(*util.AddressPublicKey); !ok {
|
||||
t.Fatalf("The address is of unexpected type")
|
||||
}
|
||||
}
|
||||
|
||||
scriptPublicKey, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
t.Fatalf("PayToAddrScript: %+v", err)
|
||||
}
|
||||
|
||||
coinbaseData := &externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: scriptPublicKey,
|
||||
ExtraData: nil,
|
||||
}
|
||||
|
||||
fundingBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, coinbaseData, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
block1Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlockHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
block1, err := tc.GetBlock(block1Hash)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBlock: %+v", err)
|
||||
}
|
||||
|
||||
block1Tx := block1.Transactions[0]
|
||||
block1TxOut := block1Tx.Outputs[0]
|
||||
selectedUTXOs := []*externalapi.OutpointAndUTXOEntryPair{{
|
||||
Outpoint: &externalapi.DomainOutpoint{
|
||||
TransactionID: *consensushashing.TransactionID(block1.Transactions[0]),
|
||||
Index: 0,
|
||||
},
|
||||
UTXOEntry: utxo.NewUTXOEntry(block1TxOut.Value, block1TxOut.ScriptPublicKey, true, 0),
|
||||
}}
|
||||
|
||||
unsignedTransaction, err := libkaspawallet.CreateUnsignedTransaction(publicKeys, minimumSignatures,
|
||||
ecdsa,
|
||||
[]*libkaspawallet.Payment{{
|
||||
Address: address,
|
||||
Amount: 10,
|
||||
}}, selectedUTXOs)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateUnsignedTransaction: %+v", err)
|
||||
}
|
||||
|
||||
isFullySigned, err := libkaspawallet.IsTransactionFullySigned(unsignedTransaction)
|
||||
if err != nil {
|
||||
t.Fatalf("IsTransactionFullySigned: %+v", err)
|
||||
}
|
||||
|
||||
if isFullySigned {
|
||||
t.Fatalf("Transaction is not expected to be signed")
|
||||
}
|
||||
|
||||
_, err = libkaspawallet.ExtractTransaction(unsignedTransaction)
|
||||
if err == nil || !strings.Contains(err.Error(), "missing signature") {
|
||||
t.Fatal("Unexpectedly succeed to extract a valid transaction out of unsigned transaction")
|
||||
}
|
||||
|
||||
signedTx, err := libkaspawallet.Sign(privateKeys, unsignedTransaction, ecdsa)
|
||||
if err != nil {
|
||||
t.Fatalf("IsTransactionFullySigned: %+v", err)
|
||||
}
|
||||
|
||||
tx, err := libkaspawallet.ExtractTransaction(signedTx)
|
||||
if err != nil {
|
||||
t.Fatalf("ExtractTransaction: %+v", err)
|
||||
}
|
||||
|
||||
_, insertionResult, err := tc.AddBlock([]*externalapi.DomainHash{block1Hash}, nil, []*externalapi.DomainTransaction{tx})
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
addedUTXO := &externalapi.DomainOutpoint{
|
||||
TransactionID: *consensushashing.TransactionID(tx),
|
||||
Index: 0,
|
||||
}
|
||||
if !insertionResult.VirtualUTXODiff.ToAdd().Contains(addedUTXO) {
|
||||
t.Fatalf("Transaction wasn't accepted in the DAG")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
33
cmd/kaspawallet/main.go
Normal file
33
cmd/kaspawallet/main.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
func main() {
|
||||
subCmd, config := parseCommandLine()
|
||||
|
||||
var err error
|
||||
switch subCmd {
|
||||
case createSubCmd:
|
||||
err = create(config.(*createConfig))
|
||||
case balanceSubCmd:
|
||||
err = balance(config.(*balanceConfig))
|
||||
case sendSubCmd:
|
||||
err = send(config.(*sendConfig))
|
||||
case createUnsignedTransactionSubCmd:
|
||||
err = createUnsignedTransaction(config.(*createUnsignedTransactionConfig))
|
||||
case signSubCmd:
|
||||
err = sign(config.(*signConfig))
|
||||
case broadcastSubCmd:
|
||||
err = broadcast(config.(*broadcastConfig))
|
||||
case showAddressSubCmd:
|
||||
err = showAddress(config.(*showAddressConfig))
|
||||
case dumpUnencryptedDataSubCmd:
|
||||
err = dumpUnencryptedData(config.(*dumpUnencryptedDataConfig))
|
||||
default:
|
||||
err = errors.Errorf("Unknown sub-command '%s'\n", subCmd)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
}
|
||||
171
cmd/kaspawallet/send.go
Normal file
171
cmd/kaspawallet/send.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
utxopkg "github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func send(conf *sendConfig) error {
|
||||
keysFile, err := keys.ReadKeysFile(conf.NetParams(), conf.KeysFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
toAddress, err := util.DecodeAddress(conf.ToAddress, conf.ActiveNetParams.Prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fromAddress, err := libkaspawallet.Address(conf.NetParams(), keysFile.PublicKeys, keysFile.MinimumSignatures, keysFile.ECDSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := connectToRPC(conf.NetParams(), conf.RPCServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
utxos, err := fetchSpendableUTXOs(conf.NetParams(), client, fromAddress.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sendAmountSompi := uint64(conf.SendAmount * util.SompiPerKaspa)
|
||||
|
||||
const feePerInput = 1000
|
||||
selectedUTXOs, changeSompi, err := selectUTXOs(utxos, sendAmountSompi, feePerInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
psTx, err := libkaspawallet.CreateUnsignedTransaction(keysFile.PublicKeys,
|
||||
keysFile.MinimumSignatures,
|
||||
keysFile.ECDSA,
|
||||
[]*libkaspawallet.Payment{{
|
||||
Address: toAddress,
|
||||
Amount: sendAmountSompi,
|
||||
}, {
|
||||
Address: fromAddress,
|
||||
Amount: changeSompi,
|
||||
}},
|
||||
selectedUTXOs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privateKeys, err := keysFile.DecryptPrivateKeys()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updatedPSTx, err := libkaspawallet.Sign(privateKeys, psTx, keysFile.ECDSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tx, err := libkaspawallet.ExtractTransaction(updatedPSTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transactionID, err := sendTransaction(client, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Transaction was sent successfully")
|
||||
fmt.Printf("Transaction ID: \t%s\n", transactionID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchSpendableUTXOs(params *dagconfig.Params, client *rpcclient.RPCClient, address string) ([]*appmessage.UTXOsByAddressesEntry, error) {
|
||||
getUTXOsByAddressesResponse, err := client.GetUTXOsByAddresses([]string{address})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockDAGInfo, err := client.GetBlockDAGInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spendableUTXOs := make([]*appmessage.UTXOsByAddressesEntry, 0)
|
||||
for _, entry := range getUTXOsByAddressesResponse.Entries {
|
||||
if !isUTXOSpendable(entry, blockDAGInfo.VirtualDAAScore, params.BlockCoinbaseMaturity) {
|
||||
continue
|
||||
}
|
||||
spendableUTXOs = append(spendableUTXOs, entry)
|
||||
}
|
||||
return spendableUTXOs, nil
|
||||
}
|
||||
|
||||
func selectUTXOs(utxos []*appmessage.UTXOsByAddressesEntry, spendAmount uint64, feePerInput uint64) (
|
||||
selectedUTXOs []*externalapi.OutpointAndUTXOEntryPair, changeSompi uint64, err error) {
|
||||
|
||||
selectedUTXOs = []*externalapi.OutpointAndUTXOEntryPair{}
|
||||
totalValue := uint64(0)
|
||||
|
||||
for _, utxo := range utxos {
|
||||
txID, err := transactionid.FromString(utxo.Outpoint.TransactionID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
rpcUTXOEntry := utxo.UTXOEntry
|
||||
scriptPublicKeyScript, err := hex.DecodeString(rpcUTXOEntry.ScriptPublicKey.Script)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
scriptPublicKey := &externalapi.ScriptPublicKey{
|
||||
Script: scriptPublicKeyScript,
|
||||
Version: rpcUTXOEntry.ScriptPublicKey.Version,
|
||||
}
|
||||
|
||||
utxoEntry := utxopkg.NewUTXOEntry(rpcUTXOEntry.Amount, scriptPublicKey, rpcUTXOEntry.IsCoinbase, rpcUTXOEntry.BlockDAAScore)
|
||||
selectedUTXOs = append(selectedUTXOs, &externalapi.OutpointAndUTXOEntryPair{
|
||||
Outpoint: &externalapi.DomainOutpoint{
|
||||
TransactionID: *txID,
|
||||
Index: utxo.Outpoint.Index,
|
||||
},
|
||||
UTXOEntry: utxoEntry,
|
||||
})
|
||||
totalValue += utxo.UTXOEntry.Amount
|
||||
|
||||
fee := feePerInput * uint64(len(selectedUTXOs))
|
||||
totalSpend := spendAmount + fee
|
||||
if totalValue >= totalSpend {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
fee := feePerInput * uint64(len(selectedUTXOs))
|
||||
totalSpend := spendAmount + fee
|
||||
if totalValue < totalSpend {
|
||||
return nil, 0, errors.Errorf("Insufficient funds for send: %f required, while only %f available",
|
||||
float64(totalSpend)/util.SompiPerKaspa, float64(totalValue)/util.SompiPerKaspa)
|
||||
}
|
||||
|
||||
return selectedUTXOs, totalValue - totalSpend, nil
|
||||
}
|
||||
|
||||
func sendTransaction(client *rpcclient.RPCClient, tx *externalapi.DomainTransaction) (string, error) {
|
||||
submitTransactionResponse, err := client.SubmitTransaction(appmessage.DomainTransactionToRPCTransaction(tx))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error submitting transaction")
|
||||
}
|
||||
return submitTransactionResponse.TransactionID, nil
|
||||
}
|
||||
23
cmd/kaspawallet/show_address.go
Normal file
23
cmd/kaspawallet/show_address.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
)
|
||||
|
||||
func showAddress(conf *showAddressConfig) error {
|
||||
keysFile, err := keys.ReadKeysFile(conf.NetParams(), conf.KeysFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
address, err := libkaspawallet.Address(conf.NetParams(), keysFile.PublicKeys, keysFile.MinimumSignatures, keysFile.ECDSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("The wallet address is:\n%s\n", address)
|
||||
return nil
|
||||
}
|
||||
45
cmd/kaspawallet/sign.go
Normal file
45
cmd/kaspawallet/sign.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
||||
)
|
||||
|
||||
func sign(conf *signConfig) error {
|
||||
keysFile, err := keys.ReadKeysFile(conf.NetParams(), conf.KeysFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
psTxBytes, err := hex.DecodeString(conf.Transaction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privateKeys, err := keysFile.DecryptPrivateKeys()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updatedPSTxBytes, err := libkaspawallet.Sign(privateKeys, psTxBytes, keysFile.ECDSA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isFullySigned, err := libkaspawallet.IsTransactionFullySigned(updatedPSTxBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isFullySigned {
|
||||
fmt.Println("The transaction is signed and ready to broadcast")
|
||||
} else {
|
||||
fmt.Println("Successfully signed transaction")
|
||||
}
|
||||
|
||||
fmt.Printf("Transaction: %x\n", updatedPSTxBytes)
|
||||
return nil
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
WALLET
|
||||
======
|
||||
|
||||
## IMPORTANT:
|
||||
|
||||
### This software is for TESTING ONLY. Do NOT use it for handling real money.
|
||||
|
||||
`wallet` is a simple, no-frills wallet software operated via the command line.\
|
||||
It is capable of generating wallet key-pairs, printing a wallet's current balance, and sending simple transactions.
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.16 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
#### Build from Source
|
||||
|
||||
- Install Go according to the installation instructions here:
|
||||
http://golang.org/doc/install
|
||||
|
||||
- Ensure Go was installed properly and is a supported version:
|
||||
|
||||
```bash
|
||||
$ go version
|
||||
```
|
||||
|
||||
- Run the following commands to obtain and install kaspad including all dependencies:
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/kaspanet/kaspad
|
||||
$ cd kaspad/cmd/wallet
|
||||
$ go install .
|
||||
```
|
||||
|
||||
- Wallet should now be installed in `$(go env GOPATH)/bin`. If you did
|
||||
not already add the bin directory to your system path during Go installation,
|
||||
you are encouraged to do so now.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
* Create a new wallet key-pair: `wallet create --testnet`
|
||||
* Print a wallet's current balance:
|
||||
`wallet balance --testnet --address=kaspatest:000000000000000000000000000000000000000000`
|
||||
* Send funds to another wallet:
|
||||
`wallet send --testnet --private-key=0000000000000000000000000000000000000000000000000000000000000000 --send-amount=50 --to-address=kaspatest:000000000000000000000000000000000000000000`
|
||||
@@ -1,21 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"os"
|
||||
)
|
||||
|
||||
func isUTXOSpendable(entry *appmessage.UTXOsByAddressesEntry, virtualSelectedParentBlueScore uint64, coinbaseMaturity uint64) bool {
|
||||
if !entry.UTXOEntry.IsCoinbase {
|
||||
return true
|
||||
}
|
||||
blockBlueScore := entry.UTXOEntry.BlockDAAScore
|
||||
// TODO: Check for a better alternative than virtualSelectedParentBlueScore
|
||||
return blockBlueScore+coinbaseMaturity < virtualSelectedParentBlueScore
|
||||
}
|
||||
|
||||
func printErrorAndExit(err error) {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||
"github.com/pkg/errors"
|
||||
"os"
|
||||
|
||||
"github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
const (
|
||||
createSubCmd = "create"
|
||||
balanceSubCmd = "balance"
|
||||
sendSubCmd = "send"
|
||||
)
|
||||
|
||||
type createConfig struct {
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type balanceConfig struct {
|
||||
RPCServer string `long:"rpcserver" short:"s" description:"RPC server to connect to"`
|
||||
Address string `long:"address" short:"d" description:"The public address to check the balance of" required:"true"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
type sendConfig struct {
|
||||
RPCServer string `long:"rpcserver" short:"s" description:"RPC server to connect to"`
|
||||
PrivateKey string `long:"private-key" short:"k" description:"The private key of the sender (encoded in hex)" required:"true"`
|
||||
ToAddress string `long:"to-address" short:"t" description:"The public address to send Kaspa to" required:"true"`
|
||||
SendAmount float64 `long:"send-amount" short:"v" description:"An amount to send in Kaspa (e.g. 1234.12345678)" required:"true"`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
func parseCommandLine() (subCommand string, config interface{}) {
|
||||
cfg := &struct{}{}
|
||||
parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag)
|
||||
|
||||
createConf := &createConfig{}
|
||||
parser.AddCommand(createSubCmd, "Creates a new wallet",
|
||||
"Creates a private key and 3 public addresses, one for each of MainNet, TestNet and DevNet", createConf)
|
||||
|
||||
balanceConf := &balanceConfig{}
|
||||
parser.AddCommand(balanceSubCmd, "Shows the balance of a public address",
|
||||
"Shows the balance for a public address in Kaspa", balanceConf)
|
||||
|
||||
sendConf := &sendConfig{}
|
||||
parser.AddCommand(sendSubCmd, "Sends a Kaspa transaction to a public address",
|
||||
"Sends a Kaspa transaction to a public address", sendConf)
|
||||
|
||||
_, err := parser.Parse()
|
||||
|
||||
if err != nil {
|
||||
var flagsErr *flags.Error
|
||||
if ok := errors.As(err, &flagsErr); ok && flagsErr.Type == flags.ErrHelp {
|
||||
os.Exit(0)
|
||||
} else {
|
||||
os.Exit(1)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
switch parser.Command.Active.Name {
|
||||
case createSubCmd:
|
||||
err := createConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = createConf
|
||||
case balanceSubCmd:
|
||||
err := balanceConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = balanceConf
|
||||
case sendSubCmd:
|
||||
err := sendConf.ResolveNetwork(parser)
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
config = sendConf
|
||||
}
|
||||
|
||||
return parser.Command.Active.Name, config
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kaspanet/go-secp256k1"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func create(conf *createConfig) error {
|
||||
privateKey, err := secp256k1.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to generate private key")
|
||||
}
|
||||
|
||||
fmt.Println("This is your private key, granting access to all wallet funds. Keep it safe. Use it only when sending Kaspa.")
|
||||
fmt.Printf("Private key (hex):\t%s\n\n", privateKey.SerializePrivateKey())
|
||||
|
||||
fmt.Println("This is your public address, where money is to be sent.")
|
||||
publicKey, err := privateKey.SchnorrPublicKey()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to generate public key")
|
||||
}
|
||||
publicKeySerialized, err := publicKey.Serialize()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to serialize public key")
|
||||
}
|
||||
|
||||
addr, err := util.NewAddressPubKeyHashFromPublicKey(publicKeySerialized[:], conf.ActiveNetParams.Prefix)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to generate p2pkh address")
|
||||
}
|
||||
fmt.Printf("Address (%s):\t%s\n", conf.ActiveNetParams.Name, addr)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func main() {
|
||||
subCmd, config := parseCommandLine()
|
||||
|
||||
var err error
|
||||
switch subCmd {
|
||||
case createSubCmd:
|
||||
err = create(config.(*createConfig))
|
||||
case balanceSubCmd:
|
||||
err = balance(config.(*balanceConfig))
|
||||
case sendSubCmd:
|
||||
err = send(config.(*sendConfig))
|
||||
default:
|
||||
err = errors.Errorf("Unknown sub-command '%s'\n", subCmd)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
printErrorAndExit(err)
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/kaspanet/go-secp256k1"
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
|
||||
"github.com/kaspanet/kaspad/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const feeSompis uint64 = 1000
|
||||
|
||||
func send(conf *sendConfig) error {
|
||||
toAddress, err := util.DecodeAddress(conf.ToAddress, conf.ActiveNetParams.Prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyPair, publicKey, err := parsePrivateKey(conf.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serializedPublicKey, err := publicKey.Serialize()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fromAddress, err := util.NewAddressPubKeyHashFromPublicKey(serializedPublicKey[:], conf.ActiveNetParams.Prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := rpcclient.NewRPCClient(conf.RPCServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
utxos, err := fetchSpendableUTXOs(conf, client, fromAddress.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sendAmountSompi := uint64(conf.SendAmount * util.SompiPerKaspa)
|
||||
totalToSend := sendAmountSompi + feeSompis
|
||||
|
||||
selectedUTXOs, changeSompi, err := selectUTXOs(utxos, totalToSend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rpcTransaction, err := generateTransaction(keyPair, selectedUTXOs, sendAmountSompi, changeSompi, toAddress, fromAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transactionID, err := sendTransaction(client, rpcTransaction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Transaction was sent successfully")
|
||||
fmt.Printf("Transaction ID: \t%s\n", transactionID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parsePrivateKey(privateKeyHex string) (*secp256k1.SchnorrKeyPair, *secp256k1.SchnorrPublicKey, error) {
|
||||
privateKeyBytes, err := hex.DecodeString(privateKeyHex)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Error parsing private key hex")
|
||||
}
|
||||
keyPair, err := secp256k1.DeserializePrivateKeyFromSlice(privateKeyBytes)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Error deserializing private key")
|
||||
}
|
||||
publicKey, err := keyPair.SchnorrPublicKey()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Error generating public key")
|
||||
}
|
||||
return keyPair, publicKey, nil
|
||||
}
|
||||
|
||||
func fetchSpendableUTXOs(conf *sendConfig, client *rpcclient.RPCClient, address string) ([]*appmessage.UTXOsByAddressesEntry, error) {
|
||||
getUTXOsByAddressesResponse, err := client.GetUTXOsByAddresses([]string{address})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
virtualSelectedParentBlueScoreResponse, err := client.GetVirtualSelectedParentBlueScore()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
virtualSelectedParentBlueScore := virtualSelectedParentBlueScoreResponse.BlueScore
|
||||
|
||||
spendableUTXOs := make([]*appmessage.UTXOsByAddressesEntry, 0)
|
||||
for _, entry := range getUTXOsByAddressesResponse.Entries {
|
||||
if !isUTXOSpendable(entry, virtualSelectedParentBlueScore, conf.ActiveNetParams.BlockCoinbaseMaturity) {
|
||||
continue
|
||||
}
|
||||
spendableUTXOs = append(spendableUTXOs, entry)
|
||||
}
|
||||
return spendableUTXOs, nil
|
||||
}
|
||||
|
||||
func selectUTXOs(utxos []*appmessage.UTXOsByAddressesEntry, totalToSpend uint64) (
|
||||
selectedUTXOs []*appmessage.UTXOsByAddressesEntry, changeSompi uint64, err error) {
|
||||
|
||||
selectedUTXOs = []*appmessage.UTXOsByAddressesEntry{}
|
||||
totalValue := uint64(0)
|
||||
|
||||
for _, utxo := range utxos {
|
||||
selectedUTXOs = append(selectedUTXOs, utxo)
|
||||
totalValue += utxo.UTXOEntry.Amount
|
||||
|
||||
if totalValue >= totalToSpend {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if totalValue < totalToSpend {
|
||||
return nil, 0, errors.Errorf("Insufficient funds for send: %f required, while only %f available",
|
||||
float64(totalToSpend)/util.SompiPerKaspa, float64(totalValue)/util.SompiPerKaspa)
|
||||
}
|
||||
|
||||
return selectedUTXOs, totalValue - totalToSpend, nil
|
||||
}
|
||||
|
||||
func generateTransaction(keyPair *secp256k1.SchnorrKeyPair, selectedUTXOs []*appmessage.UTXOsByAddressesEntry,
|
||||
sompisToSend uint64, change uint64, toAddress util.Address,
|
||||
fromAddress util.Address) (*appmessage.RPCTransaction, error) {
|
||||
|
||||
inputs := make([]*externalapi.DomainTransactionInput, len(selectedUTXOs))
|
||||
for i, utxo := range selectedUTXOs {
|
||||
outpointTransactionIDBytes, err := hex.DecodeString(utxo.Outpoint.TransactionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outpointTransactionID, err := transactionid.FromBytes(outpointTransactionIDBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outpoint := externalapi.DomainOutpoint{
|
||||
TransactionID: *outpointTransactionID,
|
||||
Index: utxo.Outpoint.Index,
|
||||
}
|
||||
inputs[i] = &externalapi.DomainTransactionInput{PreviousOutpoint: outpoint}
|
||||
}
|
||||
|
||||
toScript, err := txscript.PayToAddrScript(toAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mainOutput := &externalapi.DomainTransactionOutput{
|
||||
Value: sompisToSend,
|
||||
ScriptPublicKey: toScript,
|
||||
}
|
||||
fromScript, err := txscript.PayToAddrScript(fromAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changeOutput := &externalapi.DomainTransactionOutput{
|
||||
Value: change,
|
||||
ScriptPublicKey: fromScript,
|
||||
}
|
||||
outputs := []*externalapi.DomainTransactionOutput{mainOutput, changeOutput}
|
||||
|
||||
domainTransaction := &externalapi.DomainTransaction{
|
||||
Version: constants.MaxTransactionVersion,
|
||||
Inputs: inputs,
|
||||
Outputs: outputs,
|
||||
LockTime: 0,
|
||||
SubnetworkID: subnetworks.SubnetworkIDNative,
|
||||
Gas: 0,
|
||||
Payload: nil,
|
||||
}
|
||||
sighashReusedValues := &consensushashing.SighashReusedValues{}
|
||||
|
||||
for i, input := range domainTransaction.Inputs {
|
||||
signatureScript, err := txscript.SignatureScript(
|
||||
domainTransaction, i, consensushashing.SigHashAll, keyPair, sighashReusedValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
input.SignatureScript = signatureScript
|
||||
}
|
||||
|
||||
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(domainTransaction)
|
||||
return rpcTransaction, nil
|
||||
}
|
||||
|
||||
func sendTransaction(client *rpcclient.RPCClient, rpcTransaction *appmessage.RPCTransaction) (string, error) {
|
||||
submitTransactionResponse, err := client.SubmitTransaction(rpcTransaction)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error submitting transaction")
|
||||
}
|
||||
return submitTransactionResponse.TransactionID, nil
|
||||
}
|
||||
@@ -112,6 +112,30 @@ func (s *consensus) GetBlock(blockHash *externalapi.DomainHash) (*externalapi.Do
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func (s *consensus) GetBlockEvenIfHeaderOnly(blockHash *externalapi.DomainHash) (*externalapi.DomainBlock, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
stagingArea := model.NewStagingArea()
|
||||
|
||||
block, err := s.blockStore.Block(s.databaseContext, stagingArea, blockHash)
|
||||
if err == nil {
|
||||
return block, nil
|
||||
}
|
||||
if !errors.Is(err, database.ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
header, err := s.blockHeaderStore.BlockHeader(s.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
if errors.Is(err, database.ErrNotFound) {
|
||||
return nil, errors.Wrapf(err, "block %s does not exist", blockHash)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &externalapi.DomainBlock{Header: header}, nil
|
||||
}
|
||||
|
||||
func (s *consensus) GetBlockHeader(blockHash *externalapi.DomainHash) (externalapi.BlockHeader, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
@@ -166,7 +190,10 @@ func (s *consensus) GetBlockInfo(blockHash *externalapi.DomainHash) (*externalap
|
||||
return blockInfo, nil
|
||||
}
|
||||
|
||||
func (s *consensus) GetBlockChildren(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
func (s *consensus) GetBlockRelations(blockHash *externalapi.DomainHash) (
|
||||
parents []*externalapi.DomainHash, selectedParent *externalapi.DomainHash,
|
||||
children []*externalapi.DomainHash, err error) {
|
||||
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
@@ -174,10 +201,15 @@ func (s *consensus) GetBlockChildren(blockHash *externalapi.DomainHash) ([]*exte
|
||||
|
||||
blockRelation, err := s.blockRelationStore.BlockRelation(s.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return blockRelation.Children, nil
|
||||
blockGHOSTDAGData, err := s.ghostdagDataStore.Get(s.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return blockRelation.Parents, blockGHOSTDAGData.SelectedParent(), blockRelation.Children, nil
|
||||
}
|
||||
|
||||
func (s *consensus) GetBlockAcceptanceData(blockHash *externalapi.DomainHash) (externalapi.AcceptanceData, error) {
|
||||
@@ -355,11 +387,17 @@ func (s *consensus) GetVirtualInfo() (*externalapi.VirtualInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
daaScore, err := s.daaBlocksStore.DAAScore(s.databaseContext, stagingArea, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &externalapi.VirtualInfo{
|
||||
ParentHashes: blockRelations.Parents,
|
||||
Bits: bits,
|
||||
PastMedianTime: pastMedianTime,
|
||||
BlueScore: virtualGHOSTDAGData.BlueScore(),
|
||||
DAAScore: daaScore,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
package consensus
|
||||
package consensus_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestConsensus_GetBlockInfo(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
factory := NewFactory()
|
||||
consensus, teardown, err := factory.NewTestConsensus(params, false, "TestConsensus_GetBlockInfo")
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
consensus, teardown, err := factory.NewTestConsensus(consensusConfig, "TestConsensus_GetBlockInfo")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
invalidBlock, _, err := consensus.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
invalidBlock, _, err := consensus.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ import (
|
||||
type pruningStagingShard struct {
|
||||
store *pruningStore
|
||||
|
||||
newPruningPoint *externalapi.DomainHash
|
||||
currentPruningPoint *externalapi.DomainHash
|
||||
previousPruningPoint *externalapi.DomainHash
|
||||
newPruningPointCandidate *externalapi.DomainHash
|
||||
startUpdatingPruningPointUTXOSet bool
|
||||
}
|
||||
@@ -17,7 +18,8 @@ func (ps *pruningStore) stagingShard(stagingArea *model.StagingArea) *pruningSta
|
||||
return stagingArea.GetOrCreateShard(model.StagingShardIDPruning, func() model.StagingShard {
|
||||
return &pruningStagingShard{
|
||||
store: ps,
|
||||
newPruningPoint: nil,
|
||||
currentPruningPoint: nil,
|
||||
previousPruningPoint: nil,
|
||||
newPruningPointCandidate: nil,
|
||||
startUpdatingPruningPointUTXOSet: false,
|
||||
}
|
||||
@@ -25,8 +27,8 @@ func (ps *pruningStore) stagingShard(stagingArea *model.StagingArea) *pruningSta
|
||||
}
|
||||
|
||||
func (mss *pruningStagingShard) Commit(dbTx model.DBTransaction) error {
|
||||
if mss.newPruningPoint != nil {
|
||||
pruningPointBytes, err := mss.store.serializeHash(mss.newPruningPoint)
|
||||
if mss.currentPruningPoint != nil {
|
||||
pruningPointBytes, err := mss.store.serializeHash(mss.currentPruningPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -34,7 +36,19 @@ func (mss *pruningStagingShard) Commit(dbTx model.DBTransaction) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mss.store.pruningPointCache = mss.newPruningPoint
|
||||
mss.store.pruningPointCache = mss.currentPruningPoint
|
||||
}
|
||||
|
||||
if mss.previousPruningPoint != nil {
|
||||
oldPruningPointBytes, err := mss.store.serializeHash(mss.previousPruningPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(previousPruningBlockHashKey, oldPruningPointBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mss.store.oldPruningPointCache = mss.previousPruningPoint
|
||||
}
|
||||
|
||||
if mss.newPruningPointCandidate != nil {
|
||||
@@ -60,5 +74,5 @@ func (mss *pruningStagingShard) Commit(dbTx model.DBTransaction) error {
|
||||
}
|
||||
|
||||
func (mss *pruningStagingShard) isStaged() bool {
|
||||
return mss.newPruningPoint != nil || mss.startUpdatingPruningPointUTXOSet
|
||||
return mss.currentPruningPoint != nil || mss.newPruningPointCandidate != nil || mss.previousPruningPoint != nil || mss.startUpdatingPruningPointUTXOSet
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
)
|
||||
|
||||
var pruningBlockHashKey = database.MakeBucket(nil).Key([]byte("pruning-block-hash"))
|
||||
var previousPruningBlockHashKey = database.MakeBucket(nil).Key([]byte("previous-pruning-block-hash"))
|
||||
var candidatePruningPointHashKey = database.MakeBucket(nil).Key([]byte("candidate-pruning-point-hash"))
|
||||
var pruningPointUTXOSetBucket = database.MakeBucket([]byte("pruning-point-utxo-set"))
|
||||
var updatingPruningPointUTXOSetKey = database.MakeBucket(nil).Key([]byte("updating-pruning-point-utxo-set"))
|
||||
@@ -16,6 +17,7 @@ var updatingPruningPointUTXOSetKey = database.MakeBucket(nil).Key([]byte("updati
|
||||
// pruningStore represents a store for the current pruning state
|
||||
type pruningStore struct {
|
||||
pruningPointCache *externalapi.DomainHash
|
||||
oldPruningPointCache *externalapi.DomainHash
|
||||
pruningPointCandidateCache *externalapi.DomainHash
|
||||
}
|
||||
|
||||
@@ -72,54 +74,56 @@ func (ps *pruningStore) HasPruningPointCandidate(dbContext model.DBReader, stagi
|
||||
func (ps *pruningStore) StagePruningPoint(stagingArea *model.StagingArea, pruningPointBlockHash *externalapi.DomainHash) {
|
||||
stagingShard := ps.stagingShard(stagingArea)
|
||||
|
||||
stagingShard.newPruningPoint = pruningPointBlockHash
|
||||
stagingShard.currentPruningPoint = pruningPointBlockHash
|
||||
}
|
||||
|
||||
func (ps *pruningStore) StagePreviousPruningPoint(stagingArea *model.StagingArea, oldPruningPointBlockHash *externalapi.DomainHash) {
|
||||
stagingShard := ps.stagingShard(stagingArea)
|
||||
stagingShard.previousPruningPoint = oldPruningPointBlockHash
|
||||
}
|
||||
|
||||
func (ps *pruningStore) IsStaged(stagingArea *model.StagingArea) bool {
|
||||
return ps.stagingShard(stagingArea).isStaged()
|
||||
}
|
||||
|
||||
func (ps *pruningStore) UpdatePruningPointUTXOSet(dbContext model.DBWriter,
|
||||
utxoSetIterator externalapi.ReadOnlyUTXOSetIterator) error {
|
||||
|
||||
// Delete all the old UTXOs from the database
|
||||
deleteCursor, err := dbContext.Cursor(pruningPointUTXOSetBucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer deleteCursor.Close()
|
||||
for ok := deleteCursor.First(); ok; ok = deleteCursor.Next() {
|
||||
key, err := deleteCursor.Key()
|
||||
func (ps *pruningStore) UpdatePruningPointUTXOSet(dbContext model.DBWriter, diff externalapi.UTXODiff) error {
|
||||
toRemoveIterator := diff.ToRemove().Iterator()
|
||||
defer toRemoveIterator.Close()
|
||||
for ok := toRemoveIterator.First(); ok; ok = toRemoveIterator.Next() {
|
||||
toRemoveOutpoint, _, err := toRemoveIterator.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbContext.Delete(key)
|
||||
serializedOutpoint, err := serializeOutpoint(toRemoveOutpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbContext.Delete(pruningPointUTXOSetBucket.Key(serializedOutpoint))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Insert all the new UTXOs into the database
|
||||
for ok := utxoSetIterator.First(); ok; ok = utxoSetIterator.Next() {
|
||||
outpoint, entry, err := utxoSetIterator.Get()
|
||||
toAddIterator := diff.ToAdd().Iterator()
|
||||
defer toAddIterator.Close()
|
||||
for ok := toAddIterator.First(); ok; ok = toAddIterator.Next() {
|
||||
toAddOutpoint, entry, err := toAddIterator.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serializedOutpoint, err := serializeOutpoint(outpoint)
|
||||
serializedOutpoint, err := serializeOutpoint(toAddOutpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := pruningPointUTXOSetBucket.Key(serializedOutpoint)
|
||||
serializedUTXOEntry, err := serializeUTXOEntry(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbContext.Put(key, serializedUTXOEntry)
|
||||
err = dbContext.Put(pruningPointUTXOSetBucket.Key(serializedOutpoint), serializedUTXOEntry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -127,8 +131,8 @@ func (ps *pruningStore) UpdatePruningPointUTXOSet(dbContext model.DBWriter,
|
||||
func (ps *pruningStore) PruningPoint(dbContext model.DBReader, stagingArea *model.StagingArea) (*externalapi.DomainHash, error) {
|
||||
stagingShard := ps.stagingShard(stagingArea)
|
||||
|
||||
if stagingShard.newPruningPoint != nil {
|
||||
return stagingShard.newPruningPoint, nil
|
||||
if stagingShard.currentPruningPoint != nil {
|
||||
return stagingShard.currentPruningPoint, nil
|
||||
}
|
||||
|
||||
if ps.pruningPointCache != nil {
|
||||
@@ -148,6 +152,30 @@ func (ps *pruningStore) PruningPoint(dbContext model.DBReader, stagingArea *mode
|
||||
return pruningPoint, nil
|
||||
}
|
||||
|
||||
// OldPruningPoint returns the pruning point *before* the current one
|
||||
func (ps *pruningStore) PreviousPruningPoint(dbContext model.DBReader, stagingArea *model.StagingArea) (*externalapi.DomainHash, error) {
|
||||
stagingShard := ps.stagingShard(stagingArea)
|
||||
|
||||
if stagingShard.previousPruningPoint != nil {
|
||||
return stagingShard.previousPruningPoint, nil
|
||||
}
|
||||
if ps.oldPruningPointCache != nil {
|
||||
return ps.oldPruningPointCache, nil
|
||||
}
|
||||
|
||||
oldPruningPointBytes, err := dbContext.Get(previousPruningBlockHashKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldPruningPoint, err := ps.deserializePruningPoint(oldPruningPointBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ps.oldPruningPointCache = oldPruningPoint
|
||||
return oldPruningPoint, nil
|
||||
}
|
||||
|
||||
func (ps *pruningStore) serializeHash(hash *externalapi.DomainHash) ([]byte, error) {
|
||||
return proto.Marshal(serialization.DomainHashToDbHash(hash))
|
||||
}
|
||||
@@ -165,7 +193,7 @@ func (ps *pruningStore) deserializePruningPoint(pruningPointBytes []byte) (*exte
|
||||
func (ps *pruningStore) HasPruningPoint(dbContext model.DBReader, stagingArea *model.StagingArea) (bool, error) {
|
||||
stagingShard := ps.stagingShard(stagingArea)
|
||||
|
||||
if stagingShard.newPruningPoint != nil {
|
||||
if stagingShard.currentPruningPoint != nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -176,6 +204,14 @@ func (ps *pruningStore) HasPruningPoint(dbContext model.DBReader, stagingArea *m
|
||||
return dbContext.Has(pruningBlockHashKey)
|
||||
}
|
||||
|
||||
func (ps *pruningStore) PruningPointUTXOIterator(dbContext model.DBReader) (externalapi.ReadOnlyUTXOSetIterator, error) {
|
||||
cursor, err := dbContext.Cursor(pruningPointUTXOSetBucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ps.newCursorUTXOSetIterator(cursor), nil
|
||||
}
|
||||
|
||||
func (ps *pruningStore) PruningPointUTXOs(dbContext model.DBReader,
|
||||
fromOutpoint *externalapi.DomainOutpoint, limit int) ([]*externalapi.OutpointAndUTXOEntryPair, error) {
|
||||
|
||||
|
||||
@@ -28,7 +28,9 @@ func New(cacheSize int, preallocate bool) model.UTXODiffStore {
|
||||
}
|
||||
|
||||
// Stage stages the given utxoDiff for the given blockHash
|
||||
func (uds *utxoDiffStore) Stage(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash, utxoDiff externalapi.UTXODiff, utxoDiffChild *externalapi.DomainHash) {
|
||||
func (uds *utxoDiffStore) Stage(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
||||
utxoDiff externalapi.UTXODiff, utxoDiffChild *externalapi.DomainHash) {
|
||||
|
||||
stagingShard := uds.stagingShard(stagingArea)
|
||||
|
||||
stagingShard.utxoDiffToAdd[*blockHash] = utxoDiff
|
||||
|
||||
@@ -5,13 +5,6 @@ import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
daablocksstore "github.com/kaspanet/kaspad/domain/consensus/datastructures/daablocksstore"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/headersselectedchainstore"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtraversalmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/finalitymanager"
|
||||
|
||||
consensusdatabase "github.com/kaspanet/kaspad/domain/consensus/database"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/acceptancedatastore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/blockheaderstore"
|
||||
@@ -19,8 +12,10 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/blockstatusstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/blockstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/consensusstatestore"
|
||||
daablocksstore "github.com/kaspanet/kaspad/domain/consensus/datastructures/daablocksstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/finalitystore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/ghostdagdatastore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/headersselectedchainstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/headersselectedtipstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/multisetstore"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/pruningstore"
|
||||
@@ -34,7 +29,9 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/coinbasemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/consensusstatemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtopologymanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtraversalmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/difficultymanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/finalitymanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdagmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/headersselectedtipmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/mergedepthmanager"
|
||||
@@ -54,11 +51,20 @@ const (
|
||||
defaultTestPreallocateCaches = false
|
||||
)
|
||||
|
||||
// Config is the full config required to run consensus
|
||||
type Config struct {
|
||||
dagconfig.Params
|
||||
// IsArchival tells the consensus if it should not prune old blocks
|
||||
IsArchival bool
|
||||
// EnableSanityCheckPruningUTXOSet checks the full pruning point utxo set against the commitment at every pruning movement
|
||||
EnableSanityCheckPruningUTXOSet bool
|
||||
}
|
||||
|
||||
// Factory instantiates new Consensuses
|
||||
type Factory interface {
|
||||
NewConsensus(dagParams *dagconfig.Params, db infrastructuredatabase.Database, isArchivalNode bool) (
|
||||
NewConsensus(config *Config, db infrastructuredatabase.Database) (
|
||||
externalapi.Consensus, error)
|
||||
NewTestConsensus(dagParams *dagconfig.Params, isArchivalNode bool, testName string) (
|
||||
NewTestConsensus(config *Config, testName string) (
|
||||
tc testapi.TestConsensus, teardown func(keepDataDir bool), err error)
|
||||
|
||||
SetTestDataDir(dataDir string)
|
||||
@@ -88,12 +94,12 @@ func NewFactory() Factory {
|
||||
}
|
||||
|
||||
// NewConsensus instantiates a new Consensus
|
||||
func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredatabase.Database, isArchivalNode bool) (
|
||||
func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Database) (
|
||||
externalapi.Consensus, error) {
|
||||
|
||||
dbManager := consensusdatabase.New(db)
|
||||
|
||||
pruningWindowSizeForCaches := int(dagParams.PruningDepth())
|
||||
pruningWindowSizeForCaches := int(config.PruningDepth())
|
||||
|
||||
var preallocateCaches bool
|
||||
if f.preallocateCaches != nil {
|
||||
@@ -104,7 +110,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
|
||||
// This is used for caches that are used as part of deletePastBlocks that need to traverse until
|
||||
// the previous pruning point.
|
||||
pruningWindowSizePlusFinalityDepthForCache := int(dagParams.PruningDepth() + dagParams.FinalityDepth())
|
||||
pruningWindowSizePlusFinalityDepthForCache := int(config.PruningDepth() + config.FinalityDepth())
|
||||
|
||||
// Data Structures
|
||||
acceptanceDataStore := acceptancedatastore.New(200, preallocateCaches)
|
||||
@@ -129,15 +135,15 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
// a single DifficultyAdjustmentWindow. To alleviate this problem we make sure that the cache size is at least
|
||||
// dagParams.DifficultyAdjustmentWindowSize
|
||||
ghostdagDataCacheSize := pruningWindowSizeForCaches
|
||||
if ghostdagDataCacheSize < dagParams.DifficultyAdjustmentWindowSize {
|
||||
ghostdagDataCacheSize = dagParams.DifficultyAdjustmentWindowSize
|
||||
if ghostdagDataCacheSize < config.DifficultyAdjustmentWindowSize {
|
||||
ghostdagDataCacheSize = config.DifficultyAdjustmentWindowSize
|
||||
}
|
||||
ghostdagDataStore := ghostdagdatastore.New(ghostdagDataCacheSize, preallocateCaches)
|
||||
|
||||
headersSelectedTipStore := headersselectedtipstore.New()
|
||||
finalityStore := finalitystore.New(200, preallocateCaches)
|
||||
headersSelectedChainStore := headersselectedchainstore.New(pruningWindowSizeForCaches, preallocateCaches)
|
||||
daaBlocksStore := daablocksstore.New(pruningWindowSizeForCaches, int(dagParams.FinalityDepth()), preallocateCaches)
|
||||
daaBlocksStore := daablocksstore.New(pruningWindowSizeForCaches, int(config.FinalityDepth()), preallocateCaches)
|
||||
|
||||
// Processes
|
||||
reachabilityManager := reachabilitymanager.New(
|
||||
@@ -154,7 +160,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
dagTopologyManager,
|
||||
ghostdagDataStore,
|
||||
blockHeaderStore,
|
||||
dagParams.K)
|
||||
config.K)
|
||||
dagTraversalManager := dagtraversalmanager.New(
|
||||
dbManager,
|
||||
dagTopologyManager,
|
||||
@@ -162,20 +168,20 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
reachabilityDataStore,
|
||||
ghostdagManager,
|
||||
consensusStateStore,
|
||||
dagParams.GenesisHash)
|
||||
config.GenesisHash)
|
||||
pastMedianTimeManager := f.pastMedianTimeConsructor(
|
||||
dagParams.TimestampDeviationTolerance,
|
||||
config.TimestampDeviationTolerance,
|
||||
dbManager,
|
||||
dagTraversalManager,
|
||||
blockHeaderStore,
|
||||
ghostdagDataStore,
|
||||
dagParams.GenesisHash)
|
||||
transactionValidator := transactionvalidator.New(dagParams.BlockCoinbaseMaturity,
|
||||
dagParams.EnableNonNativeSubnetworks,
|
||||
dagParams.MassPerTxByte,
|
||||
dagParams.MassPerScriptPubKeyByte,
|
||||
dagParams.MassPerSigOp,
|
||||
dagParams.MaxCoinbasePayloadLength,
|
||||
config.GenesisHash)
|
||||
transactionValidator := transactionvalidator.New(config.BlockCoinbaseMaturity,
|
||||
config.EnableNonNativeSubnetworks,
|
||||
config.MassPerTxByte,
|
||||
config.MassPerScriptPubKeyByte,
|
||||
config.MassPerSigOp,
|
||||
config.MaxCoinbasePayloadLength,
|
||||
dbManager,
|
||||
pastMedianTimeManager,
|
||||
ghostdagDataStore,
|
||||
@@ -188,29 +194,29 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
daaBlocksStore,
|
||||
dagTopologyManager,
|
||||
dagTraversalManager,
|
||||
dagParams.PowMax,
|
||||
dagParams.DifficultyAdjustmentWindowSize,
|
||||
dagParams.DisableDifficultyAdjustment,
|
||||
dagParams.TargetTimePerBlock,
|
||||
dagParams.GenesisHash)
|
||||
config.PowMax,
|
||||
config.DifficultyAdjustmentWindowSize,
|
||||
config.DisableDifficultyAdjustment,
|
||||
config.TargetTimePerBlock,
|
||||
config.GenesisHash)
|
||||
coinbaseManager := coinbasemanager.New(
|
||||
dbManager,
|
||||
dagParams.SubsidyReductionInterval,
|
||||
dagParams.BaseSubsidy,
|
||||
dagParams.CoinbasePayloadScriptPublicKeyMaxLength,
|
||||
config.SubsidyReductionInterval,
|
||||
config.BaseSubsidy,
|
||||
config.CoinbasePayloadScriptPublicKeyMaxLength,
|
||||
ghostdagDataStore,
|
||||
acceptanceDataStore,
|
||||
daaBlocksStore)
|
||||
headerTipsManager := headersselectedtipmanager.New(dbManager, dagTopologyManager, dagTraversalManager,
|
||||
ghostdagManager, headersSelectedTipStore, headersSelectedChainStore)
|
||||
genesisHash := dagParams.GenesisHash
|
||||
genesisHash := config.GenesisHash
|
||||
finalityManager := finalitymanager.New(
|
||||
dbManager,
|
||||
dagTopologyManager,
|
||||
finalityStore,
|
||||
ghostdagDataStore,
|
||||
genesisHash,
|
||||
dagParams.FinalityDepth())
|
||||
config.FinalityDepth())
|
||||
mergeDepthManager := mergedepthmanager.New(
|
||||
dbManager,
|
||||
dagTopologyManager,
|
||||
@@ -218,15 +224,15 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
finalityManager,
|
||||
ghostdagDataStore)
|
||||
blockValidator := blockvalidator.New(
|
||||
dagParams.PowMax,
|
||||
dagParams.SkipProofOfWork,
|
||||
config.PowMax,
|
||||
config.SkipProofOfWork,
|
||||
genesisHash,
|
||||
dagParams.EnableNonNativeSubnetworks,
|
||||
dagParams.MaxBlockSize,
|
||||
dagParams.MergeSetSizeLimit,
|
||||
dagParams.MaxBlockParents,
|
||||
dagParams.TimestampDeviationTolerance,
|
||||
dagParams.TargetTimePerBlock,
|
||||
config.EnableNonNativeSubnetworks,
|
||||
config.MaxBlockSize,
|
||||
config.MergeSetSizeLimit,
|
||||
config.MaxBlockParents,
|
||||
config.TimestampDeviationTolerance,
|
||||
config.TargetTimePerBlock,
|
||||
|
||||
dbManager,
|
||||
difficultyManager,
|
||||
@@ -249,10 +255,10 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
)
|
||||
consensusStateManager, err := consensusstatemanager.New(
|
||||
dbManager,
|
||||
dagParams.PruningDepth(),
|
||||
dagParams.MaxMassAcceptedByBlock,
|
||||
dagParams.MaxBlockParents,
|
||||
dagParams.MergeSetSizeLimit,
|
||||
config.PruningDepth(),
|
||||
config.MaxMassAcceptedByBlock,
|
||||
config.MaxBlockParents,
|
||||
config.MergeSetSizeLimit,
|
||||
genesisHash,
|
||||
|
||||
ghostdagManager,
|
||||
@@ -299,10 +305,11 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
blockHeaderStore,
|
||||
utxoDiffStore,
|
||||
daaBlocksStore,
|
||||
isArchivalNode,
|
||||
config.IsArchival,
|
||||
genesisHash,
|
||||
dagParams.FinalityDepth(),
|
||||
dagParams.PruningDepth())
|
||||
config.FinalityDepth(),
|
||||
config.PruningDepth(),
|
||||
config.EnableSanityCheckPruningUTXOSet)
|
||||
|
||||
syncManager := syncmanager.New(
|
||||
dbManager,
|
||||
@@ -336,7 +343,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
|
||||
blockProcessor := blockprocessor.New(
|
||||
genesisHash,
|
||||
dagParams.TargetTimePerBlock,
|
||||
config.TargetTimePerBlock,
|
||||
dbManager,
|
||||
consensusStateManager,
|
||||
pruningManager,
|
||||
@@ -411,7 +418,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
}
|
||||
|
||||
if !genesisInfo.Exists {
|
||||
_, err = c.ValidateAndInsertBlock(dagParams.GenesisBlock)
|
||||
_, err = c.ValidateAndInsertBlock(config.GenesisBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -425,7 +432,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = pruningManager.UpdatePruningPointUTXOSetIfRequired()
|
||||
err = pruningManager.UpdatePruningPointIfRequired()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -433,7 +440,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (f *factory) NewTestConsensus(dagParams *dagconfig.Params, isArchivalNode bool, testName string) (
|
||||
func (f *factory) NewTestConsensus(config *Config, testName string) (
|
||||
tc testapi.TestConsensus, teardown func(keepDataDir bool), err error) {
|
||||
datadir := f.dataDir
|
||||
if datadir == "" {
|
||||
@@ -455,7 +462,7 @@ func (f *factory) NewTestConsensus(dagParams *dagconfig.Params, isArchivalNode b
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
consensusAsInterface, err := f.NewConsensus(dagParams, db, isArchivalNode)
|
||||
consensusAsInterface, err := f.NewConsensus(config, db)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -465,7 +472,7 @@ func (f *factory) NewTestConsensus(dagParams *dagconfig.Params, isArchivalNode b
|
||||
testTransactionValidator := transactionvalidator.NewTestTransactionValidator(consensusAsImplementation.transactionValidator)
|
||||
|
||||
tstConsensus := &testConsensus{
|
||||
dagParams: dagParams,
|
||||
dagParams: &config.Params,
|
||||
consensus: consensusAsImplementation,
|
||||
database: db,
|
||||
testConsensusStateManager: testConsensusStateManager,
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
func TestNewConsensus(t *testing.T) {
|
||||
f := NewFactory()
|
||||
|
||||
dagParams := &dagconfig.DevnetParams
|
||||
config := &Config{Params: dagconfig.DevnetParams}
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "TestNewConsensus")
|
||||
if err != nil {
|
||||
@@ -23,7 +23,7 @@ func TestNewConsensus(t *testing.T) {
|
||||
t.Fatalf("error in NewLevelDB: %s", err)
|
||||
}
|
||||
|
||||
_, err = f.NewConsensus(dagParams, db, false)
|
||||
_, err = f.NewConsensus(config, db)
|
||||
if err != nil {
|
||||
t.Fatalf("error in NewConsensus: %+v", err)
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
package consensus
|
||||
package consensus_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFinality(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
// Set finalityInterval to 50 blocks, so that test runs quickly
|
||||
params.FinalityDuration = 50 * params.TargetTimePerBlock
|
||||
consensusConfig.FinalityDuration = 50 * consensusConfig.TargetTimePerBlock
|
||||
|
||||
factory := NewFactory()
|
||||
consensus, teardown, err := factory.NewTestConsensus(params, false, "TestFinality")
|
||||
factory := consensus.NewFactory()
|
||||
consensus, teardown, err := factory.NewTestConsensus(consensusConfig, "TestFinality")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
@@ -40,9 +40,9 @@ func TestFinality(t *testing.T) {
|
||||
}
|
||||
|
||||
// Build a chain of `finalityInterval - 1` blocks
|
||||
finalityInterval := params.FinalityDepth()
|
||||
finalityInterval := consensusConfig.FinalityDepth()
|
||||
var mainChainTip *externalapi.DomainBlock
|
||||
mainChainTipHash := params.GenesisHash
|
||||
mainChainTipHash := consensusConfig.GenesisHash
|
||||
|
||||
for i := uint64(0); i < finalityInterval-1; i++ {
|
||||
mainChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{mainChainTipHash})
|
||||
@@ -63,11 +63,11 @@ func TestFinality(t *testing.T) {
|
||||
|
||||
// Mine another chain of `finality-Interval - 2` blocks
|
||||
var sideChainTip *externalapi.DomainBlock
|
||||
sideChainTipHash := params.GenesisHash
|
||||
sideChainTipHash := consensusConfig.GenesisHash
|
||||
for i := uint64(0); i < finalityInterval-2; i++ {
|
||||
sideChainTip, err = buildAndInsertBlock([]*externalapi.DomainHash{sideChainTipHash})
|
||||
if err != nil {
|
||||
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %v", i, err)
|
||||
t.Fatalf("TestFinality: Failed to process sidechain Block #%d: %+v", i, err)
|
||||
}
|
||||
sideChainTipHash = consensushashing.BlockHash(sideChainTip)
|
||||
|
||||
@@ -127,7 +127,7 @@ func TestFinality(t *testing.T) {
|
||||
t.Fatalf("TestFinality: Failed getting the virtual's finality point: %v", err)
|
||||
}
|
||||
|
||||
if virtualFinality.Equal(params.GenesisHash) {
|
||||
if virtualFinality.Equal(consensusConfig.GenesisHash) {
|
||||
t.Fatalf("virtual's finalityPoint is still genesis after adding finalityInterval + 1 blocks to the main chain")
|
||||
}
|
||||
|
||||
@@ -178,12 +178,12 @@ func TestFinality(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBoundedMergeDepth(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
// Set finalityInterval to 50 blocks, so that test runs quickly
|
||||
params.FinalityDuration = 50 * params.TargetTimePerBlock
|
||||
finalityInterval := int(params.FinalityDepth())
|
||||
consensusConfig.FinalityDuration = 50 * consensusConfig.TargetTimePerBlock
|
||||
finalityInterval := int(consensusConfig.FinalityDepth())
|
||||
|
||||
if int(params.K) >= finalityInterval {
|
||||
if int(consensusConfig.K) >= finalityInterval {
|
||||
t.Fatal("K must be smaller than finality duration for this test to run")
|
||||
}
|
||||
|
||||
@@ -235,20 +235,20 @@ func TestBoundedMergeDepth(t *testing.T) {
|
||||
return blockInfo.BlockStatus
|
||||
}
|
||||
|
||||
factory := NewFactory()
|
||||
consensusBuild, teardownFunc1, err := factory.NewTestConsensus(params, false, "TestBoundedMergeTestBuild")
|
||||
factory := consensus.NewFactory()
|
||||
consensusBuild, teardownFunc1, err := factory.NewTestConsensus(consensusConfig, "TestBoundedMergeTestBuild")
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Error setting up consensus: %+v", err)
|
||||
}
|
||||
|
||||
consensusReal, teardownFunc2, err := factory.NewTestConsensus(params, false, "TestBoundedMergeTestReal")
|
||||
consensusReal, teardownFunc2, err := factory.NewTestConsensus(consensusConfig, "TestBoundedMergeTestReal")
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardownFunc2(false)
|
||||
|
||||
// Create a block on top on genesis
|
||||
block1 := buildAndInsertBlock(consensusBuild, []*externalapi.DomainHash{params.GenesisHash})
|
||||
block1 := buildAndInsertBlock(consensusBuild, []*externalapi.DomainHash{consensusConfig.GenesisHash})
|
||||
|
||||
// Create a chain
|
||||
selectedChain := make([]*externalapi.DomainBlock, 0, finalityInterval+1)
|
||||
@@ -342,7 +342,7 @@ func TestBoundedMergeDepth(t *testing.T) {
|
||||
// Now let's make the kosherizing block red and try to merge again
|
||||
tip := consensushashing.BlockHash(selectedChain[len(selectedChain)-1])
|
||||
// we use k-1 because `kosherizingBlock` points at tip-2, so 2+k-1 = k+1 anticone.
|
||||
for i := 0; i < int(params.K)-1; i++ {
|
||||
for i := 0; i < int(consensusConfig.K)-1; i++ {
|
||||
block := buildAndInsertBlock(consensusReal, []*externalapi.DomainHash{tip})
|
||||
tip = consensushashing.BlockHash(block)
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ type Consensus interface {
|
||||
ValidateTransactionAndPopulateWithConsensusData(transaction *DomainTransaction) error
|
||||
|
||||
GetBlock(blockHash *DomainHash) (*DomainBlock, error)
|
||||
GetBlockEvenIfHeaderOnly(blockHash *DomainHash) (*DomainBlock, error)
|
||||
GetBlockHeader(blockHash *DomainHash) (BlockHeader, error)
|
||||
GetBlockInfo(blockHash *DomainHash) (*BlockInfo, error)
|
||||
GetBlockChildren(blockHash *DomainHash) ([]*DomainHash, error)
|
||||
GetBlockRelations(blockHash *DomainHash) (parents []*DomainHash, selectedParent *DomainHash, children []*DomainHash, err error)
|
||||
GetBlockAcceptanceData(blockHash *DomainHash) (AcceptanceData, error)
|
||||
|
||||
GetHashesBetween(lowHash, highHash *DomainHash, maxBlueScoreDifference uint64) (hashes []*DomainHash, actualHighHash *DomainHash, err error)
|
||||
|
||||
@@ -14,6 +14,7 @@ type UTXODiff interface {
|
||||
ToRemove() UTXOCollection
|
||||
WithDiff(other UTXODiff) (UTXODiff, error)
|
||||
DiffFrom(other UTXODiff) (UTXODiff, error)
|
||||
Reversed() UTXODiff
|
||||
CloneMutable() MutableUTXODiff
|
||||
}
|
||||
|
||||
|
||||
@@ -6,4 +6,5 @@ type VirtualInfo struct {
|
||||
Bits uint32
|
||||
PastMedianTime int64
|
||||
BlueScore uint64
|
||||
DAAScore uint64
|
||||
}
|
||||
|
||||
@@ -6,17 +6,19 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
type PruningStore interface {
|
||||
Store
|
||||
StagePruningPoint(stagingArea *StagingArea, pruningPointBlockHash *externalapi.DomainHash)
|
||||
StagePreviousPruningPoint(stagingArea *StagingArea, oldPruningPointBlockHash *externalapi.DomainHash)
|
||||
StagePruningPointCandidate(stagingArea *StagingArea, candidate *externalapi.DomainHash)
|
||||
IsStaged(stagingArea *StagingArea) bool
|
||||
PruningPointCandidate(dbContext DBReader, stagingArea *StagingArea) (*externalapi.DomainHash, error)
|
||||
HasPruningPointCandidate(dbContext DBReader, stagingArea *StagingArea) (bool, error)
|
||||
PreviousPruningPoint(dbContext DBReader, stagingArea *StagingArea) (*externalapi.DomainHash, error)
|
||||
PruningPoint(dbContext DBReader, stagingArea *StagingArea) (*externalapi.DomainHash, error)
|
||||
HasPruningPoint(dbContext DBReader, stagingArea *StagingArea) (bool, error)
|
||||
|
||||
StageStartUpdatingPruningPointUTXOSet(stagingArea *StagingArea)
|
||||
HadStartedUpdatingPruningPointUTXOSet(dbContext DBWriter) (bool, error)
|
||||
FinishUpdatingPruningPointUTXOSet(dbContext DBWriter) error
|
||||
UpdatePruningPointUTXOSet(dbContext DBWriter, utxoSetIterator externalapi.ReadOnlyUTXOSetIterator) error
|
||||
UpdatePruningPointUTXOSet(dbContext DBWriter, diff externalapi.UTXODiff) error
|
||||
|
||||
ClearImportedPruningPointUTXOs(dbContext DBWriter) error
|
||||
AppendImportedPruningPointUTXOs(dbTx DBTransaction, outpointAndUTXOEntryPairs []*externalapi.OutpointAndUTXOEntryPair) error
|
||||
@@ -26,4 +28,5 @@ type PruningStore interface {
|
||||
UpdateImportedPruningPointMultiset(dbTx DBTransaction, multiset Multiset) error
|
||||
CommitImportedPruningPointUTXOSet(dbContext DBWriter) error
|
||||
PruningPointUTXOs(dbContext DBReader, fromOutpoint *externalapi.DomainOutpoint, limit int) ([]*externalapi.OutpointAndUTXOEntryPair, error)
|
||||
PruningPointUTXOIterator(dbContext DBReader) (externalapi.ReadOnlyUTXOSetIterator, error)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// ConsensusStateManager manages the node's consensus state
|
||||
type ConsensusStateManager interface {
|
||||
AddBlock(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.SelectedChainPath, externalapi.UTXODiff, error)
|
||||
AddBlock(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.SelectedChainPath, externalapi.UTXODiff, *UTXODiffReversalData, error)
|
||||
PopulateTransactionWithUTXOEntries(stagingArea *StagingArea, transaction *externalapi.DomainTransaction) error
|
||||
ImportPruningPoint(stagingArea *StagingArea, newPruningPoint *externalapi.DomainBlock) error
|
||||
RestorePastUTXOSetIterator(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (externalapi.ReadOnlyUTXOSetIterator, error)
|
||||
CalculatePastUTXOAndAcceptanceData(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (externalapi.UTXODiff, externalapi.AcceptanceData, Multiset, error)
|
||||
GetVirtualSelectedParentChainFromBlock(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (*externalapi.SelectedChainPath, error)
|
||||
RecoverUTXOIfRequired() error
|
||||
ReverseUTXODiffs(tipHash *externalapi.DomainHash, reversalData *UTXODiffReversalData) error
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@ type PruningManager interface {
|
||||
IsValidPruningPoint(stagingArea *StagingArea, blockHash *externalapi.DomainHash) (bool, error)
|
||||
ClearImportedPruningPointData() error
|
||||
AppendImportedPruningPointUTXOs(outpointAndUTXOEntryPairs []*externalapi.OutpointAndUTXOEntryPair) error
|
||||
UpdatePruningPointUTXOSetIfRequired() error
|
||||
UpdatePruningPointIfRequired() error
|
||||
PruneAllBlocksBelow(stagingArea *StagingArea, pruningPointHash *externalapi.DomainHash) error
|
||||
}
|
||||
|
||||
@@ -10,5 +10,6 @@ type TestConsensusStateManager interface {
|
||||
model.ConsensusStateManager
|
||||
AddUTXOToMultiset(multiset model.Multiset, entry externalapi.UTXOEntry,
|
||||
outpoint *externalapi.DomainOutpoint) error
|
||||
ResolveBlockStatus(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (externalapi.BlockStatus, error)
|
||||
ResolveBlockStatus(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
||||
useSeparateStagingAreaPerBlock bool) (externalapi.BlockStatus, error)
|
||||
}
|
||||
|
||||
9
domain/consensus/model/utxo_diff_reversal_data.go
Normal file
9
domain/consensus/model/utxo_diff_reversal_data.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package model
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// UTXODiffReversalData is used by ConsensusStateManager to reverse the UTXODiffs during a re-org
|
||||
type UTXODiffReversalData struct {
|
||||
SelectedParentHash *externalapi.DomainHash
|
||||
SelectedParentUTXODiff externalapi.UTXODiff
|
||||
}
|
||||
@@ -1,24 +1,21 @@
|
||||
package blockbuilder_test
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"github.com/pkg/errors"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestBuildBlockErrorCases(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
testConsensus, teardown, err := factory.NewTestConsensus(
|
||||
params, false, "TestBlockBuilderErrorCases")
|
||||
testConsensus, teardown, err := factory.NewTestConsensus(consensusConfig, "TestBlockBuilderErrorCases")
|
||||
if err != nil {
|
||||
t.Fatalf("Error initializing consensus for: %+v", err)
|
||||
}
|
||||
@@ -36,7 +33,7 @@ func TestBuildBlockErrorCases(t *testing.T) {
|
||||
"scriptPublicKey too long",
|
||||
&externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: make([]byte, params.CoinbasePayloadScriptPublicKeyMaxLength+1),
|
||||
Script: make([]byte, consensusConfig.CoinbasePayloadScriptPublicKeyMaxLength+1),
|
||||
Version: 0,
|
||||
},
|
||||
ExtraData: nil,
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -119,7 +119,11 @@ func (bb *testBlockBuilder) buildBlockWithParents(stagingArea *model.StagingArea
|
||||
*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
|
||||
if coinbaseData == nil {
|
||||
scriptPublicKey, _ := testutils.OpTrueScript()
|
||||
scriptPublicKeyScript, err := txscript.PayToScriptHashScript([]byte{txscript.OpTrue})
|
||||
if err != nil {
|
||||
panic(errors.Wrapf(err, "Couldn't parse opTrueScript. This should never happen"))
|
||||
}
|
||||
scriptPublicKey := &externalapi.ScriptPublicKey{Script: scriptPublicKeyScript, Version: constants.MaxScriptPublicKeyVersion}
|
||||
coinbaseData = &externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: scriptPublicKey,
|
||||
ExtraData: []byte{},
|
||||
@@ -143,7 +147,8 @@ func (bb *testBlockBuilder) buildBlockWithParents(stagingArea *model.StagingArea
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
selectedParentStatus, err := bb.testConsensus.ConsensusStateManager().ResolveBlockStatus(stagingArea, ghostdagData.SelectedParent())
|
||||
selectedParentStatus, err := bb.testConsensus.ConsensusStateManager().ResolveBlockStatus(
|
||||
stagingArea, ghostdagData.SelectedParent(), false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package blockprocessor
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/staging"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
@@ -57,6 +59,22 @@ func (bp *blockProcessor) setBlockStatusAfterBlockValidation(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) updateVirtualAcceptanceDataAfterImportingPruningPoint(stagingArea *model.StagingArea) error {
|
||||
|
||||
_, virtualAcceptanceData, virtualMultiset, err :=
|
||||
bp.consensusStateManager.CalculatePastUTXOAndAcceptanceData(stagingArea, model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Staging virtual acceptance data after importing the pruning point")
|
||||
bp.acceptanceDataStore.Stage(stagingArea, model.VirtualBlockHash, virtualAcceptanceData)
|
||||
|
||||
log.Debugf("Staging virtual multiset after importing the pruning point")
|
||||
bp.multisetStore.Stage(stagingArea, model.VirtualBlockHash, virtualMultiset)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea, block *externalapi.DomainBlock,
|
||||
isPruningPoint bool) (*externalapi.BlockInsertionResult, error) {
|
||||
|
||||
@@ -88,6 +106,7 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea,
|
||||
|
||||
var selectedParentChainChanges *externalapi.SelectedChainPath
|
||||
var virtualUTXODiff externalapi.UTXODiff
|
||||
var reversalData *model.UTXODiffReversalData
|
||||
isHeaderOnlyBlock := isHeaderOnlyBlock(block)
|
||||
if !isHeaderOnlyBlock {
|
||||
// There's no need to update the consensus state manager when
|
||||
@@ -95,7 +114,12 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea,
|
||||
// in consensusStateManager.ImportPruningPoint
|
||||
if !isPruningPoint {
|
||||
// Attempt to add the block to the virtual
|
||||
selectedParentChainChanges, virtualUTXODiff, err = bp.consensusStateManager.AddBlock(stagingArea, blockHash)
|
||||
selectedParentChainChanges, virtualUTXODiff, reversalData, err = bp.consensusStateManager.AddBlock(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
err := bp.updateVirtualAcceptanceDataAfterImportingPruningPoint(stagingArea)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -117,12 +141,19 @@ func (bp *blockProcessor) validateAndInsertBlock(stagingArea *model.StagingArea,
|
||||
}
|
||||
}
|
||||
|
||||
err = bp.commitAllChanges(stagingArea)
|
||||
err = staging.CommitAllChanges(bp.databaseContext, stagingArea)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = bp.pruningManager.UpdatePruningPointUTXOSetIfRequired()
|
||||
if reversalData != nil {
|
||||
err = bp.consensusStateManager.ReverseUTXODiffs(blockHash, reversalData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = bp.pruningManager.UpdatePruningPointIfRequired()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -298,20 +329,3 @@ func (bp *blockProcessor) hasValidatedHeader(stagingArea *model.StagingArea, blo
|
||||
|
||||
return status != externalapi.StatusInvalid, nil
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) commitAllChanges(stagingArea *model.StagingArea) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "commitAllChanges")
|
||||
defer onEnd()
|
||||
|
||||
dbTx, err := bp.databaseContext.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = stagingArea.Commit(dbTx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dbTx.Commit()
|
||||
}
|
||||
|
||||
@@ -4,25 +4,22 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
|
||||
|
||||
"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/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestBlockStatus(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestBlockStatus")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestBlockStatus")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
@@ -39,7 +36,7 @@ func TestBlockStatus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
tipHash := params.GenesisHash
|
||||
tipHash := consensusConfig.GenesisHash
|
||||
for i := 0; i < 2; i++ {
|
||||
tipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil)
|
||||
if err != nil {
|
||||
@@ -56,7 +53,7 @@ func TestBlockStatus(t *testing.T) {
|
||||
|
||||
checkStatus(headerHash, externalapi.StatusHeaderOnly)
|
||||
|
||||
nonChainBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
nonChainBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
@@ -119,17 +116,17 @@ func TestValidateAndInsertErrors(t *testing.T) {
|
||||
// Each test is covering the error cases in a sub-function in "validateAndInsertBlock" function.
|
||||
// Currently, implemented only for some of the errors.
|
||||
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestBlockStatus")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestBlockStatus")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
tipHash, emptyCoinbase, tx1 := initData(params)
|
||||
tipHash, emptyCoinbase, tx1 := initData(consensusConfig)
|
||||
|
||||
// Tests all the error case on the function: "checkBlockStatus"(sub-function in function validateBlock)
|
||||
blockWithStatusInvalid, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash},
|
||||
blockWithStatusInvalid, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash},
|
||||
&emptyCoinbase, []*externalapi.DomainTransaction{tx1, tx1})
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
@@ -184,8 +181,8 @@ func TestValidateAndInsertErrors(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func initData(params *dagconfig.Params) (*externalapi.DomainHash, externalapi.DomainCoinbaseData, *externalapi.DomainTransaction) {
|
||||
return params.GenesisHash,
|
||||
func initData(consensusConfig *consensus.Config) (*externalapi.DomainHash, externalapi.DomainCoinbaseData, *externalapi.DomainTransaction) {
|
||||
return consensusConfig.GenesisHash,
|
||||
externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
|
||||
@@ -42,27 +42,27 @@ func addBlock(tcSyncer, tcSyncee testapi.TestConsensus, parentHashes []*external
|
||||
}
|
||||
|
||||
func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
// This is done to reduce the pruning depth to 6 blocks
|
||||
finalityDepth := 3
|
||||
params.FinalityDuration = time.Duration(finalityDepth) * params.TargetTimePerBlock
|
||||
params.K = 0
|
||||
consensusConfig.FinalityDuration = time.Duration(finalityDepth) * consensusConfig.TargetTimePerBlock
|
||||
consensusConfig.K = 0
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tcSyncer, teardownSyncer, err := factory.NewTestConsensus(params, false, "TestValidateAndInsertPruningPointSyncer")
|
||||
tcSyncer, teardownSyncer, err := factory.NewTestConsensus(consensusConfig, "TestValidateAndInsertPruningPointSyncer")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tcSyncer: %+v", err)
|
||||
}
|
||||
defer teardownSyncer(false)
|
||||
|
||||
tcSyncee, teardownSyncee, err := factory.NewTestConsensus(params, false, "TestValidateAndInsertPruningPointSyncee")
|
||||
tcSyncee, teardownSyncee, err := factory.NewTestConsensus(consensusConfig, "TestValidateAndInsertPruningPointSyncee")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tcSyncee: %+v", err)
|
||||
}
|
||||
defer teardownSyncee(false)
|
||||
|
||||
tipHash := params.GenesisHash
|
||||
tipHash := consensusConfig.GenesisHash
|
||||
for i := 0; i < finalityDepth-2; i++ {
|
||||
tipHash = addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{tipHash}, t)
|
||||
}
|
||||
@@ -83,7 +83,7 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
t.Fatalf("PruningPoint: %+v", err)
|
||||
}
|
||||
|
||||
if !pruningPoint.Equal(params.GenesisHash) {
|
||||
if !pruningPoint.Equal(consensusConfig.GenesisHash) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -212,28 +212,28 @@ func TestValidateAndInsertImportedPruningPoint(t *testing.T) {
|
||||
// IBD, while it already has a non-empty UTXO-Set originating from blocks mined on top of genesis - the resulting
|
||||
// UTXO set is correct
|
||||
func TestValidateAndInsertPruningPointWithSideBlocks(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
// This is done to reduce the pruning depth to 6 blocks
|
||||
finalityDepth := 3
|
||||
params.FinalityDuration = time.Duration(finalityDepth) * params.TargetTimePerBlock
|
||||
params.K = 0
|
||||
consensusConfig.FinalityDuration = time.Duration(finalityDepth) * consensusConfig.TargetTimePerBlock
|
||||
consensusConfig.K = 0
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tcSyncer, teardownSyncer, err := factory.NewTestConsensus(params, false, "TestValidateAndInsertPruningPointSyncer")
|
||||
tcSyncer, teardownSyncer, err := factory.NewTestConsensus(consensusConfig, "TestValidateAndInsertPruningPointSyncer")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tcSyncer: %+v", err)
|
||||
}
|
||||
defer teardownSyncer(false)
|
||||
|
||||
tcSyncee, teardownSyncee, err := factory.NewTestConsensus(params, false, "TestValidateAndInsertPruningPointSyncee")
|
||||
tcSyncee, teardownSyncee, err := factory.NewTestConsensus(consensusConfig, "TestValidateAndInsertPruningPointSyncee")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tcSyncee: %+v", err)
|
||||
}
|
||||
defer teardownSyncee(false)
|
||||
|
||||
// Mine 2 block in the syncee on top of genesis
|
||||
side, _, err := tcSyncee.AddBlock([]*externalapi.DomainHash{params.GenesisHash}, &externalapi.DomainCoinbaseData{ScriptPublicKey: &externalapi.ScriptPublicKey{}, ExtraData: []byte{1, 2}}, nil)
|
||||
side, _, err := tcSyncee.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, &externalapi.DomainCoinbaseData{ScriptPublicKey: &externalapi.ScriptPublicKey{}, ExtraData: []byte{1, 2}}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -242,7 +242,7 @@ func TestValidateAndInsertPruningPointWithSideBlocks(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tipHash := params.GenesisHash
|
||||
tipHash := consensusConfig.GenesisHash
|
||||
for i := 0; i < finalityDepth-2; i++ {
|
||||
tipHash = addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{tipHash}, t)
|
||||
}
|
||||
@@ -261,7 +261,7 @@ func TestValidateAndInsertPruningPointWithSideBlocks(t *testing.T) {
|
||||
t.Fatalf("PruningPoint: %+v", err)
|
||||
}
|
||||
|
||||
if !pruningPoint.Equal(params.GenesisHash) {
|
||||
if !pruningPoint.Equal(consensusConfig.GenesisHash) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -421,16 +421,16 @@ func makeFakeUTXOs() []*externalapi.OutpointAndUTXOEntryPair {
|
||||
}
|
||||
|
||||
func TestGetPruningPointUTXOs(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
// This is done to reduce the pruning depth to 8 blocks
|
||||
finalityDepth := 4
|
||||
params.FinalityDuration = time.Duration(finalityDepth) * params.TargetTimePerBlock
|
||||
params.K = 0
|
||||
consensusConfig.FinalityDuration = time.Duration(finalityDepth) * consensusConfig.TargetTimePerBlock
|
||||
consensusConfig.K = 0
|
||||
|
||||
params.BlockCoinbaseMaturity = 0
|
||||
consensusConfig.BlockCoinbaseMaturity = 0
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
testConsensus, teardown, err := factory.NewTestConsensus(params, false, "TestGetPruningPointUTXOs")
|
||||
testConsensus, teardown, err := factory.NewTestConsensus(consensusConfig, "TestGetPruningPointUTXOs")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up testConsensus: %+v", err)
|
||||
}
|
||||
@@ -518,7 +518,7 @@ func TestGetPruningPointUTXOs(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting the pruning point: %+v", err)
|
||||
}
|
||||
if !pruningPoint.Equal(params.GenesisHash) {
|
||||
if !pruningPoint.Equal(consensusConfig.GenesisHash) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -580,18 +580,18 @@ func TestGetPruningPointUTXOs(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkGetPruningPointUTXOs(b *testing.B) {
|
||||
params := dagconfig.DevnetParams
|
||||
consensusConfig := consensus.Config{Params: dagconfig.DevnetParams}
|
||||
|
||||
// This is done to reduce the pruning depth to 200 blocks
|
||||
finalityDepth := 100
|
||||
params.FinalityDuration = time.Duration(finalityDepth) * params.TargetTimePerBlock
|
||||
params.K = 0
|
||||
consensusConfig.FinalityDuration = time.Duration(finalityDepth) * consensusConfig.TargetTimePerBlock
|
||||
consensusConfig.K = 0
|
||||
|
||||
params.SkipProofOfWork = true
|
||||
params.BlockCoinbaseMaturity = 0
|
||||
consensusConfig.SkipProofOfWork = true
|
||||
consensusConfig.BlockCoinbaseMaturity = 0
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
testConsensus, teardown, err := factory.NewTestConsensus(¶ms, false, "TestGetPruningPointUTXOs")
|
||||
testConsensus, teardown, err := factory.NewTestConsensus(&consensusConfig, "TestGetPruningPointUTXOs")
|
||||
if err != nil {
|
||||
b.Fatalf("Error setting up testConsensus: %+v", err)
|
||||
}
|
||||
@@ -671,7 +671,7 @@ func BenchmarkGetPruningPointUTXOs(b *testing.B) {
|
||||
if err != nil {
|
||||
b.Fatalf("Error getting the pruning point: %+v", err)
|
||||
}
|
||||
if !pruningPoint.Equal(params.GenesisHash) {
|
||||
if !pruningPoint.Equal(consensusConfig.GenesisHash) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/util/staging"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -70,7 +71,7 @@ func (bp *blockProcessor) validateBlock(stagingArea *model.StagingArea, block *e
|
||||
stagingArea := model.NewStagingArea()
|
||||
hash := consensushashing.BlockHash(block)
|
||||
bp.blockStatusStore.Stage(stagingArea, hash, externalapi.StatusInvalid)
|
||||
commitErr := bp.commitAllChanges(stagingArea)
|
||||
commitErr := staging.CommitAllChanges(bp.databaseContext, stagingArea)
|
||||
if commitErr != nil {
|
||||
return commitErr
|
||||
}
|
||||
|
||||
@@ -11,26 +11,25 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestCheckBlockIsNotPruned(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
// This is done to reduce the pruning depth to 6 blocks
|
||||
params.FinalityDuration = 2 * params.TargetTimePerBlock
|
||||
params.K = 0
|
||||
consensusConfig.FinalityDuration = 2 * consensusConfig.TargetTimePerBlock
|
||||
consensusConfig.K = 0
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockIsNotPruned")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockIsNotPruned")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
// Add blocks until the pruning point changes
|
||||
tipHash := params.GenesisHash
|
||||
tipHash := consensusConfig.GenesisHash
|
||||
tipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
@@ -52,7 +51,7 @@ func TestCheckBlockIsNotPruned(t *testing.T) {
|
||||
t.Fatalf("PruningPoint: %+v", err)
|
||||
}
|
||||
|
||||
if !pruningPoint.Equal(params.GenesisHash) {
|
||||
if !pruningPoint.Equal(consensusConfig.GenesisHash) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -76,20 +75,20 @@ func TestCheckBlockIsNotPruned(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCheckParentBlockBodiesExist(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
// This is done to reduce the pruning depth to 6 blocks
|
||||
params.FinalityDuration = 2 * params.TargetTimePerBlock
|
||||
params.K = 0
|
||||
consensusConfig.FinalityDuration = 2 * consensusConfig.TargetTimePerBlock
|
||||
consensusConfig.K = 0
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckParentBlockBodiesExist")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckParentBlockBodiesExist")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
headerHash, _, err := tc.AddUTXOInvalidHeader([]*externalapi.DomainHash{params.GenesisHash})
|
||||
headerHash, _, err := tc.AddUTXOInvalidHeader([]*externalapi.DomainHash{consensusConfig.GenesisHash})
|
||||
if err != nil {
|
||||
t.Fatalf("AddUTXOInvalidHeader: %+v", err)
|
||||
}
|
||||
@@ -105,7 +104,7 @@ func TestCheckParentBlockBodiesExist(t *testing.T) {
|
||||
}
|
||||
|
||||
// Add blocks until the pruning point changes
|
||||
tipHash := params.GenesisHash
|
||||
tipHash := consensusConfig.GenesisHash
|
||||
anticonePruningBlock, err := tc.BuildUTXOInvalidBlock([]*externalapi.DomainHash{tipHash})
|
||||
if err != nil {
|
||||
t.Fatalf("BuildUTXOInvalidBlock: %+v", err)
|
||||
@@ -131,7 +130,7 @@ func TestCheckParentBlockBodiesExist(t *testing.T) {
|
||||
t.Fatalf("PruningPoint: %+v", err)
|
||||
}
|
||||
|
||||
if !pruningPoint.Equal(params.GenesisHash) {
|
||||
if !pruningPoint.Equal(consensusConfig.GenesisHash) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -146,20 +145,20 @@ func TestCheckParentBlockBodiesExist(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsFinalizedTransaction(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
stagingArea := model.NewStagingArea()
|
||||
|
||||
params.BlockCoinbaseMaturity = 0
|
||||
consensusConfig.BlockCoinbaseMaturity = 0
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestIsFinalizedTransaction")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestIsFinalizedTransaction")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
// Build a small DAG
|
||||
outerParents := []*externalapi.DomainHash{params.GenesisHash}
|
||||
outerParents := []*externalapi.DomainHash{consensusConfig.GenesisHash}
|
||||
for i := 0; i < 5; i++ {
|
||||
var innerParents []*externalapi.DomainHash
|
||||
for i := 0; i < 4; i++ {
|
||||
@@ -199,7 +198,7 @@ func TestIsFinalizedTransaction(t *testing.T) {
|
||||
}
|
||||
|
||||
checkForLockTimeAndSequence := func(lockTime, sequence uint64, shouldPass bool) {
|
||||
tx, err := testutils.CreateTransaction(parentToSpend.Transactions[0])
|
||||
tx, err := testutils.CreateTransaction(parentToSpend.Transactions[0], 1)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tx: %+v", err)
|
||||
}
|
||||
|
||||
@@ -2,39 +2,37 @@ package blockvalidator_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
|
||||
"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/model/testapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestChainedTransactions(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
params.BlockCoinbaseMaturity = 0
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
consensusConfig.BlockCoinbaseMaturity = 0
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestChainedTransactions")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestChainedTransactions")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
fundingBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
fundingBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
@@ -49,12 +47,12 @@ func TestChainedTransactions(t *testing.T) {
|
||||
t.Fatalf("Error getting block1: %+v", err)
|
||||
}
|
||||
|
||||
tx1, err := testutils.CreateTransaction(block1.Transactions[0])
|
||||
tx1, err := testutils.CreateTransaction(block1.Transactions[0], 1)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tx1: %+v", err)
|
||||
}
|
||||
|
||||
chainedTx, err := testutils.CreateTransaction(tx1)
|
||||
chainedTx, err := testutils.CreateTransaction(tx1, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating chainedTx: %+v", err)
|
||||
}
|
||||
@@ -76,7 +74,7 @@ func TestChainedTransactions(t *testing.T) {
|
||||
t.Fatalf("Error getting block2: %+v", err)
|
||||
}
|
||||
|
||||
tx2, err := testutils.CreateTransaction(block2.Transactions[0])
|
||||
tx2, err := testutils.CreateTransaction(block2.Transactions[0], 1)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tx2: %+v", err)
|
||||
}
|
||||
@@ -93,9 +91,9 @@ func TestChainedTransactions(t *testing.T) {
|
||||
// TestCheckBlockSanity tests the CheckBlockSanity function to ensure it works
|
||||
// as expected.
|
||||
func TestCheckBlockSanity(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockSanity")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockSanity")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
@@ -1000,15 +998,15 @@ var blockWithWrongTxOrder = externalapi.DomainBlock{
|
||||
}
|
||||
|
||||
func TestCheckBlockHashMerkleRoot(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockHashMerkleRoot")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockHashMerkleRoot")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("BuildBlockWithParents: %+v", err)
|
||||
}
|
||||
@@ -1030,16 +1028,16 @@ func TestCheckBlockHashMerkleRoot(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBlockSize(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestBlockSize")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestBlockSize")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := initBlockWithInvalidBlockSize(params, tc)
|
||||
block, _, err := initBlockWithInvalidBlockSize(consensusConfig, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
@@ -1055,7 +1053,7 @@ func TestBlockSize(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func initBlockWithInvalidBlockSize(params *dagconfig.Params, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
func initBlockWithInvalidBlockSize(consensusConfig *consensus.Config, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
emptyCoinbase := externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
@@ -1084,20 +1082,20 @@ func initBlockWithInvalidBlockSize(params *dagconfig.Params, tc testapi.TestCons
|
||||
Payload: []byte{0x01},
|
||||
}
|
||||
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx})
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx})
|
||||
}
|
||||
|
||||
func TestCheckBlockDuplicateTransactions(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockDuplicateTransactions")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockDuplicateTransactions")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := initBlockWithDuplicateTransaction(params, tc)
|
||||
block, _, err := initBlockWithDuplicateTransaction(consensusConfig, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
@@ -1113,7 +1111,7 @@ func TestCheckBlockDuplicateTransactions(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func initBlockWithDuplicateTransaction(params *dagconfig.Params, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
func initBlockWithDuplicateTransaction(consensusConfig *consensus.Config, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
emptyCoinbase := externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
@@ -1141,20 +1139,20 @@ func initBlockWithDuplicateTransaction(params *dagconfig.Params, tc testapi.Test
|
||||
SubnetworkID: subnetworks.SubnetworkIDNative,
|
||||
}
|
||||
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx, tx})
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx, tx})
|
||||
}
|
||||
|
||||
func TestCheckBlockContainsOnlyOneCoinbase(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockContainsOnlyOneCoinbase")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockContainsOnlyOneCoinbase")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := initBlockWithMoreThanOneCoinbase(params, tc)
|
||||
block, _, err := initBlockWithMoreThanOneCoinbase(consensusConfig, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
@@ -1170,7 +1168,7 @@ func TestCheckBlockContainsOnlyOneCoinbase(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func initBlockWithMoreThanOneCoinbase(params *dagconfig.Params, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
func initBlockWithMoreThanOneCoinbase(consensusConfig *consensus.Config, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
emptyCoinbase := externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
@@ -1198,20 +1196,20 @@ func initBlockWithMoreThanOneCoinbase(params *dagconfig.Params, tc testapi.TestC
|
||||
SubnetworkID: subnetworks.SubnetworkIDCoinbase,
|
||||
}
|
||||
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx})
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, &emptyCoinbase, []*externalapi.DomainTransaction{tx})
|
||||
}
|
||||
|
||||
func TestCheckBlockDoubleSpends(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockDoubleSpends")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockDoubleSpends")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := initBlockWithDoubleSpends(params, tc)
|
||||
block, _, err := initBlockWithDoubleSpends(consensusConfig, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error BuildBlockWithParents : %+v", err)
|
||||
}
|
||||
@@ -1227,7 +1225,7 @@ func TestCheckBlockDoubleSpends(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func initBlockWithDoubleSpends(params *dagconfig.Params, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
func initBlockWithDoubleSpends(consensusConfig *consensus.Config, tc testapi.TestConsensus) (*externalapi.DomainBlock, externalapi.UTXODiff, error) {
|
||||
emptyCoinbase := externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
@@ -1273,21 +1271,21 @@ func initBlockWithDoubleSpends(params *dagconfig.Params, tc testapi.TestConsensu
|
||||
SubnetworkID: subnetworks.SubnetworkIDNative,
|
||||
}
|
||||
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash},
|
||||
return tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash},
|
||||
&emptyCoinbase, []*externalapi.DomainTransaction{tx, txSameOutpoint})
|
||||
}
|
||||
|
||||
func TestCheckFirstBlockTransactionIsCoinbase(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckFirstBlockTransactionIsCoinbase")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckFirstBlockTransactionIsCoinbase")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block := initBlockWithFirstTransactionDifferentThanCoinbase(params)
|
||||
block := initBlockWithFirstTransactionDifferentThanCoinbase(consensusConfig)
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
stagingArea := model.NewStagingArea()
|
||||
tc.BlockStore().Stage(stagingArea, blockHash, block)
|
||||
@@ -1300,7 +1298,7 @@ func TestCheckFirstBlockTransactionIsCoinbase(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func initBlockWithFirstTransactionDifferentThanCoinbase(params *dagconfig.Params) *externalapi.DomainBlock {
|
||||
func initBlockWithFirstTransactionDifferentThanCoinbase(consensusConfig *consensus.Config) *externalapi.DomainBlock {
|
||||
prevOutTxID := &externalapi.DomainTransactionID{}
|
||||
prevOutPoint := externalapi.DomainOutpoint{TransactionID: *prevOutTxID, Index: 1}
|
||||
txInput := externalapi.DomainTransactionInput{
|
||||
@@ -1320,7 +1318,7 @@ func initBlockWithFirstTransactionDifferentThanCoinbase(params *dagconfig.Params
|
||||
return &externalapi.DomainBlock{
|
||||
Header: blockheader.NewImmutableBlockHeader(
|
||||
constants.MaxBlockVersion,
|
||||
[]*externalapi.DomainHash{params.GenesisHash},
|
||||
[]*externalapi.DomainHash{consensusConfig.GenesisHash},
|
||||
merkle.CalculateHashMerkleRoot([]*externalapi.DomainTransaction{tx}),
|
||||
&externalapi.DomainHash{},
|
||||
externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{
|
||||
|
||||
@@ -12,13 +12,12 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
)
|
||||
|
||||
func TestValidateMedianTime(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestValidateMedianTime")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestValidateMedianTime")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
@@ -62,8 +61,8 @@ func TestValidateMedianTime(t *testing.T) {
|
||||
return pastMedianTime
|
||||
}
|
||||
|
||||
tip := params.GenesisBlock
|
||||
tipHash := params.GenesisHash
|
||||
tip := consensusConfig.GenesisBlock
|
||||
tipHash := consensusConfig.GenesisHash
|
||||
|
||||
blockTime := tip.Header.TimeInMilliseconds()
|
||||
|
||||
@@ -84,15 +83,15 @@ func TestValidateMedianTime(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCheckParentsIncest(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckParentsIncest")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckParentsIncest")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
a, _, err := tc.AddBlock([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
a, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
@@ -102,7 +101,7 @@ func TestCheckParentsIncest(t *testing.T) {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
c, _, err := tc.AddBlock([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
c, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
@@ -129,7 +128,7 @@ func TestCheckParentsIncest(t *testing.T) {
|
||||
indirectParentsRelationBlock := &externalapi.DomainBlock{
|
||||
Header: blockheader.NewImmutableBlockHeader(
|
||||
0,
|
||||
[]*externalapi.DomainHash{params.GenesisHash, b},
|
||||
[]*externalapi.DomainHash{consensusConfig.GenesisHash, b},
|
||||
&externalapi.DomainHash{},
|
||||
&externalapi.DomainHash{},
|
||||
&externalapi.DomainHash{},
|
||||
@@ -154,25 +153,25 @@ func TestCheckParentsIncest(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCheckMergeSizeLimit(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
params.MergeSetSizeLimit = 2 * uint64(params.K)
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
consensusConfig.MergeSetSizeLimit = 2 * uint64(consensusConfig.K)
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckParentsIncest")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckParentsIncest")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
chain1TipHash := params.GenesisHash
|
||||
for i := uint64(0); i < params.MergeSetSizeLimit+2; i++ {
|
||||
chain1TipHash := consensusConfig.GenesisHash
|
||||
for i := uint64(0); i < consensusConfig.MergeSetSizeLimit+2; i++ {
|
||||
chain1TipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{chain1TipHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
chain2TipHash := params.GenesisHash
|
||||
for i := uint64(0); i < params.MergeSetSizeLimit+1; i++ {
|
||||
chain2TipHash := consensusConfig.GenesisHash
|
||||
for i := uint64(0); i < consensusConfig.MergeSetSizeLimit+1; i++ {
|
||||
chain2TipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{chain2TipHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package blockvalidator_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
@@ -8,24 +10,22 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
"github.com/pkg/errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCheckParentsLimit(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckParentsLimit")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckParentsLimit")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
for i := model.KType(0); i < params.MaxBlockParents+1; i++ {
|
||||
_, _, err = tc.AddBlock([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
for i := model.KType(0); i < consensusConfig.MaxBlockParents+1; i++ {
|
||||
_, _, err = tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
@@ -44,16 +44,16 @@ func TestCheckParentsLimit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCheckBlockVersion(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockVersion")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockVersion")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("BuildBlockWithParents: %+v", err)
|
||||
}
|
||||
@@ -77,23 +77,23 @@ func TestCheckBlockVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCheckBlockTimestampInIsolation(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckBlockTimestampInIsolation")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckBlockTimestampInIsolation")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("BuildBlockWithParents: %+v", err)
|
||||
}
|
||||
|
||||
// Give 10 seconds slack to take care of the test duration
|
||||
timestamp := mstime.Now().UnixMilliseconds() +
|
||||
int64(params.TimestampDeviationTolerance)*params.TargetTimePerBlock.Milliseconds() + 10_000
|
||||
int64(consensusConfig.TimestampDeviationTolerance)*consensusConfig.TargetTimePerBlock.Milliseconds() + 10_000
|
||||
|
||||
block.Header = blockheader.NewImmutableBlockHeader(
|
||||
block.Header.Version(),
|
||||
|
||||
@@ -3,9 +3,9 @@ package blockvalidator
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
|
||||
@@ -7,36 +7,34 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/mining"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// TestPOW tests the validation of the block's POW.
|
||||
func TestPOW(t *testing.T) {
|
||||
// We set the flag "skip pow" to be false (second argument in the function) for not skipping the check of POW and validate its correctness.
|
||||
testutils.ForAllNets(t, false, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, false, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestPOW")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestPOW")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
// Builds and checks block with invalid POW.
|
||||
invalidBlockWrongPOW, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
invalidBlockWrongPOW, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -46,12 +44,12 @@ func TestPOW(t *testing.T) {
|
||||
t.Fatalf("Expected block to be invalid with err: %v, instead found: %v", ruleerrors.ErrInvalidPoW, err)
|
||||
}
|
||||
|
||||
abovePowMaxBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
abovePowMaxBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
abovePowMaxTarget := big.NewInt(0).Add(big.NewInt(1), params.PowMax)
|
||||
abovePowMaxTarget := big.NewInt(0).Add(big.NewInt(1), consensusConfig.PowMax)
|
||||
abovePowMaxBlock.Header = blockheader.NewImmutableBlockHeader(
|
||||
abovePowMaxBlock.Header.Version(),
|
||||
abovePowMaxBlock.Header.ParentHashes(),
|
||||
@@ -68,7 +66,7 @@ func TestPOW(t *testing.T) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
|
||||
negativeTargetBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
negativeTargetBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -90,7 +88,7 @@ func TestPOW(t *testing.T) {
|
||||
}
|
||||
|
||||
// test on a valid block.
|
||||
validBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
validBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -120,16 +118,16 @@ func solveBlockWithWrongPOW(block *externalapi.DomainBlock) *externalapi.DomainB
|
||||
}
|
||||
|
||||
func TestCheckParentHeadersExist(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckParentHeadersExist")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckParentHeadersExist")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
orphanBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
orphanBlock, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -159,7 +157,7 @@ func TestCheckParentHeadersExist(t *testing.T) {
|
||||
}
|
||||
|
||||
invalidBlock, _, err := tc.BuildBlockWithParents(
|
||||
[]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
[]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -183,7 +181,7 @@ func TestCheckParentHeadersExist(t *testing.T) {
|
||||
|
||||
invalidBlockHash := consensushashing.BlockHash(invalidBlock)
|
||||
|
||||
invalidBlockChild, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
invalidBlockChild, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -207,21 +205,21 @@ func TestCheckParentHeadersExist(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCheckPruningPointViolation(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
// This is done to reduce the pruning depth to 6 blocks
|
||||
params.FinalityDuration = 2 * params.TargetTimePerBlock
|
||||
params.K = 0
|
||||
consensusConfig.FinalityDuration = 2 * consensusConfig.TargetTimePerBlock
|
||||
consensusConfig.K = 0
|
||||
|
||||
factory := consensus.NewFactory()
|
||||
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestCheckPruningPointViolation")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestCheckPruningPointViolation")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
// Add blocks until the pruning point changes
|
||||
tipHash := params.GenesisHash
|
||||
tipHash := consensusConfig.GenesisHash
|
||||
for {
|
||||
tipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{tipHash}, nil, nil)
|
||||
if err != nil {
|
||||
@@ -233,12 +231,12 @@ func TestCheckPruningPointViolation(t *testing.T) {
|
||||
t.Fatalf("PruningPoint: %+v", err)
|
||||
}
|
||||
|
||||
if !pruningPoint.Equal(params.GenesisHash) {
|
||||
if !pruningPoint.Equal(consensusConfig.GenesisHash) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_, _, err = tc.AddUTXOInvalidBlock([]*externalapi.DomainHash{params.GenesisHash})
|
||||
_, _, err = tc.AddUTXOInvalidBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash})
|
||||
if !errors.Is(err, ruleerrors.ErrPruningPointViolation) {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
@@ -251,7 +249,7 @@ func TestCheckPruningPointViolation(t *testing.T) {
|
||||
// "calculated" by the mocDifficultyManager, where mocDifficultyManager is special implementation
|
||||
// of the type DifficultyManager for this test (defined below).
|
||||
func TestValidateDifficulty(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
mocDifficulty := &mocDifficultyManager{}
|
||||
factory.SetTestDifficultyManager(func(_ model.DBReader, _ model.GHOSTDAGManager, _ model.GHOSTDAGDataStore,
|
||||
@@ -262,10 +260,10 @@ func TestValidateDifficulty(t *testing.T) {
|
||||
mocDifficulty.daaBlocksStore = daaBlocksStore
|
||||
return mocDifficulty
|
||||
})
|
||||
genesisDifficulty := params.GenesisBlock.Header.Bits()
|
||||
genesisDifficulty := consensusConfig.GenesisBlock.Header.Bits()
|
||||
mocDifficulty.testDifficulty = genesisDifficulty
|
||||
mocDifficulty.testGenesisBits = genesisDifficulty
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestValidateDifficulty")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestValidateDifficulty")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up consensus: %+v", err)
|
||||
}
|
||||
@@ -277,7 +275,7 @@ func TestValidateDifficulty(t *testing.T) {
|
||||
Version: 0,
|
||||
},
|
||||
}
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash}, &emptyCoinbase, nil)
|
||||
block, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{consensusConfig.GenesisHash}, &emptyCoinbase, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("TestValidateDifficulty: Failed build block with parents: %v.", err)
|
||||
}
|
||||
|
||||
@@ -9,16 +9,19 @@ import (
|
||||
// AddBlock submits the given block to be added to the
|
||||
// current virtual. This process may result in a new virtual block
|
||||
// getting created
|
||||
func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (*externalapi.SelectedChainPath, externalapi.UTXODiff, error) {
|
||||
func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (
|
||||
*externalapi.SelectedChainPath, externalapi.UTXODiff, *model.UTXODiffReversalData, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "csm.AddBlock")
|
||||
defer onEnd()
|
||||
|
||||
log.Debugf("Resolving whether the block %s is the next virtual selected parent", blockHash)
|
||||
isCandidateToBeNextVirtualSelectedParent, err := csm.isCandidateToBeNextVirtualSelectedParent(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
var reversalData *model.UTXODiffReversalData
|
||||
if isCandidateToBeNextVirtualSelectedParent {
|
||||
// It's important to check for finality violation before resolving the block status, because the status of
|
||||
// blocks with a selected chain that doesn't contain the pruning point cannot be resolved because they will
|
||||
@@ -27,7 +30,7 @@ func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, block
|
||||
"finality", blockHash)
|
||||
isViolatingFinality, shouldNotify, err := csm.isViolatingFinality(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
if shouldNotify {
|
||||
@@ -37,9 +40,10 @@ func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, block
|
||||
|
||||
if !isViolatingFinality {
|
||||
log.Debugf("Block %s doesn't violate finality. Resolving its block status", blockHash)
|
||||
blockStatus, err := csm.resolveBlockStatus(stagingArea, blockHash)
|
||||
var blockStatus externalapi.BlockStatus
|
||||
blockStatus, reversalData, err = csm.resolveBlockStatus(stagingArea, blockHash, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Block %s resolved to status `%s`", blockHash, blockStatus)
|
||||
@@ -52,17 +56,17 @@ func (csm *consensusStateManager) AddBlock(stagingArea *model.StagingArea, block
|
||||
log.Debugf("Adding block %s to the DAG tips", blockHash)
|
||||
newTips, err := csm.addTip(stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
log.Debugf("After adding %s, the amount of new tips are %d", blockHash, len(newTips))
|
||||
|
||||
log.Debugf("Updating the virtual with the new tips")
|
||||
selectedParentChainChanges, virtualUTXODiff, err := csm.updateVirtual(stagingArea, blockHash, newTips)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return selectedParentChainChanges, virtualUTXODiff, nil
|
||||
return selectedParentChainChanges, virtualUTXODiff, reversalData, nil
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) isCandidateToBeNextVirtualSelectedParent(
|
||||
|
||||
@@ -10,20 +10,19 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
)
|
||||
|
||||
func TestVirtualDiff(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, teardown, err := factory.NewTestConsensus(params, false, "TestVirtualDiff")
|
||||
tc, teardown, err := factory.NewTestConsensus(consensusConfig, "TestVirtualDiff")
|
||||
if err != nil {
|
||||
t.Fatalf("Error setting up tc: %+v", err)
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
// Add block A over the genesis
|
||||
blockAHash, blockInsertionResult, err := tc.AddBlock([]*externalapi.DomainHash{params.GenesisHash}, nil, nil)
|
||||
blockAHash, blockInsertionResult, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error adding block A: %+v", err)
|
||||
}
|
||||
|
||||
@@ -39,14 +39,26 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(stagingArea
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
daaScore, err := csm.daaBlocksStore.DAAScore(csm.databaseContext, stagingArea, blockHash)
|
||||
log.Debugf("Restored the past UTXO of block %s with selectedParent %s. "+
|
||||
"Diff toAdd length: %d, toRemove length: %d", blockHash, blockGHOSTDAGData.SelectedParent(),
|
||||
selectedParentPastUTXO.ToAdd().Len(), selectedParentPastUTXO.ToRemove().Len())
|
||||
|
||||
return csm.calculatePastUTXOAndAcceptanceDataWithSelectedParentUTXO(stagingArea, blockHash, selectedParentPastUTXO)
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) calculatePastUTXOAndAcceptanceDataWithSelectedParentUTXO(stagingArea *model.StagingArea,
|
||||
blockHash *externalapi.DomainHash, selectedParentPastUTXO externalapi.UTXODiff) (
|
||||
externalapi.UTXODiff, externalapi.AcceptanceData, model.Multiset, error) {
|
||||
|
||||
blockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Restored the past UTXO of block %s with selectedParent %s. "+
|
||||
"Diff toAdd length: %d, toRemove length: %d", blockHash, blockGHOSTDAGData.SelectedParent(),
|
||||
selectedParentPastUTXO.ToAdd().Len(), selectedParentPastUTXO.ToRemove().Len())
|
||||
daaScore, err := csm.daaBlocksStore.DAAScore(csm.databaseContext, stagingArea, blockHash)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Applying blue blocks to the selected parent past UTXO of block %s", blockHash)
|
||||
acceptanceData, utxoDiff, err := csm.applyMergeSetBlocks(
|
||||
@@ -66,7 +78,7 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(stagingArea
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) restorePastUTXO(
|
||||
stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (externalapi.MutableUTXODiff, error) {
|
||||
stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (externalapi.UTXODiff, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "restorePastUTXO")
|
||||
defer onEnd()
|
||||
@@ -120,11 +132,11 @@ func (csm *consensusStateManager) restorePastUTXO(
|
||||
}
|
||||
log.Tracef("The accumulated diff for block %s is: %s", blockHash, accumulatedDiff)
|
||||
|
||||
return accumulatedDiff, nil
|
||||
return accumulatedDiff.ToImmutable(), nil
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) applyMergeSetBlocks(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash,
|
||||
selectedParentPastUTXODiff externalapi.MutableUTXODiff, ghostdagData *model.BlockGHOSTDAGData, daaScore uint64) (
|
||||
selectedParentPastUTXODiff externalapi.UTXODiff, ghostdagData *model.BlockGHOSTDAGData, daaScore uint64) (
|
||||
externalapi.AcceptanceData, externalapi.MutableUTXODiff, error) {
|
||||
|
||||
log.Debugf("applyMergeSetBlocks start for block %s", blockHash)
|
||||
@@ -144,7 +156,7 @@ func (csm *consensusStateManager) applyMergeSetBlocks(stagingArea *model.Staging
|
||||
log.Tracef("The past median time for block %s is: %d", blockHash, selectedParentMedianTime)
|
||||
|
||||
multiblockAcceptanceData := make(externalapi.AcceptanceData, len(mergeSetBlocks))
|
||||
accumulatedUTXODiff := selectedParentPastUTXODiff
|
||||
accumulatedUTXODiff := selectedParentPastUTXODiff.CloneMutable()
|
||||
accumulatedMass := uint64(0)
|
||||
|
||||
for i, mergeSetBlock := range mergeSetBlocks {
|
||||
@@ -277,12 +289,13 @@ func (csm *consensusStateManager) checkTransactionMass(
|
||||
}
|
||||
|
||||
// RestorePastUTXOSetIterator restores the given block's UTXOSet iterator, and returns it as a externalapi.ReadOnlyUTXOSetIterator
|
||||
func (csm *consensusStateManager) RestorePastUTXOSetIterator(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (externalapi.ReadOnlyUTXOSetIterator, error) {
|
||||
func (csm *consensusStateManager) RestorePastUTXOSetIterator(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (
|
||||
externalapi.ReadOnlyUTXOSetIterator, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RestorePastUTXOSetIterator")
|
||||
defer onEnd()
|
||||
|
||||
blockStatus, err := csm.resolveBlockStatus(stagingArea, blockHash)
|
||||
blockStatus, _, err := csm.resolveBlockStatus(stagingArea, blockHash, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -306,5 +319,5 @@ func (csm *consensusStateManager) RestorePastUTXOSetIterator(stagingArea *model.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return utxo.IteratorWithDiff(virtualUTXOSetIterator, blockDiff.ToImmutable())
|
||||
return utxo.IteratorWithDiff(virtualUTXOSetIterator, blockDiff)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user