mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-07-06 04:42:31 +00:00
[NOD-1597] Implement a UTXO index (#1221)
* [NOD-1579] Rename AcceptedTxIDs to AcceptedTransactionIDs. * [NOD-1579] Add InsertBlockResult to ValidateAndInsertBlock results. * [NOD-1593] Rename InsertBlockResult to BlockInsertionResult. * [NOD-1593] Add SelectedParentChainChanges to AddBlockToVirtual's result. * [NOD-1593] Implement findSelectedParentChainChanges. * [NOD-1593] Implement TestFindSelectedParentChainChanges. * [NOD-1593] Fix a string. * [NOD-1593] Finish implementing TestFindSelectedParentChainChanges. * [NOD-1593] Fix merge errors. * [NOD-1597] Begin implementing UTXOIndex. * [NOD-1597] Connect UTXOIndex to RPC. * [NOD-1597] Connect Consensus to UTXOIndex. * [NOD-1597] Add AcceptanceData to BlockInfo. * [NOD-1597] Implement UTXOIndex.Update(). * [NOD-1597] Implement add(), remove(), and discard() in utxoIndexStore. * [NOD-1597] Add error cases to add() and remove(). * [NOD-1597] Add special cases to add() and remove(). * [NOD-1597] Implement commit. * [NOD-1597] Add a mutex around UTXOIndex.Update(). * [NOD-1597] Return changes to the UTXO from Update(). * [NOD-1597] Add NotifyUTXOsChangedRequestMessage and related structs. * [NOD-1597] Implement HandleNotifyUTXOsChanged. * [NOD-1597] Begin implementing TestUTXOIndex. * [NOD-1597] Implement RegisterForUTXOsChangedNotifications. * [NOD-1597] Fix bad transaction.ID usage. * [NOD-1597] Implement convertUTXOChangesToUTXOsChangedNotification. * [NOD-1597] Make UTXOsChangedNotificationMessage.Removed UTXOsByAddressesEntry instead of just RPCOutpoint so that the client can discern which address was the UTXO removed for. * [NOD-1597] Collect outpoints in TestUTXOIndex. * [NOD-1597] Rename RPC stuff. * [NOD-1597] Add messages for GetUTXOsByAddresses. * [NOD-1597] Implement HandleGetUTXOsByAddresses. * [NOD-1597] Implement GetUTXOsByAddresses. * [NOD-1597] Implement UTXOs(). * [NOD-1597] Implement getUTXOOutpointEntryPairs(). * [NOD-1597] Expand TestUTXOIndex. * [NOD-1597] Convert SubmitTransaction to use RPCTransaction instead of MsgTx. * [NOD-1597] Finish implementing TestUTXOIndex. * [NOD-1597] Add messages for GetVirtualSelectedParentBlueScore. * [NOD-1597] Implement HandleGetVirtualSelectedParentBlueScore and GetVirtualSelectedParentBlueScore. * [NOD-1597] Implement TestVirtualSelectedParentBlueScore. * [NOD-1597] Implement NotifyVirtualSelectedParentBlueScoreChanged. * [NOD-1597] Expand TestVirtualSelectedParentBlueScore. * [NOD-1597] Implement notifyVirtualSelectedParentBlueScoreChanged. * [NOD-1597] Make go lint happy. * [NOD-1593] Fix merge errors. * [NOD-1593] Rename findSelectedParentChainChanges to calculateSelectedParentChainChanges. * [NOD-1593] Expand TestCalculateSelectedParentChainChanges. * [NOD-1597] Add logs to utxoindex.go. * [NOD-1597] Add logs to utxoindex/store.go. * [NOD-1597] Add logs to RPCManager.NotifyXXX functions. * [NOD-1597] Ignore transactions that aren't accepted. * [NOD-1597] Use GetBlockAcceptanceData instead of GetBlockInfo. * [NOD-1597] Convert scriptPublicKey to string directly, instead of using hex. * [NOD-1597] Add a comment. * [NOD-1597] Guard against calling utxoindex methods when utxoindex is turned off. * [NOD-1597] Add lock to UTXOs. * [NOD-1597] Guard against calls to getUTXOOutpointEntryPairs when staging isn't empty.
This commit is contained in:
parent
843edc4ba5
commit
053bb351b5
@ -1,7 +1,11 @@
|
|||||||
package appmessage
|
package appmessage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
|
||||||
"github.com/kaspanet/kaspad/util/mstime"
|
"github.com/kaspanet/kaspad/util/mstime"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -153,3 +157,113 @@ func outpointToDomainOutpoint(outpoint *Outpoint) *externalapi.DomainOutpoint {
|
|||||||
Index: outpoint.Index,
|
Index: outpoint.Index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RPCTransactionToDomainTransaction converts RPCTransactions to DomainTransactions
|
||||||
|
func RPCTransactionToDomainTransaction(rpcTransaction *RPCTransaction) (*externalapi.DomainTransaction, error) {
|
||||||
|
inputs := make([]*externalapi.DomainTransactionInput, len(rpcTransaction.Inputs))
|
||||||
|
for i, input := range rpcTransaction.Inputs {
|
||||||
|
transactionIDBytes, err := hex.DecodeString(input.PreviousOutpoint.TransactionID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
transactionID, err := transactionid.FromBytes(transactionIDBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
previousOutpoint := &externalapi.DomainOutpoint{
|
||||||
|
TransactionID: *transactionID,
|
||||||
|
Index: input.PreviousOutpoint.Index,
|
||||||
|
}
|
||||||
|
signatureScript, err := hex.DecodeString(input.SignatureScript)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inputs[i] = &externalapi.DomainTransactionInput{
|
||||||
|
PreviousOutpoint: *previousOutpoint,
|
||||||
|
SignatureScript: signatureScript,
|
||||||
|
Sequence: input.Sequence,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputs := make([]*externalapi.DomainTransactionOutput, len(rpcTransaction.Outputs))
|
||||||
|
for i, output := range rpcTransaction.Outputs {
|
||||||
|
scriptPublicKey, err := hex.DecodeString(output.ScriptPubKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
outputs[i] = &externalapi.DomainTransactionOutput{
|
||||||
|
Value: output.Amount,
|
||||||
|
ScriptPublicKey: scriptPublicKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subnetworkIDBytes, err := hex.DecodeString(rpcTransaction.SubnetworkID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
subnetworkID, err := subnetworks.FromBytes(subnetworkIDBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payloadHashBytes, err := hex.DecodeString(rpcTransaction.PayloadHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payloadHash, err := hashes.FromBytes(payloadHashBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payload, err := hex.DecodeString(rpcTransaction.Payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &externalapi.DomainTransaction{
|
||||||
|
Version: rpcTransaction.Version,
|
||||||
|
Inputs: inputs,
|
||||||
|
Outputs: outputs,
|
||||||
|
LockTime: rpcTransaction.LockTime,
|
||||||
|
SubnetworkID: *subnetworkID,
|
||||||
|
Gas: rpcTransaction.LockTime,
|
||||||
|
PayloadHash: *payloadHash,
|
||||||
|
Payload: payload,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainTransactionToRPCTransaction converts DomainTransactions to RPCTransactions
|
||||||
|
func DomainTransactionToRPCTransaction(transaction *externalapi.DomainTransaction) *RPCTransaction {
|
||||||
|
inputs := make([]*RPCTransactionInput, len(transaction.Inputs))
|
||||||
|
for i, input := range transaction.Inputs {
|
||||||
|
transactionID := hex.EncodeToString(input.PreviousOutpoint.TransactionID[:])
|
||||||
|
previousOutpoint := &RPCOutpoint{
|
||||||
|
TransactionID: transactionID,
|
||||||
|
Index: input.PreviousOutpoint.Index,
|
||||||
|
}
|
||||||
|
signatureScript := hex.EncodeToString(input.SignatureScript)
|
||||||
|
inputs[i] = &RPCTransactionInput{
|
||||||
|
PreviousOutpoint: previousOutpoint,
|
||||||
|
SignatureScript: signatureScript,
|
||||||
|
Sequence: input.Sequence,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputs := make([]*RPCTransactionOutput, len(transaction.Outputs))
|
||||||
|
for i, output := range transaction.Outputs {
|
||||||
|
scriptPublicKey := hex.EncodeToString(output.ScriptPublicKey)
|
||||||
|
outputs[i] = &RPCTransactionOutput{
|
||||||
|
Amount: output.Value,
|
||||||
|
ScriptPubKey: scriptPublicKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subnetworkID := hex.EncodeToString(transaction.SubnetworkID[:])
|
||||||
|
payloadHash := hex.EncodeToString(transaction.PayloadHash[:])
|
||||||
|
payload := hex.EncodeToString(transaction.Payload)
|
||||||
|
return &RPCTransaction{
|
||||||
|
Version: transaction.Version,
|
||||||
|
Inputs: inputs,
|
||||||
|
Outputs: outputs,
|
||||||
|
LockTime: transaction.LockTime,
|
||||||
|
SubnetworkID: subnetworkID,
|
||||||
|
Gas: transaction.LockTime,
|
||||||
|
PayloadHash: payloadHash,
|
||||||
|
Payload: payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -106,6 +106,16 @@ const (
|
|||||||
CmdShutDownResponseMessage
|
CmdShutDownResponseMessage
|
||||||
CmdGetHeadersRequestMessage
|
CmdGetHeadersRequestMessage
|
||||||
CmdGetHeadersResponseMessage
|
CmdGetHeadersResponseMessage
|
||||||
|
CmdNotifyUTXOsChangedRequestMessage
|
||||||
|
CmdNotifyUTXOsChangedResponseMessage
|
||||||
|
CmdUTXOsChangedNotificationMessage
|
||||||
|
CmdGetUTXOsByAddressesRequestMessage
|
||||||
|
CmdGetUTXOsByAddressesResponseMessage
|
||||||
|
CmdGetVirtualSelectedParentBlueScoreRequestMessage
|
||||||
|
CmdGetVirtualSelectedParentBlueScoreResponseMessage
|
||||||
|
CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage
|
||||||
|
CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage
|
||||||
|
CmdVirtualSelectedParentBlueScoreChangedNotificationMessage
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
|
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
|
||||||
@ -139,53 +149,63 @@ var ProtocolMessageCommandToString = map[MessageCommand]string{
|
|||||||
|
|
||||||
// RPCMessageCommandToString maps all MessageCommands to their string representation
|
// RPCMessageCommandToString maps all MessageCommands to their string representation
|
||||||
var RPCMessageCommandToString = map[MessageCommand]string{
|
var RPCMessageCommandToString = map[MessageCommand]string{
|
||||||
CmdGetCurrentNetworkRequestMessage: "GetCurrentNetworkRequest",
|
CmdGetCurrentNetworkRequestMessage: "GetCurrentNetworkRequest",
|
||||||
CmdGetCurrentNetworkResponseMessage: "GetCurrentNetworkResponse",
|
CmdGetCurrentNetworkResponseMessage: "GetCurrentNetworkResponse",
|
||||||
CmdSubmitBlockRequestMessage: "SubmitBlockRequest",
|
CmdSubmitBlockRequestMessage: "SubmitBlockRequest",
|
||||||
CmdSubmitBlockResponseMessage: "SubmitBlockResponse",
|
CmdSubmitBlockResponseMessage: "SubmitBlockResponse",
|
||||||
CmdGetBlockTemplateRequestMessage: "GetBlockTemplateRequest",
|
CmdGetBlockTemplateRequestMessage: "GetBlockTemplateRequest",
|
||||||
CmdGetBlockTemplateResponseMessage: "GetBlockTemplateResponse",
|
CmdGetBlockTemplateResponseMessage: "GetBlockTemplateResponse",
|
||||||
CmdGetBlockTemplateTransactionMessage: "CmdGetBlockTemplateTransaction",
|
CmdGetBlockTemplateTransactionMessage: "CmdGetBlockTemplateTransaction",
|
||||||
CmdNotifyBlockAddedRequestMessage: "NotifyBlockAddedRequest",
|
CmdNotifyBlockAddedRequestMessage: "NotifyBlockAddedRequest",
|
||||||
CmdNotifyBlockAddedResponseMessage: "NotifyBlockAddedResponse",
|
CmdNotifyBlockAddedResponseMessage: "NotifyBlockAddedResponse",
|
||||||
CmdBlockAddedNotificationMessage: "BlockAddedNotification",
|
CmdBlockAddedNotificationMessage: "BlockAddedNotification",
|
||||||
CmdGetPeerAddressesRequestMessage: "GetPeerAddressesRequest",
|
CmdGetPeerAddressesRequestMessage: "GetPeerAddressesRequest",
|
||||||
CmdGetPeerAddressesResponseMessage: "GetPeerAddressesResponse",
|
CmdGetPeerAddressesResponseMessage: "GetPeerAddressesResponse",
|
||||||
CmdGetSelectedTipHashRequestMessage: "GetSelectedTipHashRequest",
|
CmdGetSelectedTipHashRequestMessage: "GetSelectedTipHashRequest",
|
||||||
CmdGetSelectedTipHashResponseMessage: "GetSelectedTipHashResponse",
|
CmdGetSelectedTipHashResponseMessage: "GetSelectedTipHashResponse",
|
||||||
CmdGetMempoolEntryRequestMessage: "GetMempoolEntryRequest",
|
CmdGetMempoolEntryRequestMessage: "GetMempoolEntryRequest",
|
||||||
CmdGetMempoolEntryResponseMessage: "GetMempoolEntryResponse",
|
CmdGetMempoolEntryResponseMessage: "GetMempoolEntryResponse",
|
||||||
CmdGetConnectedPeerInfoRequestMessage: "GetConnectedPeerInfoRequest",
|
CmdGetConnectedPeerInfoRequestMessage: "GetConnectedPeerInfoRequest",
|
||||||
CmdGetConnectedPeerInfoResponseMessage: "GetConnectedPeerInfoResponse",
|
CmdGetConnectedPeerInfoResponseMessage: "GetConnectedPeerInfoResponse",
|
||||||
CmdAddPeerRequestMessage: "AddPeerRequest",
|
CmdAddPeerRequestMessage: "AddPeerRequest",
|
||||||
CmdAddPeerResponseMessage: "AddPeerResponse",
|
CmdAddPeerResponseMessage: "AddPeerResponse",
|
||||||
CmdSubmitTransactionRequestMessage: "SubmitTransactionRequest",
|
CmdSubmitTransactionRequestMessage: "SubmitTransactionRequest",
|
||||||
CmdSubmitTransactionResponseMessage: "SubmitTransactionResponse",
|
CmdSubmitTransactionResponseMessage: "SubmitTransactionResponse",
|
||||||
CmdNotifyChainChangedRequestMessage: "NotifyChainChangedRequest",
|
CmdNotifyChainChangedRequestMessage: "NotifyChainChangedRequest",
|
||||||
CmdNotifyChainChangedResponseMessage: "NotifyChainChangedResponse",
|
CmdNotifyChainChangedResponseMessage: "NotifyChainChangedResponse",
|
||||||
CmdChainChangedNotificationMessage: "ChainChangedNotification",
|
CmdChainChangedNotificationMessage: "ChainChangedNotification",
|
||||||
CmdGetBlockRequestMessage: "GetBlockRequest",
|
CmdGetBlockRequestMessage: "GetBlockRequest",
|
||||||
CmdGetBlockResponseMessage: "GetBlockResponse",
|
CmdGetBlockResponseMessage: "GetBlockResponse",
|
||||||
CmdGetSubnetworkRequestMessage: "GetSubnetworkRequest",
|
CmdGetSubnetworkRequestMessage: "GetSubnetworkRequest",
|
||||||
CmdGetSubnetworkResponseMessage: "GetSubnetworkResponse",
|
CmdGetSubnetworkResponseMessage: "GetSubnetworkResponse",
|
||||||
CmdGetChainFromBlockRequestMessage: "GetChainFromBlockRequest",
|
CmdGetChainFromBlockRequestMessage: "GetChainFromBlockRequest",
|
||||||
CmdGetChainFromBlockResponseMessage: "GetChainFromBlockResponse",
|
CmdGetChainFromBlockResponseMessage: "GetChainFromBlockResponse",
|
||||||
CmdGetBlocksRequestMessage: "GetBlocksRequest",
|
CmdGetBlocksRequestMessage: "GetBlocksRequest",
|
||||||
CmdGetBlocksResponseMessage: "GetBlocksResponse",
|
CmdGetBlocksResponseMessage: "GetBlocksResponse",
|
||||||
CmdGetBlockCountRequestMessage: "GetBlockCountRequest",
|
CmdGetBlockCountRequestMessage: "GetBlockCountRequest",
|
||||||
CmdGetBlockCountResponseMessage: "GetBlockCountResponse",
|
CmdGetBlockCountResponseMessage: "GetBlockCountResponse",
|
||||||
CmdGetBlockDAGInfoRequestMessage: "GetBlockDAGInfoRequest",
|
CmdGetBlockDAGInfoRequestMessage: "GetBlockDAGInfoRequest",
|
||||||
CmdGetBlockDAGInfoResponseMessage: "GetBlockDAGInfoResponse",
|
CmdGetBlockDAGInfoResponseMessage: "GetBlockDAGInfoResponse",
|
||||||
CmdResolveFinalityConflictRequestMessage: "ResolveFinalityConflictRequest",
|
CmdResolveFinalityConflictRequestMessage: "ResolveFinalityConflictRequest",
|
||||||
CmdResolveFinalityConflictResponseMessage: "ResolveFinalityConflictResponse",
|
CmdResolveFinalityConflictResponseMessage: "ResolveFinalityConflictResponse",
|
||||||
CmdNotifyFinalityConflictsRequestMessage: "NotifyFinalityConflictsRequest",
|
CmdNotifyFinalityConflictsRequestMessage: "NotifyFinalityConflictsRequest",
|
||||||
CmdNotifyFinalityConflictsResponseMessage: "NotifyFinalityConflictsResponse",
|
CmdNotifyFinalityConflictsResponseMessage: "NotifyFinalityConflictsResponse",
|
||||||
CmdFinalityConflictNotificationMessage: "FinalityConflictNotification",
|
CmdFinalityConflictNotificationMessage: "FinalityConflictNotification",
|
||||||
CmdFinalityConflictResolvedNotificationMessage: "FinalityConflictResolvedNotification",
|
CmdFinalityConflictResolvedNotificationMessage: "FinalityConflictResolvedNotification",
|
||||||
CmdGetMempoolEntriesRequestMessage: "GetMempoolEntriesRequestMessage",
|
CmdGetMempoolEntriesRequestMessage: "GetMempoolEntriesRequest",
|
||||||
CmdGetMempoolEntriesResponseMessage: "GetMempoolEntriesResponseMessage",
|
CmdGetMempoolEntriesResponseMessage: "GetMempoolEntriesResponse",
|
||||||
CmdGetHeadersRequestMessage: "GetHeadersRequest",
|
CmdGetHeadersRequestMessage: "GetHeadersRequest",
|
||||||
CmdGetHeadersResponseMessage: "GetHeadersResponse",
|
CmdGetHeadersResponseMessage: "GetHeadersResponse",
|
||||||
|
CmdNotifyUTXOsChangedRequestMessage: "NotifyUTXOsChangedRequest",
|
||||||
|
CmdNotifyUTXOsChangedResponseMessage: "NotifyUTXOsChangedResponse",
|
||||||
|
CmdUTXOsChangedNotificationMessage: "UTXOsChangedNotification",
|
||||||
|
CmdGetUTXOsByAddressesRequestMessage: "GetUTXOsByAddressesRequest",
|
||||||
|
CmdGetUTXOsByAddressesResponseMessage: "GetUTXOsByAddressesResponse",
|
||||||
|
CmdGetVirtualSelectedParentBlueScoreRequestMessage: "GetVirtualSelectedParentBlueScoreRequest",
|
||||||
|
CmdGetVirtualSelectedParentBlueScoreResponseMessage: "GetVirtualSelectedParentBlueScoreResponse",
|
||||||
|
CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage: "NotifyVirtualSelectedParentBlueScoreChangedRequest",
|
||||||
|
CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage: "NotifyVirtualSelectedParentBlueScoreChangedResponse",
|
||||||
|
CmdVirtualSelectedParentBlueScoreChangedNotificationMessage: "VirtualSelectedParentBlueScoreChangedNotification",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message is an interface that describes a kaspa message. A type that
|
// Message is an interface that describes a kaspa message. A type that
|
||||||
|
41
app/appmessage/rpc_get_utxos_by_addresses.go
Normal file
41
app/appmessage/rpc_get_utxos_by_addresses.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package appmessage
|
||||||
|
|
||||||
|
// GetUTXOsByAddressesRequestMessage is an appmessage corresponding to
|
||||||
|
// its respective RPC message
|
||||||
|
type GetUTXOsByAddressesRequestMessage struct {
|
||||||
|
baseMessage
|
||||||
|
Addresses []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message
|
||||||
|
func (msg *GetUTXOsByAddressesRequestMessage) Command() MessageCommand {
|
||||||
|
return CmdGetUTXOsByAddressesRequestMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGetUTXOsByAddressesRequestMessage returns a instance of the message
|
||||||
|
func NewGetUTXOsByAddressesRequestMessage(addresses []string) *GetUTXOsByAddressesRequestMessage {
|
||||||
|
return &GetUTXOsByAddressesRequestMessage{
|
||||||
|
Addresses: addresses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUTXOsByAddressesResponseMessage is an appmessage corresponding to
|
||||||
|
// its respective RPC message
|
||||||
|
type GetUTXOsByAddressesResponseMessage struct {
|
||||||
|
baseMessage
|
||||||
|
Entries []*UTXOsByAddressesEntry
|
||||||
|
|
||||||
|
Error *RPCError
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message
|
||||||
|
func (msg *GetUTXOsByAddressesResponseMessage) Command() MessageCommand {
|
||||||
|
return CmdGetUTXOsByAddressesResponseMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGetUTXOsByAddressesResponseMessage returns a instance of the message
|
||||||
|
func NewGetUTXOsByAddressesResponseMessage(entries []*UTXOsByAddressesEntry) *GetUTXOsByAddressesResponseMessage {
|
||||||
|
return &GetUTXOsByAddressesResponseMessage{
|
||||||
|
Entries: entries,
|
||||||
|
}
|
||||||
|
}
|
38
app/appmessage/rpc_get_virtual_selected_parent_blue_score.go
Normal file
38
app/appmessage/rpc_get_virtual_selected_parent_blue_score.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package appmessage
|
||||||
|
|
||||||
|
// GetVirtualSelectedParentBlueScoreRequestMessage is an appmessage corresponding to
|
||||||
|
// its respective RPC message
|
||||||
|
type GetVirtualSelectedParentBlueScoreRequestMessage struct {
|
||||||
|
baseMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message
|
||||||
|
func (msg *GetVirtualSelectedParentBlueScoreRequestMessage) Command() MessageCommand {
|
||||||
|
return CmdGetVirtualSelectedParentBlueScoreRequestMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGetVirtualSelectedParentBlueScoreRequestMessage returns a instance of the message
|
||||||
|
func NewGetVirtualSelectedParentBlueScoreRequestMessage() *GetVirtualSelectedParentBlueScoreRequestMessage {
|
||||||
|
return &GetVirtualSelectedParentBlueScoreRequestMessage{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVirtualSelectedParentBlueScoreResponseMessage is an appmessage corresponding to
|
||||||
|
// its respective RPC message
|
||||||
|
type GetVirtualSelectedParentBlueScoreResponseMessage struct {
|
||||||
|
baseMessage
|
||||||
|
BlueScore uint64
|
||||||
|
|
||||||
|
Error *RPCError
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message
|
||||||
|
func (msg *GetVirtualSelectedParentBlueScoreResponseMessage) Command() MessageCommand {
|
||||||
|
return CmdGetVirtualSelectedParentBlueScoreResponseMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGetVirtualSelectedParentBlueScoreResponseMessage returns a instance of the message
|
||||||
|
func NewGetVirtualSelectedParentBlueScoreResponseMessage(blueScore uint64) *GetVirtualSelectedParentBlueScoreResponseMessage {
|
||||||
|
return &GetVirtualSelectedParentBlueScoreResponseMessage{
|
||||||
|
BlueScore: blueScore,
|
||||||
|
}
|
||||||
|
}
|
62
app/appmessage/rpc_notify_utxos_changed.go
Normal file
62
app/appmessage/rpc_notify_utxos_changed.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package appmessage
|
||||||
|
|
||||||
|
// NotifyUTXOsChangedRequestMessage is an appmessage corresponding to
|
||||||
|
// its respective RPC message
|
||||||
|
type NotifyUTXOsChangedRequestMessage struct {
|
||||||
|
baseMessage
|
||||||
|
Addresses []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message
|
||||||
|
func (msg *NotifyUTXOsChangedRequestMessage) Command() MessageCommand {
|
||||||
|
return CmdNotifyUTXOsChangedRequestMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNotifyUTXOsChangedRequestMessage returns a instance of the message
|
||||||
|
func NewNotifyUTXOsChangedRequestMessage(addresses []string) *NotifyUTXOsChangedRequestMessage {
|
||||||
|
return &NotifyUTXOsChangedRequestMessage{
|
||||||
|
Addresses: addresses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyUTXOsChangedResponseMessage is an appmessage corresponding to
|
||||||
|
// its respective RPC message
|
||||||
|
type NotifyUTXOsChangedResponseMessage struct {
|
||||||
|
baseMessage
|
||||||
|
Error *RPCError
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message
|
||||||
|
func (msg *NotifyUTXOsChangedResponseMessage) Command() MessageCommand {
|
||||||
|
return CmdNotifyUTXOsChangedResponseMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNotifyUTXOsChangedResponseMessage returns a instance of the message
|
||||||
|
func NewNotifyUTXOsChangedResponseMessage() *NotifyUTXOsChangedResponseMessage {
|
||||||
|
return &NotifyUTXOsChangedResponseMessage{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTXOsChangedNotificationMessage is an appmessage corresponding to
|
||||||
|
// its respective RPC message
|
||||||
|
type UTXOsChangedNotificationMessage struct {
|
||||||
|
baseMessage
|
||||||
|
Added []*UTXOsByAddressesEntry
|
||||||
|
Removed []*UTXOsByAddressesEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTXOsByAddressesEntry represents a UTXO of some address
|
||||||
|
type UTXOsByAddressesEntry struct {
|
||||||
|
Address string
|
||||||
|
Outpoint *RPCOutpoint
|
||||||
|
UTXOEntry *RPCUTXOEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message
|
||||||
|
func (msg *UTXOsChangedNotificationMessage) Command() MessageCommand {
|
||||||
|
return CmdUTXOsChangedNotificationMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUTXOsChangedNotificationMessage returns a instance of the message
|
||||||
|
func NewUTXOsChangedNotificationMessage() *UTXOsChangedNotificationMessage {
|
||||||
|
return &UTXOsChangedNotificationMessage{}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package appmessage
|
||||||
|
|
||||||
|
// NotifyVirtualSelectedParentBlueScoreChangedRequestMessage is an appmessage corresponding to
|
||||||
|
// its respective RPC message
|
||||||
|
type NotifyVirtualSelectedParentBlueScoreChangedRequestMessage struct {
|
||||||
|
baseMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message
|
||||||
|
func (msg *NotifyVirtualSelectedParentBlueScoreChangedRequestMessage) Command() MessageCommand {
|
||||||
|
return CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNotifyVirtualSelectedParentBlueScoreChangedRequestMessage returns a instance of the message
|
||||||
|
func NewNotifyVirtualSelectedParentBlueScoreChangedRequestMessage() *NotifyVirtualSelectedParentBlueScoreChangedRequestMessage {
|
||||||
|
return &NotifyVirtualSelectedParentBlueScoreChangedRequestMessage{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyVirtualSelectedParentBlueScoreChangedResponseMessage is an appmessage corresponding to
|
||||||
|
// its respective RPC message
|
||||||
|
type NotifyVirtualSelectedParentBlueScoreChangedResponseMessage struct {
|
||||||
|
baseMessage
|
||||||
|
Error *RPCError
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message
|
||||||
|
func (msg *NotifyVirtualSelectedParentBlueScoreChangedResponseMessage) Command() MessageCommand {
|
||||||
|
return CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNotifyVirtualSelectedParentBlueScoreChangedResponseMessage returns a instance of the message
|
||||||
|
func NewNotifyVirtualSelectedParentBlueScoreChangedResponseMessage() *NotifyVirtualSelectedParentBlueScoreChangedResponseMessage {
|
||||||
|
return &NotifyVirtualSelectedParentBlueScoreChangedResponseMessage{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualSelectedParentBlueScoreChangedNotificationMessage is an appmessage corresponding to
|
||||||
|
// its respective RPC message
|
||||||
|
type VirtualSelectedParentBlueScoreChangedNotificationMessage struct {
|
||||||
|
baseMessage
|
||||||
|
VirtualSelectedParentBlueScore uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message
|
||||||
|
func (msg *VirtualSelectedParentBlueScoreChangedNotificationMessage) Command() MessageCommand {
|
||||||
|
return CmdVirtualSelectedParentBlueScoreChangedNotificationMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewVirtualSelectedParentBlueScoreChangedNotificationMessage returns a instance of the message
|
||||||
|
func NewVirtualSelectedParentBlueScoreChangedNotificationMessage(
|
||||||
|
virtualSelectedParentBlueScore uint64) *VirtualSelectedParentBlueScoreChangedNotificationMessage {
|
||||||
|
|
||||||
|
return &VirtualSelectedParentBlueScoreChangedNotificationMessage{
|
||||||
|
VirtualSelectedParentBlueScore: virtualSelectedParentBlueScore,
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ package appmessage
|
|||||||
// its respective RPC message
|
// its respective RPC message
|
||||||
type SubmitTransactionRequestMessage struct {
|
type SubmitTransactionRequestMessage struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
Transaction *MsgTx
|
Transaction *RPCTransaction
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns the protocol command string for the message
|
// Command returns the protocol command string for the message
|
||||||
@ -13,7 +13,7 @@ func (msg *SubmitTransactionRequestMessage) Command() MessageCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewSubmitTransactionRequestMessage returns a instance of the message
|
// NewSubmitTransactionRequestMessage returns a instance of the message
|
||||||
func NewSubmitTransactionRequestMessage(transaction *MsgTx) *SubmitTransactionRequestMessage {
|
func NewSubmitTransactionRequestMessage(transaction *RPCTransaction) *SubmitTransactionRequestMessage {
|
||||||
return &SubmitTransactionRequestMessage{
|
return &SubmitTransactionRequestMessage{
|
||||||
Transaction: transaction,
|
Transaction: transaction,
|
||||||
}
|
}
|
||||||
@ -23,7 +23,7 @@ func NewSubmitTransactionRequestMessage(transaction *MsgTx) *SubmitTransactionRe
|
|||||||
// its respective RPC message
|
// its respective RPC message
|
||||||
type SubmitTransactionResponseMessage struct {
|
type SubmitTransactionResponseMessage struct {
|
||||||
baseMessage
|
baseMessage
|
||||||
TxID string
|
TransactionID string
|
||||||
|
|
||||||
Error *RPCError
|
Error *RPCError
|
||||||
}
|
}
|
||||||
@ -34,8 +34,52 @@ func (msg *SubmitTransactionResponseMessage) Command() MessageCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewSubmitTransactionResponseMessage returns a instance of the message
|
// NewSubmitTransactionResponseMessage returns a instance of the message
|
||||||
func NewSubmitTransactionResponseMessage(txID string) *SubmitTransactionResponseMessage {
|
func NewSubmitTransactionResponseMessage(transactionID string) *SubmitTransactionResponseMessage {
|
||||||
return &SubmitTransactionResponseMessage{
|
return &SubmitTransactionResponseMessage{
|
||||||
TxID: txID,
|
TransactionID: transactionID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RPCTransaction is a kaspad transaction representation meant to be
|
||||||
|
// used over RPC
|
||||||
|
type RPCTransaction struct {
|
||||||
|
Version int32
|
||||||
|
Inputs []*RPCTransactionInput
|
||||||
|
Outputs []*RPCTransactionOutput
|
||||||
|
LockTime uint64
|
||||||
|
SubnetworkID string
|
||||||
|
Gas uint64
|
||||||
|
PayloadHash string
|
||||||
|
Payload string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPCTransactionInput is a kaspad transaction input representation
|
||||||
|
// meant to be used over RPC
|
||||||
|
type RPCTransactionInput struct {
|
||||||
|
PreviousOutpoint *RPCOutpoint
|
||||||
|
SignatureScript string
|
||||||
|
Sequence uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPCTransactionOutput is a kaspad transaction output representation
|
||||||
|
// meant to be used over RPC
|
||||||
|
type RPCTransactionOutput struct {
|
||||||
|
Amount uint64
|
||||||
|
ScriptPubKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPCOutpoint is a kaspad outpoint representation meant to be used
|
||||||
|
// over RPC
|
||||||
|
type RPCOutpoint struct {
|
||||||
|
TransactionID string
|
||||||
|
Index uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPCUTXOEntry is a kaspad utxo entry representation meant to be used
|
||||||
|
// over RPC
|
||||||
|
type RPCUTXOEntry struct {
|
||||||
|
Amount uint64
|
||||||
|
ScriptPubKey string
|
||||||
|
BlockBlueScore uint64
|
||||||
|
IsCoinbase bool
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/kaspanet/kaspad/domain/utxoindex"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database"
|
infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||||
@ -93,6 +94,12 @@ func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var utxoIndex *utxoindex.UTXOIndex
|
||||||
|
if cfg.UTXOIndex {
|
||||||
|
utxoIndex = utxoindex.New(domain.Consensus(), db)
|
||||||
|
log.Infof("UTXO index started")
|
||||||
|
}
|
||||||
|
|
||||||
connectionManager, err := connmanager.New(cfg, netAdapter, addressManager)
|
connectionManager, err := connmanager.New(cfg, netAdapter, addressManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -101,7 +108,7 @@ func NewComponentManager(cfg *config.Config, db infrastructuredatabase.Database,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rpcManager := setupRPC(cfg, domain, netAdapter, protocolManager, connectionManager, addressManager, interrupt)
|
rpcManager := setupRPC(cfg, domain, netAdapter, protocolManager, connectionManager, addressManager, utxoIndex, interrupt)
|
||||||
|
|
||||||
return &ComponentManager{
|
return &ComponentManager{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
@ -121,11 +128,20 @@ func setupRPC(
|
|||||||
protocolManager *protocol.Manager,
|
protocolManager *protocol.Manager,
|
||||||
connectionManager *connmanager.ConnectionManager,
|
connectionManager *connmanager.ConnectionManager,
|
||||||
addressManager *addressmanager.AddressManager,
|
addressManager *addressmanager.AddressManager,
|
||||||
|
utxoIndex *utxoindex.UTXOIndex,
|
||||||
shutDownChan chan<- struct{},
|
shutDownChan chan<- struct{},
|
||||||
) *rpc.Manager {
|
) *rpc.Manager {
|
||||||
|
|
||||||
rpcManager := rpc.NewManager(
|
rpcManager := rpc.NewManager(
|
||||||
cfg, domain, netAdapter, protocolManager, connectionManager, addressManager, shutDownChan)
|
cfg,
|
||||||
|
domain,
|
||||||
|
netAdapter,
|
||||||
|
protocolManager,
|
||||||
|
connectionManager,
|
||||||
|
addressManager,
|
||||||
|
utxoIndex,
|
||||||
|
shutDownChan,
|
||||||
|
)
|
||||||
protocolManager.SetOnBlockAddedToDAGHandler(rpcManager.NotifyBlockAddedToDAG)
|
protocolManager.SetOnBlockAddedToDAGHandler(rpcManager.NotifyBlockAddedToDAG)
|
||||||
|
|
||||||
return rpcManager
|
return rpcManager
|
||||||
|
@ -17,7 +17,9 @@ import (
|
|||||||
// OnNewBlock updates the mempool after a new block arrival, and
|
// OnNewBlock updates the mempool after a new block arrival, and
|
||||||
// relays newly unorphaned transactions and possibly rebroadcast
|
// relays newly unorphaned transactions and possibly rebroadcast
|
||||||
// manually added transactions when not in IBD.
|
// manually added transactions when not in IBD.
|
||||||
func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock) error {
|
func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
|
||||||
|
blockInsertionResult *externalapi.BlockInsertionResult) error {
|
||||||
|
|
||||||
hash := consensushashing.BlockHash(block)
|
hash := consensushashing.BlockHash(block)
|
||||||
log.Debugf("OnNewBlock start for block %s", hash)
|
log.Debugf("OnNewBlock start for block %s", hash)
|
||||||
defer log.Debugf("OnNewBlock end for block %s", hash)
|
defer log.Debugf("OnNewBlock end for block %s", hash)
|
||||||
@ -37,7 +39,7 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock) error {
|
|||||||
|
|
||||||
if f.onBlockAddedToDAGHandler != nil {
|
if f.onBlockAddedToDAGHandler != nil {
|
||||||
log.Tracef("OnNewBlock: calling f.onBlockAddedToDAGHandler for block %s", hash)
|
log.Tracef("OnNewBlock: calling f.onBlockAddedToDAGHandler for block %s", hash)
|
||||||
err := f.onBlockAddedToDAGHandler(newBlock)
|
err := f.onBlockAddedToDAGHandler(newBlock, blockInsertionResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -89,7 +91,7 @@ func (f *FlowContext) SharedRequestedBlocks() *blockrelay.SharedRequestedBlocks
|
|||||||
|
|
||||||
// AddBlock adds the given block to the DAG and propagates it.
|
// AddBlock adds the given block to the DAG and propagates it.
|
||||||
func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
|
func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
|
||||||
_, err := f.Domain().Consensus().ValidateAndInsertBlock(block)
|
blockInsertionResult, err := f.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.As(err, &ruleerrors.RuleError{}) {
|
if errors.As(err, &ruleerrors.RuleError{}) {
|
||||||
log.Infof("Validation failed for block %s: %s", consensushashing.BlockHash(block), err)
|
log.Infof("Validation failed for block %s: %s", consensushashing.BlockHash(block), err)
|
||||||
@ -97,7 +99,7 @@ func (f *FlowContext) AddBlock(block *externalapi.DomainBlock) error {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = f.OnNewBlock(block)
|
err = f.OnNewBlock(block, blockInsertionResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
|
|
||||||
// OnBlockAddedToDAGHandler is a handler function that's triggered
|
// OnBlockAddedToDAGHandler is a handler function that's triggered
|
||||||
// when a block is added to the DAG
|
// when a block is added to the DAG
|
||||||
type OnBlockAddedToDAGHandler func(block *externalapi.DomainBlock) error
|
type OnBlockAddedToDAGHandler func(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error
|
||||||
|
|
||||||
// OnTransactionAddedToMempoolHandler is a handler function that's triggered
|
// OnTransactionAddedToMempoolHandler is a handler function that's triggered
|
||||||
// when a transaction is added to the mempool
|
// when a transaction is added to the mempool
|
||||||
|
@ -25,7 +25,7 @@ type RelayInvsContext interface {
|
|||||||
Domain() domain.Domain
|
Domain() domain.Domain
|
||||||
Config() *config.Config
|
Config() *config.Config
|
||||||
NetAdapter() *netadapter.NetAdapter
|
NetAdapter() *netadapter.NetAdapter
|
||||||
OnNewBlock(block *externalapi.DomainBlock) error
|
OnNewBlock(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error
|
||||||
SharedRequestedBlocks() *SharedRequestedBlocks
|
SharedRequestedBlocks() *SharedRequestedBlocks
|
||||||
Broadcast(message appmessage.Message) error
|
Broadcast(message appmessage.Message) error
|
||||||
AddOrphan(orphanBlock *externalapi.DomainBlock)
|
AddOrphan(orphanBlock *externalapi.DomainBlock)
|
||||||
@ -102,7 +102,7 @@ func (flow *handleRelayInvsFlow) start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Processing block %s", inv.Hash)
|
log.Debugf("Processing block %s", inv.Hash)
|
||||||
missingParents, err := flow.processBlock(block)
|
missingParents, blockInsertionResult, err := flow.processBlock(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -121,7 +121,7 @@ func (flow *handleRelayInvsFlow) start() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Infof("Accepted block %s via relay", inv.Hash)
|
log.Infof("Accepted block %s via relay", inv.Hash)
|
||||||
err = flow.OnNewBlock(block)
|
err = flow.OnNewBlock(block, blockInsertionResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -199,22 +199,22 @@ func (flow *handleRelayInvsFlow) readMsgBlock() (msgBlock *appmessage.MsgBlock,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (flow *handleRelayInvsFlow) processBlock(block *externalapi.DomainBlock) ([]*externalapi.DomainHash, error) {
|
func (flow *handleRelayInvsFlow) processBlock(block *externalapi.DomainBlock) ([]*externalapi.DomainHash, *externalapi.BlockInsertionResult, error) {
|
||||||
blockHash := consensushashing.BlockHash(block)
|
blockHash := consensushashing.BlockHash(block)
|
||||||
_, err := flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
blockInsertionResult, err := flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.As(err, &ruleerrors.RuleError{}) {
|
if !errors.As(err, &ruleerrors.RuleError{}) {
|
||||||
return nil, errors.Wrapf(err, "failed to process block %s", blockHash)
|
return nil, nil, errors.Wrapf(err, "failed to process block %s", blockHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
missingParentsError := &ruleerrors.ErrMissingParents{}
|
missingParentsError := &ruleerrors.ErrMissingParents{}
|
||||||
if errors.As(err, missingParentsError) {
|
if errors.As(err, missingParentsError) {
|
||||||
return missingParentsError.MissingParentHashes, nil
|
return missingParentsError.MissingParentHashes, nil, nil
|
||||||
}
|
}
|
||||||
log.Warnf("Rejected block %s from %s: %s", blockHash, flow.peer, err)
|
log.Warnf("Rejected block %s from %s: %s", blockHash, flow.peer, err)
|
||||||
return nil, protocolerrors.Wrapf(true, err, "got invalid block %s from relay", blockHash)
|
return nil, nil, protocolerrors.Wrapf(true, err, "got invalid block %s from relay", blockHash)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, blockInsertionResult, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (flow *handleRelayInvsFlow) relayBlock(block *externalapi.DomainBlock) error {
|
func (flow *handleRelayInvsFlow) relayBlock(block *externalapi.DomainBlock) error {
|
||||||
|
@ -283,11 +283,11 @@ func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.Do
|
|||||||
return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash)
|
return protocolerrors.Errorf(true, "expected block %s but got %s", expectedHash, blockHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
blockInsertionResult, err := flow.Domain().Consensus().ValidateAndInsertBlock(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "invalid block %s", blockHash)
|
return protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "invalid block %s", blockHash)
|
||||||
}
|
}
|
||||||
err = flow.OnNewBlock(block)
|
err = flow.OnNewBlock(block, blockInsertionResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||||
"github.com/kaspanet/kaspad/domain"
|
"github.com/kaspanet/kaspad/domain"
|
||||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
"github.com/kaspanet/kaspad/domain/utxoindex"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
|
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter"
|
||||||
@ -25,6 +27,7 @@ func NewManager(
|
|||||||
protocolManager *protocol.Manager,
|
protocolManager *protocol.Manager,
|
||||||
connectionManager *connmanager.ConnectionManager,
|
connectionManager *connmanager.ConnectionManager,
|
||||||
addressManager *addressmanager.AddressManager,
|
addressManager *addressmanager.AddressManager,
|
||||||
|
utxoIndex *utxoindex.UTXOIndex,
|
||||||
shutDownChan chan<- struct{}) *Manager {
|
shutDownChan chan<- struct{}) *Manager {
|
||||||
|
|
||||||
manager := Manager{
|
manager := Manager{
|
||||||
@ -35,6 +38,7 @@ func NewManager(
|
|||||||
protocolManager,
|
protocolManager,
|
||||||
connectionManager,
|
connectionManager,
|
||||||
addressManager,
|
addressManager,
|
||||||
|
utxoIndex,
|
||||||
shutDownChan,
|
shutDownChan,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -44,19 +48,63 @@ func NewManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NotifyBlockAddedToDAG notifies the manager that a block has been added to the DAG
|
// NotifyBlockAddedToDAG notifies the manager that a block has been added to the DAG
|
||||||
func (m *Manager) NotifyBlockAddedToDAG(block *externalapi.DomainBlock) error {
|
func (m *Manager) NotifyBlockAddedToDAG(block *externalapi.DomainBlock, blockInsertionResult *externalapi.BlockInsertionResult) error {
|
||||||
notification := appmessage.NewBlockAddedNotificationMessage(appmessage.DomainBlockToMsgBlock(block))
|
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyBlockAddedToDAG")
|
||||||
return m.context.NotificationManager.NotifyBlockAdded(notification)
|
defer onEnd()
|
||||||
|
|
||||||
|
if m.context.Config.UTXOIndex {
|
||||||
|
err := m.notifyUTXOsChanged(blockInsertionResult)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := m.notifyVirtualSelectedParentBlueScoreChanged()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(appmessage.DomainBlockToMsgBlock(block))
|
||||||
|
return m.context.NotificationManager.NotifyBlockAdded(blockAddedNotification)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyFinalityConflict notifies the manager that there's a finality conflict in the DAG
|
// NotifyFinalityConflict notifies the manager that there's a finality conflict in the DAG
|
||||||
func (m *Manager) NotifyFinalityConflict(violatingBlockHash string) error {
|
func (m *Manager) NotifyFinalityConflict(violatingBlockHash string) error {
|
||||||
|
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyFinalityConflict")
|
||||||
|
defer onEnd()
|
||||||
|
|
||||||
notification := appmessage.NewFinalityConflictNotificationMessage(violatingBlockHash)
|
notification := appmessage.NewFinalityConflictNotificationMessage(violatingBlockHash)
|
||||||
return m.context.NotificationManager.NotifyFinalityConflict(notification)
|
return m.context.NotificationManager.NotifyFinalityConflict(notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyFinalityConflictResolved notifies the manager that a finality conflict in the DAG has been resolved
|
// NotifyFinalityConflictResolved notifies the manager that a finality conflict in the DAG has been resolved
|
||||||
func (m *Manager) NotifyFinalityConflictResolved(finalityBlockHash string) error {
|
func (m *Manager) NotifyFinalityConflictResolved(finalityBlockHash string) error {
|
||||||
|
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyFinalityConflictResolved")
|
||||||
|
defer onEnd()
|
||||||
|
|
||||||
notification := appmessage.NewFinalityConflictResolvedNotificationMessage(finalityBlockHash)
|
notification := appmessage.NewFinalityConflictResolvedNotificationMessage(finalityBlockHash)
|
||||||
return m.context.NotificationManager.NotifyFinalityConflictResolved(notification)
|
return m.context.NotificationManager.NotifyFinalityConflictResolved(notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) notifyUTXOsChanged(blockInsertionResult *externalapi.BlockInsertionResult) error {
|
||||||
|
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyUTXOsChanged")
|
||||||
|
defer onEnd()
|
||||||
|
|
||||||
|
utxoIndexChanges, err := m.context.UTXOIndex.Update(blockInsertionResult.SelectedParentChainChanges)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return m.context.NotificationManager.NotifyUTXOsChanged(utxoIndexChanges)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) notifyVirtualSelectedParentBlueScoreChanged() error {
|
||||||
|
onEnd := logger.LogAndMeasureExecutionTime(log, "RPCManager.NotifyVirtualSelectedParentBlueScoreChanged")
|
||||||
|
defer onEnd()
|
||||||
|
|
||||||
|
virtualInfo, err := m.context.Domain.Consensus().GetVirtualInfo()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
notification := appmessage.NewVirtualSelectedParentBlueScoreChangedNotificationMessage(virtualInfo.BlueScore)
|
||||||
|
return m.context.NotificationManager.NotifyVirtualSelectedParentBlueScoreChanged(notification)
|
||||||
|
}
|
||||||
|
@ -12,28 +12,32 @@ import (
|
|||||||
type handler func(context *rpccontext.Context, router *router.Router, request appmessage.Message) (appmessage.Message, error)
|
type handler func(context *rpccontext.Context, router *router.Router, request appmessage.Message) (appmessage.Message, error)
|
||||||
|
|
||||||
var handlers = map[appmessage.MessageCommand]handler{
|
var handlers = map[appmessage.MessageCommand]handler{
|
||||||
appmessage.CmdGetCurrentNetworkRequestMessage: rpchandlers.HandleGetCurrentNetwork,
|
appmessage.CmdGetCurrentNetworkRequestMessage: rpchandlers.HandleGetCurrentNetwork,
|
||||||
appmessage.CmdSubmitBlockRequestMessage: rpchandlers.HandleSubmitBlock,
|
appmessage.CmdSubmitBlockRequestMessage: rpchandlers.HandleSubmitBlock,
|
||||||
appmessage.CmdGetBlockTemplateRequestMessage: rpchandlers.HandleGetBlockTemplate,
|
appmessage.CmdGetBlockTemplateRequestMessage: rpchandlers.HandleGetBlockTemplate,
|
||||||
appmessage.CmdNotifyBlockAddedRequestMessage: rpchandlers.HandleNotifyBlockAdded,
|
appmessage.CmdNotifyBlockAddedRequestMessage: rpchandlers.HandleNotifyBlockAdded,
|
||||||
appmessage.CmdGetPeerAddressesRequestMessage: rpchandlers.HandleGetPeerAddresses,
|
appmessage.CmdGetPeerAddressesRequestMessage: rpchandlers.HandleGetPeerAddresses,
|
||||||
appmessage.CmdGetSelectedTipHashRequestMessage: rpchandlers.HandleGetSelectedTipHash,
|
appmessage.CmdGetSelectedTipHashRequestMessage: rpchandlers.HandleGetSelectedTipHash,
|
||||||
appmessage.CmdGetMempoolEntryRequestMessage: rpchandlers.HandleGetMempoolEntry,
|
appmessage.CmdGetMempoolEntryRequestMessage: rpchandlers.HandleGetMempoolEntry,
|
||||||
appmessage.CmdGetConnectedPeerInfoRequestMessage: rpchandlers.HandleGetConnectedPeerInfo,
|
appmessage.CmdGetConnectedPeerInfoRequestMessage: rpchandlers.HandleGetConnectedPeerInfo,
|
||||||
appmessage.CmdAddPeerRequestMessage: rpchandlers.HandleAddPeer,
|
appmessage.CmdAddPeerRequestMessage: rpchandlers.HandleAddPeer,
|
||||||
appmessage.CmdSubmitTransactionRequestMessage: rpchandlers.HandleSubmitTransaction,
|
appmessage.CmdSubmitTransactionRequestMessage: rpchandlers.HandleSubmitTransaction,
|
||||||
appmessage.CmdNotifyChainChangedRequestMessage: rpchandlers.HandleNotifyChainChanged,
|
appmessage.CmdNotifyChainChangedRequestMessage: rpchandlers.HandleNotifyChainChanged,
|
||||||
appmessage.CmdGetBlockRequestMessage: rpchandlers.HandleGetBlock,
|
appmessage.CmdGetBlockRequestMessage: rpchandlers.HandleGetBlock,
|
||||||
appmessage.CmdGetSubnetworkRequestMessage: rpchandlers.HandleGetSubnetwork,
|
appmessage.CmdGetSubnetworkRequestMessage: rpchandlers.HandleGetSubnetwork,
|
||||||
appmessage.CmdGetChainFromBlockRequestMessage: rpchandlers.HandleGetChainFromBlock,
|
appmessage.CmdGetChainFromBlockRequestMessage: rpchandlers.HandleGetChainFromBlock,
|
||||||
appmessage.CmdGetBlocksRequestMessage: rpchandlers.HandleGetBlocks,
|
appmessage.CmdGetBlocksRequestMessage: rpchandlers.HandleGetBlocks,
|
||||||
appmessage.CmdGetBlockCountRequestMessage: rpchandlers.HandleGetBlockCount,
|
appmessage.CmdGetBlockCountRequestMessage: rpchandlers.HandleGetBlockCount,
|
||||||
appmessage.CmdGetBlockDAGInfoRequestMessage: rpchandlers.HandleGetBlockDAGInfo,
|
appmessage.CmdGetBlockDAGInfoRequestMessage: rpchandlers.HandleGetBlockDAGInfo,
|
||||||
appmessage.CmdResolveFinalityConflictRequestMessage: rpchandlers.HandleResolveFinalityConflict,
|
appmessage.CmdResolveFinalityConflictRequestMessage: rpchandlers.HandleResolveFinalityConflict,
|
||||||
appmessage.CmdNotifyFinalityConflictsRequestMessage: rpchandlers.HandleNotifyFinalityConflicts,
|
appmessage.CmdNotifyFinalityConflictsRequestMessage: rpchandlers.HandleNotifyFinalityConflicts,
|
||||||
appmessage.CmdGetMempoolEntriesRequestMessage: rpchandlers.HandleGetMempoolEntries,
|
appmessage.CmdGetMempoolEntriesRequestMessage: rpchandlers.HandleGetMempoolEntries,
|
||||||
appmessage.CmdShutDownRequestMessage: rpchandlers.HandleShutDown,
|
appmessage.CmdShutDownRequestMessage: rpchandlers.HandleShutDown,
|
||||||
appmessage.CmdGetHeadersRequestMessage: rpchandlers.HandleGetHeaders,
|
appmessage.CmdGetHeadersRequestMessage: rpchandlers.HandleGetHeaders,
|
||||||
|
appmessage.CmdNotifyUTXOsChangedRequestMessage: rpchandlers.HandleNotifyUTXOsChanged,
|
||||||
|
appmessage.CmdGetUTXOsByAddressesRequestMessage: rpchandlers.HandleGetUTXOsByAddresses,
|
||||||
|
appmessage.CmdGetVirtualSelectedParentBlueScoreRequestMessage: rpchandlers.HandleGetVirtualSelectedParentBlueScore,
|
||||||
|
appmessage.CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage: rpchandlers.HandleNotifyVirtualSelectedParentBlueScoreChanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) routerInitializer(router *router.Router, netConnection *netadapter.NetConnection) {
|
func (m *Manager) routerInitializer(router *router.Router, netConnection *netadapter.NetConnection) {
|
||||||
|
@ -3,6 +3,7 @@ package rpccontext
|
|||||||
import (
|
import (
|
||||||
"github.com/kaspanet/kaspad/app/protocol"
|
"github.com/kaspanet/kaspad/app/protocol"
|
||||||
"github.com/kaspanet/kaspad/domain"
|
"github.com/kaspanet/kaspad/domain"
|
||||||
|
"github.com/kaspanet/kaspad/domain/utxoindex"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/config"
|
"github.com/kaspanet/kaspad/infrastructure/config"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
|
||||||
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
|
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
|
||||||
@ -17,6 +18,7 @@ type Context struct {
|
|||||||
ProtocolManager *protocol.Manager
|
ProtocolManager *protocol.Manager
|
||||||
ConnectionManager *connmanager.ConnectionManager
|
ConnectionManager *connmanager.ConnectionManager
|
||||||
AddressManager *addressmanager.AddressManager
|
AddressManager *addressmanager.AddressManager
|
||||||
|
UTXOIndex *utxoindex.UTXOIndex
|
||||||
ShutDownChan chan<- struct{}
|
ShutDownChan chan<- struct{}
|
||||||
|
|
||||||
NotificationManager *NotificationManager
|
NotificationManager *NotificationManager
|
||||||
@ -29,6 +31,7 @@ func NewContext(cfg *config.Config,
|
|||||||
protocolManager *protocol.Manager,
|
protocolManager *protocol.Manager,
|
||||||
connectionManager *connmanager.ConnectionManager,
|
connectionManager *connmanager.ConnectionManager,
|
||||||
addressManager *addressmanager.AddressManager,
|
addressManager *addressmanager.AddressManager,
|
||||||
|
utxoIndex *utxoindex.UTXOIndex,
|
||||||
shutDownChan chan<- struct{}) *Context {
|
shutDownChan chan<- struct{}) *Context {
|
||||||
|
|
||||||
context := &Context{
|
context := &Context{
|
||||||
@ -38,8 +41,10 @@ func NewContext(cfg *config.Config,
|
|||||||
ProtocolManager: protocolManager,
|
ProtocolManager: protocolManager,
|
||||||
ConnectionManager: connectionManager,
|
ConnectionManager: connectionManager,
|
||||||
AddressManager: addressManager,
|
AddressManager: addressManager,
|
||||||
|
UTXOIndex: utxoIndex,
|
||||||
ShutDownChan: shutDownChan,
|
ShutDownChan: shutDownChan,
|
||||||
}
|
}
|
||||||
context.NotificationManager = NewNotificationManager()
|
context.NotificationManager = NewNotificationManager()
|
||||||
|
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package rpccontext
|
package rpccontext
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"github.com/kaspanet/kaspad/app/appmessage"
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
"github.com/kaspanet/kaspad/domain/utxoindex"
|
||||||
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"sync"
|
"sync"
|
||||||
@ -13,12 +15,23 @@ type NotificationManager struct {
|
|||||||
listeners map[*routerpkg.Router]*NotificationListener
|
listeners map[*routerpkg.Router]*NotificationListener
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UTXOsChangedNotificationAddress represents a kaspad address.
|
||||||
|
// This type is meant to be used in UTXOsChanged notifications
|
||||||
|
type UTXOsChangedNotificationAddress struct {
|
||||||
|
Address string
|
||||||
|
ScriptPublicKeyString utxoindex.ScriptPublicKeyString
|
||||||
|
}
|
||||||
|
|
||||||
// NotificationListener represents a registered RPC notification listener
|
// NotificationListener represents a registered RPC notification listener
|
||||||
type NotificationListener struct {
|
type NotificationListener struct {
|
||||||
propagateBlockAddedNotifications bool
|
propagateBlockAddedNotifications bool
|
||||||
propagateChainChangedNotifications bool
|
propagateChainChangedNotifications bool
|
||||||
propagateFinalityConflictNotifications bool
|
propagateFinalityConflictNotifications bool
|
||||||
propagateFinalityConflictResolvedNotifications bool
|
propagateFinalityConflictResolvedNotifications bool
|
||||||
|
propagateUTXOsChangedNotifications bool
|
||||||
|
propagateVirtualSelectedParentBlueScoreChangedNotifications bool
|
||||||
|
|
||||||
|
propagateUTXOsChangedNotificationAddresses []*UTXOsChangedNotificationAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNotificationManager creates a new NotificationManager
|
// NewNotificationManager creates a new NotificationManager
|
||||||
@ -123,12 +136,58 @@ func (nm *NotificationManager) NotifyFinalityConflictResolved(notification *appm
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotifyUTXOsChanged notifies the notification manager that UTXOs have been changed
|
||||||
|
func (nm *NotificationManager) NotifyUTXOsChanged(utxoChanges *utxoindex.UTXOChanges) error {
|
||||||
|
nm.RLock()
|
||||||
|
defer nm.RUnlock()
|
||||||
|
|
||||||
|
for router, listener := range nm.listeners {
|
||||||
|
if listener.propagateUTXOsChangedNotifications {
|
||||||
|
// Filter utxoChanges and create a notification
|
||||||
|
notification := listener.convertUTXOChangesToUTXOsChangedNotification(utxoChanges)
|
||||||
|
|
||||||
|
// Don't send the notification if it's empty
|
||||||
|
if len(notification.Added) == 0 && len(notification.Removed) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue the notification
|
||||||
|
err := router.OutgoingRoute().Enqueue(notification)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyVirtualSelectedParentBlueScoreChanged notifies the notification manager that the DAG's
|
||||||
|
// virtual selected parent blue score has changed
|
||||||
|
func (nm *NotificationManager) NotifyVirtualSelectedParentBlueScoreChanged(
|
||||||
|
notification *appmessage.VirtualSelectedParentBlueScoreChangedNotificationMessage) error {
|
||||||
|
|
||||||
|
nm.RLock()
|
||||||
|
defer nm.RUnlock()
|
||||||
|
|
||||||
|
for router, listener := range nm.listeners {
|
||||||
|
if listener.propagateVirtualSelectedParentBlueScoreChangedNotifications {
|
||||||
|
err := router.OutgoingRoute().Enqueue(notification)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func newNotificationListener() *NotificationListener {
|
func newNotificationListener() *NotificationListener {
|
||||||
return &NotificationListener{
|
return &NotificationListener{
|
||||||
propagateBlockAddedNotifications: false,
|
propagateBlockAddedNotifications: false,
|
||||||
propagateChainChangedNotifications: false,
|
propagateChainChangedNotifications: false,
|
||||||
propagateFinalityConflictNotifications: false,
|
propagateFinalityConflictNotifications: false,
|
||||||
propagateFinalityConflictResolvedNotifications: false,
|
propagateFinalityConflictResolvedNotifications: false,
|
||||||
|
propagateUTXOsChangedNotifications: false,
|
||||||
|
propagateVirtualSelectedParentBlueScoreChangedNotifications: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,3 +214,40 @@ func (nl *NotificationListener) PropagateFinalityConflictNotifications() {
|
|||||||
func (nl *NotificationListener) PropagateFinalityConflictResolvedNotifications() {
|
func (nl *NotificationListener) PropagateFinalityConflictResolvedNotifications() {
|
||||||
nl.propagateFinalityConflictResolvedNotifications = true
|
nl.propagateFinalityConflictResolvedNotifications = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PropagateUTXOsChangedNotifications instructs the listener to send UTXOs changed notifications
|
||||||
|
// to the remote listener
|
||||||
|
func (nl *NotificationListener) PropagateUTXOsChangedNotifications(addresses []*UTXOsChangedNotificationAddress) {
|
||||||
|
nl.propagateUTXOsChangedNotifications = true
|
||||||
|
nl.propagateUTXOsChangedNotificationAddresses = addresses
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nl *NotificationListener) convertUTXOChangesToUTXOsChangedNotification(
|
||||||
|
utxoChanges *utxoindex.UTXOChanges) *appmessage.UTXOsChangedNotificationMessage {
|
||||||
|
|
||||||
|
notification := &appmessage.UTXOsChangedNotificationMessage{}
|
||||||
|
for _, listenerAddress := range nl.propagateUTXOsChangedNotificationAddresses {
|
||||||
|
listenerScriptPublicKeyString := listenerAddress.ScriptPublicKeyString
|
||||||
|
if addedPairs, ok := utxoChanges.Added[listenerScriptPublicKeyString]; ok {
|
||||||
|
notification.Added = ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(listenerAddress.Address, addedPairs)
|
||||||
|
}
|
||||||
|
if removedOutpoints, ok := utxoChanges.Removed[listenerScriptPublicKeyString]; ok {
|
||||||
|
for outpoint := range removedOutpoints {
|
||||||
|
notification.Removed = append(notification.Removed, &appmessage.UTXOsByAddressesEntry{
|
||||||
|
Address: listenerAddress.Address,
|
||||||
|
Outpoint: &appmessage.RPCOutpoint{
|
||||||
|
TransactionID: hex.EncodeToString(outpoint.TransactionID[:]),
|
||||||
|
Index: outpoint.Index,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return notification
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropagateVirtualSelectedParentBlueScoreChangedNotifications instructs the listener to send
|
||||||
|
// virtual selected parent blue score notifications to the remote listener
|
||||||
|
func (nl *NotificationListener) PropagateVirtualSelectedParentBlueScoreChangedNotifications() {
|
||||||
|
nl.propagateVirtualSelectedParentBlueScoreChangedNotifications = true
|
||||||
|
}
|
||||||
|
29
app/rpc/rpccontext/utxos_by_addresses.go
Normal file
29
app/rpc/rpccontext/utxos_by_addresses.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package rpccontext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
"github.com/kaspanet/kaspad/domain/utxoindex"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries converts
|
||||||
|
// UTXOOutpointEntryPairs to a slice of UTXOsByAddressesEntry
|
||||||
|
func ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(address string, pairs utxoindex.UTXOOutpointEntryPairs) []*appmessage.UTXOsByAddressesEntry {
|
||||||
|
utxosByAddressesEntries := make([]*appmessage.UTXOsByAddressesEntry, 0, len(pairs))
|
||||||
|
for outpoint, utxoEntry := range pairs {
|
||||||
|
utxosByAddressesEntries = append(utxosByAddressesEntries, &appmessage.UTXOsByAddressesEntry{
|
||||||
|
Address: address,
|
||||||
|
Outpoint: &appmessage.RPCOutpoint{
|
||||||
|
TransactionID: hex.EncodeToString(outpoint.TransactionID[:]),
|
||||||
|
Index: outpoint.Index,
|
||||||
|
},
|
||||||
|
UTXOEntry: &appmessage.RPCUTXOEntry{
|
||||||
|
Amount: utxoEntry.Amount(),
|
||||||
|
ScriptPubKey: hex.EncodeToString(utxoEntry.ScriptPublicKey()),
|
||||||
|
BlockBlueScore: utxoEntry.BlockBlueScore(),
|
||||||
|
IsCoinbase: utxoEntry.IsCoinbase(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return utxosByAddressesEntries
|
||||||
|
}
|
45
app/rpc/rpchandlers/get_utxos_by_addresses.go
Normal file
45
app/rpc/rpchandlers/get_utxos_by_addresses.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package rpchandlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||||
|
"github.com/kaspanet/kaspad/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandleGetUTXOsByAddresses handles the respectively named RPC command
|
||||||
|
func HandleGetUTXOsByAddresses(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||||
|
if !context.Config.UTXOIndex {
|
||||||
|
errorMessage := &appmessage.GetUTXOsByAddressesResponseMessage{}
|
||||||
|
errorMessage.Error = appmessage.RPCErrorf("Method unavailable when kaspad is run without --utxoindex")
|
||||||
|
return errorMessage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
getUTXOsByAddressesRequest := request.(*appmessage.GetUTXOsByAddressesRequestMessage)
|
||||||
|
|
||||||
|
allEntries := make([]*appmessage.UTXOsByAddressesEntry, 0)
|
||||||
|
for _, addressString := range getUTXOsByAddressesRequest.Addresses {
|
||||||
|
address, err := util.DecodeAddress(addressString, context.Config.ActiveNetParams.Prefix)
|
||||||
|
if err != nil {
|
||||||
|
errorMessage := &appmessage.GetUTXOsByAddressesResponseMessage{}
|
||||||
|
errorMessage.Error = appmessage.RPCErrorf("Could not decode address '%s': %s", addressString, err)
|
||||||
|
return errorMessage, nil
|
||||||
|
}
|
||||||
|
scriptPublicKey, err := txscript.PayToAddrScript(address)
|
||||||
|
if err != nil {
|
||||||
|
errorMessage := &appmessage.GetUTXOsByAddressesResponseMessage{}
|
||||||
|
errorMessage.Error = appmessage.RPCErrorf("Could not create a scriptPublicKey for address '%s': %s", addressString, err)
|
||||||
|
return errorMessage, nil
|
||||||
|
}
|
||||||
|
utxoOutpointEntryPairs, err := context.UTXOIndex.UTXOs(scriptPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
entries := rpccontext.ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(addressString, utxoOutpointEntryPairs)
|
||||||
|
allEntries = append(allEntries, entries...)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := appmessage.NewGetUTXOsByAddressesResponseMessage(allEntries)
|
||||||
|
return response, nil
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package rpchandlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandleGetVirtualSelectedParentBlueScore handles the respectively named RPC command
|
||||||
|
func HandleGetVirtualSelectedParentBlueScore(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||||
|
virtualInfo, err := context.Domain.Consensus().GetVirtualInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return appmessage.NewGetVirtualSelectedParentBlueScoreResponseMessage(virtualInfo.BlueScore), nil
|
||||||
|
}
|
51
app/rpc/rpchandlers/notify_utxos_changed.go
Normal file
51
app/rpc/rpchandlers/notify_utxos_changed.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package rpchandlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||||
|
"github.com/kaspanet/kaspad/domain/utxoindex"
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||||
|
"github.com/kaspanet/kaspad/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandleNotifyUTXOsChanged handles the respectively named RPC command
|
||||||
|
func HandleNotifyUTXOsChanged(context *rpccontext.Context, router *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||||
|
if !context.Config.UTXOIndex {
|
||||||
|
errorMessage := appmessage.NewNotifyUTXOsChangedResponseMessage()
|
||||||
|
errorMessage.Error = appmessage.RPCErrorf("Method unavailable when kaspad is run without --utxoindex")
|
||||||
|
return errorMessage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyUTXOsChangedRequest := request.(*appmessage.NotifyUTXOsChangedRequestMessage)
|
||||||
|
|
||||||
|
addresses := make([]*rpccontext.UTXOsChangedNotificationAddress, len(notifyUTXOsChangedRequest.Addresses))
|
||||||
|
for i, addressString := range notifyUTXOsChangedRequest.Addresses {
|
||||||
|
address, err := util.DecodeAddress(addressString, context.Config.ActiveNetParams.Prefix)
|
||||||
|
if err != nil {
|
||||||
|
errorMessage := appmessage.NewNotifyUTXOsChangedResponseMessage()
|
||||||
|
errorMessage.Error = appmessage.RPCErrorf("Could not decode address '%s': %s", addressString, err)
|
||||||
|
return errorMessage, nil
|
||||||
|
}
|
||||||
|
scriptPublicKey, err := txscript.PayToAddrScript(address)
|
||||||
|
if err != nil {
|
||||||
|
errorMessage := appmessage.NewNotifyUTXOsChangedResponseMessage()
|
||||||
|
errorMessage.Error = appmessage.RPCErrorf("Could not create a scriptPublicKey for address '%s': %s", addressString, err)
|
||||||
|
return errorMessage, nil
|
||||||
|
}
|
||||||
|
scriptPublicKeyString := utxoindex.ConvertScriptPublicKeyToString(scriptPublicKey)
|
||||||
|
addresses[i] = &rpccontext.UTXOsChangedNotificationAddress{
|
||||||
|
Address: addressString,
|
||||||
|
ScriptPublicKeyString: scriptPublicKeyString,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listener, err := context.NotificationManager.Listener(router)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
listener.PropagateUTXOsChangedNotifications(addresses)
|
||||||
|
|
||||||
|
response := appmessage.NewNotifyUTXOsChangedResponseMessage()
|
||||||
|
return response, nil
|
||||||
|
}
|
@ -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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandleNotifyVirtualSelectedParentBlueScoreChanged handles the respectively named RPC command
|
||||||
|
func HandleNotifyVirtualSelectedParentBlueScoreChanged(context *rpccontext.Context, router *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||||
|
listener, err := context.NotificationManager.Listener(router)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
listener.PropagateVirtualSelectedParentBlueScoreChangedNotifications()
|
||||||
|
|
||||||
|
response := appmessage.NewNotifyVirtualSelectedParentBlueScoreChangedResponseMessage()
|
||||||
|
return response, nil
|
||||||
|
}
|
@ -13,9 +13,15 @@ import (
|
|||||||
func HandleSubmitTransaction(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
func HandleSubmitTransaction(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||||
submitTransactionRequest := request.(*appmessage.SubmitTransactionRequestMessage)
|
submitTransactionRequest := request.(*appmessage.SubmitTransactionRequestMessage)
|
||||||
|
|
||||||
domainTransaction := appmessage.MsgTxToDomainTransaction(submitTransactionRequest.Transaction)
|
domainTransaction, err := appmessage.RPCTransactionToDomainTransaction(submitTransactionRequest.Transaction)
|
||||||
|
if err != nil {
|
||||||
|
errorMessage := &appmessage.SubmitTransactionResponseMessage{}
|
||||||
|
errorMessage.Error = appmessage.RPCErrorf("Could not parse transaction: %s", err)
|
||||||
|
return errorMessage, nil
|
||||||
|
}
|
||||||
|
|
||||||
transactionID := consensushashing.TransactionID(domainTransaction)
|
transactionID := consensushashing.TransactionID(domainTransaction)
|
||||||
err := context.ProtocolManager.AddTransaction(domainTransaction)
|
err = context.ProtocolManager.AddTransaction(domainTransaction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.As(err, &mempool.RuleError{}) {
|
if !errors.As(err, &mempool.RuleError{}) {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -141,6 +141,13 @@ func (s *consensus) GetBlockInfo(blockHash *externalapi.DomainHash) (*externalap
|
|||||||
return blockInfo, nil
|
return blockInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *consensus) GetBlockAcceptanceData(blockHash *externalapi.DomainHash) (externalapi.AcceptanceData, error) {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
|
return s.acceptanceDataStore.Get(s.databaseContext, blockHash)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *consensus) GetHashesBetween(lowHash, highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
func (s *consensus) GetHashesBetween(lowHash, highHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.lock.Unlock()
|
||||||
@ -196,10 +203,16 @@ func (s *consensus) GetVirtualSelectedParent() (*externalapi.DomainBlock, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *consensus) Tips() ([]*externalapi.DomainHash, error) {
|
func (s *consensus) Tips() ([]*externalapi.DomainHash, error) {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
return s.consensusStateStore.Tips(s.databaseContext)
|
return s.consensusStateStore.Tips(s.databaseContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *consensus) GetVirtualInfo() (*externalapi.VirtualInfo, error) {
|
func (s *consensus) GetVirtualInfo() (*externalapi.VirtualInfo, error) {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
blockRelations, err := s.blockRelationStore.BlockRelation(s.databaseContext, model.VirtualBlockHash)
|
blockRelations, err := s.blockRelationStore.BlockRelation(s.databaseContext, model.VirtualBlockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -212,11 +225,16 @@ func (s *consensus) GetVirtualInfo() (*externalapi.VirtualInfo, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
virtualGHOSTDAGData, err := s.ghostdagDataStore.Get(s.databaseContext, model.VirtualBlockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &externalapi.VirtualInfo{
|
return &externalapi.VirtualInfo{
|
||||||
ParentHashes: blockRelations.Parents,
|
ParentHashes: blockRelations.Parents,
|
||||||
Bits: bits,
|
Bits: bits,
|
||||||
PastMedianTime: pastMedianTime,
|
PastMedianTime: pastMedianTime,
|
||||||
|
BlueScore: virtualGHOSTDAGData.BlueScore(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package serialization
|
package serialization
|
||||||
|
|
||||||
import "github.com/kaspanet/kaspad/domain/consensus/model"
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
)
|
||||||
|
|
||||||
// DomainAcceptanceDataToDbAcceptanceData converts model.AcceptanceData to DbAcceptanceData
|
// DomainAcceptanceDataToDbAcceptanceData converts model.AcceptanceData to DbAcceptanceData
|
||||||
func DomainAcceptanceDataToDbAcceptanceData(domainAcceptanceData model.AcceptanceData) *DbAcceptanceData {
|
func DomainAcceptanceDataToDbAcceptanceData(domainAcceptanceData externalapi.AcceptanceData) *DbAcceptanceData {
|
||||||
dbBlockAcceptanceData := make([]*DbBlockAcceptanceData, len(domainAcceptanceData))
|
dbBlockAcceptanceData := make([]*DbBlockAcceptanceData, len(domainAcceptanceData))
|
||||||
for i, blockAcceptanceData := range domainAcceptanceData {
|
for i, blockAcceptanceData := range domainAcceptanceData {
|
||||||
dbTransactionAcceptanceData := make([]*DbTransactionAcceptanceData,
|
dbTransactionAcceptanceData := make([]*DbTransactionAcceptanceData,
|
||||||
@ -29,10 +31,10 @@ func DomainAcceptanceDataToDbAcceptanceData(domainAcceptanceData model.Acceptanc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DbAcceptanceDataToDomainAcceptanceData converts DbAcceptanceData to model.AcceptanceData
|
// DbAcceptanceDataToDomainAcceptanceData converts DbAcceptanceData to model.AcceptanceData
|
||||||
func DbAcceptanceDataToDomainAcceptanceData(dbAcceptanceData *DbAcceptanceData) (model.AcceptanceData, error) {
|
func DbAcceptanceDataToDomainAcceptanceData(dbAcceptanceData *DbAcceptanceData) (externalapi.AcceptanceData, error) {
|
||||||
domainAcceptanceData := make(model.AcceptanceData, len(dbAcceptanceData.BlockAcceptanceData))
|
domainAcceptanceData := make(externalapi.AcceptanceData, len(dbAcceptanceData.BlockAcceptanceData))
|
||||||
for i, dbBlockAcceptanceData := range dbAcceptanceData.BlockAcceptanceData {
|
for i, dbBlockAcceptanceData := range dbAcceptanceData.BlockAcceptanceData {
|
||||||
domainTransactionAcceptanceData := make([]*model.TransactionAcceptanceData,
|
domainTransactionAcceptanceData := make([]*externalapi.TransactionAcceptanceData,
|
||||||
len(dbBlockAcceptanceData.TransactionAcceptanceData))
|
len(dbBlockAcceptanceData.TransactionAcceptanceData))
|
||||||
|
|
||||||
for j, dbTransactionAcceptanceData := range dbBlockAcceptanceData.TransactionAcceptanceData {
|
for j, dbTransactionAcceptanceData := range dbBlockAcceptanceData.TransactionAcceptanceData {
|
||||||
@ -40,14 +42,14 @@ func DbAcceptanceDataToDomainAcceptanceData(dbAcceptanceData *DbAcceptanceData)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
domainTransactionAcceptanceData[j] = &model.TransactionAcceptanceData{
|
domainTransactionAcceptanceData[j] = &externalapi.TransactionAcceptanceData{
|
||||||
Transaction: domainTransaction,
|
Transaction: domainTransaction,
|
||||||
Fee: dbTransactionAcceptanceData.Fee,
|
Fee: dbTransactionAcceptanceData.Fee,
|
||||||
IsAccepted: dbTransactionAcceptanceData.IsAccepted,
|
IsAccepted: dbTransactionAcceptanceData.IsAccepted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
domainAcceptanceData[i] = &model.BlockAcceptanceData{
|
domainAcceptanceData[i] = &externalapi.BlockAcceptanceData{
|
||||||
TransactionAcceptanceData: domainTransactionAcceptanceData,
|
TransactionAcceptanceData: domainTransactionAcceptanceData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ var bucket = dbkeys.MakeBucket([]byte("acceptance-data"))
|
|||||||
|
|
||||||
// acceptanceDataStore represents a store of AcceptanceData
|
// acceptanceDataStore represents a store of AcceptanceData
|
||||||
type acceptanceDataStore struct {
|
type acceptanceDataStore struct {
|
||||||
staging map[externalapi.DomainHash]model.AcceptanceData
|
staging map[externalapi.DomainHash]externalapi.AcceptanceData
|
||||||
toDelete map[externalapi.DomainHash]struct{}
|
toDelete map[externalapi.DomainHash]struct{}
|
||||||
cache *lrucache.LRUCache
|
cache *lrucache.LRUCache
|
||||||
}
|
}
|
||||||
@ -21,14 +21,14 @@ type acceptanceDataStore struct {
|
|||||||
// New instantiates a new AcceptanceDataStore
|
// New instantiates a new AcceptanceDataStore
|
||||||
func New(cacheSize int) model.AcceptanceDataStore {
|
func New(cacheSize int) model.AcceptanceDataStore {
|
||||||
return &acceptanceDataStore{
|
return &acceptanceDataStore{
|
||||||
staging: make(map[externalapi.DomainHash]model.AcceptanceData),
|
staging: make(map[externalapi.DomainHash]externalapi.AcceptanceData),
|
||||||
toDelete: make(map[externalapi.DomainHash]struct{}),
|
toDelete: make(map[externalapi.DomainHash]struct{}),
|
||||||
cache: lrucache.New(cacheSize),
|
cache: lrucache.New(cacheSize),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stage stages the given acceptanceData for the given blockHash
|
// Stage stages the given acceptanceData for the given blockHash
|
||||||
func (ads *acceptanceDataStore) Stage(blockHash *externalapi.DomainHash, acceptanceData model.AcceptanceData) {
|
func (ads *acceptanceDataStore) Stage(blockHash *externalapi.DomainHash, acceptanceData externalapi.AcceptanceData) {
|
||||||
ads.staging[*blockHash] = acceptanceData.Clone()
|
ads.staging[*blockHash] = acceptanceData.Clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ func (ads *acceptanceDataStore) IsStaged() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ads *acceptanceDataStore) Discard() {
|
func (ads *acceptanceDataStore) Discard() {
|
||||||
ads.staging = make(map[externalapi.DomainHash]model.AcceptanceData)
|
ads.staging = make(map[externalapi.DomainHash]externalapi.AcceptanceData)
|
||||||
ads.toDelete = make(map[externalapi.DomainHash]struct{})
|
ads.toDelete = make(map[externalapi.DomainHash]struct{})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,13 +67,13 @@ func (ads *acceptanceDataStore) Commit(dbTx model.DBTransaction) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get gets the acceptanceData associated with the given blockHash
|
// Get gets the acceptanceData associated with the given blockHash
|
||||||
func (ads *acceptanceDataStore) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (model.AcceptanceData, error) {
|
func (ads *acceptanceDataStore) Get(dbContext model.DBReader, blockHash *externalapi.DomainHash) (externalapi.AcceptanceData, error) {
|
||||||
if acceptanceData, ok := ads.staging[*blockHash]; ok {
|
if acceptanceData, ok := ads.staging[*blockHash]; ok {
|
||||||
return acceptanceData.Clone(), nil
|
return acceptanceData.Clone(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if acceptanceData, ok := ads.cache.Get(blockHash); ok {
|
if acceptanceData, ok := ads.cache.Get(blockHash); ok {
|
||||||
return acceptanceData.(model.AcceptanceData).Clone(), nil
|
return acceptanceData.(externalapi.AcceptanceData).Clone(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
acceptanceDataBytes, err := dbContext.Get(ads.hashAsKey(blockHash))
|
acceptanceDataBytes, err := dbContext.Get(ads.hashAsKey(blockHash))
|
||||||
@ -98,12 +98,12 @@ func (ads *acceptanceDataStore) Delete(blockHash *externalapi.DomainHash) {
|
|||||||
ads.toDelete[*blockHash] = struct{}{}
|
ads.toDelete[*blockHash] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ads *acceptanceDataStore) serializeAcceptanceData(acceptanceData model.AcceptanceData) ([]byte, error) {
|
func (ads *acceptanceDataStore) serializeAcceptanceData(acceptanceData externalapi.AcceptanceData) ([]byte, error) {
|
||||||
dbAcceptanceData := serialization.DomainAcceptanceDataToDbAcceptanceData(acceptanceData)
|
dbAcceptanceData := serialization.DomainAcceptanceDataToDbAcceptanceData(acceptanceData)
|
||||||
return proto.Marshal(dbAcceptanceData)
|
return proto.Marshal(dbAcceptanceData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ads *acceptanceDataStore) deserializeAcceptanceData(acceptanceDataBytes []byte) (model.AcceptanceData, error) {
|
func (ads *acceptanceDataStore) deserializeAcceptanceData(acceptanceDataBytes []byte) (externalapi.AcceptanceData, error) {
|
||||||
dbAcceptanceData := &serialization.DbAcceptanceData{}
|
dbAcceptanceData := &serialization.DbAcceptanceData{}
|
||||||
err := proto.Unmarshal(acceptanceDataBytes, dbAcceptanceData)
|
err := proto.Unmarshal(acceptanceDataBytes, dbAcceptanceData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
package model
|
package externalapi
|
||||||
|
|
||||||
import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
||||||
|
|
||||||
// AcceptanceData stores data about which transactions were accepted by a block.
|
// AcceptanceData stores data about which transactions were accepted by a block.
|
||||||
// It's ordered in the same way as the block merge set blues.
|
// It's ordered in the same way as the block merge set blues.
|
||||||
@ -42,7 +40,7 @@ func (bad *BlockAcceptanceData) Clone() *BlockAcceptanceData {
|
|||||||
// TransactionAcceptanceData stores a transaction together with an indication
|
// TransactionAcceptanceData stores a transaction together with an indication
|
||||||
// if it was accepted or not by some block
|
// if it was accepted or not by some block
|
||||||
type TransactionAcceptanceData struct {
|
type TransactionAcceptanceData struct {
|
||||||
Transaction *externalapi.DomainTransaction
|
Transaction *DomainTransaction
|
||||||
Fee uint64
|
Fee uint64
|
||||||
IsAccepted bool
|
IsAccepted bool
|
||||||
}
|
}
|
@ -9,6 +9,7 @@ type Consensus interface {
|
|||||||
GetBlock(blockHash *DomainHash) (*DomainBlock, error)
|
GetBlock(blockHash *DomainHash) (*DomainBlock, error)
|
||||||
GetBlockHeader(blockHash *DomainHash) (*DomainBlockHeader, error)
|
GetBlockHeader(blockHash *DomainHash) (*DomainBlockHeader, error)
|
||||||
GetBlockInfo(blockHash *DomainHash) (*BlockInfo, error)
|
GetBlockInfo(blockHash *DomainHash) (*BlockInfo, error)
|
||||||
|
GetBlockAcceptanceData(blockHash *DomainHash) (AcceptanceData, error)
|
||||||
|
|
||||||
GetHashesBetween(lowHash, highHash *DomainHash) ([]*DomainHash, error)
|
GetHashesBetween(lowHash, highHash *DomainHash) ([]*DomainHash, error)
|
||||||
GetMissingBlockBodyHashes(highHash *DomainHash) ([]*DomainHash, error)
|
GetMissingBlockBodyHashes(highHash *DomainHash) ([]*DomainHash, error)
|
||||||
|
@ -5,4 +5,5 @@ type VirtualInfo struct {
|
|||||||
ParentHashes []*DomainHash
|
ParentHashes []*DomainHash
|
||||||
Bits uint32
|
Bits uint32
|
||||||
PastMedianTime int64
|
PastMedianTime int64
|
||||||
|
BlueScore uint64
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,8 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|||||||
// AcceptanceDataStore represents a store of AcceptanceData
|
// AcceptanceDataStore represents a store of AcceptanceData
|
||||||
type AcceptanceDataStore interface {
|
type AcceptanceDataStore interface {
|
||||||
Store
|
Store
|
||||||
Stage(blockHash *externalapi.DomainHash, acceptanceData AcceptanceData)
|
Stage(blockHash *externalapi.DomainHash, acceptanceData externalapi.AcceptanceData)
|
||||||
IsStaged() bool
|
IsStaged() bool
|
||||||
Get(dbContext DBReader, blockHash *externalapi.DomainHash) (AcceptanceData, error)
|
Get(dbContext DBReader, blockHash *externalapi.DomainHash) (externalapi.AcceptanceData, error)
|
||||||
Delete(blockHash *externalapi.DomainHash)
|
Delete(blockHash *externalapi.DomainHash)
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,5 @@ type ConsensusStateManager interface {
|
|||||||
PopulateTransactionWithUTXOEntries(transaction *externalapi.DomainTransaction) error
|
PopulateTransactionWithUTXOEntries(transaction *externalapi.DomainTransaction) error
|
||||||
UpdatePruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error
|
UpdatePruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error
|
||||||
RestorePastUTXOSetIterator(blockHash *externalapi.DomainHash) (ReadOnlyUTXOSetIterator, error)
|
RestorePastUTXOSetIterator(blockHash *externalapi.DomainHash) (ReadOnlyUTXOSetIterator, error)
|
||||||
CalculatePastUTXOAndAcceptanceData(blockHash *externalapi.DomainHash) (UTXODiff, AcceptanceData, Multiset, error)
|
CalculatePastUTXOAndAcceptanceData(blockHash *externalapi.DomainHash) (UTXODiff, externalapi.AcceptanceData, Multiset, error)
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ func (bb *blockBuilder) newBlockAcceptedIDMerkleRoot() (*externalapi.DomainHash,
|
|||||||
return bb.calculateAcceptedIDMerkleRoot(newBlockAcceptanceData)
|
return bb.calculateAcceptedIDMerkleRoot(newBlockAcceptanceData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bb *blockBuilder) calculateAcceptedIDMerkleRoot(acceptanceData model.AcceptanceData) (*externalapi.DomainHash, error) {
|
func (bb *blockBuilder) calculateAcceptedIDMerkleRoot(acceptanceData externalapi.AcceptanceData) (*externalapi.DomainHash, error) {
|
||||||
var acceptedTransactions []*externalapi.DomainTransaction
|
var acceptedTransactions []*externalapi.DomainTransaction
|
||||||
for _, blockAcceptanceData := range acceptanceData {
|
for _, blockAcceptanceData := range acceptanceData {
|
||||||
for _, transactionAcceptance := range blockAcceptanceData.TransactionAcceptanceData {
|
for _, transactionAcceptance := range blockAcceptanceData.TransactionAcceptanceData {
|
||||||
|
@ -40,7 +40,7 @@ func (bb *testBlockBuilder) BuildBlockWithParents(parentHashes []*externalapi.Do
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bb *testBlockBuilder) buildHeaderWithParents(parentHashes []*externalapi.DomainHash,
|
func (bb *testBlockBuilder) buildHeaderWithParents(parentHashes []*externalapi.DomainHash,
|
||||||
transactions []*externalapi.DomainTransaction, acceptanceData model.AcceptanceData, multiset model.Multiset) (
|
transactions []*externalapi.DomainTransaction, acceptanceData externalapi.AcceptanceData, multiset model.Multiset) (
|
||||||
*externalapi.DomainBlockHeader, error) {
|
*externalapi.DomainBlockHeader, error) {
|
||||||
|
|
||||||
timeInMilliseconds, err := bb.minBlockTime(tempBlockHash)
|
timeInMilliseconds, err := bb.minBlockTime(tempBlockHash)
|
||||||
|
@ -65,7 +65,7 @@ func (c *coinbaseManager) ExpectedCoinbaseTransaction(blockHash *externalapi.Dom
|
|||||||
// coinbaseOutputForBlueBlock calculates the output that should go into the coinbase transaction of blueBlock
|
// coinbaseOutputForBlueBlock calculates the output that should go into the coinbase transaction of blueBlock
|
||||||
// If blueBlock gets no fee - returns nil for txOut
|
// If blueBlock gets no fee - returns nil for txOut
|
||||||
func (c *coinbaseManager) coinbaseOutputForBlueBlock(blueBlock *externalapi.DomainHash,
|
func (c *coinbaseManager) coinbaseOutputForBlueBlock(blueBlock *externalapi.DomainHash,
|
||||||
blockAcceptanceData *model.BlockAcceptanceData) (*externalapi.DomainTransactionOutput, bool, error) {
|
blockAcceptanceData *externalapi.BlockAcceptanceData) (*externalapi.DomainTransactionOutput, bool, error) {
|
||||||
|
|
||||||
totalFees := uint64(0)
|
totalFees := uint64(0)
|
||||||
for _, txAcceptanceData := range blockAcceptanceData.TransactionAcceptanceData {
|
for _, txAcceptanceData := range blockAcceptanceData.TransactionAcceptanceData {
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *externalapi.DomainHash) (
|
func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *externalapi.DomainHash) (
|
||||||
model.UTXODiff, model.AcceptanceData, model.Multiset, error) {
|
model.UTXODiff, externalapi.AcceptanceData, model.Multiset, error) {
|
||||||
|
|
||||||
log.Tracef("CalculatePastUTXOAndAcceptanceData start for block %s", blockHash)
|
log.Tracef("CalculatePastUTXOAndAcceptanceData start for block %s", blockHash)
|
||||||
defer log.Tracef("CalculatePastUTXOAndAcceptanceData end for block %s", blockHash)
|
defer log.Tracef("CalculatePastUTXOAndAcceptanceData end for block %s", blockHash)
|
||||||
@ -21,7 +21,7 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *
|
|||||||
if *blockHash == *csm.genesisHash {
|
if *blockHash == *csm.genesisHash {
|
||||||
log.Tracef("Block %s is the genesis. By definition, "+
|
log.Tracef("Block %s is the genesis. By definition, "+
|
||||||
"it has an empty UTXO diff, empty acceptance data, and a blank multiset", blockHash)
|
"it has an empty UTXO diff, empty acceptance data, and a blank multiset", blockHash)
|
||||||
return utxo.NewUTXODiff(), model.AcceptanceData{}, multiset.New(), nil
|
return utxo.NewUTXODiff(), externalapi.AcceptanceData{}, multiset.New(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
blockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, blockHash)
|
blockGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, blockHash)
|
||||||
@ -109,7 +109,7 @@ func (csm *consensusStateManager) restorePastUTXO(blockHash *externalapi.DomainH
|
|||||||
|
|
||||||
func (csm *consensusStateManager) applyBlueBlocks(blockHash *externalapi.DomainHash,
|
func (csm *consensusStateManager) applyBlueBlocks(blockHash *externalapi.DomainHash,
|
||||||
selectedParentPastUTXODiff model.MutableUTXODiff, ghostdagData model.BlockGHOSTDAGData) (
|
selectedParentPastUTXODiff model.MutableUTXODiff, ghostdagData model.BlockGHOSTDAGData) (
|
||||||
model.AcceptanceData, model.MutableUTXODiff, error) {
|
externalapi.AcceptanceData, model.MutableUTXODiff, error) {
|
||||||
|
|
||||||
log.Tracef("applyBlueBlocks start for block %s", blockHash)
|
log.Tracef("applyBlueBlocks start for block %s", blockHash)
|
||||||
defer log.Tracef("applyBlueBlocks end for block %s", blockHash)
|
defer log.Tracef("applyBlueBlocks end for block %s", blockHash)
|
||||||
@ -125,15 +125,15 @@ func (csm *consensusStateManager) applyBlueBlocks(blockHash *externalapi.DomainH
|
|||||||
}
|
}
|
||||||
log.Tracef("The past median time for block %s is: %d", blockHash, selectedParentMedianTime)
|
log.Tracef("The past median time for block %s is: %d", blockHash, selectedParentMedianTime)
|
||||||
|
|
||||||
multiblockAcceptanceData := make(model.AcceptanceData, len(blueBlocks))
|
multiblockAcceptanceData := make(externalapi.AcceptanceData, len(blueBlocks))
|
||||||
accumulatedUTXODiff := selectedParentPastUTXODiff
|
accumulatedUTXODiff := selectedParentPastUTXODiff
|
||||||
accumulatedMass := uint64(0)
|
accumulatedMass := uint64(0)
|
||||||
|
|
||||||
for i, blueBlock := range blueBlocks {
|
for i, blueBlock := range blueBlocks {
|
||||||
blueBlockHash := consensushashing.BlockHash(blueBlock)
|
blueBlockHash := consensushashing.BlockHash(blueBlock)
|
||||||
log.Tracef("Applying blue block %s", blueBlockHash)
|
log.Tracef("Applying blue block %s", blueBlockHash)
|
||||||
blockAcceptanceData := &model.BlockAcceptanceData{
|
blockAcceptanceData := &externalapi.BlockAcceptanceData{
|
||||||
TransactionAcceptanceData: make([]*model.TransactionAcceptanceData, len(blueBlock.Transactions)),
|
TransactionAcceptanceData: make([]*externalapi.TransactionAcceptanceData, len(blueBlock.Transactions)),
|
||||||
}
|
}
|
||||||
isSelectedParent := i == 0
|
isSelectedParent := i == 0
|
||||||
log.Tracef("Is blue block %s the selected parent: %t", blueBlockHash, isSelectedParent)
|
log.Tracef("Is blue block %s the selected parent: %t", blueBlockHash, isSelectedParent)
|
||||||
@ -153,7 +153,7 @@ func (csm *consensusStateManager) applyBlueBlocks(blockHash *externalapi.DomainH
|
|||||||
log.Tracef("Transaction %s in block %s isAccepted: %t, fee: %d",
|
log.Tracef("Transaction %s in block %s isAccepted: %t, fee: %d",
|
||||||
transactionID, blueBlockHash, isAccepted, transaction.Fee)
|
transactionID, blueBlockHash, isAccepted, transaction.Fee)
|
||||||
|
|
||||||
blockAcceptanceData.TransactionAcceptanceData[j] = &model.TransactionAcceptanceData{
|
blockAcceptanceData.TransactionAcceptanceData[j] = &externalapi.TransactionAcceptanceData{
|
||||||
Transaction: transaction,
|
Transaction: transaction,
|
||||||
Fee: transaction.Fee,
|
Fee: transaction.Fee,
|
||||||
IsAccepted: isAccepted,
|
IsAccepted: isAccepted,
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (csm *consensusStateManager) calculateMultiset(
|
func (csm *consensusStateManager) calculateMultiset(
|
||||||
acceptanceData model.AcceptanceData, blockGHOSTDAGData model.BlockGHOSTDAGData) (model.Multiset, error) {
|
acceptanceData externalapi.AcceptanceData, blockGHOSTDAGData model.BlockGHOSTDAGData) (model.Multiset, error) {
|
||||||
|
|
||||||
log.Tracef("calculateMultiset start for block with selected parent %s", blockGHOSTDAGData.SelectedParent())
|
log.Tracef("calculateMultiset start for block with selected parent %s", blockGHOSTDAGData.SelectedParent())
|
||||||
defer log.Tracef("calculateMultiset end for block with selected parent %s", blockGHOSTDAGData.SelectedParent())
|
defer log.Tracef("calculateMultiset end for block with selected parent %s", blockGHOSTDAGData.SelectedParent())
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (csm *consensusStateManager) verifyUTXO(block *externalapi.DomainBlock, blockHash *externalapi.DomainHash,
|
func (csm *consensusStateManager) verifyUTXO(block *externalapi.DomainBlock, blockHash *externalapi.DomainHash,
|
||||||
pastUTXODiff model.UTXODiff, acceptanceData model.AcceptanceData, multiset model.Multiset) error {
|
pastUTXODiff model.UTXODiff, acceptanceData externalapi.AcceptanceData, multiset model.Multiset) error {
|
||||||
|
|
||||||
log.Tracef("verifyUTXO start for block %s", blockHash)
|
log.Tracef("verifyUTXO start for block %s", blockHash)
|
||||||
defer log.Tracef("verifyUTXO end for block %s", blockHash)
|
defer log.Tracef("verifyUTXO end for block %s", blockHash)
|
||||||
@ -97,7 +97,7 @@ func (csm *consensusStateManager) validateBlockTransactionsAgainstPastUTXO(block
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (csm *consensusStateManager) validateAcceptedIDMerkleRoot(block *externalapi.DomainBlock,
|
func (csm *consensusStateManager) validateAcceptedIDMerkleRoot(block *externalapi.DomainBlock,
|
||||||
blockHash *externalapi.DomainHash, acceptanceData model.AcceptanceData) error {
|
blockHash *externalapi.DomainHash, acceptanceData externalapi.AcceptanceData) error {
|
||||||
|
|
||||||
log.Tracef("validateAcceptedIDMerkleRoot start for block %s", blockHash)
|
log.Tracef("validateAcceptedIDMerkleRoot start for block %s", blockHash)
|
||||||
defer log.Tracef("validateAcceptedIDMerkleRoot end for block %s", blockHash)
|
defer log.Tracef("validateAcceptedIDMerkleRoot end for block %s", blockHash)
|
||||||
@ -127,7 +127,7 @@ func (csm *consensusStateManager) validateUTXOCommitment(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateAcceptedIDMerkleRoot(multiblockAcceptanceData model.AcceptanceData) *externalapi.DomainHash {
|
func calculateAcceptedIDMerkleRoot(multiblockAcceptanceData externalapi.AcceptanceData) *externalapi.DomainHash {
|
||||||
log.Tracef("calculateAcceptedIDMerkleRoot start")
|
log.Tracef("calculateAcceptedIDMerkleRoot start")
|
||||||
defer log.Tracef("calculateAcceptedIDMerkleRoot end")
|
defer log.Tracef("calculateAcceptedIDMerkleRoot end")
|
||||||
|
|
||||||
|
11
domain/utxoindex/log.go
Normal file
11
domain/utxoindex/log.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) 2016 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package utxoindex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log, _ = logger.Get(logger.SubsystemTags.INDX)
|
33
domain/utxoindex/model.go
Normal file
33
domain/utxoindex/model.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package utxoindex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ScriptPublicKeyString is a script public key represented as a string
|
||||||
|
// We use this type rather than just a byte slice because Go maps don't
|
||||||
|
// support slices as keys. See: UTXOChanges
|
||||||
|
type ScriptPublicKeyString string
|
||||||
|
|
||||||
|
// UTXOOutpointEntryPairs is a map between UTXO outpoints to UTXO entries
|
||||||
|
type UTXOOutpointEntryPairs map[externalapi.DomainOutpoint]externalapi.UTXOEntry
|
||||||
|
|
||||||
|
// UTXOOutpoints is a set of UTXO outpoints
|
||||||
|
type UTXOOutpoints map[externalapi.DomainOutpoint]interface{}
|
||||||
|
|
||||||
|
// UTXOChanges is the set of changes made to the UTXO index after
|
||||||
|
// a successful update
|
||||||
|
type UTXOChanges struct {
|
||||||
|
Added map[ScriptPublicKeyString]UTXOOutpointEntryPairs
|
||||||
|
Removed map[ScriptPublicKeyString]UTXOOutpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertScriptPublicKeyToString converts the given scriptPublicKey to a string
|
||||||
|
func ConvertScriptPublicKeyToString(scriptPublicKey []byte) ScriptPublicKeyString {
|
||||||
|
return ScriptPublicKeyString(scriptPublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertStringToScriptPublicKey converts the given string to a scriptPublicKey byte slice
|
||||||
|
func ConvertStringToScriptPublicKey(string ScriptPublicKeyString) []byte {
|
||||||
|
return []byte(string)
|
||||||
|
}
|
255
domain/utxoindex/store.go
Normal file
255
domain/utxoindex/store.go
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
package utxoindex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var utxoIndexBucket = database.MakeBucket([]byte("utxo-index"))
|
||||||
|
|
||||||
|
type utxoIndexStore struct {
|
||||||
|
database database.Database
|
||||||
|
toAdd map[ScriptPublicKeyString]UTXOOutpointEntryPairs
|
||||||
|
toRemove map[ScriptPublicKeyString]UTXOOutpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUTXOIndexStore(database database.Database) *utxoIndexStore {
|
||||||
|
return &utxoIndexStore{
|
||||||
|
database: database,
|
||||||
|
toAdd: make(map[ScriptPublicKeyString]UTXOOutpointEntryPairs),
|
||||||
|
toRemove: make(map[ScriptPublicKeyString]UTXOOutpoints),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) add(scriptPublicKey []byte, outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry) error {
|
||||||
|
key := ConvertScriptPublicKeyToString(scriptPublicKey)
|
||||||
|
log.Tracef("Adding outpoint %s:%d to scriptPublicKey %s",
|
||||||
|
outpoint.TransactionID, outpoint.Index, key)
|
||||||
|
|
||||||
|
// If the outpoint exists in `toRemove` simply remove it from there and return
|
||||||
|
if toRemoveOutpointsOfKey, ok := uis.toRemove[key]; ok {
|
||||||
|
if _, ok := toRemoveOutpointsOfKey[*outpoint]; ok {
|
||||||
|
log.Tracef("Outpoint %s:%d exists in `toRemove`. Deleting it from there",
|
||||||
|
outpoint.TransactionID, outpoint.Index)
|
||||||
|
delete(toRemoveOutpointsOfKey, *outpoint)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a UTXOOutpointEntryPairs entry in `toAdd` if it doesn't exist
|
||||||
|
if _, ok := uis.toAdd[key]; !ok {
|
||||||
|
log.Tracef("Creating key %s in `toAdd`", key)
|
||||||
|
uis.toAdd[key] = make(UTXOOutpointEntryPairs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an error if the outpoint already exists in `toAdd`
|
||||||
|
toAddPairsOfKey := uis.toAdd[key]
|
||||||
|
if _, ok := toAddPairsOfKey[*outpoint]; ok {
|
||||||
|
return errors.Errorf("cannot add outpoint %s because it's being added already", outpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
toAddPairsOfKey[*outpoint] = *utxoEntry
|
||||||
|
|
||||||
|
log.Tracef("Added outpoint %s:%d to scriptPublicKey %s",
|
||||||
|
outpoint.TransactionID, outpoint.Index, key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) remove(scriptPublicKey []byte, outpoint *externalapi.DomainOutpoint) error {
|
||||||
|
key := ConvertScriptPublicKeyToString(scriptPublicKey)
|
||||||
|
log.Tracef("Removing outpoint %s:%d from scriptPublicKey %s",
|
||||||
|
outpoint.TransactionID, outpoint.Index, key)
|
||||||
|
|
||||||
|
// If the outpoint exists in `toAdd` simply remove it from there and return
|
||||||
|
if toAddPairsOfKey, ok := uis.toAdd[key]; ok {
|
||||||
|
if _, ok := toAddPairsOfKey[*outpoint]; ok {
|
||||||
|
log.Tracef("Outpoint %s:%d exists in `toAdd`. Deleting it from there",
|
||||||
|
outpoint.TransactionID, outpoint.Index)
|
||||||
|
delete(toAddPairsOfKey, *outpoint)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a UTXOOutpoints entry in `toRemove` if it doesn't exist
|
||||||
|
if _, ok := uis.toRemove[key]; !ok {
|
||||||
|
log.Tracef("Creating key %s in `toRemove`", key)
|
||||||
|
uis.toRemove[key] = make(UTXOOutpoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an error if the outpoint already exists in `toRemove`
|
||||||
|
toRemoveOutpointsOfKey := uis.toRemove[key]
|
||||||
|
if _, ok := toRemoveOutpointsOfKey[*outpoint]; ok {
|
||||||
|
return errors.Errorf("cannot remove outpoint %s because it's being removed already", outpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
toRemoveOutpointsOfKey[*outpoint] = struct{}{}
|
||||||
|
|
||||||
|
log.Tracef("Removed outpoint %s:%d from scriptPublicKey %s",
|
||||||
|
outpoint.TransactionID, outpoint.Index, key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) discard() {
|
||||||
|
uis.toAdd = make(map[ScriptPublicKeyString]UTXOOutpointEntryPairs)
|
||||||
|
uis.toRemove = make(map[ScriptPublicKeyString]UTXOOutpoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) commit() error {
|
||||||
|
onEnd := logger.LogAndMeasureExecutionTime(log, "utxoIndexStore.commit")
|
||||||
|
defer onEnd()
|
||||||
|
|
||||||
|
dbTransaction, err := uis.database.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dbTransaction.RollbackUnlessClosed()
|
||||||
|
|
||||||
|
for scriptPublicKeyString, toRemoveOutpointsOfKey := range uis.toRemove {
|
||||||
|
scriptPublicKey := ConvertStringToScriptPublicKey(scriptPublicKeyString)
|
||||||
|
bucket := uis.bucketForScriptPublicKey(scriptPublicKey)
|
||||||
|
for outpointToRemove := range toRemoveOutpointsOfKey {
|
||||||
|
key, err := uis.convertOutpointToKey(bucket, &outpointToRemove)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = dbTransaction.Delete(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for scriptPublicKeyString, toAddUTXOOutpointEntryPairs := range uis.toAdd {
|
||||||
|
scriptPublicKey := ConvertStringToScriptPublicKey(scriptPublicKeyString)
|
||||||
|
bucket := uis.bucketForScriptPublicKey(scriptPublicKey)
|
||||||
|
for outpointToAdd, utxoEntryToAdd := range toAddUTXOOutpointEntryPairs {
|
||||||
|
key, err := uis.convertOutpointToKey(bucket, &outpointToAdd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
serializedUTXOEntry, err := uis.serializeUTXOEntry(utxoEntryToAdd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = dbTransaction.Put(key, serializedUTXOEntry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbTransaction.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
uis.discard()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) bucketForScriptPublicKey(scriptPublicKey []byte) *database.Bucket {
|
||||||
|
return utxoIndexBucket.Bucket(scriptPublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) convertOutpointToKey(bucket *database.Bucket, outpoint *externalapi.DomainOutpoint) (*database.Key, error) {
|
||||||
|
serializedOutpoint, err := uis.serializeOutpoint(outpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bucket.Key(serializedOutpoint), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) convertKeyToOutpoint(key *database.Key) (*externalapi.DomainOutpoint, error) {
|
||||||
|
serializedOutpoint := key.Suffix()
|
||||||
|
return uis.deserializeOutpoint(serializedOutpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) serializeOutpoint(outpoint *externalapi.DomainOutpoint) ([]byte, error) {
|
||||||
|
dbOutpoint := serialization.DomainOutpointToDbOutpoint(outpoint)
|
||||||
|
return proto.Marshal(dbOutpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) deserializeOutpoint(serializedOutpoint []byte) (*externalapi.DomainOutpoint, error) {
|
||||||
|
var dbOutpoint serialization.DbOutpoint
|
||||||
|
err := proto.Unmarshal(serializedOutpoint, &dbOutpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return serialization.DbOutpointToDomainOutpoint(&dbOutpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) serializeUTXOEntry(utxoEntry externalapi.UTXOEntry) ([]byte, error) {
|
||||||
|
dbUTXOEntry := serialization.UTXOEntryToDBUTXOEntry(utxoEntry)
|
||||||
|
return proto.Marshal(dbUTXOEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) deserializeUTXOEntry(serializedUTXOEntry []byte) (externalapi.UTXOEntry, error) {
|
||||||
|
var dbUTXOEntry serialization.DbUtxoEntry
|
||||||
|
err := proto.Unmarshal(serializedUTXOEntry, &dbUTXOEntry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return serialization.DBUTXOEntryToUTXOEntry(&dbUTXOEntry), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) stagedData() (
|
||||||
|
toAdd map[ScriptPublicKeyString]UTXOOutpointEntryPairs,
|
||||||
|
toRemove map[ScriptPublicKeyString]UTXOOutpoints) {
|
||||||
|
|
||||||
|
toAddClone := make(map[ScriptPublicKeyString]UTXOOutpointEntryPairs, len(uis.toAdd))
|
||||||
|
for scriptPublicKeyString, toAddUTXOOutpointEntryPairs := range uis.toAdd {
|
||||||
|
toAddUTXOOutpointEntryPairsClone := make(UTXOOutpointEntryPairs, len(toAddUTXOOutpointEntryPairs))
|
||||||
|
for outpoint, utxoEntry := range toAddUTXOOutpointEntryPairs {
|
||||||
|
toAddUTXOOutpointEntryPairsClone[outpoint] = utxoEntry
|
||||||
|
}
|
||||||
|
toAddClone[scriptPublicKeyString] = toAddUTXOOutpointEntryPairsClone
|
||||||
|
}
|
||||||
|
|
||||||
|
toRemoveClone := make(map[ScriptPublicKeyString]UTXOOutpoints, len(uis.toRemove))
|
||||||
|
for scriptPublicKeyString, toRemoveOutpoints := range uis.toRemove {
|
||||||
|
toRemoveOutpointsClone := make(UTXOOutpoints, len(toRemoveOutpoints))
|
||||||
|
for outpoint := range toRemoveOutpoints {
|
||||||
|
toRemoveOutpointsClone[outpoint] = struct{}{}
|
||||||
|
}
|
||||||
|
toRemoveClone[scriptPublicKeyString] = toRemoveOutpointsClone
|
||||||
|
}
|
||||||
|
|
||||||
|
return toAddClone, toRemoveClone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uis *utxoIndexStore) getUTXOOutpointEntryPairs(scriptPublicKey []byte) (UTXOOutpointEntryPairs, error) {
|
||||||
|
if len(uis.toAdd) > 0 || len(uis.toRemove) > 0 {
|
||||||
|
return nil, errors.Errorf("cannot get utxo outpoint entry pairs while staging isn't empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := uis.bucketForScriptPublicKey(scriptPublicKey)
|
||||||
|
cursor, err := uis.database.Cursor(bucket)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
utxoOutpointEntryPairs := make(UTXOOutpointEntryPairs)
|
||||||
|
for cursor.Next() {
|
||||||
|
key, err := cursor.Key()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
outpoint, err := uis.convertKeyToOutpoint(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
serializedUTXOEntry, err := cursor.Value()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
utxoEntry, err := uis.deserializeUTXOEntry(serializedUTXOEntry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
utxoOutpointEntryPairs[*outpoint] = utxoEntry
|
||||||
|
}
|
||||||
|
return utxoOutpointEntryPairs, nil
|
||||||
|
}
|
168
domain/utxoindex/utxoindex.go
Normal file
168
domain/utxoindex/utxoindex.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package utxoindex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/db/database"
|
||||||
|
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UTXOIndex maintains an index between transaction scriptPublicKeys
|
||||||
|
// and UTXOs
|
||||||
|
type UTXOIndex struct {
|
||||||
|
consensus externalapi.Consensus
|
||||||
|
store *utxoIndexStore
|
||||||
|
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new UTXO index
|
||||||
|
func New(consensus externalapi.Consensus, database database.Database) *UTXOIndex {
|
||||||
|
store := newUTXOIndexStore(database)
|
||||||
|
return &UTXOIndex{
|
||||||
|
consensus: consensus,
|
||||||
|
store: store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the UTXO index with the given DAG selected parent chain changes
|
||||||
|
func (ui *UTXOIndex) Update(chainChanges *externalapi.SelectedParentChainChanges) (*UTXOChanges, error) {
|
||||||
|
onEnd := logger.LogAndMeasureExecutionTime(log, "UTXOIndex.Update")
|
||||||
|
defer onEnd()
|
||||||
|
|
||||||
|
ui.mutex.Lock()
|
||||||
|
defer ui.mutex.Unlock()
|
||||||
|
|
||||||
|
log.Tracef("Updating UTXO index with chainChanges: %+v", chainChanges)
|
||||||
|
for _, removedBlockHash := range chainChanges.Removed {
|
||||||
|
err := ui.removeBlock(removedBlockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, addedBlockHash := range chainChanges.Added {
|
||||||
|
err := ui.addBlock(addedBlockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
added, removed := ui.store.stagedData()
|
||||||
|
utxoIndexChanges := &UTXOChanges{
|
||||||
|
Added: added,
|
||||||
|
Removed: removed,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ui.store.commit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Tracef("UTXO index updated with the UTXOChanged: %+v", utxoIndexChanges)
|
||||||
|
return utxoIndexChanges, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UTXOIndex) addBlock(blockHash *externalapi.DomainHash) error {
|
||||||
|
log.Tracef("Adding block %s to UTXO index", blockHash)
|
||||||
|
acceptanceData, err := ui.consensus.GetBlockAcceptanceData(blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
blockInfo, err := ui.consensus.GetBlockInfo(blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, blockAcceptanceData := range acceptanceData {
|
||||||
|
for _, transactionAcceptanceData := range blockAcceptanceData.TransactionAcceptanceData {
|
||||||
|
if !transactionAcceptanceData.IsAccepted {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := ui.addTransaction(transactionAcceptanceData.Transaction, blockInfo.BlueScore)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UTXOIndex) removeBlock(blockHash *externalapi.DomainHash) error {
|
||||||
|
log.Tracef("Removing block %s from UTXO index", blockHash)
|
||||||
|
acceptanceData, err := ui.consensus.GetBlockAcceptanceData(blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, blockAcceptanceData := range acceptanceData {
|
||||||
|
for _, transactionAcceptanceData := range blockAcceptanceData.TransactionAcceptanceData {
|
||||||
|
if !transactionAcceptanceData.IsAccepted {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := ui.removeTransaction(transactionAcceptanceData.Transaction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UTXOIndex) addTransaction(transaction *externalapi.DomainTransaction, blockBlueScore uint64) error {
|
||||||
|
transactionID := consensushashing.TransactionID(transaction)
|
||||||
|
log.Tracef("Adding transaction %s to UTXO index", transactionID)
|
||||||
|
|
||||||
|
isCoinbase := transactionhelper.IsCoinBase(transaction)
|
||||||
|
for _, transactionInput := range transaction.Inputs {
|
||||||
|
log.Tracef("Removing outpoint %s:%d from UTXO index",
|
||||||
|
transactionInput.PreviousOutpoint.TransactionID, transactionInput.PreviousOutpoint.Index)
|
||||||
|
err := ui.store.remove(transactionInput.UTXOEntry.ScriptPublicKey(), &transactionInput.PreviousOutpoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for index, transactionOutput := range transaction.Outputs {
|
||||||
|
log.Tracef("Adding outpoint %s:%d to UTXO index", transactionID, index)
|
||||||
|
outpoint := externalapi.NewDomainOutpoint(transactionID, uint32(index))
|
||||||
|
utxoEntry := utxo.NewUTXOEntry(transactionOutput.Value, transactionOutput.ScriptPublicKey, isCoinbase, blockBlueScore)
|
||||||
|
err := ui.store.add(transactionOutput.ScriptPublicKey, outpoint, &utxoEntry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UTXOIndex) removeTransaction(transaction *externalapi.DomainTransaction) error {
|
||||||
|
transactionID := consensushashing.TransactionID(transaction)
|
||||||
|
log.Tracef("Removing transaction %s from UTXO index", transactionID)
|
||||||
|
for index, transactionOutput := range transaction.Outputs {
|
||||||
|
log.Tracef("Removing outpoint %s:%d from UTXO index", transactionID, index)
|
||||||
|
outpoint := externalapi.NewDomainOutpoint(transactionID, uint32(index))
|
||||||
|
err := ui.store.remove(transactionOutput.ScriptPublicKey, outpoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, transactionInput := range transaction.Inputs {
|
||||||
|
log.Tracef("Adding outpoint %s:%d to UTXO index",
|
||||||
|
transactionInput.PreviousOutpoint.TransactionID, transactionInput.PreviousOutpoint.Index)
|
||||||
|
err := ui.store.add(transactionInput.UTXOEntry.ScriptPublicKey(), &transactionInput.PreviousOutpoint, &transactionInput.UTXOEntry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTXOs returns all the UTXOs for the given scriptPublicKey
|
||||||
|
func (ui *UTXOIndex) UTXOs(scriptPublicKey []byte) (UTXOOutpointEntryPairs, error) {
|
||||||
|
onEnd := logger.LogAndMeasureExecutionTime(log, "UTXOIndex.UTXOs")
|
||||||
|
defer onEnd()
|
||||||
|
|
||||||
|
ui.mutex.Lock()
|
||||||
|
defer ui.mutex.Unlock()
|
||||||
|
|
||||||
|
return ui.store.getUTXOOutpointEntryPairs(scriptPublicKey)
|
||||||
|
}
|
@ -123,6 +123,7 @@ type Flags struct {
|
|||||||
RejectNonStd bool `long:"rejectnonstd" description:"Reject non-standard transactions regardless of the default settings for the active network."`
|
RejectNonStd bool `long:"rejectnonstd" description:"Reject non-standard transactions regardless of the default settings for the active network."`
|
||||||
ResetDatabase bool `long:"reset-db" description:"Reset database before starting node. It's needed when switching between subnetworks."`
|
ResetDatabase bool `long:"reset-db" description:"Reset database before starting node. It's needed when switching between subnetworks."`
|
||||||
MaxUTXOCacheSize uint64 `long:"maxutxocachesize" description:"Max size of loaded UTXO into ram from the disk in bytes"`
|
MaxUTXOCacheSize uint64 `long:"maxutxocachesize" description:"Max size of loaded UTXO into ram from the disk in bytes"`
|
||||||
|
UTXOIndex bool `long:"utxoindex" description:"Enable the UTXO index"`
|
||||||
NetworkFlags
|
NetworkFlags
|
||||||
ServiceOptions *ServiceOptions
|
ServiceOptions *ServiceOptions
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -77,8 +77,18 @@ message KaspadMessage {
|
|||||||
GetMempoolEntriesResponseMessage getMempoolEntriesResponse = 1044;
|
GetMempoolEntriesResponseMessage getMempoolEntriesResponse = 1044;
|
||||||
ShutDownRequestMessage shutDownRequest = 1045;
|
ShutDownRequestMessage shutDownRequest = 1045;
|
||||||
ShutDownResponseMessage shutDownResponse = 1046;
|
ShutDownResponseMessage shutDownResponse = 1046;
|
||||||
GetHeadersRequestMessage getHeadersRequest = 10347;
|
GetHeadersRequestMessage getHeadersRequest = 1047;
|
||||||
GetHeadersResponseMessage getHeadersResponse = 1048;
|
GetHeadersResponseMessage getHeadersResponse = 1048;
|
||||||
|
NotifyUtxosChangedRequestMessage notifyUtxosChangedRequest = 1049;
|
||||||
|
NotifyUtxosChangedResponseMessage notifyUtxosChangedResponse = 1050;
|
||||||
|
UtxosChangedNotificationMessage utxosChangedNotification = 1051;
|
||||||
|
GetUtxosByAddressesRequestMessage getUtxosByAddressesRequest = 1052;
|
||||||
|
GetUtxosByAddressesResponseMessage getUtxosByAddressesResponse = 1053;
|
||||||
|
GetVirtualSelectedParentBlueScoreRequestMessage getVirtualSelectedParentBlueScoreRequest = 1054;
|
||||||
|
GetVirtualSelectedParentBlueScoreResponseMessage getVirtualSelectedParentBlueScoreResponse = 1055;
|
||||||
|
NotifyVirtualSelectedParentBlueScoreChangedRequestMessage notifyVirtualSelectedParentBlueScoreChangedRequest = 1056;
|
||||||
|
NotifyVirtualSelectedParentBlueScoreChangedResponseMessage notifyVirtualSelectedParentBlueScoreChangedResponse = 1057;
|
||||||
|
VirtualSelectedParentBlueScoreChangedNotificationMessage virtualSelectedParentBlueScoreChangedNotification = 1058;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,11 +422,12 @@ message AddPeerResponseMessage{
|
|||||||
}
|
}
|
||||||
|
|
||||||
message SubmitTransactionRequestMessage{
|
message SubmitTransactionRequestMessage{
|
||||||
TransactionMessage transaction = 1;
|
RpcTransaction transaction = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SubmitTransactionResponseMessage{
|
message SubmitTransactionResponseMessage{
|
||||||
string txId = 1;
|
string transactionId = 1;
|
||||||
|
|
||||||
RPCError error = 1000;
|
RPCError error = 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,6 +623,89 @@ message GetHeadersResponseMessage{
|
|||||||
RPCError error = 1000;
|
RPCError error = 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message NotifyUtxosChangedRequestMessage {
|
||||||
|
repeated string addresses = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NotifyUtxosChangedResponseMessage {
|
||||||
|
RPCError error = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UtxosChangedNotificationMessage {
|
||||||
|
repeated UtxosByAddressesEntry added = 1;
|
||||||
|
repeated UtxosByAddressesEntry removed = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UtxosByAddressesEntry {
|
||||||
|
string address = 1;
|
||||||
|
RpcOutpoint outpoint = 2;
|
||||||
|
RpcUtxoEntry utxoEntry = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RpcTransaction {
|
||||||
|
int32 version = 1;
|
||||||
|
repeated RpcTransactionInput inputs = 2;
|
||||||
|
repeated RpcTransactionOutput outputs = 3;
|
||||||
|
uint64 lockTime = 4;
|
||||||
|
string subnetworkId = 5;
|
||||||
|
uint64 gas = 6;
|
||||||
|
string payloadHash = 7;
|
||||||
|
string payload = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RpcTransactionInput {
|
||||||
|
RpcOutpoint previousOutpoint = 1;
|
||||||
|
string signatureScript = 2;
|
||||||
|
uint64 sequence = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RpcTransactionOutput {
|
||||||
|
uint64 amount = 1;
|
||||||
|
string scriptPubKey = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RpcOutpoint {
|
||||||
|
string transactionId = 1;
|
||||||
|
uint32 index = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RpcUtxoEntry {
|
||||||
|
uint64 amount = 1;
|
||||||
|
string scriptPubKey = 2;
|
||||||
|
uint64 blockBlueScore = 3;
|
||||||
|
bool isCoinbase = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetUtxosByAddressesRequestMessage {
|
||||||
|
repeated string addresses = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetUtxosByAddressesResponseMessage {
|
||||||
|
repeated UtxosByAddressesEntry entries = 1;
|
||||||
|
|
||||||
|
RPCError error = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetVirtualSelectedParentBlueScoreRequestMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetVirtualSelectedParentBlueScoreResponseMessage {
|
||||||
|
uint64 blueScore = 1;
|
||||||
|
|
||||||
|
RPCError error = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NotifyVirtualSelectedParentBlueScoreChangedRequestMessage {
|
||||||
|
}
|
||||||
|
|
||||||
|
message NotifyVirtualSelectedParentBlueScoreChangedResponseMessage {
|
||||||
|
RPCError error = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VirtualSelectedParentBlueScoreChangedNotificationMessage {
|
||||||
|
uint64 virtualSelectedParentBlueScore = 1;
|
||||||
|
}
|
||||||
|
|
||||||
service RPC {
|
service RPC {
|
||||||
rpc MessageStream (stream KaspadMessage) returns (stream KaspadMessage) {}
|
rpc MessageStream (stream KaspadMessage) returns (stream KaspadMessage) {}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package protowire
|
||||||
|
|
||||||
|
import "github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
|
||||||
|
func (x *KaspadMessage_GetUtxosByAddressesRequest) toAppMessage() (appmessage.Message, error) {
|
||||||
|
return &appmessage.GetUTXOsByAddressesRequestMessage{
|
||||||
|
Addresses: x.GetUtxosByAddressesRequest.Addresses,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_GetUtxosByAddressesRequest) fromAppMessage(message *appmessage.GetUTXOsByAddressesRequestMessage) error {
|
||||||
|
x.GetUtxosByAddressesRequest = &GetUtxosByAddressesRequestMessage{
|
||||||
|
Addresses: message.Addresses,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_GetUtxosByAddressesResponse) toAppMessage() (appmessage.Message, error) {
|
||||||
|
var err *appmessage.RPCError
|
||||||
|
if x.GetUtxosByAddressesResponse.Error != nil {
|
||||||
|
err = &appmessage.RPCError{Message: x.GetUtxosByAddressesResponse.Error.Message}
|
||||||
|
}
|
||||||
|
entries := make([]*appmessage.UTXOsByAddressesEntry, len(x.GetUtxosByAddressesResponse.Entries))
|
||||||
|
for i, entry := range x.GetUtxosByAddressesResponse.Entries {
|
||||||
|
entries[i] = entry.toAppMessage()
|
||||||
|
}
|
||||||
|
return &appmessage.GetUTXOsByAddressesResponseMessage{
|
||||||
|
Entries: entries,
|
||||||
|
Error: err,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_GetUtxosByAddressesResponse) fromAppMessage(message *appmessage.GetUTXOsByAddressesResponseMessage) error {
|
||||||
|
var err *RPCError
|
||||||
|
if message.Error != nil {
|
||||||
|
err = &RPCError{Message: message.Error.Message}
|
||||||
|
}
|
||||||
|
entries := make([]*UtxosByAddressesEntry, len(message.Entries))
|
||||||
|
for i, entry := range message.Entries {
|
||||||
|
entries[i] = &UtxosByAddressesEntry{}
|
||||||
|
entries[i].fromAppMessage(entry)
|
||||||
|
}
|
||||||
|
x.GetUtxosByAddressesResponse = &GetUtxosByAddressesResponseMessage{
|
||||||
|
Entries: entries,
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package protowire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x *KaspadMessage_GetVirtualSelectedParentBlueScoreRequest) toAppMessage() (appmessage.Message, error) {
|
||||||
|
return &appmessage.GetVirtualSelectedParentBlueScoreRequestMessage{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_GetVirtualSelectedParentBlueScoreRequest) fromAppMessage(message *appmessage.GetVirtualSelectedParentBlueScoreRequestMessage) error {
|
||||||
|
x.GetVirtualSelectedParentBlueScoreRequest = &GetVirtualSelectedParentBlueScoreRequestMessage{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_GetVirtualSelectedParentBlueScoreResponse) toAppMessage() (appmessage.Message, error) {
|
||||||
|
var err *appmessage.RPCError
|
||||||
|
if x.GetVirtualSelectedParentBlueScoreResponse.Error != nil {
|
||||||
|
err = &appmessage.RPCError{Message: x.GetVirtualSelectedParentBlueScoreResponse.Error.Message}
|
||||||
|
}
|
||||||
|
return &appmessage.GetVirtualSelectedParentBlueScoreResponseMessage{
|
||||||
|
BlueScore: x.GetVirtualSelectedParentBlueScoreResponse.BlueScore,
|
||||||
|
Error: err,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_GetVirtualSelectedParentBlueScoreResponse) fromAppMessage(message *appmessage.GetVirtualSelectedParentBlueScoreResponseMessage) error {
|
||||||
|
var err *RPCError
|
||||||
|
if message.Error != nil {
|
||||||
|
err = &RPCError{Message: message.Error.Message}
|
||||||
|
}
|
||||||
|
x.GetVirtualSelectedParentBlueScoreResponse = &GetVirtualSelectedParentBlueScoreResponseMessage{
|
||||||
|
BlueScore: message.BlueScore,
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
package protowire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x *KaspadMessage_NotifyUtxosChangedRequest) toAppMessage() (appmessage.Message, error) {
|
||||||
|
return &appmessage.NotifyUTXOsChangedRequestMessage{
|
||||||
|
Addresses: x.NotifyUtxosChangedRequest.Addresses,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_NotifyUtxosChangedRequest) fromAppMessage(message *appmessage.NotifyUTXOsChangedRequestMessage) error {
|
||||||
|
x.NotifyUtxosChangedRequest = &NotifyUtxosChangedRequestMessage{
|
||||||
|
Addresses: message.Addresses,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_NotifyUtxosChangedResponse) toAppMessage() (appmessage.Message, error) {
|
||||||
|
var err *appmessage.RPCError
|
||||||
|
if x.NotifyUtxosChangedResponse.Error != nil {
|
||||||
|
err = &appmessage.RPCError{Message: x.NotifyUtxosChangedResponse.Error.Message}
|
||||||
|
}
|
||||||
|
return &appmessage.NotifyUTXOsChangedResponseMessage{
|
||||||
|
Error: err,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_NotifyUtxosChangedResponse) fromAppMessage(message *appmessage.NotifyUTXOsChangedResponseMessage) error {
|
||||||
|
var err *RPCError
|
||||||
|
if message.Error != nil {
|
||||||
|
err = &RPCError{Message: message.Error.Message}
|
||||||
|
}
|
||||||
|
x.NotifyUtxosChangedResponse = &NotifyUtxosChangedResponseMessage{
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_UtxosChangedNotification) toAppMessage() (appmessage.Message, error) {
|
||||||
|
added := make([]*appmessage.UTXOsByAddressesEntry, len(x.UtxosChangedNotification.Added))
|
||||||
|
for i, entry := range x.UtxosChangedNotification.Added {
|
||||||
|
added[i] = entry.toAppMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
removed := make([]*appmessage.UTXOsByAddressesEntry, len(x.UtxosChangedNotification.Removed))
|
||||||
|
for i, entry := range x.UtxosChangedNotification.Removed {
|
||||||
|
removed[i] = entry.toAppMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &appmessage.UTXOsChangedNotificationMessage{
|
||||||
|
Added: added,
|
||||||
|
Removed: removed,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_UtxosChangedNotification) fromAppMessage(message *appmessage.UTXOsChangedNotificationMessage) error {
|
||||||
|
added := make([]*UtxosByAddressesEntry, len(message.Added))
|
||||||
|
for i, entry := range message.Added {
|
||||||
|
added[i] = &UtxosByAddressesEntry{}
|
||||||
|
added[i].fromAppMessage(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
removed := make([]*UtxosByAddressesEntry, len(message.Removed))
|
||||||
|
for i, entry := range message.Removed {
|
||||||
|
removed[i] = &UtxosByAddressesEntry{}
|
||||||
|
removed[i].fromAppMessage(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
x.UtxosChangedNotification = &UtxosChangedNotificationMessage{
|
||||||
|
Added: added,
|
||||||
|
Removed: removed,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UtxosByAddressesEntry) toAppMessage() *appmessage.UTXOsByAddressesEntry {
|
||||||
|
outpoint := &appmessage.RPCOutpoint{
|
||||||
|
TransactionID: x.Outpoint.TransactionId,
|
||||||
|
Index: x.Outpoint.Index,
|
||||||
|
}
|
||||||
|
var utxoEntry *appmessage.RPCUTXOEntry
|
||||||
|
if x.UtxoEntry != nil {
|
||||||
|
utxoEntry = &appmessage.RPCUTXOEntry{
|
||||||
|
Amount: x.UtxoEntry.Amount,
|
||||||
|
ScriptPubKey: x.UtxoEntry.ScriptPubKey,
|
||||||
|
BlockBlueScore: x.UtxoEntry.BlockBlueScore,
|
||||||
|
IsCoinbase: x.UtxoEntry.IsCoinbase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &appmessage.UTXOsByAddressesEntry{
|
||||||
|
Address: x.Address,
|
||||||
|
Outpoint: outpoint,
|
||||||
|
UTXOEntry: utxoEntry,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UtxosByAddressesEntry) fromAppMessage(entry *appmessage.UTXOsByAddressesEntry) {
|
||||||
|
outpoint := &RpcOutpoint{
|
||||||
|
TransactionId: entry.Outpoint.TransactionID,
|
||||||
|
Index: entry.Outpoint.Index,
|
||||||
|
}
|
||||||
|
var utxoEntry *RpcUtxoEntry
|
||||||
|
if entry.UTXOEntry != nil {
|
||||||
|
utxoEntry = &RpcUtxoEntry{
|
||||||
|
Amount: entry.UTXOEntry.Amount,
|
||||||
|
ScriptPubKey: entry.UTXOEntry.ScriptPubKey,
|
||||||
|
BlockBlueScore: entry.UTXOEntry.BlockBlueScore,
|
||||||
|
IsCoinbase: entry.UTXOEntry.IsCoinbase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*x = UtxosByAddressesEntry{
|
||||||
|
Address: entry.Address,
|
||||||
|
Outpoint: outpoint,
|
||||||
|
UtxoEntry: utxoEntry,
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package protowire
|
||||||
|
|
||||||
|
import "github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
|
||||||
|
func (x *KaspadMessage_NotifyVirtualSelectedParentBlueScoreChangedRequest) toAppMessage() (appmessage.Message, error) {
|
||||||
|
return &appmessage.NotifyVirtualSelectedParentBlueScoreChangedRequestMessage{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_NotifyVirtualSelectedParentBlueScoreChangedRequest) fromAppMessage(_ *appmessage.NotifyVirtualSelectedParentBlueScoreChangedRequestMessage) error {
|
||||||
|
x.NotifyVirtualSelectedParentBlueScoreChangedRequest = &NotifyVirtualSelectedParentBlueScoreChangedRequestMessage{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_NotifyVirtualSelectedParentBlueScoreChangedResponse) toAppMessage() (appmessage.Message, error) {
|
||||||
|
var err *appmessage.RPCError
|
||||||
|
if x.NotifyVirtualSelectedParentBlueScoreChangedResponse.Error != nil {
|
||||||
|
err = &appmessage.RPCError{Message: x.NotifyVirtualSelectedParentBlueScoreChangedResponse.Error.Message}
|
||||||
|
}
|
||||||
|
return &appmessage.NotifyVirtualSelectedParentBlueScoreChangedResponseMessage{
|
||||||
|
Error: err,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_NotifyVirtualSelectedParentBlueScoreChangedResponse) fromAppMessage(message *appmessage.NotifyVirtualSelectedParentBlueScoreChangedResponseMessage) error {
|
||||||
|
var err *RPCError
|
||||||
|
if message.Error != nil {
|
||||||
|
err = &RPCError{Message: message.Error.Message}
|
||||||
|
}
|
||||||
|
x.NotifyVirtualSelectedParentBlueScoreChangedResponse = &NotifyVirtualSelectedParentBlueScoreChangedResponseMessage{
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_VirtualSelectedParentBlueScoreChangedNotification) toAppMessage() (appmessage.Message, error) {
|
||||||
|
return &appmessage.VirtualSelectedParentBlueScoreChangedNotificationMessage{
|
||||||
|
VirtualSelectedParentBlueScore: x.VirtualSelectedParentBlueScoreChangedNotification.VirtualSelectedParentBlueScore,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *KaspadMessage_VirtualSelectedParentBlueScoreChangedNotification) fromAppMessage(message *appmessage.VirtualSelectedParentBlueScoreChangedNotificationMessage) error {
|
||||||
|
x.VirtualSelectedParentBlueScoreChangedNotification = &VirtualSelectedParentBlueScoreChangedNotificationMessage{
|
||||||
|
VirtualSelectedParentBlueScore: message.VirtualSelectedParentBlueScore,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,20 +1,19 @@
|
|||||||
package protowire
|
package protowire
|
||||||
|
|
||||||
import "github.com/kaspanet/kaspad/app/appmessage"
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
)
|
||||||
|
|
||||||
func (x *KaspadMessage_SubmitTransactionRequest) toAppMessage() (appmessage.Message, error) {
|
func (x *KaspadMessage_SubmitTransactionRequest) toAppMessage() (appmessage.Message, error) {
|
||||||
msgTx, err := x.SubmitTransactionRequest.Transaction.toAppMessage()
|
rpcTransaction := x.SubmitTransactionRequest.Transaction.toAppMessage()
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &appmessage.SubmitTransactionRequestMessage{
|
return &appmessage.SubmitTransactionRequestMessage{
|
||||||
Transaction: msgTx.(*appmessage.MsgTx),
|
Transaction: rpcTransaction,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *KaspadMessage_SubmitTransactionRequest) fromAppMessage(message *appmessage.SubmitTransactionRequestMessage) error {
|
func (x *KaspadMessage_SubmitTransactionRequest) fromAppMessage(message *appmessage.SubmitTransactionRequestMessage) error {
|
||||||
x.SubmitTransactionRequest = &SubmitTransactionRequestMessage{
|
x.SubmitTransactionRequest = &SubmitTransactionRequestMessage{
|
||||||
Transaction: &TransactionMessage{},
|
Transaction: &RpcTransaction{},
|
||||||
}
|
}
|
||||||
x.SubmitTransactionRequest.Transaction.fromAppMessage(message.Transaction)
|
x.SubmitTransactionRequest.Transaction.fromAppMessage(message.Transaction)
|
||||||
return nil
|
return nil
|
||||||
@ -26,8 +25,8 @@ func (x *KaspadMessage_SubmitTransactionResponse) toAppMessage() (appmessage.Mes
|
|||||||
err = &appmessage.RPCError{Message: x.SubmitTransactionResponse.Error.Message}
|
err = &appmessage.RPCError{Message: x.SubmitTransactionResponse.Error.Message}
|
||||||
}
|
}
|
||||||
return &appmessage.SubmitTransactionResponseMessage{
|
return &appmessage.SubmitTransactionResponseMessage{
|
||||||
TxID: x.SubmitTransactionResponse.TxId,
|
TransactionID: x.SubmitTransactionResponse.TransactionId,
|
||||||
Error: err,
|
Error: err,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,8 +36,72 @@ func (x *KaspadMessage_SubmitTransactionResponse) fromAppMessage(message *appmes
|
|||||||
err = &RPCError{Message: message.Error.Message}
|
err = &RPCError{Message: message.Error.Message}
|
||||||
}
|
}
|
||||||
x.SubmitTransactionResponse = &SubmitTransactionResponseMessage{
|
x.SubmitTransactionResponse = &SubmitTransactionResponseMessage{
|
||||||
TxId: message.TxID,
|
TransactionId: message.TransactionID,
|
||||||
Error: err,
|
Error: err,
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *RpcTransaction) toAppMessage() *appmessage.RPCTransaction {
|
||||||
|
inputs := make([]*appmessage.RPCTransactionInput, len(x.Inputs))
|
||||||
|
for i, input := range x.Inputs {
|
||||||
|
previousOutpoint := &appmessage.RPCOutpoint{
|
||||||
|
TransactionID: input.PreviousOutpoint.TransactionId,
|
||||||
|
Index: input.PreviousOutpoint.Index,
|
||||||
|
}
|
||||||
|
inputs[i] = &appmessage.RPCTransactionInput{
|
||||||
|
PreviousOutpoint: previousOutpoint,
|
||||||
|
SignatureScript: input.SignatureScript,
|
||||||
|
Sequence: input.Sequence,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputs := make([]*appmessage.RPCTransactionOutput, len(x.Outputs))
|
||||||
|
for i, output := range x.Outputs {
|
||||||
|
outputs[i] = &appmessage.RPCTransactionOutput{
|
||||||
|
Amount: output.Amount,
|
||||||
|
ScriptPubKey: output.ScriptPubKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &appmessage.RPCTransaction{
|
||||||
|
Version: x.Version,
|
||||||
|
Inputs: inputs,
|
||||||
|
Outputs: outputs,
|
||||||
|
LockTime: x.LockTime,
|
||||||
|
SubnetworkID: x.SubnetworkId,
|
||||||
|
Gas: x.Gas,
|
||||||
|
PayloadHash: x.PayloadHash,
|
||||||
|
Payload: x.Payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RpcTransaction) fromAppMessage(transaction *appmessage.RPCTransaction) {
|
||||||
|
inputs := make([]*RpcTransactionInput, len(transaction.Inputs))
|
||||||
|
for i, input := range transaction.Inputs {
|
||||||
|
previousOutpoint := &RpcOutpoint{
|
||||||
|
TransactionId: input.PreviousOutpoint.TransactionID,
|
||||||
|
Index: input.PreviousOutpoint.Index,
|
||||||
|
}
|
||||||
|
inputs[i] = &RpcTransactionInput{
|
||||||
|
PreviousOutpoint: previousOutpoint,
|
||||||
|
SignatureScript: input.SignatureScript,
|
||||||
|
Sequence: input.Sequence,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputs := make([]*RpcTransactionOutput, len(transaction.Outputs))
|
||||||
|
for i, output := range transaction.Outputs {
|
||||||
|
outputs[i] = &RpcTransactionOutput{
|
||||||
|
Amount: output.Amount,
|
||||||
|
ScriptPubKey: output.ScriptPubKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*x = RpcTransaction{
|
||||||
|
Version: transaction.Version,
|
||||||
|
Inputs: inputs,
|
||||||
|
Outputs: outputs,
|
||||||
|
LockTime: transaction.LockTime,
|
||||||
|
SubnetworkId: transaction.SubnetworkID,
|
||||||
|
Gas: transaction.Gas,
|
||||||
|
PayloadHash: transaction.PayloadHash,
|
||||||
|
Payload: transaction.Payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -575,6 +575,76 @@ func toRPCPayload(message appmessage.Message) (isKaspadMessage_Payload, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return payload, nil
|
return payload, nil
|
||||||
|
case *appmessage.NotifyUTXOsChangedRequestMessage:
|
||||||
|
payload := new(KaspadMessage_NotifyUtxosChangedRequest)
|
||||||
|
err := payload.fromAppMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return payload, nil
|
||||||
|
case *appmessage.NotifyUTXOsChangedResponseMessage:
|
||||||
|
payload := new(KaspadMessage_NotifyUtxosChangedResponse)
|
||||||
|
err := payload.fromAppMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return payload, nil
|
||||||
|
case *appmessage.UTXOsChangedNotificationMessage:
|
||||||
|
payload := new(KaspadMessage_UtxosChangedNotification)
|
||||||
|
err := payload.fromAppMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return payload, nil
|
||||||
|
case *appmessage.GetUTXOsByAddressesRequestMessage:
|
||||||
|
payload := new(KaspadMessage_GetUtxosByAddressesRequest)
|
||||||
|
err := payload.fromAppMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return payload, nil
|
||||||
|
case *appmessage.GetUTXOsByAddressesResponseMessage:
|
||||||
|
payload := new(KaspadMessage_GetUtxosByAddressesResponse)
|
||||||
|
err := payload.fromAppMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return payload, nil
|
||||||
|
case *appmessage.GetVirtualSelectedParentBlueScoreRequestMessage:
|
||||||
|
payload := new(KaspadMessage_GetVirtualSelectedParentBlueScoreRequest)
|
||||||
|
err := payload.fromAppMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return payload, nil
|
||||||
|
case *appmessage.GetVirtualSelectedParentBlueScoreResponseMessage:
|
||||||
|
payload := new(KaspadMessage_GetVirtualSelectedParentBlueScoreResponse)
|
||||||
|
err := payload.fromAppMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return payload, nil
|
||||||
|
case *appmessage.NotifyVirtualSelectedParentBlueScoreChangedRequestMessage:
|
||||||
|
payload := new(KaspadMessage_NotifyVirtualSelectedParentBlueScoreChangedRequest)
|
||||||
|
err := payload.fromAppMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return payload, nil
|
||||||
|
case *appmessage.NotifyVirtualSelectedParentBlueScoreChangedResponseMessage:
|
||||||
|
payload := new(KaspadMessage_NotifyVirtualSelectedParentBlueScoreChangedResponse)
|
||||||
|
err := payload.fromAppMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return payload, nil
|
||||||
|
case *appmessage.VirtualSelectedParentBlueScoreChangedNotificationMessage:
|
||||||
|
payload := new(KaspadMessage_VirtualSelectedParentBlueScoreChangedNotification)
|
||||||
|
err := payload.fromAppMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return payload, nil
|
||||||
default:
|
default:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package rpcclient
|
||||||
|
|
||||||
|
import "github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
|
||||||
|
// GetUTXOsByAddresses sends an RPC request respective to the function's name and returns the RPC server's response
|
||||||
|
func (c *RPCClient) GetUTXOsByAddresses(addresses []string) (*appmessage.GetUTXOsByAddressesResponseMessage, error) {
|
||||||
|
err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewGetUTXOsByAddressesRequestMessage(addresses))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response, err := c.route(appmessage.CmdGetUTXOsByAddressesResponseMessage).DequeueWithTimeout(c.timeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
getUTXOsByAddressesResponse := response.(*appmessage.GetUTXOsByAddressesResponseMessage)
|
||||||
|
if getUTXOsByAddressesResponse.Error != nil {
|
||||||
|
return nil, c.convertRPCError(getUTXOsByAddressesResponse.Error)
|
||||||
|
}
|
||||||
|
return getUTXOsByAddressesResponse, nil
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package rpcclient
|
||||||
|
|
||||||
|
import "github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
|
||||||
|
// GetVirtualSelectedParentBlueScore sends an RPC request respective to the function's name and returns the RPC server's response
|
||||||
|
func (c *RPCClient) GetVirtualSelectedParentBlueScore() (*appmessage.GetVirtualSelectedParentBlueScoreResponseMessage, error) {
|
||||||
|
err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewGetVirtualSelectedParentBlueScoreRequestMessage())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response, err := c.route(appmessage.CmdGetVirtualSelectedParentBlueScoreResponseMessage).DequeueWithTimeout(c.timeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
getVirtualSelectedParentBlueScoreResponse := response.(*appmessage.GetVirtualSelectedParentBlueScoreResponseMessage)
|
||||||
|
if getVirtualSelectedParentBlueScoreResponse.Error != nil {
|
||||||
|
return nil, c.convertRPCError(getVirtualSelectedParentBlueScoreResponse.Error)
|
||||||
|
}
|
||||||
|
return getVirtualSelectedParentBlueScoreResponse, nil
|
||||||
|
}
|
40
infrastructure/network/rpcclient/rpc_on_utxos_changed.go
Normal file
40
infrastructure/network/rpcclient/rpc_on_utxos_changed.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package rpcclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterForUTXOsChangedNotifications sends an RPC request respective to the function's name and returns the RPC server's response.
|
||||||
|
// Additionally, it starts listening for the appropriate notification using the given handler function
|
||||||
|
func (c *RPCClient) RegisterForUTXOsChangedNotifications(addresses []string,
|
||||||
|
onUTXOsChanged func(notification *appmessage.UTXOsChangedNotificationMessage)) error {
|
||||||
|
|
||||||
|
err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewNotifyUTXOsChangedRequestMessage(addresses))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
response, err := c.route(appmessage.CmdNotifyUTXOsChangedResponseMessage).DequeueWithTimeout(c.timeout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
notifyUTXOsChangedResponse := response.(*appmessage.NotifyUTXOsChangedResponseMessage)
|
||||||
|
if notifyUTXOsChangedResponse.Error != nil {
|
||||||
|
return c.convertRPCError(notifyUTXOsChangedResponse.Error)
|
||||||
|
}
|
||||||
|
spawn("RegisterForUTXOsChangedNotifications", func() {
|
||||||
|
for {
|
||||||
|
notification, err := c.route(appmessage.CmdUTXOsChangedNotificationMessage).Dequeue()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, routerpkg.ErrRouteClosed) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
UTXOsChangedNotification := notification.(*appmessage.UTXOsChangedNotificationMessage)
|
||||||
|
onUTXOsChanged(UTXOsChangedNotification)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package rpcclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterForVirtualSelectedParentBlueScoreChangedNotifications sends an RPC request respective to the function's
|
||||||
|
// name and returns the RPC server's response. Additionally, it starts listening for the appropriate notification
|
||||||
|
// using the given handler function
|
||||||
|
func (c *RPCClient) RegisterForVirtualSelectedParentBlueScoreChangedNotifications(
|
||||||
|
onVirtualSelectedParentBlueScoreChanged func(notification *appmessage.VirtualSelectedParentBlueScoreChangedNotificationMessage)) error {
|
||||||
|
|
||||||
|
err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewNotifyVirtualSelectedParentBlueScoreChangedRequestMessage())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
response, err := c.route(appmessage.CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage).DequeueWithTimeout(c.timeout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
notifyVirtualSelectedParentBlueScoreChangedResponse := response.(*appmessage.NotifyVirtualSelectedParentBlueScoreChangedResponseMessage)
|
||||||
|
if notifyVirtualSelectedParentBlueScoreChangedResponse.Error != nil {
|
||||||
|
return c.convertRPCError(notifyVirtualSelectedParentBlueScoreChangedResponse.Error)
|
||||||
|
}
|
||||||
|
spawn("RegisterForVirtualSelectedParentBlueScoreChangedNotifications", func() {
|
||||||
|
for {
|
||||||
|
notification, err := c.route(appmessage.CmdVirtualSelectedParentBlueScoreChangedNotificationMessage).Dequeue()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, routerpkg.ErrRouteClosed) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
VirtualSelectedParentBlueScoreChangedNotification := notification.(*appmessage.VirtualSelectedParentBlueScoreChangedNotificationMessage)
|
||||||
|
onVirtualSelectedParentBlueScoreChanged(VirtualSelectedParentBlueScoreChangedNotification)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
@ -5,8 +5,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// SubmitTransaction sends an RPC request respective to the function's name and returns the RPC server's response
|
// SubmitTransaction sends an RPC request respective to the function's name and returns the RPC server's response
|
||||||
func (c *RPCClient) SubmitTransaction(msgTx *appmessage.MsgTx) (*appmessage.SubmitTransactionResponseMessage, error) {
|
func (c *RPCClient) SubmitTransaction(transaction *appmessage.RPCTransaction) (*appmessage.SubmitTransactionResponseMessage, error) {
|
||||||
err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewSubmitTransactionRequestMessage(msgTx))
|
err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewSubmitTransactionRequestMessage(transaction))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ func setConfig(t *testing.T, harness *appHarness) {
|
|||||||
harness.config.DataDir = randomDirectory(t)
|
harness.config.DataDir = randomDirectory(t)
|
||||||
harness.config.Listeners = []string{harness.p2pAddress}
|
harness.config.Listeners = []string{harness.p2pAddress}
|
||||||
harness.config.RPCListeners = []string{harness.rpcAddress}
|
harness.config.RPCListeners = []string{harness.rpcAddress}
|
||||||
|
harness.config.UTXOIndex = harness.utxoIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
func commonConfig() *config.Config {
|
func commonConfig() *config.Config {
|
||||||
|
@ -21,6 +21,7 @@ type appHarness struct {
|
|||||||
miningAddressPrivateKey string
|
miningAddressPrivateKey string
|
||||||
config *config.Config
|
config *config.Config
|
||||||
database database.Database
|
database database.Database
|
||||||
|
utxoIndex bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type harnessParams struct {
|
type harnessParams struct {
|
||||||
@ -28,6 +29,7 @@ type harnessParams struct {
|
|||||||
rpcAddress string
|
rpcAddress string
|
||||||
miningAddress string
|
miningAddress string
|
||||||
miningAddressPrivateKey string
|
miningAddressPrivateKey string
|
||||||
|
utxoIndex bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupHarness creates a single appHarness with given parameters
|
// setupHarness creates a single appHarness with given parameters
|
||||||
@ -37,6 +39,7 @@ func setupHarness(t *testing.T, params *harnessParams) (harness *appHarness, tea
|
|||||||
rpcAddress: params.rpcAddress,
|
rpcAddress: params.rpcAddress,
|
||||||
miningAddress: params.miningAddress,
|
miningAddress: params.miningAddress,
|
||||||
miningAddressPrivateKey: params.miningAddressPrivateKey,
|
miningAddressPrivateKey: params.miningAddressPrivateKey,
|
||||||
|
utxoIndex: params.utxoIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(t, harness)
|
setConfig(t, harness)
|
||||||
|
@ -42,12 +42,14 @@ func TestTxRelay(t *testing.T) {
|
|||||||
waitForPayeeToReceiveBlock(t, payeeBlockAddedChan)
|
waitForPayeeToReceiveBlock(t, payeeBlockAddedChan)
|
||||||
}
|
}
|
||||||
|
|
||||||
tx := generateTx(t, secondBlock.Transactions[transactionhelper.CoinbaseTransactionIndex], payer, payee)
|
msgTx := generateTx(t, secondBlock.Transactions[transactionhelper.CoinbaseTransactionIndex], payer, payee)
|
||||||
response, err := payer.rpcClient.SubmitTransaction(tx)
|
domainTransaction := appmessage.MsgTxToDomainTransaction(msgTx)
|
||||||
|
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(domainTransaction)
|
||||||
|
response, err := payer.rpcClient.SubmitTransaction(rpcTransaction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error submitting transaction: %+v", err)
|
t.Fatalf("Error submitting transaction: %+v", err)
|
||||||
}
|
}
|
||||||
txID := response.TxID
|
txID := response.TransactionID
|
||||||
|
|
||||||
txAddedToMempoolChan := make(chan struct{})
|
txAddedToMempoolChan := make(chan struct{})
|
||||||
|
|
||||||
|
192
testing/integration/utxo_index_test.go
Normal file
192
testing/integration/utxo_index_test.go
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"github.com/kaspanet/go-secp256k1"
|
||||||
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
|
||||||
|
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
||||||
|
"github.com/kaspanet/kaspad/util"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUTXOIndex(t *testing.T) {
|
||||||
|
// Setup a single kaspad instance
|
||||||
|
harnessParams := &harnessParams{
|
||||||
|
p2pAddress: p2pAddress1,
|
||||||
|
rpcAddress: rpcAddress1,
|
||||||
|
miningAddress: miningAddress1,
|
||||||
|
miningAddressPrivateKey: miningAddress1PrivateKey,
|
||||||
|
utxoIndex: true,
|
||||||
|
}
|
||||||
|
kaspad, teardown := setupHarness(t, harnessParams)
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
// skip the first block because it's paying to genesis script,
|
||||||
|
// which contains no outputs
|
||||||
|
mineNextBlock(t, kaspad)
|
||||||
|
|
||||||
|
// Register for UTXO changes
|
||||||
|
const blockAmountToMine = 100
|
||||||
|
onUTXOsChangedChan := make(chan *appmessage.UTXOsChangedNotificationMessage, blockAmountToMine)
|
||||||
|
err := kaspad.rpcClient.RegisterForUTXOsChangedNotifications([]string{miningAddress1}, func(
|
||||||
|
notification *appmessage.UTXOsChangedNotificationMessage) {
|
||||||
|
|
||||||
|
onUTXOsChangedChan <- notification
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to register for UTXO change notifications: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mine some blocks
|
||||||
|
for i := 0; i < blockAmountToMine; i++ {
|
||||||
|
mineNextBlock(t, kaspad)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect the UTXO and make sure there's nothing in Removed
|
||||||
|
// Note that we expect blockAmountToMine-1 messages because
|
||||||
|
// the last block won't be accepted until the next block is
|
||||||
|
// mined
|
||||||
|
var notificationEntries []*appmessage.UTXOsByAddressesEntry
|
||||||
|
for i := 0; i < blockAmountToMine-1; i++ {
|
||||||
|
notification := <-onUTXOsChangedChan
|
||||||
|
if len(notification.Removed) > 0 {
|
||||||
|
t.Fatalf("Unexpectedly received that a UTXO has been removed")
|
||||||
|
}
|
||||||
|
for _, added := range notification.Added {
|
||||||
|
notificationEntries = append(notificationEntries, added)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit a few transactions that spends some UTXOs
|
||||||
|
const transactionAmountToSpend = 5
|
||||||
|
for i := 0; i < transactionAmountToSpend; i++ {
|
||||||
|
rpcTransaction := buildTransactionForUTXOIndexTest(t, notificationEntries[i])
|
||||||
|
_, err = kaspad.rpcClient.SubmitTransaction(rpcTransaction)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error submitting transaction: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mine a block to include the above transaction
|
||||||
|
mineNextBlock(t, kaspad)
|
||||||
|
notification := <-onUTXOsChangedChan
|
||||||
|
if len(notification.Removed) > 0 {
|
||||||
|
t.Fatalf("Unexpectedly received that a UTXO has been removed")
|
||||||
|
}
|
||||||
|
for _, added := range notification.Added {
|
||||||
|
notificationEntries = append(notificationEntries, added)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mine another block to accept the above block
|
||||||
|
mineNextBlock(t, kaspad)
|
||||||
|
|
||||||
|
// Make sure this block removed the UTXOs we spent
|
||||||
|
notification = <-onUTXOsChangedChan
|
||||||
|
if len(notification.Removed) != transactionAmountToSpend {
|
||||||
|
t.Fatalf("Unexpected amount of removed UTXOs. Want: %d, got: %d",
|
||||||
|
transactionAmountToSpend, len(notification.Removed))
|
||||||
|
}
|
||||||
|
for i := 0; i < transactionAmountToSpend; i++ {
|
||||||
|
entry := notificationEntries[i]
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, removed := range notification.Removed {
|
||||||
|
if *removed.Outpoint == *entry.Outpoint {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Fatalf("Missing entry amongst removed UTXOs: %s:%d",
|
||||||
|
entry.Outpoint.TransactionID, entry.Outpoint.Index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, added := range notification.Added {
|
||||||
|
notificationEntries = append(notificationEntries, added)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the UTXOs we spent from `notificationEntries`
|
||||||
|
notificationEntries = notificationEntries[transactionAmountToSpend:]
|
||||||
|
|
||||||
|
// Get all the UTXOs and make sure the response is equivalent
|
||||||
|
// to the data collected via notifications
|
||||||
|
utxosByAddressesResponse, err := kaspad.rpcClient.GetUTXOsByAddresses([]string{miningAddress1})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get UTXOs: %s", err)
|
||||||
|
}
|
||||||
|
if len(notificationEntries) != len(utxosByAddressesResponse.Entries) {
|
||||||
|
t.Fatalf("Unexpected amount of UTXOs. Want: %d, got: %d",
|
||||||
|
len(notificationEntries), len(utxosByAddressesResponse.Entries))
|
||||||
|
}
|
||||||
|
for _, notificationEntry := range notificationEntries {
|
||||||
|
var foundResponseEntry *appmessage.UTXOsByAddressesEntry
|
||||||
|
for _, responseEntry := range utxosByAddressesResponse.Entries {
|
||||||
|
if *notificationEntry.Outpoint == *responseEntry.Outpoint {
|
||||||
|
foundResponseEntry = responseEntry
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundResponseEntry == nil {
|
||||||
|
t.Fatalf("Missing entry in UTXOs response: %s:%d",
|
||||||
|
notificationEntry.Outpoint.TransactionID, notificationEntry.Outpoint.Index)
|
||||||
|
}
|
||||||
|
if *notificationEntry.UTXOEntry != *foundResponseEntry.UTXOEntry {
|
||||||
|
t.Fatalf("Unexpected UTXOEntry for outpoint %s:%d. Want: %+v, got: %+v",
|
||||||
|
notificationEntry.Outpoint.TransactionID, notificationEntry.Outpoint.Index,
|
||||||
|
notificationEntry.UTXOEntry, foundResponseEntry.UTXOEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildTransactionForUTXOIndexTest(t *testing.T, entry *appmessage.UTXOsByAddressesEntry) *appmessage.RPCTransaction {
|
||||||
|
transactionIDBytes, err := hex.DecodeString(entry.Outpoint.TransactionID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error decoding transaction ID: %s", err)
|
||||||
|
}
|
||||||
|
transactionID, err := transactionid.FromBytes(transactionIDBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error decoding transaction ID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
txIns := make([]*appmessage.TxIn, 1)
|
||||||
|
txIns[0] = appmessage.NewTxIn(appmessage.NewOutpoint(transactionID, entry.Outpoint.Index), []byte{})
|
||||||
|
|
||||||
|
payeeAddress, err := util.DecodeAddress(miningAddress1, util.Bech32PrefixKaspaSim)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error decoding payeeAddress: %+v", err)
|
||||||
|
}
|
||||||
|
toScript, err := txscript.PayToAddrScript(payeeAddress)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error generating script: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
txOuts := []*appmessage.TxOut{appmessage.NewTxOut(entry.UTXOEntry.Amount-1000, toScript)}
|
||||||
|
|
||||||
|
fromScript, err := hex.DecodeString(entry.UTXOEntry.ScriptPubKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error decoding script public key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgTx := appmessage.NewNativeMsgTx(constants.TransactionVersion, txIns, txOuts)
|
||||||
|
|
||||||
|
privateKeyBytes, err := hex.DecodeString(miningAddress1PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error decoding private key: %+v", err)
|
||||||
|
}
|
||||||
|
privateKey, err := secp256k1.DeserializePrivateKeyFromSlice(privateKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error deserializing private key: %+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
signatureScript, err := txscript.SignatureScript(appmessage.MsgTxToDomainTransaction(msgTx), 0,
|
||||||
|
fromScript, txscript.SigHashAll, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error signing transaction: %+v", err)
|
||||||
|
}
|
||||||
|
msgTx.TxIn[0].SignatureScript = signatureScript
|
||||||
|
|
||||||
|
domainTransaction := appmessage.MsgTxToDomainTransaction(msgTx)
|
||||||
|
return appmessage.DomainTransactionToRPCTransaction(domainTransaction)
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/app/appmessage"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVirtualSelectedParentBlueScore(t *testing.T) {
|
||||||
|
// Setup a single kaspad instance
|
||||||
|
harnessParams := &harnessParams{
|
||||||
|
p2pAddress: p2pAddress1,
|
||||||
|
rpcAddress: rpcAddress1,
|
||||||
|
miningAddress: miningAddress1,
|
||||||
|
miningAddressPrivateKey: miningAddress1PrivateKey,
|
||||||
|
utxoIndex: true,
|
||||||
|
}
|
||||||
|
kaspad, teardown := setupHarness(t, harnessParams)
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
// Make sure that the initial blue score is 1
|
||||||
|
response, err := kaspad.rpcClient.GetVirtualSelectedParentBlueScore()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error getting virtual selected parent blue score: %s", err)
|
||||||
|
}
|
||||||
|
if response.BlueScore != 1 {
|
||||||
|
t.Fatalf("Unexpected virtual selected parent blue score. Want: %d, got: %d",
|
||||||
|
1, response.BlueScore)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register to virtual selected parent blue score changes
|
||||||
|
onVirtualSelectedParentBlueScoreChangedChan := make(chan *appmessage.VirtualSelectedParentBlueScoreChangedNotificationMessage)
|
||||||
|
err = kaspad.rpcClient.RegisterForVirtualSelectedParentBlueScoreChangedNotifications(
|
||||||
|
func(notification *appmessage.VirtualSelectedParentBlueScoreChangedNotificationMessage) {
|
||||||
|
onVirtualSelectedParentBlueScoreChangedChan <- notification
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to register for virtual selected parent "+
|
||||||
|
"blue score change notifications: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mine some blocks and make sure that the notifications
|
||||||
|
// report correct blue score values
|
||||||
|
const blockAmountToMine = 100
|
||||||
|
for i := 0; i < blockAmountToMine; i++ {
|
||||||
|
mineNextBlock(t, kaspad)
|
||||||
|
notification := <-onVirtualSelectedParentBlueScoreChangedChan
|
||||||
|
if notification.VirtualSelectedParentBlueScore != 2+uint64(i) {
|
||||||
|
t.Fatalf("Unexpected virtual selected parent blue score. Want: %d, got: %d",
|
||||||
|
2+uint64(i), notification.VirtualSelectedParentBlueScore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that the blue score after all that mining is as expected
|
||||||
|
response, err = kaspad.rpcClient.GetVirtualSelectedParentBlueScore()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error getting virtual selected parent blue score: %s", err)
|
||||||
|
}
|
||||||
|
if response.BlueScore != 1+blockAmountToMine {
|
||||||
|
t.Fatalf("Unexpected virtual selected parent blue score. Want: %d, got: %d",
|
||||||
|
1+blockAmountToMine, response.BlueScore)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user