diff --git a/app/app.go b/app/app.go index 89ba4adb2..60ec5a8b3 100644 --- a/app/app.go +++ b/app/app.go @@ -10,6 +10,7 @@ import ( "github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/app/protocol" + "github.com/kaspanet/kaspad/app/rpc" "github.com/kaspanet/kaspad/domain/blockdag" "github.com/kaspanet/kaspad/domain/blockdag/indexers" "github.com/kaspanet/kaspad/domain/mempool" @@ -20,8 +21,6 @@ import ( "github.com/kaspanet/kaspad/infrastructure/network/connmanager" "github.com/kaspanet/kaspad/infrastructure/network/dnsseed" "github.com/kaspanet/kaspad/infrastructure/network/netadapter" - "github.com/kaspanet/kaspad/infrastructure/network/rpc" - "github.com/kaspanet/kaspad/infrastructure/os/signal" "github.com/kaspanet/kaspad/util" "github.com/kaspanet/kaspad/util/panics" ) @@ -29,9 +28,9 @@ import ( // App is a wrapper for all the kaspad services type App struct { cfg *config.Config - rpcServer *rpc.Server addressManager *addressmanager.AddressManager protocolManager *protocol.Manager + rpcManager *rpc.Manager connectionManager *connmanager.ConnectionManager netAdapter *netadapter.NetAdapter @@ -47,18 +46,14 @@ func (a *App) Start() { log.Trace("Starting kaspad") - err := a.protocolManager.Start() + err := a.netAdapter.Start() if err != nil { - panics.Exit(log, fmt.Sprintf("Error starting the p2p protocol: %+v", err)) + panics.Exit(log, fmt.Sprintf("Error starting the net adapter: %+v", err)) } a.maybeSeedFromDNS() a.connectionManager.Start() - - if !a.cfg.DisableRPC { - a.rpcServer.Start() - } } // Stop gracefully shuts down all the kaspad services. @@ -73,17 +68,9 @@ func (a *App) Stop() { a.connectionManager.Stop() - err := a.protocolManager.Stop() + err := a.netAdapter.Stop() if err != nil { - log.Errorf("Error stopping the p2p protocol: %+v", err) - } - - // Shutdown the RPC server if it's not disabled. - if !a.cfg.DisableRPC { - err := a.rpcServer.Stop() - if err != nil { - log.Errorf("Error stopping rpcServer: %+v", err) - } + log.Errorf("Error stopping the net adapter: %+v", err) } err = a.addressManager.Stop() @@ -126,22 +113,56 @@ func New(cfg *config.Config, databaseContext *dbaccess.DatabaseContext, interrup if err != nil { return nil, err } - rpcServer, err := setupRPC( - cfg, dag, txMempool, sigCache, acceptanceIndex, connectionManager, addressManager, protocolManager) - if err != nil { - return nil, err - } + rpcManager := setupRPC(cfg, txMempool, dag, sigCache, netAdapter, protocolManager, connectionManager, addressManager, acceptanceIndex) return &App{ cfg: cfg, - rpcServer: rpcServer, protocolManager: protocolManager, + rpcManager: rpcManager, connectionManager: connectionManager, netAdapter: netAdapter, addressManager: addressManager, }, nil } +func setupRPC( + cfg *config.Config, + txMempool *mempool.TxPool, + dag *blockdag.BlockDAG, + sigCache *txscript.SigCache, + netAdapter *netadapter.NetAdapter, + protocolManager *protocol.Manager, + connectionManager *connmanager.ConnectionManager, + addressManager *addressmanager.AddressManager, + acceptanceIndex *indexers.AcceptanceIndex) *rpc.Manager { + + blockTemplateGenerator := mining.NewBlkTmplGenerator(&mining.Policy{BlockMaxMass: cfg.BlockMaxMass}, txMempool, dag, sigCache) + rpcManager := rpc.NewManager(cfg, netAdapter, dag, protocolManager, connectionManager, blockTemplateGenerator, txMempool, addressManager, acceptanceIndex) + protocolManager.SetOnBlockAddedToDAGHandler(rpcManager.NotifyBlockAddedToDAG) + protocolManager.SetOnTransactionAddedToMempoolHandler(rpcManager.NotifyTransactionAddedToMempool) + dag.Subscribe(func(notification *blockdag.Notification) { + err := handleBlockDAGNotifications(notification, acceptanceIndex, rpcManager) + if err != nil { + panic(err) + } + }) + return rpcManager +} + +func handleBlockDAGNotifications(notification *blockdag.Notification, + acceptanceIndex *indexers.AcceptanceIndex, rpcManager *rpc.Manager) error { + + if notification.Type == blockdag.NTChainChanged && acceptanceIndex != nil { + chainChangedNotificationData := notification.Data.(*blockdag.ChainChangedNotificationData) + err := rpcManager.NotifyChainChanged(chainChangedNotificationData.RemovedChainBlockHashes, + chainChangedNotificationData.AddedChainBlockHashes) + if err != nil { + return err + } + } + return nil +} + func (a *App) maybeSeedFromDNS() { if !a.cfg.DisableDNSSeed { dnsseed.SeedFromDNS(a.cfg.NetParams(), a.cfg.DNSSeed, appmessage.SFNodeNetwork, false, nil, @@ -205,38 +226,6 @@ func setupMempool(cfg *config.Config, dag *blockdag.BlockDAG, sigCache *txscript return mempool.New(&mempoolConfig) } -func setupRPC(cfg *config.Config, - dag *blockdag.BlockDAG, - txMempool *mempool.TxPool, - sigCache *txscript.SigCache, - acceptanceIndex *indexers.AcceptanceIndex, - connectionManager *connmanager.ConnectionManager, - addressManager *addressmanager.AddressManager, - protocolManager *protocol.Manager) (*rpc.Server, error) { - - if !cfg.DisableRPC { - policy := mining.Policy{ - BlockMaxMass: cfg.BlockMaxMass, - } - blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy, txMempool, dag, sigCache) - - rpcServer, err := rpc.NewRPCServer(cfg, dag, txMempool, acceptanceIndex, blockTemplateGenerator, - connectionManager, addressManager, protocolManager) - if err != nil { - return nil, err - } - - // Signal process shutdown when the RPC server requests it. - spawn("setupRPC-handleShutdownRequest", func() { - <-rpcServer.RequestedProcessShutdown() - signal.ShutdownRequestChannel <- struct{}{} - }) - - return rpcServer, nil - } - return nil, nil -} - // P2PNodeID returns the network ID associated with this App func (a *App) P2PNodeID() *id.ID { return a.netAdapter.ID() diff --git a/app/appmessage/error.go b/app/appmessage/error.go index 134fef0c7..e6d13f074 100644 --- a/app/appmessage/error.go +++ b/app/appmessage/error.go @@ -32,3 +32,16 @@ func (e *MessageError) Error() string { func messageError(f string, desc string) *MessageError { return &MessageError{Func: f, Description: desc} } + +// RPCError represents an error arriving from the RPC +type RPCError struct { + Message string +} + +// RPCErrorf formats according to a format specifier and returns the string +// as an RPCError. +func RPCErrorf(format string, args ...interface{}) *RPCError { + return &RPCError{ + Message: fmt.Sprintf(format, args...), + } +} diff --git a/app/appmessage/fakemessage_test.go b/app/appmessage/fakemessage_test.go deleted file mode 100644 index cbf783f96..000000000 --- a/app/appmessage/fakemessage_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package appmessage - -import "io" - -// fakeMessage implements the Message interface and is used to force encode -// errors in messages. -type fakeMessage struct { - command MessageCommand - payload []byte - forceEncodeErr bool - forceLenErr bool -} - -// KaspaDecode doesn't do anything. It just satisfies the appmessage.Message -// interface. -func (msg *fakeMessage) KaspaDecode(r io.Reader, pver uint32) error { - return nil -} - -// KaspaEncode writes the payload field of the fake message or forces an error -// if the forceEncodeErr flag of the fake message is set. It also satisfies the -// appmessage.Message interface. -func (msg *fakeMessage) KaspaEncode(w io.Writer, pver uint32) error { - if msg.forceEncodeErr { - err := &MessageError{ - Func: "fakeMessage.KaspaEncode", - Description: "intentional error", - } - return err - } - - _, err := w.Write(msg.payload) - return err -} - -// Command returns the command field of the fake message and satisfies the -// Message interface. -func (msg *fakeMessage) Command() MessageCommand { - return msg.command -} - -// MaxPayloadLength returns the length of the payload field of fake message -// or a smaller value if the forceLenErr flag of the fake message is set. It -// satisfies the Message interface. -func (msg *fakeMessage) MaxPayloadLength(pver uint32) uint32 { - lenp := uint32(len(msg.payload)) - if msg.forceLenErr { - return lenp - 1 - } - - return lenp -} diff --git a/app/appmessage/message.go b/app/appmessage/message.go index 1009e61a3..e13f291ac 100644 --- a/app/appmessage/message.go +++ b/app/appmessage/message.go @@ -17,7 +17,10 @@ const MaxMessagePayload = (1024 * 1024 * 32) // 32MB type MessageCommand uint32 func (cmd MessageCommand) String() string { - cmdString, ok := MessageCommandToString[cmd] + cmdString, ok := ProtocolMessageCommandToString[cmd] + if !ok { + cmdString, ok = RPCMessageCommandToString[cmd] + } if !ok { cmdString = "unknown command" } @@ -26,6 +29,7 @@ func (cmd MessageCommand) String() string { // Commands used in kaspa message headers which describe the type of message. const ( + // protocol CmdVersion MessageCommand = iota CmdVerAck CmdRequestAddresses @@ -48,10 +52,45 @@ const ( CmdDoneIBDBlocks CmdTransactionNotFound CmdReject + + // rpc + CmdGetCurrentNetworkRequestMessage + CmdGetCurrentNetworkResponseMessage + CmdSubmitBlockRequestMessage + CmdSubmitBlockResponseMessage + CmdGetBlockTemplateRequestMessage + CmdGetBlockTemplateResponseMessage + CmdGetBlockTemplateTransactionMessage + CmdNotifyBlockAddedRequestMessage + CmdNotifyBlockAddedResponseMessage + CmdBlockAddedNotificationMessage + CmdGetPeerAddressesRequestMessage + CmdGetPeerAddressesResponseMessage + CmdGetSelectedTipHashRequestMessage + CmdGetSelectedTipHashResponseMessage + CmdGetMempoolEntryRequestMessage + CmdGetMempoolEntryResponseMessage + CmdGetConnectedPeerInfoRequestMessage + CmdGetConnectedPeerInfoResponseMessage + CmdAddPeerRequestMessage + CmdAddPeerResponseMessage + CmdSubmitTransactionRequestMessage + CmdSubmitTransactionResponseMessage + CmdNotifyChainChangedRequestMessage + CmdNotifyChainChangedResponseMessage + CmdChainChangedNotificationMessage + CmdGetBlockRequestMessage + CmdGetBlockResponseMessage + CmdGetSubnetworkRequestMessage + CmdGetSubnetworkResponseMessage + CmdGetChainFromBlockRequestMessage + CmdGetChainFromBlockResponseMessage + CmdGetBlocksRequestMessage + CmdGetBlocksResponseMessage ) -// MessageCommandToString maps all MessageCommands to their string representation -var MessageCommandToString = map[MessageCommand]string{ +// ProtocolMessageCommandToString maps all MessageCommands to their string representation +var ProtocolMessageCommandToString = map[MessageCommand]string{ CmdVersion: "Version", CmdVerAck: "VerAck", CmdRequestAddresses: "RequestAddresses", @@ -76,6 +115,43 @@ var MessageCommandToString = map[MessageCommand]string{ CmdReject: "Reject", } +// RPCMessageCommandToString maps all MessageCommands to their string representation +var RPCMessageCommandToString = map[MessageCommand]string{ + CmdGetCurrentNetworkRequestMessage: "GetCurrentNetworkRequest", + CmdGetCurrentNetworkResponseMessage: "GetCurrentNetworkResponse", + CmdSubmitBlockRequestMessage: "SubmitBlockRequest", + CmdSubmitBlockResponseMessage: "SubmitBlockResponse", + CmdGetBlockTemplateRequestMessage: "GetBlockTemplateRequest", + CmdGetBlockTemplateResponseMessage: "GetBlockTemplateResponse", + CmdGetBlockTemplateTransactionMessage: "CmdGetBlockTemplateTransaction", + CmdNotifyBlockAddedRequestMessage: "NotifyBlockAddedRequest", + CmdNotifyBlockAddedResponseMessage: "NotifyBlockAddedResponse", + CmdBlockAddedNotificationMessage: "BlockAddedNotification", + CmdGetPeerAddressesRequestMessage: "GetPeerAddressesRequest", + CmdGetPeerAddressesResponseMessage: "GetPeerAddressesResponse", + CmdGetSelectedTipHashRequestMessage: "GetSelectedTipHashRequest", + CmdGetSelectedTipHashResponseMessage: "GetSelectedTipHashResponse", + CmdGetMempoolEntryRequestMessage: "GetMempoolEntryRequest", + CmdGetMempoolEntryResponseMessage: "GetMempoolEntryResponse", + CmdGetConnectedPeerInfoRequestMessage: "GetConnectedPeerInfoRequest", + CmdGetConnectedPeerInfoResponseMessage: "GetConnectedPeerInfoResponse", + CmdAddPeerRequestMessage: "AddPeerRequest", + CmdAddPeerResponseMessage: "AddPeerResponse", + CmdSubmitTransactionRequestMessage: "SubmitTransactionRequest", + CmdSubmitTransactionResponseMessage: "SubmitTransactionResponse", + CmdNotifyChainChangedRequestMessage: "NotifyChainChangedRequest", + CmdNotifyChainChangedResponseMessage: "NotifyChainChangedResponse", + CmdChainChangedNotificationMessage: "ChainChangedNotification", + CmdGetBlockRequestMessage: "GetBlockRequest", + CmdGetBlockResponseMessage: "GetBlockResponse", + CmdGetSubnetworkRequestMessage: "GetSubnetworkRequest", + CmdGetSubnetworkResponseMessage: "GetSubnetworkResponse", + CmdGetChainFromBlockRequestMessage: "GetChainFromBlockRequest", + CmdGetChainFromBlockResponseMessage: "GetChainFromBlockResponse", + CmdGetBlocksRequestMessage: "GetBlocksRequest", + CmdGetBlocksResponseMessage: "GetBlocksResponse", +} + // Message is an interface that describes a kaspa message. A type that // implements Message has complete control over the representation of its data // and may therefore contain additional or fewer fields than those which diff --git a/app/appmessage/blockheader.go b/app/appmessage/p2p_blockheader.go similarity index 100% rename from app/appmessage/blockheader.go rename to app/appmessage/p2p_blockheader.go diff --git a/app/appmessage/blockheader_test.go b/app/appmessage/p2p_blockheader_test.go similarity index 100% rename from app/appmessage/blockheader_test.go rename to app/appmessage/p2p_blockheader_test.go diff --git a/app/appmessage/msgaddresses.go b/app/appmessage/p2p_msgaddresses.go similarity index 100% rename from app/appmessage/msgaddresses.go rename to app/appmessage/p2p_msgaddresses.go diff --git a/app/appmessage/msgaddresses_test.go b/app/appmessage/p2p_msgaddresses_test.go similarity index 100% rename from app/appmessage/msgaddresses_test.go rename to app/appmessage/p2p_msgaddresses_test.go diff --git a/app/appmessage/msgblock.go b/app/appmessage/p2p_msgblock.go similarity index 100% rename from app/appmessage/msgblock.go rename to app/appmessage/p2p_msgblock.go diff --git a/app/appmessage/msgblock_test.go b/app/appmessage/p2p_msgblock_test.go similarity index 100% rename from app/appmessage/msgblock_test.go rename to app/appmessage/p2p_msgblock_test.go diff --git a/app/appmessage/msgblocklocator.go b/app/appmessage/p2p_msgblocklocator.go similarity index 100% rename from app/appmessage/msgblocklocator.go rename to app/appmessage/p2p_msgblocklocator.go diff --git a/app/appmessage/msgblocklocator_test.go b/app/appmessage/p2p_msgblocklocator_test.go similarity index 100% rename from app/appmessage/msgblocklocator_test.go rename to app/appmessage/p2p_msgblocklocator_test.go diff --git a/app/appmessage/msgdoneibdblocks.go b/app/appmessage/p2p_msgdoneibdblocks.go similarity index 100% rename from app/appmessage/msgdoneibdblocks.go rename to app/appmessage/p2p_msgdoneibdblocks.go diff --git a/app/appmessage/msgibdblock.go b/app/appmessage/p2p_msgibdblock.go similarity index 100% rename from app/appmessage/msgibdblock.go rename to app/appmessage/p2p_msgibdblock.go diff --git a/app/appmessage/msgibdblock_test.go b/app/appmessage/p2p_msgibdblock_test.go similarity index 100% rename from app/appmessage/msgibdblock_test.go rename to app/appmessage/p2p_msgibdblock_test.go diff --git a/app/appmessage/msginvrelayblock.go b/app/appmessage/p2p_msginvrelayblock.go similarity index 100% rename from app/appmessage/msginvrelayblock.go rename to app/appmessage/p2p_msginvrelayblock.go diff --git a/app/appmessage/msginvtransaction.go b/app/appmessage/p2p_msginvtransaction.go similarity index 100% rename from app/appmessage/msginvtransaction.go rename to app/appmessage/p2p_msginvtransaction.go diff --git a/app/appmessage/msgping.go b/app/appmessage/p2p_msgping.go similarity index 100% rename from app/appmessage/msgping.go rename to app/appmessage/p2p_msgping.go diff --git a/app/appmessage/msgping_test.go b/app/appmessage/p2p_msgping_test.go similarity index 100% rename from app/appmessage/msgping_test.go rename to app/appmessage/p2p_msgping_test.go diff --git a/app/appmessage/msgpong.go b/app/appmessage/p2p_msgpong.go similarity index 100% rename from app/appmessage/msgpong.go rename to app/appmessage/p2p_msgpong.go diff --git a/app/appmessage/msgpong_test.go b/app/appmessage/p2p_msgpong_test.go similarity index 100% rename from app/appmessage/msgpong_test.go rename to app/appmessage/p2p_msgpong_test.go diff --git a/app/appmessage/msgreject.go b/app/appmessage/p2p_msgreject.go similarity index 100% rename from app/appmessage/msgreject.go rename to app/appmessage/p2p_msgreject.go diff --git a/app/appmessage/msgrequestaddresses.go b/app/appmessage/p2p_msgrequestaddresses.go similarity index 100% rename from app/appmessage/msgrequestaddresses.go rename to app/appmessage/p2p_msgrequestaddresses.go diff --git a/app/appmessage/msgrequestaddresses_test.go b/app/appmessage/p2p_msgrequestaddresses_test.go similarity index 100% rename from app/appmessage/msgrequestaddresses_test.go rename to app/appmessage/p2p_msgrequestaddresses_test.go diff --git a/app/appmessage/msgrequestblocklocator.go b/app/appmessage/p2p_msgrequestblocklocator.go similarity index 100% rename from app/appmessage/msgrequestblocklocator.go rename to app/appmessage/p2p_msgrequestblocklocator.go diff --git a/app/appmessage/msgrequestblocklocator_test.go b/app/appmessage/p2p_msgrequestblocklocator_test.go similarity index 100% rename from app/appmessage/msgrequestblocklocator_test.go rename to app/appmessage/p2p_msgrequestblocklocator_test.go diff --git a/app/appmessage/msgrequestibdblocks.go b/app/appmessage/p2p_msgrequestibdblocks.go similarity index 100% rename from app/appmessage/msgrequestibdblocks.go rename to app/appmessage/p2p_msgrequestibdblocks.go diff --git a/app/appmessage/msgrequestibdblocks_test.go b/app/appmessage/p2p_msgrequestibdblocks_test.go similarity index 100% rename from app/appmessage/msgrequestibdblocks_test.go rename to app/appmessage/p2p_msgrequestibdblocks_test.go diff --git a/app/appmessage/msgrequestnextibdblocks.go b/app/appmessage/p2p_msgrequestnextibdblocks.go similarity index 100% rename from app/appmessage/msgrequestnextibdblocks.go rename to app/appmessage/p2p_msgrequestnextibdblocks.go diff --git a/app/appmessage/msgrequestrelayblocks.go b/app/appmessage/p2p_msgrequestrelayblocks.go similarity index 100% rename from app/appmessage/msgrequestrelayblocks.go rename to app/appmessage/p2p_msgrequestrelayblocks.go diff --git a/app/appmessage/msgrequestselectedtip.go b/app/appmessage/p2p_msgrequestselectedtip.go similarity index 100% rename from app/appmessage/msgrequestselectedtip.go rename to app/appmessage/p2p_msgrequestselectedtip.go diff --git a/app/appmessage/msgrequestselectedtip_test.go b/app/appmessage/p2p_msgrequestselectedtip_test.go similarity index 100% rename from app/appmessage/msgrequestselectedtip_test.go rename to app/appmessage/p2p_msgrequestselectedtip_test.go diff --git a/app/appmessage/msgrequesttransactions.go b/app/appmessage/p2p_msgrequesttransactions.go similarity index 100% rename from app/appmessage/msgrequesttransactions.go rename to app/appmessage/p2p_msgrequesttransactions.go diff --git a/app/appmessage/msgselectedtip.go b/app/appmessage/p2p_msgselectedtip.go similarity index 100% rename from app/appmessage/msgselectedtip.go rename to app/appmessage/p2p_msgselectedtip.go diff --git a/app/appmessage/msgselectedtip_test.go b/app/appmessage/p2p_msgselectedtip_test.go similarity index 100% rename from app/appmessage/msgselectedtip_test.go rename to app/appmessage/p2p_msgselectedtip_test.go diff --git a/app/appmessage/msgtransactionnotfound.go b/app/appmessage/p2p_msgtransactionnotfound.go similarity index 100% rename from app/appmessage/msgtransactionnotfound.go rename to app/appmessage/p2p_msgtransactionnotfound.go diff --git a/app/appmessage/msgtx.go b/app/appmessage/p2p_msgtx.go similarity index 100% rename from app/appmessage/msgtx.go rename to app/appmessage/p2p_msgtx.go diff --git a/app/appmessage/msgtx_test.go b/app/appmessage/p2p_msgtx_test.go similarity index 100% rename from app/appmessage/msgtx_test.go rename to app/appmessage/p2p_msgtx_test.go diff --git a/app/appmessage/msgverack.go b/app/appmessage/p2p_msgverack.go similarity index 100% rename from app/appmessage/msgverack.go rename to app/appmessage/p2p_msgverack.go diff --git a/app/appmessage/msgverack_test.go b/app/appmessage/p2p_msgverack_test.go similarity index 100% rename from app/appmessage/msgverack_test.go rename to app/appmessage/p2p_msgverack_test.go diff --git a/app/appmessage/msgversion.go b/app/appmessage/p2p_msgversion.go similarity index 100% rename from app/appmessage/msgversion.go rename to app/appmessage/p2p_msgversion.go diff --git a/app/appmessage/msgversion_test.go b/app/appmessage/p2p_msgversion_test.go similarity index 100% rename from app/appmessage/msgversion_test.go rename to app/appmessage/p2p_msgversion_test.go diff --git a/app/appmessage/netaddress.go b/app/appmessage/p2p_netaddress.go similarity index 100% rename from app/appmessage/netaddress.go rename to app/appmessage/p2p_netaddress.go diff --git a/app/appmessage/netaddress_test.go b/app/appmessage/p2p_netaddress_test.go similarity index 100% rename from app/appmessage/netaddress_test.go rename to app/appmessage/p2p_netaddress_test.go diff --git a/app/appmessage/rpc_add_peer.go b/app/appmessage/rpc_add_peer.go new file mode 100644 index 000000000..7d6d7334c --- /dev/null +++ b/app/appmessage/rpc_add_peer.go @@ -0,0 +1,39 @@ +package appmessage + +// AddPeerRequestMessage is an appmessage corresponding to +// its respective RPC message +type AddPeerRequestMessage struct { + baseMessage + Address string + IsPermanent bool +} + +// Command returns the protocol command string for the message +func (msg *AddPeerRequestMessage) Command() MessageCommand { + return CmdAddPeerRequestMessage +} + +// NewAddPeerRequestMessage returns a instance of the message +func NewAddPeerRequestMessage(address string, isPermanent bool) *AddPeerRequestMessage { + return &AddPeerRequestMessage{ + Address: address, + IsPermanent: isPermanent, + } +} + +// AddPeerResponseMessage is an appmessage corresponding to +// its respective RPC message +type AddPeerResponseMessage struct { + baseMessage + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *AddPeerResponseMessage) Command() MessageCommand { + return CmdAddPeerResponseMessage +} + +// NewAddPeerResponseMessage returns a instance of the message +func NewAddPeerResponseMessage() *AddPeerResponseMessage { + return &AddPeerResponseMessage{} +} diff --git a/app/appmessage/rpc_get_block.go b/app/appmessage/rpc_get_block.go new file mode 100644 index 000000000..7d7775fe4 --- /dev/null +++ b/app/appmessage/rpc_get_block.go @@ -0,0 +1,123 @@ +package appmessage + +// GetBlockRequestMessage is an appmessage corresponding to +// its respective RPC message +type GetBlockRequestMessage struct { + baseMessage + Hash string + SubnetworkID string + IncludeBlockHex bool + IncludeBlockVerboseData bool + IncludeTransactionVerboseData bool +} + +// Command returns the protocol command string for the message +func (msg *GetBlockRequestMessage) Command() MessageCommand { + return CmdGetBlockRequestMessage +} + +// NewGetBlockRequestMessage returns a instance of the message +func NewGetBlockRequestMessage(hash string, subnetworkID string, includeBlockHex bool, + includeBlockVerboseData bool, includeTransactionVerboseData bool) *GetBlockRequestMessage { + return &GetBlockRequestMessage{ + Hash: hash, + SubnetworkID: subnetworkID, + IncludeBlockHex: includeBlockHex, + IncludeBlockVerboseData: includeBlockVerboseData, + IncludeTransactionVerboseData: includeTransactionVerboseData, + } +} + +// GetBlockResponseMessage is an appmessage corresponding to +// its respective RPC message +type GetBlockResponseMessage struct { + baseMessage + BlockHex string + BlockVerboseData *BlockVerboseData + + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *GetBlockResponseMessage) Command() MessageCommand { + return CmdGetBlockResponseMessage +} + +// NewGetBlockResponseMessage returns a instance of the message +func NewGetBlockResponseMessage() *GetBlockResponseMessage { + return &GetBlockResponseMessage{} +} + +// BlockVerboseData holds verbose data about a block +type BlockVerboseData struct { + Hash string + Confirmations uint64 + Size int32 + BlueScore uint64 + IsChainBlock bool + Version int32 + VersionHex string + HashMerkleRoot string + AcceptedIDMerkleRoot string + UTXOCommitment string + TxIDs []string + TransactionVerboseData []*TransactionVerboseData + Time int64 + Nonce uint64 + Bits string + Difficulty float64 + ParentHashes []string + SelectedParentHash string + ChildHashes []string + AcceptedBlockHashes []string +} + +// TransactionVerboseData holds verbose data about a transaction +type TransactionVerboseData struct { + Hex string + TxID string + Hash string + Size int32 + Version int32 + LockTime uint64 + SubnetworkID string + Gas uint64 + PayloadHash string + Payload string + TransactionVerboseInputs []*TransactionVerboseInput + TransactionVerboseOutputs []*TransactionVerboseOutput + BlockHash string + AcceptedBy string + IsInMempool bool + Time uint64 + BlockTime uint64 +} + +// TransactionVerboseInput holds data about a transaction input +type TransactionVerboseInput struct { + TxID string + OutputIndex uint32 + ScriptSig *ScriptSig + Sequence uint64 +} + +// ScriptSig holds data about a script signature +type ScriptSig struct { + Asm string + Hex string +} + +// TransactionVerboseOutput holds data about a transaction output +type TransactionVerboseOutput struct { + Value uint64 + Index uint32 + ScriptPubKey *ScriptPubKeyResult +} + +// ScriptPubKeyResult holds data about a script public key +type ScriptPubKeyResult struct { + Asm string + Hex string + Type string + Address string +} diff --git a/app/appmessage/rpc_get_block_template.go b/app/appmessage/rpc_get_block_template.go new file mode 100644 index 000000000..36e69c805 --- /dev/null +++ b/app/appmessage/rpc_get_block_template.go @@ -0,0 +1,78 @@ +package appmessage + +// GetBlockTemplateRequestMessage is an appmessage corresponding to +// its respective RPC message +type GetBlockTemplateRequestMessage struct { + baseMessage + PayAddress string + LongPollID string +} + +// Command returns the protocol command string for the message +func (msg *GetBlockTemplateRequestMessage) Command() MessageCommand { + return CmdGetBlockTemplateRequestMessage +} + +// NewGetBlockTemplateRequestMessage returns a instance of the message +func NewGetBlockTemplateRequestMessage(payAddress string, longPollID string) *GetBlockTemplateRequestMessage { + return &GetBlockTemplateRequestMessage{ + PayAddress: payAddress, + LongPollID: longPollID, + } +} + +// GetBlockTemplateResponseMessage is an appmessage corresponding to +// its respective RPC message +type GetBlockTemplateResponseMessage struct { + baseMessage + Bits string + CurrentTime int64 + ParentHashes []string + MassLimit int + Transactions []GetBlockTemplateTransactionMessage + HashMerkleRoot string + AcceptedIDMerkleRoot string + UTXOCommitment string + Version int32 + LongPollID string + TargetDifficulty string + MinTime int64 + MaxTime int64 + MutableFields []string + NonceRange string + IsSynced bool + IsConnected bool + + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *GetBlockTemplateResponseMessage) Command() MessageCommand { + return CmdGetBlockTemplateResponseMessage +} + +// NewGetBlockTemplateResponseMessage returns a instance of the message +func NewGetBlockTemplateResponseMessage() *GetBlockTemplateResponseMessage { + return &GetBlockTemplateResponseMessage{} +} + +// GetBlockTemplateTransactionMessage is an appmessage corresponding to +// its respective RPC message +type GetBlockTemplateTransactionMessage struct { + baseMessage + Data string + ID string + Depends []int64 + Mass uint64 + Fee uint64 +} + +// Command returns the protocol command string for the message +func (msg *GetBlockTemplateTransactionMessage) Command() MessageCommand { + return CmdGetBlockTemplateTransactionMessage +} + +// NewGetBlockTemplateTransactionMessage returns a instance of the message +func NewGetBlockTemplateTransactionMessage() *GetBlockTemplateTransactionMessage { + return &GetBlockTemplateTransactionMessage{} +} diff --git a/app/appmessage/rpc_get_blocks.go b/app/appmessage/rpc_get_blocks.go new file mode 100644 index 000000000..3a2866406 --- /dev/null +++ b/app/appmessage/rpc_get_blocks.go @@ -0,0 +1,51 @@ +package appmessage + +// GetBlocksRequestMessage is an appmessage corresponding to +// its respective RPC message +type GetBlocksRequestMessage struct { + baseMessage + LowHash string + IncludeBlockHexes bool + IncludeBlockVerboseData bool +} + +// Command returns the protocol command string for the message +func (msg *GetBlocksRequestMessage) Command() MessageCommand { + return CmdGetBlocksRequestMessage +} + +// NewGetBlocksRequestMessage returns a instance of the message +func NewGetBlocksRequestMessage(lowHash string, includeBlockHexes bool, includeBlockVerboseData bool) *GetBlocksRequestMessage { + return &GetBlocksRequestMessage{ + LowHash: lowHash, + IncludeBlockHexes: includeBlockHexes, + IncludeBlockVerboseData: includeBlockVerboseData, + } +} + +// GetBlocksResponseMessage is an appmessage corresponding to +// its respective RPC message +type GetBlocksResponseMessage struct { + baseMessage + BlockHashes []string + BlockHexes []string + BlockVerboseData []*BlockVerboseData + + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *GetBlocksResponseMessage) Command() MessageCommand { + return CmdGetBlocksResponseMessage +} + +// NewGetBlocksResponseMessage returns a instance of the message +func NewGetBlocksResponseMessage(blockHashes []string, blockHexes []string, + blockVerboseData []*BlockVerboseData) *GetBlocksResponseMessage { + + return &GetBlocksResponseMessage{ + BlockHashes: blockHashes, + BlockHexes: blockHexes, + BlockVerboseData: blockVerboseData, + } +} diff --git a/app/appmessage/rpc_get_chain_from_block.go b/app/appmessage/rpc_get_chain_from_block.go new file mode 100644 index 000000000..09103b04e --- /dev/null +++ b/app/appmessage/rpc_get_chain_from_block.go @@ -0,0 +1,49 @@ +package appmessage + +// GetChainFromBlockRequestMessage is an appmessage corresponding to +// its respective RPC message +type GetChainFromBlockRequestMessage struct { + baseMessage + StartHash string + IncludeBlockVerboseData bool +} + +// Command returns the protocol command string for the message +func (msg *GetChainFromBlockRequestMessage) Command() MessageCommand { + return CmdGetChainFromBlockRequestMessage +} + +// NewGetChainFromBlockRequestMessage returns a instance of the message +func NewGetChainFromBlockRequestMessage(startHash string, includeBlockVerboseData bool) *GetChainFromBlockRequestMessage { + return &GetChainFromBlockRequestMessage{ + StartHash: startHash, + IncludeBlockVerboseData: includeBlockVerboseData, + } +} + +// GetChainFromBlockResponseMessage is an appmessage corresponding to +// its respective RPC message +type GetChainFromBlockResponseMessage struct { + baseMessage + RemovedChainBlockHashes []string + AddedChainBlocks []*ChainBlock + BlockVerboseData []*BlockVerboseData + + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *GetChainFromBlockResponseMessage) Command() MessageCommand { + return CmdGetChainFromBlockResponseMessage +} + +// NewGetChainFromBlockResponseMessage returns a instance of the message +func NewGetChainFromBlockResponseMessage(removedChainBlockHashes []string, + addedChainBlocks []*ChainBlock, blockVerboseData []*BlockVerboseData) *GetChainFromBlockResponseMessage { + + return &GetChainFromBlockResponseMessage{ + RemovedChainBlockHashes: removedChainBlockHashes, + AddedChainBlocks: addedChainBlocks, + BlockVerboseData: blockVerboseData, + } +} diff --git a/app/appmessage/rpc_get_connected_peer_info.go b/app/appmessage/rpc_get_connected_peer_info.go new file mode 100644 index 000000000..c6d937029 --- /dev/null +++ b/app/appmessage/rpc_get_connected_peer_info.go @@ -0,0 +1,51 @@ +package appmessage + +// GetConnectedPeerInfoRequestMessage is an appmessage corresponding to +// its respective RPC message +type GetConnectedPeerInfoRequestMessage struct { + baseMessage +} + +// Command returns the protocol command string for the message +func (msg *GetConnectedPeerInfoRequestMessage) Command() MessageCommand { + return CmdGetConnectedPeerInfoRequestMessage +} + +// NewGetConnectedPeerInfoRequestMessage returns a instance of the message +func NewGetConnectedPeerInfoRequestMessage() *GetConnectedPeerInfoRequestMessage { + return &GetConnectedPeerInfoRequestMessage{} +} + +// GetConnectedPeerInfoResponseMessage is an appmessage corresponding to +// its respective RPC message +type GetConnectedPeerInfoResponseMessage struct { + baseMessage + Infos []*GetConnectedPeerInfoMessage + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *GetConnectedPeerInfoResponseMessage) Command() MessageCommand { + return CmdGetConnectedPeerInfoResponseMessage +} + +// NewGetConnectedPeerInfoResponseMessage returns a instance of the message +func NewGetConnectedPeerInfoResponseMessage(infos []*GetConnectedPeerInfoMessage) *GetConnectedPeerInfoResponseMessage { + return &GetConnectedPeerInfoResponseMessage{ + Infos: infos, + } +} + +// GetConnectedPeerInfoMessage holds information about a connected peer +type GetConnectedPeerInfoMessage struct { + ID string + Address string + LastPingDuration int64 + SelectedTipHash string + IsSyncNode bool + IsOutbound bool + TimeOffset int64 + UserAgent string + AdvertisedProtocolVersion uint32 + TimeConnected int64 +} diff --git a/app/appmessage/rpc_get_current_network.go b/app/appmessage/rpc_get_current_network.go new file mode 100644 index 000000000..f787a4be8 --- /dev/null +++ b/app/appmessage/rpc_get_current_network.go @@ -0,0 +1,38 @@ +package appmessage + +// GetCurrentNetworkRequestMessage is an appmessage corresponding to +// its respective RPC message +type GetCurrentNetworkRequestMessage struct { + baseMessage +} + +// Command returns the protocol command string for the message +func (msg *GetCurrentNetworkRequestMessage) Command() MessageCommand { + return CmdGetCurrentNetworkRequestMessage +} + +// NewGetCurrentNetworkRequestMessage returns a instance of the message +func NewGetCurrentNetworkRequestMessage() *GetCurrentNetworkRequestMessage { + return &GetCurrentNetworkRequestMessage{} +} + +// GetCurrentNetworkResponseMessage is an appmessage corresponding to +// its respective RPC message +type GetCurrentNetworkResponseMessage struct { + baseMessage + CurrentNetwork string + + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *GetCurrentNetworkResponseMessage) Command() MessageCommand { + return CmdGetCurrentNetworkResponseMessage +} + +// NewGetCurrentNetworkResponseMessage returns a instance of the message +func NewGetCurrentNetworkResponseMessage(currentNetwork string) *GetCurrentNetworkResponseMessage { + return &GetCurrentNetworkResponseMessage{ + CurrentNetwork: currentNetwork, + } +} diff --git a/app/appmessage/rpc_get_mempool_entry.go b/app/appmessage/rpc_get_mempool_entry.go new file mode 100644 index 000000000..1e49dcb80 --- /dev/null +++ b/app/appmessage/rpc_get_mempool_entry.go @@ -0,0 +1,35 @@ +package appmessage + +// GetMempoolEntryRequestMessage is an appmessage corresponding to +// its respective RPC message +type GetMempoolEntryRequestMessage struct { + baseMessage + TxID string +} + +// Command returns the protocol command string for the message +func (msg *GetMempoolEntryRequestMessage) Command() MessageCommand { + return CmdGetMempoolEntryRequestMessage +} + +// NewGetMempoolEntryRequestMessage returns a instance of the message +func NewGetMempoolEntryRequestMessage(txID string) *GetMempoolEntryRequestMessage { + return &GetMempoolEntryRequestMessage{TxID: txID} +} + +// GetMempoolEntryResponseMessage is an appmessage corresponding to +// its respective RPC message +type GetMempoolEntryResponseMessage struct { + baseMessage + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *GetMempoolEntryResponseMessage) Command() MessageCommand { + return CmdGetMempoolEntryResponseMessage +} + +// NewGetMempoolEntryResponseMessage returns a instance of the message +func NewGetMempoolEntryResponseMessage() *GetMempoolEntryResponseMessage { + return &GetMempoolEntryResponseMessage{} +} diff --git a/app/appmessage/rpc_get_peer_addresses.go b/app/appmessage/rpc_get_peer_addresses.go new file mode 100644 index 000000000..7ad2f7868 --- /dev/null +++ b/app/appmessage/rpc_get_peer_addresses.go @@ -0,0 +1,44 @@ +package appmessage + +// GetPeerAddressesRequestMessage is an appmessage corresponding to +// its respective RPC message +type GetPeerAddressesRequestMessage struct { + baseMessage +} + +// Command returns the protocol command string for the message +func (msg *GetPeerAddressesRequestMessage) Command() MessageCommand { + return CmdGetPeerAddressesRequestMessage +} + +// NewGetPeerAddressesRequestMessage returns a instance of the message +func NewGetPeerAddressesRequestMessage() *GetPeerAddressesRequestMessage { + return &GetPeerAddressesRequestMessage{} +} + +// GetPeerAddressesResponseMessage is an appmessage corresponding to +// its respective RPC message +type GetPeerAddressesResponseMessage struct { + baseMessage + Addresses []*GetPeerAddressesKnownAddressMessage + + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *GetPeerAddressesResponseMessage) Command() MessageCommand { + return CmdGetPeerAddressesResponseMessage +} + +// NewGetPeerAddressesResponseMessage returns a instance of the message +func NewGetPeerAddressesResponseMessage(addresses []*GetPeerAddressesKnownAddressMessage) *GetPeerAddressesResponseMessage { + return &GetPeerAddressesResponseMessage{ + Addresses: addresses, + } +} + +// GetPeerAddressesKnownAddressMessage is an appmessage corresponding to +// its respective RPC message +type GetPeerAddressesKnownAddressMessage struct { + Addr string +} diff --git a/app/appmessage/rpc_get_selected_tip_hash.go b/app/appmessage/rpc_get_selected_tip_hash.go new file mode 100644 index 000000000..fbb18c541 --- /dev/null +++ b/app/appmessage/rpc_get_selected_tip_hash.go @@ -0,0 +1,38 @@ +package appmessage + +// GetSelectedTipHashRequestMessage is an appmessage corresponding to +// its respective RPC message +type GetSelectedTipHashRequestMessage struct { + baseMessage +} + +// Command returns the protocol command string for the message +func (msg *GetSelectedTipHashRequestMessage) Command() MessageCommand { + return CmdGetSelectedTipHashRequestMessage +} + +// NewGetSelectedTipHashRequestMessage returns a instance of the message +func NewGetSelectedTipHashRequestMessage() *GetSelectedTipHashRequestMessage { + return &GetSelectedTipHashRequestMessage{} +} + +// GetSelectedTipHashResponseMessage is an appmessage corresponding to +// its respective RPC message +type GetSelectedTipHashResponseMessage struct { + baseMessage + SelectedTipHash string + + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *GetSelectedTipHashResponseMessage) Command() MessageCommand { + return CmdGetSelectedTipHashResponseMessage +} + +// NewGetSelectedTipHashResponseMessage returns a instance of the message +func NewGetSelectedTipHashResponseMessage(selectedTipHash string) *GetSelectedTipHashResponseMessage { + return &GetSelectedTipHashResponseMessage{ + SelectedTipHash: selectedTipHash, + } +} diff --git a/app/appmessage/rpc_get_subnetwork.go b/app/appmessage/rpc_get_subnetwork.go new file mode 100644 index 000000000..bb21bdbff --- /dev/null +++ b/app/appmessage/rpc_get_subnetwork.go @@ -0,0 +1,41 @@ +package appmessage + +// GetSubnetworkRequestMessage is an appmessage corresponding to +// its respective RPC message +type GetSubnetworkRequestMessage struct { + baseMessage + SubnetworkID string +} + +// Command returns the protocol command string for the message +func (msg *GetSubnetworkRequestMessage) Command() MessageCommand { + return CmdGetSubnetworkRequestMessage +} + +// NewGetSubnetworkRequestMessage returns a instance of the message +func NewGetSubnetworkRequestMessage(subnetworkID string) *GetSubnetworkRequestMessage { + return &GetSubnetworkRequestMessage{ + SubnetworkID: subnetworkID, + } +} + +// GetSubnetworkResponseMessage is an appmessage corresponding to +// its respective RPC message +type GetSubnetworkResponseMessage struct { + baseMessage + GasLimit uint64 + + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *GetSubnetworkResponseMessage) Command() MessageCommand { + return CmdGetSubnetworkResponseMessage +} + +// NewGetSubnetworkResponseMessage returns a instance of the message +func NewGetSubnetworkResponseMessage(gasLimit uint64) *GetSubnetworkResponseMessage { + return &GetSubnetworkResponseMessage{ + GasLimit: gasLimit, + } +} diff --git a/app/appmessage/rpc_notify_block_added.go b/app/appmessage/rpc_notify_block_added.go new file mode 100644 index 000000000..4580a3216 --- /dev/null +++ b/app/appmessage/rpc_notify_block_added.go @@ -0,0 +1,53 @@ +package appmessage + +// NotifyBlockAddedRequestMessage is an appmessage corresponding to +// its respective RPC message +type NotifyBlockAddedRequestMessage struct { + baseMessage +} + +// Command returns the protocol command string for the message +func (msg *NotifyBlockAddedRequestMessage) Command() MessageCommand { + return CmdNotifyBlockAddedRequestMessage +} + +// NewNotifyBlockAddedRequestMessage returns a instance of the message +func NewNotifyBlockAddedRequestMessage() *NotifyBlockAddedRequestMessage { + return &NotifyBlockAddedRequestMessage{} +} + +// NotifyBlockAddedResponseMessage is an appmessage corresponding to +// its respective RPC message +type NotifyBlockAddedResponseMessage struct { + baseMessage + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *NotifyBlockAddedResponseMessage) Command() MessageCommand { + return CmdNotifyBlockAddedResponseMessage +} + +// NewNotifyBlockAddedResponseMessage returns a instance of the message +func NewNotifyBlockAddedResponseMessage() *NotifyBlockAddedResponseMessage { + return &NotifyBlockAddedResponseMessage{} +} + +// BlockAddedNotificationMessage is an appmessage corresponding to +// its respective RPC message +type BlockAddedNotificationMessage struct { + baseMessage + Block *MsgBlock +} + +// Command returns the protocol command string for the message +func (msg *BlockAddedNotificationMessage) Command() MessageCommand { + return CmdBlockAddedNotificationMessage +} + +// NewBlockAddedNotificationMessage returns a instance of the message +func NewBlockAddedNotificationMessage(block *MsgBlock) *BlockAddedNotificationMessage { + return &BlockAddedNotificationMessage{ + Block: block, + } +} diff --git a/app/appmessage/rpc_notify_chain_changed.go b/app/appmessage/rpc_notify_chain_changed.go new file mode 100644 index 000000000..7a4e5173e --- /dev/null +++ b/app/appmessage/rpc_notify_chain_changed.go @@ -0,0 +1,69 @@ +package appmessage + +// NotifyChainChangedRequestMessage is an appmessage corresponding to +// its respective RPC message +type NotifyChainChangedRequestMessage struct { + baseMessage +} + +// Command returns the protocol command string for the message +func (msg *NotifyChainChangedRequestMessage) Command() MessageCommand { + return CmdNotifyChainChangedRequestMessage +} + +// NewNotifyChainChangedRequestMessage returns a instance of the message +func NewNotifyChainChangedRequestMessage() *NotifyChainChangedRequestMessage { + return &NotifyChainChangedRequestMessage{} +} + +// NotifyChainChangedResponseMessage is an appmessage corresponding to +// its respective RPC message +type NotifyChainChangedResponseMessage struct { + baseMessage + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *NotifyChainChangedResponseMessage) Command() MessageCommand { + return CmdNotifyChainChangedResponseMessage +} + +// NewNotifyChainChangedResponseMessage returns a instance of the message +func NewNotifyChainChangedResponseMessage() *NotifyChainChangedResponseMessage { + return &NotifyChainChangedResponseMessage{} +} + +// ChainChangedNotificationMessage is an appmessage corresponding to +// its respective RPC message +type ChainChangedNotificationMessage struct { + baseMessage + RemovedChainBlockHashes []string + AddedChainBlocks []*ChainBlock +} + +// ChainBlock represents a DAG chain-block +type ChainBlock struct { + Hash string + AcceptedBlocks []*AcceptedBlock +} + +// AcceptedBlock represents a block accepted into the DAG +type AcceptedBlock struct { + Hash string + AcceptedTxIDs []string +} + +// Command returns the protocol command string for the message +func (msg *ChainChangedNotificationMessage) Command() MessageCommand { + return CmdChainChangedNotificationMessage +} + +// NewChainChangedNotificationMessage returns a instance of the message +func NewChainChangedNotificationMessage(removedChainBlockHashes []string, + addedChainBlocks []*ChainBlock) *ChainChangedNotificationMessage { + + return &ChainChangedNotificationMessage{ + RemovedChainBlockHashes: removedChainBlockHashes, + AddedChainBlocks: addedChainBlocks, + } +} diff --git a/app/appmessage/rpc_submit_block.go b/app/appmessage/rpc_submit_block.go new file mode 100644 index 000000000..dd1875251 --- /dev/null +++ b/app/appmessage/rpc_submit_block.go @@ -0,0 +1,37 @@ +package appmessage + +// SubmitBlockRequestMessage is an appmessage corresponding to +// its respective RPC message +type SubmitBlockRequestMessage struct { + baseMessage + BlockHex string +} + +// Command returns the protocol command string for the message +func (msg *SubmitBlockRequestMessage) Command() MessageCommand { + return CmdSubmitBlockRequestMessage +} + +// NewSubmitBlockRequestMessage returns a instance of the message +func NewSubmitBlockRequestMessage(blockHex string) *SubmitBlockRequestMessage { + return &SubmitBlockRequestMessage{ + BlockHex: blockHex, + } +} + +// SubmitBlockResponseMessage is an appmessage corresponding to +// its respective RPC message +type SubmitBlockResponseMessage struct { + baseMessage + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *SubmitBlockResponseMessage) Command() MessageCommand { + return CmdSubmitBlockResponseMessage +} + +// NewSubmitBlockResponseMessage returns a instance of the message +func NewSubmitBlockResponseMessage() *SubmitBlockResponseMessage { + return &SubmitBlockResponseMessage{} +} diff --git a/app/appmessage/rpc_submit_transaction.go b/app/appmessage/rpc_submit_transaction.go new file mode 100644 index 000000000..1a4a58931 --- /dev/null +++ b/app/appmessage/rpc_submit_transaction.go @@ -0,0 +1,41 @@ +package appmessage + +// SubmitTransactionRequestMessage is an appmessage corresponding to +// its respective RPC message +type SubmitTransactionRequestMessage struct { + baseMessage + TransactionHex string +} + +// Command returns the protocol command string for the message +func (msg *SubmitTransactionRequestMessage) Command() MessageCommand { + return CmdSubmitTransactionRequestMessage +} + +// NewSubmitTransactionRequestMessage returns a instance of the message +func NewSubmitTransactionRequestMessage(transactionHex string) *SubmitTransactionRequestMessage { + return &SubmitTransactionRequestMessage{ + TransactionHex: transactionHex, + } +} + +// SubmitTransactionResponseMessage is an appmessage corresponding to +// its respective RPC message +type SubmitTransactionResponseMessage struct { + baseMessage + TxID string + + Error *RPCError +} + +// Command returns the protocol command string for the message +func (msg *SubmitTransactionResponseMessage) Command() MessageCommand { + return CmdSubmitTransactionResponseMessage +} + +// NewSubmitTransactionResponseMessage returns a instance of the message +func NewSubmitTransactionResponseMessage(txID string) *SubmitTransactionResponseMessage { + return &SubmitTransactionResponseMessage{ + TxID: txID, + } +} diff --git a/app/protocol/flowcontext/blocks.go b/app/protocol/flowcontext/blocks.go index 2cbf9cd90..0e29ef478 100644 --- a/app/protocol/flowcontext/blocks.go +++ b/app/protocol/flowcontext/blocks.go @@ -18,6 +18,9 @@ func (f *FlowContext) OnNewBlock(block *util.Block) error { if err != nil { return err } + if f.onBlockAddedToDAGHandler != nil { + f.onBlockAddedToDAGHandler(block) + } return f.broadcastTransactionsAfterBlockAdded(block, transactionsAcceptedToMempool) } diff --git a/app/protocol/flowcontext/flow_context.go b/app/protocol/flowcontext/flow_context.go index 4086b0615..bb56518c1 100644 --- a/app/protocol/flowcontext/flow_context.go +++ b/app/protocol/flowcontext/flow_context.go @@ -18,6 +18,14 @@ import ( "github.com/kaspanet/kaspad/util/daghash" ) +// OnBlockAddedToDAGHandler is a handler function that's triggered +// when a block is added to the DAG +type OnBlockAddedToDAGHandler func(block *util.Block) + +// OnTransactionAddedToMempoolHandler is a handler function that's triggered +// when a transaction is added to the mempool +type OnTransactionAddedToMempoolHandler func() + // FlowContext holds state that is relevant to more than one flow or one peer, and allows communication between // different flows that can be associated to different peers. type FlowContext struct { @@ -28,6 +36,9 @@ type FlowContext struct { addressManager *addressmanager.AddressManager connectionManager *connmanager.ConnectionManager + onBlockAddedToDAGHandler OnBlockAddedToDAGHandler + onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler + transactionsToRebroadcastLock sync.Mutex transactionsToRebroadcast map[daghash.TxID]*util.Tx lastRebroadcastTime time.Time @@ -61,3 +72,13 @@ func New(cfg *config.Config, dag *blockdag.BlockDAG, addressManager *addressmana transactionsToRebroadcast: make(map[daghash.TxID]*util.Tx), } } + +// SetOnBlockAddedToDAGHandler sets the onBlockAddedToDAG handler +func (f *FlowContext) SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler OnBlockAddedToDAGHandler) { + f.onBlockAddedToDAGHandler = onBlockAddedToDAGHandler +} + +// SetOnTransactionAddedToMempoolHandler sets the onTransactionAddedToMempool handler +func (f *FlowContext) SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler) { + f.onTransactionAddedToMempoolHandler = onTransactionAddedToMempoolHandler +} diff --git a/app/protocol/flowcontext/network.go b/app/protocol/flowcontext/network.go index d0dca1697..1533baed8 100644 --- a/app/protocol/flowcontext/network.go +++ b/app/protocol/flowcontext/network.go @@ -56,7 +56,7 @@ func (f *FlowContext) readyPeerConnections() []*netadapter.NetConnection { // Broadcast broadcast the given message to all the ready peers. func (f *FlowContext) Broadcast(message appmessage.Message) error { - return f.netAdapter.Broadcast(f.readyPeerConnections(), message) + return f.netAdapter.P2PBroadcast(f.readyPeerConnections(), message) } // Peers returns the currently active peers diff --git a/app/protocol/flowcontext/transactions.go b/app/protocol/flowcontext/transactions.go index 3e9aef7ff..b0a8424f3 100644 --- a/app/protocol/flowcontext/transactions.go +++ b/app/protocol/flowcontext/transactions.go @@ -68,3 +68,11 @@ func (f *FlowContext) SharedRequestedTransactions() *relaytransactions.SharedReq func (f *FlowContext) TxPool() *mempool.TxPool { return f.txPool } + +// OnTransactionAddedToMempool notifies the handler function that a transaction +// has been added to the mempool +func (f *FlowContext) OnTransactionAddedToMempool() { + if f.onTransactionAddedToMempoolHandler != nil { + f.onTransactionAddedToMempoolHandler() + } +} diff --git a/app/protocol/flows/relaytransactions/handle_relayed_transactions.go b/app/protocol/flows/relaytransactions/handle_relayed_transactions.go index f480e0fbe..41f15c018 100644 --- a/app/protocol/flows/relaytransactions/handle_relayed_transactions.go +++ b/app/protocol/flows/relaytransactions/handle_relayed_transactions.go @@ -21,6 +21,7 @@ type TransactionsRelayContext interface { SharedRequestedTransactions() *SharedRequestedTransactions TxPool() *mempool.TxPool Broadcast(message appmessage.Message) error + OnTransactionAddedToMempool() } type handleRelayedTransactionsFlow struct { @@ -218,6 +219,7 @@ func (flow *handleRelayedTransactionsFlow) receiveTransactions(requestedTransact if err != nil { return err } + flow.OnTransactionAddedToMempool() } return nil } diff --git a/app/protocol/manager.go b/app/protocol/manager.go index f130e5f4c..ede2159d7 100644 --- a/app/protocol/manager.go +++ b/app/protocol/manager.go @@ -27,20 +27,10 @@ func NewManager(cfg *config.Config, dag *blockdag.BlockDAG, netAdapter *netadapt manager := Manager{ context: flowcontext.New(cfg, dag, addressManager, txPool, netAdapter, connectionManager), } - netAdapter.SetRouterInitializer(manager.routerInitializer) + netAdapter.SetP2PRouterInitializer(manager.routerInitializer) return &manager, nil } -// Start starts the p2p protocol -func (m *Manager) Start() error { - return m.context.NetAdapter().Start() -} - -// Stop stops the p2p protocol -func (m *Manager) Stop() error { - return m.context.NetAdapter().Stop() -} - // Peers returns the currently active peers func (m *Manager) Peers() []*peerpkg.Peer { return m.context.Peers() @@ -72,3 +62,13 @@ func (m *Manager) runFlows(flows []*flow, peer *peerpkg.Peer, errChan <-chan err return <-errChan } + +// SetOnBlockAddedToDAGHandler sets the onBlockAddedToDAG handler +func (m *Manager) SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler flowcontext.OnBlockAddedToDAGHandler) { + m.context.SetOnBlockAddedToDAGHandler(onBlockAddedToDAGHandler) +} + +// SetOnTransactionAddedToMempoolHandler sets the onTransactionAddedToMempool handler +func (m *Manager) SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler flowcontext.OnTransactionAddedToMempoolHandler) { + m.context.SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler) +} diff --git a/app/rpc/log.go b/app/rpc/log.go new file mode 100644 index 000000000..179def91d --- /dev/null +++ b/app/rpc/log.go @@ -0,0 +1,9 @@ +package rpc + +import ( + "github.com/kaspanet/kaspad/infrastructure/logger" + "github.com/kaspanet/kaspad/util/panics" +) + +var log, _ = logger.Get(logger.SubsystemTags.RPCS) +var spawn = panics.GoroutineWrapperFunc(log) diff --git a/app/rpc/manager.go b/app/rpc/manager.go new file mode 100644 index 000000000..7f6442c91 --- /dev/null +++ b/app/rpc/manager.go @@ -0,0 +1,80 @@ +package rpc + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/protocol" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/domain/blockdag" + "github.com/kaspanet/kaspad/domain/blockdag/indexers" + "github.com/kaspanet/kaspad/domain/mempool" + "github.com/kaspanet/kaspad/domain/mining" + "github.com/kaspanet/kaspad/infrastructure/config" + "github.com/kaspanet/kaspad/infrastructure/network/addressmanager" + "github.com/kaspanet/kaspad/infrastructure/network/connmanager" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter" + "github.com/kaspanet/kaspad/util" + "github.com/kaspanet/kaspad/util/daghash" +) + +// Manager is an RPC manager +type Manager struct { + context *rpccontext.Context +} + +// NewManager creates a new RPC Manager +func NewManager( + cfg *config.Config, + netAdapter *netadapter.NetAdapter, + dag *blockdag.BlockDAG, + protocolManager *protocol.Manager, + connectionManager *connmanager.ConnectionManager, + blockTemplateGenerator *mining.BlkTmplGenerator, + mempool *mempool.TxPool, + addressManager *addressmanager.AddressManager, + acceptanceIndex *indexers.AcceptanceIndex) *Manager { + + manager := Manager{ + context: rpccontext.NewContext( + cfg, + netAdapter, + dag, + protocolManager, + connectionManager, + blockTemplateGenerator, + mempool, + addressManager, + acceptanceIndex, + ), + } + netAdapter.SetRPCRouterInitializer(manager.routerInitializer) + + return &manager +} + +// NotifyBlockAddedToDAG notifies the manager that a block has been added to the DAG +func (m *Manager) NotifyBlockAddedToDAG(block *util.Block) { + m.context.BlockTemplateState.NotifyBlockAdded(block) + + notification := appmessage.NewBlockAddedNotificationMessage(block.MsgBlock()) + m.context.NotificationManager.NotifyBlockAdded(notification) +} + +// NotifyChainChanged notifies the manager that the DAG's selected parent chain has changed +func (m *Manager) NotifyChainChanged(removedChainBlockHashes []*daghash.Hash, addedChainBlockHashes []*daghash.Hash) error { + addedChainBlocks, err := m.context.CollectChainBlocks(addedChainBlockHashes) + if err != nil { + return err + } + removedChainBlockHashStrings := make([]string, len(removedChainBlockHashes)) + for i, removedChainBlockHash := range removedChainBlockHashes { + removedChainBlockHashStrings[i] = removedChainBlockHash.String() + } + notification := appmessage.NewChainChangedNotificationMessage(removedChainBlockHashStrings, addedChainBlocks) + m.context.NotificationManager.NotifyChainChanged(notification) + return nil +} + +// NotifyTransactionAddedToMempool notifies the manager that a transaction has been added to the mempool +func (m *Manager) NotifyTransactionAddedToMempool() { + m.context.BlockTemplateState.NotifyMempoolTx() +} diff --git a/app/rpc/rpc.go b/app/rpc/rpc.go new file mode 100644 index 000000000..115f7cd2f --- /dev/null +++ b/app/rpc/rpc.go @@ -0,0 +1,94 @@ +package rpc + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/app/rpc/rpchandlers" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/pkg/errors" +) + +type handler func(context *rpccontext.Context, router *router.Router, request appmessage.Message) (appmessage.Message, error) + +var handlers = map[appmessage.MessageCommand]handler{ + appmessage.CmdGetCurrentNetworkRequestMessage: rpchandlers.HandleGetCurrentNetwork, + appmessage.CmdSubmitBlockRequestMessage: rpchandlers.HandleSubmitBlock, + appmessage.CmdGetBlockTemplateRequestMessage: rpchandlers.HandleGetBlockTemplate, + appmessage.CmdNotifyBlockAddedRequestMessage: rpchandlers.HandleNotifyBlockAdded, + appmessage.CmdGetPeerAddressesRequestMessage: rpchandlers.HandleGetPeerAddresses, + appmessage.CmdGetSelectedTipHashRequestMessage: rpchandlers.HandleGetSelectedTipHash, + appmessage.CmdGetMempoolEntryRequestMessage: rpchandlers.HandleGetMempoolEntry, + appmessage.CmdGetConnectedPeerInfoRequestMessage: rpchandlers.HandleGetConnectedPeerInfo, + appmessage.CmdAddPeerRequestMessage: rpchandlers.HandleAddPeer, + appmessage.CmdSubmitTransactionRequestMessage: rpchandlers.HandleSubmitTransaction, + appmessage.CmdNotifyChainChangedRequestMessage: rpchandlers.HandleNotifyChainChanged, + appmessage.CmdGetBlockRequestMessage: rpchandlers.HandleGetBlock, + appmessage.CmdGetSubnetworkRequestMessage: rpchandlers.HandleGetSubnetwork, + appmessage.CmdGetChainFromBlockRequestMessage: rpchandlers.HandleGetChainFromBlock, + appmessage.CmdGetBlocksRequestMessage: rpchandlers.HandleGetBlocks, +} + +func (m *Manager) routerInitializer(router *router.Router, netConnection *netadapter.NetConnection) { + messageTypes := make([]appmessage.MessageCommand, 0, len(handlers)) + for messageType := range handlers { + messageTypes = append(messageTypes, messageType) + } + incomingRoute, err := router.AddIncomingRoute(messageTypes) + if err != nil { + panic(err) + } + spawn("routerInitializer-handleIncomingMessages", func() { + err := m.handleIncomingMessages(router, incomingRoute) + m.handleError(err, netConnection) + }) + spawn("routerInitializer-handleOutgoingNotifications", func() { + err := m.handleOutgoingNotifications(router) + m.handleError(err, netConnection) + }) +} + +func (m *Manager) handleIncomingMessages(router *router.Router, incomingRoute *router.Route) error { + outgoingRoute := router.OutgoingRoute() + for { + request, err := incomingRoute.Dequeue() + if err != nil { + return err + } + handler, ok := handlers[request.Command()] + if !ok { + return err + } + response, err := handler(m.context, router, request) + if err != nil { + return err + } + err = outgoingRoute.Enqueue(response) + if err != nil { + return err + } + } +} + +func (m *Manager) handleOutgoingNotifications(router *router.Router) error { + notificationListener := m.context.NotificationManager.AddListener(router) + defer m.context.NotificationManager.RemoveListener(router) + for { + err := notificationListener.ProcessNextNotification() + if err != nil { + return err + } + } +} + +func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection) { + if errors.Is(err, router.ErrTimeout) { + log.Warnf("Got timeout from %s. Disconnecting...", netConnection) + netConnection.Disconnect() + return + } + if errors.Is(err, router.ErrRouteClosed) { + return + } + panic(err) +} diff --git a/app/rpc/rpccontext/blocktemplate.go b/app/rpc/rpccontext/blocktemplate.go new file mode 100644 index 000000000..629952b99 --- /dev/null +++ b/app/rpc/rpccontext/blocktemplate.go @@ -0,0 +1,476 @@ +package rpccontext + +import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/domain/mining" + "github.com/kaspanet/kaspad/util" + "github.com/kaspanet/kaspad/util/daghash" + "github.com/kaspanet/kaspad/util/mstime" + "github.com/kaspanet/kaspad/util/random" + "github.com/pkg/errors" + "strconv" + "strings" + "sync" + "time" +) + +const ( + // blockTemplateNonceRange is two 64-bit big-endian hexadecimal integers which + // represent the valid ranges of nonces returned by the getBlockTemplate + // RPC. + blockTemplateNonceRange = "000000000000ffffffffffff" + + // blockTemplateRegenerateSeconds is the number of seconds that must pass before + // a new template is generated when the parent block hashes has not + // changed and there have been changes to the available transactions + // in the memory pool. + blockTemplateRegenerateSeconds = 60 +) + +var ( + // blockTemplateMutableFields are the manipulations the server allows to be made + // to block templates generated by the getBlockTemplate RPC. It is + // declared here to avoid the overhead of creating the slice on every + // invocation for constant data. + blockTemplateMutableFields = []string{ + "time", "transactions/add", "parentblock", "coinbase/append", + } +) + +// BlockTemplateState houses state that is used in between multiple RPC invocations to +// getBlockTemplate. +type BlockTemplateState struct { + sync.Mutex + + context *Context + + lastTxUpdate mstime.Time + lastGenerated mstime.Time + tipHashes []*daghash.Hash + minTimestamp mstime.Time + template *mining.BlockTemplate + notifyMap map[string]map[int64]chan struct{} + payAddress util.Address +} + +// NewBlockTemplateState returns a new instance of a BlockTemplateState with all internal +// fields initialized and ready to use. +func NewBlockTemplateState(context *Context) *BlockTemplateState { + return &BlockTemplateState{ + context: context, + notifyMap: make(map[string]map[int64]chan struct{}), + } +} + +// Update updates the block template state +func (bt *BlockTemplateState) Update(payAddress util.Address) error { + generator := bt.context.BlockTemplateGenerator + lastTxUpdate := generator.TxSource().LastUpdated() + if lastTxUpdate.IsZero() { + lastTxUpdate = mstime.Now() + } + + // Generate a new block template when the current best block has + // changed or the transactions in the memory pool have been updated and + // it has been at least gbtRegenerateSecond since the last template was + // generated. + var msgBlock *appmessage.MsgBlock + var targetDifficulty string + tipHashes := bt.context.DAG.TipHashes() + template := bt.template + if template == nil || bt.tipHashes == nil || + !daghash.AreEqual(bt.tipHashes, tipHashes) || + bt.payAddress.String() != payAddress.String() || + (bt.lastTxUpdate != lastTxUpdate && + mstime.Now().After(bt.lastGenerated.Add(time.Second* + blockTemplateRegenerateSeconds))) { + + // Reset the previous best hash the block template was generated + // against so any errors below cause the next invocation to try + // again. + bt.tipHashes = nil + + // Create a new block template that has a coinbase which anyone + // can redeem. This is only acceptable because the returned + // block template doesn't include the coinbase, so the caller + // will ultimately create their own coinbase which pays to the + // appropriate address(es). + + extraNonce, err := random.Uint64() + if err != nil { + return errors.Wrapf(err, "failed to randomize extra nonce") + } + + blockTemplate, err := generator.NewBlockTemplate(payAddress, extraNonce) + if err != nil { + return errors.Wrapf(err, "failed to create new block template") + } + template = blockTemplate + msgBlock = template.Block + targetDifficulty = fmt.Sprintf("%064x", util.CompactToBig(msgBlock.Header.Bits)) + + // Get the minimum allowed timestamp for the block based on the + // median timestamp of the last several blocks per the DAG + // consensus rules. + minTimestamp := bt.context.DAG.NextBlockMinimumTime() + + // Update work state to ensure another block template isn't + // generated until needed. + bt.template = template + bt.lastGenerated = mstime.Now() + bt.lastTxUpdate = lastTxUpdate + bt.tipHashes = tipHashes + bt.minTimestamp = minTimestamp + bt.payAddress = payAddress + + log.Debugf("Generated block template (timestamp %s, "+ + "target %s, merkle root %s)", + msgBlock.Header.Timestamp, targetDifficulty, + msgBlock.Header.HashMerkleRoot) + + // Notify any clients that are long polling about the new + // template. + bt.notifyLongPollers(tipHashes, lastTxUpdate) + } else { + // At this point, there is a saved block template and another + // request for a template was made, but either the available + // transactions haven't change or it hasn't been long enough to + // trigger a new block template to be generated. So, update the + // existing block template. + + // Set locals for convenience. + msgBlock = template.Block + targetDifficulty = fmt.Sprintf("%064x", + util.CompactToBig(msgBlock.Header.Bits)) + + // Update the time of the block template to the current time + // while accounting for the median time of the past several + // blocks per the DAG consensus rules. + err := generator.UpdateBlockTime(msgBlock) + if err != nil { + return errors.Wrapf(err, "failed to update block time") + } + msgBlock.Header.Nonce = 0 + + log.Debugf("Updated block template (timestamp %s, "+ + "target %s)", msgBlock.Header.Timestamp, + targetDifficulty) + } + + return nil +} + +// Response builds a GetBlockTemplateResponseMessage from the current state +func (bt *BlockTemplateState) Response() (*appmessage.GetBlockTemplateResponseMessage, error) { + dag := bt.context.DAG + // Ensure the timestamps are still in valid range for the template. + // This should really only ever happen if the local clock is changed + // after the template is generated, but it's important to avoid serving + // block templates that will be delayed on other nodes. + template := bt.template + msgBlock := template.Block + header := &msgBlock.Header + adjustedTime := dag.Now() + maxTime := adjustedTime.Add(time.Millisecond * time.Duration(dag.TimestampDeviationTolerance)) + if header.Timestamp.After(maxTime) { + errorMessage := &appmessage.GetBlockTemplateResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("The template time is after the "+ + "maximum allowed time for a block - template "+ + "time %s, maximum time %s", adjustedTime, + maxTime) + return errorMessage, nil + } + + // Convert each transaction in the block template to a template result + // transaction. The result does not include the coinbase, so notice + // the adjustments to the various lengths and indices. + numTx := len(msgBlock.Transactions) + transactions := make([]appmessage.GetBlockTemplateTransactionMessage, 0, numTx-1) + txIndex := make(map[daghash.TxID]int64, numTx) + for i, tx := range msgBlock.Transactions { + txID := tx.TxID() + txIndex[*txID] = int64(i) + + // Create an array of 1-based indices to transactions that come + // before this one in the transactions list which this one + // depends on. This is necessary since the created block must + // ensure proper ordering of the dependencies. A map is used + // before creating the final array to prevent duplicate entries + // when multiple inputs reference the same transaction. + dependsMap := make(map[int64]struct{}) + for _, txIn := range tx.TxIn { + if idx, ok := txIndex[txIn.PreviousOutpoint.TxID]; ok { + dependsMap[idx] = struct{}{} + } + } + depends := make([]int64, 0, len(dependsMap)) + for idx := range dependsMap { + depends = append(depends, idx) + } + + // Serialize the transaction for later conversion to hex. + txBuf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(txBuf); err != nil { + errorMessage := &appmessage.GetBlockTemplateResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Failed to serialize transaction: %s", err) + return errorMessage, nil + } + + resultTx := appmessage.GetBlockTemplateTransactionMessage{ + Data: hex.EncodeToString(txBuf.Bytes()), + ID: txID.String(), + Depends: depends, + Mass: template.TxMasses[i], + Fee: template.Fees[i], + } + transactions = append(transactions, resultTx) + } + + // Generate the block template reply. Note that following mutations are + // implied by the included or omission of fields: + // Including MinTime -> time/decrement + // Omitting CoinbaseTxn -> coinbase, generation + targetDifficulty := fmt.Sprintf("%064x", util.CompactToBig(header.Bits)) + longPollID := bt.encodeLongPollID(bt.tipHashes, bt.payAddress, bt.lastGenerated) + + // Check whether this node is synced with the rest of of the + // network. There's almost never a good reason to mine on top + // of an unsynced DAG, and miners are generally expected not to + // mine when isSynced is false. + // This is not a straight-up error because the choice of whether + // to mine or not is the responsibility of the miner rather + // than the node's. + isSynced := bt.context.BlockTemplateGenerator.IsSynced() + isConnected := len(bt.context.ProtocolManager.Peers()) > 0 + + reply := appmessage.GetBlockTemplateResponseMessage{ + Bits: strconv.FormatInt(int64(header.Bits), 16), + CurrentTime: header.Timestamp.UnixMilliseconds(), + ParentHashes: daghash.Strings(header.ParentHashes), + MassLimit: appmessage.MaxMassPerBlock, + Transactions: transactions, + HashMerkleRoot: header.HashMerkleRoot.String(), + AcceptedIDMerkleRoot: header.AcceptedIDMerkleRoot.String(), + UTXOCommitment: header.UTXOCommitment.String(), + Version: header.Version, + LongPollID: longPollID, + TargetDifficulty: targetDifficulty, + MinTime: bt.minTimestamp.UnixMilliseconds(), + MaxTime: maxTime.UnixMilliseconds(), + MutableFields: blockTemplateMutableFields, + NonceRange: blockTemplateNonceRange, + IsSynced: isSynced, + IsConnected: isConnected, + } + + return &reply, nil +} + +// notifyLongPollers notifies any channels that have been registered to be +// notified when block templates are stale. +// +// This function MUST be called with the state locked. +func (bt *BlockTemplateState) notifyLongPollers(tipHashes []*daghash.Hash, lastGenerated mstime.Time) { + // Notify anything that is waiting for a block template update from + // hashes which are not the current tip hashes. + tipHashesStr := daghash.JoinHashesStrings(tipHashes, "") + for hashesStr, channels := range bt.notifyMap { + if hashesStr != tipHashesStr { + for _, c := range channels { + close(c) + } + delete(bt.notifyMap, hashesStr) + } + } + + // Return now if the provided last generated timestamp has not been + // initialized. + if lastGenerated.IsZero() { + return + } + + // Return now if there is nothing registered for updates to the current + // best block hash. + channels, ok := bt.notifyMap[tipHashesStr] + if !ok { + return + } + + // Notify anything that is waiting for a block template update from a + // block template generated before the most recently generated block + // template. + lastGeneratedUnix := lastGenerated.UnixSeconds() + for lastGen, c := range channels { + if lastGen < lastGeneratedUnix { + close(c) + delete(channels, lastGen) + } + } + + // Remove the entry altogether if there are no more registered + // channels. + if len(channels) == 0 { + delete(bt.notifyMap, tipHashesStr) + } +} + +// NotifyBlockAdded uses the newly-added block to notify any long poll +// clients with a new block template when their existing block template is +// stale due to the newly added block. +func (bt *BlockTemplateState) NotifyBlockAdded(block *util.Block) { + spawn("BlockTemplateState.NotifyBlockAdded", func() { + bt.Lock() + defer bt.Unlock() + + bt.notifyLongPollers(block.MsgBlock().Header.ParentHashes, bt.lastTxUpdate) + }) +} + +// NotifyMempoolTx uses the new last updated time for the transaction memory +// pool to notify any long poll clients with a new block template when their +// existing block template is stale due to enough time passing and the contents +// of the memory pool changing. +func (bt *BlockTemplateState) NotifyMempoolTx() { + lastUpdated := bt.context.Mempool.LastUpdated() + spawn("BlockTemplateState", func() { + bt.Lock() + defer bt.Unlock() + + // No need to notify anything if no block templates have been generated + // yet. + if bt.tipHashes == nil || bt.lastGenerated.IsZero() { + return + } + + if mstime.Now().After(bt.lastGenerated.Add(time.Second * + blockTemplateRegenerateSeconds)) { + + bt.notifyLongPollers(bt.tipHashes, lastUpdated) + } + }) +} + +// BlockTemplateOrLongPollChan returns a block template if the +// template identified by the provided long poll ID is stale or +// invalid. Otherwise, it returns a channel that will notify +// when there's a more current template. +func (bt *BlockTemplateState) BlockTemplateOrLongPollChan(longPollID string, + payAddress util.Address) (*appmessage.GetBlockTemplateResponseMessage, chan struct{}, error) { + + bt.Lock() + defer bt.Unlock() + + if err := bt.Update(payAddress); err != nil { + return nil, nil, err + } + + // Just return the current block template if the long poll ID provided by + // the caller is invalid. + parentHashes, lastGenerated, err := bt.decodeLongPollID(longPollID) + if err != nil { + result, err := bt.Response() + if err != nil { + return nil, nil, err + } + + return result, nil, nil + } + + // Return the block template now if the specific block template + // identified by the long poll ID no longer matches the current block + // template as this means the provided template is stale. + areHashesEqual := daghash.AreEqual(bt.template.Block.Header.ParentHashes, parentHashes) + if !areHashesEqual || + lastGenerated != bt.lastGenerated.UnixSeconds() { + + // Include whether or not it is valid to submit work against the + // old block template depending on whether or not a solution has + // already been found and added to the block DAG. + result, err := bt.Response() + if err != nil { + return nil, nil, err + } + + return result, nil, nil + } + + // Register the parent hashes and last generated time for notifications + // Get a channel that will be notified when the template associated with + // the provided ID is stale and a new block template should be returned to + // the caller. + longPollChan := bt.templateUpdateChan(parentHashes, lastGenerated) + return nil, longPollChan, nil +} + +// templateUpdateChan returns a channel that will be closed once the block +// template associated with the passed parent hashes and last generated time +// is stale. The function will return existing channels for duplicate +// parameters which allows multiple clients to wait for the same block template +// without requiring a different channel for each client. +// +// This function MUST be called with the state locked. +func (bt *BlockTemplateState) templateUpdateChan(tipHashes []*daghash.Hash, lastGenerated int64) chan struct{} { + tipHashesStr := daghash.JoinHashesStrings(tipHashes, "") + // Either get the current list of channels waiting for updates about + // changes to block template for the parent hashes or create a new one. + channels, ok := bt.notifyMap[tipHashesStr] + if !ok { + m := make(map[int64]chan struct{}) + bt.notifyMap[tipHashesStr] = m + channels = m + } + + // Get the current channel associated with the time the block template + // was last generated or create a new one. + c, ok := channels[lastGenerated] + if !ok { + c = make(chan struct{}) + channels[lastGenerated] = c + } + + return c +} + +// encodeLongPollID encodes the passed details into an ID that can be used to +// uniquely identify a block template. +func (bt *BlockTemplateState) encodeLongPollID(parentHashes []*daghash.Hash, miningAddress util.Address, lastGenerated mstime.Time) string { + return fmt.Sprintf("%s-%s-%d", daghash.JoinHashesStrings(parentHashes, ""), miningAddress, lastGenerated.UnixSeconds()) +} + +// decodeLongPollID decodes an ID that is used to uniquely identify a block +// template. This is mainly used as a mechanism to track when to update clients +// that are using long polling for block templates. The ID consists of the +// parent blocks hashes for the associated template and the time the associated +// template was generated. +func (bt *BlockTemplateState) decodeLongPollID(longPollID string) ([]*daghash.Hash, int64, error) { + fields := strings.Split(longPollID, "-") + if len(fields) != 2 { + return nil, 0, errors.New("decodeLongPollID: invalid number of fields") + } + + parentHashesStr := fields[0] + if len(parentHashesStr)%daghash.HashSize != 0 { + return nil, 0, errors.New("decodeLongPollID: invalid parent hashes format") + } + numberOfHashes := len(parentHashesStr) / daghash.HashSize + + parentHashes := make([]*daghash.Hash, 0, numberOfHashes) + + for i := 0; i < len(parentHashesStr); i += daghash.HashSize { + hash, err := daghash.NewHashFromStr(parentHashesStr[i : i+daghash.HashSize]) + if err != nil { + return nil, 0, errors.Errorf("decodeLongPollID: NewHashFromStr: %s", err) + } + parentHashes = append(parentHashes, hash) + } + + lastGenerated, err := strconv.ParseInt(fields[1], 10, 64) + if err != nil { + return nil, 0, errors.Errorf("decodeLongPollID: Cannot parse timestamp %s: %s", fields[1], err) + } + + return parentHashes, lastGenerated, nil +} diff --git a/app/rpc/rpccontext/chainblocks.go b/app/rpc/rpccontext/chainblocks.go new file mode 100644 index 000000000..aa6962ab0 --- /dev/null +++ b/app/rpc/rpccontext/chainblocks.go @@ -0,0 +1,40 @@ +package rpccontext + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/util/daghash" + "github.com/pkg/errors" +) + +// CollectChainBlocks creates a slice of chain blocks from the given hashes +func (ctx *Context) CollectChainBlocks(hashes []*daghash.Hash) ([]*appmessage.ChainBlock, error) { + chainBlocks := make([]*appmessage.ChainBlock, 0, len(hashes)) + for _, hash := range hashes { + acceptanceData, err := ctx.AcceptanceIndex.TxsAcceptanceData(hash) + if err != nil { + return nil, errors.Errorf("could not retrieve acceptance data for block %s", hash) + } + + acceptedBlocks := make([]*appmessage.AcceptedBlock, 0, len(acceptanceData)) + for _, blockAcceptanceData := range acceptanceData { + acceptedTxIds := make([]string, 0, len(blockAcceptanceData.TxAcceptanceData)) + for _, txAcceptanceData := range blockAcceptanceData.TxAcceptanceData { + if txAcceptanceData.IsAccepted { + acceptedTxIds = append(acceptedTxIds, txAcceptanceData.Tx.ID().String()) + } + } + acceptedBlock := &appmessage.AcceptedBlock{ + Hash: blockAcceptanceData.BlockHash.String(), + AcceptedTxIDs: acceptedTxIds, + } + acceptedBlocks = append(acceptedBlocks, acceptedBlock) + } + + chainBlock := &appmessage.ChainBlock{ + Hash: hash.String(), + AcceptedBlocks: acceptedBlocks, + } + chainBlocks = append(chainBlocks, chainBlock) + } + return chainBlocks, nil +} diff --git a/app/rpc/rpccontext/context.go b/app/rpc/rpccontext/context.go new file mode 100644 index 000000000..b997ee789 --- /dev/null +++ b/app/rpc/rpccontext/context.go @@ -0,0 +1,56 @@ +package rpccontext + +import ( + "github.com/kaspanet/kaspad/app/protocol" + "github.com/kaspanet/kaspad/domain/blockdag" + "github.com/kaspanet/kaspad/domain/blockdag/indexers" + "github.com/kaspanet/kaspad/domain/mempool" + "github.com/kaspanet/kaspad/domain/mining" + "github.com/kaspanet/kaspad/infrastructure/config" + "github.com/kaspanet/kaspad/infrastructure/network/addressmanager" + "github.com/kaspanet/kaspad/infrastructure/network/connmanager" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter" +) + +// Context represents the RPC context +type Context struct { + Config *config.Config + NetAdapter *netadapter.NetAdapter + DAG *blockdag.BlockDAG + ProtocolManager *protocol.Manager + ConnectionManager *connmanager.ConnectionManager + BlockTemplateGenerator *mining.BlkTmplGenerator + Mempool *mempool.TxPool + AddressManager *addressmanager.AddressManager + AcceptanceIndex *indexers.AcceptanceIndex + + BlockTemplateState *BlockTemplateState + NotificationManager *NotificationManager +} + +// NewContext creates a new RPC context +func NewContext( + cfg *config.Config, + netAdapter *netadapter.NetAdapter, + dag *blockdag.BlockDAG, + protocolManager *protocol.Manager, + connectionManager *connmanager.ConnectionManager, + blockTemplateGenerator *mining.BlkTmplGenerator, + mempool *mempool.TxPool, + addressManager *addressmanager.AddressManager, + acceptanceIndex *indexers.AcceptanceIndex) *Context { + context := &Context{ + Config: cfg, + NetAdapter: netAdapter, + DAG: dag, + ProtocolManager: protocolManager, + ConnectionManager: connectionManager, + BlockTemplateGenerator: blockTemplateGenerator, + Mempool: mempool, + AddressManager: addressManager, + AcceptanceIndex: acceptanceIndex, + } + context.BlockTemplateState = NewBlockTemplateState(context) + context.NotificationManager = NewNotificationManager() + return context +} diff --git a/app/rpc/rpccontext/log.go b/app/rpc/rpccontext/log.go new file mode 100644 index 000000000..077026970 --- /dev/null +++ b/app/rpc/rpccontext/log.go @@ -0,0 +1,9 @@ +package rpccontext + +import ( + "github.com/kaspanet/kaspad/infrastructure/logger" + "github.com/kaspanet/kaspad/util/panics" +) + +var log, _ = logger.Get(logger.SubsystemTags.RPCS) +var spawn = panics.GoroutineWrapperFunc(log) diff --git a/app/rpc/rpccontext/notificationmanager.go b/app/rpc/rpccontext/notificationmanager.go new file mode 100644 index 000000000..bb24dd45a --- /dev/null +++ b/app/rpc/rpccontext/notificationmanager.go @@ -0,0 +1,139 @@ +package rpccontext + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/pkg/errors" + "sync" +) + +// NotificationManager manages notifications for the RPC +type NotificationManager struct { + sync.RWMutex + listeners map[*router.Router]*NotificationListener +} + +// OnBlockAddedListener is a listener function for when a block is added to the DAG +type OnBlockAddedListener func(notification *appmessage.BlockAddedNotificationMessage) error + +// OnChainChangedListener is a listener function for when the DAG's selected parent chain changes +type OnChainChangedListener func(notification *appmessage.ChainChangedNotificationMessage) error + +// NotificationListener represents a registered RPC notification listener +type NotificationListener struct { + onBlockAddedListener OnBlockAddedListener + onBlockAddedNotificationChan chan *appmessage.BlockAddedNotificationMessage + onChainChangedListener OnChainChangedListener + onChainChangedNotificationChan chan *appmessage.ChainChangedNotificationMessage + + closeChan chan struct{} +} + +// NewNotificationManager creates a new NotificationManager +func NewNotificationManager() *NotificationManager { + return &NotificationManager{ + listeners: make(map[*router.Router]*NotificationListener), + } +} + +// AddListener registers a listener with the given router +func (nm *NotificationManager) AddListener(router *router.Router) *NotificationListener { + nm.Lock() + defer nm.Unlock() + + listener := newNotificationListener() + nm.listeners[router] = listener + return listener +} + +// RemoveListener unregisters the given router +func (nm *NotificationManager) RemoveListener(router *router.Router) { + nm.Lock() + defer nm.Unlock() + + listener, ok := nm.listeners[router] + if !ok { + return + } + listener.close() + + delete(nm.listeners, router) +} + +// Listener retrieves the listener registered with the given router +func (nm *NotificationManager) Listener(router *router.Router) (*NotificationListener, error) { + nm.RLock() + defer nm.RUnlock() + + listener, ok := nm.listeners[router] + if !ok { + return nil, errors.Errorf("listener not found") + } + return listener, nil +} + +// NotifyBlockAdded notifies the notification manager that a block has been added to the DAG +func (nm *NotificationManager) NotifyBlockAdded(notification *appmessage.BlockAddedNotificationMessage) { + nm.RLock() + defer nm.RUnlock() + + for _, listener := range nm.listeners { + if listener.onBlockAddedListener != nil { + select { + case listener.onBlockAddedNotificationChan <- notification: + case <-listener.closeChan: + continue + } + } + } +} + +// NotifyChainChanged notifies the notification manager that the DAG's selected parent chain has changed +func (nm *NotificationManager) NotifyChainChanged(message *appmessage.ChainChangedNotificationMessage) { + nm.RLock() + defer nm.RUnlock() + + for _, listener := range nm.listeners { + if listener.onChainChangedListener != nil { + select { + case listener.onChainChangedNotificationChan <- message: + case <-listener.closeChan: + continue + } + } + } +} + +func newNotificationListener() *NotificationListener { + return &NotificationListener{ + onBlockAddedNotificationChan: make(chan *appmessage.BlockAddedNotificationMessage), + onChainChangedNotificationChan: make(chan *appmessage.ChainChangedNotificationMessage), + closeChan: make(chan struct{}, 1), + } +} + +// SetOnBlockAddedListener sets the onBlockAddedListener handler for this listener +func (nl *NotificationListener) SetOnBlockAddedListener(onBlockAddedListener OnBlockAddedListener) { + nl.onBlockAddedListener = onBlockAddedListener +} + +// SetOnChainChangedListener sets the onChainChangedListener handler for this listener +func (nl *NotificationListener) SetOnChainChangedListener(onChainChangedListener OnChainChangedListener) { + nl.onChainChangedListener = onChainChangedListener +} + +// ProcessNextNotification waits until a notification arrives and processes it +func (nl *NotificationListener) ProcessNextNotification() error { + select { + case block := <-nl.onBlockAddedNotificationChan: + return nl.onBlockAddedListener(block) + case notification := <-nl.onChainChangedNotificationChan: + return nl.onChainChangedListener(notification) + case <-nl.closeChan: + return nil + } +} + +func (nl *NotificationListener) close() { + nl.closeChan <- struct{}{} +} diff --git a/app/rpc/rpccontext/verbosedata.go b/app/rpc/rpccontext/verbosedata.go new file mode 100644 index 000000000..4f1a11b7d --- /dev/null +++ b/app/rpc/rpccontext/verbosedata.go @@ -0,0 +1,246 @@ +package rpccontext + +import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/domain/dagconfig" + "github.com/kaspanet/kaspad/domain/txscript" + "github.com/kaspanet/kaspad/util" + "github.com/kaspanet/kaspad/util/daghash" + "github.com/kaspanet/kaspad/util/pointers" + "math/big" + "strconv" +) + +// BuildBlockVerboseData builds a BlockVerboseData from the given block. +// This method must be called with the DAG lock held for reads +func (ctx *Context) BuildBlockVerboseData(block *util.Block, includeTransactionVerboseData bool) (*appmessage.BlockVerboseData, error) { + hash := block.Hash() + params := ctx.DAG.Params + blockHeader := block.MsgBlock().Header + + blockBlueScore, err := ctx.DAG.BlueScoreByBlockHash(hash) + if err != nil { + return nil, err + } + + // Get the hashes for the next blocks unless there are none. + childHashes, err := ctx.DAG.ChildHashesByHash(hash) + if err != nil { + return nil, err + } + + blockConfirmations, err := ctx.DAG.BlockConfirmationsByHashNoLock(hash) + if err != nil { + return nil, err + } + + selectedParentHash, err := ctx.DAG.SelectedParentHash(hash) + if err != nil { + return nil, err + } + selectedParentHashStr := "" + if selectedParentHash != nil { + selectedParentHashStr = selectedParentHash.String() + } + + isChainBlock, err := ctx.DAG.IsInSelectedParentChain(hash) + if err != nil { + return nil, err + } + + acceptedBlockHashes, err := ctx.DAG.BluesByBlockHash(hash) + if err != nil { + return nil, err + } + + result := &appmessage.BlockVerboseData{ + Hash: hash.String(), + Version: blockHeader.Version, + VersionHex: fmt.Sprintf("%08x", blockHeader.Version), + HashMerkleRoot: blockHeader.HashMerkleRoot.String(), + AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot.String(), + UTXOCommitment: blockHeader.UTXOCommitment.String(), + ParentHashes: daghash.Strings(blockHeader.ParentHashes), + SelectedParentHash: selectedParentHashStr, + Nonce: blockHeader.Nonce, + Time: blockHeader.Timestamp.UnixMilliseconds(), + Confirmations: blockConfirmations, + BlueScore: blockBlueScore, + IsChainBlock: isChainBlock, + Size: int32(block.MsgBlock().SerializeSize()), + Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), + Difficulty: getDifficultyRatio(blockHeader.Bits, params), + ChildHashes: daghash.Strings(childHashes), + AcceptedBlockHashes: daghash.Strings(acceptedBlockHashes), + } + + transactions := block.Transactions() + txIDs := make([]string, len(transactions)) + for i, tx := range transactions { + txIDs[i] = tx.ID().String() + } + result.TxIDs = txIDs + + if includeTransactionVerboseData { + transactions := block.Transactions() + transactionVerboseData := make([]*appmessage.TransactionVerboseData, len(transactions)) + for i, tx := range transactions { + data, err := ctx.buildTransactionVerboseData(tx.MsgTx(), tx.ID().String(), + &blockHeader, hash.String(), nil, false) + if err != nil { + return nil, err + } + transactionVerboseData[i] = data + } + result.TransactionVerboseData = transactionVerboseData + } + + return result, nil +} + +// getDifficultyRatio returns the proof-of-work difficulty as a multiple of the +// minimum difficulty using the passed bits field from the header of a block. +func getDifficultyRatio(bits uint32, params *dagconfig.Params) float64 { + // The minimum difficulty is the max possible proof-of-work limit bits + // converted back to a number. Note this is not the same as the proof of + // work limit directly because the block difficulty is encoded in a block + // with the compact form which loses precision. + target := util.CompactToBig(bits) + + difficulty := new(big.Rat).SetFrac(params.PowMax, target) + outString := difficulty.FloatString(8) + diff, err := strconv.ParseFloat(outString, 64) + if err != nil { + log.Errorf("Cannot get difficulty: %s", err) + return 0 + } + return diff +} + +func (ctx *Context) buildTransactionVerboseData(mtx *appmessage.MsgTx, + txID string, blockHeader *appmessage.BlockHeader, blockHash string, + acceptingBlock *daghash.Hash, isInMempool bool) (*appmessage.TransactionVerboseData, error) { + + mtxHex, err := msgTxToHex(mtx) + if err != nil { + return nil, err + } + + var payloadHash string + if mtx.PayloadHash != nil { + payloadHash = mtx.PayloadHash.String() + } + + txReply := &appmessage.TransactionVerboseData{ + Hex: mtxHex, + TxID: txID, + Hash: mtx.TxHash().String(), + Size: int32(mtx.SerializeSize()), + TransactionVerboseInputs: ctx.buildTransactionVerboseInputs(mtx), + TransactionVerboseOutputs: ctx.buildTransactionVerboseOutputs(mtx, nil), + Version: mtx.Version, + LockTime: mtx.LockTime, + SubnetworkID: mtx.SubnetworkID.String(), + Gas: mtx.Gas, + PayloadHash: payloadHash, + Payload: hex.EncodeToString(mtx.Payload), + } + + if blockHeader != nil { + txReply.Time = uint64(blockHeader.Timestamp.UnixMilliseconds()) + txReply.BlockTime = uint64(blockHeader.Timestamp.UnixMilliseconds()) + txReply.BlockHash = blockHash + } + + txReply.IsInMempool = isInMempool + if acceptingBlock != nil { + txReply.AcceptedBy = acceptingBlock.String() + } + + return txReply, nil +} + +// msgTxToHex serializes a transaction using the latest protocol version and +// returns a hex-encoded string of the result. +func msgTxToHex(msgTx *appmessage.MsgTx) (string, error) { + var buf bytes.Buffer + err := msgTx.KaspaEncode(&buf, 0) + if err != nil { + return "", err + } + + return hex.EncodeToString(buf.Bytes()), nil +} + +func (ctx *Context) buildTransactionVerboseInputs(mtx *appmessage.MsgTx) []*appmessage.TransactionVerboseInput { + inputs := make([]*appmessage.TransactionVerboseInput, len(mtx.TxIn)) + for i, txIn := range mtx.TxIn { + // The disassembled string will contain [error] inline + // if the script doesn't fully parse, so ignore the + // error here. + disbuf, _ := txscript.DisasmString(txIn.SignatureScript) + + input := &appmessage.TransactionVerboseInput{} + input.TxID = txIn.PreviousOutpoint.TxID.String() + input.OutputIndex = txIn.PreviousOutpoint.Index + input.Sequence = txIn.Sequence + input.ScriptSig = &appmessage.ScriptSig{ + Asm: disbuf, + Hex: hex.EncodeToString(txIn.SignatureScript), + } + inputs[i] = input + } + + return inputs +} + +// buildTransactionVerboseOutputs returns a slice of JSON objects for the outputs of the passed +// transaction. +func (ctx *Context) buildTransactionVerboseOutputs(mtx *appmessage.MsgTx, filterAddrMap map[string]struct{}) []*appmessage.TransactionVerboseOutput { + outputs := make([]*appmessage.TransactionVerboseOutput, len(mtx.TxOut)) + for i, v := range mtx.TxOut { + // The disassembled string will contain [error] inline if the + // script doesn't fully parse, so ignore the error here. + disbuf, _ := txscript.DisasmString(v.ScriptPubKey) + + // Ignore the error here since an error means the script + // couldn't parse and there is no additional information about + // it anyways. + scriptClass, addr, _ := txscript.ExtractScriptPubKeyAddress( + v.ScriptPubKey, ctx.DAG.Params) + + // Encode the addresses while checking if the address passes the + // filter when needed. + passesFilter := len(filterAddrMap) == 0 + var encodedAddr string + if addr != nil { + encodedAddr = *pointers.String(addr.EncodeAddress()) + + // If the filter doesn't already pass, make it pass if + // the address exists in the filter. + if _, exists := filterAddrMap[encodedAddr]; exists { + passesFilter = true + } + } + + if !passesFilter { + continue + } + + output := &appmessage.TransactionVerboseOutput{} + output.Index = uint32(i) + output.Value = v.Value + output.ScriptPubKey = &appmessage.ScriptPubKeyResult{ + Address: encodedAddr, + Asm: disbuf, + Hex: hex.EncodeToString(v.ScriptPubKey), + Type: scriptClass.String(), + } + outputs[i] = output + } + + return outputs +} diff --git a/app/rpc/rpchandlers/add_peer.go b/app/rpc/rpchandlers/add_peer.go new file mode 100644 index 000000000..5f64276c5 --- /dev/null +++ b/app/rpc/rpchandlers/add_peer.go @@ -0,0 +1,24 @@ +package rpchandlers + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/kaspanet/kaspad/util/network" +) + +// HandleAddPeer handles the respectively named RPC command +func HandleAddPeer(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) { + AddPeerRequest := request.(*appmessage.AddPeerRequestMessage) + address, err := network.NormalizeAddress(AddPeerRequest.Address, context.DAG.Params.DefaultPort) + if err != nil { + errorMessage := &appmessage.AddPeerResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Could not parse address: %s", err) + return errorMessage, nil + } + + context.ConnectionManager.AddConnectionRequest(address, AddPeerRequest.IsPermanent) + + response := appmessage.NewAddPeerResponseMessage() + return response, nil +} diff --git a/app/rpc/rpchandlers/get_block.go b/app/rpc/rpchandlers/get_block.go new file mode 100644 index 000000000..6e6c2bbb3 --- /dev/null +++ b/app/rpc/rpchandlers/get_block.go @@ -0,0 +1,98 @@ +package rpchandlers + +import ( + "bufio" + "bytes" + "encoding/hex" + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/kaspanet/kaspad/util/daghash" + "github.com/kaspanet/kaspad/util/subnetworkid" +) + +// HandleGetBlock handles the respectively named RPC command +func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) { + getBlockRequest := request.(*appmessage.GetBlockRequestMessage) + + // Load the raw block bytes from the database. + hash, err := daghash.NewHashFromStr(getBlockRequest.Hash) + if err != nil { + errorMessage := &appmessage.GetBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Hash could not be parsed: %s", err) + return errorMessage, nil + } + + context.DAG.RLock() + defer context.DAG.RUnlock() + + if context.DAG.IsKnownInvalid(hash) { + errorMessage := &appmessage.GetBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Block %s is known to be invalid", hash) + return errorMessage, nil + } + if context.DAG.IsKnownOrphan(hash) { + errorMessage := &appmessage.GetBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Block %s is an orphan", hash) + return errorMessage, nil + } + block, err := context.DAG.BlockByHash(hash) + if err != nil { + errorMessage := &appmessage.GetBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Block %s not found", hash) + return errorMessage, nil + } + + blockBytes, err := block.Bytes() + if err != nil { + return nil, err + } + + // Handle partial blocks + if getBlockRequest.SubnetworkID != "" { + requestSubnetworkID, err := subnetworkid.NewFromStr(getBlockRequest.SubnetworkID) + if err != nil { + errorMessage := &appmessage.GetBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("SubnetworkID could not be parsed: %s", err) + return errorMessage, nil + } + nodeSubnetworkID := context.Config.SubnetworkID + + if requestSubnetworkID != nil { + if nodeSubnetworkID != nil { + if !nodeSubnetworkID.IsEqual(requestSubnetworkID) { + errorMessage := &appmessage.GetBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("subnetwork %s does not match this partial node", + getBlockRequest.SubnetworkID) + return errorMessage, nil + } + // nothing to do - partial node stores partial blocks + } else { + // Deserialize the block. + msgBlock := block.MsgBlock() + msgBlock.ConvertToPartial(requestSubnetworkID) + var b bytes.Buffer + err := msgBlock.Serialize(bufio.NewWriter(&b)) + if err != nil { + return nil, err + } + blockBytes = b.Bytes() + } + } + } + + response := appmessage.NewGetBlockResponseMessage() + + if getBlockRequest.IncludeBlockHex { + response.BlockHex = hex.EncodeToString(blockBytes) + } + if getBlockRequest.IncludeBlockVerboseData { + blockVerboseData, err := context.BuildBlockVerboseData(block, getBlockRequest.IncludeTransactionVerboseData) + if err != nil { + return nil, err + } + response.BlockVerboseData = blockVerboseData + } + + return response, nil +} diff --git a/app/rpc/rpchandlers/get_block_template.go b/app/rpc/rpchandlers/get_block_template.go new file mode 100644 index 000000000..494833afe --- /dev/null +++ b/app/rpc/rpchandlers/get_block_template.go @@ -0,0 +1,86 @@ +package rpchandlers + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/kaspanet/kaspad/util" +) + +// HandleGetBlockTemplate handles the respectively named RPC command +func HandleGetBlockTemplate(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) { + getBlockTemplateRequest := request.(*appmessage.GetBlockTemplateRequestMessage) + + payAddress, err := util.DecodeAddress(getBlockTemplateRequest.PayAddress, context.DAG.Params.Prefix) + if err != nil { + errorMessage := &appmessage.GetBlockTemplateResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Could not decode address: %s", err) + return errorMessage, nil + } + + // When a long poll ID was provided, this is a long poll request by the + // client to be notified when block template referenced by the ID should + // be replaced with a new one. + if getBlockTemplateRequest.LongPollID != "" { + return handleGetBlockTemplateLongPoll(context, getBlockTemplateRequest.LongPollID, payAddress) + } + + // Protect concurrent access when updating block templates. + context.BlockTemplateState.Lock() + defer context.BlockTemplateState.Unlock() + + // Get and return a block template. A new block template will be + // generated when the current best block has changed or the transactions + // in the memory pool have been updated and it has been at least five + // seconds since the last template was generated. Otherwise, the + // timestamp for the existing block template is updated (and possibly + // the difficulty on testnet per the consesus rules). + err = context.BlockTemplateState.Update(payAddress) + if err != nil { + return nil, err + } + return context.BlockTemplateState.Response() +} + +// handleGetBlockTemplateLongPoll is a helper for handleGetBlockTemplateRequest +// which deals with handling long polling for block templates. When a caller +// sends a request with a long poll ID that was previously returned, a response +// is not sent until the caller should stop working on the previous block +// template in favor of the new one. In particular, this is the case when the +// old block template is no longer valid due to a solution already being found +// and added to the block DAG, or new transactions have shown up and some time +// has passed without finding a solution. +func handleGetBlockTemplateLongPoll(context *rpccontext.Context, longPollID string, + payAddress util.Address) (*appmessage.GetBlockTemplateResponseMessage, error) { + state := context.BlockTemplateState + + result, longPollChan, err := state.BlockTemplateOrLongPollChan(longPollID, payAddress) + if err != nil { + return nil, err + } + + if result != nil { + return result, nil + } + + // Wait until signal received to send the reply. + <-longPollChan + + // Get the lastest block template + state.Lock() + defer state.Unlock() + + if err := state.Update(payAddress); err != nil { + return nil, err + } + + // Include whether or not it is valid to submit work against the old + // block template depending on whether or not a solution has already + // been found and added to the block DAG. + result, err = state.Response() + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/app/rpc/rpchandlers/get_blocks.go b/app/rpc/rpchandlers/get_blocks.go new file mode 100644 index 000000000..9a2966e8e --- /dev/null +++ b/app/rpc/rpchandlers/get_blocks.go @@ -0,0 +1,118 @@ +package rpchandlers + +import ( + "encoding/hex" + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/kaspanet/kaspad/util" + "github.com/kaspanet/kaspad/util/daghash" +) + +const ( + // maxBlocksInGetBlocksResponse is the max amount of blocks that are + // allowed in a GetBlocksResult. + maxBlocksInGetBlocksResponse = 1000 +) + +// HandleGetBlocks handles the respectively named RPC command +func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) { + getBlocksRequest := request.(*appmessage.GetBlocksRequestMessage) + + var lowHash *daghash.Hash + if getBlocksRequest.LowHash != "" { + lowHash = &daghash.Hash{} + err := daghash.Decode(lowHash, getBlocksRequest.LowHash) + if err != nil { + errorMessage := &appmessage.GetBlocksResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Could not parse lowHash: %s", err) + return errorMessage, nil + } + } + + context.DAG.RLock() + defer context.DAG.RUnlock() + + // If lowHash is not in the DAG, there's nothing to do; return an error. + if lowHash != nil && !context.DAG.IsKnownBlock(lowHash) { + errorMessage := &appmessage.GetBlocksResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Block %s not found in DAG", lowHash) + return errorMessage, nil + } + + // Retrieve the block hashes. + blockHashes, err := context.DAG.BlockHashesFrom(lowHash, maxBlocksInGetBlocksResponse) + if err != nil { + return nil, err + } + + // Convert the hashes to strings + hashes := make([]string, len(blockHashes)) + for i, blockHash := range blockHashes { + hashes[i] = blockHash.String() + } + + // Include more data if requested + var blockHexes []string + var blockVerboseData []*appmessage.BlockVerboseData + if getBlocksRequest.IncludeBlockHexes || getBlocksRequest.IncludeBlockVerboseData { + blockBytesSlice, err := hashesToBlockBytes(context, blockHashes) + if err != nil { + return nil, err + } + if getBlocksRequest.IncludeBlockHexes { + blockHexes = blockBytesToStrings(blockBytesSlice) + } + if getBlocksRequest.IncludeBlockVerboseData { + data, err := blockBytesToBlockVerboseResults(context, blockBytesSlice, getBlocksRequest.IncludeBlockVerboseData) + if err != nil { + return nil, err + } + blockVerboseData = data + } + } + + response := appmessage.NewGetBlocksResponseMessage(hashes, blockHexes, blockVerboseData) + return response, nil +} + +func hashesToBlockBytes(context *rpccontext.Context, hashes []*daghash.Hash) ([][]byte, error) { + blocks := make([][]byte, len(hashes)) + for i, hash := range hashes { + block, err := context.DAG.BlockByHash(hash) + if err != nil { + return nil, err + } + blockBytes, err := block.Bytes() + if err != nil { + return nil, err + } + blocks[i] = blockBytes + } + return blocks, nil +} +func blockBytesToStrings(blockBytesSlice [][]byte) []string { + rawBlocks := make([]string, len(blockBytesSlice)) + for i, blockBytes := range blockBytesSlice { + rawBlocks[i] = hex.EncodeToString(blockBytes) + } + return rawBlocks +} + +func blockBytesToBlockVerboseResults(context *rpccontext.Context, blockBytesSlice [][]byte, + includeTransactionVerboseData bool) ([]*appmessage.BlockVerboseData, error) { + + verboseBlocks := make([]*appmessage.BlockVerboseData, len(blockBytesSlice)) + for i, blockBytes := range blockBytesSlice { + block, err := util.NewBlockFromBytes(blockBytes) + if err != nil { + return nil, err + } + getBlockVerboseResult, err := context.BuildBlockVerboseData(block, includeTransactionVerboseData) + if err != nil { + return nil, err + } + verboseBlocks[i] = getBlockVerboseResult + } + return verboseBlocks, nil +} diff --git a/app/rpc/rpchandlers/get_chain_from_block.go b/app/rpc/rpchandlers/get_chain_from_block.go new file mode 100644 index 000000000..aaad7f327 --- /dev/null +++ b/app/rpc/rpchandlers/get_chain_from_block.go @@ -0,0 +1,106 @@ +package rpchandlers + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/kaspanet/kaspad/util/daghash" + "github.com/pkg/errors" +) + +const ( + // maxBlocksInGetChainFromBlockResponse is the max amount of blocks that + // are allowed in a GetChainFromBlockResponse. + maxBlocksInGetChainFromBlockResponse = 1000 +) + +// HandleGetChainFromBlock handles the respectively named RPC command +func HandleGetChainFromBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) { + getChainFromBlockRequest := request.(*appmessage.GetChainFromBlockRequestMessage) + + if context.AcceptanceIndex == nil { + errorMessage := &appmessage.GetChainFromBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("The acceptance index must be " + + "enabled to get the selected parent chain " + + "(specify --acceptanceindex)") + return errorMessage, nil + } + + var startHash *daghash.Hash + if getChainFromBlockRequest.StartHash != "" { + startHash = &daghash.Hash{} + err := daghash.Decode(startHash, getChainFromBlockRequest.StartHash) + if err != nil { + errorMessage := &appmessage.GetChainFromBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Could not parse startHash: %s", err) + return errorMessage, nil + } + } + + context.DAG.RLock() + defer context.DAG.RUnlock() + + // If startHash is not in the selected parent chain, there's nothing + // to do; return an error. + if startHash != nil && !context.DAG.IsInDAG(startHash) { + errorMessage := &appmessage.GetChainFromBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Block %s not found in the DAG", startHash) + return errorMessage, nil + } + + // Retrieve the selected parent chain. + removedChainHashes, addedChainHashes, err := context.DAG.SelectedParentChain(startHash) + if err != nil { + return nil, err + } + + // Limit the amount of blocks in the response + if len(addedChainHashes) > maxBlocksInGetChainFromBlockResponse { + addedChainHashes = addedChainHashes[:maxBlocksInGetChainFromBlockResponse] + } + + // Collect addedChainBlocks. + addedChainBlocks, err := context.CollectChainBlocks(addedChainHashes) + if err != nil { + errorMessage := &appmessage.GetChainFromBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Could not collect chain blocks: %s", err) + return errorMessage, nil + } + + // Collect removedHashes. + removedHashes := make([]string, len(removedChainHashes)) + for i, hash := range removedChainHashes { + removedHashes[i] = hash.String() + } + + // If the user specified to include the blocks, collect them as well. + var blockVerboseData []*appmessage.BlockVerboseData + if getChainFromBlockRequest.IncludeBlockVerboseData { + data, err := hashesToBlockVerboseData(context, addedChainHashes) + if err != nil { + return nil, err + } + blockVerboseData = data + } + + response := appmessage.NewGetChainFromBlockResponseMessage(removedHashes, addedChainBlocks, blockVerboseData) + return response, nil +} + +// hashesToBlockVerboseData takes block hashes and returns their +// correspondent block verbose. +func hashesToBlockVerboseData(context *rpccontext.Context, hashes []*daghash.Hash) ([]*appmessage.BlockVerboseData, error) { + getBlockVerboseResults := make([]*appmessage.BlockVerboseData, 0, len(hashes)) + for _, blockHash := range hashes { + block, err := context.DAG.BlockByHash(blockHash) + if err != nil { + return nil, errors.Errorf("could not retrieve block %s.", blockHash) + } + getBlockVerboseResult, err := context.BuildBlockVerboseData(block, false) + if err != nil { + return nil, errors.Wrapf(err, "could not build getBlockVerboseResult for block %s", blockHash) + } + getBlockVerboseResults = append(getBlockVerboseResults, getBlockVerboseResult) + } + return getBlockVerboseResults, nil +} diff --git a/app/rpc/rpchandlers/get_connected_peer_info.go b/app/rpc/rpchandlers/get_connected_peer_info.go new file mode 100644 index 000000000..130a4a7d7 --- /dev/null +++ b/app/rpc/rpchandlers/get_connected_peer_info.go @@ -0,0 +1,31 @@ +package rpchandlers + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" +) + +// HandleGetConnectedPeerInfo handles the respectively named RPC command +func HandleGetConnectedPeerInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) { + peers := context.ProtocolManager.Peers() + ibdPeer := context.ProtocolManager.IBDPeer() + infos := make([]*appmessage.GetConnectedPeerInfoMessage, 0, len(peers)) + for _, peer := range peers { + info := &appmessage.GetConnectedPeerInfoMessage{ + ID: peer.ID().String(), + Address: peer.Address(), + LastPingDuration: peer.LastPingDuration().Milliseconds(), + SelectedTipHash: peer.SelectedTipHash().String(), + IsSyncNode: peer == ibdPeer, + IsOutbound: peer.IsOutbound(), + TimeOffset: peer.TimeOffset().Milliseconds(), + UserAgent: peer.UserAgent(), + AdvertisedProtocolVersion: peer.AdvertisedProtocolVersion(), + TimeConnected: peer.TimeConnected().Milliseconds(), + } + infos = append(infos, info) + } + response := appmessage.NewGetConnectedPeerInfoResponseMessage(infos) + return response, nil +} diff --git a/app/rpc/rpchandlers/get_current_network.go b/app/rpc/rpchandlers/get_current_network.go new file mode 100644 index 000000000..4edd63ec5 --- /dev/null +++ b/app/rpc/rpchandlers/get_current_network.go @@ -0,0 +1,13 @@ +package rpchandlers + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" +) + +// HandleGetCurrentNetwork handles the respectively named RPC command +func HandleGetCurrentNetwork(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) { + response := appmessage.NewGetCurrentNetworkResponseMessage(context.DAG.Params.Net.String()) + return response, nil +} diff --git a/app/rpc/rpchandlers/get_mempool_entry.go b/app/rpc/rpchandlers/get_mempool_entry.go new file mode 100644 index 000000000..dfb1cfab1 --- /dev/null +++ b/app/rpc/rpchandlers/get_mempool_entry.go @@ -0,0 +1,29 @@ +package rpchandlers + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/kaspanet/kaspad/util/daghash" +) + +// HandleGetMempoolEntry handles the respectively named RPC command +func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) { + getMempoolEntryRequest := request.(*appmessage.GetMempoolEntryRequestMessage) + txID, err := daghash.NewTxIDFromStr(getMempoolEntryRequest.TxID) + if err != nil { + errorMessage := &appmessage.GetMempoolEntryResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Could not parse txId: %s", err) + return errorMessage, nil + } + + _, ok := context.Mempool.FetchTxDesc(txID) + if !ok { + errorMessage := &appmessage.GetMempoolEntryResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("transaction is not in the pool") + return errorMessage, nil + } + + response := appmessage.NewGetMempoolEntryResponseMessage() + return response, nil +} diff --git a/app/rpc/rpchandlers/get_peer_addresses.go b/app/rpc/rpchandlers/get_peer_addresses.go new file mode 100644 index 000000000..849303442 --- /dev/null +++ b/app/rpc/rpchandlers/get_peer_addresses.go @@ -0,0 +1,21 @@ +package rpchandlers + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" +) + +// HandleGetPeerAddresses handles the respectively named RPC command +func HandleGetPeerAddresses(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) { + peersState, err := context.AddressManager.PeersStateForSerialization() + if err != nil { + return nil, err + } + addresses := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(peersState.Addresses)) + for i, address := range peersState.Addresses { + addresses[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: string(address.Address)} + } + response := appmessage.NewGetPeerAddressesResponseMessage(addresses) + return response, nil +} diff --git a/app/rpc/rpchandlers/get_selected_tip_hash.go b/app/rpc/rpchandlers/get_selected_tip_hash.go new file mode 100644 index 000000000..1e459a590 --- /dev/null +++ b/app/rpc/rpchandlers/get_selected_tip_hash.go @@ -0,0 +1,13 @@ +package rpchandlers + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" +) + +// HandleGetSelectedTipHash handles the respectively named RPC command +func HandleGetSelectedTipHash(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) { + response := appmessage.NewGetSelectedTipHashResponseMessage(context.DAG.SelectedTipHash().String()) + return response, nil +} diff --git a/app/rpc/rpchandlers/get_subnetwork.go b/app/rpc/rpchandlers/get_subnetwork.go new file mode 100644 index 000000000..e6f493a91 --- /dev/null +++ b/app/rpc/rpchandlers/get_subnetwork.go @@ -0,0 +1,34 @@ +package rpchandlers + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/kaspanet/kaspad/util/subnetworkid" +) + +// HandleGetSubnetwork handles the respectively named RPC command +func HandleGetSubnetwork(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) { + getSubnetworkRequest := request.(*appmessage.GetSubnetworkRequestMessage) + + subnetworkID, err := subnetworkid.NewFromStr(getSubnetworkRequest.SubnetworkID) + if err != nil { + errorMessage := &appmessage.GetSubnetworkResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Could not parse subnetworkID: %s", err) + return errorMessage, nil + } + + var gasLimit uint64 + if !subnetworkID.IsBuiltInOrNative() { + limit, err := context.DAG.GasLimit(subnetworkID) + if err != nil { + errorMessage := &appmessage.GetSubnetworkResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Subnetwork %s not found.", subnetworkID) + return errorMessage, nil + } + gasLimit = limit + } + + response := appmessage.NewGetSubnetworkResponseMessage(gasLimit) + return response, nil +} diff --git a/app/rpc/rpchandlers/log.go b/app/rpc/rpchandlers/log.go new file mode 100644 index 000000000..f9c6effe7 --- /dev/null +++ b/app/rpc/rpchandlers/log.go @@ -0,0 +1,7 @@ +package rpchandlers + +import ( + "github.com/kaspanet/kaspad/infrastructure/logger" +) + +var log, _ = logger.Get(logger.SubsystemTags.RPCS) diff --git a/app/rpc/rpchandlers/notify_block_added.go b/app/rpc/rpchandlers/notify_block_added.go new file mode 100644 index 000000000..b86ef03b8 --- /dev/null +++ b/app/rpc/rpchandlers/notify_block_added.go @@ -0,0 +1,21 @@ +package rpchandlers + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" +) + +// HandleNotifyBlockAdded handles the respectively named RPC command +func HandleNotifyBlockAdded(context *rpccontext.Context, router *router.Router, _ appmessage.Message) (appmessage.Message, error) { + listener, err := context.NotificationManager.Listener(router) + if err != nil { + return nil, err + } + listener.SetOnBlockAddedListener(func(notification *appmessage.BlockAddedNotificationMessage) error { + return router.OutgoingRoute().Enqueue(notification) + }) + + response := appmessage.NewNotifyBlockAddedResponseMessage() + return response, nil +} diff --git a/app/rpc/rpchandlers/notify_chain_changed.go b/app/rpc/rpchandlers/notify_chain_changed.go new file mode 100644 index 000000000..872c9dced --- /dev/null +++ b/app/rpc/rpchandlers/notify_chain_changed.go @@ -0,0 +1,27 @@ +package rpchandlers + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" +) + +// HandleNotifyChainChanged handles the respectively named RPC command +func HandleNotifyChainChanged(context *rpccontext.Context, router *router.Router, _ appmessage.Message) (appmessage.Message, error) { + if context.AcceptanceIndex == nil { + errorMessage := appmessage.NewNotifyChainChangedResponseMessage() + errorMessage.Error = appmessage.RPCErrorf("Acceptance index is not available") + return errorMessage, nil + } + + listener, err := context.NotificationManager.Listener(router) + if err != nil { + return nil, err + } + listener.SetOnChainChangedListener(func(message *appmessage.ChainChangedNotificationMessage) error { + return router.OutgoingRoute().Enqueue(message) + }) + + response := appmessage.NewNotifyChainChangedResponseMessage() + return response, nil +} diff --git a/app/rpc/rpchandlers/submit_block.go b/app/rpc/rpchandlers/submit_block.go new file mode 100644 index 000000000..41ec9d678 --- /dev/null +++ b/app/rpc/rpchandlers/submit_block.go @@ -0,0 +1,41 @@ +package rpchandlers + +import ( + "encoding/hex" + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/domain/blockdag" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/kaspanet/kaspad/util" +) + +// HandleSubmitBlock handles the respectively named RPC command +func HandleSubmitBlock(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) { + submitBlockRequest := request.(*appmessage.SubmitBlockRequestMessage) + + // Deserialize the submitted block. + serializedBlock, err := hex.DecodeString(submitBlockRequest.BlockHex) + if err != nil { + errorMessage := &appmessage.SubmitBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Block hex could not be parsed: %s", err) + return errorMessage, nil + } + block, err := util.NewBlockFromBytes(serializedBlock) + if err != nil { + errorMessage := &appmessage.SubmitBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Block decode failed: %s", err) + return errorMessage, nil + } + + err = context.ProtocolManager.AddBlock(block, blockdag.BFDisallowDelay|blockdag.BFDisallowOrphans) + if err != nil { + errorMessage := &appmessage.SubmitBlockResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Block rejected. Reason: %s", err) + return errorMessage, nil + } + + log.Infof("Accepted block %s via submitBlock", block.Hash()) + + response := appmessage.NewSubmitBlockResponseMessage() + return response, nil +} diff --git a/app/rpc/rpchandlers/submit_transaction.go b/app/rpc/rpchandlers/submit_transaction.go new file mode 100644 index 000000000..a6c2148c5 --- /dev/null +++ b/app/rpc/rpchandlers/submit_transaction.go @@ -0,0 +1,47 @@ +package rpchandlers + +import ( + "bytes" + "encoding/hex" + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/app/rpc/rpccontext" + "github.com/kaspanet/kaspad/domain/mempool" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/kaspanet/kaspad/util" + "github.com/pkg/errors" +) + +// HandleSubmitTransaction handles the respectively named RPC command +func HandleSubmitTransaction(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) { + submitTransactionRequest := request.(*appmessage.SubmitTransactionRequestMessage) + + serializedTx, err := hex.DecodeString(submitTransactionRequest.TransactionHex) + if err != nil { + errorMessage := &appmessage.SubmitTransactionResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Transaction hex could not be parsed: %s", err) + return errorMessage, nil + } + var msgTx appmessage.MsgTx + err = msgTx.Deserialize(bytes.NewReader(serializedTx)) + if err != nil { + errorMessage := &appmessage.SubmitTransactionResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Transaction decode failed: %s", err) + return errorMessage, nil + } + + tx := util.NewTx(&msgTx) + err = context.ProtocolManager.AddTransaction(tx) + if err != nil { + if !errors.As(err, &mempool.RuleError{}) { + return nil, err + } + + log.Debugf("Rejected transaction %s: %s", tx.ID(), err) + errorMessage := &appmessage.SubmitTransactionResponseMessage{} + errorMessage.Error = appmessage.RPCErrorf("Rejected transaction %s: %s", tx.ID(), err) + return errorMessage, nil + } + + response := appmessage.NewSubmitTransactionResponseMessage(tx.ID().String()) + return response, nil +} diff --git a/cmd/kaspactl/config.go b/cmd/kaspactl/config.go index 01b3612d4..886227344 100644 --- a/cmd/kaspactl/config.go +++ b/cmd/kaspactl/config.go @@ -1,298 +1,41 @@ -// Copyright (c) 2013-2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - package main import ( - "fmt" "github.com/kaspanet/kaspad/infrastructure/config" - "github.com/kaspanet/kaspad/version" "github.com/pkg/errors" - "io/ioutil" - "os" - "path/filepath" - "regexp" - "strings" "github.com/jessevdk/go-flags" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util" -) - -const ( - // unusableFlags are the command usage flags which this utility are not - // able to use. In particular it doesn't support websockets and - // consequently notifications. - unusableFlags = model.UFWebsocketOnly | model.UFNotification ) var ( - kaspadHomeDir = util.AppDataDir("kaspad", false) - kaspactlHomeDir = util.AppDataDir("kaspactl", false) - defaultConfigFile = filepath.Join(kaspactlHomeDir, "kaspactl.conf") - defaultRPCServer = "localhost" - defaultRPCCertFile = filepath.Join(kaspadHomeDir, "rpc.cert") - activeConfig *ConfigFlags + defaultRPCServer = "localhost" ) -// listCommands categorizes and lists all of the usable commands along with -// their one-line usage. -func listCommands() { - const ( - categoryDAG uint8 = iota - numCategories - ) - - // Get a list of registered commands and categorize and filter them. - cmdMethods := model.RegisteredCmdMethods() - categorized := make([][]string, numCategories) - for _, method := range cmdMethods { - flags, err := model.MethodUsageFlags(method) - if err != nil { - // This should never happen since the method was just - // returned from the package, but be safe. - continue - } - - // Skip the commands that aren't usable from this utility. - if flags&unusableFlags != 0 { - continue - } - - usage, err := model.MethodUsageText(method) - if err != nil { - // This should never happen since the method was just - // returned from the package, but be safe. - continue - } - - // Categorize the command based on the usage flags. - category := categoryDAG - categorized[category] = append(categorized[category], usage) - } - - // Display the command according to their categories. - categoryTitles := make([]string, numCategories) - categoryTitles[categoryDAG] = "DAG Server Commands:" - for category := uint8(0); category < numCategories; category++ { - fmt.Println(categoryTitles[category]) - for _, usage := range categorized[category] { - fmt.Println(usage) - } - fmt.Println() - } -} - -// ConfigFlags defines the configuration options for kaspactl. -// -// See loadConfig for details on the configuration load process. -type ConfigFlags struct { - ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"` - ListCommands bool `short:"l" long:"listcommands" description:"List all of the supported commands and exit"` - ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"` - RPCUser string `short:"u" long:"rpcuser" description:"RPC username"` - RPCPassword string `short:"P" long:"rpcpass" default-mask:"-" description:"RPC password"` - RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"` - RPCCert string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"` - NoTLS bool `long:"notls" description:"Disable TLS"` - Proxy string `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"` - ProxyUser string `long:"proxyuser" description:"Username for proxy server"` - ProxyPass string `long:"proxypass" default-mask:"-" description:"Password for proxy server"` - TLSSkipVerify bool `long:"skipverify" description:"Do not verify tls certificates (not recommended!)"` +type configFlags struct { + RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"` + RequestJSON string `description:"The request in JSON format"` config.NetworkFlags } -// cleanAndExpandPath expands environement variables and leading ~ in the -// passed path, cleans the result, and returns it. -func cleanAndExpandPath(path string) string { - // Expand initial ~ to OS specific home directory. - if strings.HasPrefix(path, "~") { - homeDir := filepath.Dir(kaspactlHomeDir) - path = strings.Replace(path, "~", homeDir, 1) +func parseConfig() (*configFlags, error) { + cfg := &configFlags{ + RPCServer: defaultRPCServer, + } + parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag) + args, err := parser.Parse() + if err != nil { + return nil, err } - // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%, - // but they variables can still be expanded via POSIX-style $VARIABLE. - return filepath.Clean(os.ExpandEnv(path)) -} - -// loadConfig initializes and parses the config using a config file and command -// line options. -// -// The configuration proceeds as follows: -// 1) Start with a default config with sane settings -// 2) Pre-parse the command line to check for an alternative config file -// 3) Load configuration file overwriting defaults with any specified options -// 4) Parse CLI options and overwrite/add any specified options -// -// The above results in functioning properly without any config settings -// while still allowing the user to override settings with config files and -// command line options. Command line options always take precedence. -func loadConfig() (*ConfigFlags, []string, error) { - // Default config. - activeConfig = &ConfigFlags{ - ConfigFile: defaultConfigFile, - RPCServer: defaultRPCServer, - RPCCert: defaultRPCCertFile, - } - - // Pre-parse the command line options to see if an alternative config - // file, the version flag, or the list commands flag was specified. Any - // errors aside from the help message error can be ignored here since - // they will be caught by the final parse below. - preCfg := activeConfig - preParser := flags.NewParser(preCfg, flags.HelpFlag) - _, err := preParser.Parse() - if err != nil { - var flagsErr *flags.Error - if ok := errors.As(err, &flagsErr); ok && flagsErr.Type == flags.ErrHelp { - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, "") - fmt.Fprintln(os.Stderr, "The special parameter `-` "+ - "indicates that a parameter should be read "+ - "from the\nnext unread line from standard "+ - "input.") - return nil, nil, err - } - } - - // Show the version and exit if the version flag was specified. - appName := filepath.Base(os.Args[0]) - appName = strings.TrimSuffix(appName, filepath.Ext(appName)) - usageMessage := fmt.Sprintf("Use %s -h to show options", appName) - if preCfg.ShowVersion { - fmt.Println(appName, "version", version.Version()) - os.Exit(0) - } - - // Show the available commands and exit if the associated flag was - // specified. - if preCfg.ListCommands { - listCommands() - os.Exit(0) - } - - // If no rpc user and password were configured, create - // a kaspactl default config file based on the rpc login - // details written in the RPC server configuration file - if preCfg.RPCUser == "" && preCfg.RPCPassword == "" { - if _, err := os.Stat(preCfg.ConfigFile); os.IsNotExist(err) { - serverConfigPath := filepath.Join(kaspadHomeDir, "kaspad.conf") - err := createDefaultConfigFile(preCfg.ConfigFile, serverConfigPath) - if err != nil { - fmt.Fprintf(os.Stderr, "Error creating a default config file: %s\n", err) - } - } - } - - // Load additional config from file. - parser := flags.NewParser(activeConfig, flags.Default) - err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile) - if err != nil { - if pErr := &(os.PathError{}); !errors.As(err, &pErr) { - fmt.Fprintf(os.Stderr, "Error parsing config file: %s\n", - err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, nil, err - } - } - - // Parse command line options again to ensure they take precedence. - remainingArgs, err := parser.Parse() - if err != nil { - var flagsErr *flags.Error - if ok := errors.As(err, &flagsErr); !ok || flagsErr.Type != flags.ErrHelp { - fmt.Fprintln(os.Stderr, usageMessage) - } - return nil, nil, err - } - - err = activeConfig.ResolveNetwork(parser) - if err != nil { - return nil, nil, err - } - // Handle environment variable expansion in the RPC certificate path. - activeConfig.RPCCert = cleanAndExpandPath(activeConfig.RPCCert) - - // Add default port to RPC server based on --testnet and --simnet flags - // if needed. - activeConfig.RPCServer, err = activeConfig.NetParams().NormalizeRPCServerAddress(activeConfig.RPCServer) - if err != nil { - return nil, nil, err - } - - return activeConfig, remainingArgs, nil -} - -// createDefaultConfig creates a basic config file at the given destination path. -// For this it tries to read the config file for the RPC server and extract the -// RPC user and password from it. -func createDefaultConfigFile(destinationPath, serverConfigPath string) error { - // Read the RPC server config - serverConfigFile, err := os.Open(serverConfigPath) - if os.IsNotExist(err) { - return errors.Errorf("the RPC server configuration file could not be found at %s", serverConfigPath) - } - if err != nil { - return err - } - defer serverConfigFile.Close() - content, err := ioutil.ReadAll(serverConfigFile) - if err != nil { - return err - } - - // Extract the rpcuser - rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser=([^\s]+)`) - if err != nil { - return err - } - userSubmatches := rpcUserRegexp.FindSubmatch(content) - if userSubmatches == nil { - // No user found, nothing to do - return nil - } - - // Extract the rpcpass - rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpass=([^\s]+)`) - if err != nil { - return err - } - passSubmatches := rpcPassRegexp.FindSubmatch(content) - if passSubmatches == nil { - // No password found, nothing to do - return nil - } - - // Extract the notls - noTLSRegexp, err := regexp.Compile(`(?m)^\s*notls=(0|1)(?:\s|$)`) - if err != nil { - return err - } - noTLSSubmatches := noTLSRegexp.FindSubmatch(content) - - // Create the destination directory if it does not exists - err = os.MkdirAll(filepath.Dir(destinationPath), 0700) - if err != nil { - return err - } - - // Create the destination file and write the rpcuser and rpcpass to it - dest, err := os.OpenFile(destinationPath, - os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return err - } - defer dest.Close() - - destString := fmt.Sprintf("rpcuser=%s\nrpcpass=%s\n", - string(userSubmatches[1]), string(passSubmatches[1])) - if noTLSSubmatches != nil { - destString += fmt.Sprintf("notls=%s\n", noTLSSubmatches[1]) - } - - dest.WriteString(destString) - - return nil + err = cfg.ResolveNetwork(parser) + if err != nil { + return nil, err + } + + if len(args) != 1 { + return nil, errors.New("the last parameter must be the request in JSON format") + } + cfg.RequestJSON = args[0] + + return cfg, nil } diff --git a/cmd/kaspactl/httpclient.go b/cmd/kaspactl/httpclient.go deleted file mode 100644 index 36e01c447..000000000 --- a/cmd/kaspactl/httpclient.go +++ /dev/null @@ -1,129 +0,0 @@ -package main - -import ( - "bytes" - "crypto/tls" - "crypto/x509" - "encoding/json" - "github.com/pkg/errors" - "io/ioutil" - "net" - "net/http" - - "github.com/btcsuite/go-socks/socks" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// newHTTPClient returns a new HTTP client that is configured according to the -// proxy and TLS settings in the associated connection configuration. -func newHTTPClient(cfg *ConfigFlags) (*http.Client, error) { - // Configure proxy if needed. - var dial func(network, addr string) (net.Conn, error) - if cfg.Proxy != "" { - proxy := &socks.Proxy{ - Addr: cfg.Proxy, - Username: cfg.ProxyUser, - Password: cfg.ProxyPass, - } - dial = func(network, addr string) (net.Conn, error) { - c, err := proxy.Dial(network, addr) - if err != nil { - return nil, err - } - return c, nil - } - } - - // Configure TLS if needed. - var tlsConfig *tls.Config - if !cfg.NoTLS && cfg.RPCCert != "" { - pem, err := ioutil.ReadFile(cfg.RPCCert) - if err != nil { - return nil, err - } - - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(pem) - tlsConfig = &tls.Config{ - RootCAs: pool, - InsecureSkipVerify: cfg.TLSSkipVerify, - } - } - - // Create and return the new HTTP client potentially configured with a - // proxy and TLS. - client := http.Client{ - Transport: &http.Transport{ - Dial: dial, - TLSClientConfig: tlsConfig, - }, - } - return &client, nil -} - -// sendPostRequest sends the marshalled JSON-RPC command using HTTP-POST mode -// to the server described in the passed config struct. It also attempts to -// unmarshal the response as a JSON-RPC response and returns either the result -// field or the error field depending on whether or not there is an error. -func sendPostRequest(marshalledJSON []byte, cfg *ConfigFlags) ([]byte, error) { - // Generate a request to the configured RPC server. - protocol := "http" - if !cfg.NoTLS { - protocol = "https" - } - url := protocol + "://" + cfg.RPCServer - bodyReader := bytes.NewReader(marshalledJSON) - httpRequest, err := http.NewRequest("POST", url, bodyReader) - if err != nil { - return nil, err - } - httpRequest.Close = true - httpRequest.Header.Set("Content-Type", "application/json") - - // Configure basic access authorization. - httpRequest.SetBasicAuth(cfg.RPCUser, cfg.RPCPassword) - - // Create the new HTTP client that is configured according to the user- - // specified options and submit the request. - httpClient, err := newHTTPClient(cfg) - if err != nil { - return nil, err - } - httpResponse, err := httpClient.Do(httpRequest) - if err != nil { - return nil, err - } - - // Read the raw bytes and close the response. - respBytes, err := func() ([]byte, error) { - defer httpResponse.Body.Close() - return ioutil.ReadAll(httpResponse.Body) - }() - if err != nil { - return nil, errors.Wrap(err, "error reading json reply") - } - - // Handle unsuccessful HTTP responses - if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 { - // Generate a standard error to return if the server body is - // empty. This should not happen very often, but it's better - // than showing nothing in case the target server has a poor - // implementation. - if len(respBytes) == 0 { - return nil, errors.Errorf("%d %s", httpResponse.StatusCode, - http.StatusText(httpResponse.StatusCode)) - } - return nil, errors.Errorf("%s", respBytes) - } - - // Unmarshal the response. - var resp model.Response - if err := json.Unmarshal(respBytes, &resp); err != nil { - return nil, err - } - - if resp.Error != nil { - return nil, resp.Error - } - return resp.Result, nil -} diff --git a/cmd/kaspactl/kaspactl.go b/cmd/kaspactl/kaspactl.go deleted file mode 100644 index fea52189d..000000000 --- a/cmd/kaspactl/kaspactl.go +++ /dev/null @@ -1,169 +0,0 @@ -package main - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "github.com/pkg/errors" - "io" - "os" - "path/filepath" - "strings" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -const ( - showHelpMessage = "Specify -h to show available options" - listCmdMessage = "Specify -l to list available commands" -) - -// commandUsage display the usage for a specific command. -func commandUsage(method string) { - usage, err := model.MethodUsageText(method) - if err != nil { - // This should never happen since the method was already checked - // before calling this function, but be safe. - fmt.Fprintln(os.Stderr, "Failed to obtain command usage:", err) - return - } - - fmt.Fprintln(os.Stderr, "Usage:") - fmt.Fprintf(os.Stderr, " %s\n", usage) -} - -// usage displays the general usage when the help flag is not displayed and -// and an invalid command was specified. The commandUsage function is used -// instead when a valid command was specified. -func usage(errorMessage string) { - appName := filepath.Base(os.Args[0]) - appName = strings.TrimSuffix(appName, filepath.Ext(appName)) - fmt.Fprintln(os.Stderr, errorMessage) - fmt.Fprintln(os.Stderr, "Usage:") - fmt.Fprintf(os.Stderr, " %s [OPTIONS] \n\n", - appName) - fmt.Fprintln(os.Stderr, showHelpMessage) - fmt.Fprintln(os.Stderr, listCmdMessage) -} - -func main() { - cfg, args, err := loadConfig() - if err != nil { - os.Exit(1) - } - if len(args) < 1 { - usage("No command specified") - os.Exit(1) - } - - // Ensure the specified method identifies a valid registered command and - // is one of the usable types. - method := args[0] - usageFlags, err := model.MethodUsageFlags(method) - if err != nil { - fmt.Fprintf(os.Stderr, "Unrecognized command '%s'\n", method) - fmt.Fprintln(os.Stderr, listCmdMessage) - os.Exit(1) - } - if usageFlags&unusableFlags != 0 { - fmt.Fprintf(os.Stderr, "The '%s' command can only be used via "+ - "websockets\n", method) - fmt.Fprintln(os.Stderr, listCmdMessage) - os.Exit(1) - } - - // Convert remaining command line args to a slice of interface values - // to be passed along as parameters to new command creation function. - // - // Since some commands, such as submitblock, can involve data which is - // too large for the Operating System to allow as a normal command line - // parameter, support using '-' as an argument to allow the argument - // to be read from a stdin pipe. - bio := bufio.NewReader(os.Stdin) - params := make([]interface{}, 0, len(args[1:])) - for _, arg := range args[1:] { - if arg == "-" { - param, err := bio.ReadString('\n') - if err != nil && err != io.EOF { - fmt.Fprintf(os.Stderr, "Failed to read data "+ - "from stdin: %s\n", err) - os.Exit(1) - } - if err == io.EOF && len(param) == 0 { - fmt.Fprintln(os.Stderr, "Not enough lines "+ - "provided on stdin") - os.Exit(1) - } - param = strings.TrimRight(param, "\r\n") - params = append(params, param) - continue - } - - params = append(params, arg) - } - - // Attempt to create the appropriate command using the arguments - // provided by the user. - cmd, err := model.NewCommand(method, params...) - if err != nil { - // Show the error along with its error code when it's a - // model.Error as it reallistcally will always be since the - // NewCommand function is only supposed to return errors of that - // type. - var rpcModelErr model.Error - if ok := errors.As(err, &rpcModelErr); ok { - fmt.Fprintf(os.Stderr, "%s error: %s (command code: %s)\n", - method, err, rpcModelErr.ErrorCode) - commandUsage(method) - os.Exit(1) - } - - // The error is not a model.Error and this really should not - // happen. Nevertheless, fallback to just showing the error - // if it should happen due to a bug in the package. - fmt.Fprintf(os.Stderr, "%s error: %s\n", method, err) - commandUsage(method) - os.Exit(1) - } - - // Marshal the command into a JSON-RPC byte slice in preparation for - // sending it to the RPC server. - marshalledJSON, err := model.MarshalCommand(1, cmd) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - - // Send the JSON-RPC request to the server using the user-specified - // connection configuration. - result, err := sendPostRequest(marshalledJSON, cfg) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - - // Choose how to display the result based on its type. - strResult := string(result) - if strings.HasPrefix(strResult, "{") || strings.HasPrefix(strResult, "[") { - var dst bytes.Buffer - if err := json.Indent(&dst, result, "", " "); err != nil { - fmt.Fprintf(os.Stderr, "Failed to format result: %s", - err) - os.Exit(1) - } - fmt.Println(dst.String()) - - } else if strings.HasPrefix(strResult, `"`) { - var str string - if err := json.Unmarshal(result, &str); err != nil { - fmt.Fprintf(os.Stderr, "Failed to unmarshal result: %s", - err) - os.Exit(1) - } - fmt.Println(str) - - } else if strResult != "null" { - fmt.Println(strResult) - } -} diff --git a/cmd/kaspactl/main.go b/cmd/kaspactl/main.go new file mode 100644 index 000000000..e17742c74 --- /dev/null +++ b/cmd/kaspactl/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "fmt" + "github.com/kaspanet/kaspad/infrastructure/network/rpcclient/grpcclient" + "os" +) + +func main() { + cfg, err := parseConfig() + if err != nil { + printErrorAndExit(fmt.Sprintf("error parsing command-line arguments: %s", err)) + } + + client, err := grpcclient.Connect(cfg.RPCServer) + if err != nil { + printErrorAndExit(fmt.Sprintf("error connecting to the RPC server: %s", err)) + } + defer client.Disconnect() + + requestString := cfg.RequestJSON + responseString, err := client.PostJSON(requestString) + if err != nil { + printErrorAndExit(fmt.Sprintf("error posting the request to the RPC server: %s", err)) + } + + fmt.Println(responseString) +} + +func printErrorAndExit(message string) { + fmt.Fprintf(os.Stderr, fmt.Sprintf("%s\n", message)) + os.Exit(1) +} diff --git a/cmd/kaspaminer/client.go b/cmd/kaspaminer/client.go index 1349c0973..5cdc36bcd 100644 --- a/cmd/kaspaminer/client.go +++ b/cmd/kaspaminer/client.go @@ -2,80 +2,46 @@ package main import ( "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/client" - "github.com/kaspanet/kaspad/util" + "github.com/kaspanet/kaspad/infrastructure/logger" + "github.com/kaspanet/kaspad/infrastructure/network/rpcclient" "github.com/pkg/errors" - "io/ioutil" "time" ) +const minerTimeout = 10 * time.Second + type minerClient struct { - *client.Client - onBlockAdded chan struct{} + *rpcclient.RPCClient + + blockAddedNotificationChan chan struct{} } -func newMinerClient(connCfg *client.ConnConfig) (*minerClient, error) { - minerClient := &minerClient{ - onBlockAdded: make(chan struct{}, 1), - } - notificationHandlers := &client.NotificationHandlers{ - OnFilteredBlockAdded: func(_ uint64, header *appmessage.BlockHeader, - txs []*util.Tx) { - minerClient.onBlockAdded <- struct{}{} - }, - } - var err error - minerClient.Client, err = client.New(connCfg, notificationHandlers) +func newMinerClient(cfg *configFlags) (*minerClient, error) { + rpcAddress, err := cfg.NetParams().NormalizeRPCServerAddress(cfg.RPCServer) if err != nil { - return nil, errors.Errorf("Error connecting to address %s: %s", connCfg.Host, err) + return nil, err + } + rpcClient, err := rpcclient.NewRPCClient(rpcAddress) + if err != nil { + return nil, err + } + rpcClient.SetTimeout(minerTimeout) + rpcClient.SetLogger(backendLog, logger.LevelTrace) + + minerClient := &minerClient{ + RPCClient: rpcClient, + blockAddedNotificationChan: make(chan struct{}), } - if err = minerClient.NotifyBlocks(); err != nil { - return nil, errors.Wrapf(err, "error while registering minerClient %s for block notifications", minerClient.Host()) + err = rpcClient.RegisterForBlockAddedNotifications(func(_ *appmessage.BlockAddedNotificationMessage) { + select { + case minerClient.blockAddedNotificationChan <- struct{}{}: + default: + } + }) + if err != nil { + return nil, errors.Wrapf(err, "error requesting block-added notifications") } + return minerClient, nil } - -func connectToServer(cfg *configFlags) (*minerClient, error) { - cert, err := readCert(cfg) - if err != nil { - return nil, err - } - - rpcAddr, err := cfg.NetParams().NormalizeRPCServerAddress(cfg.RPCServer) - if err != nil { - return nil, err - } - - connCfg := &client.ConnConfig{ - Host: rpcAddr, - Endpoint: "ws", - User: cfg.RPCUser, - Pass: cfg.RPCPassword, - DisableTLS: cfg.DisableTLS, - RequestTimeout: time.Second * 10, - Certificates: cert, - } - - client, err := newMinerClient(connCfg) - if err != nil { - return nil, err - } - - log.Infof("Connected to server %s", client.Host()) - - return client, nil -} - -func readCert(cfg *configFlags) ([]byte, error) { - if cfg.DisableTLS { - return nil, nil - } - - cert, err := ioutil.ReadFile(cfg.RPCCert) - if err != nil { - return nil, errors.Errorf("Error reading certificates file: %s", err) - } - - return cert, nil -} diff --git a/cmd/kaspaminer/config.go b/cmd/kaspaminer/config.go index 54f06349a..c1ac0d3aa 100644 --- a/cmd/kaspaminer/config.go +++ b/cmd/kaspaminer/config.go @@ -31,13 +31,8 @@ var ( type configFlags struct { ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"` - RPCUser string `short:"u" long:"rpcuser" description:"RPC username"` - RPCPassword string `short:"P" long:"rpcpass" default-mask:"-" description:"RPC password"` RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"` - RPCCert string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"` - DisableTLS bool `long:"notls" description:"Disable TLS"` MiningAddr string `long:"miningaddr" description:"Address to mine to"` - Verbose bool `long:"verbose" short:"v" description:"Enable logging of RPC requests"` NumberOfBlocks uint64 `short:"n" long:"numblocks" description:"Number of blocks to mine. If omitted, will mine until the process is interrupted."` BlockDelay uint64 `long:"block-delay" description:"Delay for block submission (in milliseconds). This is used only for testing purposes."` MineWhenNotSynced bool `long:"mine-when-not-synced" description:"Mine even if the node is not synced with the rest of the network."` @@ -69,20 +64,6 @@ func parseConfig() (*configFlags, error) { return nil, err } - if cfg.RPCUser == "" { - return nil, errors.New("--rpcuser is required") - } - if cfg.RPCPassword == "" { - return nil, errors.New("--rpcpass is required") - } - - if cfg.RPCCert == "" && !cfg.DisableTLS { - return nil, errors.New("either --notls or --rpccert must be specified") - } - if cfg.RPCCert != "" && cfg.DisableTLS { - return nil, errors.New("--rpccert should be omitted if --notls is used") - } - if cfg.Profile != "" { profilePort, err := strconv.Atoi(cfg.Profile) if err != nil || profilePort < 1024 || profilePort > 65535 { diff --git a/cmd/kaspaminer/log.go b/cmd/kaspaminer/log.go index 6b8fba38a..827e53308 100644 --- a/cmd/kaspaminer/log.go +++ b/cmd/kaspaminer/log.go @@ -3,7 +3,6 @@ package main import ( "fmt" "github.com/kaspanet/kaspad/infrastructure/logger" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/client" "github.com/kaspanet/kaspad/util/panics" "os" ) @@ -26,7 +25,3 @@ func initLog(logFile, errLogFile string) { os.Exit(1) } } - -func enableRPCLogging() { - client.UseLogger(backendLog, logger.LevelTrace) -} diff --git a/cmd/kaspaminer/main.go b/cmd/kaspaminer/main.go index 72168d695..6acc8a032 100644 --- a/cmd/kaspaminer/main.go +++ b/cmd/kaspaminer/main.go @@ -29,16 +29,12 @@ func main() { // Show version at startup. log.Infof("Version %s", version.Version()) - if cfg.Verbose { - enableRPCLogging() - } - // Enable http profiling server if requested. if cfg.Profile != "" { profiling.Start(cfg.Profile, log) } - client, err := connectToServer(cfg) + client, err := newMinerClient(cfg) if err != nil { panic(errors.Wrap(err, "error connecting to the RPC server")) } diff --git a/cmd/kaspaminer/mineloop.go b/cmd/kaspaminer/mineloop.go index ce1f7c4ac..7ff3af885 100644 --- a/cmd/kaspaminer/mineloop.go +++ b/cmd/kaspaminer/mineloop.go @@ -2,13 +2,14 @@ package main import ( nativeerrors "errors" + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/domain/mining" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" "math/rand" "sync" "sync/atomic" "time" - clientpkg "github.com/kaspanet/kaspad/infrastructure/network/rpc/client" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" "github.com/kaspanet/kaspad/util" "github.com/kaspanet/kaspad/util/daghash" "github.com/pkg/errors" @@ -79,7 +80,7 @@ func logHashRate() { func mineNextBlock(client *minerClient, miningAddr util.Address, foundBlock chan *util.Block, mineWhenNotSynced bool, templateStopChan chan struct{}, errChan chan error) { - newTemplateChan := make(chan *model.GetBlockTemplateResult) + newTemplateChan := make(chan *appmessage.GetBlockTemplateResponseMessage) spawn("templatesLoop", func() { templatesLoop(client, miningAddr, newTemplateChan, errChan, templateStopChan) }) @@ -89,11 +90,11 @@ func mineNextBlock(client *minerClient, miningAddr util.Address, foundBlock chan } func handleFoundBlock(client *minerClient, block *util.Block) error { - log.Infof("Found block %s with parents %s. Submitting to %s", block.Hash(), block.MsgBlock().Header.ParentHashes, client.Host()) + log.Infof("Found block %s with parents %s. Submitting to %s", block.Hash(), block.MsgBlock().Header.ParentHashes, client.Address()) - err := client.SubmitBlock(block, &model.SubmitBlockOptions{}) + err := client.SubmitBlock(block) if err != nil { - return errors.Errorf("Error submitting block %s to %s: %s", block.Hash(), client.Host(), err) + return errors.Errorf("Error submitting block %s to %s: %s", block.Hash(), client.Address(), err) } return nil } @@ -116,25 +117,28 @@ func solveBlock(block *util.Block, stopChan chan struct{}, foundBlock chan *util } } } - } func templatesLoop(client *minerClient, miningAddr util.Address, - newTemplateChan chan *model.GetBlockTemplateResult, errChan chan error, stopChan chan struct{}) { + newTemplateChan chan *appmessage.GetBlockTemplateResponseMessage, errChan chan error, stopChan chan struct{}) { longPollID := "" getBlockTemplateLongPoll := func() { if longPollID != "" { - log.Infof("Requesting template with longPollID '%s' from %s", longPollID, client.Host()) + log.Infof("Requesting template with longPollID '%s' from %s", longPollID, client.Address()) } else { - log.Infof("Requesting template without longPollID from %s", client.Host()) + log.Infof("Requesting template without longPollID from %s", client.Address()) } - template, err := getBlockTemplate(client, miningAddr, longPollID) - if nativeerrors.Is(err, clientpkg.ErrResponseTimedOut) { - log.Infof("Got timeout while requesting template '%s' from %s", longPollID, client.Host()) + template, err := client.GetBlockTemplate(miningAddr.String(), longPollID) + if nativeerrors.Is(err, router.ErrTimeout) { + log.Infof("Got timeout while requesting template '%s' from %s", longPollID, client.Address()) return } else if err != nil { - errChan <- errors.Errorf("Error getting block template from %s: %s", client.Host(), err) + errChan <- errors.Errorf("Error getting block template from %s: %s", client.Address(), err) + return + } + if !template.IsConnected { + errChan <- errors.Errorf("Kaspad is not connected for %s", client.Address()) return } if template.LongPollID != longPollID { @@ -149,7 +153,7 @@ func templatesLoop(client *minerClient, miningAddr util.Address, case <-stopChan: close(newTemplateChan) return - case <-client.onBlockAdded: + case <-client.blockAddedNotificationChan: getBlockTemplateLongPoll() case <-time.Tick(500 * time.Millisecond): getBlockTemplateLongPoll() @@ -157,11 +161,7 @@ func templatesLoop(client *minerClient, miningAddr util.Address, } } -func getBlockTemplate(client *minerClient, miningAddr util.Address, longPollID string) (*model.GetBlockTemplateResult, error) { - return client.GetBlockTemplate(miningAddr.String(), longPollID) -} - -func solveLoop(newTemplateChan chan *model.GetBlockTemplateResult, foundBlock chan *util.Block, +func solveLoop(newTemplateChan chan *appmessage.GetBlockTemplateResponseMessage, foundBlock chan *util.Block, mineWhenNotSynced bool, errChan chan error) { var stopOldTemplateSolving chan struct{} @@ -179,7 +179,7 @@ func solveLoop(newTemplateChan chan *model.GetBlockTemplateResult, foundBlock ch } stopOldTemplateSolving = make(chan struct{}) - block, err := clientpkg.ConvertGetBlockTemplateResultToBlock(template) + block, err := mining.ConvertGetBlockTemplateResultToBlock(template) if err != nil { errChan <- errors.Errorf("Error parsing block: %s", err) return diff --git a/domain/mining/mining.go b/domain/mining/mining.go index 0eb6c76a7..92de2a10e 100644 --- a/domain/mining/mining.go +++ b/domain/mining/mining.go @@ -5,8 +5,11 @@ package mining import ( + "encoding/hex" "github.com/kaspanet/kaspad/util/mstime" "github.com/pkg/errors" + "strconv" + "strings" "time" "github.com/kaspanet/kaspad/app/appmessage" @@ -247,3 +250,55 @@ func (g *BlkTmplGenerator) IsSynced() bool { const maxSyncRateDeviation = 1.05 return g.dag.IsSyncRateBelowThreshold(maxSyncRateDeviation) } + +// ConvertGetBlockTemplateResultToBlock converts the given template into a util.Block +func ConvertGetBlockTemplateResultToBlock(template *appmessage.GetBlockTemplateResponseMessage) (*util.Block, error) { + // parse parent hashes + parentHashes := make([]*daghash.Hash, len(template.ParentHashes)) + for i, parentHash := range template.ParentHashes { + hash, err := daghash.NewHashFromStr(parentHash) + if err != nil { + return nil, errors.Wrapf(err, "error decoding hash: '%s'", parentHash) + } + parentHashes[i] = hash + } + + // parse Bits + bitsUint64, err := strconv.ParseUint(template.Bits, 16, 32) + if err != nil { + return nil, errors.Wrapf(err, "error decoding bits: '%s'", template.Bits) + } + bits := uint32(bitsUint64) + + // parse hashMerkleRoot + hashMerkleRoot, err := daghash.NewHashFromStr(template.HashMerkleRoot) + if err != nil { + return nil, errors.Wrapf(err, "error parsing HashMerkleRoot: '%s'", template.HashMerkleRoot) + } + + // parse AcceptedIDMerkleRoot + acceptedIDMerkleRoot, err := daghash.NewHashFromStr(template.AcceptedIDMerkleRoot) + if err != nil { + return nil, errors.Wrapf(err, "error parsing acceptedIDMerkleRoot: '%s'", template.AcceptedIDMerkleRoot) + } + utxoCommitment, err := daghash.NewHashFromStr(template.UTXOCommitment) + if err != nil { + return nil, errors.Wrapf(err, "error parsing utxoCommitment '%s'", template.UTXOCommitment) + } + // parse rest of block + msgBlock := appmessage.NewMsgBlock( + appmessage.NewBlockHeader(template.Version, parentHashes, hashMerkleRoot, + acceptedIDMerkleRoot, utxoCommitment, bits, 0)) + + for i, txResult := range template.Transactions { + reader := hex.NewDecoder(strings.NewReader(txResult.Data)) + tx := &appmessage.MsgTx{} + if err := tx.KaspaDecode(reader, 0); err != nil { + return nil, errors.Wrapf(err, "error decoding tx #%d", i) + } + msgBlock.AddTransaction(tx) + } + + block := util.NewBlock(msgBlock) + return block, nil +} diff --git a/infrastructure/logger/logger.go b/infrastructure/logger/logger.go index fe6d141fb..280816cfd 100644 --- a/infrastructure/logger/logger.go +++ b/infrastructure/logger/logger.go @@ -38,6 +38,7 @@ var ( minrLog = BackendLog.Logger("MINR") peerLog = BackendLog.Logger("PEER") rpcsLog = BackendLog.Logger("RPCS") + rpccLog = BackendLog.Logger("RPCC") scrpLog = BackendLog.Logger("SCRP") srvrLog = BackendLog.Logger("SRVR") syncLog = BackendLog.Logger("SYNC") @@ -68,6 +69,7 @@ var SubsystemTags = struct { MINR, PEER, RPCS, + RPCC, SCRP, SRVR, SYNC, @@ -95,6 +97,7 @@ var SubsystemTags = struct { MINR: "MINR", PEER: "PEER", RPCS: "RPCS", + RPCC: "RPCC", SCRP: "SCRP", SRVR: "SRVR", SYNC: "SYNC", @@ -125,6 +128,7 @@ var subsystemLoggers = map[string]*Logger{ SubsystemTags.MINR: minrLog, SubsystemTags.PEER: peerLog, SubsystemTags.RPCS: rpcsLog, + SubsystemTags.RPCC: rpccLog, SubsystemTags.SCRP: scrpLog, SubsystemTags.SRVR: srvrLog, SubsystemTags.SYNC: syncLog, diff --git a/infrastructure/network/connmanager/connmanager.go b/infrastructure/network/connmanager/connmanager.go index 33c74c8f1..eb3ec4f9d 100644 --- a/infrastructure/network/connmanager/connmanager.go +++ b/infrastructure/network/connmanager/connmanager.go @@ -82,7 +82,7 @@ func (c *ConnectionManager) Start() { func (c *ConnectionManager) Stop() { atomic.StoreUint32(&c.stop, 1) - for _, connection := range c.netAdapter.Connections() { + for _, connection := range c.netAdapter.P2PConnections() { connection.Disconnect() } @@ -95,14 +95,14 @@ func (c *ConnectionManager) run() { func (c *ConnectionManager) initiateConnection(address string) error { log.Infof("Connecting to %s", address) - return c.netAdapter.Connect(address) + return c.netAdapter.P2PConnect(address) } const connectionsLoopInterval = 30 * time.Second func (c *ConnectionManager) connectionsLoop() { for atomic.LoadUint32(&c.stop) == 0 { - connections := c.netAdapter.Connections() + connections := c.netAdapter.P2PConnections() // We convert the connections list to a set, so that connections can be found quickly // Then we go over the set, classifying connection by category: requested, outgoing or incoming. @@ -122,7 +122,7 @@ func (c *ConnectionManager) connectionsLoop() { // ConnectionCount returns the count of the connected connections func (c *ConnectionManager) ConnectionCount() int { - return c.netAdapter.ConnectionCount() + return c.netAdapter.P2PConnectionCount() } // Ban marks the given netConnection as banned diff --git a/infrastructure/network/netadapter/netadapter.go b/infrastructure/network/netadapter/netadapter.go index 113c4d643..a2111e52a 100644 --- a/infrastructure/network/netadapter/netadapter.go +++ b/infrastructure/network/netadapter/netadapter.go @@ -23,14 +23,16 @@ type RouterInitializer func(*routerpkg.Router, *NetConnection) // and message handlers) without exposing anything related // to networking internals. type NetAdapter struct { - cfg *config.Config - id *id.ID - server server.Server - routerInitializer RouterInitializer - stop uint32 + cfg *config.Config + id *id.ID + p2pServer server.P2PServer + p2pRouterInitializer RouterInitializer + rpcServer server.Server + rpcRouterInitializer RouterInitializer + stop uint32 - connections map[*NetConnection]struct{} - connectionsLock sync.RWMutex + p2pConnections map[*NetConnection]struct{} + p2pConnectionsLock sync.RWMutex } // NewNetAdapter creates and starts a new NetAdapter on the @@ -40,30 +42,43 @@ func NewNetAdapter(cfg *config.Config) (*NetAdapter, error) { if err != nil { return nil, err } - s, err := grpcserver.NewGRPCServer(cfg.Listeners) + p2pServer, err := grpcserver.NewP2PServer(cfg.Listeners) + if err != nil { + return nil, err + } + rpcServer, err := grpcserver.NewRPCServer(cfg.RPCListeners) if err != nil { return nil, err } adapter := NetAdapter{ - cfg: cfg, - id: netAdapterID, - server: s, + cfg: cfg, + id: netAdapterID, + p2pServer: p2pServer, + rpcServer: rpcServer, - connections: make(map[*NetConnection]struct{}), + p2pConnections: make(map[*NetConnection]struct{}), } - adapter.server.SetOnConnectedHandler(adapter.onConnectedHandler) + adapter.p2pServer.SetOnConnectedHandler(adapter.onP2PConnectedHandler) + adapter.rpcServer.SetOnConnectedHandler(adapter.onRPCConnectedHandler) return &adapter, nil } // Start begins the operation of the NetAdapter func (na *NetAdapter) Start() error { - if na.routerInitializer == nil { - return errors.New("routerInitializer was not set") + if na.p2pRouterInitializer == nil { + return errors.New("p2pRouterInitializer was not set") + } + if na.rpcRouterInitializer == nil { + return errors.New("rpcRouterInitializer was not set") } - err := na.server.Start() + err := na.p2pServer.Start() + if err != nil { + return err + } + err = na.rpcServer.Start() if err != nil { return err } @@ -76,62 +91,80 @@ func (na *NetAdapter) Stop() error { if atomic.AddUint32(&na.stop, 1) != 1 { return errors.New("net adapter stopped more than once") } - return na.server.Stop() + err := na.p2pServer.Stop() + if err != nil { + return err + } + return na.rpcServer.Stop() } -// Connect tells the NetAdapter's underlying server to initiate a connection +// P2PConnect tells the NetAdapter's underlying p2p server to initiate a connection // to the given address -func (na *NetAdapter) Connect(address string) error { - _, err := na.server.Connect(address) +func (na *NetAdapter) P2PConnect(address string) error { + _, err := na.p2pServer.Connect(address) return err } -// Connections returns a list of connections currently connected and active -func (na *NetAdapter) Connections() []*NetConnection { - na.connectionsLock.RLock() - defer na.connectionsLock.RUnlock() +// P2PConnections returns a list of p2p connections currently connected and active +func (na *NetAdapter) P2PConnections() []*NetConnection { + na.p2pConnectionsLock.RLock() + defer na.p2pConnectionsLock.RUnlock() - netConnections := make([]*NetConnection, 0, len(na.connections)) + netConnections := make([]*NetConnection, 0, len(na.p2pConnections)) - for netConnection := range na.connections { + for netConnection := range na.p2pConnections { netConnections = append(netConnections, netConnection) } return netConnections } -// ConnectionCount returns the count of the connected connections -func (na *NetAdapter) ConnectionCount() int { - na.connectionsLock.RLock() - defer na.connectionsLock.RUnlock() +// P2PConnectionCount returns the count of the connected p2p connections +func (na *NetAdapter) P2PConnectionCount() int { + na.p2pConnectionsLock.RLock() + defer na.p2pConnectionsLock.RUnlock() - return len(na.connections) + return len(na.p2pConnections) } -func (na *NetAdapter) onConnectedHandler(connection server.Connection) error { - netConnection := newNetConnection(connection, na.routerInitializer) +func (na *NetAdapter) onP2PConnectedHandler(connection server.Connection) error { + netConnection := newNetConnection(connection, na.p2pRouterInitializer) - na.connectionsLock.Lock() - defer na.connectionsLock.Unlock() + na.p2pConnectionsLock.Lock() + defer na.p2pConnectionsLock.Unlock() netConnection.setOnDisconnectedHandler(func() { - na.connectionsLock.Lock() - defer na.connectionsLock.Unlock() + na.p2pConnectionsLock.Lock() + defer na.p2pConnectionsLock.Unlock() - delete(na.connections, netConnection) + delete(na.p2pConnections, netConnection) }) - na.connections[netConnection] = struct{}{} + na.p2pConnections[netConnection] = struct{}{} netConnection.start() return nil } -// SetRouterInitializer sets the routerInitializer function +func (na *NetAdapter) onRPCConnectedHandler(connection server.Connection) error { + netConnection := newNetConnection(connection, na.rpcRouterInitializer) + netConnection.setOnDisconnectedHandler(func() {}) + netConnection.start() + + return nil +} + +// SetP2PRouterInitializer sets the p2pRouterInitializer function // for the net adapter -func (na *NetAdapter) SetRouterInitializer(routerInitializer RouterInitializer) { - na.routerInitializer = routerInitializer +func (na *NetAdapter) SetP2PRouterInitializer(routerInitializer RouterInitializer) { + na.p2pRouterInitializer = routerInitializer +} + +// SetRPCRouterInitializer sets the rpcRouterInitializer function +// for the net adapter +func (na *NetAdapter) SetRPCRouterInitializer(routerInitializer RouterInitializer) { + na.rpcRouterInitializer = routerInitializer } // ID returns this netAdapter's ID in the network @@ -139,11 +172,11 @@ func (na *NetAdapter) ID() *id.ID { return na.id } -// Broadcast sends the given `message` to every peer corresponding +// P2PBroadcast sends the given `message` to every peer corresponding // to each NetConnection in the given netConnections -func (na *NetAdapter) Broadcast(netConnections []*NetConnection, message appmessage.Message) error { - na.connectionsLock.RLock() - defer na.connectionsLock.RUnlock() +func (na *NetAdapter) P2PBroadcast(netConnections []*NetConnection, message appmessage.Message) error { + na.p2pConnectionsLock.RLock() + defer na.p2pConnectionsLock.RUnlock() for _, netConnection := range netConnections { err := netConnection.router.OutgoingRoute().Enqueue(message) diff --git a/infrastructure/network/netadapter/netadapter_test.go b/infrastructure/network/netadapter/netadapter_test.go index a04254494..72bc2854e 100644 --- a/infrastructure/network/netadapter/netadapter_test.go +++ b/infrastructure/network/netadapter/netadapter_test.go @@ -55,7 +55,8 @@ func TestNetAdapter(t *testing.T) { t.Fatalf("TestNetAdapter: NetAdapter instantiation failed: %+v", err) } - adapterA.SetRouterInitializer(func(router *router.Router, connection *NetConnection) {}) + adapterA.SetP2PRouterInitializer(func(router *router.Router, connection *NetConnection) {}) + adapterA.SetRPCRouterInitializer(func(router *router.Router, connection *NetConnection) {}) err = adapterA.Start() if err != nil { t.Fatalf("TestNetAdapter: Start() failed: %+v", err) @@ -67,7 +68,8 @@ func TestNetAdapter(t *testing.T) { } initializer := routerInitializerForTest(t, routes, "B", wg) - adapterB.SetRouterInitializer(initializer) + adapterB.SetP2PRouterInitializer(initializer) + adapterB.SetRPCRouterInitializer(func(router *router.Router, connection *NetConnection) {}) err = adapterB.Start() if err != nil { t.Fatalf("TestNetAdapter: Start() failed: %+v", err) @@ -79,30 +81,31 @@ func TestNetAdapter(t *testing.T) { } initializer = routerInitializerForTest(t, routes, "C", wg) - adapterC.SetRouterInitializer(initializer) + adapterC.SetP2PRouterInitializer(initializer) + adapterC.SetRPCRouterInitializer(func(router *router.Router, connection *NetConnection) {}) err = adapterC.Start() if err != nil { t.Fatalf("TestNetAdapter: Start() failed: %+v", err) } - err = adapterA.Connect(addressB) + err = adapterA.P2PConnect(addressB) if err != nil { t.Fatalf("TestNetAdapter: connection to %s failed: %+v", addressB, err) } - err = adapterA.Connect(addressC) + err = adapterA.P2PConnect(addressC) if err != nil { t.Fatalf("TestNetAdapter: connection to %s failed: %+v", addressC, err) } // Ensure adapter has two connections - if count := adapterA.ConnectionCount(); count != 2 { + if count := adapterA.P2PConnectionCount(); count != 2 { t.Fatalf("TestNetAdapter: expected 2 connections, got - %d", count) } // Ensure all connected peers have received broadcasted message - connections := adapterA.Connections() - err = adapterA.Broadcast(connections, appmessage.NewMsgPing(1)) + connections := adapterA.P2PConnections() + err = adapterA.P2PBroadcast(connections, appmessage.NewMsgPing(1)) if err != nil { t.Fatalf("TestNetAdapter: broadcast failed: %+v", err) } @@ -122,8 +125,8 @@ func TestNetAdapter(t *testing.T) { if command := msg.Command(); command != appmessage.CmdPing { t.Fatalf("TestNetAdapter: expected '%s' message to be received but got '%s'", - appmessage.MessageCommandToString[appmessage.CmdPing], - appmessage.MessageCommandToString[command]) + appmessage.ProtocolMessageCommandToString[appmessage.CmdPing], + appmessage.ProtocolMessageCommandToString[command]) } if number := msg.MessageNumber(); number != nonce { @@ -142,8 +145,8 @@ func TestNetAdapter(t *testing.T) { if command := msg.Command(); command != appmessage.CmdPing { t.Fatalf("TestNetAdapter: expected '%s' message to be received but got '%s'", - appmessage.MessageCommandToString[appmessage.CmdPing], - appmessage.MessageCommandToString[command]) + appmessage.ProtocolMessageCommandToString[appmessage.CmdPing], + appmessage.ProtocolMessageCommandToString[command]) } if number := msg.MessageNumber(); number != nonce { diff --git a/infrastructure/network/netadapter/server/grpcserver/connection_loops.go b/infrastructure/network/netadapter/server/grpcserver/connection_loops.go index 8b6ff66bf..e10111eb8 100644 --- a/infrastructure/network/netadapter/server/grpcserver/connection_loops.go +++ b/infrastructure/network/netadapter/server/grpcserver/connection_loops.go @@ -13,11 +13,6 @@ import ( "github.com/kaspanet/kaspad/infrastructure/network/netadapter/server/grpcserver/protowire" ) -type grpcStream interface { - Send(*protowire.KaspadMessage) error - Recv() (*protowire.KaspadMessage, error) -} - func (c *gRPCConnection) connectionLoops() error { errChan := make(chan error, 1) // buffered channel because one of the loops might try write after disconnect diff --git a/infrastructure/network/netadapter/server/grpcserver/grpc_connection.go b/infrastructure/network/netadapter/server/grpcserver/grpc_connection.go index 74f311cc9..006dec285 100644 --- a/infrastructure/network/netadapter/server/grpcserver/grpc_connection.go +++ b/infrastructure/network/netadapter/server/grpcserver/grpc_connection.go @@ -34,9 +34,13 @@ type gRPCConnection struct { isConnected uint32 } +type grpcStream interface { + Send(*protowire.KaspadMessage) error + Recv() (*protowire.KaspadMessage, error) +} + func newConnection(server *gRPCServer, address *net.TCPAddr, stream grpcStream, lowLevelClientConnection *grpc.ClientConn) *gRPCConnection { - connection := &gRPCConnection{ server: server, address: address, @@ -139,7 +143,7 @@ func (c *gRPCConnection) closeSend() { c.streamLock.Lock() defer c.streamLock.Unlock() - clientStream := c.stream.(protowire.P2P_MessageStreamClient) + clientStream := c.stream.(grpc.ClientStream) // ignore error because we don't really know what's the status of the connection _ = clientStream.CloseSend() diff --git a/infrastructure/network/netadapter/server/grpcserver/grpc_server.go b/infrastructure/network/netadapter/server/grpcserver/grpc_server.go index ad82eb376..57352e1d2 100644 --- a/infrastructure/network/netadapter/server/grpcserver/grpc_server.go +++ b/infrastructure/network/netadapter/server/grpcserver/grpc_server.go @@ -3,37 +3,28 @@ package grpcserver import ( "context" "fmt" - "google.golang.org/grpc/encoding/gzip" - "net" - "time" - - "google.golang.org/grpc/peer" - "github.com/kaspanet/kaspad/infrastructure/network/netadapter/server" - "github.com/kaspanet/kaspad/infrastructure/network/netadapter/server/grpcserver/protowire" "github.com/kaspanet/kaspad/util/panics" "github.com/pkg/errors" "google.golang.org/grpc" + "google.golang.org/grpc/peer" + "net" ) type gRPCServer struct { onConnectedHandler server.OnConnectedHandler - listeningAddrs []string + listeningAddresses []string server *grpc.Server } const maxMessageSize = 1024 * 1024 * 10 // 10MB -// NewGRPCServer creates and starts a gRPC server, listening on the -// provided addresses/ports -func NewGRPCServer(listeningAddrs []string) (server.Server, error) { - s := &gRPCServer{ - server: grpc.NewServer(grpc.MaxRecvMsgSize(maxMessageSize), grpc.MaxSendMsgSize(maxMessageSize)), - listeningAddrs: listeningAddrs, +// newGRPCServer creates a gRPC server +func newGRPCServer(listeningAddresses []string) *gRPCServer { + return &gRPCServer{ + server: grpc.NewServer(grpc.MaxRecvMsgSize(maxMessageSize), grpc.MaxSendMsgSize(maxMessageSize)), + listeningAddresses: listeningAddresses, } - protowire.RegisterP2PServer(s.server, newP2PServer(s)) - - return s, nil } func (s *gRPCServer) Start() error { @@ -41,14 +32,14 @@ func (s *gRPCServer) Start() error { return errors.New("onConnectedHandler is nil") } - for _, listenAddr := range s.listeningAddrs { - err := s.listenOn(listenAddr) + for _, listenAddress := range s.listeningAddresses { + err := s.listenOn(listenAddress) if err != nil { return err } } - log.Debugf("P2P server started with maxMessageSize %d", maxMessageSize) + log.Debugf("Server started with maxMessageSize %d", maxMessageSize) return nil } @@ -66,7 +57,7 @@ func (s *gRPCServer) listenOn(listenAddr string) error { } }) - log.Infof("P2P server listening on %s", listenAddr) + log.Infof("Server listening on %s", listenAddr) return nil } @@ -81,43 +72,26 @@ func (s *gRPCServer) SetOnConnectedHandler(onConnectedHandler server.OnConnected s.onConnectedHandler = onConnectedHandler } -// Connect connects to the given address -// This is part of the Server interface -func (s *gRPCServer) Connect(address string) (server.Connection, error) { - log.Infof("Dialing to %s", address) - - const dialTimeout = 30 * time.Second - ctx, cancel := context.WithTimeout(context.Background(), dialTimeout) - defer cancel() - - gRPCClientConnection, err := grpc.DialContext(ctx, address, grpc.WithInsecure(), grpc.WithBlock()) - if err != nil { - return nil, errors.Wrapf(err, "error connecting to %s", address) - } - - client := protowire.NewP2PClient(gRPCClientConnection) - stream, err := client.MessageStream(context.Background(), grpc.UseCompressor(gzip.Name)) - if err != nil { - return nil, errors.Wrapf(err, "error getting client stream for %s", address) - } - - peerInfo, ok := peer.FromContext(stream.Context()) +func (s *gRPCServer) handleInboundConnection(ctx context.Context, stream grpcStream) error { + peerInfo, ok := peer.FromContext(ctx) if !ok { - return nil, errors.Errorf("error getting stream peer info from context for %s", address) + return errors.Errorf("Error getting stream peer info from context") } tcpAddress, ok := peerInfo.Addr.(*net.TCPAddr) if !ok { - return nil, errors.Errorf("non-tcp addresses are not supported") + return errors.Errorf("non-tcp connections are not supported") } - connection := newConnection(s, tcpAddress, stream, gRPCClientConnection) + connection := newConnection(s, tcpAddress, stream, nil) - err = s.onConnectedHandler(connection) + err := s.onConnectedHandler(connection) if err != nil { - return nil, err + return err } - log.Infof("Connected to %s", address) + log.Infof("Incoming connection from %s", peerInfo.Addr) - return connection, nil + <-connection.stopChan + + return nil } diff --git a/infrastructure/network/netadapter/server/grpcserver/p2pserver.go b/infrastructure/network/netadapter/server/grpcserver/p2pserver.go index 92c28e06e..73dfbcd26 100644 --- a/infrastructure/network/netadapter/server/grpcserver/p2pserver.go +++ b/infrastructure/network/netadapter/server/grpcserver/p2pserver.go @@ -1,44 +1,74 @@ package grpcserver import ( + "context" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/server" "github.com/kaspanet/kaspad/infrastructure/network/netadapter/server/grpcserver/protowire" "github.com/kaspanet/kaspad/util/panics" "github.com/pkg/errors" + "google.golang.org/grpc" + "google.golang.org/grpc/encoding/gzip" "google.golang.org/grpc/peer" "net" + "time" ) type p2pServer struct { protowire.UnimplementedP2PServer - server *gRPCServer + gRPCServer } -func newP2PServer(s *gRPCServer) *p2pServer { - return &p2pServer{server: s} +// NewP2PServer creates a new P2PServer +func NewP2PServer(listeningAddresses []string) (server.P2PServer, error) { + gRPCServer := newGRPCServer(listeningAddresses) + p2pServer := &p2pServer{gRPCServer: *gRPCServer} + protowire.RegisterP2PServer(gRPCServer.server, p2pServer) + return p2pServer, nil } func (p *p2pServer) MessageStream(stream protowire.P2P_MessageStreamServer) error { defer panics.HandlePanic(log, "p2pServer.MessageStream", nil) + return p.handleInboundConnection(stream.Context(), stream) +} + +// Connect connects to the given address +// This is part of the P2PServer interface +func (p *p2pServer) Connect(address string) (server.Connection, error) { + log.Infof("Dialing to %s", address) + + const dialTimeout = 30 * time.Second + ctx, cancel := context.WithTimeout(context.Background(), dialTimeout) + defer cancel() + + gRPCClientConnection, err := grpc.DialContext(ctx, address, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + return nil, errors.Wrapf(err, "error connecting to %s", address) + } + + client := protowire.NewP2PClient(gRPCClientConnection) + stream, err := client.MessageStream(context.Background(), grpc.UseCompressor(gzip.Name)) + if err != nil { + return nil, errors.Wrapf(err, "error getting client stream for %s", address) + } + peerInfo, ok := peer.FromContext(stream.Context()) if !ok { - return errors.Errorf("Error getting stream peer info from context") + return nil, errors.Errorf("error getting stream peer info from context for %s", address) } tcpAddress, ok := peerInfo.Addr.(*net.TCPAddr) if !ok { - return errors.Errorf("non-tcp connections are not supported") + return nil, errors.Errorf("non-tcp addresses are not supported") } - connection := newConnection(p.server, tcpAddress, stream, nil) + connection := newConnection(&p.gRPCServer, tcpAddress, stream, gRPCClientConnection) - err := p.server.onConnectedHandler(connection) + err = p.onConnectedHandler(connection) if err != nil { - return err + return nil, err } - log.Infof("Incoming connection from %s", peerInfo.Addr) + log.Infof("Connected to %s", address) - <-connection.stopChan - - return nil + return connection, nil } diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/messages.pb.go b/infrastructure/network/netadapter/server/grpcserver/protowire/messages.pb.go index 59e457a76..602e24c95 100644 --- a/infrastructure/network/netadapter/server/grpcserver/protowire/messages.pb.go +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/messages.pb.go @@ -53,6 +53,38 @@ type KaspadMessage struct { // *KaspadMessage_Version // *KaspadMessage_TransactionNotFound // *KaspadMessage_Reject + // *KaspadMessage_GetCurrentNetworkRequest + // *KaspadMessage_GetCurrentNetworkResponse + // *KaspadMessage_SubmitBlockRequest + // *KaspadMessage_SubmitBlockResponse + // *KaspadMessage_GetBlockTemplateRequest + // *KaspadMessage_GetBlockTemplateResponse + // *KaspadMessage_NotifyBlockAddedRequest + // *KaspadMessage_NotifyBlockAddedResponse + // *KaspadMessage_BlockAddedNotification + // *KaspadMessage_GetPeerAddressesRequest + // *KaspadMessage_GetPeerAddressesResponse + // *KaspadMessage_GetSelectedTipHashRequest + // *KaspadMessage_GetSelectedTipHashResponse + // *KaspadMessage_GetMempoolEntryRequest + // *KaspadMessage_GetMempoolEntryResponse + // *KaspadMessage_GetConnectedPeerInfoRequest + // *KaspadMessage_GetConnectedPeerInfoResponse + // *KaspadMessage_AddPeerRequest + // *KaspadMessage_AddPeerResponse + // *KaspadMessage_SubmitTransactionRequest + // *KaspadMessage_SubmitTransactionResponse + // *KaspadMessage_NotifyChainChangedRequest + // *KaspadMessage_NotifyChainChangedResponse + // *KaspadMessage_ChainChangedNotification + // *KaspadMessage_GetBlockRequest + // *KaspadMessage_GetBlockResponse + // *KaspadMessage_GetSubnetworkRequest + // *KaspadMessage_GetSubnetworkResponse + // *KaspadMessage_GetChainFromBlockRequest + // *KaspadMessage_GetChainFromBlockResponse + // *KaspadMessage_GetBlocksRequest + // *KaspadMessage_GetBlocksResponse Payload isKaspadMessage_Payload `protobuf_oneof:"payload"` } @@ -249,6 +281,230 @@ func (x *KaspadMessage) GetReject() *RejectMessage { return nil } +func (x *KaspadMessage) GetGetCurrentNetworkRequest() *GetCurrentNetworkRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetCurrentNetworkRequest); ok { + return x.GetCurrentNetworkRequest + } + return nil +} + +func (x *KaspadMessage) GetGetCurrentNetworkResponse() *GetCurrentNetworkResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetCurrentNetworkResponse); ok { + return x.GetCurrentNetworkResponse + } + return nil +} + +func (x *KaspadMessage) GetSubmitBlockRequest() *SubmitBlockRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_SubmitBlockRequest); ok { + return x.SubmitBlockRequest + } + return nil +} + +func (x *KaspadMessage) GetSubmitBlockResponse() *SubmitBlockResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_SubmitBlockResponse); ok { + return x.SubmitBlockResponse + } + return nil +} + +func (x *KaspadMessage) GetGetBlockTemplateRequest() *GetBlockTemplateRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetBlockTemplateRequest); ok { + return x.GetBlockTemplateRequest + } + return nil +} + +func (x *KaspadMessage) GetGetBlockTemplateResponse() *GetBlockTemplateResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetBlockTemplateResponse); ok { + return x.GetBlockTemplateResponse + } + return nil +} + +func (x *KaspadMessage) GetNotifyBlockAddedRequest() *NotifyBlockAddedRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_NotifyBlockAddedRequest); ok { + return x.NotifyBlockAddedRequest + } + return nil +} + +func (x *KaspadMessage) GetNotifyBlockAddedResponse() *NotifyBlockAddedResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_NotifyBlockAddedResponse); ok { + return x.NotifyBlockAddedResponse + } + return nil +} + +func (x *KaspadMessage) GetBlockAddedNotification() *BlockAddedNotificationMessage { + if x, ok := x.GetPayload().(*KaspadMessage_BlockAddedNotification); ok { + return x.BlockAddedNotification + } + return nil +} + +func (x *KaspadMessage) GetGetPeerAddressesRequest() *GetPeerAddressesRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetPeerAddressesRequest); ok { + return x.GetPeerAddressesRequest + } + return nil +} + +func (x *KaspadMessage) GetGetPeerAddressesResponse() *GetPeerAddressesResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetPeerAddressesResponse); ok { + return x.GetPeerAddressesResponse + } + return nil +} + +func (x *KaspadMessage) GetGetSelectedTipHashRequest() *GetSelectedTipHashRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetSelectedTipHashRequest); ok { + return x.GetSelectedTipHashRequest + } + return nil +} + +func (x *KaspadMessage) GetGetSelectedTipHashResponse() *GetSelectedTipHashResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetSelectedTipHashResponse); ok { + return x.GetSelectedTipHashResponse + } + return nil +} + +func (x *KaspadMessage) GetGetMempoolEntryRequest() *GetMempoolEntryRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetMempoolEntryRequest); ok { + return x.GetMempoolEntryRequest + } + return nil +} + +func (x *KaspadMessage) GetGetMempoolEntryResponse() *GetMempoolEntryResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetMempoolEntryResponse); ok { + return x.GetMempoolEntryResponse + } + return nil +} + +func (x *KaspadMessage) GetGetConnectedPeerInfoRequest() *GetConnectedPeerInfoRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetConnectedPeerInfoRequest); ok { + return x.GetConnectedPeerInfoRequest + } + return nil +} + +func (x *KaspadMessage) GetGetConnectedPeerInfoResponse() *GetConnectedPeerInfoResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetConnectedPeerInfoResponse); ok { + return x.GetConnectedPeerInfoResponse + } + return nil +} + +func (x *KaspadMessage) GetAddPeerRequest() *AddPeerRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_AddPeerRequest); ok { + return x.AddPeerRequest + } + return nil +} + +func (x *KaspadMessage) GetAddPeerResponse() *AddPeerResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_AddPeerResponse); ok { + return x.AddPeerResponse + } + return nil +} + +func (x *KaspadMessage) GetSubmitTransactionRequest() *SubmitTransactionRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_SubmitTransactionRequest); ok { + return x.SubmitTransactionRequest + } + return nil +} + +func (x *KaspadMessage) GetSubmitTransactionResponse() *SubmitTransactionResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_SubmitTransactionResponse); ok { + return x.SubmitTransactionResponse + } + return nil +} + +func (x *KaspadMessage) GetNotifyChainChangedRequest() *NotifyChainChangedRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_NotifyChainChangedRequest); ok { + return x.NotifyChainChangedRequest + } + return nil +} + +func (x *KaspadMessage) GetNotifyChainChangedResponse() *NotifyChainChangedResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_NotifyChainChangedResponse); ok { + return x.NotifyChainChangedResponse + } + return nil +} + +func (x *KaspadMessage) GetChainChangedNotification() *ChainChangedNotificationMessage { + if x, ok := x.GetPayload().(*KaspadMessage_ChainChangedNotification); ok { + return x.ChainChangedNotification + } + return nil +} + +func (x *KaspadMessage) GetGetBlockRequest() *GetBlockRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetBlockRequest); ok { + return x.GetBlockRequest + } + return nil +} + +func (x *KaspadMessage) GetGetBlockResponse() *GetBlockResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetBlockResponse); ok { + return x.GetBlockResponse + } + return nil +} + +func (x *KaspadMessage) GetGetSubnetworkRequest() *GetSubnetworkRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetSubnetworkRequest); ok { + return x.GetSubnetworkRequest + } + return nil +} + +func (x *KaspadMessage) GetGetSubnetworkResponse() *GetSubnetworkResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetSubnetworkResponse); ok { + return x.GetSubnetworkResponse + } + return nil +} + +func (x *KaspadMessage) GetGetChainFromBlockRequest() *GetChainFromBlockRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetChainFromBlockRequest); ok { + return x.GetChainFromBlockRequest + } + return nil +} + +func (x *KaspadMessage) GetGetChainFromBlockResponse() *GetChainFromBlockResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetChainFromBlockResponse); ok { + return x.GetChainFromBlockResponse + } + return nil +} + +func (x *KaspadMessage) GetGetBlocksRequest() *GetBlocksRequestMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetBlocksRequest); ok { + return x.GetBlocksRequest + } + return nil +} + +func (x *KaspadMessage) GetGetBlocksResponse() *GetBlocksResponseMessage { + if x, ok := x.GetPayload().(*KaspadMessage_GetBlocksResponse); ok { + return x.GetBlocksResponse + } + return nil +} + type isKaspadMessage_Payload interface { isKaspadMessage_Payload() } @@ -341,6 +597,134 @@ type KaspadMessage_Reject struct { Reject *RejectMessage `protobuf:"bytes,22,opt,name=reject,proto3,oneof"` } +type KaspadMessage_GetCurrentNetworkRequest struct { + GetCurrentNetworkRequest *GetCurrentNetworkRequestMessage `protobuf:"bytes,1001,opt,name=getCurrentNetworkRequest,proto3,oneof"` +} + +type KaspadMessage_GetCurrentNetworkResponse struct { + GetCurrentNetworkResponse *GetCurrentNetworkResponseMessage `protobuf:"bytes,1002,opt,name=getCurrentNetworkResponse,proto3,oneof"` +} + +type KaspadMessage_SubmitBlockRequest struct { + SubmitBlockRequest *SubmitBlockRequestMessage `protobuf:"bytes,1003,opt,name=submitBlockRequest,proto3,oneof"` +} + +type KaspadMessage_SubmitBlockResponse struct { + SubmitBlockResponse *SubmitBlockResponseMessage `protobuf:"bytes,1004,opt,name=submitBlockResponse,proto3,oneof"` +} + +type KaspadMessage_GetBlockTemplateRequest struct { + GetBlockTemplateRequest *GetBlockTemplateRequestMessage `protobuf:"bytes,1005,opt,name=getBlockTemplateRequest,proto3,oneof"` +} + +type KaspadMessage_GetBlockTemplateResponse struct { + GetBlockTemplateResponse *GetBlockTemplateResponseMessage `protobuf:"bytes,1006,opt,name=getBlockTemplateResponse,proto3,oneof"` +} + +type KaspadMessage_NotifyBlockAddedRequest struct { + NotifyBlockAddedRequest *NotifyBlockAddedRequestMessage `protobuf:"bytes,1007,opt,name=notifyBlockAddedRequest,proto3,oneof"` +} + +type KaspadMessage_NotifyBlockAddedResponse struct { + NotifyBlockAddedResponse *NotifyBlockAddedResponseMessage `protobuf:"bytes,1008,opt,name=notifyBlockAddedResponse,proto3,oneof"` +} + +type KaspadMessage_BlockAddedNotification struct { + BlockAddedNotification *BlockAddedNotificationMessage `protobuf:"bytes,1009,opt,name=blockAddedNotification,proto3,oneof"` +} + +type KaspadMessage_GetPeerAddressesRequest struct { + GetPeerAddressesRequest *GetPeerAddressesRequestMessage `protobuf:"bytes,1010,opt,name=getPeerAddressesRequest,proto3,oneof"` +} + +type KaspadMessage_GetPeerAddressesResponse struct { + GetPeerAddressesResponse *GetPeerAddressesResponseMessage `protobuf:"bytes,1011,opt,name=getPeerAddressesResponse,proto3,oneof"` +} + +type KaspadMessage_GetSelectedTipHashRequest struct { + GetSelectedTipHashRequest *GetSelectedTipHashRequestMessage `protobuf:"bytes,1012,opt,name=getSelectedTipHashRequest,proto3,oneof"` +} + +type KaspadMessage_GetSelectedTipHashResponse struct { + GetSelectedTipHashResponse *GetSelectedTipHashResponseMessage `protobuf:"bytes,1013,opt,name=getSelectedTipHashResponse,proto3,oneof"` +} + +type KaspadMessage_GetMempoolEntryRequest struct { + GetMempoolEntryRequest *GetMempoolEntryRequestMessage `protobuf:"bytes,1014,opt,name=getMempoolEntryRequest,proto3,oneof"` +} + +type KaspadMessage_GetMempoolEntryResponse struct { + GetMempoolEntryResponse *GetMempoolEntryResponseMessage `protobuf:"bytes,1015,opt,name=getMempoolEntryResponse,proto3,oneof"` +} + +type KaspadMessage_GetConnectedPeerInfoRequest struct { + GetConnectedPeerInfoRequest *GetConnectedPeerInfoRequestMessage `protobuf:"bytes,1016,opt,name=getConnectedPeerInfoRequest,proto3,oneof"` +} + +type KaspadMessage_GetConnectedPeerInfoResponse struct { + GetConnectedPeerInfoResponse *GetConnectedPeerInfoResponseMessage `protobuf:"bytes,1017,opt,name=getConnectedPeerInfoResponse,proto3,oneof"` +} + +type KaspadMessage_AddPeerRequest struct { + AddPeerRequest *AddPeerRequestMessage `protobuf:"bytes,1018,opt,name=addPeerRequest,proto3,oneof"` +} + +type KaspadMessage_AddPeerResponse struct { + AddPeerResponse *AddPeerResponseMessage `protobuf:"bytes,1019,opt,name=addPeerResponse,proto3,oneof"` +} + +type KaspadMessage_SubmitTransactionRequest struct { + SubmitTransactionRequest *SubmitTransactionRequestMessage `protobuf:"bytes,1020,opt,name=submitTransactionRequest,proto3,oneof"` +} + +type KaspadMessage_SubmitTransactionResponse struct { + SubmitTransactionResponse *SubmitTransactionResponseMessage `protobuf:"bytes,1021,opt,name=submitTransactionResponse,proto3,oneof"` +} + +type KaspadMessage_NotifyChainChangedRequest struct { + NotifyChainChangedRequest *NotifyChainChangedRequestMessage `protobuf:"bytes,1022,opt,name=notifyChainChangedRequest,proto3,oneof"` +} + +type KaspadMessage_NotifyChainChangedResponse struct { + NotifyChainChangedResponse *NotifyChainChangedResponseMessage `protobuf:"bytes,1023,opt,name=notifyChainChangedResponse,proto3,oneof"` +} + +type KaspadMessage_ChainChangedNotification struct { + ChainChangedNotification *ChainChangedNotificationMessage `protobuf:"bytes,1024,opt,name=chainChangedNotification,proto3,oneof"` +} + +type KaspadMessage_GetBlockRequest struct { + GetBlockRequest *GetBlockRequestMessage `protobuf:"bytes,1025,opt,name=getBlockRequest,proto3,oneof"` +} + +type KaspadMessage_GetBlockResponse struct { + GetBlockResponse *GetBlockResponseMessage `protobuf:"bytes,1026,opt,name=getBlockResponse,proto3,oneof"` +} + +type KaspadMessage_GetSubnetworkRequest struct { + GetSubnetworkRequest *GetSubnetworkRequestMessage `protobuf:"bytes,1027,opt,name=getSubnetworkRequest,proto3,oneof"` +} + +type KaspadMessage_GetSubnetworkResponse struct { + GetSubnetworkResponse *GetSubnetworkResponseMessage `protobuf:"bytes,1028,opt,name=getSubnetworkResponse,proto3,oneof"` +} + +type KaspadMessage_GetChainFromBlockRequest struct { + GetChainFromBlockRequest *GetChainFromBlockRequestMessage `protobuf:"bytes,1029,opt,name=getChainFromBlockRequest,proto3,oneof"` +} + +type KaspadMessage_GetChainFromBlockResponse struct { + GetChainFromBlockResponse *GetChainFromBlockResponseMessage `protobuf:"bytes,1030,opt,name=getChainFromBlockResponse,proto3,oneof"` +} + +type KaspadMessage_GetBlocksRequest struct { + GetBlocksRequest *GetBlocksRequestMessage `protobuf:"bytes,1031,opt,name=getBlocksRequest,proto3,oneof"` +} + +type KaspadMessage_GetBlocksResponse struct { + GetBlocksResponse *GetBlocksResponseMessage `protobuf:"bytes,1032,opt,name=getBlocksResponse,proto3,oneof"` +} + func (*KaspadMessage_Addresses) isKaspadMessage_Payload() {} func (*KaspadMessage_Block) isKaspadMessage_Payload() {} @@ -385,6 +769,70 @@ func (*KaspadMessage_TransactionNotFound) isKaspadMessage_Payload() {} func (*KaspadMessage_Reject) isKaspadMessage_Payload() {} +func (*KaspadMessage_GetCurrentNetworkRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetCurrentNetworkResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_SubmitBlockRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_SubmitBlockResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetBlockTemplateRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetBlockTemplateResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_NotifyBlockAddedRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_NotifyBlockAddedResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_BlockAddedNotification) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetPeerAddressesRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetPeerAddressesResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetSelectedTipHashRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetSelectedTipHashResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetMempoolEntryRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetMempoolEntryResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetConnectedPeerInfoRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetConnectedPeerInfoResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_AddPeerRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_AddPeerResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_SubmitTransactionRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_SubmitTransactionResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_NotifyChainChangedRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_NotifyChainChangedResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_ChainChangedNotification) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetBlockRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetBlockResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetSubnetworkRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetSubnetworkResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetChainFromBlockRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetChainFromBlockResponse) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetBlocksRequest) isKaspadMessage_Payload() {} + +func (*KaspadMessage_GetBlocksResponse) isKaspadMessage_Payload() {} + // AddressesMessage start type AddressesMessage struct { state protoimpl.MessageState @@ -2021,11 +2469,2841 @@ func (x *RejectMessage) GetReason() string { return "" } +type RPCError struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *RPCError) Reset() { + *x = RPCError{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RPCError) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RPCError) ProtoMessage() {} + +func (x *RPCError) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RPCError.ProtoReflect.Descriptor instead. +func (*RPCError) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{30} +} + +func (x *RPCError) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type GetCurrentNetworkRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetCurrentNetworkRequestMessage) Reset() { + *x = GetCurrentNetworkRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetCurrentNetworkRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCurrentNetworkRequestMessage) ProtoMessage() {} + +func (x *GetCurrentNetworkRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCurrentNetworkRequestMessage.ProtoReflect.Descriptor instead. +func (*GetCurrentNetworkRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{31} +} + +type GetCurrentNetworkResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CurrentNetwork string `protobuf:"bytes,1,opt,name=currentNetwork,proto3" json:"currentNetwork,omitempty"` + Error *RPCError `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *GetCurrentNetworkResponseMessage) Reset() { + *x = GetCurrentNetworkResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetCurrentNetworkResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCurrentNetworkResponseMessage) ProtoMessage() {} + +func (x *GetCurrentNetworkResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCurrentNetworkResponseMessage.ProtoReflect.Descriptor instead. +func (*GetCurrentNetworkResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{32} +} + +func (x *GetCurrentNetworkResponseMessage) GetCurrentNetwork() string { + if x != nil { + return x.CurrentNetwork + } + return "" +} + +func (x *GetCurrentNetworkResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type SubmitBlockRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BlockHex string `protobuf:"bytes,1,opt,name=blockHex,proto3" json:"blockHex,omitempty"` +} + +func (x *SubmitBlockRequestMessage) Reset() { + *x = SubmitBlockRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubmitBlockRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitBlockRequestMessage) ProtoMessage() {} + +func (x *SubmitBlockRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitBlockRequestMessage.ProtoReflect.Descriptor instead. +func (*SubmitBlockRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{33} +} + +func (x *SubmitBlockRequestMessage) GetBlockHex() string { + if x != nil { + return x.BlockHex + } + return "" +} + +type SubmitBlockResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Error *RPCError `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *SubmitBlockResponseMessage) Reset() { + *x = SubmitBlockResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubmitBlockResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitBlockResponseMessage) ProtoMessage() {} + +func (x *SubmitBlockResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitBlockResponseMessage.ProtoReflect.Descriptor instead. +func (*SubmitBlockResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{34} +} + +func (x *SubmitBlockResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type GetBlockTemplateRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PayAddress string `protobuf:"bytes,1,opt,name=payAddress,proto3" json:"payAddress,omitempty"` + LongPollId string `protobuf:"bytes,2,opt,name=longPollId,proto3" json:"longPollId,omitempty"` +} + +func (x *GetBlockTemplateRequestMessage) Reset() { + *x = GetBlockTemplateRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockTemplateRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockTemplateRequestMessage) ProtoMessage() {} + +func (x *GetBlockTemplateRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[35] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBlockTemplateRequestMessage.ProtoReflect.Descriptor instead. +func (*GetBlockTemplateRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{35} +} + +func (x *GetBlockTemplateRequestMessage) GetPayAddress() string { + if x != nil { + return x.PayAddress + } + return "" +} + +func (x *GetBlockTemplateRequestMessage) GetLongPollId() string { + if x != nil { + return x.LongPollId + } + return "" +} + +type GetBlockTemplateResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Bits string `protobuf:"bytes,1,opt,name=bits,proto3" json:"bits,omitempty"` + CurrentTime int64 `protobuf:"varint,2,opt,name=currentTime,proto3" json:"currentTime,omitempty"` + ParentHashes []string `protobuf:"bytes,3,rep,name=parentHashes,proto3" json:"parentHashes,omitempty"` + MassLimit int32 `protobuf:"varint,4,opt,name=massLimit,proto3" json:"massLimit,omitempty"` + Transactions []*GetBlockTemplateTransactionMessage `protobuf:"bytes,5,rep,name=transactions,proto3" json:"transactions,omitempty"` + HashMerkleRoot string `protobuf:"bytes,6,opt,name=hashMerkleRoot,proto3" json:"hashMerkleRoot,omitempty"` + AcceptedIDMerkleRoot string `protobuf:"bytes,7,opt,name=acceptedIDMerkleRoot,proto3" json:"acceptedIDMerkleRoot,omitempty"` + UtxoCommitment string `protobuf:"bytes,8,opt,name=utxoCommitment,proto3" json:"utxoCommitment,omitempty"` + Version int32 `protobuf:"varint,9,opt,name=version,proto3" json:"version,omitempty"` + LongPollId string `protobuf:"bytes,10,opt,name=longPollId,proto3" json:"longPollId,omitempty"` + TargetDifficulty string `protobuf:"bytes,11,opt,name=targetDifficulty,proto3" json:"targetDifficulty,omitempty"` + MinTime int64 `protobuf:"varint,12,opt,name=minTime,proto3" json:"minTime,omitempty"` + MaxTime int64 `protobuf:"varint,13,opt,name=maxTime,proto3" json:"maxTime,omitempty"` + MutableFields []string `protobuf:"bytes,14,rep,name=mutableFields,proto3" json:"mutableFields,omitempty"` + NonceRange string `protobuf:"bytes,15,opt,name=nonceRange,proto3" json:"nonceRange,omitempty"` + IsSynced bool `protobuf:"varint,16,opt,name=isSynced,proto3" json:"isSynced,omitempty"` + IsConnected bool `protobuf:"varint,17,opt,name=isConnected,proto3" json:"isConnected,omitempty"` + Error *RPCError `protobuf:"bytes,18,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *GetBlockTemplateResponseMessage) Reset() { + *x = GetBlockTemplateResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockTemplateResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockTemplateResponseMessage) ProtoMessage() {} + +func (x *GetBlockTemplateResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBlockTemplateResponseMessage.ProtoReflect.Descriptor instead. +func (*GetBlockTemplateResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{36} +} + +func (x *GetBlockTemplateResponseMessage) GetBits() string { + if x != nil { + return x.Bits + } + return "" +} + +func (x *GetBlockTemplateResponseMessage) GetCurrentTime() int64 { + if x != nil { + return x.CurrentTime + } + return 0 +} + +func (x *GetBlockTemplateResponseMessage) GetParentHashes() []string { + if x != nil { + return x.ParentHashes + } + return nil +} + +func (x *GetBlockTemplateResponseMessage) GetMassLimit() int32 { + if x != nil { + return x.MassLimit + } + return 0 +} + +func (x *GetBlockTemplateResponseMessage) GetTransactions() []*GetBlockTemplateTransactionMessage { + if x != nil { + return x.Transactions + } + return nil +} + +func (x *GetBlockTemplateResponseMessage) GetHashMerkleRoot() string { + if x != nil { + return x.HashMerkleRoot + } + return "" +} + +func (x *GetBlockTemplateResponseMessage) GetAcceptedIDMerkleRoot() string { + if x != nil { + return x.AcceptedIDMerkleRoot + } + return "" +} + +func (x *GetBlockTemplateResponseMessage) GetUtxoCommitment() string { + if x != nil { + return x.UtxoCommitment + } + return "" +} + +func (x *GetBlockTemplateResponseMessage) GetVersion() int32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *GetBlockTemplateResponseMessage) GetLongPollId() string { + if x != nil { + return x.LongPollId + } + return "" +} + +func (x *GetBlockTemplateResponseMessage) GetTargetDifficulty() string { + if x != nil { + return x.TargetDifficulty + } + return "" +} + +func (x *GetBlockTemplateResponseMessage) GetMinTime() int64 { + if x != nil { + return x.MinTime + } + return 0 +} + +func (x *GetBlockTemplateResponseMessage) GetMaxTime() int64 { + if x != nil { + return x.MaxTime + } + return 0 +} + +func (x *GetBlockTemplateResponseMessage) GetMutableFields() []string { + if x != nil { + return x.MutableFields + } + return nil +} + +func (x *GetBlockTemplateResponseMessage) GetNonceRange() string { + if x != nil { + return x.NonceRange + } + return "" +} + +func (x *GetBlockTemplateResponseMessage) GetIsSynced() bool { + if x != nil { + return x.IsSynced + } + return false +} + +func (x *GetBlockTemplateResponseMessage) GetIsConnected() bool { + if x != nil { + return x.IsConnected + } + return false +} + +func (x *GetBlockTemplateResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type GetBlockTemplateTransactionMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Depends []int64 `protobuf:"varint,3,rep,packed,name=depends,proto3" json:"depends,omitempty"` + Mass uint64 `protobuf:"varint,4,opt,name=mass,proto3" json:"mass,omitempty"` + Fee uint64 `protobuf:"varint,5,opt,name=fee,proto3" json:"fee,omitempty"` +} + +func (x *GetBlockTemplateTransactionMessage) Reset() { + *x = GetBlockTemplateTransactionMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockTemplateTransactionMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockTemplateTransactionMessage) ProtoMessage() {} + +func (x *GetBlockTemplateTransactionMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBlockTemplateTransactionMessage.ProtoReflect.Descriptor instead. +func (*GetBlockTemplateTransactionMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{37} +} + +func (x *GetBlockTemplateTransactionMessage) GetData() string { + if x != nil { + return x.Data + } + return "" +} + +func (x *GetBlockTemplateTransactionMessage) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *GetBlockTemplateTransactionMessage) GetDepends() []int64 { + if x != nil { + return x.Depends + } + return nil +} + +func (x *GetBlockTemplateTransactionMessage) GetMass() uint64 { + if x != nil { + return x.Mass + } + return 0 +} + +func (x *GetBlockTemplateTransactionMessage) GetFee() uint64 { + if x != nil { + return x.Fee + } + return 0 +} + +type NotifyBlockAddedRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *NotifyBlockAddedRequestMessage) Reset() { + *x = NotifyBlockAddedRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NotifyBlockAddedRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotifyBlockAddedRequestMessage) ProtoMessage() {} + +func (x *NotifyBlockAddedRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NotifyBlockAddedRequestMessage.ProtoReflect.Descriptor instead. +func (*NotifyBlockAddedRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{38} +} + +type NotifyBlockAddedResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Error *RPCError `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *NotifyBlockAddedResponseMessage) Reset() { + *x = NotifyBlockAddedResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NotifyBlockAddedResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotifyBlockAddedResponseMessage) ProtoMessage() {} + +func (x *NotifyBlockAddedResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[39] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NotifyBlockAddedResponseMessage.ProtoReflect.Descriptor instead. +func (*NotifyBlockAddedResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{39} +} + +func (x *NotifyBlockAddedResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type BlockAddedNotificationMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Block *BlockMessage `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"` +} + +func (x *BlockAddedNotificationMessage) Reset() { + *x = BlockAddedNotificationMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockAddedNotificationMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockAddedNotificationMessage) ProtoMessage() {} + +func (x *BlockAddedNotificationMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[40] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlockAddedNotificationMessage.ProtoReflect.Descriptor instead. +func (*BlockAddedNotificationMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{40} +} + +func (x *BlockAddedNotificationMessage) GetBlock() *BlockMessage { + if x != nil { + return x.Block + } + return nil +} + +type GetPeerAddressesRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetPeerAddressesRequestMessage) Reset() { + *x = GetPeerAddressesRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPeerAddressesRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPeerAddressesRequestMessage) ProtoMessage() {} + +func (x *GetPeerAddressesRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPeerAddressesRequestMessage.ProtoReflect.Descriptor instead. +func (*GetPeerAddressesRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{41} +} + +type GetPeerAddressesResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addresses []*GetPeerAddressesKnownAddressMessage `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` + Error *RPCError `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *GetPeerAddressesResponseMessage) Reset() { + *x = GetPeerAddressesResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPeerAddressesResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPeerAddressesResponseMessage) ProtoMessage() {} + +func (x *GetPeerAddressesResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[42] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPeerAddressesResponseMessage.ProtoReflect.Descriptor instead. +func (*GetPeerAddressesResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{42} +} + +func (x *GetPeerAddressesResponseMessage) GetAddresses() []*GetPeerAddressesKnownAddressMessage { + if x != nil { + return x.Addresses + } + return nil +} + +func (x *GetPeerAddressesResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type GetPeerAddressesKnownAddressMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addr string `protobuf:"bytes,1,opt,name=Addr,proto3" json:"Addr,omitempty"` +} + +func (x *GetPeerAddressesKnownAddressMessage) Reset() { + *x = GetPeerAddressesKnownAddressMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPeerAddressesKnownAddressMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPeerAddressesKnownAddressMessage) ProtoMessage() {} + +func (x *GetPeerAddressesKnownAddressMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[43] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPeerAddressesKnownAddressMessage.ProtoReflect.Descriptor instead. +func (*GetPeerAddressesKnownAddressMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{43} +} + +func (x *GetPeerAddressesKnownAddressMessage) GetAddr() string { + if x != nil { + return x.Addr + } + return "" +} + +type GetSelectedTipHashRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetSelectedTipHashRequestMessage) Reset() { + *x = GetSelectedTipHashRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetSelectedTipHashRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSelectedTipHashRequestMessage) ProtoMessage() {} + +func (x *GetSelectedTipHashRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[44] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSelectedTipHashRequestMessage.ProtoReflect.Descriptor instead. +func (*GetSelectedTipHashRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{44} +} + +type GetSelectedTipHashResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SelectedTipHash string `protobuf:"bytes,1,opt,name=selectedTipHash,proto3" json:"selectedTipHash,omitempty"` + Error *RPCError `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *GetSelectedTipHashResponseMessage) Reset() { + *x = GetSelectedTipHashResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetSelectedTipHashResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSelectedTipHashResponseMessage) ProtoMessage() {} + +func (x *GetSelectedTipHashResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[45] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSelectedTipHashResponseMessage.ProtoReflect.Descriptor instead. +func (*GetSelectedTipHashResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{45} +} + +func (x *GetSelectedTipHashResponseMessage) GetSelectedTipHash() string { + if x != nil { + return x.SelectedTipHash + } + return "" +} + +func (x *GetSelectedTipHashResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type GetMempoolEntryRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TxId string `protobuf:"bytes,1,opt,name=txId,proto3" json:"txId,omitempty"` +} + +func (x *GetMempoolEntryRequestMessage) Reset() { + *x = GetMempoolEntryRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMempoolEntryRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMempoolEntryRequestMessage) ProtoMessage() {} + +func (x *GetMempoolEntryRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[46] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetMempoolEntryRequestMessage.ProtoReflect.Descriptor instead. +func (*GetMempoolEntryRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{46} +} + +func (x *GetMempoolEntryRequestMessage) GetTxId() string { + if x != nil { + return x.TxId + } + return "" +} + +type GetMempoolEntryResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Error *RPCError `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *GetMempoolEntryResponseMessage) Reset() { + *x = GetMempoolEntryResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMempoolEntryResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMempoolEntryResponseMessage) ProtoMessage() {} + +func (x *GetMempoolEntryResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[47] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetMempoolEntryResponseMessage.ProtoReflect.Descriptor instead. +func (*GetMempoolEntryResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{47} +} + +func (x *GetMempoolEntryResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type GetConnectedPeerInfoRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetConnectedPeerInfoRequestMessage) Reset() { + *x = GetConnectedPeerInfoRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetConnectedPeerInfoRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetConnectedPeerInfoRequestMessage) ProtoMessage() {} + +func (x *GetConnectedPeerInfoRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[48] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetConnectedPeerInfoRequestMessage.ProtoReflect.Descriptor instead. +func (*GetConnectedPeerInfoRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{48} +} + +type GetConnectedPeerInfoResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Infos []*GetConnectedPeerInfoMessage `protobuf:"bytes,1,rep,name=infos,proto3" json:"infos,omitempty"` + Error *RPCError `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *GetConnectedPeerInfoResponseMessage) Reset() { + *x = GetConnectedPeerInfoResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetConnectedPeerInfoResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetConnectedPeerInfoResponseMessage) ProtoMessage() {} + +func (x *GetConnectedPeerInfoResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[49] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetConnectedPeerInfoResponseMessage.ProtoReflect.Descriptor instead. +func (*GetConnectedPeerInfoResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{49} +} + +func (x *GetConnectedPeerInfoResponseMessage) GetInfos() []*GetConnectedPeerInfoMessage { + if x != nil { + return x.Infos + } + return nil +} + +func (x *GetConnectedPeerInfoResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type GetConnectedPeerInfoMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + LastPingDuration int64 `protobuf:"varint,3,opt,name=lastPingDuration,proto3" json:"lastPingDuration,omitempty"` + SelectedTipHash string `protobuf:"bytes,4,opt,name=selectedTipHash,proto3" json:"selectedTipHash,omitempty"` + IsSyncNode bool `protobuf:"varint,5,opt,name=isSyncNode,proto3" json:"isSyncNode,omitempty"` + IsOutbound bool `protobuf:"varint,6,opt,name=isOutbound,proto3" json:"isOutbound,omitempty"` + TimeOffset int64 `protobuf:"varint,7,opt,name=timeOffset,proto3" json:"timeOffset,omitempty"` + UserAgent string `protobuf:"bytes,8,opt,name=userAgent,proto3" json:"userAgent,omitempty"` + AdvertisedProtocolVersion uint32 `protobuf:"varint,9,opt,name=advertisedProtocolVersion,proto3" json:"advertisedProtocolVersion,omitempty"` + TimeConnected int64 `protobuf:"varint,10,opt,name=timeConnected,proto3" json:"timeConnected,omitempty"` +} + +func (x *GetConnectedPeerInfoMessage) Reset() { + *x = GetConnectedPeerInfoMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetConnectedPeerInfoMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetConnectedPeerInfoMessage) ProtoMessage() {} + +func (x *GetConnectedPeerInfoMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[50] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetConnectedPeerInfoMessage.ProtoReflect.Descriptor instead. +func (*GetConnectedPeerInfoMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{50} +} + +func (x *GetConnectedPeerInfoMessage) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *GetConnectedPeerInfoMessage) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *GetConnectedPeerInfoMessage) GetLastPingDuration() int64 { + if x != nil { + return x.LastPingDuration + } + return 0 +} + +func (x *GetConnectedPeerInfoMessage) GetSelectedTipHash() string { + if x != nil { + return x.SelectedTipHash + } + return "" +} + +func (x *GetConnectedPeerInfoMessage) GetIsSyncNode() bool { + if x != nil { + return x.IsSyncNode + } + return false +} + +func (x *GetConnectedPeerInfoMessage) GetIsOutbound() bool { + if x != nil { + return x.IsOutbound + } + return false +} + +func (x *GetConnectedPeerInfoMessage) GetTimeOffset() int64 { + if x != nil { + return x.TimeOffset + } + return 0 +} + +func (x *GetConnectedPeerInfoMessage) GetUserAgent() string { + if x != nil { + return x.UserAgent + } + return "" +} + +func (x *GetConnectedPeerInfoMessage) GetAdvertisedProtocolVersion() uint32 { + if x != nil { + return x.AdvertisedProtocolVersion + } + return 0 +} + +func (x *GetConnectedPeerInfoMessage) GetTimeConnected() int64 { + if x != nil { + return x.TimeConnected + } + return 0 +} + +type AddPeerRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + IsPermanent bool `protobuf:"varint,2,opt,name=isPermanent,proto3" json:"isPermanent,omitempty"` +} + +func (x *AddPeerRequestMessage) Reset() { + *x = AddPeerRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddPeerRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddPeerRequestMessage) ProtoMessage() {} + +func (x *AddPeerRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[51] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddPeerRequestMessage.ProtoReflect.Descriptor instead. +func (*AddPeerRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{51} +} + +func (x *AddPeerRequestMessage) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *AddPeerRequestMessage) GetIsPermanent() bool { + if x != nil { + return x.IsPermanent + } + return false +} + +type AddPeerResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Error *RPCError `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *AddPeerResponseMessage) Reset() { + *x = AddPeerResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddPeerResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddPeerResponseMessage) ProtoMessage() {} + +func (x *AddPeerResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[52] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddPeerResponseMessage.ProtoReflect.Descriptor instead. +func (*AddPeerResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{52} +} + +func (x *AddPeerResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type SubmitTransactionRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TransactionHex string `protobuf:"bytes,1,opt,name=transactionHex,proto3" json:"transactionHex,omitempty"` +} + +func (x *SubmitTransactionRequestMessage) Reset() { + *x = SubmitTransactionRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubmitTransactionRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitTransactionRequestMessage) ProtoMessage() {} + +func (x *SubmitTransactionRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[53] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitTransactionRequestMessage.ProtoReflect.Descriptor instead. +func (*SubmitTransactionRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{53} +} + +func (x *SubmitTransactionRequestMessage) GetTransactionHex() string { + if x != nil { + return x.TransactionHex + } + return "" +} + +type SubmitTransactionResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TxId string `protobuf:"bytes,1,opt,name=txId,proto3" json:"txId,omitempty"` + Error *RPCError `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *SubmitTransactionResponseMessage) Reset() { + *x = SubmitTransactionResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[54] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SubmitTransactionResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitTransactionResponseMessage) ProtoMessage() {} + +func (x *SubmitTransactionResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[54] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitTransactionResponseMessage.ProtoReflect.Descriptor instead. +func (*SubmitTransactionResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{54} +} + +func (x *SubmitTransactionResponseMessage) GetTxId() string { + if x != nil { + return x.TxId + } + return "" +} + +func (x *SubmitTransactionResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type NotifyChainChangedRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *NotifyChainChangedRequestMessage) Reset() { + *x = NotifyChainChangedRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[55] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NotifyChainChangedRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotifyChainChangedRequestMessage) ProtoMessage() {} + +func (x *NotifyChainChangedRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[55] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NotifyChainChangedRequestMessage.ProtoReflect.Descriptor instead. +func (*NotifyChainChangedRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{55} +} + +type NotifyChainChangedResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Error *RPCError `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *NotifyChainChangedResponseMessage) Reset() { + *x = NotifyChainChangedResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[56] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NotifyChainChangedResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotifyChainChangedResponseMessage) ProtoMessage() {} + +func (x *NotifyChainChangedResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[56] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NotifyChainChangedResponseMessage.ProtoReflect.Descriptor instead. +func (*NotifyChainChangedResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{56} +} + +func (x *NotifyChainChangedResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type ChainChangedNotificationMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RemovedChainBlockHashes []string `protobuf:"bytes,1,rep,name=removedChainBlockHashes,proto3" json:"removedChainBlockHashes,omitempty"` + AddedChainBlocks []*ChainBlock `protobuf:"bytes,2,rep,name=addedChainBlocks,proto3" json:"addedChainBlocks,omitempty"` +} + +func (x *ChainChangedNotificationMessage) Reset() { + *x = ChainChangedNotificationMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[57] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChainChangedNotificationMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChainChangedNotificationMessage) ProtoMessage() {} + +func (x *ChainChangedNotificationMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[57] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChainChangedNotificationMessage.ProtoReflect.Descriptor instead. +func (*ChainChangedNotificationMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{57} +} + +func (x *ChainChangedNotificationMessage) GetRemovedChainBlockHashes() []string { + if x != nil { + return x.RemovedChainBlockHashes + } + return nil +} + +func (x *ChainChangedNotificationMessage) GetAddedChainBlocks() []*ChainBlock { + if x != nil { + return x.AddedChainBlocks + } + return nil +} + +type ChainBlock struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + AcceptedBlocks []*AcceptedBlock `protobuf:"bytes,2,rep,name=acceptedBlocks,proto3" json:"acceptedBlocks,omitempty"` +} + +func (x *ChainBlock) Reset() { + *x = ChainBlock{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[58] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChainBlock) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChainBlock) ProtoMessage() {} + +func (x *ChainBlock) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[58] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChainBlock.ProtoReflect.Descriptor instead. +func (*ChainBlock) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{58} +} + +func (x *ChainBlock) GetHash() string { + if x != nil { + return x.Hash + } + return "" +} + +func (x *ChainBlock) GetAcceptedBlocks() []*AcceptedBlock { + if x != nil { + return x.AcceptedBlocks + } + return nil +} + +type AcceptedBlock struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + AcceptedTxIds []string `protobuf:"bytes,2,rep,name=acceptedTxIds,proto3" json:"acceptedTxIds,omitempty"` +} + +func (x *AcceptedBlock) Reset() { + *x = AcceptedBlock{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[59] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AcceptedBlock) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AcceptedBlock) ProtoMessage() {} + +func (x *AcceptedBlock) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[59] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AcceptedBlock.ProtoReflect.Descriptor instead. +func (*AcceptedBlock) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{59} +} + +func (x *AcceptedBlock) GetHash() string { + if x != nil { + return x.Hash + } + return "" +} + +func (x *AcceptedBlock) GetAcceptedTxIds() []string { + if x != nil { + return x.AcceptedTxIds + } + return nil +} + +type GetBlockRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + SubnetworkId string `protobuf:"bytes,2,opt,name=subnetworkId,proto3" json:"subnetworkId,omitempty"` + IncludeBlockHex bool `protobuf:"varint,3,opt,name=includeBlockHex,proto3" json:"includeBlockHex,omitempty"` + IncludeBlockVerboseData bool `protobuf:"varint,4,opt,name=includeBlockVerboseData,proto3" json:"includeBlockVerboseData,omitempty"` + IncludeTransactionVerboseData bool `protobuf:"varint,5,opt,name=includeTransactionVerboseData,proto3" json:"includeTransactionVerboseData,omitempty"` +} + +func (x *GetBlockRequestMessage) Reset() { + *x = GetBlockRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[60] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockRequestMessage) ProtoMessage() {} + +func (x *GetBlockRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[60] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBlockRequestMessage.ProtoReflect.Descriptor instead. +func (*GetBlockRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{60} +} + +func (x *GetBlockRequestMessage) GetHash() string { + if x != nil { + return x.Hash + } + return "" +} + +func (x *GetBlockRequestMessage) GetSubnetworkId() string { + if x != nil { + return x.SubnetworkId + } + return "" +} + +func (x *GetBlockRequestMessage) GetIncludeBlockHex() bool { + if x != nil { + return x.IncludeBlockHex + } + return false +} + +func (x *GetBlockRequestMessage) GetIncludeBlockVerboseData() bool { + if x != nil { + return x.IncludeBlockVerboseData + } + return false +} + +func (x *GetBlockRequestMessage) GetIncludeTransactionVerboseData() bool { + if x != nil { + return x.IncludeTransactionVerboseData + } + return false +} + +type GetBlockResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BlockHash string `protobuf:"bytes,1,opt,name=blockHash,proto3" json:"blockHash,omitempty"` + BlockHex string `protobuf:"bytes,2,opt,name=blockHex,proto3" json:"blockHex,omitempty"` + BlockVerboseData *BlockVerboseData `protobuf:"bytes,3,opt,name=blockVerboseData,proto3" json:"blockVerboseData,omitempty"` + Error *RPCError `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *GetBlockResponseMessage) Reset() { + *x = GetBlockResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[61] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockResponseMessage) ProtoMessage() {} + +func (x *GetBlockResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[61] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBlockResponseMessage.ProtoReflect.Descriptor instead. +func (*GetBlockResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{61} +} + +func (x *GetBlockResponseMessage) GetBlockHash() string { + if x != nil { + return x.BlockHash + } + return "" +} + +func (x *GetBlockResponseMessage) GetBlockHex() string { + if x != nil { + return x.BlockHex + } + return "" +} + +func (x *GetBlockResponseMessage) GetBlockVerboseData() *BlockVerboseData { + if x != nil { + return x.BlockVerboseData + } + return nil +} + +func (x *GetBlockResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type BlockVerboseData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + Confirmations uint64 `protobuf:"varint,2,opt,name=confirmations,proto3" json:"confirmations,omitempty"` + Size int32 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` + BlueScore uint64 `protobuf:"varint,4,opt,name=blueScore,proto3" json:"blueScore,omitempty"` + IsChainBlock bool `protobuf:"varint,5,opt,name=isChainBlock,proto3" json:"isChainBlock,omitempty"` + Version int32 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"` + VersionHex string `protobuf:"bytes,7,opt,name=versionHex,proto3" json:"versionHex,omitempty"` + HashMerkleRoot string `protobuf:"bytes,8,opt,name=hashMerkleRoot,proto3" json:"hashMerkleRoot,omitempty"` + AcceptedIDMerkleRoot string `protobuf:"bytes,9,opt,name=acceptedIDMerkleRoot,proto3" json:"acceptedIDMerkleRoot,omitempty"` + UtxoCommitment string `protobuf:"bytes,10,opt,name=utxoCommitment,proto3" json:"utxoCommitment,omitempty"` + TransactionHex []string `protobuf:"bytes,11,rep,name=transactionHex,proto3" json:"transactionHex,omitempty"` + TransactionVerboseData []*TransactionVerboseData `protobuf:"bytes,12,rep,name=transactionVerboseData,proto3" json:"transactionVerboseData,omitempty"` + Time int64 `protobuf:"varint,13,opt,name=time,proto3" json:"time,omitempty"` + Nonce uint64 `protobuf:"varint,14,opt,name=nonce,proto3" json:"nonce,omitempty"` + Bits string `protobuf:"bytes,15,opt,name=bits,proto3" json:"bits,omitempty"` + Difficulty float64 `protobuf:"fixed64,16,opt,name=difficulty,proto3" json:"difficulty,omitempty"` + ParentHashes []string `protobuf:"bytes,17,rep,name=parentHashes,proto3" json:"parentHashes,omitempty"` + SelectedParentHash string `protobuf:"bytes,18,opt,name=selectedParentHash,proto3" json:"selectedParentHash,omitempty"` + ChildHashes []string `protobuf:"bytes,19,rep,name=childHashes,proto3" json:"childHashes,omitempty"` + AcceptedBlockHashes []string `protobuf:"bytes,20,rep,name=acceptedBlockHashes,proto3" json:"acceptedBlockHashes,omitempty"` +} + +func (x *BlockVerboseData) Reset() { + *x = BlockVerboseData{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[62] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockVerboseData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockVerboseData) ProtoMessage() {} + +func (x *BlockVerboseData) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[62] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlockVerboseData.ProtoReflect.Descriptor instead. +func (*BlockVerboseData) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{62} +} + +func (x *BlockVerboseData) GetHash() string { + if x != nil { + return x.Hash + } + return "" +} + +func (x *BlockVerboseData) GetConfirmations() uint64 { + if x != nil { + return x.Confirmations + } + return 0 +} + +func (x *BlockVerboseData) GetSize() int32 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *BlockVerboseData) GetBlueScore() uint64 { + if x != nil { + return x.BlueScore + } + return 0 +} + +func (x *BlockVerboseData) GetIsChainBlock() bool { + if x != nil { + return x.IsChainBlock + } + return false +} + +func (x *BlockVerboseData) GetVersion() int32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *BlockVerboseData) GetVersionHex() string { + if x != nil { + return x.VersionHex + } + return "" +} + +func (x *BlockVerboseData) GetHashMerkleRoot() string { + if x != nil { + return x.HashMerkleRoot + } + return "" +} + +func (x *BlockVerboseData) GetAcceptedIDMerkleRoot() string { + if x != nil { + return x.AcceptedIDMerkleRoot + } + return "" +} + +func (x *BlockVerboseData) GetUtxoCommitment() string { + if x != nil { + return x.UtxoCommitment + } + return "" +} + +func (x *BlockVerboseData) GetTransactionHex() []string { + if x != nil { + return x.TransactionHex + } + return nil +} + +func (x *BlockVerboseData) GetTransactionVerboseData() []*TransactionVerboseData { + if x != nil { + return x.TransactionVerboseData + } + return nil +} + +func (x *BlockVerboseData) GetTime() int64 { + if x != nil { + return x.Time + } + return 0 +} + +func (x *BlockVerboseData) GetNonce() uint64 { + if x != nil { + return x.Nonce + } + return 0 +} + +func (x *BlockVerboseData) GetBits() string { + if x != nil { + return x.Bits + } + return "" +} + +func (x *BlockVerboseData) GetDifficulty() float64 { + if x != nil { + return x.Difficulty + } + return 0 +} + +func (x *BlockVerboseData) GetParentHashes() []string { + if x != nil { + return x.ParentHashes + } + return nil +} + +func (x *BlockVerboseData) GetSelectedParentHash() string { + if x != nil { + return x.SelectedParentHash + } + return "" +} + +func (x *BlockVerboseData) GetChildHashes() []string { + if x != nil { + return x.ChildHashes + } + return nil +} + +func (x *BlockVerboseData) GetAcceptedBlockHashes() []string { + if x != nil { + return x.AcceptedBlockHashes + } + return nil +} + +type TransactionVerboseData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hex string `protobuf:"bytes,1,opt,name=hex,proto3" json:"hex,omitempty"` + TxId string `protobuf:"bytes,2,opt,name=txId,proto3" json:"txId,omitempty"` + Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty"` + Size int32 `protobuf:"varint,4,opt,name=size,proto3" json:"size,omitempty"` + Version int32 `protobuf:"varint,5,opt,name=version,proto3" json:"version,omitempty"` + LockTime uint64 `protobuf:"varint,6,opt,name=lockTime,proto3" json:"lockTime,omitempty"` + SubnetworkId string `protobuf:"bytes,7,opt,name=subnetworkId,proto3" json:"subnetworkId,omitempty"` + Gas uint64 `protobuf:"varint,8,opt,name=gas,proto3" json:"gas,omitempty"` + PayloadHash string `protobuf:"bytes,9,opt,name=payloadHash,proto3" json:"payloadHash,omitempty"` + Payload string `protobuf:"bytes,10,opt,name=payload,proto3" json:"payload,omitempty"` + TransactionVerboseInputs []*TransactionVerboseInput `protobuf:"bytes,11,rep,name=transactionVerboseInputs,proto3" json:"transactionVerboseInputs,omitempty"` + TransactionVerboseOutputs []*TransactionVerboseOutput `protobuf:"bytes,12,rep,name=transactionVerboseOutputs,proto3" json:"transactionVerboseOutputs,omitempty"` + BlockHash string `protobuf:"bytes,13,opt,name=blockHash,proto3" json:"blockHash,omitempty"` + AcceptedBy string `protobuf:"bytes,14,opt,name=acceptedBy,proto3" json:"acceptedBy,omitempty"` + IsInMempool bool `protobuf:"varint,15,opt,name=isInMempool,proto3" json:"isInMempool,omitempty"` + Time uint64 `protobuf:"varint,16,opt,name=time,proto3" json:"time,omitempty"` + BlockTime uint64 `protobuf:"varint,17,opt,name=blockTime,proto3" json:"blockTime,omitempty"` +} + +func (x *TransactionVerboseData) Reset() { + *x = TransactionVerboseData{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[63] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransactionVerboseData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransactionVerboseData) ProtoMessage() {} + +func (x *TransactionVerboseData) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[63] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransactionVerboseData.ProtoReflect.Descriptor instead. +func (*TransactionVerboseData) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{63} +} + +func (x *TransactionVerboseData) GetHex() string { + if x != nil { + return x.Hex + } + return "" +} + +func (x *TransactionVerboseData) GetTxId() string { + if x != nil { + return x.TxId + } + return "" +} + +func (x *TransactionVerboseData) GetHash() string { + if x != nil { + return x.Hash + } + return "" +} + +func (x *TransactionVerboseData) GetSize() int32 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *TransactionVerboseData) GetVersion() int32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *TransactionVerboseData) GetLockTime() uint64 { + if x != nil { + return x.LockTime + } + return 0 +} + +func (x *TransactionVerboseData) GetSubnetworkId() string { + if x != nil { + return x.SubnetworkId + } + return "" +} + +func (x *TransactionVerboseData) GetGas() uint64 { + if x != nil { + return x.Gas + } + return 0 +} + +func (x *TransactionVerboseData) GetPayloadHash() string { + if x != nil { + return x.PayloadHash + } + return "" +} + +func (x *TransactionVerboseData) GetPayload() string { + if x != nil { + return x.Payload + } + return "" +} + +func (x *TransactionVerboseData) GetTransactionVerboseInputs() []*TransactionVerboseInput { + if x != nil { + return x.TransactionVerboseInputs + } + return nil +} + +func (x *TransactionVerboseData) GetTransactionVerboseOutputs() []*TransactionVerboseOutput { + if x != nil { + return x.TransactionVerboseOutputs + } + return nil +} + +func (x *TransactionVerboseData) GetBlockHash() string { + if x != nil { + return x.BlockHash + } + return "" +} + +func (x *TransactionVerboseData) GetAcceptedBy() string { + if x != nil { + return x.AcceptedBy + } + return "" +} + +func (x *TransactionVerboseData) GetIsInMempool() bool { + if x != nil { + return x.IsInMempool + } + return false +} + +func (x *TransactionVerboseData) GetTime() uint64 { + if x != nil { + return x.Time + } + return 0 +} + +func (x *TransactionVerboseData) GetBlockTime() uint64 { + if x != nil { + return x.BlockTime + } + return 0 +} + +type TransactionVerboseInput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TxId string `protobuf:"bytes,1,opt,name=txId,proto3" json:"txId,omitempty"` + OutputIndex uint32 `protobuf:"varint,2,opt,name=outputIndex,proto3" json:"outputIndex,omitempty"` + ScriptSig *ScriptSig `protobuf:"bytes,3,opt,name=scriptSig,proto3" json:"scriptSig,omitempty"` + Sequence uint64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"` +} + +func (x *TransactionVerboseInput) Reset() { + *x = TransactionVerboseInput{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[64] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransactionVerboseInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransactionVerboseInput) ProtoMessage() {} + +func (x *TransactionVerboseInput) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[64] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransactionVerboseInput.ProtoReflect.Descriptor instead. +func (*TransactionVerboseInput) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{64} +} + +func (x *TransactionVerboseInput) GetTxId() string { + if x != nil { + return x.TxId + } + return "" +} + +func (x *TransactionVerboseInput) GetOutputIndex() uint32 { + if x != nil { + return x.OutputIndex + } + return 0 +} + +func (x *TransactionVerboseInput) GetScriptSig() *ScriptSig { + if x != nil { + return x.ScriptSig + } + return nil +} + +func (x *TransactionVerboseInput) GetSequence() uint64 { + if x != nil { + return x.Sequence + } + return 0 +} + +type ScriptSig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Asm string `protobuf:"bytes,1,opt,name=asm,proto3" json:"asm,omitempty"` + Hex string `protobuf:"bytes,2,opt,name=hex,proto3" json:"hex,omitempty"` +} + +func (x *ScriptSig) Reset() { + *x = ScriptSig{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[65] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ScriptSig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ScriptSig) ProtoMessage() {} + +func (x *ScriptSig) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[65] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ScriptSig.ProtoReflect.Descriptor instead. +func (*ScriptSig) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{65} +} + +func (x *ScriptSig) GetAsm() string { + if x != nil { + return x.Asm + } + return "" +} + +func (x *ScriptSig) GetHex() string { + if x != nil { + return x.Hex + } + return "" +} + +type TransactionVerboseOutput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + Index uint32 `protobuf:"varint,2,opt,name=index,proto3" json:"index,omitempty"` + ScriptPubKey *ScriptPubKeyResult `protobuf:"bytes,3,opt,name=scriptPubKey,proto3" json:"scriptPubKey,omitempty"` +} + +func (x *TransactionVerboseOutput) Reset() { + *x = TransactionVerboseOutput{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[66] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransactionVerboseOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransactionVerboseOutput) ProtoMessage() {} + +func (x *TransactionVerboseOutput) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[66] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransactionVerboseOutput.ProtoReflect.Descriptor instead. +func (*TransactionVerboseOutput) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{66} +} + +func (x *TransactionVerboseOutput) GetValue() uint64 { + if x != nil { + return x.Value + } + return 0 +} + +func (x *TransactionVerboseOutput) GetIndex() uint32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *TransactionVerboseOutput) GetScriptPubKey() *ScriptPubKeyResult { + if x != nil { + return x.ScriptPubKey + } + return nil +} + +type ScriptPubKeyResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Asm string `protobuf:"bytes,1,opt,name=asm,proto3" json:"asm,omitempty"` + Hex string `protobuf:"bytes,2,opt,name=hex,proto3" json:"hex,omitempty"` + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` + Address string `protobuf:"bytes,4,opt,name=address,proto3" json:"address,omitempty"` +} + +func (x *ScriptPubKeyResult) Reset() { + *x = ScriptPubKeyResult{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[67] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ScriptPubKeyResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ScriptPubKeyResult) ProtoMessage() {} + +func (x *ScriptPubKeyResult) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[67] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ScriptPubKeyResult.ProtoReflect.Descriptor instead. +func (*ScriptPubKeyResult) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{67} +} + +func (x *ScriptPubKeyResult) GetAsm() string { + if x != nil { + return x.Asm + } + return "" +} + +func (x *ScriptPubKeyResult) GetHex() string { + if x != nil { + return x.Hex + } + return "" +} + +func (x *ScriptPubKeyResult) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *ScriptPubKeyResult) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +type GetSubnetworkRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SubnetworkId string `protobuf:"bytes,1,opt,name=subnetworkId,proto3" json:"subnetworkId,omitempty"` +} + +func (x *GetSubnetworkRequestMessage) Reset() { + *x = GetSubnetworkRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[68] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetSubnetworkRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSubnetworkRequestMessage) ProtoMessage() {} + +func (x *GetSubnetworkRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[68] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSubnetworkRequestMessage.ProtoReflect.Descriptor instead. +func (*GetSubnetworkRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{68} +} + +func (x *GetSubnetworkRequestMessage) GetSubnetworkId() string { + if x != nil { + return x.SubnetworkId + } + return "" +} + +type GetSubnetworkResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GasLimit uint64 `protobuf:"varint,1,opt,name=gasLimit,proto3" json:"gasLimit,omitempty"` + Error *RPCError `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *GetSubnetworkResponseMessage) Reset() { + *x = GetSubnetworkResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[69] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetSubnetworkResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSubnetworkResponseMessage) ProtoMessage() {} + +func (x *GetSubnetworkResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[69] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSubnetworkResponseMessage.ProtoReflect.Descriptor instead. +func (*GetSubnetworkResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{69} +} + +func (x *GetSubnetworkResponseMessage) GetGasLimit() uint64 { + if x != nil { + return x.GasLimit + } + return 0 +} + +func (x *GetSubnetworkResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type GetChainFromBlockRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StartHash string `protobuf:"bytes,1,opt,name=startHash,proto3" json:"startHash,omitempty"` + IncludeBlockVerboseData bool `protobuf:"varint,2,opt,name=includeBlockVerboseData,proto3" json:"includeBlockVerboseData,omitempty"` +} + +func (x *GetChainFromBlockRequestMessage) Reset() { + *x = GetChainFromBlockRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[70] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetChainFromBlockRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetChainFromBlockRequestMessage) ProtoMessage() {} + +func (x *GetChainFromBlockRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[70] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetChainFromBlockRequestMessage.ProtoReflect.Descriptor instead. +func (*GetChainFromBlockRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{70} +} + +func (x *GetChainFromBlockRequestMessage) GetStartHash() string { + if x != nil { + return x.StartHash + } + return "" +} + +func (x *GetChainFromBlockRequestMessage) GetIncludeBlockVerboseData() bool { + if x != nil { + return x.IncludeBlockVerboseData + } + return false +} + +type GetChainFromBlockResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RemovedChainBlockHashes []string `protobuf:"bytes,1,rep,name=removedChainBlockHashes,proto3" json:"removedChainBlockHashes,omitempty"` + AddedChainBlocks []*ChainBlock `protobuf:"bytes,2,rep,name=addedChainBlocks,proto3" json:"addedChainBlocks,omitempty"` + BlockVerboseData []*BlockVerboseData `protobuf:"bytes,3,rep,name=blockVerboseData,proto3" json:"blockVerboseData,omitempty"` + Error *RPCError `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *GetChainFromBlockResponseMessage) Reset() { + *x = GetChainFromBlockResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[71] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetChainFromBlockResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetChainFromBlockResponseMessage) ProtoMessage() {} + +func (x *GetChainFromBlockResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[71] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetChainFromBlockResponseMessage.ProtoReflect.Descriptor instead. +func (*GetChainFromBlockResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{71} +} + +func (x *GetChainFromBlockResponseMessage) GetRemovedChainBlockHashes() []string { + if x != nil { + return x.RemovedChainBlockHashes + } + return nil +} + +func (x *GetChainFromBlockResponseMessage) GetAddedChainBlocks() []*ChainBlock { + if x != nil { + return x.AddedChainBlocks + } + return nil +} + +func (x *GetChainFromBlockResponseMessage) GetBlockVerboseData() []*BlockVerboseData { + if x != nil { + return x.BlockVerboseData + } + return nil +} + +func (x *GetChainFromBlockResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + +type GetBlocksRequestMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LowHash string `protobuf:"bytes,1,opt,name=lowHash,proto3" json:"lowHash,omitempty"` + IncludeBlockHexes bool `protobuf:"varint,2,opt,name=includeBlockHexes,proto3" json:"includeBlockHexes,omitempty"` + IncludeBlockVerboseData bool `protobuf:"varint,3,opt,name=includeBlockVerboseData,proto3" json:"includeBlockVerboseData,omitempty"` + IncludeTransactionVerboseData bool `protobuf:"varint,4,opt,name=includeTransactionVerboseData,proto3" json:"includeTransactionVerboseData,omitempty"` +} + +func (x *GetBlocksRequestMessage) Reset() { + *x = GetBlocksRequestMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[72] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlocksRequestMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlocksRequestMessage) ProtoMessage() {} + +func (x *GetBlocksRequestMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[72] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBlocksRequestMessage.ProtoReflect.Descriptor instead. +func (*GetBlocksRequestMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{72} +} + +func (x *GetBlocksRequestMessage) GetLowHash() string { + if x != nil { + return x.LowHash + } + return "" +} + +func (x *GetBlocksRequestMessage) GetIncludeBlockHexes() bool { + if x != nil { + return x.IncludeBlockHexes + } + return false +} + +func (x *GetBlocksRequestMessage) GetIncludeBlockVerboseData() bool { + if x != nil { + return x.IncludeBlockVerboseData + } + return false +} + +func (x *GetBlocksRequestMessage) GetIncludeTransactionVerboseData() bool { + if x != nil { + return x.IncludeTransactionVerboseData + } + return false +} + +type GetBlocksResponseMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BlockHashes []string `protobuf:"bytes,1,rep,name=blockHashes,proto3" json:"blockHashes,omitempty"` + BlockHexes []string `protobuf:"bytes,2,rep,name=blockHexes,proto3" json:"blockHexes,omitempty"` + BlockVerboseData []*BlockVerboseData `protobuf:"bytes,3,rep,name=blockVerboseData,proto3" json:"blockVerboseData,omitempty"` + Error *RPCError `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *GetBlocksResponseMessage) Reset() { + *x = GetBlocksResponseMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_messages_proto_msgTypes[73] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlocksResponseMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlocksResponseMessage) ProtoMessage() {} + +func (x *GetBlocksResponseMessage) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[73] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBlocksResponseMessage.ProtoReflect.Descriptor instead. +func (*GetBlocksResponseMessage) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{73} +} + +func (x *GetBlocksResponseMessage) GetBlockHashes() []string { + if x != nil { + return x.BlockHashes + } + return nil +} + +func (x *GetBlocksResponseMessage) GetBlockHexes() []string { + if x != nil { + return x.BlockHexes + } + return nil +} + +func (x *GetBlocksResponseMessage) GetBlockVerboseData() []*BlockVerboseData { + if x != nil { + return x.BlockVerboseData + } + return nil +} + +func (x *GetBlocksResponseMessage) GetError() *RPCError { + if x != nil { + return x.Error + } + return nil +} + var File_messages_proto protoreflect.FileDescriptor var file_messages_proto_rawDesc = []byte{ 0x0a, 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x09, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x22, 0xaa, 0x0c, 0x0a, 0x0d, + 0x12, 0x09, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x22, 0xd3, 0x25, 0x0a, 0x0d, 0x4b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x64, 0x64, @@ -2123,207 +5401,828 @@ var file_messages_proto_rawDesc = []byte{ 0x6e, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x06, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x09, 0x0a, - 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xbe, 0x01, 0x0a, 0x10, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, + 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x69, 0x0a, + 0x18, 0x67, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x18, + 0x67, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6c, 0x0a, 0x19, 0x67, 0x65, 0x74, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0xea, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x19, 0x67, 0x65, 0x74, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x12, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0xeb, 0x07, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, + 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x12, 0x73, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x5a, 0x0a, 0x13, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0xec, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x13, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x17, 0x67, + 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0xed, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x17, 0x67, 0x65, 0x74, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x69, 0x0a, 0x18, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, + 0xee, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, + 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x48, 0x00, 0x52, 0x18, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, + 0x0a, 0x17, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x64, + 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0xef, 0x07, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x65, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x17, 0x6e, + 0x6f, 0x74, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x65, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x69, 0x0a, 0x18, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x18, 0xf0, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x41, 0x64, 0x64, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x18, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x63, 0x0a, 0x16, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x65, 0x64, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xf1, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x16, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x66, 0x0a, 0x17, 0x67, 0x65, 0x74, 0x50, 0x65, 0x65, + 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x18, 0xf2, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x17, 0x67, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x69, + 0x0a, 0x18, 0x67, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0xf3, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, + 0x74, 0x50, 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, + 0x18, 0x67, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6c, 0x0a, 0x19, 0x67, 0x65, 0x74, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0xf4, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x19, 0x67, 0x65, + 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6f, 0x0a, 0x1a, 0x67, 0x65, 0x74, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0xf5, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x1a, 0x67, 0x65, + 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x16, 0x67, 0x65, 0x74, 0x4d, + 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x18, 0xf6, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x16, 0x67, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, + 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x66, 0x0a, + 0x17, 0x67, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0xf7, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x4d, + 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x17, 0x67, 0x65, + 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, 0x1b, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x18, 0xf8, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x1b, 0x67, 0x65, + 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x75, 0x0a, 0x1c, 0x67, 0x65, 0x74, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0xf9, 0x07, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x48, 0x00, 0x52, 0x1c, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4b, 0x0a, 0x0e, 0x61, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x18, 0xfa, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x61, + 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4e, 0x0a, + 0x0f, 0x61, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x18, 0xfb, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, + 0x69, 0x72, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x61, 0x64, + 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, + 0x18, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0xfc, 0x07, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x18, + 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6c, 0x0a, 0x19, 0x73, 0x75, 0x62, 0x6d, + 0x69, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0xfd, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x19, 0x73, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6c, 0x0a, 0x19, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0xfe, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x19, 0x6e, 0x6f, 0x74, 0x69, 0x66, + 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x6f, 0x0a, 0x1a, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x43, 0x68, + 0x61, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x18, 0xff, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x69, + 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x1a, 0x6e, 0x6f, 0x74, 0x69, 0x66, + 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x18, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x80, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x18, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x4e, 0x0a, 0x0f, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0x81, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, + 0x0f, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x51, 0x0a, 0x10, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x82, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, + 0x00, 0x52, 0x10, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x14, 0x67, 0x65, 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x83, 0x08, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, + 0x65, 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x14, 0x67, 0x65, + 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x60, 0x0a, 0x15, 0x67, 0x65, 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x84, 0x08, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, + 0x65, 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x15, 0x67, + 0x65, 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x18, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x18, 0x85, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, + 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x6f, 0x6d, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x18, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x46, + 0x72, 0x6f, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x6c, 0x0a, 0x19, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x86, 0x08, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x48, 0x00, 0x52, 0x19, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x6f, 0x6d, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, + 0x10, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x18, 0x87, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x10, + 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x54, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x88, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x48, 0x00, 0x52, 0x11, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x22, 0xbe, 0x01, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x15, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x41, 0x6c, 0x6c, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x41, 0x6c, + 0x6c, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x3b, 0x0a, 0x0c, + 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, + 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x75, 0x62, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x12, 0x37, 0x0a, 0x0b, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4e, 0x65, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x69, + 0x73, 0x74, 0x22, 0x6a, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1a, + 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, + 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x24, + 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x12, 0x14, + 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x22, 0x8c, 0x01, 0x0a, 0x17, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x34, 0x0a, 0x15, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x41, 0x6c, 0x6c, 0x53, 0x75, + 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x41, 0x6c, 0x6c, 0x53, 0x75, 0x62, 0x6e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x69, 0x6e, - 0x63, 0x6c, 0x75, 0x64, 0x65, 0x41, 0x6c, 0x6c, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x73, 0x12, 0x3b, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x49, 0x44, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, - 0x12, 0x37, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, - 0x65, 0x2e, 0x4e, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0b, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x6a, 0x0a, 0x0a, 0x4e, 0x65, 0x74, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, - 0x70, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x24, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, 0x8c, 0x01, 0x0a, 0x17, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x15, 0x69, 0x6e, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x41, 0x6c, 0x6c, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x41, - 0x6c, 0x6c, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x3b, 0x0a, - 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x3b, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x49, 0x44, 0x22, 0xd3, 0x02, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, + 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x07, 0x6f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3b, 0x0a, + 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x75, - 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x22, 0xd3, 0x02, 0x0a, 0x12, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x06, 0x69, - 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, - 0x12, 0x36, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, - 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x6b, - 0x54, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, - 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, - 0x44, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x61, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, - 0x67, 0x61, 0x73, 0x12, 0x31, 0x0a, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x61, - 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x22, 0x99, 0x01, 0x0a, 0x10, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x3f, 0x0a, 0x10, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, - 0x73, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4f, 0x75, 0x74, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x10, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x4f, 0x75, - 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x08, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x60, 0x0a, 0x08, - 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x25, - 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, - 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, 0x4d, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x12, 0x22, 0x0a, 0x0c, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, - 0x62, 0x4b, 0x65, 0x79, 0x22, 0x81, 0x01, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, - 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xdb, 0x02, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, - 0x74, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x4d, - 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x61, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x67, 0x61, 0x73, 0x12, 0x31, 0x0a, 0x0b, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, + 0x73, 0x68, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, + 0x18, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x99, 0x01, 0x0a, 0x10, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x3f, + 0x0a, 0x10, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x10, 0x50, + 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, + 0x28, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x53, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x53, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x60, 0x0a, 0x08, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x12, 0x3e, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x44, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x25, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, 0x4d, + 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x53, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0c, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x81, 0x01, + 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, + 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x41, + 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, + 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x22, 0xdb, 0x02, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x0c, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, + 0x73, 0x68, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, + 0x12, 0x37, 0x0a, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, + 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x4d, + 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x43, 0x0a, 0x14, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x49, 0x44, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, + 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x14, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, + 0x65, 0x64, 0x49, 0x44, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x37, + 0x0a, 0x0e, 0x75, 0x74, 0x78, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, + 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x0e, 0x75, 0x74, 0x78, 0x6f, 0x43, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x04, 0x62, 0x69, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, + 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, + 0x1c, 0x0a, 0x04, 0x48, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, 0x74, 0x0a, + 0x1a, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4c, 0x6f, 0x63, + 0x61, 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x6c, + 0x6f, 0x77, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x07, 0x6c, + 0x6f, 0x77, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2b, 0x0a, 0x08, 0x68, 0x69, 0x67, 0x68, 0x48, 0x61, + 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x08, 0x68, 0x69, 0x67, 0x68, 0x48, + 0x61, 0x73, 0x68, 0x22, 0x3e, 0x0a, 0x13, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x68, 0x61, + 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x06, 0x68, 0x61, 0x73, + 0x68, 0x65, 0x73, 0x22, 0x71, 0x0a, 0x17, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x42, + 0x44, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, + 0x0a, 0x07, 0x6c, 0x6f, 0x77, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, - 0x52, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, - 0x12, 0x43, 0x0a, 0x14, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x49, 0x44, 0x4d, 0x65, - 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, + 0x52, 0x07, 0x6c, 0x6f, 0x77, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2b, 0x0a, 0x08, 0x68, 0x69, 0x67, + 0x68, 0x48, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x08, 0x68, 0x69, + 0x67, 0x68, 0x48, 0x61, 0x73, 0x68, 0x22, 0x1d, 0x0a, 0x1b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x42, 0x44, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x44, 0x6f, 0x6e, 0x65, 0x49, 0x42, 0x44, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x44, 0x0a, + 0x19, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x68, 0x61, + 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x06, 0x68, 0x61, 0x73, + 0x68, 0x65, 0x73, 0x22, 0x1b, 0x0a, 0x19, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0x48, 0x0a, 0x1a, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, + 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x46, 0x0a, 0x1a, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, + 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, + 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x02, + 0x69, 0x64, 0x22, 0x3b, 0x0a, 0x14, 0x49, 0x6e, 0x76, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, + 0x44, 0x0a, 0x16, 0x49, 0x6e, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x03, 0x69, 0x64, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, + 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, + 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x23, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x23, 0x0a, 0x0b, 0x50, 0x6f, + 0x6e, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, + 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, + 0x4f, 0x0a, 0x12, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x39, 0x0a, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, - 0x14, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x49, 0x44, 0x4d, 0x65, 0x72, 0x6b, 0x6c, - 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x37, 0x0a, 0x0e, 0x75, 0x74, 0x78, 0x6f, 0x43, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x0e, - 0x75, 0x74, 0x78, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1c, - 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, 0x0a, 0x04, - 0x62, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x62, 0x69, 0x74, 0x73, - 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x1c, 0x0a, 0x04, 0x48, 0x61, 0x73, 0x68, 0x12, 0x14, - 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, - 0x79, 0x74, 0x65, 0x73, 0x22, 0x74, 0x0a, 0x1a, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x6c, 0x6f, 0x77, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, - 0x48, 0x61, 0x73, 0x68, 0x52, 0x07, 0x6c, 0x6f, 0x77, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2b, 0x0a, - 0x08, 0x68, 0x69, 0x67, 0x68, 0x48, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, - 0x52, 0x08, 0x68, 0x69, 0x67, 0x68, 0x48, 0x61, 0x73, 0x68, 0x22, 0x3e, 0x0a, 0x13, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x27, 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, - 0x73, 0x68, 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0x71, 0x0a, 0x17, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x42, 0x44, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x6c, 0x6f, 0x77, 0x48, 0x61, 0x73, 0x68, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, - 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x07, 0x6c, 0x6f, 0x77, 0x48, 0x61, 0x73, 0x68, - 0x12, 0x2b, 0x0a, 0x08, 0x68, 0x69, 0x67, 0x68, 0x48, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, - 0x61, 0x73, 0x68, 0x52, 0x08, 0x68, 0x69, 0x67, 0x68, 0x48, 0x61, 0x73, 0x68, 0x22, 0x1d, 0x0a, - 0x1b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4e, 0x65, 0x78, 0x74, 0x49, 0x42, 0x44, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x16, 0x0a, 0x14, - 0x44, 0x6f, 0x6e, 0x65, 0x49, 0x42, 0x44, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x44, 0x0a, 0x19, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, - 0x65, 0x6c, 0x61, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x27, 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, - 0x73, 0x68, 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0x1b, 0x0a, 0x19, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x48, 0x0a, 0x1a, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2a, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x03, 0x69, 0x64, - 0x73, 0x22, 0x46, 0x0a, 0x1a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x28, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3b, 0x0a, 0x14, 0x49, 0x6e, 0x76, - 0x52, 0x65, 0x6c, 0x61, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, - 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0x44, 0x0a, 0x16, 0x49, 0x6e, 0x76, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x2a, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x23, 0x0a, 0x0b, - 0x50, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, - 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, - 0x65, 0x22, 0x23, 0x0a, 0x0b, 0x50, 0x6f, 0x6e, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x4f, 0x0a, 0x12, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x65, 0x64, 0x54, 0x69, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x39, 0x0a, 0x0f, + 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, + 0x22, 0x0f, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x61, 0x63, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x8d, 0x03, 0x0a, 0x0e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, + 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2f, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4e, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x73, 0x65, + 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, + 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, + 0x68, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, + 0x73, 0x68, 0x12, 0x26, 0x0a, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x6c, + 0x61, 0x79, 0x54, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x54, 0x78, 0x12, 0x3b, 0x0a, 0x0c, 0x73, 0x75, + 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x22, 0x27, 0x0a, 0x0d, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x24, 0x0a, 0x08, 0x52, 0x50, + 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0x21, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x22, 0x75, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, + 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x37, 0x0a, 0x19, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x48, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x48, 0x65, 0x78, 0x22, 0x47, 0x0a, 0x1a, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x60, 0x0a, 0x1e, + 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x70, 0x61, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1e, + 0x0a, 0x0a, 0x6c, 0x6f, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x6c, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x6c, 0x6f, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x6c, 0x49, 0x64, 0x22, 0xb9, + 0x05, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x69, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x62, 0x69, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, + 0x6d, 0x61, 0x73, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x09, 0x6d, 0x61, 0x73, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x51, 0x0a, 0x0c, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, + 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, + 0x0e, 0x68, 0x61, 0x73, 0x68, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x4d, 0x65, 0x72, 0x6b, 0x6c, + 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x32, 0x0a, 0x14, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x49, 0x44, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x14, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x49, 0x44, 0x4d, + 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x75, 0x74, 0x78, + 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x75, 0x74, 0x78, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x6c, + 0x6f, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x6c, 0x49, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x6c, 0x6f, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x10, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x69, 0x66, + 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x54, 0x69, + 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6d, + 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x0e, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, + 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x73, 0x53, 0x79, 0x6e, 0x63, 0x65, 0x64, 0x18, 0x10, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x53, 0x79, 0x6e, 0x63, 0x65, 0x64, 0x12, 0x20, 0x0a, + 0x0b, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x11, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, + 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x88, 0x01, 0x0a, 0x22, 0x47, + 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x03, 0x52, 0x07, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x6d, + 0x61, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x03, 0x66, 0x65, 0x65, 0x22, 0x20, 0x0a, 0x1e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x4c, 0x0a, 0x1f, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x4e, 0x0a, 0x1d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x64, + 0x64, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, + 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x20, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x9a, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x50, + 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, + 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x09, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x22, 0x39, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x41, + 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x41, 0x64, 0x64, 0x72, 0x22, + 0x22, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, + 0x70, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x22, 0x78, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, + 0x73, 0x68, 0x12, 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, + 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x33, 0x0a, + 0x1d, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x78, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, + 0x49, 0x64, 0x22, 0x4b, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, + 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, + 0x24, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, + 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x8e, 0x01, 0x0a, 0x23, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3c, 0x0a, + 0x05, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xff, 0x02, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x2a, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x50, 0x69, 0x6e, 0x67, 0x44, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, + 0x50, 0x69, 0x6e, 0x67, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, - 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x22, 0x0f, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x61, 0x63, - 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x8d, 0x03, 0x0a, 0x0e, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, - 0x2f, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4e, 0x65, 0x74, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x39, - 0x0a, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, - 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, - 0x69, 0x72, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x65, 0x64, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x26, 0x0a, 0x0e, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x54, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x54, - 0x78, 0x12, 0x3b, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, - 0x44, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, - 0x69, 0x72, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, - 0x52, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x12, 0x18, - 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x22, 0x27, 0x0a, 0x0d, 0x52, 0x65, 0x6a, 0x65, - 0x63, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x32, 0x50, 0x0a, 0x03, 0x50, 0x32, 0x50, 0x12, 0x49, 0x0a, 0x0d, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, - 0x4b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x28, - 0x01, 0x30, 0x01, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, - 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x54, + 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x53, 0x79, 0x6e, 0x63, + 0x4e, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x53, 0x79, + 0x6e, 0x63, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x4f, 0x75, 0x74, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x4f, 0x75, + 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, + 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x41, 0x67, + 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x19, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, + 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, + 0x73, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x53, 0x0a, 0x15, 0x41, 0x64, 0x64, 0x50, + 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x69, + 0x73, 0x50, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0b, 0x69, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x22, 0x43, 0x0a, + 0x16, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, + 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x22, 0x49, 0x0a, 0x1f, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x22, 0x61, 0x0a, + 0x20, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, + 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x22, 0x22, 0x0a, 0x20, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x22, 0x4e, 0x0a, 0x21, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x43, 0x68, + 0x61, 0x69, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x22, 0x9e, 0x01, 0x0a, 0x1f, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x17, 0x72, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, + 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, + 0x65, 0x73, 0x12, 0x41, 0x0a, 0x10, 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x10, 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x62, 0x0a, 0x0a, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x40, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x41, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x49, 0x0a, 0x0d, 0x41, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x24, + 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x54, 0x78, 0x49, 0x64, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x54, + 0x78, 0x49, 0x64, 0x73, 0x22, 0xfa, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, + 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, + 0x78, 0x12, 0x38, 0x0a, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x44, 0x0a, 0x1d, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x1d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x22, 0xc7, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x12, 0x47, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x10, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xe9, 0x05, 0x0a, 0x10, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x68, 0x61, 0x73, 0x68, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, + 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1c, + 0x0a, 0x09, 0x62, 0x6c, 0x75, 0x65, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x09, 0x62, 0x6c, 0x75, 0x65, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x22, 0x0a, 0x0c, + 0x69, 0x73, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x12, 0x26, 0x0a, 0x0e, 0x68, 0x61, + 0x73, 0x68, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, + 0x6f, 0x74, 0x12, 0x32, 0x0a, 0x14, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x49, 0x44, + 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x14, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x49, 0x44, 0x4d, 0x65, 0x72, 0x6b, + 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x75, 0x74, 0x78, 0x6f, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x75, 0x74, 0x78, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x26, + 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, + 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x78, 0x12, 0x59, 0x0a, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, + 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, + 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, + 0x69, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x69, 0x74, 0x73, 0x12, + 0x1e, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x10, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, + 0x22, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, + 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, + 0x68, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x12, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, + 0x61, 0x73, 0x68, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x48, 0x61, 0x73, 0x68, + 0x65, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x48, + 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0xe3, 0x04, 0x0a, 0x16, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x68, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x68, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, + 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6c, 0x6f, + 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x75, + 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x61, + 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x67, 0x61, 0x73, 0x12, 0x20, 0x0a, 0x0b, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x61, 0x73, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x18, + 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x5e, 0x0a, 0x18, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x18, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, + 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x61, 0x0a, 0x19, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x52, 0x19, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, + 0x62, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x73, 0x49, + 0x6e, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, + 0x69, 0x73, 0x49, 0x6e, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x11, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x9f, 0x01, + 0x0a, 0x17, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, + 0x62, 0x6f, 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, 0x20, 0x0a, + 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, + 0x32, 0x0a, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x69, 0x67, 0x52, 0x09, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x53, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, + 0x2f, 0x0a, 0x09, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x73, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x73, 0x6d, 0x12, 0x10, + 0x0a, 0x03, 0x68, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x68, 0x65, 0x78, + 0x22, 0x89, 0x01, 0x0a, 0x18, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x41, 0x0a, 0x0c, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0c, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x66, 0x0a, 0x12, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x73, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x68, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x68, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x22, 0x41, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x22, 0x65, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x75, + 0x62, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x12, 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, + 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x79, + 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, + 0x38, 0x0a, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, + 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, + 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0x93, 0x02, 0x0a, 0x20, 0x47, 0x65, + 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, + 0x0a, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x10, 0x61, 0x64, 0x64, 0x65, + 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x10, 0x61, 0x64, 0x64, 0x65, 0x64, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x47, 0x0a, 0x10, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, + 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, + 0x74, 0x61, 0x52, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x29, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, + 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, + 0xe1, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6c, + 0x6f, 0x77, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, + 0x77, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2c, 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, + 0x78, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x44, 0x0a, + 0x1d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x22, 0xd0, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, + 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, 0x65, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x78, + 0x65, 0x73, 0x12, 0x47, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, 0x72, 0x62, 0x6f, + 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x56, 0x65, + 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x56, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x29, 0x0a, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0x50, 0x0a, 0x03, 0x50, 0x32, 0x50, 0x12, 0x49, 0x0a, + 0x0d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x18, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4b, 0x61, 0x73, 0x70, 0x61, + 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x32, 0x50, 0x0a, 0x03, 0x52, 0x50, 0x43, 0x12, + 0x49, 0x0a, 0x0d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4b, 0x61, 0x73, + 0x70, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x77, 0x69, 0x72, 0x65, 0x2e, 0x4b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x6e, 0x65, + 0x74, 0x2f, 0x6b, 0x61, 0x73, 0x70, 0x61, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x77, 0x69, + 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2338,98 +6237,206 @@ func file_messages_proto_rawDescGZIP() []byte { return file_messages_proto_rawDescData } -var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 30) +var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 74) var file_messages_proto_goTypes = []interface{}{ - (*KaspadMessage)(nil), // 0: protowire.KaspadMessage - (*AddressesMessage)(nil), // 1: protowire.AddressesMessage - (*NetAddress)(nil), // 2: protowire.NetAddress - (*SubnetworkID)(nil), // 3: protowire.SubnetworkID - (*RequestAddressesMessage)(nil), // 4: protowire.RequestAddressesMessage - (*TransactionMessage)(nil), // 5: protowire.TransactionMessage - (*TransactionInput)(nil), // 6: protowire.TransactionInput - (*Outpoint)(nil), // 7: protowire.Outpoint - (*TransactionID)(nil), // 8: protowire.TransactionID - (*TransactionOutput)(nil), // 9: protowire.TransactionOutput - (*BlockMessage)(nil), // 10: protowire.BlockMessage - (*BlockHeader)(nil), // 11: protowire.BlockHeader - (*Hash)(nil), // 12: protowire.Hash - (*RequestBlockLocatorMessage)(nil), // 13: protowire.RequestBlockLocatorMessage - (*BlockLocatorMessage)(nil), // 14: protowire.BlockLocatorMessage - (*RequestIBDBlocksMessage)(nil), // 15: protowire.RequestIBDBlocksMessage - (*RequestNextIBDBlocksMessage)(nil), // 16: protowire.RequestNextIBDBlocksMessage - (*DoneIBDBlocksMessage)(nil), // 17: protowire.DoneIBDBlocksMessage - (*RequestRelayBlocksMessage)(nil), // 18: protowire.RequestRelayBlocksMessage - (*RequestSelectedTipMessage)(nil), // 19: protowire.RequestSelectedTipMessage - (*RequestTransactionsMessage)(nil), // 20: protowire.RequestTransactionsMessage - (*TransactionNotFoundMessage)(nil), // 21: protowire.TransactionNotFoundMessage - (*InvRelayBlockMessage)(nil), // 22: protowire.InvRelayBlockMessage - (*InvTransactionsMessage)(nil), // 23: protowire.InvTransactionsMessage - (*PingMessage)(nil), // 24: protowire.PingMessage - (*PongMessage)(nil), // 25: protowire.PongMessage - (*SelectedTipMessage)(nil), // 26: protowire.SelectedTipMessage - (*VerackMessage)(nil), // 27: protowire.VerackMessage - (*VersionMessage)(nil), // 28: protowire.VersionMessage - (*RejectMessage)(nil), // 29: protowire.RejectMessage + (*KaspadMessage)(nil), // 0: protowire.KaspadMessage + (*AddressesMessage)(nil), // 1: protowire.AddressesMessage + (*NetAddress)(nil), // 2: protowire.NetAddress + (*SubnetworkID)(nil), // 3: protowire.SubnetworkID + (*RequestAddressesMessage)(nil), // 4: protowire.RequestAddressesMessage + (*TransactionMessage)(nil), // 5: protowire.TransactionMessage + (*TransactionInput)(nil), // 6: protowire.TransactionInput + (*Outpoint)(nil), // 7: protowire.Outpoint + (*TransactionID)(nil), // 8: protowire.TransactionID + (*TransactionOutput)(nil), // 9: protowire.TransactionOutput + (*BlockMessage)(nil), // 10: protowire.BlockMessage + (*BlockHeader)(nil), // 11: protowire.BlockHeader + (*Hash)(nil), // 12: protowire.Hash + (*RequestBlockLocatorMessage)(nil), // 13: protowire.RequestBlockLocatorMessage + (*BlockLocatorMessage)(nil), // 14: protowire.BlockLocatorMessage + (*RequestIBDBlocksMessage)(nil), // 15: protowire.RequestIBDBlocksMessage + (*RequestNextIBDBlocksMessage)(nil), // 16: protowire.RequestNextIBDBlocksMessage + (*DoneIBDBlocksMessage)(nil), // 17: protowire.DoneIBDBlocksMessage + (*RequestRelayBlocksMessage)(nil), // 18: protowire.RequestRelayBlocksMessage + (*RequestSelectedTipMessage)(nil), // 19: protowire.RequestSelectedTipMessage + (*RequestTransactionsMessage)(nil), // 20: protowire.RequestTransactionsMessage + (*TransactionNotFoundMessage)(nil), // 21: protowire.TransactionNotFoundMessage + (*InvRelayBlockMessage)(nil), // 22: protowire.InvRelayBlockMessage + (*InvTransactionsMessage)(nil), // 23: protowire.InvTransactionsMessage + (*PingMessage)(nil), // 24: protowire.PingMessage + (*PongMessage)(nil), // 25: protowire.PongMessage + (*SelectedTipMessage)(nil), // 26: protowire.SelectedTipMessage + (*VerackMessage)(nil), // 27: protowire.VerackMessage + (*VersionMessage)(nil), // 28: protowire.VersionMessage + (*RejectMessage)(nil), // 29: protowire.RejectMessage + (*RPCError)(nil), // 30: protowire.RPCError + (*GetCurrentNetworkRequestMessage)(nil), // 31: protowire.GetCurrentNetworkRequestMessage + (*GetCurrentNetworkResponseMessage)(nil), // 32: protowire.GetCurrentNetworkResponseMessage + (*SubmitBlockRequestMessage)(nil), // 33: protowire.SubmitBlockRequestMessage + (*SubmitBlockResponseMessage)(nil), // 34: protowire.SubmitBlockResponseMessage + (*GetBlockTemplateRequestMessage)(nil), // 35: protowire.GetBlockTemplateRequestMessage + (*GetBlockTemplateResponseMessage)(nil), // 36: protowire.GetBlockTemplateResponseMessage + (*GetBlockTemplateTransactionMessage)(nil), // 37: protowire.GetBlockTemplateTransactionMessage + (*NotifyBlockAddedRequestMessage)(nil), // 38: protowire.NotifyBlockAddedRequestMessage + (*NotifyBlockAddedResponseMessage)(nil), // 39: protowire.NotifyBlockAddedResponseMessage + (*BlockAddedNotificationMessage)(nil), // 40: protowire.BlockAddedNotificationMessage + (*GetPeerAddressesRequestMessage)(nil), // 41: protowire.GetPeerAddressesRequestMessage + (*GetPeerAddressesResponseMessage)(nil), // 42: protowire.GetPeerAddressesResponseMessage + (*GetPeerAddressesKnownAddressMessage)(nil), // 43: protowire.GetPeerAddressesKnownAddressMessage + (*GetSelectedTipHashRequestMessage)(nil), // 44: protowire.GetSelectedTipHashRequestMessage + (*GetSelectedTipHashResponseMessage)(nil), // 45: protowire.GetSelectedTipHashResponseMessage + (*GetMempoolEntryRequestMessage)(nil), // 46: protowire.GetMempoolEntryRequestMessage + (*GetMempoolEntryResponseMessage)(nil), // 47: protowire.GetMempoolEntryResponseMessage + (*GetConnectedPeerInfoRequestMessage)(nil), // 48: protowire.GetConnectedPeerInfoRequestMessage + (*GetConnectedPeerInfoResponseMessage)(nil), // 49: protowire.GetConnectedPeerInfoResponseMessage + (*GetConnectedPeerInfoMessage)(nil), // 50: protowire.GetConnectedPeerInfoMessage + (*AddPeerRequestMessage)(nil), // 51: protowire.AddPeerRequestMessage + (*AddPeerResponseMessage)(nil), // 52: protowire.AddPeerResponseMessage + (*SubmitTransactionRequestMessage)(nil), // 53: protowire.SubmitTransactionRequestMessage + (*SubmitTransactionResponseMessage)(nil), // 54: protowire.SubmitTransactionResponseMessage + (*NotifyChainChangedRequestMessage)(nil), // 55: protowire.NotifyChainChangedRequestMessage + (*NotifyChainChangedResponseMessage)(nil), // 56: protowire.NotifyChainChangedResponseMessage + (*ChainChangedNotificationMessage)(nil), // 57: protowire.ChainChangedNotificationMessage + (*ChainBlock)(nil), // 58: protowire.ChainBlock + (*AcceptedBlock)(nil), // 59: protowire.AcceptedBlock + (*GetBlockRequestMessage)(nil), // 60: protowire.GetBlockRequestMessage + (*GetBlockResponseMessage)(nil), // 61: protowire.GetBlockResponseMessage + (*BlockVerboseData)(nil), // 62: protowire.BlockVerboseData + (*TransactionVerboseData)(nil), // 63: protowire.TransactionVerboseData + (*TransactionVerboseInput)(nil), // 64: protowire.TransactionVerboseInput + (*ScriptSig)(nil), // 65: protowire.ScriptSig + (*TransactionVerboseOutput)(nil), // 66: protowire.TransactionVerboseOutput + (*ScriptPubKeyResult)(nil), // 67: protowire.ScriptPubKeyResult + (*GetSubnetworkRequestMessage)(nil), // 68: protowire.GetSubnetworkRequestMessage + (*GetSubnetworkResponseMessage)(nil), // 69: protowire.GetSubnetworkResponseMessage + (*GetChainFromBlockRequestMessage)(nil), // 70: protowire.GetChainFromBlockRequestMessage + (*GetChainFromBlockResponseMessage)(nil), // 71: protowire.GetChainFromBlockResponseMessage + (*GetBlocksRequestMessage)(nil), // 72: protowire.GetBlocksRequestMessage + (*GetBlocksResponseMessage)(nil), // 73: protowire.GetBlocksResponseMessage } var file_messages_proto_depIdxs = []int32{ - 1, // 0: protowire.KaspadMessage.addresses:type_name -> protowire.AddressesMessage - 10, // 1: protowire.KaspadMessage.block:type_name -> protowire.BlockMessage - 5, // 2: protowire.KaspadMessage.transaction:type_name -> protowire.TransactionMessage - 13, // 3: protowire.KaspadMessage.requestBlockLocator:type_name -> protowire.RequestBlockLocatorMessage - 14, // 4: protowire.KaspadMessage.blockLocator:type_name -> protowire.BlockLocatorMessage - 4, // 5: protowire.KaspadMessage.requestAddresses:type_name -> protowire.RequestAddressesMessage - 15, // 6: protowire.KaspadMessage.requestIBDBlocks:type_name -> protowire.RequestIBDBlocksMessage - 16, // 7: protowire.KaspadMessage.requestNextIBDBlocks:type_name -> protowire.RequestNextIBDBlocksMessage - 17, // 8: protowire.KaspadMessage.DoneIBDBlocks:type_name -> protowire.DoneIBDBlocksMessage - 18, // 9: protowire.KaspadMessage.requestRelayBlocks:type_name -> protowire.RequestRelayBlocksMessage - 19, // 10: protowire.KaspadMessage.requestSelectedTip:type_name -> protowire.RequestSelectedTipMessage - 20, // 11: protowire.KaspadMessage.requestTransactions:type_name -> protowire.RequestTransactionsMessage - 10, // 12: protowire.KaspadMessage.ibdBlock:type_name -> protowire.BlockMessage - 22, // 13: protowire.KaspadMessage.invRelayBlock:type_name -> protowire.InvRelayBlockMessage - 23, // 14: protowire.KaspadMessage.invTransactions:type_name -> protowire.InvTransactionsMessage - 24, // 15: protowire.KaspadMessage.ping:type_name -> protowire.PingMessage - 25, // 16: protowire.KaspadMessage.pong:type_name -> protowire.PongMessage - 26, // 17: protowire.KaspadMessage.selectedTip:type_name -> protowire.SelectedTipMessage - 27, // 18: protowire.KaspadMessage.verack:type_name -> protowire.VerackMessage - 28, // 19: protowire.KaspadMessage.version:type_name -> protowire.VersionMessage - 21, // 20: protowire.KaspadMessage.transactionNotFound:type_name -> protowire.TransactionNotFoundMessage - 29, // 21: protowire.KaspadMessage.reject:type_name -> protowire.RejectMessage - 3, // 22: protowire.AddressesMessage.subnetworkID:type_name -> protowire.SubnetworkID - 2, // 23: protowire.AddressesMessage.addressList:type_name -> protowire.NetAddress - 3, // 24: protowire.RequestAddressesMessage.subnetworkID:type_name -> protowire.SubnetworkID - 6, // 25: protowire.TransactionMessage.inputs:type_name -> protowire.TransactionInput - 9, // 26: protowire.TransactionMessage.outputs:type_name -> protowire.TransactionOutput - 3, // 27: protowire.TransactionMessage.subnetworkID:type_name -> protowire.SubnetworkID - 12, // 28: protowire.TransactionMessage.payloadHash:type_name -> protowire.Hash - 7, // 29: protowire.TransactionInput.PreviousOutpoint:type_name -> protowire.Outpoint - 8, // 30: protowire.Outpoint.transactionID:type_name -> protowire.TransactionID - 11, // 31: protowire.BlockMessage.header:type_name -> protowire.BlockHeader - 5, // 32: protowire.BlockMessage.transactions:type_name -> protowire.TransactionMessage - 12, // 33: protowire.BlockHeader.parentHashes:type_name -> protowire.Hash - 12, // 34: protowire.BlockHeader.hashMerkleRoot:type_name -> protowire.Hash - 12, // 35: protowire.BlockHeader.acceptedIDMerkleRoot:type_name -> protowire.Hash - 12, // 36: protowire.BlockHeader.utxoCommitment:type_name -> protowire.Hash - 12, // 37: protowire.RequestBlockLocatorMessage.lowHash:type_name -> protowire.Hash - 12, // 38: protowire.RequestBlockLocatorMessage.highHash:type_name -> protowire.Hash - 12, // 39: protowire.BlockLocatorMessage.hashes:type_name -> protowire.Hash - 12, // 40: protowire.RequestIBDBlocksMessage.lowHash:type_name -> protowire.Hash - 12, // 41: protowire.RequestIBDBlocksMessage.highHash:type_name -> protowire.Hash - 12, // 42: protowire.RequestRelayBlocksMessage.hashes:type_name -> protowire.Hash - 8, // 43: protowire.RequestTransactionsMessage.ids:type_name -> protowire.TransactionID - 8, // 44: protowire.TransactionNotFoundMessage.id:type_name -> protowire.TransactionID - 12, // 45: protowire.InvRelayBlockMessage.hash:type_name -> protowire.Hash - 8, // 46: protowire.InvTransactionsMessage.ids:type_name -> protowire.TransactionID - 12, // 47: protowire.SelectedTipMessage.selectedTipHash:type_name -> protowire.Hash - 2, // 48: protowire.VersionMessage.address:type_name -> protowire.NetAddress - 12, // 49: protowire.VersionMessage.selectedTipHash:type_name -> protowire.Hash - 3, // 50: protowire.VersionMessage.subnetworkID:type_name -> protowire.SubnetworkID - 0, // 51: protowire.P2P.MessageStream:input_type -> protowire.KaspadMessage - 0, // 52: protowire.P2P.MessageStream:output_type -> protowire.KaspadMessage - 52, // [52:53] is the sub-list for method output_type - 51, // [51:52] is the sub-list for method input_type - 51, // [51:51] is the sub-list for extension type_name - 51, // [51:51] is the sub-list for extension extendee - 0, // [0:51] is the sub-list for field type_name + 1, // 0: protowire.KaspadMessage.addresses:type_name -> protowire.AddressesMessage + 10, // 1: protowire.KaspadMessage.block:type_name -> protowire.BlockMessage + 5, // 2: protowire.KaspadMessage.transaction:type_name -> protowire.TransactionMessage + 13, // 3: protowire.KaspadMessage.requestBlockLocator:type_name -> protowire.RequestBlockLocatorMessage + 14, // 4: protowire.KaspadMessage.blockLocator:type_name -> protowire.BlockLocatorMessage + 4, // 5: protowire.KaspadMessage.requestAddresses:type_name -> protowire.RequestAddressesMessage + 15, // 6: protowire.KaspadMessage.requestIBDBlocks:type_name -> protowire.RequestIBDBlocksMessage + 16, // 7: protowire.KaspadMessage.requestNextIBDBlocks:type_name -> protowire.RequestNextIBDBlocksMessage + 17, // 8: protowire.KaspadMessage.DoneIBDBlocks:type_name -> protowire.DoneIBDBlocksMessage + 18, // 9: protowire.KaspadMessage.requestRelayBlocks:type_name -> protowire.RequestRelayBlocksMessage + 19, // 10: protowire.KaspadMessage.requestSelectedTip:type_name -> protowire.RequestSelectedTipMessage + 20, // 11: protowire.KaspadMessage.requestTransactions:type_name -> protowire.RequestTransactionsMessage + 10, // 12: protowire.KaspadMessage.ibdBlock:type_name -> protowire.BlockMessage + 22, // 13: protowire.KaspadMessage.invRelayBlock:type_name -> protowire.InvRelayBlockMessage + 23, // 14: protowire.KaspadMessage.invTransactions:type_name -> protowire.InvTransactionsMessage + 24, // 15: protowire.KaspadMessage.ping:type_name -> protowire.PingMessage + 25, // 16: protowire.KaspadMessage.pong:type_name -> protowire.PongMessage + 26, // 17: protowire.KaspadMessage.selectedTip:type_name -> protowire.SelectedTipMessage + 27, // 18: protowire.KaspadMessage.verack:type_name -> protowire.VerackMessage + 28, // 19: protowire.KaspadMessage.version:type_name -> protowire.VersionMessage + 21, // 20: protowire.KaspadMessage.transactionNotFound:type_name -> protowire.TransactionNotFoundMessage + 29, // 21: protowire.KaspadMessage.reject:type_name -> protowire.RejectMessage + 31, // 22: protowire.KaspadMessage.getCurrentNetworkRequest:type_name -> protowire.GetCurrentNetworkRequestMessage + 32, // 23: protowire.KaspadMessage.getCurrentNetworkResponse:type_name -> protowire.GetCurrentNetworkResponseMessage + 33, // 24: protowire.KaspadMessage.submitBlockRequest:type_name -> protowire.SubmitBlockRequestMessage + 34, // 25: protowire.KaspadMessage.submitBlockResponse:type_name -> protowire.SubmitBlockResponseMessage + 35, // 26: protowire.KaspadMessage.getBlockTemplateRequest:type_name -> protowire.GetBlockTemplateRequestMessage + 36, // 27: protowire.KaspadMessage.getBlockTemplateResponse:type_name -> protowire.GetBlockTemplateResponseMessage + 38, // 28: protowire.KaspadMessage.notifyBlockAddedRequest:type_name -> protowire.NotifyBlockAddedRequestMessage + 39, // 29: protowire.KaspadMessage.notifyBlockAddedResponse:type_name -> protowire.NotifyBlockAddedResponseMessage + 40, // 30: protowire.KaspadMessage.blockAddedNotification:type_name -> protowire.BlockAddedNotificationMessage + 41, // 31: protowire.KaspadMessage.getPeerAddressesRequest:type_name -> protowire.GetPeerAddressesRequestMessage + 42, // 32: protowire.KaspadMessage.getPeerAddressesResponse:type_name -> protowire.GetPeerAddressesResponseMessage + 44, // 33: protowire.KaspadMessage.getSelectedTipHashRequest:type_name -> protowire.GetSelectedTipHashRequestMessage + 45, // 34: protowire.KaspadMessage.getSelectedTipHashResponse:type_name -> protowire.GetSelectedTipHashResponseMessage + 46, // 35: protowire.KaspadMessage.getMempoolEntryRequest:type_name -> protowire.GetMempoolEntryRequestMessage + 47, // 36: protowire.KaspadMessage.getMempoolEntryResponse:type_name -> protowire.GetMempoolEntryResponseMessage + 48, // 37: protowire.KaspadMessage.getConnectedPeerInfoRequest:type_name -> protowire.GetConnectedPeerInfoRequestMessage + 49, // 38: protowire.KaspadMessage.getConnectedPeerInfoResponse:type_name -> protowire.GetConnectedPeerInfoResponseMessage + 51, // 39: protowire.KaspadMessage.addPeerRequest:type_name -> protowire.AddPeerRequestMessage + 52, // 40: protowire.KaspadMessage.addPeerResponse:type_name -> protowire.AddPeerResponseMessage + 53, // 41: protowire.KaspadMessage.submitTransactionRequest:type_name -> protowire.SubmitTransactionRequestMessage + 54, // 42: protowire.KaspadMessage.submitTransactionResponse:type_name -> protowire.SubmitTransactionResponseMessage + 55, // 43: protowire.KaspadMessage.notifyChainChangedRequest:type_name -> protowire.NotifyChainChangedRequestMessage + 56, // 44: protowire.KaspadMessage.notifyChainChangedResponse:type_name -> protowire.NotifyChainChangedResponseMessage + 57, // 45: protowire.KaspadMessage.chainChangedNotification:type_name -> protowire.ChainChangedNotificationMessage + 60, // 46: protowire.KaspadMessage.getBlockRequest:type_name -> protowire.GetBlockRequestMessage + 61, // 47: protowire.KaspadMessage.getBlockResponse:type_name -> protowire.GetBlockResponseMessage + 68, // 48: protowire.KaspadMessage.getSubnetworkRequest:type_name -> protowire.GetSubnetworkRequestMessage + 69, // 49: protowire.KaspadMessage.getSubnetworkResponse:type_name -> protowire.GetSubnetworkResponseMessage + 70, // 50: protowire.KaspadMessage.getChainFromBlockRequest:type_name -> protowire.GetChainFromBlockRequestMessage + 71, // 51: protowire.KaspadMessage.getChainFromBlockResponse:type_name -> protowire.GetChainFromBlockResponseMessage + 72, // 52: protowire.KaspadMessage.getBlocksRequest:type_name -> protowire.GetBlocksRequestMessage + 73, // 53: protowire.KaspadMessage.getBlocksResponse:type_name -> protowire.GetBlocksResponseMessage + 3, // 54: protowire.AddressesMessage.subnetworkID:type_name -> protowire.SubnetworkID + 2, // 55: protowire.AddressesMessage.addressList:type_name -> protowire.NetAddress + 3, // 56: protowire.RequestAddressesMessage.subnetworkID:type_name -> protowire.SubnetworkID + 6, // 57: protowire.TransactionMessage.inputs:type_name -> protowire.TransactionInput + 9, // 58: protowire.TransactionMessage.outputs:type_name -> protowire.TransactionOutput + 3, // 59: protowire.TransactionMessage.subnetworkID:type_name -> protowire.SubnetworkID + 12, // 60: protowire.TransactionMessage.payloadHash:type_name -> protowire.Hash + 7, // 61: protowire.TransactionInput.PreviousOutpoint:type_name -> protowire.Outpoint + 8, // 62: protowire.Outpoint.transactionID:type_name -> protowire.TransactionID + 11, // 63: protowire.BlockMessage.header:type_name -> protowire.BlockHeader + 5, // 64: protowire.BlockMessage.transactions:type_name -> protowire.TransactionMessage + 12, // 65: protowire.BlockHeader.parentHashes:type_name -> protowire.Hash + 12, // 66: protowire.BlockHeader.hashMerkleRoot:type_name -> protowire.Hash + 12, // 67: protowire.BlockHeader.acceptedIDMerkleRoot:type_name -> protowire.Hash + 12, // 68: protowire.BlockHeader.utxoCommitment:type_name -> protowire.Hash + 12, // 69: protowire.RequestBlockLocatorMessage.lowHash:type_name -> protowire.Hash + 12, // 70: protowire.RequestBlockLocatorMessage.highHash:type_name -> protowire.Hash + 12, // 71: protowire.BlockLocatorMessage.hashes:type_name -> protowire.Hash + 12, // 72: protowire.RequestIBDBlocksMessage.lowHash:type_name -> protowire.Hash + 12, // 73: protowire.RequestIBDBlocksMessage.highHash:type_name -> protowire.Hash + 12, // 74: protowire.RequestRelayBlocksMessage.hashes:type_name -> protowire.Hash + 8, // 75: protowire.RequestTransactionsMessage.ids:type_name -> protowire.TransactionID + 8, // 76: protowire.TransactionNotFoundMessage.id:type_name -> protowire.TransactionID + 12, // 77: protowire.InvRelayBlockMessage.hash:type_name -> protowire.Hash + 8, // 78: protowire.InvTransactionsMessage.ids:type_name -> protowire.TransactionID + 12, // 79: protowire.SelectedTipMessage.selectedTipHash:type_name -> protowire.Hash + 2, // 80: protowire.VersionMessage.address:type_name -> protowire.NetAddress + 12, // 81: protowire.VersionMessage.selectedTipHash:type_name -> protowire.Hash + 3, // 82: protowire.VersionMessage.subnetworkID:type_name -> protowire.SubnetworkID + 30, // 83: protowire.GetCurrentNetworkResponseMessage.error:type_name -> protowire.RPCError + 30, // 84: protowire.SubmitBlockResponseMessage.error:type_name -> protowire.RPCError + 37, // 85: protowire.GetBlockTemplateResponseMessage.transactions:type_name -> protowire.GetBlockTemplateTransactionMessage + 30, // 86: protowire.GetBlockTemplateResponseMessage.error:type_name -> protowire.RPCError + 30, // 87: protowire.NotifyBlockAddedResponseMessage.error:type_name -> protowire.RPCError + 10, // 88: protowire.BlockAddedNotificationMessage.block:type_name -> protowire.BlockMessage + 43, // 89: protowire.GetPeerAddressesResponseMessage.addresses:type_name -> protowire.GetPeerAddressesKnownAddressMessage + 30, // 90: protowire.GetPeerAddressesResponseMessage.error:type_name -> protowire.RPCError + 30, // 91: protowire.GetSelectedTipHashResponseMessage.error:type_name -> protowire.RPCError + 30, // 92: protowire.GetMempoolEntryResponseMessage.error:type_name -> protowire.RPCError + 50, // 93: protowire.GetConnectedPeerInfoResponseMessage.infos:type_name -> protowire.GetConnectedPeerInfoMessage + 30, // 94: protowire.GetConnectedPeerInfoResponseMessage.error:type_name -> protowire.RPCError + 30, // 95: protowire.AddPeerResponseMessage.error:type_name -> protowire.RPCError + 30, // 96: protowire.SubmitTransactionResponseMessage.error:type_name -> protowire.RPCError + 30, // 97: protowire.NotifyChainChangedResponseMessage.error:type_name -> protowire.RPCError + 58, // 98: protowire.ChainChangedNotificationMessage.addedChainBlocks:type_name -> protowire.ChainBlock + 59, // 99: protowire.ChainBlock.acceptedBlocks:type_name -> protowire.AcceptedBlock + 62, // 100: protowire.GetBlockResponseMessage.blockVerboseData:type_name -> protowire.BlockVerboseData + 30, // 101: protowire.GetBlockResponseMessage.error:type_name -> protowire.RPCError + 63, // 102: protowire.BlockVerboseData.transactionVerboseData:type_name -> protowire.TransactionVerboseData + 64, // 103: protowire.TransactionVerboseData.transactionVerboseInputs:type_name -> protowire.TransactionVerboseInput + 66, // 104: protowire.TransactionVerboseData.transactionVerboseOutputs:type_name -> protowire.TransactionVerboseOutput + 65, // 105: protowire.TransactionVerboseInput.scriptSig:type_name -> protowire.ScriptSig + 67, // 106: protowire.TransactionVerboseOutput.scriptPubKey:type_name -> protowire.ScriptPubKeyResult + 30, // 107: protowire.GetSubnetworkResponseMessage.error:type_name -> protowire.RPCError + 58, // 108: protowire.GetChainFromBlockResponseMessage.addedChainBlocks:type_name -> protowire.ChainBlock + 62, // 109: protowire.GetChainFromBlockResponseMessage.blockVerboseData:type_name -> protowire.BlockVerboseData + 30, // 110: protowire.GetChainFromBlockResponseMessage.error:type_name -> protowire.RPCError + 62, // 111: protowire.GetBlocksResponseMessage.blockVerboseData:type_name -> protowire.BlockVerboseData + 30, // 112: protowire.GetBlocksResponseMessage.error:type_name -> protowire.RPCError + 0, // 113: protowire.P2P.MessageStream:input_type -> protowire.KaspadMessage + 0, // 114: protowire.RPC.MessageStream:input_type -> protowire.KaspadMessage + 0, // 115: protowire.P2P.MessageStream:output_type -> protowire.KaspadMessage + 0, // 116: protowire.RPC.MessageStream:output_type -> protowire.KaspadMessage + 115, // [115:117] is the sub-list for method output_type + 113, // [113:115] is the sub-list for method input_type + 113, // [113:113] is the sub-list for extension type_name + 113, // [113:113] is the sub-list for extension extendee + 0, // [0:113] is the sub-list for field type_name } func init() { file_messages_proto_init() } @@ -2798,6 +6805,534 @@ func file_messages_proto_init() { return nil } } + file_messages_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RPCError); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetCurrentNetworkRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetCurrentNetworkResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubmitBlockRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubmitBlockResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlockTemplateRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlockTemplateResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlockTemplateTransactionMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NotifyBlockAddedRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NotifyBlockAddedResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockAddedNotificationMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPeerAddressesRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPeerAddressesResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPeerAddressesKnownAddressMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSelectedTipHashRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSelectedTipHashResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMempoolEntryRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMempoolEntryResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetConnectedPeerInfoRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetConnectedPeerInfoResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetConnectedPeerInfoMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddPeerRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddPeerResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubmitTransactionRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SubmitTransactionResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NotifyChainChangedRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NotifyChainChangedResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChainChangedNotificationMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChainBlock); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AcceptedBlock); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlockRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlockResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockVerboseData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionVerboseData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionVerboseInput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ScriptSig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionVerboseOutput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ScriptPubKeyResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSubnetworkRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSubnetworkResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetChainFromBlockRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetChainFromBlockResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlocksRequestMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_messages_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlocksResponseMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_messages_proto_msgTypes[0].OneofWrappers = []interface{}{ (*KaspadMessage_Addresses)(nil), @@ -2822,6 +7357,38 @@ func file_messages_proto_init() { (*KaspadMessage_Version)(nil), (*KaspadMessage_TransactionNotFound)(nil), (*KaspadMessage_Reject)(nil), + (*KaspadMessage_GetCurrentNetworkRequest)(nil), + (*KaspadMessage_GetCurrentNetworkResponse)(nil), + (*KaspadMessage_SubmitBlockRequest)(nil), + (*KaspadMessage_SubmitBlockResponse)(nil), + (*KaspadMessage_GetBlockTemplateRequest)(nil), + (*KaspadMessage_GetBlockTemplateResponse)(nil), + (*KaspadMessage_NotifyBlockAddedRequest)(nil), + (*KaspadMessage_NotifyBlockAddedResponse)(nil), + (*KaspadMessage_BlockAddedNotification)(nil), + (*KaspadMessage_GetPeerAddressesRequest)(nil), + (*KaspadMessage_GetPeerAddressesResponse)(nil), + (*KaspadMessage_GetSelectedTipHashRequest)(nil), + (*KaspadMessage_GetSelectedTipHashResponse)(nil), + (*KaspadMessage_GetMempoolEntryRequest)(nil), + (*KaspadMessage_GetMempoolEntryResponse)(nil), + (*KaspadMessage_GetConnectedPeerInfoRequest)(nil), + (*KaspadMessage_GetConnectedPeerInfoResponse)(nil), + (*KaspadMessage_AddPeerRequest)(nil), + (*KaspadMessage_AddPeerResponse)(nil), + (*KaspadMessage_SubmitTransactionRequest)(nil), + (*KaspadMessage_SubmitTransactionResponse)(nil), + (*KaspadMessage_NotifyChainChangedRequest)(nil), + (*KaspadMessage_NotifyChainChangedResponse)(nil), + (*KaspadMessage_ChainChangedNotification)(nil), + (*KaspadMessage_GetBlockRequest)(nil), + (*KaspadMessage_GetBlockResponse)(nil), + (*KaspadMessage_GetSubnetworkRequest)(nil), + (*KaspadMessage_GetSubnetworkResponse)(nil), + (*KaspadMessage_GetChainFromBlockRequest)(nil), + (*KaspadMessage_GetChainFromBlockResponse)(nil), + (*KaspadMessage_GetBlocksRequest)(nil), + (*KaspadMessage_GetBlocksResponse)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -2829,9 +7396,9 @@ func file_messages_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_messages_proto_rawDesc, NumEnums: 0, - NumMessages: 30, + NumMessages: 74, NumExtensions: 0, - NumServices: 1, + NumServices: 2, }, GoTypes: file_messages_proto_goTypes, DependencyIndexes: file_messages_proto_depIdxs, diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/messages.proto b/infrastructure/network/netadapter/server/grpcserver/protowire/messages.proto index 860cd8288..62ade524f 100644 --- a/infrastructure/network/netadapter/server/grpcserver/protowire/messages.proto +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/messages.proto @@ -27,9 +27,46 @@ message KaspadMessage { VersionMessage version = 20; TransactionNotFoundMessage transactionNotFound = 21; RejectMessage reject = 22; + + GetCurrentNetworkRequestMessage getCurrentNetworkRequest = 1001; + GetCurrentNetworkResponseMessage getCurrentNetworkResponse = 1002; + SubmitBlockRequestMessage submitBlockRequest = 1003; + SubmitBlockResponseMessage submitBlockResponse = 1004; + GetBlockTemplateRequestMessage getBlockTemplateRequest = 1005; + GetBlockTemplateResponseMessage getBlockTemplateResponse = 1006; + NotifyBlockAddedRequestMessage notifyBlockAddedRequest = 1007; + NotifyBlockAddedResponseMessage notifyBlockAddedResponse = 1008; + BlockAddedNotificationMessage blockAddedNotification = 1009; + GetPeerAddressesRequestMessage getPeerAddressesRequest = 1010; + GetPeerAddressesResponseMessage getPeerAddressesResponse = 1011; + GetSelectedTipHashRequestMessage getSelectedTipHashRequest = 1012; + GetSelectedTipHashResponseMessage getSelectedTipHashResponse = 1013; + GetMempoolEntryRequestMessage getMempoolEntryRequest = 1014; + GetMempoolEntryResponseMessage getMempoolEntryResponse = 1015; + GetConnectedPeerInfoRequestMessage getConnectedPeerInfoRequest = 1016; + GetConnectedPeerInfoResponseMessage getConnectedPeerInfoResponse = 1017; + AddPeerRequestMessage addPeerRequest = 1018; + AddPeerResponseMessage addPeerResponse = 1019; + SubmitTransactionRequestMessage submitTransactionRequest = 1020; + SubmitTransactionResponseMessage submitTransactionResponse = 1021; + NotifyChainChangedRequestMessage notifyChainChangedRequest = 1022; + NotifyChainChangedResponseMessage notifyChainChangedResponse = 1023; + ChainChangedNotificationMessage chainChangedNotification = 1024; + GetBlockRequestMessage getBlockRequest = 1025; + GetBlockResponseMessage getBlockResponse = 1026; + GetSubnetworkRequestMessage getSubnetworkRequest = 1027; + GetSubnetworkResponseMessage getSubnetworkResponse = 1028; + GetChainFromBlockRequestMessage getChainFromBlockRequest = 1029; + GetChainFromBlockResponseMessage getChainFromBlockResponse = 1030; + GetBlocksRequestMessage getBlocksRequest = 1031; + GetBlocksResponseMessage getBlocksResponse = 1032; } } +///////////////////////////////////////////////////////////////////////////// +// P2P // +///////////////////////////////////////////////////////////////////////////// + // AddressesMessage start message AddressesMessage{ bool includeAllSubnetworks = 1; @@ -223,3 +260,284 @@ message RejectMessage{ service P2P { rpc MessageStream (stream KaspadMessage) returns (stream KaspadMessage) {} } + +///////////////////////////////////////////////////////////////////////////// +// RPC // +///////////////////////////////////////////////////////////////////////////// + +message RPCError{ + string message = 1; +} + +message GetCurrentNetworkRequestMessage{ +} + +message GetCurrentNetworkResponseMessage{ + string currentNetwork = 1; + RPCError error = 2; +} + +message SubmitBlockRequestMessage{ + string blockHex = 1; +} + +message SubmitBlockResponseMessage{ + RPCError error = 1; +} + +message GetBlockTemplateRequestMessage{ + string payAddress = 1; + string longPollId = 2; +} + +message GetBlockTemplateResponseMessage{ + string bits = 1; + int64 currentTime = 2; + repeated string parentHashes = 3; + int32 massLimit = 4; + repeated GetBlockTemplateTransactionMessage transactions = 5; + string hashMerkleRoot = 6; + string acceptedIDMerkleRoot = 7; + string utxoCommitment = 8; + int32 version = 9; + string longPollId = 10; + string targetDifficulty = 11; + int64 minTime = 12; + int64 maxTime = 13; + repeated string mutableFields = 14; + string nonceRange = 15; + bool isSynced = 16; + bool isConnected = 17; + + RPCError error = 18; +} + +message GetBlockTemplateTransactionMessage{ + string data = 1; + string id = 2; + repeated int64 depends = 3; + uint64 mass = 4; + uint64 fee = 5; +} + +message NotifyBlockAddedRequestMessage{ +} + +message NotifyBlockAddedResponseMessage{ + RPCError error = 1; +} + +message BlockAddedNotificationMessage{ + BlockMessage block = 1; +} + +message GetPeerAddressesRequestMessage{ +} + +message GetPeerAddressesResponseMessage{ + repeated GetPeerAddressesKnownAddressMessage addresses = 1; + RPCError error = 2; +} + +message GetPeerAddressesKnownAddressMessage { + string Addr = 1; +} + +message GetSelectedTipHashRequestMessage{ +} + +message GetSelectedTipHashResponseMessage{ + string selectedTipHash = 1; + RPCError error = 2; +} + +message GetMempoolEntryRequestMessage{ + string txId = 1; +} + +message GetMempoolEntryResponseMessage{ + RPCError error = 1; +} + +message GetConnectedPeerInfoRequestMessage{ +} + +message GetConnectedPeerInfoResponseMessage{ + repeated GetConnectedPeerInfoMessage infos = 1; + RPCError error = 2; +} + +message GetConnectedPeerInfoMessage{ + string id = 1; + string address = 2; + int64 lastPingDuration = 3; + string selectedTipHash = 4; + bool isSyncNode = 5; + bool isOutbound = 6; + int64 timeOffset = 7; + string userAgent = 8; + uint32 advertisedProtocolVersion = 9; + int64 timeConnected = 10; +} + +message AddPeerRequestMessage{ + string address = 1; + bool isPermanent = 2; +} + +message AddPeerResponseMessage{ + RPCError error = 1; +} + +message SubmitTransactionRequestMessage{ + string transactionHex = 1; +} + +message SubmitTransactionResponseMessage{ + string txId = 1; + RPCError error = 2; +} + +message NotifyChainChangedRequestMessage{ +} + +message NotifyChainChangedResponseMessage{ + RPCError error = 1; +} + +message ChainChangedNotificationMessage{ + repeated string removedChainBlockHashes = 1; + repeated ChainBlock addedChainBlocks = 2; +} + +message ChainBlock{ + string hash = 1; + repeated AcceptedBlock acceptedBlocks = 2; +} + +message AcceptedBlock{ + string hash = 1; + repeated string acceptedTxIds = 2; +} + +message GetBlockRequestMessage{ + string hash = 1; + string subnetworkId = 2; + bool includeBlockHex = 3; + bool includeBlockVerboseData = 4; + bool includeTransactionVerboseData = 5; +} + +message GetBlockResponseMessage{ + string blockHash = 1; + string blockHex = 2; + BlockVerboseData blockVerboseData = 3; + RPCError error = 4; +} + +message BlockVerboseData{ + string hash = 1; + uint64 confirmations = 2; + int32 size = 3; + uint64 blueScore = 4; + bool isChainBlock = 5; + int32 version = 6; + string versionHex = 7; + string hashMerkleRoot = 8; + string acceptedIDMerkleRoot = 9; + string utxoCommitment = 10; + repeated string transactionHex = 11; + repeated TransactionVerboseData transactionVerboseData = 12; + int64 time = 13; + uint64 nonce = 14; + string bits = 15; + double difficulty = 16; + repeated string parentHashes = 17; + string selectedParentHash = 18; + repeated string childHashes = 19; + repeated string acceptedBlockHashes = 20; +} + +message TransactionVerboseData{ + string hex = 1; + string txId = 2; + string hash = 3; + int32 size = 4; + int32 version = 5; + uint64 lockTime = 6; + string subnetworkId = 7; + uint64 gas = 8; + string payloadHash = 9; + string payload = 10; + repeated TransactionVerboseInput transactionVerboseInputs = 11; + repeated TransactionVerboseOutput transactionVerboseOutputs = 12; + string blockHash = 13; + string acceptedBy = 14; + bool isInMempool = 15; + uint64 time = 16; + uint64 blockTime = 17; +} + +message TransactionVerboseInput{ + string txId = 1; + uint32 outputIndex = 2; + ScriptSig scriptSig = 3; + uint64 sequence = 4; +} + +message ScriptSig{ + string asm = 1; + string hex = 2; +} + +message TransactionVerboseOutput{ + uint64 value = 1; + uint32 index = 2; + ScriptPubKeyResult scriptPubKey = 3; +} + +message ScriptPubKeyResult{ + string asm = 1; + string hex = 2; + string type = 3; + string address = 4; +} + +message GetSubnetworkRequestMessage{ + string subnetworkId = 1; +} + +message GetSubnetworkResponseMessage{ + uint64 gasLimit = 1; + RPCError error = 2; +} + +message GetChainFromBlockRequestMessage{ + string startHash = 1; + bool includeBlockVerboseData = 2; +} + +message GetChainFromBlockResponseMessage{ + repeated string removedChainBlockHashes = 1; + repeated ChainBlock addedChainBlocks = 2; + repeated BlockVerboseData blockVerboseData = 3; + RPCError error = 4; +} + +message GetBlocksRequestMessage{ + string lowHash = 1; + bool includeBlockHexes = 2; + bool includeBlockVerboseData = 3; + bool includeTransactionVerboseData = 4; +} + +message GetBlocksResponseMessage{ + repeated string blockHashes = 1; + repeated string blockHexes = 2; + repeated BlockVerboseData blockVerboseData = 3; + RPCError error = 4; +} + +service RPC { + rpc MessageStream (stream KaspadMessage) returns (stream KaspadMessage) {} +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/messages_grpc.pb.go b/infrastructure/network/netadapter/server/grpcserver/protowire/messages_grpc.pb.go index f25726530..102e61cfd 100644 --- a/infrastructure/network/netadapter/server/grpcserver/protowire/messages_grpc.pb.go +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/messages_grpc.pb.go @@ -120,3 +120,111 @@ var _P2P_serviceDesc = grpc.ServiceDesc{ }, Metadata: "messages.proto", } + +// RPCClient is the client API for RPC service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type RPCClient interface { + MessageStream(ctx context.Context, opts ...grpc.CallOption) (RPC_MessageStreamClient, error) +} + +type rPCClient struct { + cc grpc.ClientConnInterface +} + +func NewRPCClient(cc grpc.ClientConnInterface) RPCClient { + return &rPCClient{cc} +} + +func (c *rPCClient) MessageStream(ctx context.Context, opts ...grpc.CallOption) (RPC_MessageStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &_RPC_serviceDesc.Streams[0], "/protowire.RPC/MessageStream", opts...) + if err != nil { + return nil, err + } + x := &rPCMessageStreamClient{stream} + return x, nil +} + +type RPC_MessageStreamClient interface { + Send(*KaspadMessage) error + Recv() (*KaspadMessage, error) + grpc.ClientStream +} + +type rPCMessageStreamClient struct { + grpc.ClientStream +} + +func (x *rPCMessageStreamClient) Send(m *KaspadMessage) error { + return x.ClientStream.SendMsg(m) +} + +func (x *rPCMessageStreamClient) Recv() (*KaspadMessage, error) { + m := new(KaspadMessage) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// RPCServer is the server API for RPC service. +// All implementations must embed UnimplementedRPCServer +// for forward compatibility +type RPCServer interface { + MessageStream(RPC_MessageStreamServer) error + mustEmbedUnimplementedRPCServer() +} + +// UnimplementedRPCServer must be embedded to have forward compatible implementations. +type UnimplementedRPCServer struct { +} + +func (*UnimplementedRPCServer) MessageStream(RPC_MessageStreamServer) error { + return status.Errorf(codes.Unimplemented, "method MessageStream not implemented") +} +func (*UnimplementedRPCServer) mustEmbedUnimplementedRPCServer() {} + +func RegisterRPCServer(s *grpc.Server, srv RPCServer) { + s.RegisterService(&_RPC_serviceDesc, srv) +} + +func _RPC_MessageStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(RPCServer).MessageStream(&rPCMessageStreamServer{stream}) +} + +type RPC_MessageStreamServer interface { + Send(*KaspadMessage) error + Recv() (*KaspadMessage, error) + grpc.ServerStream +} + +type rPCMessageStreamServer struct { + grpc.ServerStream +} + +func (x *rPCMessageStreamServer) Send(m *KaspadMessage) error { + return x.ServerStream.SendMsg(m) +} + +func (x *rPCMessageStreamServer) Recv() (*KaspadMessage, error) { + m := new(KaspadMessage) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +var _RPC_serviceDesc = grpc.ServiceDesc{ + ServiceName: "protowire.RPC", + HandlerType: (*RPCServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "MessageStream", + Handler: _RPC_MessageStream_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "messages.proto", +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_addresses.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_addresses.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_addresses.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_addresses.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_block.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_block.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_block.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_block.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_block_locator.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_block_locator.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_block_locator.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_block_locator.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_done_ibd_blocks.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_done_ibd_blocks.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_done_ibd_blocks.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_done_ibd_blocks.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_ibd_block.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_ibd_block.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_ibd_block.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_ibd_block.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_inv_relay_block.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_inv_relay_block.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_inv_relay_block.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_inv_relay_block.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_inv_transactions.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_inv_transactions.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_inv_transactions.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_inv_transactions.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_ping.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_ping.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_ping.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_ping.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_pong.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_pong.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_pong.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_pong.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_reject.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_reject.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_reject.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_reject.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_request_addresses.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_addresses.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_request_addresses.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_addresses.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_request_block_locator.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_block_locator.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_request_block_locator.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_block_locator.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_request_ibd_blocks.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_ibd_blocks.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_request_ibd_blocks.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_ibd_blocks.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_request_next_ibd_blocks.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_next_ibd_blocks.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_request_next_ibd_blocks.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_next_ibd_blocks.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_request_relay_blocks.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_relay_blocks.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_request_relay_blocks.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_relay_blocks.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_request_selected_tip.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_selected_tip.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_request_selected_tip.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_selected_tip.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_request_transactions.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_transactions.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_request_transactions.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_request_transactions.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_selected_tip.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_selected_tip.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_selected_tip.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_selected_tip.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_transaction.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_transaction.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_transaction.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_transaction.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_transaction_not_found.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_transaction_not_found.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_transaction_not_found.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_transaction_not_found.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_verack.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_verack.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_verack.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_verack.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/message_version.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_version.go similarity index 100% rename from infrastructure/network/netadapter/server/grpcserver/protowire/message_version.go rename to infrastructure/network/netadapter/server/grpcserver/protowire/p2p_version.go diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_add_peer.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_add_peer.go new file mode 100644 index 000000000..e33265e89 --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_add_peer.go @@ -0,0 +1,39 @@ +package protowire + +import "github.com/kaspanet/kaspad/app/appmessage" + +func (x *KaspadMessage_AddPeerRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.AddPeerRequestMessage{ + Address: x.AddPeerRequest.Address, + IsPermanent: x.AddPeerRequest.IsPermanent, + }, nil +} + +func (x *KaspadMessage_AddPeerRequest) fromAppMessage(message *appmessage.AddPeerRequestMessage) error { + x.AddPeerRequest = &AddPeerRequestMessage{ + Address: message.Address, + IsPermanent: message.IsPermanent, + } + return nil +} + +func (x *KaspadMessage_AddPeerResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.AddPeerResponse.Error != nil { + err = &appmessage.RPCError{Message: x.AddPeerResponse.Error.Message} + } + return &appmessage.AddPeerResponseMessage{ + Error: err, + }, nil +} + +func (x *KaspadMessage_AddPeerResponse) fromAppMessage(message *appmessage.AddPeerResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + x.AddPeerResponse = &AddPeerResponseMessage{ + Error: err, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_block.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_block.go new file mode 100644 index 000000000..077c0925f --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_block.go @@ -0,0 +1,217 @@ +package protowire + +import ( + "github.com/kaspanet/kaspad/app/appmessage" +) + +func (x *KaspadMessage_GetBlockRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.GetBlockRequestMessage{ + Hash: x.GetBlockRequest.Hash, + SubnetworkID: x.GetBlockRequest.SubnetworkId, + IncludeBlockHex: x.GetBlockRequest.IncludeBlockHex, + IncludeBlockVerboseData: x.GetBlockRequest.IncludeBlockVerboseData, + IncludeTransactionVerboseData: x.GetBlockRequest.IncludeTransactionVerboseData, + }, nil +} + +func (x *KaspadMessage_GetBlockRequest) fromAppMessage(message *appmessage.GetBlockRequestMessage) error { + x.GetBlockRequest = &GetBlockRequestMessage{ + Hash: message.Hash, + SubnetworkId: message.SubnetworkID, + IncludeBlockHex: message.IncludeBlockHex, + IncludeBlockVerboseData: message.IncludeBlockVerboseData, + IncludeTransactionVerboseData: message.IncludeTransactionVerboseData, + } + return nil +} + +func (x *KaspadMessage_GetBlockResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.GetBlockResponse.Error != nil { + err = &appmessage.RPCError{Message: x.GetBlockResponse.Error.Message} + } + var blockVerboseData *appmessage.BlockVerboseData + if x.GetBlockResponse.BlockVerboseData != nil { + appBlockVerboseData, err := x.GetBlockResponse.BlockVerboseData.toAppMessage() + if err != nil { + return nil, err + } + blockVerboseData = appBlockVerboseData + } + return &appmessage.GetBlockResponseMessage{ + BlockHex: x.GetBlockResponse.BlockHex, + BlockVerboseData: blockVerboseData, + Error: err, + }, nil +} + +func (x *KaspadMessage_GetBlockResponse) fromAppMessage(message *appmessage.GetBlockResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + var blockVerboseData *BlockVerboseData + if message.BlockVerboseData != nil { + protoBlockVerboseData := &BlockVerboseData{} + err := protoBlockVerboseData.fromAppMessage(message.BlockVerboseData) + if err != nil { + return err + } + blockVerboseData = protoBlockVerboseData + } + x.GetBlockResponse = &GetBlockResponseMessage{ + BlockHex: message.BlockHex, + BlockVerboseData: blockVerboseData, + Error: err, + } + return nil +} + +func (x *BlockVerboseData) toAppMessage() (*appmessage.BlockVerboseData, error) { + transactionVerboseData := make([]*appmessage.TransactionVerboseData, len(x.TransactionVerboseData)) + for i, data := range x.TransactionVerboseData { + inputs := make([]*appmessage.TransactionVerboseInput, len(data.TransactionVerboseInputs)) + for j, item := range data.TransactionVerboseInputs { + scriptSig := &appmessage.ScriptSig{ + Asm: item.ScriptSig.Asm, + Hex: item.ScriptSig.Hex, + } + inputs[j] = &appmessage.TransactionVerboseInput{ + TxID: item.TxId, + OutputIndex: item.OutputIndex, + ScriptSig: scriptSig, + Sequence: item.Sequence, + } + } + outputs := make([]*appmessage.TransactionVerboseOutput, len(data.TransactionVerboseOutputs)) + for j, item := range data.TransactionVerboseOutputs { + scriptPubKey := &appmessage.ScriptPubKeyResult{ + Asm: item.ScriptPubKey.Asm, + Hex: item.ScriptPubKey.Hex, + Type: item.ScriptPubKey.Type, + Address: item.ScriptPubKey.Address, + } + outputs[j] = &appmessage.TransactionVerboseOutput{ + Value: item.Value, + Index: item.Index, + ScriptPubKey: scriptPubKey, + } + } + transactionVerboseData[i] = &appmessage.TransactionVerboseData{ + Hex: data.Hex, + TxID: data.TxId, + Hash: data.Hash, + Size: data.Size, + Version: data.Version, + LockTime: data.LockTime, + SubnetworkID: data.SubnetworkId, + Gas: data.Gas, + PayloadHash: data.PayloadHash, + Payload: data.Payload, + TransactionVerboseInputs: inputs, + TransactionVerboseOutputs: outputs, + BlockHash: data.BlockHash, + AcceptedBy: data.AcceptedBy, + IsInMempool: data.IsInMempool, + Time: data.Time, + BlockTime: data.BlockTime, + } + } + return &appmessage.BlockVerboseData{ + Hash: x.Hash, + Confirmations: x.Confirmations, + Size: x.Size, + BlueScore: x.BlueScore, + IsChainBlock: x.IsChainBlock, + Version: x.Version, + VersionHex: x.VersionHex, + HashMerkleRoot: x.HashMerkleRoot, + AcceptedIDMerkleRoot: x.AcceptedIDMerkleRoot, + UTXOCommitment: x.UtxoCommitment, + TxIDs: x.TransactionHex, + TransactionVerboseData: transactionVerboseData, + Time: x.Time, + Nonce: x.Nonce, + Bits: x.Bits, + Difficulty: x.Difficulty, + ParentHashes: x.ParentHashes, + SelectedParentHash: x.SelectedParentHash, + ChildHashes: x.ChildHashes, + AcceptedBlockHashes: x.AcceptedBlockHashes, + }, nil +} + +func (x *BlockVerboseData) fromAppMessage(message *appmessage.BlockVerboseData) error { + transactionVerboseData := make([]*TransactionVerboseData, len(message.TransactionVerboseData)) + for i, data := range message.TransactionVerboseData { + inputs := make([]*TransactionVerboseInput, len(data.TransactionVerboseInputs)) + for j, item := range data.TransactionVerboseInputs { + scriptSig := &ScriptSig{ + Asm: item.ScriptSig.Asm, + Hex: item.ScriptSig.Hex, + } + inputs[j] = &TransactionVerboseInput{ + TxId: item.TxID, + OutputIndex: item.OutputIndex, + ScriptSig: scriptSig, + Sequence: item.Sequence, + } + } + outputs := make([]*TransactionVerboseOutput, len(data.TransactionVerboseOutputs)) + for j, item := range data.TransactionVerboseOutputs { + scriptPubKey := &ScriptPubKeyResult{ + Asm: item.ScriptPubKey.Asm, + Hex: item.ScriptPubKey.Hex, + Type: item.ScriptPubKey.Type, + Address: item.ScriptPubKey.Address, + } + outputs[j] = &TransactionVerboseOutput{ + Value: item.Value, + Index: item.Index, + ScriptPubKey: scriptPubKey, + } + } + transactionVerboseData[i] = &TransactionVerboseData{ + Hex: data.Hex, + TxId: data.TxID, + Hash: data.Hash, + Size: data.Size, + Version: data.Version, + LockTime: data.LockTime, + SubnetworkId: data.SubnetworkID, + Gas: data.Gas, + PayloadHash: data.PayloadHash, + Payload: data.Payload, + TransactionVerboseInputs: inputs, + TransactionVerboseOutputs: outputs, + BlockHash: data.BlockHash, + AcceptedBy: data.AcceptedBy, + IsInMempool: data.IsInMempool, + Time: data.Time, + BlockTime: data.BlockTime, + } + } + *x = BlockVerboseData{ + Hash: message.Hash, + Confirmations: message.Confirmations, + Size: message.Size, + BlueScore: message.BlueScore, + IsChainBlock: message.IsChainBlock, + Version: message.Version, + VersionHex: message.VersionHex, + HashMerkleRoot: message.HashMerkleRoot, + AcceptedIDMerkleRoot: message.AcceptedIDMerkleRoot, + UtxoCommitment: message.UTXOCommitment, + TransactionHex: message.TxIDs, + TransactionVerboseData: transactionVerboseData, + Time: message.Time, + Nonce: message.Nonce, + Bits: message.Bits, + Difficulty: message.Difficulty, + ParentHashes: message.ParentHashes, + SelectedParentHash: message.SelectedParentHash, + ChildHashes: message.ChildHashes, + AcceptedBlockHashes: message.AcceptedBlockHashes, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_block_template.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_block_template.go new file mode 100644 index 000000000..5c4b540ac --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_block_template.go @@ -0,0 +1,111 @@ +package protowire + +import ( + "github.com/kaspanet/kaspad/app/appmessage" +) + +func (x *KaspadMessage_GetBlockTemplateRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.GetBlockTemplateRequestMessage{ + PayAddress: x.GetBlockTemplateRequest.PayAddress, + LongPollID: x.GetBlockTemplateRequest.LongPollId, + }, nil +} + +func (x *KaspadMessage_GetBlockTemplateRequest) fromAppMessage(message *appmessage.GetBlockTemplateRequestMessage) error { + x.GetBlockTemplateRequest = &GetBlockTemplateRequestMessage{ + PayAddress: message.PayAddress, + LongPollId: message.LongPollID, + } + return nil +} + +func (x *KaspadMessage_GetBlockTemplateResponse) toAppMessage() (appmessage.Message, error) { + transactions := make([]appmessage.GetBlockTemplateTransactionMessage, len(x.GetBlockTemplateResponse.Transactions)) + for i, transaction := range x.GetBlockTemplateResponse.Transactions { + appMessageTransaction, err := transaction.toAppMessage() + if err != nil { + return nil, err + } + transactions[i] = *appMessageTransaction.(*appmessage.GetBlockTemplateTransactionMessage) + } + var err *appmessage.RPCError + if x.GetBlockTemplateResponse.Error != nil { + err = &appmessage.RPCError{Message: x.GetBlockTemplateResponse.Error.Message} + } + return &appmessage.GetBlockTemplateResponseMessage{ + Bits: x.GetBlockTemplateResponse.Bits, + CurrentTime: x.GetBlockTemplateResponse.CurrentTime, + ParentHashes: x.GetBlockTemplateResponse.ParentHashes, + MassLimit: int(x.GetBlockTemplateResponse.MassLimit), + Transactions: transactions, + HashMerkleRoot: x.GetBlockTemplateResponse.HashMerkleRoot, + AcceptedIDMerkleRoot: x.GetBlockTemplateResponse.AcceptedIDMerkleRoot, + UTXOCommitment: x.GetBlockTemplateResponse.UtxoCommitment, + Version: x.GetBlockTemplateResponse.Version, + LongPollID: x.GetBlockTemplateResponse.LongPollId, + TargetDifficulty: x.GetBlockTemplateResponse.TargetDifficulty, + MinTime: x.GetBlockTemplateResponse.MinTime, + MaxTime: x.GetBlockTemplateResponse.MaxTime, + MutableFields: x.GetBlockTemplateResponse.MutableFields, + NonceRange: x.GetBlockTemplateResponse.NonceRange, + IsSynced: x.GetBlockTemplateResponse.IsSynced, + IsConnected: x.GetBlockTemplateResponse.IsConnected, + Error: err, + }, nil +} + +func (x *KaspadMessage_GetBlockTemplateResponse) fromAppMessage(message *appmessage.GetBlockTemplateResponseMessage) error { + transactions := make([]*GetBlockTemplateTransactionMessage, len(message.Transactions)) + for i, transaction := range message.Transactions { + protoMessageTransaction := GetBlockTemplateTransactionMessage{} + err := protoMessageTransaction.fromAppMessage(&transaction) + if err != nil { + return err + } + transactions[i] = &protoMessageTransaction + } + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + x.GetBlockTemplateResponse = &GetBlockTemplateResponseMessage{ + Bits: message.Bits, + CurrentTime: message.CurrentTime, + ParentHashes: message.ParentHashes, + MassLimit: int32(message.MassLimit), + Transactions: transactions, + HashMerkleRoot: message.HashMerkleRoot, + AcceptedIDMerkleRoot: message.AcceptedIDMerkleRoot, + UtxoCommitment: message.UTXOCommitment, + Version: message.Version, + LongPollId: message.LongPollID, + TargetDifficulty: message.TargetDifficulty, + MinTime: message.MinTime, + MaxTime: message.MaxTime, + MutableFields: message.MutableFields, + NonceRange: message.NonceRange, + IsSynced: message.IsSynced, + IsConnected: message.IsConnected, + Error: err, + } + return nil +} + +func (x *GetBlockTemplateTransactionMessage) toAppMessage() (appmessage.Message, error) { + return &appmessage.GetBlockTemplateTransactionMessage{ + Data: x.Data, + ID: x.Id, + Depends: x.Depends, + Mass: x.Mass, + Fee: x.Fee, + }, nil +} + +func (x *GetBlockTemplateTransactionMessage) fromAppMessage(message *appmessage.GetBlockTemplateTransactionMessage) error { + x.Data = message.Data + x.Id = message.ID + x.Depends = message.Depends + x.Mass = message.Mass + x.Fee = message.Fee + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_blocks.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_blocks.go new file mode 100644 index 000000000..79bff1d06 --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_blocks.go @@ -0,0 +1,64 @@ +package protowire + +import "github.com/kaspanet/kaspad/app/appmessage" + +func (x *KaspadMessage_GetBlocksRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.GetBlocksRequestMessage{ + LowHash: x.GetBlocksRequest.LowHash, + IncludeBlockHexes: x.GetBlocksRequest.IncludeBlockHexes, + IncludeBlockVerboseData: x.GetBlocksRequest.IncludeBlockVerboseData, + }, nil +} + +func (x *KaspadMessage_GetBlocksRequest) fromAppMessage(message *appmessage.GetBlocksRequestMessage) error { + x.GetBlocksRequest = &GetBlocksRequestMessage{ + LowHash: message.LowHash, + IncludeBlockHexes: message.IncludeBlockHexes, + IncludeBlockVerboseData: message.IncludeBlockVerboseData, + } + return nil +} + +func (x *KaspadMessage_GetBlocksResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.GetBlocksResponse.Error != nil { + err = &appmessage.RPCError{Message: x.GetBlocksResponse.Error.Message} + } + blockVerboseData := make([]*appmessage.BlockVerboseData, len(x.GetBlocksResponse.BlockVerboseData)) + for i, blockVerboseDatum := range x.GetBlocksResponse.BlockVerboseData { + appBlockVerboseDatum, err := blockVerboseDatum.toAppMessage() + if err != nil { + return nil, err + } + blockVerboseData[i] = appBlockVerboseDatum + } + return &appmessage.GetBlocksResponseMessage{ + BlockHashes: x.GetBlocksResponse.BlockHashes, + BlockHexes: x.GetBlocksResponse.BlockHexes, + BlockVerboseData: blockVerboseData, + Error: err, + }, nil +} + +func (x *KaspadMessage_GetBlocksResponse) fromAppMessage(message *appmessage.GetBlocksResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + blockVerboseData := make([]*BlockVerboseData, len(message.BlockVerboseData)) + for i, blockVerboseDatum := range message.BlockVerboseData { + protoBlockVerboseDatum := &BlockVerboseData{} + err := protoBlockVerboseDatum.fromAppMessage(blockVerboseDatum) + if err != nil { + return err + } + blockVerboseData[i] = protoBlockVerboseDatum + } + x.GetBlocksResponse = &GetBlocksResponseMessage{ + BlockHashes: message.BlockHashes, + BlockHexes: message.BlockHexes, + BlockVerboseData: blockVerboseData, + Error: err, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_chain_from_block.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_chain_from_block.go new file mode 100644 index 000000000..cb8b6d169 --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_chain_from_block.go @@ -0,0 +1,79 @@ +package protowire + +import "github.com/kaspanet/kaspad/app/appmessage" + +func (x *KaspadMessage_GetChainFromBlockRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.GetChainFromBlockRequestMessage{ + StartHash: x.GetChainFromBlockRequest.StartHash, + IncludeBlockVerboseData: x.GetChainFromBlockRequest.IncludeBlockVerboseData, + }, nil +} + +func (x *KaspadMessage_GetChainFromBlockRequest) fromAppMessage(message *appmessage.GetChainFromBlockRequestMessage) error { + x.GetChainFromBlockRequest = &GetChainFromBlockRequestMessage{ + StartHash: message.StartHash, + IncludeBlockVerboseData: message.IncludeBlockVerboseData, + } + return nil +} + +func (x *KaspadMessage_GetChainFromBlockResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.GetChainFromBlockResponse.Error != nil { + err = &appmessage.RPCError{Message: x.GetChainFromBlockResponse.Error.Message} + } + addedChainBlocks := make([]*appmessage.ChainBlock, len(x.GetChainFromBlockResponse.AddedChainBlocks)) + for i, addedChainBlock := range x.GetChainFromBlockResponse.AddedChainBlocks { + appAddedChainBlock, err := addedChainBlock.toAppMessage() + if err != nil { + return nil, err + } + addedChainBlocks[i] = appAddedChainBlock + } + blockVerboseData := make([]*appmessage.BlockVerboseData, len(x.GetChainFromBlockResponse.BlockVerboseData)) + for i, blockVerboseDatum := range x.GetChainFromBlockResponse.BlockVerboseData { + appBlockVerboseDatum, err := blockVerboseDatum.toAppMessage() + if err != nil { + return nil, err + } + blockVerboseData[i] = appBlockVerboseDatum + } + return &appmessage.GetChainFromBlockResponseMessage{ + RemovedChainBlockHashes: x.GetChainFromBlockResponse.RemovedChainBlockHashes, + AddedChainBlocks: addedChainBlocks, + BlockVerboseData: blockVerboseData, + Error: err, + }, nil +} + +func (x *KaspadMessage_GetChainFromBlockResponse) fromAppMessage(message *appmessage.GetChainFromBlockResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + addedChainBlocks := make([]*ChainBlock, len(message.AddedChainBlocks)) + for i, addedChainBlock := range message.AddedChainBlocks { + protoAddedChainBlock := &ChainBlock{} + err := protoAddedChainBlock.fromAppMessage(addedChainBlock) + if err != nil { + return err + } + addedChainBlocks[i] = protoAddedChainBlock + } + blockVerboseData := make([]*BlockVerboseData, len(message.BlockVerboseData)) + for i, blockVerboseDatum := range message.BlockVerboseData { + protoBlockVerboseDatum := &BlockVerboseData{} + err := protoBlockVerboseDatum.fromAppMessage(blockVerboseDatum) + if err != nil { + return err + } + blockVerboseData[i] = protoBlockVerboseDatum + } + x.GetChainFromBlockResponse = &GetChainFromBlockResponseMessage{ + RemovedChainBlockHashes: message.RemovedChainBlockHashes, + AddedChainBlocks: addedChainBlocks, + BlockVerboseData: blockVerboseData, + Error: err, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_connected_peer_info.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_connected_peer_info.go new file mode 100644 index 000000000..32ed3109d --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_connected_peer_info.go @@ -0,0 +1,66 @@ +package protowire + +import ( + "github.com/kaspanet/kaspad/app/appmessage" +) + +func (x *KaspadMessage_GetConnectedPeerInfoRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.GetConnectedPeerInfoRequestMessage{}, nil +} + +func (x *KaspadMessage_GetConnectedPeerInfoRequest) fromAppMessage(_ *appmessage.GetConnectedPeerInfoRequestMessage) error { + return nil +} + +func (x *KaspadMessage_GetConnectedPeerInfoResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.GetConnectedPeerInfoResponse.Error != nil { + err = &appmessage.RPCError{Message: x.GetConnectedPeerInfoResponse.Error.Message} + } + infos := make([]*appmessage.GetConnectedPeerInfoMessage, len(x.GetConnectedPeerInfoResponse.Infos)) + for i, info := range x.GetConnectedPeerInfoResponse.Infos { + infos[i] = &appmessage.GetConnectedPeerInfoMessage{ + ID: info.Id, + Address: info.Address, + LastPingDuration: info.LastPingDuration, + SelectedTipHash: info.SelectedTipHash, + IsSyncNode: info.IsSyncNode, + IsOutbound: info.IsOutbound, + TimeOffset: info.TimeOffset, + UserAgent: info.UserAgent, + AdvertisedProtocolVersion: info.AdvertisedProtocolVersion, + TimeConnected: info.TimeOffset, + } + } + return &appmessage.GetConnectedPeerInfoResponseMessage{ + Infos: infos, + Error: err, + }, nil +} + +func (x *KaspadMessage_GetConnectedPeerInfoResponse) fromAppMessage(message *appmessage.GetConnectedPeerInfoResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + infos := make([]*GetConnectedPeerInfoMessage, len(message.Infos)) + for i, info := range message.Infos { + infos[i] = &GetConnectedPeerInfoMessage{ + Id: info.ID, + Address: info.Address, + LastPingDuration: info.LastPingDuration, + SelectedTipHash: info.SelectedTipHash, + IsSyncNode: info.IsSyncNode, + IsOutbound: info.IsOutbound, + TimeOffset: info.TimeOffset, + UserAgent: info.UserAgent, + AdvertisedProtocolVersion: info.AdvertisedProtocolVersion, + TimeConnected: info.TimeOffset, + } + } + x.GetConnectedPeerInfoResponse = &GetConnectedPeerInfoResponseMessage{ + Infos: infos, + Error: err, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_current_network.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_current_network.go new file mode 100644 index 000000000..ffd808362 --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_current_network.go @@ -0,0 +1,34 @@ +package protowire + +import "github.com/kaspanet/kaspad/app/appmessage" + +func (x *KaspadMessage_GetCurrentNetworkRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.GetCurrentNetworkRequestMessage{}, nil +} + +func (x *KaspadMessage_GetCurrentNetworkRequest) fromAppMessage(_ *appmessage.GetCurrentNetworkRequestMessage) error { + return nil +} + +func (x *KaspadMessage_GetCurrentNetworkResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.GetCurrentNetworkResponse.Error != nil { + err = &appmessage.RPCError{Message: x.GetCurrentNetworkResponse.Error.Message} + } + return &appmessage.GetCurrentNetworkResponseMessage{ + CurrentNetwork: x.GetCurrentNetworkResponse.CurrentNetwork, + Error: err, + }, nil +} + +func (x *KaspadMessage_GetCurrentNetworkResponse) fromAppMessage(message *appmessage.GetCurrentNetworkResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + x.GetCurrentNetworkResponse = &GetCurrentNetworkResponseMessage{ + CurrentNetwork: message.CurrentNetwork, + Error: err, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_mempool_entry.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_mempool_entry.go new file mode 100644 index 000000000..bee3bbf55 --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_mempool_entry.go @@ -0,0 +1,37 @@ +package protowire + +import "github.com/kaspanet/kaspad/app/appmessage" + +func (x *KaspadMessage_GetMempoolEntryRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.GetMempoolEntryRequestMessage{ + TxID: x.GetMempoolEntryRequest.TxId, + }, nil +} + +func (x *KaspadMessage_GetMempoolEntryRequest) fromAppMessage(message *appmessage.GetMempoolEntryRequestMessage) error { + x.GetMempoolEntryRequest = &GetMempoolEntryRequestMessage{ + TxId: message.TxID, + } + return nil +} + +func (x *KaspadMessage_GetMempoolEntryResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.GetMempoolEntryResponse.Error != nil { + err = &appmessage.RPCError{Message: x.GetMempoolEntryResponse.Error.Message} + } + return &appmessage.GetMempoolEntryResponseMessage{ + Error: err, + }, nil +} + +func (x *KaspadMessage_GetMempoolEntryResponse) fromAppMessage(message *appmessage.GetMempoolEntryResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + x.GetMempoolEntryResponse = &GetMempoolEntryResponseMessage{ + Error: err, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_peer_addresses.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_peer_addresses.go new file mode 100644 index 000000000..cf3e7b3cc --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_peer_addresses.go @@ -0,0 +1,42 @@ +package protowire + +import "github.com/kaspanet/kaspad/app/appmessage" + +func (x *KaspadMessage_GetPeerAddressesRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.GetPeerAddressesRequestMessage{}, nil +} + +func (x *KaspadMessage_GetPeerAddressesRequest) fromAppMessage(_ *appmessage.GetPeerAddressesRequestMessage) error { + return nil +} + +func (x *KaspadMessage_GetPeerAddressesResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.GetPeerAddressesResponse.Error != nil { + err = &appmessage.RPCError{Message: x.GetPeerAddressesResponse.Error.Message} + } + addresses := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(x.GetPeerAddressesResponse.Addresses)) + for i, address := range x.GetPeerAddressesResponse.Addresses { + addresses[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: address.Addr} + } + return &appmessage.GetPeerAddressesResponseMessage{ + Addresses: addresses, + Error: err, + }, nil +} + +func (x *KaspadMessage_GetPeerAddressesResponse) fromAppMessage(message *appmessage.GetPeerAddressesResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + addresses := make([]*GetPeerAddressesKnownAddressMessage, len(message.Addresses)) + for i, address := range message.Addresses { + addresses[i] = &GetPeerAddressesKnownAddressMessage{Addr: address.Addr} + } + x.GetPeerAddressesResponse = &GetPeerAddressesResponseMessage{ + Addresses: addresses, + Error: err, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_selected_tip_hash.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_selected_tip_hash.go new file mode 100644 index 000000000..f625d887d --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_selected_tip_hash.go @@ -0,0 +1,34 @@ +package protowire + +import "github.com/kaspanet/kaspad/app/appmessage" + +func (x *KaspadMessage_GetSelectedTipHashRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.GetSelectedTipHashRequestMessage{}, nil +} + +func (x *KaspadMessage_GetSelectedTipHashRequest) fromAppMessage(_ *appmessage.GetSelectedTipHashRequestMessage) error { + return nil +} + +func (x *KaspadMessage_GetSelectedTipHashResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.GetSelectedTipHashResponse.Error != nil { + err = &appmessage.RPCError{Message: x.GetSelectedTipHashResponse.Error.Message} + } + return &appmessage.GetSelectedTipHashResponseMessage{ + SelectedTipHash: x.GetSelectedTipHashResponse.SelectedTipHash, + Error: err, + }, nil +} + +func (x *KaspadMessage_GetSelectedTipHashResponse) fromAppMessage(message *appmessage.GetSelectedTipHashResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + x.GetSelectedTipHashResponse = &GetSelectedTipHashResponseMessage{ + SelectedTipHash: message.SelectedTipHash, + Error: err, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_subnetwork.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_subnetwork.go new file mode 100644 index 000000000..c7579f249 --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_get_subnetwork.go @@ -0,0 +1,39 @@ +package protowire + +import "github.com/kaspanet/kaspad/app/appmessage" + +func (x *KaspadMessage_GetSubnetworkRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.GetSubnetworkRequestMessage{ + SubnetworkID: x.GetSubnetworkRequest.SubnetworkId, + }, nil +} + +func (x *KaspadMessage_GetSubnetworkRequest) fromAppMessage(message *appmessage.GetSubnetworkRequestMessage) error { + x.GetSubnetworkRequest = &GetSubnetworkRequestMessage{ + SubnetworkId: message.SubnetworkID, + } + return nil +} + +func (x *KaspadMessage_GetSubnetworkResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.GetSubnetworkResponse.Error != nil { + err = &appmessage.RPCError{Message: x.GetSubnetworkResponse.Error.Message} + } + return &appmessage.GetSubnetworkResponseMessage{ + GasLimit: x.GetSubnetworkResponse.GasLimit, + Error: err, + }, nil +} + +func (x *KaspadMessage_GetSubnetworkResponse) fromAppMessage(message *appmessage.GetSubnetworkResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + x.GetSubnetworkResponse = &GetSubnetworkResponseMessage{ + GasLimit: message.GasLimit, + Error: err, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_notify_block_added.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_notify_block_added.go new file mode 100644 index 000000000..2e08fb26b --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_notify_block_added.go @@ -0,0 +1,55 @@ +package protowire + +import "github.com/kaspanet/kaspad/app/appmessage" + +func (x *KaspadMessage_NotifyBlockAddedRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.NotifyBlockAddedRequestMessage{}, nil +} + +func (x *KaspadMessage_NotifyBlockAddedRequest) fromAppMessage(_ *appmessage.NotifyBlockAddedRequestMessage) error { + x.NotifyBlockAddedRequest = &NotifyBlockAddedRequestMessage{} + return nil +} + +func (x *KaspadMessage_NotifyBlockAddedResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.NotifyBlockAddedResponse.Error != nil { + err = &appmessage.RPCError{Message: x.NotifyBlockAddedResponse.Error.Message} + } + return &appmessage.NotifyBlockAddedResponseMessage{ + Error: err, + }, nil +} + +func (x *KaspadMessage_NotifyBlockAddedResponse) fromAppMessage(message *appmessage.NotifyBlockAddedResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + x.NotifyBlockAddedResponse = &NotifyBlockAddedResponseMessage{ + Error: err, + } + return nil +} + +func (x *KaspadMessage_BlockAddedNotification) toAppMessage() (appmessage.Message, error) { + block, err := x.BlockAddedNotification.Block.toAppMessage() + if err != nil { + return nil, err + } + return &appmessage.BlockAddedNotificationMessage{ + Block: block.(*appmessage.MsgBlock), + }, nil +} + +func (x *KaspadMessage_BlockAddedNotification) fromAppMessage(message *appmessage.BlockAddedNotificationMessage) error { + blockMessage := &BlockMessage{} + err := blockMessage.fromAppMessage(message.Block) + if err != nil { + return err + } + x.BlockAddedNotification = &BlockAddedNotificationMessage{ + Block: blockMessage, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_notify_chain_changed.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_notify_chain_changed.go new file mode 100644 index 000000000..ad503799e --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_notify_chain_changed.go @@ -0,0 +1,94 @@ +package protowire + +import "github.com/kaspanet/kaspad/app/appmessage" + +func (x *KaspadMessage_NotifyChainChangedRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.NotifyChainChangedRequestMessage{}, nil +} + +func (x *KaspadMessage_NotifyChainChangedRequest) fromAppMessage(_ *appmessage.NotifyChainChangedRequestMessage) error { + x.NotifyChainChangedRequest = &NotifyChainChangedRequestMessage{} + return nil +} + +func (x *KaspadMessage_NotifyChainChangedResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.NotifyChainChangedResponse.Error != nil { + err = &appmessage.RPCError{Message: x.NotifyChainChangedResponse.Error.Message} + } + return &appmessage.NotifyChainChangedResponseMessage{ + Error: err, + }, nil +} + +func (x *KaspadMessage_NotifyChainChangedResponse) fromAppMessage(message *appmessage.NotifyChainChangedResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + x.NotifyChainChangedResponse = &NotifyChainChangedResponseMessage{ + Error: err, + } + return nil +} + +func (x *KaspadMessage_ChainChangedNotification) toAppMessage() (appmessage.Message, error) { + addedChainBlocks := make([]*appmessage.ChainBlock, len(x.ChainChangedNotification.AddedChainBlocks)) + for i, addedChainBlock := range x.ChainChangedNotification.AddedChainBlocks { + appAddedChainBlock, err := addedChainBlock.toAppMessage() + if err != nil { + return nil, err + } + addedChainBlocks[i] = appAddedChainBlock + } + return &appmessage.ChainChangedNotificationMessage{ + RemovedChainBlockHashes: x.ChainChangedNotification.RemovedChainBlockHashes, + AddedChainBlocks: addedChainBlocks, + }, nil +} + +func (x *KaspadMessage_ChainChangedNotification) fromAppMessage(message *appmessage.ChainChangedNotificationMessage) error { + addedChainBlocks := make([]*ChainBlock, len(message.AddedChainBlocks)) + for i, addedChainBlock := range message.AddedChainBlocks { + protoAddedChainBlock := &ChainBlock{} + err := protoAddedChainBlock.fromAppMessage(addedChainBlock) + if err != nil { + return err + } + addedChainBlocks[i] = protoAddedChainBlock + } + x.ChainChangedNotification = &ChainChangedNotificationMessage{ + RemovedChainBlockHashes: message.RemovedChainBlockHashes, + AddedChainBlocks: addedChainBlocks, + } + return nil +} + +func (x *ChainBlock) toAppMessage() (*appmessage.ChainBlock, error) { + acceptedBlocks := make([]*appmessage.AcceptedBlock, len(x.AcceptedBlocks)) + for j, acceptedBlock := range x.AcceptedBlocks { + acceptedBlocks[j] = &appmessage.AcceptedBlock{ + Hash: acceptedBlock.Hash, + AcceptedTxIDs: acceptedBlock.AcceptedTxIds, + } + } + return &appmessage.ChainBlock{ + Hash: x.Hash, + AcceptedBlocks: acceptedBlocks, + }, nil +} + +func (x *ChainBlock) fromAppMessage(message *appmessage.ChainBlock) error { + acceptedBlocks := make([]*AcceptedBlock, len(message.AcceptedBlocks)) + for j, acceptedBlock := range message.AcceptedBlocks { + acceptedBlocks[j] = &AcceptedBlock{ + Hash: acceptedBlock.Hash, + AcceptedTxIds: acceptedBlock.AcceptedTxIDs, + } + } + *x = ChainBlock{ + Hash: message.Hash, + AcceptedBlocks: acceptedBlocks, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_submit_block.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_submit_block.go new file mode 100644 index 000000000..cda9ff231 --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_submit_block.go @@ -0,0 +1,37 @@ +package protowire + +import "github.com/kaspanet/kaspad/app/appmessage" + +func (x *KaspadMessage_SubmitBlockRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.SubmitBlockRequestMessage{ + BlockHex: x.SubmitBlockRequest.BlockHex, + }, nil +} + +func (x *KaspadMessage_SubmitBlockRequest) fromAppMessage(message *appmessage.SubmitBlockRequestMessage) error { + x.SubmitBlockRequest = &SubmitBlockRequestMessage{ + BlockHex: message.BlockHex, + } + return nil +} + +func (x *KaspadMessage_SubmitBlockResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.SubmitBlockResponse.Error != nil { + err = &appmessage.RPCError{Message: x.SubmitBlockResponse.Error.Message} + } + return &appmessage.SubmitBlockResponseMessage{ + Error: err, + }, nil +} + +func (x *KaspadMessage_SubmitBlockResponse) fromAppMessage(message *appmessage.SubmitBlockResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + x.SubmitBlockResponse = &SubmitBlockResponseMessage{ + Error: err, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_submit_transaction.go b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_submit_transaction.go new file mode 100644 index 000000000..69896484d --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/rpc_submit_transaction.go @@ -0,0 +1,39 @@ +package protowire + +import "github.com/kaspanet/kaspad/app/appmessage" + +func (x *KaspadMessage_SubmitTransactionRequest) toAppMessage() (appmessage.Message, error) { + return &appmessage.SubmitTransactionRequestMessage{ + TransactionHex: x.SubmitTransactionRequest.TransactionHex, + }, nil +} + +func (x *KaspadMessage_SubmitTransactionRequest) fromAppMessage(message *appmessage.SubmitTransactionRequestMessage) error { + x.SubmitTransactionRequest = &SubmitTransactionRequestMessage{ + TransactionHex: message.TransactionHex, + } + return nil +} + +func (x *KaspadMessage_SubmitTransactionResponse) toAppMessage() (appmessage.Message, error) { + var err *appmessage.RPCError + if x.SubmitTransactionResponse.Error != nil { + err = &appmessage.RPCError{Message: x.SubmitTransactionResponse.Error.Message} + } + return &appmessage.SubmitTransactionResponseMessage{ + TxID: x.SubmitTransactionResponse.TxId, + Error: err, + }, nil +} + +func (x *KaspadMessage_SubmitTransactionResponse) fromAppMessage(message *appmessage.SubmitTransactionResponseMessage) error { + var err *RPCError + if message.Error != nil { + err = &RPCError{Message: message.Error.Message} + } + x.SubmitTransactionResponse = &SubmitTransactionResponseMessage{ + TxId: message.TxID, + Error: err, + } + return nil +} diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/wire.go b/infrastructure/network/netadapter/server/grpcserver/protowire/wire.go index 04d485f55..fd4c6d1b4 100644 --- a/infrastructure/network/netadapter/server/grpcserver/protowire/wire.go +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/wire.go @@ -11,7 +11,11 @@ type converter interface { // ToAppMessage converts a KaspadMessage to its appmessage.Message representation func (x *KaspadMessage) ToAppMessage() (appmessage.Message, error) { - return x.Payload.(converter).toAppMessage() + appMessage, err := x.Payload.(converter).toAppMessage() + if err != nil { + return nil, err + } + return appMessage, nil } // FromAppMessage creates a KaspadMessage from a appmessage.Message @@ -26,6 +30,26 @@ func FromAppMessage(message appmessage.Message) (*KaspadMessage, error) { } func toPayload(message appmessage.Message) (isKaspadMessage_Payload, error) { + p2pPayload, err := toP2PPayload(message) + if err != nil { + return nil, err + } + if p2pPayload != nil { + return p2pPayload, nil + } + + rpcPayload, err := toRPCPayload(message) + if err != nil { + return nil, err + } + if rpcPayload != nil { + return rpcPayload, nil + } + + return nil, errors.Errorf("unknown message type %T", message) +} + +func toP2PPayload(message appmessage.Message) (isKaspadMessage_Payload, error) { switch message := message.(type) { case *appmessage.MsgAddresses: payload := new(KaspadMessage_Addresses) @@ -182,6 +206,237 @@ func toPayload(message appmessage.Message) (isKaspadMessage_Payload, error) { } return payload, nil default: - return nil, errors.Errorf("unknown message type %T", message) + return nil, nil + } +} + +func toRPCPayload(message appmessage.Message) (isKaspadMessage_Payload, error) { + switch message := message.(type) { + case *appmessage.GetCurrentNetworkRequestMessage: + payload := new(KaspadMessage_GetCurrentNetworkRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetCurrentNetworkResponseMessage: + payload := new(KaspadMessage_GetCurrentNetworkResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.SubmitBlockRequestMessage: + payload := new(KaspadMessage_SubmitBlockRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.SubmitBlockResponseMessage: + payload := new(KaspadMessage_SubmitBlockResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetBlockTemplateRequestMessage: + payload := new(KaspadMessage_GetBlockTemplateRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetBlockTemplateResponseMessage: + payload := new(KaspadMessage_GetBlockTemplateResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.NotifyBlockAddedRequestMessage: + payload := new(KaspadMessage_NotifyBlockAddedRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.NotifyBlockAddedResponseMessage: + payload := new(KaspadMessage_NotifyBlockAddedResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.BlockAddedNotificationMessage: + payload := new(KaspadMessage_BlockAddedNotification) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetPeerAddressesRequestMessage: + payload := new(KaspadMessage_GetPeerAddressesRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetPeerAddressesResponseMessage: + payload := new(KaspadMessage_GetPeerAddressesResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetSelectedTipHashRequestMessage: + payload := new(KaspadMessage_GetSelectedTipHashRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetSelectedTipHashResponseMessage: + payload := new(KaspadMessage_GetSelectedTipHashResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetMempoolEntryRequestMessage: + payload := new(KaspadMessage_GetMempoolEntryRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetMempoolEntryResponseMessage: + payload := new(KaspadMessage_GetMempoolEntryResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetConnectedPeerInfoRequestMessage: + payload := new(KaspadMessage_GetConnectedPeerInfoRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetConnectedPeerInfoResponseMessage: + payload := new(KaspadMessage_GetConnectedPeerInfoResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.AddPeerRequestMessage: + payload := new(KaspadMessage_AddPeerRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.AddPeerResponseMessage: + payload := new(KaspadMessage_AddPeerResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.SubmitTransactionRequestMessage: + payload := new(KaspadMessage_SubmitTransactionRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.SubmitTransactionResponseMessage: + payload := new(KaspadMessage_SubmitTransactionResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.NotifyChainChangedRequestMessage: + payload := new(KaspadMessage_NotifyChainChangedRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.NotifyChainChangedResponseMessage: + payload := new(KaspadMessage_NotifyChainChangedResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.ChainChangedNotificationMessage: + payload := new(KaspadMessage_ChainChangedNotification) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetBlockRequestMessage: + payload := new(KaspadMessage_GetBlockRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetBlockResponseMessage: + payload := new(KaspadMessage_GetBlockResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetSubnetworkRequestMessage: + payload := new(KaspadMessage_GetSubnetworkRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetSubnetworkResponseMessage: + payload := new(KaspadMessage_GetSubnetworkResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetChainFromBlockRequestMessage: + payload := new(KaspadMessage_GetChainFromBlockRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetChainFromBlockResponseMessage: + payload := new(KaspadMessage_GetChainFromBlockResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetBlocksRequestMessage: + payload := new(KaspadMessage_GetBlocksRequest) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + case *appmessage.GetBlocksResponseMessage: + payload := new(KaspadMessage_GetBlocksResponse) + err := payload.fromAppMessage(message) + if err != nil { + return nil, err + } + return payload, nil + default: + return nil, nil } } diff --git a/infrastructure/network/netadapter/server/grpcserver/rpcserver.go b/infrastructure/network/netadapter/server/grpcserver/rpcserver.go new file mode 100644 index 000000000..6cd1a5bd0 --- /dev/null +++ b/infrastructure/network/netadapter/server/grpcserver/rpcserver.go @@ -0,0 +1,26 @@ +package grpcserver + +import ( + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/server" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/server/grpcserver/protowire" + "github.com/kaspanet/kaspad/util/panics" +) + +type rpcServer struct { + protowire.UnimplementedRPCServer + gRPCServer +} + +// NewRPCServer creates a new RPCServer +func NewRPCServer(listeningAddresses []string) (server.Server, error) { + gRPCServer := newGRPCServer(listeningAddresses) + rpcServer := &rpcServer{gRPCServer: *gRPCServer} + protowire.RegisterRPCServer(gRPCServer.server, rpcServer) + return rpcServer, nil +} + +func (r *rpcServer) MessageStream(stream protowire.RPC_MessageStreamServer) error { + defer panics.HandlePanic(log, "rpcServer.MessageStream", nil) + + return r.handleInboundConnection(stream.Context(), stream) +} diff --git a/infrastructure/network/netadapter/server/server.go b/infrastructure/network/netadapter/server/server.go index 527df26bf..90b3b9050 100644 --- a/infrastructure/network/netadapter/server/server.go +++ b/infrastructure/network/netadapter/server/server.go @@ -4,8 +4,6 @@ import ( "fmt" "net" - "github.com/pkg/errors" - "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" ) @@ -22,15 +20,20 @@ type OnDisconnectedHandler func() // was received from a connection. type OnInvalidMessageHandler func(err error) -// Server represents a p2p server. +// Server represents a server. type Server interface { - Connect(address string) (Connection, error) Start() error Stop() error SetOnConnectedHandler(onConnectedHandler OnConnectedHandler) } -// Connection represents a p2p server connection. +// P2PServer represents a p2p server. +type P2PServer interface { + Server + Connect(address string) (Connection, error) +} + +// Connection represents a server connection. type Connection interface { fmt.Stringer Start(router *router.Router) @@ -41,7 +44,3 @@ type Connection interface { SetOnInvalidMessageHandler(onInvalidMessageHandler OnInvalidMessageHandler) Address() *net.TCPAddr } - -// ErrNetwork is an error related to the internals of the connection, and not an error that -// came from outside (e.g. from OnDisconnectedHandler). -var ErrNetwork = errors.New("network error") diff --git a/infrastructure/network/netadapter/standalone/minimal_net_adapter.go b/infrastructure/network/netadapter/standalone/minimal_net_adapter.go index c13c5d8f6..da65d7571 100644 --- a/infrastructure/network/netadapter/standalone/minimal_net_adapter.go +++ b/infrastructure/network/netadapter/standalone/minimal_net_adapter.go @@ -35,7 +35,7 @@ func NewMinimalNetAdapter(cfg *config.Config) (*MinimalNetAdapter, error) { routerInitializer, routesChan := generateRouteInitializer() - netAdapter.SetRouterInitializer(routerInitializer) + netAdapter.SetP2PRouterInitializer(routerInitializer) err = netAdapter.Start() if err != nil { return nil, errors.Wrap(err, "Error starting netAdapter") @@ -57,7 +57,7 @@ func (mna *MinimalNetAdapter) Connect(address string) (*Routes, error) { mna.lock.Lock() defer mna.lock.Unlock() - err := mna.netAdapter.Connect(address) + err := mna.netAdapter.P2PConnect(address) if err != nil { return nil, err } @@ -148,9 +148,9 @@ func (mna *MinimalNetAdapter) handleHandshake(routes *Routes, ourID *id.ID) erro func generateRouteInitializer() (netadapter.RouterInitializer, <-chan *Routes) { cmdsWithBuiltInRoutes := []appmessage.MessageCommand{appmessage.CmdVerAck, appmessage.CmdVersion, appmessage.CmdPing} - everythingElse := make([]appmessage.MessageCommand, 0, len(appmessage.MessageCommandToString)-len(cmdsWithBuiltInRoutes)) + everythingElse := make([]appmessage.MessageCommand, 0, len(appmessage.ProtocolMessageCommandToString)-len(cmdsWithBuiltInRoutes)) outerLoop: - for command := range appmessage.MessageCommandToString { + for command := range appmessage.ProtocolMessageCommandToString { for _, cmdWithBuiltInRoute := range cmdsWithBuiltInRoutes { if command == cmdWithBuiltInRoute { continue outerLoop diff --git a/infrastructure/network/rpc/client/CONTRIBUTORS b/infrastructure/network/rpc/client/CONTRIBUTORS deleted file mode 100644 index 86c2b9938..000000000 --- a/infrastructure/network/rpc/client/CONTRIBUTORS +++ /dev/null @@ -1,13 +0,0 @@ -# This is the list of people who have contributed code to the repository. -# -# Names should be added to this file only after verifying that the individual -# or the individual's organization has agreed to the LICENSE. -# -# Names should be added to this file like so: -# Name - -Dave Collins -Geert-Johan Riemer -Josh Rickmar -Michalis Kargakis -Ruben de Vries time.Minute { - scaledDuration = time.Minute - } - log.Infof("Retrying connection to %s in "+ - "%s", c.config.Host, scaledDuration) - time.Sleep(scaledDuration) - continue reconnect - } - - log.Infof("Reestablished connection to RPC server %s", - c.config.Host) - - // Reset the connection state and signal the reconnect - // has happened. - c.wsConn = wsConn - c.retryCount = 0 - - func() { - c.mtx.Lock() - defer c.mtx.Unlock() - c.disconnect = make(chan struct{}) - c.disconnected = false - }() - - // Start processing input and output for the - // new connection. - c.start() - - // Reissue pending requests in another goroutine since - // the send can block. - spawn("Client.resendRequests", c.resendRequests) - - // Break out of the reconnect loop back to wait for - // disconnect again. - break reconnect - } - } - c.wg.Done() - log.Tracef("RPC client reconnect handler done for %s", c.config.Host) -} - -// handleSendPostMessage handles performing the passed HTTP request, reading the -// result, unmarshalling it, and delivering the unmarshalled result to the -// provided response channel. -func (c *Client) handleSendPostMessage(details *sendPostDetails) { - jReq := details.jsonRequest - log.Tracef("Sending command [%s] with id %d", jReq.method, jReq.id) - httpResponse, err := c.httpClient.Do(details.httpRequest) - if err != nil { - jReq.responseChan <- &response{err: err} - return - } - - // Read the raw bytes and close the response. - respBytes, err := func() ([]byte, error) { - defer httpResponse.Body.Close() - return ioutil.ReadAll(httpResponse.Body) - }() - if err != nil { - jReq.responseChan <- &response{ - err: errors.Wrap(err, "error reading json reply"), - } - return - } - - // Try to unmarshal the response as a regular JSON-RPC response. - var resp rawResponse - err = json.Unmarshal(respBytes, &resp) - if err != nil { - // When the response itself isn't a valid JSON-RPC response - // return an error which includes the HTTP status code and raw - // response bytes. - err = errors.Errorf("status code: %d, response: %q", - httpResponse.StatusCode, string(respBytes)) - jReq.responseChan <- &response{err: err} - return - } - - res, err := resp.result() - jReq.responseChan <- &response{result: res, err: err} -} - -// sendPostHandler handles all outgoing messages when the client is running -// in HTTP POST mode. It uses a buffered channel to serialize output messages -// while allowing the sender to continue running asynchronously. It must be run -// as a goroutine. -func (c *Client) sendPostHandler() { -out: - for { - // Send any messages ready for send until the shutdown channel - // is closed. - select { - case details := <-c.sendPostChan: - c.handleSendPostMessage(details) - - case <-c.shutdown: - break out - } - } - - // Drain any wait channels before exiting so nothing is left waiting - // around to send. -cleanup: - for { - select { - case details := <-c.sendPostChan: - details.jsonRequest.responseChan <- &response{ - result: nil, - err: ErrClientShutdown, - } - - default: - break cleanup - } - } - c.wg.Done() - log.Tracef("RPC client send handler done for %s", c.config.Host) - -} - -// sendPostRequest sends the passed HTTP request to the RPC server using the -// HTTP client associated with the client. It is backed by a buffered channel, -// so it will not block until the send channel is full. -func (c *Client) sendPostRequest(httpReq *http.Request, jReq *jsonRequest) { - // Don't send the message if shutting down. - select { - case <-c.shutdown: - jReq.responseChan <- &response{result: nil, err: ErrClientShutdown} - default: - } - - c.sendPostChan <- &sendPostDetails{ - jsonRequest: jReq, - httpRequest: httpReq, - } -} - -// newFutureError returns a new future result channel that already has the -// passed error waitin on the channel with the reply set to nil. This is useful -// to easily return errors from the various Async functions. -func newFutureError(err error) chan *response { - responseChan := make(chan *response, 1) - responseChan <- &response{err: err} - return responseChan -} - -// receiveFuture receives from the passed futureResult channel to extract a -// reply or any errors. The examined errors include an error in the -// futureResult and the error in the reply from the server. This will block -// until the result is available on the passed channel. -func receiveFuture(f chan *response) ([]byte, error) { - // Wait for a response on the returned channel. - r := <-f - var err error - if r.err != nil { - err = errors.Wrap(r.err, "got error from response channel") - } - return r.result, err -} - -// sendPost sends the passed request to the server by issuing an HTTP POST -// request using the provided response channel for the reply. Typically a new -// connection is opened and closed for each command when using this method, -// however, the underlying HTTP client might coalesce multiple commands -// depending on several factors including the remote server configuration. -func (c *Client) sendPost(jReq *jsonRequest) { - // Generate a request to the configured RPC server. - protocol := "http" - if !c.config.DisableTLS { - protocol = "https" - } - url := protocol + "://" + c.config.Host - bodyReader := bytes.NewReader(jReq.marshalledJSON) - httpReq, err := http.NewRequest("POST", url, bodyReader) - if err != nil { - jReq.responseChan <- &response{result: nil, err: err} - return - } - httpReq.Close = true - httpReq.Header.Set("Content-Type", "application/json") - - // Configure basic access authorization. - httpReq.SetBasicAuth(c.config.User, c.config.Pass) - - log.Tracef("Sending command [%s] with id %d", jReq.method, jReq.id) - c.sendPostRequest(httpReq, jReq) -} - -// sendRequest sends the passed json request to the associated server using the -// provided response channel for the reply. It handles both websocket and HTTP -// POST mode depending on the configuration of the client. -func (c *Client) sendRequest(data *jsonRequestData) chan *response { - jReq := &jsonRequest{ - jsonRequestData: data, - } - responseChan := make(chan *response, 1) - cancelOnTimeout := c.config.RequestTimeout != 0 && !c.config.HTTPPostMode - if cancelOnTimeout { - jReq.responseChan = make(chan *response, 1) - } else { - jReq.responseChan = responseChan - } - spawn("Client.sendRequest-sendMessage", func() { - // Choose which marshal and send function to use depending on whether - // the client running in HTTP POST mode or not. When running in HTTP - // POST mode, the command is issued via an HTTP client. Otherwise, - // the command is issued via the asynchronous websocket channels. - if c.config.HTTPPostMode { - c.sendPost(jReq) - return - } - - // Check whether the websocket connection has never been established, - // in which case the handler goroutines are not running. - select { - case <-c.connEstablished: - default: - jReq.responseChan <- &response{err: errors.WithStack(ErrClientNotConnected)} - return - } - - // Add the request to the internal tracking map so the response from the - // remote server can be properly detected and routed to the response - // channel. Then send the marshalled request via the websocket - // connection. - if err := c.addRequest(jReq); err != nil { - jReq.responseChan <- &response{err: err} - return - } - log.Tracef("Sending command [%s] with id %d", jReq.method, jReq.id) - c.sendMessage(jReq.marshalledJSON) - }) - if cancelOnTimeout { - spawn("Client.sendRequest-cancelOnTimeout", func() { - ticker := time.NewTicker(c.config.RequestTimeout) - defer ticker.Stop() - select { - case <-ticker.C: - responseChan <- &response{err: errors.WithStack(ErrResponseTimedOut)} - case resp := <-jReq.responseChan: - responseChan <- resp - } - }) - } - return responseChan -} - -// sendCmd sends the passed command to the associated server and returns a -// response channel on which the reply will be delivered at some point in the -// future. It handles both websocket and HTTP POST mode depending on the -// configuration of the client. -func (c *Client) sendCmd(cmd interface{}) chan *response { - // Get the method associated with the command. - method, err := model.CommandMethod(cmd) - if err != nil { - return newFutureError(err) - } - - // Marshal the command. - id := c.NextID() - marshalledJSON, err := model.MarshalCommand(id, cmd) - if err != nil { - return newFutureError(err) - } - - // Generate the request. - jReqData := &jsonRequestData{ - id: id, - method: method, - cmd: cmd, - marshalledJSON: marshalledJSON, - } - // Send the request and return its response channel - return c.sendRequest(jReqData) -} - -// Disconnected returns whether or not the server is disconnected. If a -// websocket client was created but never connected, this also returns false. -func (c *Client) Disconnected() bool { - c.mtx.Lock() - defer c.mtx.Unlock() - - select { - case <-c.connEstablished: - return c.disconnected - default: - return false - } -} - -// doDisconnect disconnects the websocket associated with the client if it -// hasn't already been disconnected. It will return false if the disconnect is -// not needed or the client is running in HTTP POST mode. -// -// This function is safe for concurrent access. -func (c *Client) doDisconnect() bool { - if c.config.HTTPPostMode { - return false - } - - c.mtx.Lock() - defer c.mtx.Unlock() - - // Nothing to do if already disconnected. - if c.disconnected { - return false - } - - log.Tracef("Disconnecting RPC client %s", c.config.Host) - close(c.disconnect) - if c.wsConn != nil { - c.wsConn.Close() - } - c.disconnected = true - return true -} - -// doShutdown closes the shutdown channel and logs the shutdown unless shutdown -// is already in progress. It will return false if the shutdown is not needed. -// -// This function is safe for concurrent access. -func (c *Client) doShutdown() bool { - // Ignore the shutdown request if the client is already in the process - // of shutting down or already shutdown. - select { - case <-c.shutdown: - return false - default: - } - - log.Tracef("Shutting down RPC client %s", c.config.Host) - close(c.shutdown) - return true -} - -// Disconnect disconnects the current websocket associated with the client. The -// connection will automatically be re-established unless the client was -// created with the DisableAutoReconnect flag. -// -// This function has no effect when the client is running in HTTP POST mode. -func (c *Client) Disconnect() { - // Nothing to do if already disconnected or running in HTTP POST mode. - if !c.doDisconnect() { - return - } - - c.requestLock.Lock() - defer c.requestLock.Unlock() - - // When operating without auto reconnect, send errors to any pending - // requests and shutdown the client. - if c.config.DisableAutoReconnect { - for e := c.requestList.Front(); e != nil; e = e.Next() { - req := e.Value.(*jsonRequest) - req.responseChan <- &response{ - result: nil, - err: ErrClientDisconnect, - } - } - c.removeAllRequests() - c.doShutdown() - } -} - -// Shutdown shuts down the client by disconnecting any connections associated -// with the client and, when automatic reconnect is enabled, preventing future -// attempts to reconnect. It also stops all goroutines. -func (c *Client) Shutdown() { - // Do the shutdown under the request lock to prevent clients from - // adding new requests while the client shutdown process is initiated. - c.requestLock.Lock() - defer c.requestLock.Unlock() - - // Ignore the shutdown request if the client is already in the process - // of shutting down or already shutdown. - if !c.doShutdown() { - return - } - - // Send the ErrClientShutdown error to any pending requests. - for e := c.requestList.Front(); e != nil; e = e.Next() { - req := e.Value.(*jsonRequest) - req.responseChan <- &response{ - result: nil, - err: ErrClientShutdown, - } - } - c.removeAllRequests() - - // Disconnect the client if needed. - c.doDisconnect() -} - -// start begins processing input and output messages. -func (c *Client) start() { - log.Tracef("Starting RPC client %s", c.config.Host) - - // Start the I/O processing handlers depending on whether the client is - // in HTTP POST mode or the default websocket mode. - if c.config.HTTPPostMode { - c.wg.Add(1) - spawn("Client.sendPostHandler", c.sendPostHandler) - } else { - c.wg.Add(3) - spawn("Client.OnClientConnected", func() { - if c.ntfnHandlers != nil { - if c.ntfnHandlers.OnClientConnected != nil { - c.ntfnHandlers.OnClientConnected() - } - } - c.wg.Done() - }) - spawn("Client.wsInHandler", c.wsInHandler) - spawn("Client.wsOutHandler", c.wsOutHandler) - } -} - -// WaitForShutdown blocks until the client goroutines are stopped and the -// connection is closed. -func (c *Client) WaitForShutdown() { - c.wg.Wait() -} - -// ConnConfig describes the connection configuration parameters for the client. -// This -type ConnConfig struct { - // Host is the IP address and port of the RPC server you want to connect - // to. - Host string - - // Endpoint is the websocket endpoint on the RPC server. This is - // typically "ws". - Endpoint string - - // User is the username to use to authenticate to the RPC server. - User string - - // Pass is the passphrase to use to authenticate to the RPC server. - Pass string - - // DisableTLS specifies whether transport layer security should be - // disabled. It is recommended to always use TLS if the RPC server - // supports it as otherwise your username and password is sent across - // the appmessage in cleartext. - DisableTLS bool - - // Certificates are the bytes for a PEM-encoded certificate chain used - // for the TLS connection. It has no effect if the DisableTLS parameter - // is true. - Certificates []byte - - // Proxy specifies to connect through a SOCKS 5 proxy server. It may - // be an empty string if a proxy is not required. - Proxy string - - // ProxyUser is an optional username to use for the proxy server if it - // requires authentication. It has no effect if the Proxy parameter - // is not set. - ProxyUser string - - // ProxyPass is an optional password to use for the proxy server if it - // requires authentication. It has no effect if the Proxy parameter - // is not set. - ProxyPass string - - // DisableAutoReconnect specifies the client should not automatically - // try to reconnect to the server when it has been disconnected. - DisableAutoReconnect bool - - // DisableConnectOnNew specifies that a websocket client connection - // should not be tried when creating the client with New. Instead, the - // client is created and returned unconnected, and Connect must be - // called manually. - DisableConnectOnNew bool - - // HTTPPostMode instructs the client to run using multiple independent - // connections issuing HTTP POST requests instead of using the default - // of websockets. Websockets are generally preferred as some of the - // features of the client such notifications only work with websockets, - // however, not all servers support the websocket extensions, so this - // flag can be set to true to use basic HTTP POST requests instead. - HTTPPostMode bool - - // RequestTimeout is the time it'll take for a request to timeout if - // it doesn't get a response. - RequestTimeout time.Duration - - // ConnectionTimeout is the time it'll take for to try to connect - // to the RPC server before the connection times out. - ConnectionTimeout time.Duration -} - -// newHTTPClient returns a new http client that is configured according to the -// proxy and TLS settings in the associated connection configuration. -func newHTTPClient(config *ConnConfig) (*http.Client, error) { - // Set proxy function if there is a proxy configured. - var proxyFunc func(*http.Request) (*url.URL, error) - if config.Proxy != "" { - proxyURL, err := url.Parse(config.Proxy) - if err != nil { - return nil, err - } - proxyFunc = http.ProxyURL(proxyURL) - } - - // Configure TLS if needed. - var tlsConfig *tls.Config - if !config.DisableTLS { - if len(config.Certificates) > 0 { - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(config.Certificates) - tlsConfig = &tls.Config{ - RootCAs: pool, - } - } - } - - client := http.Client{ - Transport: &http.Transport{ - Proxy: proxyFunc, - TLSClientConfig: tlsConfig, - }, - Timeout: config.ConnectionTimeout, - } - - return &client, nil -} - -// dial opens a websocket connection using the passed connection configuration -// details. -func dial(config *ConnConfig) (*websocket.Conn, error) { - // Setup TLS if not disabled. - var tlsConfig *tls.Config - var scheme = "ws" - if !config.DisableTLS { - tlsConfig = &tls.Config{ - MinVersion: tls.VersionTLS12, - } - if len(config.Certificates) > 0 { - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(config.Certificates) - tlsConfig.RootCAs = pool - } - scheme = "wss" - } - - // Create a websocket dialer that will be used to make the connection. - // It is modified by the proxy setting below as needed. - dialer := websocket.Dialer{ - TLSClientConfig: tlsConfig, - HandshakeTimeout: config.ConnectionTimeout, - } - - // Setup the proxy if one is configured. - if config.Proxy != "" { - proxy := &socks.Proxy{ - Addr: config.Proxy, - Username: config.ProxyUser, - Password: config.ProxyPass, - } - dialer.NetDial = proxy.Dial - } - - // The RPC server requires basic authorization, so create a custom - // request header with the Authorization header set. - login := config.User + ":" + config.Pass - auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) - requestHeader := make(http.Header) - requestHeader.Add("Authorization", auth) - - // Dial the connection. - url := fmt.Sprintf("%s://%s/%s", scheme, config.Host, config.Endpoint) - wsConn, resp, err := dialer.Dial(url, requestHeader) - if err != nil { - if !nativeerrors.Is(err, websocket.ErrBadHandshake) || resp == nil { - return nil, err - } - - // Detect HTTP authentication error status codes. - if resp.StatusCode == http.StatusUnauthorized || - resp.StatusCode == http.StatusForbidden { - return nil, errors.WithStack(ErrInvalidAuth) - } - - // The connection was authenticated and the status response was - // ok, but the websocket handshake still failed, so the endpoint - // is invalid in some way. - if resp.StatusCode == http.StatusOK { - return nil, errors.WithStack(ErrInvalidEndpoint) - } - - // Return the status text from the server if none of the special - // cases above apply. - return nil, errors.New(resp.Status) - } - return wsConn, nil -} - -// New creates a new RPC client based on the provided connection configuration -// details. The notification handlers parameter may be nil if you are not -// interested in receiving notifications and will be ignored if the -// configuration is set to run in HTTP POST mode. -func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error) { - // Either open a websocket connection or create an HTTP client depending - // on the HTTP POST mode. Also, set the notification handlers to nil - // when running in HTTP POST mode. - var wsConn *websocket.Conn - var httpClient *http.Client - connEstablished := make(chan struct{}) - var start bool - if config.HTTPPostMode { - ntfnHandlers = nil - start = true - - var err error - httpClient, err = newHTTPClient(config) - if err != nil { - return nil, err - } - } else { - if !config.DisableConnectOnNew { - var err error - wsConn, err = dial(config) - if err != nil { - return nil, err - } - start = true - } - } - - client := &Client{ - config: config, - wsConn: wsConn, - httpClient: httpClient, - requestMap: make(map[uint64]*list.Element), - requestList: list.New(), - ntfnHandlers: ntfnHandlers, - ntfnState: newNotificationState(), - sendChan: make(chan []byte, sendBufferSize), - sendPostChan: make(chan *sendPostDetails, sendPostBufferSize), - connEstablished: connEstablished, - disconnect: make(chan struct{}), - shutdown: make(chan struct{}), - } - - if start { - log.Infof("Established connection to RPC server %s", - config.Host) - close(connEstablished) - client.start() - if !client.config.HTTPPostMode && !client.config.DisableAutoReconnect { - client.wg.Add(1) - spawn("Client.wsReconnectHandler", client.wsReconnectHandler) - } - } - - return client, nil -} - -// Connect establishes the initial websocket connection. This is necessary when -// a client was created after setting the DisableConnectOnNew field of the -// Config struct. -// -// Up to tries number of connections (each after an increasing backoff) will -// be tried if the connection can not be established. The special value of 0 -// indicates an unlimited number of connection attempts. -// -// This method will error if the client is not configured for websockets, if the -// connection has already been established, or if none of the connection -// attempts were successful. -func (c *Client) Connect(tries int) error { - c.mtx.Lock() - defer c.mtx.Unlock() - - if c.config.HTTPPostMode { - return ErrNotWebsocketClient - } - if c.wsConn != nil { - return ErrClientAlreadyConnected - } - - // Begin connection attempts. Increase the backoff after each failed - // attempt, up to a maximum of one minute. - var err error - var backoff time.Duration - for i := 0; tries == 0 || i < tries; i++ { - var wsConn *websocket.Conn - wsConn, err = dial(c.config) - if err != nil { - backoff = connectionRetryInterval * time.Duration(i+1) - if backoff > time.Minute { - backoff = time.Minute - } - time.Sleep(backoff) - continue - } - - // Connection was established. Set the websocket connection - // member of the client and start the goroutines necessary - // to run the client. - log.Infof("Established connection to RPC server %s", - c.config.Host) - c.wsConn = wsConn - close(c.connEstablished) - c.start() - if !c.config.DisableAutoReconnect { - c.wg.Add(1) - spawn("Client.wsReconnectHandler", c.wsReconnectHandler) - } - return nil - } - - // All connection attempts failed, so return the last error. - return err -} diff --git a/infrastructure/network/rpc/client/log.go b/infrastructure/network/rpc/client/log.go deleted file mode 100644 index 3a1ed0a28..000000000 --- a/infrastructure/network/rpc/client/log.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package client - -import ( - "github.com/kaspanet/kaspad/infrastructure/logger" - "github.com/kaspanet/kaspad/util/panics" -) - -// log is a logger that is initialized with no output filters. This -// means the package will not perform any logging by default until the caller -// requests it. -var log *logger.Logger -var spawn func(name string, f func()) - -const logSubsytem = "RPCC" - -// The default amount of logging is none. -func init() { - DisableLog() -} - -// DisableLog disables all library log output. Logging output is disabled -// by default until UseLogger is called. -func DisableLog() { - backend := logger.NewBackend() - log = backend.Logger(logSubsytem) - log.SetLevel(logger.LevelOff) - spawn = panics.GoroutineWrapperFunc(log) -} - -// UseLogger uses a specified Logger to output package logging info. -func UseLogger(backend *logger.Backend, level logger.Level) { - log = backend.Logger(logSubsytem) - log.SetLevel(level) - spawn = panics.GoroutineWrapperFunc(log) -} diff --git a/infrastructure/network/rpc/client/mining.go b/infrastructure/network/rpc/client/mining.go deleted file mode 100644 index e489d50ac..000000000 --- a/infrastructure/network/rpc/client/mining.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package client - -import ( - "encoding/hex" - "encoding/json" - "strconv" - "strings" - - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/daghash" - "github.com/pkg/errors" -) - -// FutureSubmitBlockResult is a future promise to deliver the result of a -// SubmitBlockAsync RPC invocation (or an applicable error). -type FutureSubmitBlockResult chan *response - -// Receive waits for the response promised by the future and returns an error if -// any occurred when submitting the block. -func (r FutureSubmitBlockResult) Receive() error { - res, err := receiveFuture(r) - if err != nil { - return err - } - - if string(res) != "null" { - var result string - err = json.Unmarshal(res, &result) - if err != nil { - return err - } - - return errors.New(result) - } - - return nil -} - -// SubmitBlockAsync returns an instance of a type that can be used to get the -// result of the RPC at some future time by invoking the Receive function on the -// returned instance. -// -// See SubmitBlock for the blocking version and more details. -func (c *Client) SubmitBlockAsync(block *util.Block, options *model.SubmitBlockOptions) FutureSubmitBlockResult { - blockHex := "" - if block != nil { - blockBytes, err := block.Bytes() - if err != nil { - return newFutureError(err) - } - - blockHex = hex.EncodeToString(blockBytes) - } - - cmd := model.NewSubmitBlockCmd(blockHex, options) - return c.sendCmd(cmd) -} - -// SubmitBlock attempts to submit a new block into the kaspa network. -func (c *Client) SubmitBlock(block *util.Block, options *model.SubmitBlockOptions) error { - return c.SubmitBlockAsync(block, options).Receive() -} - -// FutureGetBlockTemplateResult is a future promise to deliver the result of a -// GetBlockTemplate RPC invocation (or an applicable error). -type FutureGetBlockTemplateResult chan *response - -// GetBlockTemplateAsync returns an instance of a type that can be used to get -// the result of the RPC at some future time by invoking the Receive function on -// the returned instance. -// -// See GetBlockTemplate for the blocking version and more details -func (c *Client) GetBlockTemplateAsync(payAddress string, longPollID string) FutureGetBlockTemplateResult { - request := &model.TemplateRequest{ - Mode: "template", - LongPollID: longPollID, - PayAddress: payAddress, - } - cmd := model.NewGetBlockTemplateCmd(request) - return c.sendCmd(cmd) -} - -// Receive waits for the response promised by the future and returns an error if -// any occurred when submitting the block. -func (r FutureGetBlockTemplateResult) Receive() (*model.GetBlockTemplateResult, error) { - res, err := receiveFuture(r) - if err != nil { - return nil, err - } - - var result model.GetBlockTemplateResult - if err := json.Unmarshal(res, &result); err != nil { - return nil, err - } - return &result, nil -} - -// GetBlockTemplate request a block template from the server, to mine upon -func (c *Client) GetBlockTemplate(payAddress string, longPollID string) (*model.GetBlockTemplateResult, error) { - return c.GetBlockTemplateAsync(payAddress, longPollID).Receive() -} - -// ConvertGetBlockTemplateResultToBlock Accepts a GetBlockTemplateResult and parses it into a Block -func ConvertGetBlockTemplateResultToBlock(template *model.GetBlockTemplateResult) (*util.Block, error) { - // parse parent hashes - parentHashes := make([]*daghash.Hash, len(template.ParentHashes)) - for i, parentHash := range template.ParentHashes { - hash, err := daghash.NewHashFromStr(parentHash) - if err != nil { - return nil, errors.Wrapf(err, "error decoding hash: '%s'", parentHash) - } - parentHashes[i] = hash - } - - // parse Bits - bitsUint64, err := strconv.ParseUint(template.Bits, 16, 32) - if err != nil { - return nil, errors.Wrapf(err, "error decoding bits: '%s'", template.Bits) - } - bits := uint32(bitsUint64) - - // parse hashMerkleRoot - hashMerkleRoot, err := daghash.NewHashFromStr(template.HashMerkleRoot) - if err != nil { - return nil, errors.Wrapf(err, "error parsing HashMerkleRoot: '%s'", template.HashMerkleRoot) - } - - // parse AcceptedIDMerkleRoot - acceptedIDMerkleRoot, err := daghash.NewHashFromStr(template.AcceptedIDMerkleRoot) - if err != nil { - return nil, errors.Wrapf(err, "error parsing acceptedIDMerkleRoot: '%s'", template.AcceptedIDMerkleRoot) - } - utxoCommitment, err := daghash.NewHashFromStr(template.UTXOCommitment) - if err != nil { - return nil, errors.Wrapf(err, "error parsing utxoCommitment '%s'", template.UTXOCommitment) - } - // parse rest of block - msgBlock := appmessage.NewMsgBlock( - appmessage.NewBlockHeader(template.Version, parentHashes, hashMerkleRoot, - acceptedIDMerkleRoot, utxoCommitment, bits, 0)) - - for i, txResult := range template.Transactions { - reader := hex.NewDecoder(strings.NewReader(txResult.Data)) - tx := &appmessage.MsgTx{} - if err := tx.KaspaDecode(reader, 0); err != nil { - return nil, errors.Wrapf(err, "error decoding tx #%d", i) - } - msgBlock.AddTransaction(tx) - } - - block := util.NewBlock(msgBlock) - return block, nil -} diff --git a/infrastructure/network/rpc/client/net.go b/infrastructure/network/rpc/client/net.go deleted file mode 100644 index e0fc41f2f..000000000 --- a/infrastructure/network/rpc/client/net.go +++ /dev/null @@ -1,543 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package client - -import ( - "bytes" - "encoding/hex" - "encoding/json" - - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/util/daghash" - "github.com/kaspanet/kaspad/util/pointers" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// FutureAddNodeResult is a future promise to deliver the result of an -// AddNodeAsync RPC invocation (or an applicable error). -type FutureAddNodeResult chan *response - -// Receive waits for the response promised by the future and returns an error if -// any occurred when performing the specified command. -func (r FutureAddNodeResult) Receive() error { - _, err := receiveFuture(r) - return err -} - -// ConnectNodeAsync returns an instance of a type that can be used to get the result -// of the RPC at some future time by invoking the Receive function on the -// returned instance. -// -// See Connect for the blocking version and more details. -func (c *Client) ConnectNodeAsync(host string) FutureAddNodeResult { - cmd := model.NewConnectCmd(host, pointers.Bool(false)) - return c.sendCmd(cmd) -} - -// ConnectNode attempts to perform the passed command on the passed persistent peer. -// For example, it can be used to add or a remove a persistent peer, or to do -// a one time connection to a peer. -// -// It may not be used to remove non-persistent peers. -func (c *Client) ConnectNode(host string) error { - return c.ConnectNodeAsync(host).Receive() -} - -// FutureGetConnectionCountResult is a future promise to deliver the result -// of a GetConnectionCountAsync RPC invocation (or an applicable error). -type FutureGetConnectionCountResult chan *response - -// Receive waits for the response promised by the future and returns the number -// of active connections to other peers. -func (r FutureGetConnectionCountResult) Receive() (int64, error) { - res, err := receiveFuture(r) - if err != nil { - return 0, err - } - - // Unmarshal result as an int64. - var count int64 - err = json.Unmarshal(res, &count) - if err != nil { - return 0, err - } - - return count, nil -} - -// GetConnectionCountAsync returns an instance of a type that can be used to get -// the result of the RPC at some future time by invoking the Receive function on -// the returned instance. -// -// See GetConnectionCount for the blocking version and more details. -func (c *Client) GetConnectionCountAsync() FutureGetConnectionCountResult { - cmd := model.NewGetConnectionCountCmd() - return c.sendCmd(cmd) -} - -// GetConnectionCount returns the number of active connections to other peers. -func (c *Client) GetConnectionCount() (int64, error) { - return c.GetConnectionCountAsync().Receive() -} - -// FuturePingResult is a future promise to deliver the result of a PingAsync RPC -// invocation (or an applicable error). -type FuturePingResult chan *response - -// Receive waits for the response promised by the future and returns the result -// of queueing a ping to be sent to each connected peer. -func (r FuturePingResult) Receive() error { - _, err := receiveFuture(r) - return err -} - -// PingAsync returns an instance of a type that can be used to get the result of -// the RPC at some future time by invoking the Receive function on the returned -// instance. -// -// See Ping for the blocking version and more details. -func (c *Client) PingAsync() FuturePingResult { - cmd := model.NewPingCmd() - return c.sendCmd(cmd) -} - -// Ping queues a ping to be sent to each connected peer. -// -// Use the GetConnectedPeerInfo function and examine the PingTime and PingWait fields to -// access the ping times. -func (c *Client) Ping() error { - return c.PingAsync().Receive() -} - -// FutureGetConnectedPeerInfo is a future promise to deliver the result of a -// GetConnectedPeerInfoAsync RPC invocation (or an applicable error). -type FutureGetConnectedPeerInfo chan *response - -// Receive waits for the response promised by the future and returns data about -// each connected network peer. -func (r FutureGetConnectedPeerInfo) Receive() ([]model.GetConnectedPeerInfoResult, error) { - res, err := receiveFuture(r) - if err != nil { - return nil, err - } - - // Unmarshal result as an array of getConnectedPeerInfo result objects. - var peerInfo []model.GetConnectedPeerInfoResult - err = json.Unmarshal(res, &peerInfo) - if err != nil { - return nil, err - } - - return peerInfo, nil -} - -// GetConnectedPeerInfoAsync returns an instance of a type that can be used to get the -// result of the RPC at some future time by invoking the Receive function on the -// returned instance. -// -// See GetConnectedPeerInfo for the blocking version and more details. -func (c *Client) GetConnectedPeerInfoAsync() FutureGetConnectedPeerInfo { - cmd := model.NewGetConnectedPeerInfoCmd() - return c.sendCmd(cmd) -} - -// GetConnectedPeerInfo returns data about each connected network peer. -func (c *Client) GetConnectedPeerInfo() ([]model.GetConnectedPeerInfoResult, error) { - return c.GetConnectedPeerInfoAsync().Receive() -} - -// FutureGetPeerAddresses is a future promise to deliver the result of a -// GetPeerAddresses RPC invocation (or an applicable error). -type FutureGetPeerAddresses chan *response - -// Receive waits for the response promised by the future and returns data about -// peer addresses. -func (r FutureGetPeerAddresses) Receive() (*model.GetPeerAddressesResult, error) { - res, err := receiveFuture(r) - if err != nil { - return nil, err - } - - // Unmarshal result as an array of getPeerAddresses result objects. - peerAddresses := &model.GetPeerAddressesResult{} - err = json.Unmarshal(res, peerAddresses) - if err != nil { - return nil, err - } - - return peerAddresses, nil -} - -// GetPeerAddressesAsync returns an instance of a type that can be used to get the -// result of the RPC at some future time by invoking the Receive function on the -// returned instance. -// -// See GetPeerAddresses for the blocking version and more details. -func (c *Client) GetPeerAddressesAsync() FutureGetPeerAddresses { - cmd := model.NewGetPeerAddressesCmd() - return c.sendCmd(cmd) -} - -// GetPeerAddresses returns data about each connected network peer. -func (c *Client) GetPeerAddresses() (*model.GetPeerAddressesResult, error) { - return c.GetPeerAddressesAsync().Receive() -} - -// FutureGetNetTotalsResult is a future promise to deliver the result of a -// GetNetTotalsAsync RPC invocation (or an applicable error). -type FutureGetNetTotalsResult chan *response - -// Receive waits for the response promised by the future and returns network -// traffic statistics. -func (r FutureGetNetTotalsResult) Receive() (*model.GetNetTotalsResult, error) { - res, err := receiveFuture(r) - if err != nil { - return nil, err - } - - // Unmarshal result as a getnettotals result object. - var totals model.GetNetTotalsResult - err = json.Unmarshal(res, &totals) - if err != nil { - return nil, err - } - - return &totals, nil -} - -// GetNetTotalsAsync returns an instance of a type that can be used to get the -// result of the RPC at some future time by invoking the Receive function on the -// returned instance. -// -// See GetNetTotals for the blocking version and more details. -func (c *Client) GetNetTotalsAsync() FutureGetNetTotalsResult { - cmd := model.NewGetNetTotalsCmd() - return c.sendCmd(cmd) -} - -// GetNetTotals returns network traffic statistics. -func (c *Client) GetNetTotals() (*model.GetNetTotalsResult, error) { - return c.GetNetTotalsAsync().Receive() -} - -// FutureDebugLevelResult is a future promise to deliver the result of a -// DebugLevelAsync RPC invocation (or an applicable error). -type FutureDebugLevelResult chan *response - -// Receive waits for the response promised by the future and returns the result -// of setting the debug logging level to the passed level specification or the -// list of of the available subsystems for the special keyword 'show'. -func (r FutureDebugLevelResult) Receive() (string, error) { - res, err := receiveFuture(r) - if err != nil { - return "", err - } - - // Unmashal the result as a string. - var result string - err = json.Unmarshal(res, &result) - if err != nil { - return "", err - } - return result, nil -} - -// DebugLevelAsync returns an instance of a type that can be used to get the -// result of the RPC at some future time by invoking the Receive function on -// the returned instance. -// -// See DebugLevel for the blocking version and more details. -func (c *Client) DebugLevelAsync(levelSpec string) FutureDebugLevelResult { - cmd := model.NewDebugLevelCmd(levelSpec) - return c.sendCmd(cmd) -} - -// DebugLevel dynamically sets the debug logging level to the passed level -// specification. -// -// The levelspec can be either a debug level or of the form: -// =,=,... -// -// Additionally, the special keyword 'show' can be used to get a list of the -// available subsystems. -func (c *Client) DebugLevel(levelSpec string) (string, error) { - return c.DebugLevelAsync(levelSpec).Receive() -} - -// FutureGetSelectedTipResult is a future promise to deliver the result of a -// GetSelectedTipAsync RPC invocation (or an applicable error). -type FutureGetSelectedTipResult chan *response - -// Receive waits for the response promised by the future and returns the -// selected tip block. -func (r FutureGetSelectedTipResult) Receive() (*appmessage.MsgBlock, error) { - res, err := receiveFuture(r) - if err != nil { - return nil, err - } - - // Unmarshal result as a string. - var blockHex string - err = json.Unmarshal(res, &blockHex) - if err != nil { - return nil, err - } - - // Decode the serialized block hex to raw bytes. - serializedBlock, err := hex.DecodeString(blockHex) - if err != nil { - return nil, err - } - - // Deserialize the block and return it. - var msgBlock appmessage.MsgBlock - err = msgBlock.Deserialize(bytes.NewReader(serializedBlock)) - if err != nil { - return nil, err - } - return &msgBlock, nil -} - -// GetSelectedTipAsync returns an instance of a type that can be used to get the -// result of the RPC at some future time by invoking the Receive function on the -// returned instance. -// -// See GetSelectedTip for the blocking version and more details. -func (c *Client) GetSelectedTipAsync() FutureGetSelectedTipResult { - cmd := model.NewGetSelectedTipCmd(pointers.Bool(false), pointers.Bool(false)) - return c.sendCmd(cmd) -} - -// GetSelectedTip returns the block of the selected DAG tip -func (c *Client) GetSelectedTip() (*model.GetBlockVerboseResult, error) { - return c.GetSelectedTipVerboseAsync().Receive() -} - -// FutureGetSelectedTipVerboseResult is a future promise to deliver the result of a -// GetSelectedTipVerboseAsync RPC invocation (or an applicable error). -type FutureGetSelectedTipVerboseResult chan *response - -// Receive waits for the response promised by the future and returns the data -// structure from the server with information about the requested block. -func (r FutureGetSelectedTipVerboseResult) Receive() (*model.GetBlockVerboseResult, error) { - res, err := receiveFuture(r) - if err != nil { - return nil, err - } - - // Unmarshal the raw result into a BlockResult. - var blockResult model.GetBlockVerboseResult - err = json.Unmarshal(res, &blockResult) - if err != nil { - return nil, err - } - return &blockResult, nil -} - -// GetSelectedTipVerboseAsync returns an instance of a type that can be used to get -// the result of the RPC at some future time by invoking the Receive function on -// the returned instance. -// -// See GeSelectedTipBlockVerbose for the blocking version and more details. -func (c *Client) GetSelectedTipVerboseAsync() FutureGetSelectedTipVerboseResult { - cmd := model.NewGetSelectedTipCmd(pointers.Bool(true), pointers.Bool(false)) - return c.sendCmd(cmd) -} - -// FutureGetCurrentNetResult is a future promise to deliver the result of a -// GetCurrentNetAsync RPC invocation (or an applicable error). -type FutureGetCurrentNetResult chan *response - -// Receive waits for the response promised by the future and returns the network -// the server is running on. -func (r FutureGetCurrentNetResult) Receive() (appmessage.KaspaNet, error) { - res, err := receiveFuture(r) - if err != nil { - return 0, err - } - - // Unmarshal result as an int64. - var net int64 - err = json.Unmarshal(res, &net) - if err != nil { - return 0, err - } - - return appmessage.KaspaNet(net), nil -} - -// GetCurrentNetAsync returns an instance of a type that can be used to get the -// result of the RPC at some future time by invoking the Receive function on the -// returned instance. -// -// See GetCurrentNet for the blocking version and more details. -func (c *Client) GetCurrentNetAsync() FutureGetCurrentNetResult { - cmd := model.NewGetCurrentNetCmd() - return c.sendCmd(cmd) -} - -// GetCurrentNet returns the network the server is running on. -func (c *Client) GetCurrentNet() (appmessage.KaspaNet, error) { - return c.GetCurrentNetAsync().Receive() -} - -// FutureGetHeadersResult is a future promise to deliver the result of a -// getheaders RPC invocation (or an applicable error). -type FutureGetHeadersResult chan *response - -// Receive waits for the response promised by the future and returns the -// getheaders result. -func (r FutureGetHeadersResult) Receive() ([]appmessage.BlockHeader, error) { - res, err := receiveFuture(r) - if err != nil { - return nil, err - } - - // Unmarshal result as a slice of strings. - var result []string - err = json.Unmarshal(res, &result) - if err != nil { - return nil, err - } - - // Deserialize the []string into []appmessage.BlockHeader. - headers := make([]appmessage.BlockHeader, len(result)) - for i, headerHex := range result { - serialized, err := hex.DecodeString(headerHex) - if err != nil { - return nil, err - } - err = headers[i].Deserialize(bytes.NewReader(serialized)) - if err != nil { - return nil, err - } - } - return headers, nil -} - -// GetTopHeadersAsync returns an instance of a type that can be used to get the result -// of the RPC at some future time by invoking the Receive function on the returned instance. -// -// See GetTopHeaders for the blocking version and more details. -func (c *Client) GetTopHeadersAsync(highHash *daghash.Hash) FutureGetHeadersResult { - var hash *string - if highHash != nil { - hash = pointers.String(highHash.String()) - } - cmd := model.NewGetTopHeadersCmd(hash) - return c.sendCmd(cmd) -} - -// GetTopHeaders sends a getTopHeaders rpc command to the server. -func (c *Client) GetTopHeaders(highHash *daghash.Hash) ([]appmessage.BlockHeader, error) { - return c.GetTopHeadersAsync(highHash).Receive() -} - -// GetHeadersAsync returns an instance of a type that can be used to get the result -// of the RPC at some future time by invoking the Receive function on the returned instance. -// -// See GetHeaders for the blocking version and more details. -func (c *Client) GetHeadersAsync(lowHash, highHash *daghash.Hash) FutureGetHeadersResult { - lowHashStr := "" - if lowHash != nil { - lowHashStr = lowHash.String() - } - highHashStr := "" - if highHash != nil { - highHashStr = highHash.String() - } - cmd := model.NewGetHeadersCmd(lowHashStr, highHashStr) - return c.sendCmd(cmd) -} - -// GetHeaders mimics the appmessage protocol getheaders and headers messages by -// returning all headers in the DAG after the first known block in the -// locators, up until a block hash matches highHash. -func (c *Client) GetHeaders(lowHash, highHash *daghash.Hash) ([]appmessage.BlockHeader, error) { - return c.GetHeadersAsync(lowHash, highHash).Receive() -} - -// FutureSessionResult is a future promise to deliver the result of a -// SessionAsync RPC invocation (or an applicable error). -type FutureSessionResult chan *response - -// Receive waits for the response promised by the future and returns the -// session result. -func (r FutureSessionResult) Receive() (*model.SessionResult, error) { - res, err := receiveFuture(r) - if err != nil { - return nil, err - } - - // Unmarshal result as a session result object. - var session model.SessionResult - err = json.Unmarshal(res, &session) - if err != nil { - return nil, err - } - - return &session, nil -} - -// SessionAsync returns an instance of a type that can be used to get the result -// of the RPC at some future time by invoking the Receive function on the -// returned instance. -// -// See Session for the blocking version and more details. -func (c *Client) SessionAsync() FutureSessionResult { - // Not supported in HTTP POST mode. - if c.config.HTTPPostMode { - return newFutureError(ErrWebsocketsRequired) - } - - cmd := model.NewSessionCmd() - return c.sendCmd(cmd) -} - -// Session returns details regarding a websocket client's current connection. -// -// This RPC requires the client to be running in websocket mode. -func (c *Client) Session() (*model.SessionResult, error) { - return c.SessionAsync().Receive() -} - -// FutureVersionResult is a future promise to deliver the result of a version -// RPC invocation (or an applicable error). -type FutureVersionResult chan *response - -// Receive waits for the response promised by the future and returns the version -// result. -func (r FutureVersionResult) Receive() (map[string]model.VersionResult, - error) { - res, err := receiveFuture(r) - if err != nil { - return nil, err - } - - // Unmarshal result as a version result object. - var vr map[string]model.VersionResult - err = json.Unmarshal(res, &vr) - if err != nil { - return nil, err - } - - return vr, nil -} - -// VersionAsync returns an instance of a type that can be used to get the result -// of the RPC at some future time by invoking the Receive function on the -// returned instance. -// -// See Version for the blocking version and more details. -func (c *Client) VersionAsync() FutureVersionResult { - cmd := model.NewVersionCmd() - return c.sendCmd(cmd) -} - -// Version returns information about the server's JSON-RPC API versions. -func (c *Client) Version() (map[string]model.VersionResult, error) { - return c.VersionAsync().Receive() -} diff --git a/infrastructure/network/rpc/client/notify.go b/infrastructure/network/rpc/client/notify.go deleted file mode 100644 index 5fd60e056..000000000 --- a/infrastructure/network/rpc/client/notify.go +++ /dev/null @@ -1,616 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Copyright (c) 2015-2017 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package client - -import ( - "bytes" - "encoding/hex" - "encoding/json" - "fmt" - "github.com/kaspanet/kaspad/util/mstime" - "github.com/pkg/errors" - - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/daghash" -) - -var ( - // ErrWebsocketsRequired is an error to describe the condition where the - // caller is trying to use a websocket-only feature, such as requesting - // notifications or other websocket requests when the client is - // configured to run in HTTP POST mode. - ErrWebsocketsRequired = errors.New("a websocket connection is required " + - "to use this feature") -) - -// notificationState is used to track the current state of successfully -// registered notification so the state can be automatically re-established on -// reconnect. -type notificationState struct { - notifyBlocks bool - notifyChainChanges bool - notifyNewTx bool - notifyNewTxVerbose bool - notifyNewTxSubnetworkID *string -} - -// Copy returns a deep copy of the receiver. -func (s *notificationState) Copy() *notificationState { - var stateCopy notificationState - stateCopy.notifyBlocks = s.notifyBlocks - stateCopy.notifyChainChanges = s.notifyChainChanges - stateCopy.notifyNewTx = s.notifyNewTx - stateCopy.notifyNewTxVerbose = s.notifyNewTxVerbose - stateCopy.notifyNewTxSubnetworkID = s.notifyNewTxSubnetworkID - - return &stateCopy -} - -// newNotificationState returns a new notification state ready to be populated. -func newNotificationState() *notificationState { - return ¬ificationState{} -} - -// newNilFutureResult returns a new future result channel that already has the -// result waiting on the channel with the reply set to nil. This is useful -// to ignore things such as notifications when the caller didn't specify any -// notification handlers. -func newNilFutureResult() chan *response { - responseChan := make(chan *response, 1) - responseChan <- &response{result: nil, err: nil} - return responseChan -} - -// NotificationHandlers defines callback function pointers to invoke with -// notifications. Since all of the functions are nil by default, all -// notifications are effectively ignored until their handlers are set to a -// concrete callback. -type NotificationHandlers struct { - // OnClientConnected is invoked when the client connects or reconnects - // to the RPC server. This callback is run async with the rest of the - // notification handlers, and is safe for blocking client requests. - OnClientConnected func() - - // OnBlockAdded is invoked when a block is connected to the DAG. - // It will only be invoked if a preceding call to NotifyBlocks has been made - // to register for the notification and the function is non-nil. - // - // NOTE: Deprecated. Use OnFilteredBlockAdded instead. - OnBlockAdded func(hash *daghash.Hash, height int32, t mstime.Time) - - // OnFilteredBlockAdded is invoked when a block is connected to the - // bloackDAG. It will only be invoked if a preceding call to - // NotifyBlocks has been made to register for the notification and the - // function is non-nil. Its parameters differ from OnBlockAdded: it - // receives the block's blueScore, header, and relevant transactions. - OnFilteredBlockAdded func(blueScore uint64, header *appmessage.BlockHeader, - txs []*util.Tx) - - // OnChainChanged is invoked when the selected parent chain of the - // DAG had changed. It will only be invoked if a preceding call to - // NotifyChainChanges has been made to register for the notification and the - // function is non-nil. - OnChainChanged func(removedChainBlockHashes []*daghash.Hash, - addedChainBlocks []*ChainBlock) - - // OnRelevantTxAccepted is invoked when an unmined transaction passes - // the client's transaction filter. - OnRelevantTxAccepted func(transaction []byte) - - // OnTxAccepted is invoked when a transaction is accepted into the - // memory pool. It will only be invoked if a preceding call to - // NotifyNewTransactions with the verbose flag set to false has been - // made to register for the notification and the function is non-nil. - OnTxAccepted func(hash *daghash.Hash, amount util.Amount) - - // OnTxAccepted is invoked when a transaction is accepted into the - // memory pool. It will only be invoked if a preceding call to - // NotifyNewTransactions with the verbose flag set to true has been - // made to register for the notification and the function is non-nil. - OnTxAcceptedVerbose func(txDetails *model.TxRawResult) - - // OnUnknownNotification is invoked when an unrecognized notification - // is received. This typically means the notification handling code - // for this package needs to be updated for a new notification type or - // the caller is using a custom notification this package does not know - // about. - OnUnknownNotification func(method string, params []json.RawMessage) -} - -// handleNotification examines the passed notification type, performs -// conversions to get the raw notification types into higher level types and -// delivers the notification to the appropriate On handler registered with -// the client. -func (c *Client) handleNotification(ntfn *rawNotification) { - // Ignore the notification if the client is not interested in any - // notifications. - if c.ntfnHandlers == nil { - return - } - - switch ntfn.Method { - - // ChainChangedNtfnMethod - case model.ChainChangedNtfnMethod: - // Ignore the notification if the client is not interested in - // it. - if c.ntfnHandlers.OnChainChanged == nil { - return - } - - removedChainBlockHashes, addedChainBlocks, err := parseChainChangedParams(ntfn.Params) - if err != nil { - log.Warnf("Received invalid chain changed "+ - "notification: %s", err) - return - } - - c.ntfnHandlers.OnChainChanged(removedChainBlockHashes, addedChainBlocks) - - // OnFilteredBlockAdded - case model.FilteredBlockAddedNtfnMethod: - // Ignore the notification if the client is not interested in - // it. - if c.ntfnHandlers.OnFilteredBlockAdded == nil { - return - } - - blockBlueScore, blockHeader, transactions, err := - parseFilteredBlockAddedParams(ntfn.Params) - if err != nil { - log.Warnf("Received invalid filtered block "+ - "connected notification: %s", err) - return - } - - c.ntfnHandlers.OnFilteredBlockAdded(blockBlueScore, - blockHeader, transactions) - - // OnRelevantTxAccepted - case model.RelevantTxAcceptedNtfnMethod: - // Ignore the notification if the client is not interested in - // it. - if c.ntfnHandlers.OnRelevantTxAccepted == nil { - return - } - - transaction, err := parseRelevantTxAcceptedParams(ntfn.Params) - if err != nil { - log.Warnf("Received invalid relevanttxaccepted "+ - "notification: %s", err) - return - } - - c.ntfnHandlers.OnRelevantTxAccepted(transaction) - - // OnTxAccepted - case model.TxAcceptedNtfnMethod: - // Ignore the notification if the client is not interested in - // it. - if c.ntfnHandlers.OnTxAccepted == nil { - return - } - - hash, amt, err := parseTxAcceptedNtfnParams(ntfn.Params) - if err != nil { - log.Warnf("Received invalid tx accepted "+ - "notification: %s", err) - return - } - - c.ntfnHandlers.OnTxAccepted(hash, amt) - - // OnTxAcceptedVerbose - case model.TxAcceptedVerboseNtfnMethod: - // Ignore the notification if the client is not interested in - // it. - if c.ntfnHandlers.OnTxAcceptedVerbose == nil { - return - } - - rawTx, err := parseTxAcceptedVerboseNtfnParams(ntfn.Params) - if err != nil { - log.Warnf("Received invalid tx accepted verbose "+ - "notification: %s", err) - return - } - - c.ntfnHandlers.OnTxAcceptedVerbose(rawTx) - - // OnUnknownNotification - default: - if c.ntfnHandlers.OnUnknownNotification == nil { - return - } - - c.ntfnHandlers.OnUnknownNotification(ntfn.Method, ntfn.Params) - } -} - -// wrongNumParams is an error type describing an unparseable JSON-RPC -// notificiation due to an incorrect number of parameters for the -// expected notification type. The value is the number of parameters -// of the invalid notification. -type wrongNumParams int - -// Error satisifies the builtin error interface. -func (e wrongNumParams) Error() string { - return fmt.Sprintf("wrong number of parameters (%d)", e) -} - -// ChainBlock models a block that is part of the selected parent chain. -type ChainBlock struct { - Hash *daghash.Hash - AcceptedBlocks []*AcceptedBlock -} - -// AcceptedBlock models a block that is included in the blues of a selected -// chain block. -type AcceptedBlock struct { - Hash *daghash.Hash - AcceptedTxIDs []*daghash.TxID -} - -func parseChainChangedParams(params []json.RawMessage) (removedChainBlockHashes []*daghash.Hash, addedChainBlocks []*ChainBlock, - err error) { - - if len(params) != 1 { - return nil, nil, wrongNumParams(len(params)) - } - - // Unmarshal first parameter as a raw transaction result object. - var rawParam model.ChainChangedRawParam - err = json.Unmarshal(params[0], &rawParam) - if err != nil { - return nil, nil, err - } - - removedChainBlockHashes = make([]*daghash.Hash, len(rawParam.RemovedChainBlockHashes)) - for i, hashStr := range rawParam.RemovedChainBlockHashes { - hash, err := daghash.NewHashFromStr(hashStr) - if err != nil { - return nil, nil, err - } - removedChainBlockHashes[i] = hash - } - - addedChainBlocks = make([]*ChainBlock, len(rawParam.AddedChainBlocks)) - for i, jsonChainBlock := range rawParam.AddedChainBlocks { - chainBlock := &ChainBlock{ - AcceptedBlocks: make([]*AcceptedBlock, len(jsonChainBlock.AcceptedBlocks)), - } - hash, err := daghash.NewHashFromStr(jsonChainBlock.Hash) - if err != nil { - return nil, nil, err - } - chainBlock.Hash = hash - for j, jsonAcceptedBlock := range jsonChainBlock.AcceptedBlocks { - acceptedBlock := &AcceptedBlock{ - AcceptedTxIDs: make([]*daghash.TxID, len(jsonAcceptedBlock.AcceptedTxIDs)), - } - hash, err := daghash.NewHashFromStr(jsonAcceptedBlock.Hash) - if err != nil { - return nil, nil, err - } - acceptedBlock.Hash = hash - for k, txIDStr := range jsonAcceptedBlock.AcceptedTxIDs { - txID, err := daghash.NewTxIDFromStr(txIDStr) - if err != nil { - return nil, nil, err - } - acceptedBlock.AcceptedTxIDs[k] = txID - } - chainBlock.AcceptedBlocks[j] = acceptedBlock - } - addedChainBlocks[i] = chainBlock - } - - return removedChainBlockHashes, addedChainBlocks, nil -} - -// parseFilteredBlockAddedParams parses out the parameters included in a -// filteredblockadded notification. -func parseFilteredBlockAddedParams(params []json.RawMessage) (uint64, - *appmessage.BlockHeader, []*util.Tx, error) { - - if len(params) < 3 { - return 0, nil, nil, wrongNumParams(len(params)) - } - - // Unmarshal first parameter as an integer. - var blockHeight uint64 - err := json.Unmarshal(params[0], &blockHeight) - if err != nil { - return 0, nil, nil, err - } - - // Unmarshal second parameter as a slice of bytes. - blockHeaderBytes, err := parseHexParam(params[1]) - if err != nil { - return 0, nil, nil, err - } - - // Deserialize block header from slice of bytes. - var blockHeader appmessage.BlockHeader - err = blockHeader.Deserialize(bytes.NewReader(blockHeaderBytes)) - if err != nil { - return 0, nil, nil, err - } - - // Unmarshal third parameter as a slice of hex-encoded strings. - var hexTransactions []string - err = json.Unmarshal(params[2], &hexTransactions) - if err != nil { - return 0, nil, nil, err - } - - // Create slice of transactions from slice of strings by hex-decoding. - transactions := make([]*util.Tx, len(hexTransactions)) - for i, hexTx := range hexTransactions { - transaction, err := hex.DecodeString(hexTx) - if err != nil { - return 0, nil, nil, err - } - - transactions[i], err = util.NewTxFromBytes(transaction) - if err != nil { - return 0, nil, nil, err - } - } - - return blockHeight, &blockHeader, transactions, nil -} - -func parseHexParam(param json.RawMessage) ([]byte, error) { - var s string - err := json.Unmarshal(param, &s) - if err != nil { - return nil, err - } - return hex.DecodeString(s) -} - -// parseRelevantTxAcceptedParams parses out the parameter included in a -// relevanttxaccepted notification. -func parseRelevantTxAcceptedParams(params []json.RawMessage) (transaction []byte, err error) { - if len(params) < 1 { - return nil, wrongNumParams(len(params)) - } - - return parseHexParam(params[0]) -} - -// parseTxAcceptedNtfnParams parses out the transaction hash and total amount -// from the parameters of a txaccepted notification. -func parseTxAcceptedNtfnParams(params []json.RawMessage) (*daghash.Hash, - util.Amount, error) { - - if len(params) != 2 { - return nil, 0, wrongNumParams(len(params)) - } - - // Unmarshal first parameter as a string. - var txHashStr string - err := json.Unmarshal(params[0], &txHashStr) - if err != nil { - return nil, 0, err - } - - // Unmarshal second parameter as a floating point number. - var famt float64 - err = json.Unmarshal(params[1], &famt) - if err != nil { - return nil, 0, err - } - - // Bounds check amount. - amt, err := util.NewAmount(famt) - if err != nil { - return nil, 0, err - } - - // Decode string encoding of transaction sha. - txHash, err := daghash.NewHashFromStr(txHashStr) - if err != nil { - return nil, 0, err - } - - return txHash, amt, nil -} - -// parseTxAcceptedVerboseNtfnParams parses out details about a raw transaction -// from the parameters of a txacceptedverbose notification. -func parseTxAcceptedVerboseNtfnParams(params []json.RawMessage) (*model.TxRawResult, - error) { - - if len(params) != 1 { - return nil, wrongNumParams(len(params)) - } - - // Unmarshal first parameter as a raw transaction result object. - var rawTx model.TxRawResult - err := json.Unmarshal(params[0], &rawTx) - if err != nil { - return nil, err - } - - return &rawTx, nil -} - -// FutureNotifyBlocksResult is a future promise to deliver the result of a -// NotifyBlocksAsync RPC invocation (or an applicable error). -type FutureNotifyBlocksResult chan *response - -// Receive waits for the response promised by the future and returns an error -// if the registration was not successful. -func (r FutureNotifyBlocksResult) Receive() error { - _, err := receiveFuture(r) - return err -} - -// NotifyBlocksAsync returns an instance of a type that can be used to get the -// result of the RPC at some future time by invoking the Receive function on -// the returned instance. -// -// See NotifyBlocks for the blocking version and more details. -func (c *Client) NotifyBlocksAsync() FutureNotifyBlocksResult { - // Not supported in HTTP POST mode. - if c.config.HTTPPostMode { - return newFutureError(ErrWebsocketsRequired) - } - - // Ignore the notification if the client is not interested in - // notifications. - if c.ntfnHandlers == nil { - return newNilFutureResult() - } - - cmd := model.NewNotifyBlocksCmd() - return c.sendCmd(cmd) -} - -// NotifyBlocks registers the client to receive notifications when blocks are -// connected to the DAG. The notifications are delivered to the notification -// handlers associated with the client. Calling this function has no effect -// if there are no notification handlers and will result in an error if the -// client is configured to run in HTTP POST mode. -// -// The notifications delivered as a result of this call will be via OnBlockAdded -func (c *Client) NotifyBlocks() error { - return c.NotifyBlocksAsync().Receive() -} - -// FutureNotifyChainChangesResult is a future promise to deliver the result of a -// NotifyChainChangesAsync RPC invocation (or an applicable error). -type FutureNotifyChainChangesResult chan *response - -// Receive waits for the response promised by the future and returns an error -// if the registration was not successful. -func (r FutureNotifyChainChangesResult) Receive() error { - _, err := receiveFuture(r) - return err -} - -// NotifyChainChangesAsync returns an instance of a type that can be used to get the -// result of the RPC at some future time by invoking the Receive function on -// the returned instance. -// -// See NotifyChainChanges for the blocking version and more details. -func (c *Client) NotifyChainChangesAsync() FutureNotifyBlocksResult { - // Not supported in HTTP POST mode. - if c.config.HTTPPostMode { - return newFutureError(ErrWebsocketsRequired) - } - - // Ignore the notification if the client is not interested in - // notifications. - if c.ntfnHandlers == nil { - return newNilFutureResult() - } - - cmd := model.NewNotifyChainChangesCmd() - return c.sendCmd(cmd) -} - -// NotifyChainChanges registers the client to receive notifications when the -// selected parent chain changes. The notifications are delivered to the -// notification handlers associated with the client. Calling this function has -// no effect if there are no notification handlers and will result in an error -// if the client is configured to run in HTTP POST mode. -// -// The notifications delivered as a result of this call will be via OnBlockAdded -func (c *Client) NotifyChainChanges() error { - return c.NotifyChainChangesAsync().Receive() -} - -// FutureNotifyNewTransactionsResult is a future promise to deliver the result -// of a NotifyNewTransactionsAsync RPC invocation (or an applicable error). -type FutureNotifyNewTransactionsResult chan *response - -// Receive waits for the response promised by the future and returns an error -// if the registration was not successful. -func (r FutureNotifyNewTransactionsResult) Receive() error { - _, err := receiveFuture(r) - return err -} - -// NotifyNewTransactionsAsync returns an instance of a type that can be used to -// get the result of the RPC at some future time by invoking the Receive -// function on the returned instance. -// -// See NotifyNewTransactionsAsync for the blocking version and more details. -func (c *Client) NotifyNewTransactionsAsync(verbose bool, subnetworkID *string) FutureNotifyNewTransactionsResult { - // Not supported in HTTP POST mode. - if c.config.HTTPPostMode { - return newFutureError(ErrWebsocketsRequired) - } - - // Ignore the notification if the client is not interested in - // notifications. - if c.ntfnHandlers == nil { - return newNilFutureResult() - } - - cmd := model.NewNotifyNewTransactionsCmd(&verbose, subnetworkID) - return c.sendCmd(cmd) -} - -// NotifyNewTransactions registers the client to receive notifications every -// time a new transaction is accepted to the memory pool. The notifications are -// delivered to the notification handlers associated with the client. Calling -// this function has no effect if there are no notification handlers and will -// result in an error if the client is configured to run in HTTP POST mode. -// -// The notifications delivered as a result of this call will be via one of -// OnTxAccepted (when verbose is false) or OnTxAcceptedVerbose (when verbose is -// true). -func (c *Client) NotifyNewTransactions(verbose bool, subnetworkID *string) error { - return c.NotifyNewTransactionsAsync(verbose, subnetworkID).Receive() -} - -// FutureLoadTxFilterResult is a future promise to deliver the result -// of a LoadTxFilterAsync RPC invocation (or an applicable error). -type FutureLoadTxFilterResult chan *response - -// Receive waits for the response promised by the future and returns an error -// if the registration was not successful. -func (r FutureLoadTxFilterResult) Receive() error { - _, err := receiveFuture(r) - return err -} - -// LoadTxFilterAsync returns an instance of a type that can be used to -// get the result of the RPC at some future time by invoking the Receive -// function on the returned instance. -// -// See LoadTxFilter for the blocking version and more details. -func (c *Client) LoadTxFilterAsync(reload bool, addresses []util.Address, - outpoints []appmessage.Outpoint) FutureLoadTxFilterResult { - - addrStrs := make([]string, len(addresses)) - for i, a := range addresses { - addrStrs[i] = a.EncodeAddress() - } - outpointObjects := make([]model.Outpoint, len(outpoints)) - for i := range outpoints { - outpointObjects[i] = model.Outpoint{ - TxID: outpoints[i].TxID.String(), - Index: outpoints[i].Index, - } - } - - cmd := model.NewLoadTxFilterCmd(reload, addrStrs, outpointObjects) - return c.sendCmd(cmd) -} - -// LoadTxFilter loads, reloads, or adds data to a websocket client's transaction -// filter. The filter is consistently updated based on inspected transactions -// during mempool acceptance, and for block acceptance. -func (c *Client) LoadTxFilter(reload bool, addresses []util.Address, outpoints []appmessage.Outpoint) error { - return c.LoadTxFilterAsync(reload, addresses, outpoints).Receive() -} diff --git a/infrastructure/network/rpc/client/rawrequest.go b/infrastructure/network/rpc/client/rawrequest.go deleted file mode 100644 index f18021abc..000000000 --- a/infrastructure/network/rpc/client/rawrequest.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package client - -import ( - "encoding/json" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/pkg/errors" -) - -// FutureRawResult is a future promise to deliver the result of a RawRequest RPC -// invocation (or an applicable error). -type FutureRawResult chan *response - -// Receive waits for the response promised by the future and returns the raw -// response, or an error if the request was unsuccessful. -func (r FutureRawResult) Receive() (json.RawMessage, error) { - return receiveFuture(r) -} - -// RawRequestAsync returns an instance of a type that can be used to get the -// result of a custom RPC request at some future time by invoking the Receive -// function on the returned instance. -// -// See RawRequest for the blocking version and more details. -func (c *Client) RawRequestAsync(method string, params []json.RawMessage) FutureRawResult { - // Method may not be empty. - if method == "" { - return newFutureError(errors.New("no method")) - } - - // Marshal parameters as "[]" instead of "null" when no parameters - // are passed. - if params == nil { - params = []json.RawMessage{} - } - - // Create a raw JSON-RPC request using the provided method and params - // and marshal it. This is done rather than using the sendCmd function - // since that relies on marshalling registered jsonrpc commands rather - // than custom commands. - id := c.NextID() - rawRequest := &model.Request{ - JSONRPC: "1.0", - ID: id, - Method: method, - Params: params, - } - marshalledJSON, err := json.Marshal(rawRequest) - if err != nil { - return newFutureError(err) - } - - // Generate the request. - jReqData := &jsonRequestData{ - id: id, - method: method, - cmd: nil, - marshalledJSON: marshalledJSON, - } - - // Send the request and return its response channel - return c.sendRequest(jReqData) -} - -// RawRequest allows the caller to send a raw or custom request to the server. -// This method may be used to send and receive requests and responses for -// requests that are not handled by this client package, or to proxy partially -// unmarshaled requests to another JSON-RPC server if a request cannot be -// handled directly. -func (c *Client) RawRequest(method string, params []json.RawMessage) (json.RawMessage, error) { - return c.RawRequestAsync(method, params).Receive() -} diff --git a/infrastructure/network/rpc/client/rawtransactions.go b/infrastructure/network/rpc/client/rawtransactions.go deleted file mode 100644 index 779efff7b..000000000 --- a/infrastructure/network/rpc/client/rawtransactions.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package client - -import ( - "bytes" - "encoding/hex" - "encoding/json" - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/daghash" -) - -// FutureSendRawTransactionResult is a future promise to deliver the result -// of a SendRawTransactionAsync RPC invocation (or an applicable error). -type FutureSendRawTransactionResult chan *response - -// Receive waits for the response promised by the future and returns the result -// of submitting the encoded transaction to the server which then relays it to -// the network. -func (r FutureSendRawTransactionResult) Receive() (*daghash.TxID, error) { - res, err := receiveFuture(r) - if err != nil { - return nil, err - } - - // Unmarshal result as a string. - var txIDStr string - err = json.Unmarshal(res, &txIDStr) - if err != nil { - return nil, err - } - - return daghash.NewTxIDFromStr(txIDStr) -} - -// SendRawTransactionAsync returns an instance of a type that can be used to get -// the result of the RPC at some future time by invoking the Receive function on -// the returned instance. -// -// See SendRawTransaction for the blocking version and more details. -func (c *Client) SendRawTransactionAsync(tx *appmessage.MsgTx, allowHighFees bool) FutureSendRawTransactionResult { - txHex := "" - if tx != nil { - // Serialize the transaction and convert to hex string. - buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) - if err := tx.Serialize(buf); err != nil { - return newFutureError(err) - } - txHex = hex.EncodeToString(buf.Bytes()) - } - - cmd := model.NewSendRawTransactionCmd(txHex, &allowHighFees) - return c.sendCmd(cmd) -} - -// SendRawTransaction submits the encoded transaction to the server which will -// then relay it to the network. -func (c *Client) SendRawTransaction(tx *appmessage.MsgTx, allowHighFees bool) (*daghash.TxID, error) { - return c.SendRawTransactionAsync(tx, allowHighFees).Receive() -} diff --git a/infrastructure/network/rpc/common.go b/infrastructure/network/rpc/common.go deleted file mode 100644 index 6489a9c8f..000000000 --- a/infrastructure/network/rpc/common.go +++ /dev/null @@ -1,358 +0,0 @@ -package rpc - -import ( - "bytes" - "encoding/hex" - "fmt" - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/domain/dagconfig" - "github.com/kaspanet/kaspad/domain/txscript" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/daghash" - "github.com/kaspanet/kaspad/util/pointers" - "math/big" - "strconv" -) - -var ( - // ErrRPCUnimplemented is an error returned to RPC clients when the - // provided command is recognized, but not implemented. - ErrRPCUnimplemented = &model.RPCError{ - Code: model.ErrRPCUnimplemented, - Message: "Command unimplemented", - } -) - -// internalRPCError is a convenience function to convert an internal error to -// an RPC error with the appropriate code set. It also logs the error to the -// RPC server subsystem since internal errors really should not occur. The -// context parameter is only used in the log message and may be empty if it's -// not needed. -func internalRPCError(errStr, context string) *model.RPCError { - logStr := errStr - if context != "" { - logStr = context + ": " + errStr - } - log.Error(logStr) - return model.NewRPCError(model.ErrRPCInternal.Code, errStr) -} - -// rpcDecodeHexError is a convenience function for returning a nicely formatted -// RPC error which indicates the provided hex string failed to decode. -func rpcDecodeHexError(gotHex string) *model.RPCError { - return model.NewRPCError(model.ErrRPCDecodeHexString, - fmt.Sprintf("Argument must be hexadecimal string (not %q)", - gotHex)) -} - -// rpcNoTxInfoError is a convenience function for returning a nicely formatted -// RPC error which indicates there is no information available for the provided -// transaction hash. -func rpcNoTxInfoError(txID *daghash.TxID) *model.RPCError { - return model.NewRPCError(model.ErrRPCNoTxInfo, - fmt.Sprintf("No information available about transaction %s", - txID)) -} - -// msgTxToHex serializes a transaction using the latest protocol version and -// returns a hex-encoded string of the result. -func msgTxToHex(msgTx *appmessage.MsgTx) (string, error) { - var buf bytes.Buffer - if err := msgTx.KaspaEncode(&buf, maxProtocolVersion); err != nil { - context := fmt.Sprintf("Failed to encode msg of type %T", msgTx) - return "", internalRPCError(err.Error(), context) - } - - return hex.EncodeToString(buf.Bytes()), nil -} - -// createVinList returns a slice of JSON objects for the inputs of the passed -// transaction. -func createVinList(mtx *appmessage.MsgTx) []model.Vin { - vinList := make([]model.Vin, len(mtx.TxIn)) - for i, txIn := range mtx.TxIn { - // The disassembled string will contain [error] inline - // if the script doesn't fully parse, so ignore the - // error here. - disbuf, _ := txscript.DisasmString(txIn.SignatureScript) - - vinEntry := &vinList[i] - vinEntry.TxID = txIn.PreviousOutpoint.TxID.String() - vinEntry.Vout = txIn.PreviousOutpoint.Index - vinEntry.Sequence = txIn.Sequence - vinEntry.ScriptSig = &model.ScriptSig{ - Asm: disbuf, - Hex: hex.EncodeToString(txIn.SignatureScript), - } - } - - return vinList -} - -// createVoutList returns a slice of JSON objects for the outputs of the passed -// transaction. -func createVoutList(mtx *appmessage.MsgTx, dagParams *dagconfig.Params, filterAddrMap map[string]struct{}) []model.Vout { - voutList := make([]model.Vout, 0, len(mtx.TxOut)) - for i, v := range mtx.TxOut { - // The disassembled string will contain [error] inline if the - // script doesn't fully parse, so ignore the error here. - disbuf, _ := txscript.DisasmString(v.ScriptPubKey) - - // Ignore the error here since an error means the script - // couldn't parse and there is no additional information about - // it anyways. - scriptClass, addr, _ := txscript.ExtractScriptPubKeyAddress( - v.ScriptPubKey, dagParams) - - // Encode the addresses while checking if the address passes the - // filter when needed. - passesFilter := len(filterAddrMap) == 0 - var encodedAddr *string - if addr != nil { - encodedAddr = pointers.String(addr.EncodeAddress()) - - // If the filter doesn't already pass, make it pass if - // the address exists in the filter. - if _, exists := filterAddrMap[*encodedAddr]; exists { - passesFilter = true - } - } - - if !passesFilter { - continue - } - - var vout model.Vout - vout.N = uint32(i) - vout.Value = v.Value - vout.ScriptPubKey.Address = encodedAddr - vout.ScriptPubKey.Asm = disbuf - vout.ScriptPubKey.Hex = hex.EncodeToString(v.ScriptPubKey) - vout.ScriptPubKey.Type = scriptClass.String() - - voutList = append(voutList, vout) - } - - return voutList -} - -// createTxRawResult converts the passed transaction and associated parameters -// to a raw transaction JSON object. -func createTxRawResult(dagParams *dagconfig.Params, mtx *appmessage.MsgTx, - txID string, blkHeader *appmessage.BlockHeader, blkHash string, - acceptingBlock *daghash.Hash, isInMempool bool) (*model.TxRawResult, error) { - - mtxHex, err := msgTxToHex(mtx) - if err != nil { - return nil, err - } - - var payloadHash string - if mtx.PayloadHash != nil { - payloadHash = mtx.PayloadHash.String() - } - - txReply := &model.TxRawResult{ - Hex: mtxHex, - TxID: txID, - Hash: mtx.TxHash().String(), - Size: int32(mtx.SerializeSize()), - Vin: createVinList(mtx), - Vout: createVoutList(mtx, dagParams, nil), - Version: mtx.Version, - LockTime: mtx.LockTime, - Subnetwork: mtx.SubnetworkID.String(), - Gas: mtx.Gas, - PayloadHash: payloadHash, - Payload: hex.EncodeToString(mtx.Payload), - } - - if blkHeader != nil { - txReply.Time = uint64(blkHeader.Timestamp.UnixMilliseconds()) - txReply.BlockTime = uint64(blkHeader.Timestamp.UnixMilliseconds()) - txReply.BlockHash = blkHash - } - - txReply.IsInMempool = isInMempool - if acceptingBlock != nil { - txReply.AcceptedBy = pointers.String(acceptingBlock.String()) - } - - return txReply, nil -} - -// getDifficultyRatio returns the proof-of-work difficulty as a multiple of the -// minimum difficulty using the passed bits field from the header of a block. -func getDifficultyRatio(bits uint32, params *dagconfig.Params) float64 { - // The minimum difficulty is the max possible proof-of-work limit bits - // converted back to a number. Note this is not the same as the proof of - // work limit directly because the block difficulty is encoded in a block - // with the compact form which loses precision. - target := util.CompactToBig(bits) - - difficulty := new(big.Rat).SetFrac(params.PowMax, target) - outString := difficulty.FloatString(8) - diff, err := strconv.ParseFloat(outString, 64) - if err != nil { - log.Errorf("Cannot get difficulty: %s", err) - return 0 - } - return diff -} - -// buildGetBlockVerboseResult takes a block and convert it to model.GetBlockVerboseResult -// -// This function MUST be called with the DAG state lock held (for reads). -func buildGetBlockVerboseResult(s *Server, block *util.Block, isVerboseTx bool) (*model.GetBlockVerboseResult, error) { - hash := block.Hash() - params := s.dag.Params - blockHeader := block.MsgBlock().Header - - blockBlueScore, err := s.dag.BlueScoreByBlockHash(hash) - if err != nil { - context := "Could not get block blue score" - return nil, internalRPCError(err.Error(), context) - } - - // Get the hashes for the next blocks unless there are none. - childHashes, err := s.dag.ChildHashesByHash(hash) - if err != nil { - context := "No next block" - return nil, internalRPCError(err.Error(), context) - } - - blockConfirmations, err := s.dag.BlockConfirmationsByHashNoLock(hash) - if err != nil { - context := "Could not get block confirmations" - return nil, internalRPCError(err.Error(), context) - } - - selectedParentHash, err := s.dag.SelectedParentHash(hash) - if err != nil { - context := "Could not get block selected parent" - return nil, internalRPCError(err.Error(), context) - } - selectedParentHashStr := "" - if selectedParentHash != nil { - selectedParentHashStr = selectedParentHash.String() - } - - isChainBlock, err := s.dag.IsInSelectedParentChain(hash) - if err != nil { - context := "Could not get whether block is in the selected parent chain" - return nil, internalRPCError(err.Error(), context) - } - - acceptedBlockHashes, err := s.dag.BluesByBlockHash(hash) - if err != nil { - context := fmt.Sprintf("Could not get block accepted blocks for block %s", hash) - return nil, internalRPCError(err.Error(), context) - } - - result := &model.GetBlockVerboseResult{ - Hash: hash.String(), - Version: blockHeader.Version, - VersionHex: fmt.Sprintf("%08x", blockHeader.Version), - HashMerkleRoot: blockHeader.HashMerkleRoot.String(), - AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot.String(), - UTXOCommitment: blockHeader.UTXOCommitment.String(), - ParentHashes: daghash.Strings(blockHeader.ParentHashes), - SelectedParentHash: selectedParentHashStr, - Nonce: blockHeader.Nonce, - Time: blockHeader.Timestamp.UnixMilliseconds(), - Confirmations: blockConfirmations, - BlueScore: blockBlueScore, - IsChainBlock: isChainBlock, - Size: int32(block.MsgBlock().SerializeSize()), - Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), - Difficulty: getDifficultyRatio(blockHeader.Bits, params), - ChildHashes: daghash.Strings(childHashes), - AcceptedBlockHashes: daghash.Strings(acceptedBlockHashes), - } - - if isVerboseTx { - transactions := block.Transactions() - txNames := make([]string, len(transactions)) - for i, tx := range transactions { - txNames[i] = tx.ID().String() - } - - result.Tx = txNames - } else { - txns := block.Transactions() - rawTxns := make([]model.TxRawResult, len(txns)) - for i, tx := range txns { - rawTxn, err := createTxRawResult(params, tx.MsgTx(), tx.ID().String(), - &blockHeader, hash.String(), nil, false) - if err != nil { - return nil, err - } - rawTxns[i] = *rawTxn - } - result.RawTx = rawTxns - } - - return result, nil -} - -func collectChainBlocks(s *Server, hashes []*daghash.Hash) ([]model.ChainBlock, error) { - chainBlocks := make([]model.ChainBlock, 0, len(hashes)) - for _, hash := range hashes { - acceptanceData, err := s.acceptanceIndex.TxsAcceptanceData(hash) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInternal.Code, - Message: fmt.Sprintf("could not retrieve acceptance data for block %s", hash), - } - } - - acceptedBlocks := make([]model.AcceptedBlock, 0, len(acceptanceData)) - for _, blockAcceptanceData := range acceptanceData { - acceptedTxIds := make([]string, 0, len(blockAcceptanceData.TxAcceptanceData)) - for _, txAcceptanceData := range blockAcceptanceData.TxAcceptanceData { - if txAcceptanceData.IsAccepted { - acceptedTxIds = append(acceptedTxIds, txAcceptanceData.Tx.ID().String()) - } - } - acceptedBlock := model.AcceptedBlock{ - Hash: blockAcceptanceData.BlockHash.String(), - AcceptedTxIDs: acceptedTxIds, - } - acceptedBlocks = append(acceptedBlocks, acceptedBlock) - } - - chainBlock := model.ChainBlock{ - Hash: hash.String(), - AcceptedBlocks: acceptedBlocks, - } - chainBlocks = append(chainBlocks, chainBlock) - } - return chainBlocks, nil -} - -// hashesToGetBlockVerboseResults takes block hashes and returns their -// correspondent block verbose. -// -// This function MUST be called with the DAG state lock held (for reads). -func hashesToGetBlockVerboseResults(s *Server, hashes []*daghash.Hash) ([]model.GetBlockVerboseResult, error) { - getBlockVerboseResults := make([]model.GetBlockVerboseResult, 0, len(hashes)) - for _, blockHash := range hashes { - block, err := s.dag.BlockByHash(blockHash) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInternal.Code, - Message: fmt.Sprintf("could not retrieve block %s.", blockHash), - } - } - getBlockVerboseResult, err := buildGetBlockVerboseResult(s, block, false) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInternal.Code, - Message: fmt.Sprintf("could not build getBlockVerboseResult for block %s: %s", blockHash, err), - } - } - getBlockVerboseResults = append(getBlockVerboseResults, *getBlockVerboseResult) - } - return getBlockVerboseResults, nil -} diff --git a/infrastructure/network/rpc/handle_connect.go b/infrastructure/network/rpc/handle_connect.go deleted file mode 100644 index 50b1b27b3..000000000 --- a/infrastructure/network/rpc/handle_connect.go +++ /dev/null @@ -1,24 +0,0 @@ -package rpc - -import ( - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/network" -) - -// handleConnect handles connect commands. -func handleConnect(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.ConnectCmd) - - isPermanent := c.IsPermanent != nil && *c.IsPermanent - - address, err := network.NormalizeAddress(c.Address, s.dag.Params.DefaultPort) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidParameter, - Message: err.Error(), - } - } - - s.connectionManager.AddConnectionRequest(address, isPermanent) - return nil, nil -} diff --git a/infrastructure/network/rpc/handle_debug_level.go b/infrastructure/network/rpc/handle_debug_level.go deleted file mode 100644 index 52e41c751..000000000 --- a/infrastructure/network/rpc/handle_debug_level.go +++ /dev/null @@ -1,28 +0,0 @@ -package rpc - -import ( - "fmt" - "github.com/kaspanet/kaspad/infrastructure/logger" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// handleDebugLevel handles debugLevel commands. -func handleDebugLevel(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.DebugLevelCmd) - - // Special show command to list supported subsystems. - if c.LevelSpec == "show" { - return fmt.Sprintf("Supported subsystems %s", - logger.SupportedSubsystems()), nil - } - - err := logger.ParseAndSetDebugLevels(c.LevelSpec) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidParams.Code, - Message: err.Error(), - } - } - - return "Done.", nil -} diff --git a/infrastructure/network/rpc/handle_disconnect.go b/infrastructure/network/rpc/handle_disconnect.go deleted file mode 100644 index f6dfe8fe0..000000000 --- a/infrastructure/network/rpc/handle_disconnect.go +++ /dev/null @@ -1,22 +0,0 @@ -package rpc - -import ( - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/network" -) - -// handleDisconnect handles disconnect commands. -func handleDisconnect(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.DisconnectCmd) - - address, err := network.NormalizeAddress(c.Address, s.dag.Params.DefaultPort) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidParameter, - Message: err.Error(), - } - } - - s.connectionManager.RemoveConnection(address) - return nil, nil -} diff --git a/infrastructure/network/rpc/handle_get_block.go b/infrastructure/network/rpc/handle_get_block.go deleted file mode 100644 index 475c27167..000000000 --- a/infrastructure/network/rpc/handle_get_block.go +++ /dev/null @@ -1,108 +0,0 @@ -package rpc - -import ( - "bufio" - "bytes" - "encoding/hex" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/daghash" - "github.com/kaspanet/kaspad/util/subnetworkid" -) - -// handleGetBlock implements the getBlock command. -func handleGetBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.GetBlockCmd) - - // Load the raw block bytes from the database. - hash, err := daghash.NewHashFromStr(c.Hash) - if err != nil { - return nil, rpcDecodeHexError(c.Hash) - } - - // Return an appropriate error if the block is known to be invalid - if s.dag.IsKnownInvalid(hash) { - return nil, &model.RPCError{ - Code: model.ErrRPCBlockInvalid, - Message: "Block is known to be invalid", - } - } - - // Return an appropriate error if the block is an orphan - if s.dag.IsKnownOrphan(hash) { - return nil, &model.RPCError{ - Code: model.ErrRPCOrphanBlock, - Message: "Block is an orphan", - } - } - - block, err := s.dag.BlockByHash(hash) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCBlockNotFound, - Message: "Block not found", - } - } - blockBytes, err := block.Bytes() - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCBlockInvalid, - Message: "Cannot serialize block", - } - } - - // Handle partial blocks - if c.Subnetwork != nil { - requestSubnetworkID, err := subnetworkid.NewFromStr(*c.Subnetwork) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidRequest.Code, - Message: "invalid subnetwork string", - } - } - nodeSubnetworkID := s.cfg.SubnetworkID - - if requestSubnetworkID != nil { - if nodeSubnetworkID != nil { - if !nodeSubnetworkID.IsEqual(requestSubnetworkID) { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidRequest.Code, - Message: "subnetwork does not match this partial node", - } - } - // nothing to do - partial node stores partial blocks - } else { - // Deserialize the block. - msgBlock := block.MsgBlock() - msgBlock.ConvertToPartial(requestSubnetworkID) - var b bytes.Buffer - msgBlock.Serialize(bufio.NewWriter(&b)) - blockBytes = b.Bytes() - } - } - } - - // When the verbose flag is set to false, simply return the serialized block - // as a hex-encoded string (verbose flag is on by default). - if c.Verbose != nil && !*c.Verbose { - return hex.EncodeToString(blockBytes), nil - } - - // The verbose flag is set, so generate the JSON object and return it. - - // Deserialize the block. - block, err = util.NewBlockFromBytes(blockBytes) - if err != nil { - context := "Failed to deserialize block" - return nil, internalRPCError(err.Error(), context) - } - - s.dag.RLock() - defer s.dag.RUnlock() - blockReply, err := buildGetBlockVerboseResult(s, block, c.VerboseTx == nil || !*c.VerboseTx) - if err != nil { - return nil, err - } - return blockReply, nil -} diff --git a/infrastructure/network/rpc/handle_get_block_count.go b/infrastructure/network/rpc/handle_get_block_count.go deleted file mode 100644 index f9ca4a1ee..000000000 --- a/infrastructure/network/rpc/handle_get_block_count.go +++ /dev/null @@ -1,6 +0,0 @@ -package rpc - -// handleGetBlockCount implements the getBlockCount command. -func handleGetBlockCount(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - return s.dag.BlockCount(), nil -} diff --git a/infrastructure/network/rpc/handle_get_block_dag_info.go b/infrastructure/network/rpc/handle_get_block_dag_info.go deleted file mode 100644 index 855a036e8..000000000 --- a/infrastructure/network/rpc/handle_get_block_dag_info.go +++ /dev/null @@ -1,27 +0,0 @@ -package rpc - -import ( - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/daghash" -) - -// handleGetBlockDAGInfo implements the getBlockDagInfo command. -func handleGetBlockDAGInfo(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - // Obtain a snapshot of the current best known DAG state. We'll - // populate the response to this call primarily from this snapshot. - params := s.dag.Params - dag := s.dag - - dagInfo := &model.GetBlockDAGInfoResult{ - DAG: params.Name, - Blocks: dag.BlockCount(), - Headers: dag.BlockCount(), - TipHashes: daghash.Strings(dag.TipHashes()), - Difficulty: getDifficultyRatio(dag.CurrentBits(), params), - MedianTime: dag.CalcPastMedianTime().UnixMilliseconds(), - Pruned: false, - Bip9SoftForks: make(map[string]*model.Bip9SoftForkDescription), - } - - return dagInfo, nil -} diff --git a/infrastructure/network/rpc/handle_get_block_header.go b/infrastructure/network/rpc/handle_get_block_header.go deleted file mode 100644 index 2eaf1362a..000000000 --- a/infrastructure/network/rpc/handle_get_block_header.go +++ /dev/null @@ -1,80 +0,0 @@ -package rpc - -import ( - "bytes" - "encoding/hex" - "fmt" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/daghash" - "strconv" -) - -// handleGetBlockHeader implements the getBlockHeader command. -func handleGetBlockHeader(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.GetBlockHeaderCmd) - - // Fetch the header from DAG. - hash, err := daghash.NewHashFromStr(c.Hash) - if err != nil { - return nil, rpcDecodeHexError(c.Hash) - } - blockHeader, err := s.dag.HeaderByHash(hash) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCBlockNotFound, - Message: "Block not found", - } - } - - // When the verbose flag isn't set, simply return the serialized block - // header as a hex-encoded string. - if c.Verbose != nil && !*c.Verbose { - var headerBuf bytes.Buffer - err := blockHeader.Serialize(&headerBuf) - if err != nil { - context := "Failed to serialize block header" - return nil, internalRPCError(err.Error(), context) - } - return hex.EncodeToString(headerBuf.Bytes()), nil - } - - // The verbose flag is set, so generate the JSON object and return it. - - // Get the hashes for the next blocks unless there are none. - childHashes, err := s.dag.ChildHashesByHash(hash) - if err != nil { - context := "No next block" - return nil, internalRPCError(err.Error(), context) - } - childHashStrings := daghash.Strings(childHashes) - - blockConfirmations, err := s.dag.BlockConfirmationsByHash(hash) - if err != nil { - context := "Could not get block confirmations" - return nil, internalRPCError(err.Error(), context) - } - - selectedParentHash, err := s.dag.SelectedParentHash(hash) - if err != nil { - context := "Could not get block selected parent" - return nil, internalRPCError(err.Error(), context) - } - - params := s.dag.Params - blockHeaderReply := model.GetBlockHeaderVerboseResult{ - Hash: c.Hash, - Confirmations: blockConfirmations, - Version: blockHeader.Version, - VersionHex: fmt.Sprintf("%08x", blockHeader.Version), - HashMerkleRoot: blockHeader.HashMerkleRoot.String(), - AcceptedIDMerkleRoot: blockHeader.AcceptedIDMerkleRoot.String(), - ChildHashes: childHashStrings, - ParentHashes: daghash.Strings(blockHeader.ParentHashes), - SelectedParentHash: selectedParentHash.String(), - Nonce: blockHeader.Nonce, - Time: blockHeader.Timestamp.UnixMilliseconds(), - Bits: strconv.FormatInt(int64(blockHeader.Bits), 16), - Difficulty: getDifficultyRatio(blockHeader.Bits, params), - } - return blockHeaderReply, nil -} diff --git a/infrastructure/network/rpc/handle_get_block_template.go b/infrastructure/network/rpc/handle_get_block_template.go deleted file mode 100644 index f2b2080e9..000000000 --- a/infrastructure/network/rpc/handle_get_block_template.go +++ /dev/null @@ -1,757 +0,0 @@ -package rpc - -import ( - "bytes" - "encoding/hex" - "fmt" - "strconv" - "strings" - "sync" - "time" - - "github.com/kaspanet/kaspad/util/mstime" - - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/domain/blockdag" - "github.com/kaspanet/kaspad/domain/mining" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/daghash" - "github.com/kaspanet/kaspad/util/random" - "github.com/pkg/errors" -) - -const ( - // gbtNonceRange is two 64-bit big-endian hexadecimal integers which - // represent the valid ranges of nonces returned by the getBlockTemplate - // RPC. - gbtNonceRange = "000000000000ffffffffffff" - - // gbtRegenerateSeconds is the number of seconds that must pass before - // a new template is generated when the parent block hashes has not - // changed and there have been changes to the available transactions - // in the memory pool. - gbtRegenerateSeconds = 60 -) - -var ( - // gbtMutableFields are the manipulations the server allows to be made - // to block templates generated by the getBlockTemplate RPC. It is - // declared here to avoid the overhead of creating the slice on every - // invocation for constant data. - gbtMutableFields = []string{ - "time", "transactions/add", "parentblock", "coinbase/append", - } - - // gbtCapabilities describes additional capabilities returned with a - // block template generated by the getBlockTemplate RPC. It is - // declared here to avoid the overhead of creating the slice on every - // invocation for constant data. - gbtCapabilities = []string{"proposal"} -) - -// gbtWorkState houses state that is used in between multiple RPC invocations to -// getBlockTemplate. -type gbtWorkState struct { - sync.Mutex - lastTxUpdate mstime.Time - lastGenerated mstime.Time - tipHashes []*daghash.Hash - minTimestamp mstime.Time - template *mining.BlockTemplate - notifyMap map[string]map[int64]chan struct{} - payAddress util.Address -} - -// newGbtWorkState returns a new instance of a gbtWorkState with all internal -// fields initialized and ready to use. -func newGbtWorkState() *gbtWorkState { - return &gbtWorkState{ - notifyMap: make(map[string]map[int64]chan struct{}), - } -} - -// handleGetBlockTemplate implements the getBlockTemplate command. -func handleGetBlockTemplate(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.GetBlockTemplateCmd) - request := c.Request - - // Set the default mode and override it if supplied. - mode := "template" - if request != nil && request.Mode != "" { - mode = request.Mode - } - - switch mode { - case "template": - return handleGetBlockTemplateRequest(s, request, closeChan) - case "proposal": - return handleGetBlockTemplateProposal(s, request) - } - - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidParameter, - Message: "Invalid mode", - } -} - -// handleGetBlockTemplateRequest is a helper for handleGetBlockTemplate which -// deals with generating and returning block templates to the caller. It -// handles both long poll requests as specified by BIP 0022 as well as regular -// requests. -func handleGetBlockTemplateRequest(s *Server, request *model.TemplateRequest, closeChan <-chan struct{}) (interface{}, error) { - // Return an error if there are no peers connected since there is no - // way to relay a found block or receive transactions to work on. - // However, allow this state when running in the simulation test mode. - if !s.cfg.Simnet && s.connectionManager.ConnectionCount() == 0 { - - return nil, &model.RPCError{ - Code: model.ErrRPCClientNotConnected, - Message: "Kaspa is not connected", - } - } - - payAddr, err := util.DecodeAddress(request.PayAddress, s.dag.Params.Prefix) - if err != nil { - return nil, err - } - - // When a long poll ID was provided, this is a long poll request by the - // client to be notified when block template referenced by the ID should - // be replaced with a new one. - if request != nil && request.LongPollID != "" { - return handleGetBlockTemplateLongPoll(s, request.LongPollID, payAddr, closeChan) - } - - // Protect concurrent access when updating block templates. - state := s.gbtWorkState - state.Lock() - defer state.Unlock() - - // Get and return a block template. A new block template will be - // generated when the current best block has changed or the transactions - // in the memory pool have been updated and it has been at least five - // seconds since the last template was generated. Otherwise, the - // timestamp for the existing block template is updated (and possibly - // the difficulty on testnet per the consesus rules). - if err := state.updateBlockTemplate(s, payAddr); err != nil { - return nil, err - } - return state.blockTemplateResult(s) -} - -// handleGetBlockTemplateLongPoll is a helper for handleGetBlockTemplateRequest -// which deals with handling long polling for block templates. When a caller -// sends a request with a long poll ID that was previously returned, a response -// is not sent until the caller should stop working on the previous block -// template in favor of the new one. In particular, this is the case when the -// old block template is no longer valid due to a solution already being found -// and added to the block DAG, or new transactions have shown up and some time -// has passed without finding a solution. -func handleGetBlockTemplateLongPoll(s *Server, longPollID string, payAddr util.Address, closeChan <-chan struct{}) (interface{}, error) { - state := s.gbtWorkState - - result, longPollChan, err := blockTemplateOrLongPollChan(s, longPollID, payAddr) - if err != nil { - return nil, err - } - - if result != nil { - return result, nil - } - - select { - // When the client closes before it's time to send a reply, just return - // now so the goroutine doesn't hang around. - case <-closeChan: - return nil, ErrClientQuit - - // Wait until signal received to send the reply. - case <-longPollChan: - // Fallthrough - } - - // Get the lastest block template - state.Lock() - defer state.Unlock() - - if err := state.updateBlockTemplate(s, payAddr); err != nil { - return nil, err - } - - // Include whether or not it is valid to submit work against the old - // block template depending on whether or not a solution has already - // been found and added to the block DAG. - result, err = state.blockTemplateResult(s) - if err != nil { - return nil, err - } - - return result, nil -} - -// blockTemplateOrLongPollChan returns a block template if the -// template identified by the provided long poll ID is stale or -// invalid. Otherwise, it returns a channel that will notify -// when there's a more current template. -func blockTemplateOrLongPollChan(s *Server, longPollID string, payAddr util.Address) (*model.GetBlockTemplateResult, chan struct{}, error) { - state := s.gbtWorkState - - state.Lock() - defer state.Unlock() - // The state unlock is intentionally not deferred here since it needs to - // be manually unlocked before waiting for a notification about block - // template changes. - - if err := state.updateBlockTemplate(s, payAddr); err != nil { - return nil, nil, err - } - - // Just return the current block template if the long poll ID provided by - // the caller is invalid. - parentHashes, lastGenerated, err := decodeLongPollID(longPollID) - if err != nil { - result, err := state.blockTemplateResult(s) - if err != nil { - return nil, nil, err - } - - return result, nil, nil - } - - // Return the block template now if the specific block template - // identified by the long poll ID no longer matches the current block - // template as this means the provided template is stale. - areHashesEqual := daghash.AreEqual(state.template.Block.Header.ParentHashes, parentHashes) - if !areHashesEqual || - lastGenerated != state.lastGenerated.UnixSeconds() { - - // Include whether or not it is valid to submit work against the - // old block template depending on whether or not a solution has - // already been found and added to the block DAG. - result, err := state.blockTemplateResult(s) - if err != nil { - return nil, nil, err - } - - return result, nil, nil - } - - // Register the parent hashes and last generated time for notifications - // Get a channel that will be notified when the template associated with - // the provided ID is stale and a new block template should be returned to - // the caller. - longPollChan := state.templateUpdateChan(parentHashes, lastGenerated) - return nil, longPollChan, nil -} - -// handleGetBlockTemplateProposal is a helper for handleGetBlockTemplate which -// deals with block proposals. -func handleGetBlockTemplateProposal(s *Server, request *model.TemplateRequest) (interface{}, error) { - hexData := request.Data - if hexData == "" { - return false, &model.RPCError{ - Code: model.ErrRPCType, - Message: fmt.Sprintf("Data must contain the " + - "hex-encoded serialized block that is being " + - "proposed"), - } - } - - // Ensure the provided data is sane and deserialize the proposed block. - if len(hexData)%2 != 0 { - hexData = "0" + hexData - } - dataBytes, err := hex.DecodeString(hexData) - if err != nil { - return false, &model.RPCError{ - Code: model.ErrRPCDeserialization, - Message: fmt.Sprintf("Data must be "+ - "hexadecimal string (not %q)", hexData), - } - } - var msgBlock appmessage.MsgBlock - if err := msgBlock.Deserialize(bytes.NewReader(dataBytes)); err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCDeserialization, - Message: "Block decode failed: " + err.Error(), - } - } - block := util.NewBlock(&msgBlock) - - // Ensure the block is building from the expected parent blocks. - expectedParentHashes := s.dag.TipHashes() - parentHashes := block.MsgBlock().Header.ParentHashes - if !daghash.AreEqual(expectedParentHashes, parentHashes) { - return "bad-parentblk", nil - } - - if err := s.dag.CheckConnectBlockTemplate(block); err != nil { - if !errors.As(err, &blockdag.RuleError{}) { - errStr := fmt.Sprintf("Failed to process block proposal: %s", err) - log.Error(errStr) - return nil, &model.RPCError{ - Code: model.ErrRPCVerify, - Message: errStr, - } - } - - log.Infof("Rejected block proposal: %s", err) - return dagErrToGBTErrString(err), nil - } - - return nil, nil -} - -// dagErrToGBTErrString converts an error returned from kaspa to a string -// which matches the reasons and format described in BIP0022 for rejection -// reasons. -func dagErrToGBTErrString(err error) string { - // When the passed error is not a RuleError, just return a generic - // rejected string with the error text. - var ruleErr blockdag.RuleError - if !errors.As(err, &ruleErr) { - return "rejected: " + err.Error() - } - - switch ruleErr.ErrorCode { - case blockdag.ErrDuplicateBlock: - return "duplicate" - case blockdag.ErrBlockMassTooHigh: - return "bad-blk-mass" - case blockdag.ErrBlockVersionTooOld: - return "bad-version" - case blockdag.ErrTimeTooOld: - return "time-too-old" - case blockdag.ErrTimeTooNew: - return "time-too-new" - case blockdag.ErrDifficultyTooLow: - return "bad-diffbits" - case blockdag.ErrUnexpectedDifficulty: - return "bad-diffbits" - case blockdag.ErrHighHash: - return "high-hash" - case blockdag.ErrBadMerkleRoot: - return "bad-txnmrklroot" - case blockdag.ErrFinalityPointTimeTooOld: - return "finality-point-time-too-old" - case blockdag.ErrNoTransactions: - return "bad-txns-none" - case blockdag.ErrNoTxInputs: - return "bad-txns-noinputs" - case blockdag.ErrTxMassTooHigh: - return "bad-txns-mass" - case blockdag.ErrBadTxOutValue: - return "bad-txns-outputvalue" - case blockdag.ErrDuplicateTxInputs: - return "bad-txns-dupinputs" - case blockdag.ErrBadTxInput: - return "bad-txns-badinput" - case blockdag.ErrMissingTxOut: - return "bad-txns-missinginput" - case blockdag.ErrUnfinalizedTx: - return "bad-txns-unfinalizedtx" - case blockdag.ErrDuplicateTx: - return "bad-txns-duplicate" - case blockdag.ErrOverwriteTx: - return "bad-txns-overwrite" - case blockdag.ErrImmatureSpend: - return "bad-txns-maturity" - case blockdag.ErrSpendTooHigh: - return "bad-txns-highspend" - case blockdag.ErrBadFees: - return "bad-txns-fees" - case blockdag.ErrTooManySigOps: - return "high-sigops" - case blockdag.ErrFirstTxNotCoinbase: - return "bad-txns-nocoinbase" - case blockdag.ErrMultipleCoinbases: - return "bad-txns-multicoinbase" - case blockdag.ErrBadCoinbasePayloadLen: - return "bad-cb-length" - case blockdag.ErrScriptMalformed: - return "bad-script-malformed" - case blockdag.ErrScriptValidation: - return "bad-script-validate" - case blockdag.ErrParentBlockUnknown: - return "parent-blk-not-found" - case blockdag.ErrInvalidAncestorBlock: - return "bad-parentblk" - case blockdag.ErrParentBlockNotCurrentTips: - return "inconclusive-not-best-parentblk" - } - - return "rejected: " + err.Error() -} - -// notifyLongPollers notifies any channels that have been registered to be -// notified when block templates are stale. -// -// This function MUST be called with the state locked. -func (state *gbtWorkState) notifyLongPollers(tipHashes []*daghash.Hash, lastGenerated mstime.Time) { - // Notify anything that is waiting for a block template update from - // hashes which are not the current tip hashes. - tipHashesStr := daghash.JoinHashesStrings(tipHashes, "") - for hashesStr, channels := range state.notifyMap { - if hashesStr != tipHashesStr { - for _, c := range channels { - close(c) - } - delete(state.notifyMap, hashesStr) - } - } - - // Return now if the provided last generated timestamp has not been - // initialized. - if lastGenerated.IsZero() { - return - } - - // Return now if there is nothing registered for updates to the current - // best block hash. - channels, ok := state.notifyMap[tipHashesStr] - if !ok { - return - } - - // Notify anything that is waiting for a block template update from a - // block template generated before the most recently generated block - // template. - lastGeneratedUnix := lastGenerated.UnixSeconds() - for lastGen, c := range channels { - if lastGen < lastGeneratedUnix { - close(c) - delete(channels, lastGen) - } - } - - // Remove the entry altogether if there are no more registered - // channels. - if len(channels) == 0 { - delete(state.notifyMap, tipHashesStr) - } -} - -// NotifyBlockAdded uses the newly-added block to notify any long poll -// clients with a new block template when their existing block template is -// stale due to the newly added block. -func (state *gbtWorkState) NotifyBlockAdded(tipHashes []*daghash.Hash) { - spawn("gbtWorkState.NotifyBlockAdded", func() { - state.Lock() - defer state.Unlock() - - state.notifyLongPollers(tipHashes, state.lastTxUpdate) - }) -} - -// NotifyMempoolTx uses the new last updated time for the transaction memory -// pool to notify any long poll clients with a new block template when their -// existing block template is stale due to enough time passing and the contents -// of the memory pool changing. -func (state *gbtWorkState) NotifyMempoolTx(lastUpdated mstime.Time) { - spawn("NotifyMempoolTx", func() { - state.Lock() - defer state.Unlock() - - // No need to notify anything if no block templates have been generated - // yet. - if state.tipHashes == nil || state.lastGenerated.IsZero() { - return - } - - if mstime.Now().After(state.lastGenerated.Add(time.Second * - gbtRegenerateSeconds)) { - - state.notifyLongPollers(state.tipHashes, lastUpdated) - } - }) -} - -// templateUpdateChan returns a channel that will be closed once the block -// template associated with the passed parent hashes and last generated time -// is stale. The function will return existing channels for duplicate -// parameters which allows multiple clients to wait for the same block template -// without requiring a different channel for each client. -// -// This function MUST be called with the state locked. -func (state *gbtWorkState) templateUpdateChan(tipHashes []*daghash.Hash, lastGenerated int64) chan struct{} { - tipHashesStr := daghash.JoinHashesStrings(tipHashes, "") - // Either get the current list of channels waiting for updates about - // changes to block template for the parent hashes or create a new one. - channels, ok := state.notifyMap[tipHashesStr] - if !ok { - m := make(map[int64]chan struct{}) - state.notifyMap[tipHashesStr] = m - channels = m - } - - // Get the current channel associated with the time the block template - // was last generated or create a new one. - c, ok := channels[lastGenerated] - if !ok { - c = make(chan struct{}) - channels[lastGenerated] = c - } - - return c -} - -// updateBlockTemplate creates or updates a block template for the work state. -// A new block template will be generated when the current best block has -// changed or the transactions in the memory pool have been updated and it has -// been long enough since the last template was generated. Otherwise, the -// timestamp for the existing block template is updated (and possibly the -// difficulty on testnet per the consesus rules). Finally, if the -// useCoinbaseValue flag is false and the existing block template does not -// already contain a valid payment address, the block template will be updated -// with a randomly selected payment address from the list of configured -// addresses. -// -// This function MUST be called with the state locked. -func (state *gbtWorkState) updateBlockTemplate(s *Server, payAddr util.Address) error { - generator := s.blockTemplateGenerator - lastTxUpdate := generator.TxSource().LastUpdated() - if lastTxUpdate.IsZero() { - lastTxUpdate = mstime.Now() - } - - // Generate a new block template when the current best block has - // changed or the transactions in the memory pool have been updated and - // it has been at least gbtRegenerateSecond since the last template was - // generated. - var msgBlock *appmessage.MsgBlock - var targetDifficulty string - tipHashes := s.dag.TipHashes() - template := state.template - if template == nil || state.tipHashes == nil || - !daghash.AreEqual(state.tipHashes, tipHashes) || - state.payAddress.String() != payAddr.String() || - (state.lastTxUpdate != lastTxUpdate && - mstime.Now().After(state.lastGenerated.Add(time.Second* - gbtRegenerateSeconds))) { - - // Reset the previous best hash the block template was generated - // against so any errors below cause the next invocation to try - // again. - state.tipHashes = nil - - // Create a new block template that has a coinbase which anyone - // can redeem. This is only acceptable because the returned - // block template doesn't include the coinbase, so the caller - // will ultimately create their own coinbase which pays to the - // appropriate address(es). - - extraNonce, err := random.Uint64() - if err != nil { - return internalRPCError(fmt.Sprintf("Failed to randomize "+ - "extra nonce: %s", err.Error()), "") - } - - blkTemplate, err := generator.NewBlockTemplate(payAddr, extraNonce) - if err != nil { - return internalRPCError(fmt.Sprintf("Failed to create new block "+ - "template: %s", err.Error()), "") - } - template = blkTemplate - msgBlock = template.Block - targetDifficulty = fmt.Sprintf("%064x", - util.CompactToBig(msgBlock.Header.Bits)) - - // Get the minimum allowed timestamp for the block based on the - // median timestamp of the last several blocks per the DAG - // consensus rules. - minTimestamp := s.dag.NextBlockMinimumTime() - - // Update work state to ensure another block template isn't - // generated until needed. - state.template = template - state.lastGenerated = mstime.Now() - state.lastTxUpdate = lastTxUpdate - state.tipHashes = tipHashes - state.minTimestamp = minTimestamp - state.payAddress = payAddr - - log.Debugf("Generated block template (timestamp %s, "+ - "target %s, merkle root %s)", - msgBlock.Header.Timestamp, targetDifficulty, - msgBlock.Header.HashMerkleRoot) - - // Notify any clients that are long polling about the new - // template. - state.notifyLongPollers(tipHashes, lastTxUpdate) - } else { - // At this point, there is a saved block template and another - // request for a template was made, but either the available - // transactions haven't change or it hasn't been long enough to - // trigger a new block template to be generated. So, update the - // existing block template. - - // Set locals for convenience. - msgBlock = template.Block - targetDifficulty = fmt.Sprintf("%064x", - util.CompactToBig(msgBlock.Header.Bits)) - - // Update the time of the block template to the current time - // while accounting for the median time of the past several - // blocks per the DAG consensus rules. - generator.UpdateBlockTime(msgBlock) - msgBlock.Header.Nonce = 0 - - log.Debugf("Updated block template (timestamp %s, "+ - "target %s)", msgBlock.Header.Timestamp, - targetDifficulty) - } - - return nil -} - -// blockTemplateResult returns the current block template associated with the -// state as a model.GetBlockTemplateResult that is ready to be encoded to JSON -// and returned to the caller. -// -// This function MUST be called with the state locked. -func (state *gbtWorkState) blockTemplateResult(s *Server) (*model.GetBlockTemplateResult, error) { - dag := s.dag - // Ensure the timestamps are still in valid range for the template. - // This should really only ever happen if the local clock is changed - // after the template is generated, but it's important to avoid serving - // block templates that will be delayed on other nodes. - template := state.template - msgBlock := template.Block - header := &msgBlock.Header - adjustedTime := dag.Now() - maxTime := adjustedTime.Add(time.Millisecond * time.Duration(dag.TimestampDeviationTolerance)) - if header.Timestamp.After(maxTime) { - return nil, &model.RPCError{ - Code: model.ErrRPCOutOfRange, - Message: fmt.Sprintf("The template time is after the "+ - "maximum allowed time for a block - template "+ - "time %s, maximum time %s", adjustedTime, - maxTime), - } - } - - // Convert each transaction in the block template to a template result - // transaction. The result does not include the coinbase, so notice - // the adjustments to the various lengths and indices. - numTx := len(msgBlock.Transactions) - transactions := make([]model.GetBlockTemplateResultTx, 0, numTx-1) - txIndex := make(map[daghash.TxID]int64, numTx) - for i, tx := range msgBlock.Transactions { - txID := tx.TxID() - txIndex[*txID] = int64(i) - - // Create an array of 1-based indices to transactions that come - // before this one in the transactions list which this one - // depends on. This is necessary since the created block must - // ensure proper ordering of the dependencies. A map is used - // before creating the final array to prevent duplicate entries - // when multiple inputs reference the same transaction. - dependsMap := make(map[int64]struct{}) - for _, txIn := range tx.TxIn { - if idx, ok := txIndex[txIn.PreviousOutpoint.TxID]; ok { - dependsMap[idx] = struct{}{} - } - } - depends := make([]int64, 0, len(dependsMap)) - for idx := range dependsMap { - depends = append(depends, idx) - } - - // Serialize the transaction for later conversion to hex. - txBuf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) - if err := tx.Serialize(txBuf); err != nil { - context := "Failed to serialize transaction" - return nil, internalRPCError(err.Error(), context) - } - - resultTx := model.GetBlockTemplateResultTx{ - Data: hex.EncodeToString(txBuf.Bytes()), - ID: txID.String(), - Depends: depends, - Mass: template.TxMasses[i], - Fee: template.Fees[i], - } - transactions = append(transactions, resultTx) - } - - // Generate the block template reply. Note that following mutations are - // implied by the included or omission of fields: - // Including MinTime -> time/decrement - // Omitting CoinbaseTxn -> coinbase, generation - targetDifficulty := fmt.Sprintf("%064x", util.CompactToBig(header.Bits)) - longPollID := encodeLongPollID(state.tipHashes, state.payAddress, state.lastGenerated) - - // Check whether this node is synced with the rest of of the - // network. There's almost never a good reason to mine on top - // of an unsynced DAG, and miners are generally expected not to - // mine when isSynced is false. - // This is not a straight-up error because the choice of whether - // to mine or not is the responsibility of the miner rather - // than the node's. - isSynced := s.blockTemplateGenerator.IsSynced() - - reply := model.GetBlockTemplateResult{ - Bits: strconv.FormatInt(int64(header.Bits), 16), - CurTime: header.Timestamp.UnixMilliseconds(), - Height: template.Height, - ParentHashes: daghash.Strings(header.ParentHashes), - MassLimit: appmessage.MaxMassPerBlock, - Transactions: transactions, - HashMerkleRoot: header.HashMerkleRoot.String(), - AcceptedIDMerkleRoot: header.AcceptedIDMerkleRoot.String(), - UTXOCommitment: header.UTXOCommitment.String(), - Version: header.Version, - LongPollID: longPollID, - Target: targetDifficulty, - MinTime: state.minTimestamp.UnixMilliseconds(), - MaxTime: maxTime.UnixMilliseconds(), - Mutable: gbtMutableFields, - NonceRange: gbtNonceRange, - Capabilities: gbtCapabilities, - IsSynced: isSynced, - } - - return &reply, nil -} - -// encodeLongPollID encodes the passed details into an ID that can be used to -// uniquely identify a block template. -func encodeLongPollID(parentHashes []*daghash.Hash, miningAddress util.Address, lastGenerated mstime.Time) string { - return fmt.Sprintf("%s-%s-%d", daghash.JoinHashesStrings(parentHashes, ""), miningAddress, lastGenerated.UnixSeconds()) -} - -// decodeLongPollID decodes an ID that is used to uniquely identify a block -// template. This is mainly used as a mechanism to track when to update clients -// that are using long polling for block templates. The ID consists of the -// parent blocks hashes for the associated template and the time the associated -// template was generated. -func decodeLongPollID(longPollID string) ([]*daghash.Hash, int64, error) { - fields := strings.Split(longPollID, "-") - if len(fields) != 2 { - return nil, 0, errors.New("decodeLongPollID: invalid number of fields") - } - - parentHashesStr := fields[0] - if len(parentHashesStr)%daghash.HashSize != 0 { - return nil, 0, errors.New("decodeLongPollID: invalid parent hashes format") - } - numberOfHashes := len(parentHashesStr) / daghash.HashSize - - parentHashes := make([]*daghash.Hash, 0, numberOfHashes) - - for i := 0; i < len(parentHashesStr); i += daghash.HashSize { - hash, err := daghash.NewHashFromStr(parentHashesStr[i : i+daghash.HashSize]) - if err != nil { - return nil, 0, errors.Errorf("decodeLongPollID: NewHashFromStr: %s", err) - } - parentHashes = append(parentHashes, hash) - } - - lastGenerated, err := strconv.ParseInt(fields[1], 10, 64) - if err != nil { - return nil, 0, errors.Errorf("decodeLongPollID: Cannot parse timestamp %s: %s", fields[1], err) - } - - return parentHashes, lastGenerated, nil -} diff --git a/infrastructure/network/rpc/handle_get_blocks.go b/infrastructure/network/rpc/handle_get_blocks.go deleted file mode 100644 index 10eb8c575..000000000 --- a/infrastructure/network/rpc/handle_get_blocks.go +++ /dev/null @@ -1,115 +0,0 @@ -package rpc - -import ( - "encoding/hex" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/daghash" -) - -const ( - // maxBlocksInGetBlocksResult is the max amount of blocks that are - // allowed in a GetBlocksResult. - maxBlocksInGetBlocksResult = 1000 -) - -func handleGetBlocks(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.GetBlocksCmd) - var lowHash *daghash.Hash - if c.LowHash != nil { - lowHash = &daghash.Hash{} - err := daghash.Decode(lowHash, *c.LowHash) - if err != nil { - return nil, rpcDecodeHexError(*c.LowHash) - } - } - - s.dag.RLock() - defer s.dag.RUnlock() - - // If lowHash is not in the DAG, there's nothing to do; return an error. - if lowHash != nil && !s.dag.IsKnownBlock(lowHash) { - return nil, &model.RPCError{ - Code: model.ErrRPCBlockNotFound, - Message: "Block not found", - } - } - - // Retrieve the block hashes. - blockHashes, err := s.dag.BlockHashesFrom(lowHash, maxBlocksInGetBlocksResult) - if err != nil { - return nil, err - } - - // Convert the hashes to strings - hashes := make([]string, len(blockHashes)) - for i, blockHash := range blockHashes { - hashes[i] = blockHash.String() - } - - result := &model.GetBlocksResult{ - Hashes: hashes, - RawBlocks: nil, - VerboseBlocks: nil, - } - - // Include more data if requested - if c.IncludeRawBlockData || c.IncludeVerboseBlockData { - blockBytesSlice, err := hashesToBlockBytes(s, blockHashes) - if err != nil { - return nil, err - } - if c.IncludeRawBlockData { - result.RawBlocks = blockBytesToStrings(blockBytesSlice) - } - if c.IncludeVerboseBlockData { - verboseBlocks, err := blockBytesToBlockVerboseResults(s, blockBytesSlice) - if err != nil { - return nil, err - } - result.VerboseBlocks = verboseBlocks - } - } - - return result, nil -} - -func hashesToBlockBytes(s *Server, hashes []*daghash.Hash) ([][]byte, error) { - blocks := make([][]byte, len(hashes)) - for i, hash := range hashes { - block, err := s.dag.BlockByHash(hash) - if err != nil { - return nil, err - } - blockBytes, err := block.Bytes() - if err != nil { - return nil, err - } - blocks[i] = blockBytes - } - return blocks, nil -} - -func blockBytesToStrings(blockBytesSlice [][]byte) []string { - rawBlocks := make([]string, len(blockBytesSlice)) - for i, blockBytes := range blockBytesSlice { - rawBlocks[i] = hex.EncodeToString(blockBytes) - } - return rawBlocks -} - -func blockBytesToBlockVerboseResults(s *Server, blockBytesSlice [][]byte) ([]model.GetBlockVerboseResult, error) { - verboseBlocks := make([]model.GetBlockVerboseResult, len(blockBytesSlice)) - for i, blockBytes := range blockBytesSlice { - block, err := util.NewBlockFromBytes(blockBytes) - if err != nil { - return nil, err - } - getBlockVerboseResult, err := buildGetBlockVerboseResult(s, block, false) - if err != nil { - return nil, err - } - verboseBlocks[i] = *getBlockVerboseResult - } - return verboseBlocks, nil -} diff --git a/infrastructure/network/rpc/handle_get_chain_from_block.go b/infrastructure/network/rpc/handle_get_chain_from_block.go deleted file mode 100644 index e7b55c3b2..000000000 --- a/infrastructure/network/rpc/handle_get_chain_from_block.go +++ /dev/null @@ -1,90 +0,0 @@ -package rpc - -import ( - "fmt" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/daghash" -) - -const ( - // maxBlocksInGetChainFromBlockResult is the max amount of blocks that - // are allowed in a GetChainFromBlockResult. - maxBlocksInGetChainFromBlockResult = 1000 -) - -// handleGetChainFromBlock implements the getChainFromBlock command. -func handleGetChainFromBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - if s.acceptanceIndex == nil { - return nil, &model.RPCError{ - Code: model.ErrRPCNoAcceptanceIndex, - Message: "The acceptance index must be " + - "enabled to get the selected parent chain " + - "(specify --acceptanceindex)", - } - } - - c := cmd.(*model.GetChainFromBlockCmd) - var startHash *daghash.Hash - if c.StartHash != nil { - startHash = &daghash.Hash{} - err := daghash.Decode(startHash, *c.StartHash) - if err != nil { - return nil, rpcDecodeHexError(*c.StartHash) - } - } - - s.dag.RLock() - defer s.dag.RUnlock() - - // If startHash is not in the selected parent chain, there's nothing - // to do; return an error. - if startHash != nil && !s.dag.IsInDAG(startHash) { - return nil, &model.RPCError{ - Code: model.ErrRPCBlockNotFound, - Message: "Block not found in the DAG", - } - } - - // Retrieve the selected parent chain. - removedChainHashes, addedChainHashes, err := s.dag.SelectedParentChain(startHash) - if err != nil { - return nil, err - } - - // Limit the amount of blocks in the response - if len(addedChainHashes) > maxBlocksInGetChainFromBlockResult { - addedChainHashes = addedChainHashes[:maxBlocksInGetChainFromBlockResult] - } - - // Collect addedChainBlocks. - addedChainBlocks, err := collectChainBlocks(s, addedChainHashes) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInternal.Code, - Message: fmt.Sprintf("could not collect chain blocks: %s", err), - } - } - - // Collect removedHashes. - removedHashes := make([]string, len(removedChainHashes)) - for i, hash := range removedChainHashes { - removedHashes[i] = hash.String() - } - - result := &model.GetChainFromBlockResult{ - RemovedChainBlockHashes: removedHashes, - AddedChainBlocks: addedChainBlocks, - Blocks: nil, - } - - // If the user specified to include the blocks, collect them as well. - if c.IncludeBlocks { - getBlockVerboseResults, err := hashesToGetBlockVerboseResults(s, addedChainHashes) - if err != nil { - return nil, err - } - result.Blocks = getBlockVerboseResults - } - - return result, nil -} diff --git a/infrastructure/network/rpc/handle_get_connected_peer_info.go b/infrastructure/network/rpc/handle_get_connected_peer_info.go deleted file mode 100644 index 021b75ebc..000000000 --- a/infrastructure/network/rpc/handle_get_connected_peer_info.go +++ /dev/null @@ -1,27 +0,0 @@ -package rpc - -import ( - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// handleGetConnectedPeerInfo implements the getConnectedPeerInfo command. -func handleGetConnectedPeerInfo(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - peers := s.protocolManager.Peers() - infos := make([]*model.GetConnectedPeerInfoResult, 0, len(peers)) - for _, peer := range peers { - info := &model.GetConnectedPeerInfoResult{ - ID: peer.ID().String(), - Address: peer.Address(), - LastPingDuration: peer.LastPingDuration().Milliseconds(), - SelectedTipHash: peer.SelectedTipHash().String(), - IsSyncNode: peer == s.protocolManager.IBDPeer(), - IsOutbound: peer.IsOutbound(), - TimeOffset: peer.TimeOffset().Milliseconds(), - UserAgent: peer.UserAgent(), - AdvertisedProtocolVersion: peer.AdvertisedProtocolVersion(), - TimeConnected: peer.TimeConnected().Milliseconds(), - } - infos = append(infos, info) - } - return infos, nil -} diff --git a/infrastructure/network/rpc/handle_get_connection_count.go b/infrastructure/network/rpc/handle_get_connection_count.go deleted file mode 100644 index d10f6cdf5..000000000 --- a/infrastructure/network/rpc/handle_get_connection_count.go +++ /dev/null @@ -1,6 +0,0 @@ -package rpc - -// handleGetConnectionCount implements the getConnectionCount command. -func handleGetConnectionCount(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - return s.connectionManager.ConnectionCount(), nil -} diff --git a/infrastructure/network/rpc/handle_get_current_net.go b/infrastructure/network/rpc/handle_get_current_net.go deleted file mode 100644 index 0c853206e..000000000 --- a/infrastructure/network/rpc/handle_get_current_net.go +++ /dev/null @@ -1,6 +0,0 @@ -package rpc - -// handleGetCurrentNet implements the getCurrentNet command. -func handleGetCurrentNet(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - return s.dag.Params.Net, nil -} diff --git a/infrastructure/network/rpc/handle_get_difficulty.go b/infrastructure/network/rpc/handle_get_difficulty.go deleted file mode 100644 index c43bd0842..000000000 --- a/infrastructure/network/rpc/handle_get_difficulty.go +++ /dev/null @@ -1,6 +0,0 @@ -package rpc - -// handleGetDifficulty implements the getDifficulty command. -func handleGetDifficulty(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - return getDifficultyRatio(s.dag.SelectedTipHeader().Bits, s.dag.Params), nil -} diff --git a/infrastructure/network/rpc/handle_get_headers.go b/infrastructure/network/rpc/handle_get_headers.go deleted file mode 100644 index f0d3ceb98..000000000 --- a/infrastructure/network/rpc/handle_get_headers.go +++ /dev/null @@ -1,51 +0,0 @@ -package rpc - -import ( - "bytes" - "encoding/hex" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/daghash" -) - -const getHeadersMaxHeaders = 2000 - -// handleGetHeaders implements the getHeaders command. -func handleGetHeaders(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.GetHeadersCmd) - - lowHash := &daghash.ZeroHash - if c.LowHash != "" { - err := daghash.Decode(lowHash, c.LowHash) - if err != nil { - return nil, rpcDecodeHexError(c.HighHash) - } - } - highHash := &daghash.ZeroHash - if c.HighHash != "" { - err := daghash.Decode(highHash, c.HighHash) - if err != nil { - return nil, rpcDecodeHexError(c.HighHash) - } - } - headers, err := s.dag.AntiPastHeadersBetween(lowHash, highHash, getHeadersMaxHeaders) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCMisc, - Message: err.Error(), - } - } - - // Return the serialized block headers as hex-encoded strings. - hexBlockHeaders := make([]string, len(headers)) - var buf bytes.Buffer - for i, h := range headers { - err := h.Serialize(&buf) - if err != nil { - return nil, internalRPCError(err.Error(), - "Failed to serialize block header") - } - hexBlockHeaders[i] = hex.EncodeToString(buf.Bytes()) - buf.Reset() - } - return hexBlockHeaders, nil -} diff --git a/infrastructure/network/rpc/handle_get_info.go b/infrastructure/network/rpc/handle_get_info.go deleted file mode 100644 index b7ef71ac9..000000000 --- a/infrastructure/network/rpc/handle_get_info.go +++ /dev/null @@ -1,24 +0,0 @@ -package rpc - -import ( - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/version" -) - -// handleGetInfo implements the getInfo command. We only return the fields -// that are not related to wallet functionality. -func handleGetInfo(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - ret := &model.InfoDAGResult{ - Version: version.Version(), - ProtocolVersion: int32(maxProtocolVersion), - Blocks: s.dag.BlockCount(), - Connections: int32(s.connectionManager.ConnectionCount()), - Proxy: s.cfg.Proxy, - Difficulty: getDifficultyRatio(s.dag.CurrentBits(), s.dag.Params), - Testnet: s.cfg.Testnet, - Devnet: s.cfg.Devnet, - RelayFee: s.cfg.MinRelayTxFee.ToKAS(), - } - - return ret, nil -} diff --git a/infrastructure/network/rpc/handle_get_mempool_entry.go b/infrastructure/network/rpc/handle_get_mempool_entry.go deleted file mode 100644 index cf1169f1a..000000000 --- a/infrastructure/network/rpc/handle_get_mempool_entry.go +++ /dev/null @@ -1,33 +0,0 @@ -package rpc - -import ( - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/daghash" - "github.com/pkg/errors" -) - -func handleGetMempoolEntry(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.GetMempoolEntryCmd) - txID, err := daghash.NewTxIDFromStr(c.TxID) - if err != nil { - return nil, err - } - - txDesc, ok := s.txMempool.FetchTxDesc(txID) - if !ok { - return nil, errors.Errorf("transaction is not in the pool") - } - - tx := txDesc.Tx - rawTx, err := createTxRawResult(s.dag.Params, tx.MsgTx(), tx.ID().String(), - nil, "", nil, true) - if err != nil { - return nil, err - } - - return &model.GetMempoolEntryResult{ - Fee: txDesc.Fee, - Time: txDesc.Added.UnixMilliseconds(), - RawTx: *rawTx, - }, nil -} diff --git a/infrastructure/network/rpc/handle_get_mempool_info.go b/infrastructure/network/rpc/handle_get_mempool_info.go deleted file mode 100644 index 9906d88cf..000000000 --- a/infrastructure/network/rpc/handle_get_mempool_info.go +++ /dev/null @@ -1,20 +0,0 @@ -package rpc - -import "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - -// handleGetMempoolInfo implements the getMempoolInfo command. -func handleGetMempoolInfo(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - mempoolTxns := s.txMempool.TxDescs() - - var numBytes int64 - for _, txD := range mempoolTxns { - numBytes += int64(txD.Tx.MsgTx().SerializeSize()) - } - - ret := &model.GetMempoolInfoResult{ - Size: int64(len(mempoolTxns)), - Bytes: numBytes, - } - - return ret, nil -} diff --git a/infrastructure/network/rpc/handle_get_peer_addresses.go b/infrastructure/network/rpc/handle_get_peer_addresses.go deleted file mode 100644 index f87a471f3..000000000 --- a/infrastructure/network/rpc/handle_get_peer_addresses.go +++ /dev/null @@ -1,70 +0,0 @@ -package rpc - -import ( - "github.com/kaspanet/kaspad/infrastructure/network/addressmanager" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// handleGetPeerAddresses handles getPeerAddresses commands. -func handleGetPeerAddresses(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - peersState, err := s.addressManager.PeersStateForSerialization() - if err != nil { - return nil, err - } - - rpcPeersState := model.GetPeerAddressesResult{ - Version: peersState.Version, - Key: peersState.Key, - Addresses: make([]*model.GetPeerAddressesKnownAddressResult, len(peersState.Addresses)), - NewBuckets: make(map[string]*model.GetPeerAddressesNewBucketResult), - NewBucketFullNodes: model.GetPeerAddressesNewBucketResult{}, - TriedBuckets: make(map[string]*model.GetPeerAddressesTriedBucketResult), - TriedBucketFullNodes: model.GetPeerAddressesTriedBucketResult{}, - } - - for i, addr := range peersState.Addresses { - rpcPeersState.Addresses[i] = &model.GetPeerAddressesKnownAddressResult{ - Addr: string(addr.Address), - Src: string(addr.SourceAddress), - SubnetworkID: addr.SubnetworkID, - Attempts: addr.Attempts, - TimeStamp: addr.TimeStamp, - LastAttempt: addr.LastAttempt, - LastSuccess: addr.LastSuccess, - IsBanned: addr.IsBanned, - BannedTime: addr.BannedTime, - } - } - - for subnetworkID, bucket := range peersState.SubnetworkNewAddressBucketArrays { - rpcPeersState.NewBuckets[subnetworkID] = &model.GetPeerAddressesNewBucketResult{} - for i, addr := range bucket { - rpcPeersState.NewBuckets[subnetworkID][i] = convertAddressKeySliceToString(addr) - } - } - - for i, addr := range peersState.FullNodeNewAddressBucketArray { - rpcPeersState.NewBucketFullNodes[i] = convertAddressKeySliceToString(addr) - } - - for subnetworkID, bucket := range peersState.SubnetworkTriedAddressBucketArrays { - rpcPeersState.TriedBuckets[subnetworkID] = &model.GetPeerAddressesTriedBucketResult{} - for i, addr := range bucket { - rpcPeersState.TriedBuckets[subnetworkID][i] = convertAddressKeySliceToString(addr) - } - } - - for i, addr := range peersState.FullNodeTriedAddressBucketArray { - rpcPeersState.TriedBucketFullNodes[i] = convertAddressKeySliceToString(addr) - } - - return rpcPeersState, nil -} - -func convertAddressKeySliceToString(addressKeys []addressmanager.AddressKey) []string { - strings := make([]string, len(addressKeys)) - for j, addr := range addressKeys { - strings[j] = string(addr) - } - return strings -} diff --git a/infrastructure/network/rpc/handle_get_raw_mempool.go b/infrastructure/network/rpc/handle_get_raw_mempool.go deleted file mode 100644 index 5c06f3e46..000000000 --- a/infrastructure/network/rpc/handle_get_raw_mempool.go +++ /dev/null @@ -1,58 +0,0 @@ -package rpc - -import ( - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util" -) - -// handleGetRawMempool implements the getRawMempool command. -func handleGetRawMempool(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.GetRawMempoolCmd) - mp := s.txMempool - - if c.Verbose != nil && *c.Verbose { - return rawMempoolVerbose(s), nil - } - - // The response is simply an array of the transaction hashes if the - // verbose flag is not set. - descs := mp.TxDescs() - hashStrings := make([]string, len(descs)) - for i := range hashStrings { - hashStrings[i] = descs[i].Tx.ID().String() - } - - return hashStrings, nil -} - -// rawMempoolVerbose returns all of the entries in the mempool as a fully -// populated jsonrpc result. -func rawMempoolVerbose(s *Server) map[string]*model.GetRawMempoolVerboseResult { - descs := s.txMempool.TxDescs() - result := make(map[string]*model.GetRawMempoolVerboseResult, len(descs)) - - for _, desc := range descs { - // Calculate the current priority based on the inputs to - // the transaction. Use zero if one or more of the - // input transactions can't be found for some reason. - tx := desc.Tx - - mpd := &model.GetRawMempoolVerboseResult{ - Size: int32(tx.MsgTx().SerializeSize()), - Fee: util.Amount(desc.Fee).ToKAS(), - Time: desc.Added.UnixMilliseconds(), - Depends: make([]string, 0), - } - for _, txIn := range tx.MsgTx().TxIn { - txID := &txIn.PreviousOutpoint.TxID - if s.txMempool.HaveTransaction(txID) { - mpd.Depends = append(mpd.Depends, - txID.String()) - } - } - - result[tx.ID().String()] = mpd - } - - return result -} diff --git a/infrastructure/network/rpc/handle_get_selected_tip.go b/infrastructure/network/rpc/handle_get_selected_tip.go deleted file mode 100644 index dd8d5022b..000000000 --- a/infrastructure/network/rpc/handle_get_selected_tip.go +++ /dev/null @@ -1,39 +0,0 @@ -package rpc - -import ( - "encoding/hex" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// handleGetSelectedTip implements the getSelectedTip command. -func handleGetSelectedTip(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - getSelectedTipCmd := cmd.(*model.GetSelectedTipCmd) - selectedTipHash := s.dag.SelectedTipHash() - - block, err := s.dag.BlockByHash(selectedTipHash) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCBlockNotFound, - Message: "Block not found", - } - } - blockBytes, err := block.Bytes() - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCBlockInvalid, - Message: "Cannot serialize block", - } - } - - // When the verbose flag is set to false, simply return the serialized block - // as a hex-encoded string (verbose flag is on by default). - if getSelectedTipCmd.Verbose != nil && !*getSelectedTipCmd.Verbose { - return hex.EncodeToString(blockBytes), nil - } - - blockVerboseResult, err := buildGetBlockVerboseResult(s, block, getSelectedTipCmd.VerboseTx == nil || !*getSelectedTipCmd.VerboseTx) - if err != nil { - return nil, err - } - return blockVerboseResult, nil -} diff --git a/infrastructure/network/rpc/handle_get_selected_tip_hash.go b/infrastructure/network/rpc/handle_get_selected_tip_hash.go deleted file mode 100644 index 7ec1b4fed..000000000 --- a/infrastructure/network/rpc/handle_get_selected_tip_hash.go +++ /dev/null @@ -1,6 +0,0 @@ -package rpc - -// handleGetSelectedTipHash implements the getSelectedTipHash command. -func handleGetSelectedTipHash(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - return s.dag.SelectedTipHash().String(), nil -} diff --git a/infrastructure/network/rpc/handle_get_subnetwork.go b/infrastructure/network/rpc/handle_get_subnetwork.go deleted file mode 100644 index 26bb90e35..000000000 --- a/infrastructure/network/rpc/handle_get_subnetwork.go +++ /dev/null @@ -1,33 +0,0 @@ -package rpc - -import ( - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/subnetworkid" -) - -// handleGetSubnetwork handles the getSubnetwork command. -func handleGetSubnetwork(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.GetSubnetworkCmd) - - subnetworkID, err := subnetworkid.NewFromStr(c.SubnetworkID) - if err != nil { - return nil, rpcDecodeHexError(c.SubnetworkID) - } - - var gasLimit *uint64 - if !subnetworkID.IsBuiltInOrNative() { - limit, err := s.dag.GasLimit(subnetworkID) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCSubnetworkNotFound, - Message: "Subnetwork not found.", - } - } - gasLimit = &limit - } - - subnetworkReply := &model.GetSubnetworkResult{ - GasLimit: gasLimit, - } - return subnetworkReply, nil -} diff --git a/infrastructure/network/rpc/handle_get_top_headers.go b/infrastructure/network/rpc/handle_get_top_headers.go deleted file mode 100644 index 097b561a7..000000000 --- a/infrastructure/network/rpc/handle_get_top_headers.go +++ /dev/null @@ -1,43 +0,0 @@ -package rpc - -import ( - "bytes" - "encoding/hex" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/daghash" -) - -const getTopHeadersMaxHeaders = getHeadersMaxHeaders - -// handleGetTopHeaders implements the getTopHeaders command. -func handleGetTopHeaders(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.GetTopHeadersCmd) - - var highHash *daghash.Hash - if c.HighHash != nil { - highHash = &daghash.Hash{} - err := daghash.Decode(highHash, *c.HighHash) - if err != nil { - return nil, rpcDecodeHexError(*c.HighHash) - } - } - headers, err := s.dag.GetTopHeaders(highHash, getTopHeadersMaxHeaders) - if err != nil { - return nil, internalRPCError(err.Error(), - "Failed to get top headers") - } - - // Return the serialized block headers as hex-encoded strings. - hexBlockHeaders := make([]string, len(headers)) - var buf bytes.Buffer - for i, h := range headers { - err := h.Serialize(&buf) - if err != nil { - return nil, internalRPCError(err.Error(), - "Failed to serialize block header") - } - hexBlockHeaders[i] = hex.EncodeToString(buf.Bytes()) - buf.Reset() - } - return hexBlockHeaders, nil -} diff --git a/infrastructure/network/rpc/handle_get_tx_out.go b/infrastructure/network/rpc/handle_get_tx_out.go deleted file mode 100644 index c183c645e..000000000 --- a/infrastructure/network/rpc/handle_get_tx_out.go +++ /dev/null @@ -1,120 +0,0 @@ -package rpc - -import ( - "encoding/hex" - "fmt" - - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/domain/txscript" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/daghash" - "github.com/kaspanet/kaspad/util/pointers" -) - -// handleGetTxOut handles getTxOut commands. -func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.GetTxOutCmd) - - // Convert the provided transaction hash hex to a Hash. - txID, err := daghash.NewTxIDFromStr(c.TxID) - if err != nil { - return nil, rpcDecodeHexError(c.TxID) - } - - // If requested and the tx is available in the mempool try to fetch it - // from there, otherwise attempt to fetch from the block database. - var selectedTipHash string - var confirmations *uint64 - var value uint64 - var scriptPubKey []byte - var isCoinbase bool - isInMempool := false - includeMempool := true - if c.IncludeMempool != nil { - includeMempool = *c.IncludeMempool - } - - tx, ok := s.txMempool.FetchTransaction(txID) - if includeMempool && ok { - mtx := tx.MsgTx() - if c.Vout > uint32(len(mtx.TxOut)-1) { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidTxVout, - Message: "Output index number (vout) does not " + - "exist for transaction.", - } - } - - txOut := mtx.TxOut[c.Vout] - if txOut == nil { - errStr := fmt.Sprintf("Output index: %d for txid: %s "+ - "does not exist", c.Vout, txID) - return nil, internalRPCError(errStr, "") - } - - selectedTipHash = s.dag.SelectedTipHash().String() - value = txOut.Value - scriptPubKey = txOut.ScriptPubKey - isCoinbase = mtx.IsCoinBase() - isInMempool = true - } else { - out := appmessage.Outpoint{TxID: *txID, Index: c.Vout} - entry, ok := s.dag.GetUTXOEntry(out) - if !ok { - return nil, rpcNoTxInfoError(txID) - } - - // To match the behavior of the reference client, return nil - // (JSON null) if the transaction output is spent by another - // transaction already in the DAG. Mined transactions - // that are spent by a mempool transaction are not affected by - // this. - if entry == nil { - return nil, nil - } - - utxoConfirmations, ok := s.dag.UTXOConfirmations(&out) - if !ok { - errStr := fmt.Sprintf("Cannot get confirmations for tx id %s, index %d", - out.TxID, out.Index) - return nil, internalRPCError(errStr, "") - } - confirmations = &utxoConfirmations - - selectedTipHash = s.dag.SelectedTipHash().String() - value = entry.Amount() - scriptPubKey = entry.ScriptPubKey() - isCoinbase = entry.IsCoinbase() - } - - // Disassemble script into single line printable format. - // The disassembled string will contain [error] inline if the script - // doesn't fully parse, so ignore the error here. - disbuf, _ := txscript.DisasmString(scriptPubKey) - - // Get further info about the script. - // Ignore the error here since an error means the script couldn't parse - // and there is no additional information about it anyways. - scriptClass, addr, _ := txscript.ExtractScriptPubKeyAddress(scriptPubKey, - s.dag.Params) - var address *string - if addr != nil { - address = pointers.String(addr.EncodeAddress()) - } - - txOutReply := &model.GetTxOutResult{ - SelectedTip: selectedTipHash, - Confirmations: confirmations, - IsInMempool: isInMempool, - Value: util.Amount(value).ToKAS(), - ScriptPubKey: model.ScriptPubKeyResult{ - Asm: disbuf, - Hex: hex.EncodeToString(scriptPubKey), - Type: scriptClass.String(), - Address: address, - }, - Coinbase: isCoinbase, - } - return txOutReply, nil -} diff --git a/infrastructure/network/rpc/handle_help.go b/infrastructure/network/rpc/handle_help.go deleted file mode 100644 index 55b0e73dc..000000000 --- a/infrastructure/network/rpc/handle_help.go +++ /dev/null @@ -1,42 +0,0 @@ -package rpc - -import "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - -// handleHelp implements the help command. -func handleHelp(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.HelpCmd) - - // Provide a usage overview of all commands when no specific command - // was specified. - var command string - if c.Command != nil { - command = *c.Command - } - if command == "" { - usage, err := s.helpCacher.rpcUsage(false) - if err != nil { - context := "Failed to generate RPC usage" - return nil, internalRPCError(err.Error(), context) - } - return usage, nil - } - - // Check that the command asked for is supported and implemented. Only - // search the main list of handlers since help should not be provided - // for commands that are unimplemented or related to wallet - // functionality. - if _, ok := rpcHandlers[command]; !ok { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidParameter, - Message: "Unknown command: " + command, - } - } - - // Get the help for the command. - help, err := s.helpCacher.rpcMethodHelp(command) - if err != nil { - context := "Failed to generate help" - return nil, internalRPCError(err.Error(), context) - } - return help, nil -} diff --git a/infrastructure/network/rpc/handle_load_tx_filter.go b/infrastructure/network/rpc/handle_load_tx_filter.go deleted file mode 100644 index 16013ee23..000000000 --- a/infrastructure/network/rpc/handle_load_tx_filter.go +++ /dev/null @@ -1,58 +0,0 @@ -package rpc - -import ( - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/daghash" -) - -// handleLoadTxFilter implements the loadTxFilter command extension for -// websocket connections. -// -// NOTE: This extension is ported from github.com/decred/dcrd -func handleLoadTxFilter(wsc *wsClient, icmd interface{}) (interface{}, error) { - cmd := icmd.(*model.LoadTxFilterCmd) - - outpoints := make([]appmessage.Outpoint, len(cmd.Outpoints)) - for i := range cmd.Outpoints { - txID, err := daghash.NewTxIDFromStr(cmd.Outpoints[i].TxID) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidParameter, - Message: err.Error(), - } - } - outpoints[i] = appmessage.Outpoint{ - TxID: *txID, - Index: cmd.Outpoints[i].Index, - } - } - - params := wsc.server.dag.Params - - reloadedFilterData := func() bool { - wsc.Lock() - defer wsc.Unlock() - if cmd.Reload || wsc.filterData == nil { - wsc.filterData = newWSClientFilter(cmd.Addresses, outpoints, - params) - return true - } - return false - }() - - if !reloadedFilterData { - func() { - wsc.filterData.mu.Lock() - defer wsc.filterData.mu.Unlock() - for _, a := range cmd.Addresses { - wsc.filterData.addAddressStr(a, params) - } - for i := range outpoints { - wsc.filterData.addUnspentOutpoint(&outpoints[i]) - } - }() - } - - return nil, nil -} diff --git a/infrastructure/network/rpc/handle_notify_blocks.go b/infrastructure/network/rpc/handle_notify_blocks.go deleted file mode 100644 index 3068aefb5..000000000 --- a/infrastructure/network/rpc/handle_notify_blocks.go +++ /dev/null @@ -1,8 +0,0 @@ -package rpc - -// handleNotifyBlocks implements the notifyBlocks command extension for -// websocket connections. -func handleNotifyBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) { - wsc.server.ntfnMgr.RegisterBlockUpdates(wsc) - return nil, nil -} diff --git a/infrastructure/network/rpc/handle_notify_chain_changes.go b/infrastructure/network/rpc/handle_notify_chain_changes.go deleted file mode 100644 index c5fef233e..000000000 --- a/infrastructure/network/rpc/handle_notify_chain_changes.go +++ /dev/null @@ -1,19 +0,0 @@ -package rpc - -import "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - -// handleNotifyChainChanges implements the notifyChainChanges command extension for -// websocket connections. -func handleNotifyChainChanges(wsc *wsClient, icmd interface{}) (interface{}, error) { - if wsc.server.acceptanceIndex == nil { - return nil, &model.RPCError{ - Code: model.ErrRPCNoAcceptanceIndex, - Message: "The acceptance index must be " + - "enabled to receive chain changes " + - "(specify --acceptanceindex)", - } - } - - wsc.server.ntfnMgr.RegisterChainChanges(wsc) - return nil, nil -} diff --git a/infrastructure/network/rpc/handle_notify_new_transactions.go b/infrastructure/network/rpc/handle_notify_new_transactions.go deleted file mode 100644 index 26f58e22b..000000000 --- a/infrastructure/network/rpc/handle_notify_new_transactions.go +++ /dev/null @@ -1,63 +0,0 @@ -package rpc - -import ( - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/subnetworkid" -) - -// handleNotifyNewTransations implements the notifyNewTransactions command -// extension for websocket connections. -func handleNotifyNewTransactions(wsc *wsClient, icmd interface{}) (interface{}, error) { - cmd, ok := icmd.(*model.NotifyNewTransactionsCmd) - if !ok { - return nil, model.ErrRPCInternal - } - - isVerbose := cmd.Verbose != nil && *cmd.Verbose - if !isVerbose && cmd.Subnetwork != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidParameter, - Message: "Subnetwork switch is only allowed if verbose=true", - } - } - - var subnetworkID *subnetworkid.SubnetworkID - if cmd.Subnetwork != nil { - var err error - subnetworkID, err = subnetworkid.NewFromStr(*cmd.Subnetwork) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidParameter, - Message: "Subnetwork is malformed", - } - } - } - - if isVerbose { - nodeSubnetworkID := wsc.server.dag.SubnetworkID() - if nodeSubnetworkID.IsEqual(subnetworkid.SubnetworkIDNative) && subnetworkID != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidParameter, - Message: "Subnetwork switch is disabled when node is in Native subnetwork", - } - } else if nodeSubnetworkID != nil { - if subnetworkID == nil { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidParameter, - Message: "Subnetwork switch is required when node is partial", - } - } - if !nodeSubnetworkID.IsEqual(subnetworkID) { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidParameter, - Message: "Subnetwork must equal the node's subnetwork when the node is partial", - } - } - } - } - - wsc.verboseTxUpdates = isVerbose - wsc.subnetworkIDForTxUpdates = subnetworkID - wsc.server.ntfnMgr.RegisterNewMempoolTxsUpdates(wsc) - return nil, nil -} diff --git a/infrastructure/network/rpc/handle_send_raw_transaction.go b/infrastructure/network/rpc/handle_send_raw_transaction.go deleted file mode 100644 index 923785cfb..000000000 --- a/infrastructure/network/rpc/handle_send_raw_transaction.go +++ /dev/null @@ -1,46 +0,0 @@ -package rpc - -import ( - "bytes" - "encoding/hex" - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/domain/mempool" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util" - "github.com/pkg/errors" -) - -// handleSendRawTransaction implements the sendRawTransaction command. -func handleSendRawTransaction(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.SendRawTransactionCmd) - // Deserialize and send off to tx relay - hexStr := c.HexTx - serializedTx, err := hex.DecodeString(hexStr) - if err != nil { - return nil, rpcDecodeHexError(hexStr) - } - var msgTx appmessage.MsgTx - err = msgTx.Deserialize(bytes.NewReader(serializedTx)) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCDeserialization, - Message: "TX decode failed: " + err.Error(), - } - } - - tx := util.NewTx(&msgTx) - err = s.protocolManager.AddTransaction(tx) - if err != nil { - if !errors.As(err, &mempool.RuleError{}) { - panic(err) - } - - log.Debugf("Rejected transaction %s: %s", tx.ID(), err) - return nil, &model.RPCError{ - Code: model.ErrRPCVerify, - Message: "TX rejected: " + err.Error(), - } - } - - return tx.ID().String(), nil -} diff --git a/infrastructure/network/rpc/handle_session.go b/infrastructure/network/rpc/handle_session.go deleted file mode 100644 index bfb5b94d2..000000000 --- a/infrastructure/network/rpc/handle_session.go +++ /dev/null @@ -1,9 +0,0 @@ -package rpc - -import "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - -// handleSession implements the session command extension for websocket -// connections. -func handleSession(wsc *wsClient, icmd interface{}) (interface{}, error) { - return &model.SessionResult{SessionID: wsc.sessionID}, nil -} diff --git a/infrastructure/network/rpc/handle_stop.go b/infrastructure/network/rpc/handle_stop.go deleted file mode 100644 index b3e3c0f4f..000000000 --- a/infrastructure/network/rpc/handle_stop.go +++ /dev/null @@ -1,10 +0,0 @@ -package rpc - -// handleStop implements the stop command. -func handleStop(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - select { - case s.requestProcessShutdown <- struct{}{}: - default: - } - return "kaspad stopping.", nil -} diff --git a/infrastructure/network/rpc/handle_stop_notify_blocks.go b/infrastructure/network/rpc/handle_stop_notify_blocks.go deleted file mode 100644 index f6608a4bc..000000000 --- a/infrastructure/network/rpc/handle_stop_notify_blocks.go +++ /dev/null @@ -1,8 +0,0 @@ -package rpc - -// handleStopNotifyBlocks implements the stopNotifyBlocks command extension for -// websocket connections. -func handleStopNotifyBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) { - wsc.server.ntfnMgr.UnregisterBlockUpdates(wsc) - return nil, nil -} diff --git a/infrastructure/network/rpc/handle_stop_notify_chain_changes.go b/infrastructure/network/rpc/handle_stop_notify_chain_changes.go deleted file mode 100644 index 92a2ab8c1..000000000 --- a/infrastructure/network/rpc/handle_stop_notify_chain_changes.go +++ /dev/null @@ -1,8 +0,0 @@ -package rpc - -// handleStopNotifyChainChanges implements the stopNotifyChainChanges command extension for -// websocket connections. -func handleStopNotifyChainChanges(wsc *wsClient, icmd interface{}) (interface{}, error) { - wsc.server.ntfnMgr.UnregisterChainChanges(wsc) - return nil, nil -} diff --git a/infrastructure/network/rpc/handle_stop_notify_new_transactions.go b/infrastructure/network/rpc/handle_stop_notify_new_transactions.go deleted file mode 100644 index 3657d7c1b..000000000 --- a/infrastructure/network/rpc/handle_stop_notify_new_transactions.go +++ /dev/null @@ -1,8 +0,0 @@ -package rpc - -// handleStopNotifyNewTransations implements the stopNotifyNewTransactions -// command extension for websocket connections. -func handleStopNotifyNewTransactions(wsc *wsClient, icmd interface{}) (interface{}, error) { - wsc.server.ntfnMgr.UnregisterNewMempoolTxsUpdates(wsc) - return nil, nil -} diff --git a/infrastructure/network/rpc/handle_submit_block.go b/infrastructure/network/rpc/handle_submit_block.go deleted file mode 100644 index 34ac03455..000000000 --- a/infrastructure/network/rpc/handle_submit_block.go +++ /dev/null @@ -1,40 +0,0 @@ -package rpc - -import ( - "encoding/hex" - "fmt" - "github.com/kaspanet/kaspad/domain/blockdag" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util" -) - -// handleSubmitBlock implements the submitBlock command. -func handleSubmitBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - c := cmd.(*model.SubmitBlockCmd) - - // Deserialize the submitted block. - hexStr := c.HexBlock - serializedBlock, err := hex.DecodeString(hexStr) - if err != nil { - return nil, rpcDecodeHexError(hexStr) - } - - block, err := util.NewBlockFromBytes(serializedBlock) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCDeserialization, - Message: "Block decode failed: " + err.Error(), - } - } - - err = s.protocolManager.AddBlock(block, blockdag.BFDisallowDelay|blockdag.BFDisallowOrphans) - if err != nil { - return nil, &model.RPCError{ - Code: model.ErrRPCVerify, - Message: fmt.Sprintf("Block rejected. Reason: %s", err), - } - } - - log.Infof("Accepted block %s via submitBlock", block.Hash()) - return nil, nil -} diff --git a/infrastructure/network/rpc/handle_uptime.go b/infrastructure/network/rpc/handle_uptime.go deleted file mode 100644 index 51a4aae70..000000000 --- a/infrastructure/network/rpc/handle_uptime.go +++ /dev/null @@ -1,10 +0,0 @@ -package rpc - -import ( - "github.com/kaspanet/kaspad/util/mstime" -) - -// handleUptime implements the uptime command. -func handleUptime(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - return mstime.Now().UnixMilliseconds() - s.startupTime.UnixMilliseconds(), nil -} diff --git a/infrastructure/network/rpc/handle_version.go b/infrastructure/network/rpc/handle_version.go deleted file mode 100644 index b149fed64..000000000 --- a/infrastructure/network/rpc/handle_version.go +++ /dev/null @@ -1,24 +0,0 @@ -package rpc - -import "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - -// API version constants -const ( - jsonrpcSemverString = "1.3.0" - jsonrpcSemverMajor = 1 - jsonrpcSemverMinor = 3 - jsonrpcSemverPatch = 0 -) - -// handleVersion implements the version command. -func handleVersion(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - result := map[string]model.VersionResult{ - "kaspadjsonrpcapi": { - VersionString: jsonrpcSemverString, - Major: jsonrpcSemverMajor, - Minor: jsonrpcSemverMinor, - Patch: jsonrpcSemverPatch, - }, - } - return result, nil -} diff --git a/infrastructure/network/rpc/handle_websocket_help.go b/infrastructure/network/rpc/handle_websocket_help.go deleted file mode 100644 index b61a78227..000000000 --- a/infrastructure/network/rpc/handle_websocket_help.go +++ /dev/null @@ -1,50 +0,0 @@ -package rpc - -import "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - -// handleWebsocketHelp implements the help command for websocket connections. -func handleWebsocketHelp(wsc *wsClient, icmd interface{}) (interface{}, error) { - cmd, ok := icmd.(*model.HelpCmd) - if !ok { - return nil, model.ErrRPCInternal - } - - // Provide a usage overview of all commands when no specific command - // was specified. - var command string - if cmd.Command != nil { - command = *cmd.Command - } - if command == "" { - usage, err := wsc.server.helpCacher.rpcUsage(true) - if err != nil { - context := "Failed to generate RPC usage" - return nil, internalRPCError(err.Error(), context) - } - return usage, nil - } - - // Check that the command asked for is supported and implemented. - // Search the list of websocket handlers as well as the main list of - // handlers since help should only be provided for those cases. - valid := true - if _, ok := rpcHandlers[command]; !ok { - if _, ok := wsHandlers[command]; !ok { - valid = false - } - } - if !valid { - return nil, &model.RPCError{ - Code: model.ErrRPCInvalidParameter, - Message: "Unknown command: " + command, - } - } - - // Get the help for the command. - help, err := wsc.server.helpCacher.rpcMethodHelp(command) - if err != nil { - context := "Failed to generate help" - return nil, internalRPCError(err.Error(), context) - } - return help, nil -} diff --git a/infrastructure/network/rpc/log.go b/infrastructure/network/rpc/log.go deleted file mode 100644 index 426e7121c..000000000 --- a/infrastructure/network/rpc/log.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package rpc - -import ( - "github.com/kaspanet/kaspad/infrastructure/logger" - "github.com/kaspanet/kaspad/util/panics" -) - -var ( - log, _ = logger.Get(logger.SubsystemTags.RPCS) - spawn = panics.GoroutineWrapperFunc(log) -) diff --git a/infrastructure/network/rpc/model/CONTRIBUTORS b/infrastructure/network/rpc/model/CONTRIBUTORS deleted file mode 100644 index b8d98b32f..000000000 --- a/infrastructure/network/rpc/model/CONTRIBUTORS +++ /dev/null @@ -1,16 +0,0 @@ -# This is the list of people who have contributed code to the repository. -# -# Names should be added to this file only after verifying that the individual -# or the individual's organization has agreed to the LICENSE. -# -# Names should be added to this file like so: -# Name - -John C. Vernaleo -Dave Collins -Owain G. Ainsworth -David Hill -Josh Rickmar -Andreas Metsälä -Francis Lam -Geert-Johan Riemer diff --git a/infrastructure/network/rpc/model/README.md b/infrastructure/network/rpc/model/README.md deleted file mode 100644 index 9177654ba..000000000 --- a/infrastructure/network/rpc/model/README.md +++ /dev/null @@ -1,34 +0,0 @@ -rpcmodel -======= - -[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](https://choosealicense.com/licenses/isc/) -[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/kaspanet/kaspad/rpcmodel) - -Package rpcmodel implements concrete types for marshalling to and from the -kaspa JSON-RPC API. A comprehensive suite of tests is provided to ensure -proper functionality. - -Note that although it's possible to use this package directly to implement an -RPC client, it is not recommended since it is only intended as an infrastructure -package. Instead, RPC clients should use the rpcclient package which provides -a full blown RPC client with many features such as automatic connection -management, websocket support, automatic notification re-registration on -reconnect, and conversion from the raw underlying RPC types (strings, floats, -ints, etc) to higher-level types with many nice and useful properties. - -## Examples - -* [Marshal Command](http://godoc.org/github.com/kaspanet/kaspad/rpcmodel#example-MarshalCmd) - Demonstrates how to create and marshal a command into a JSON-RPC request. - -* [Unmarshal Command](http://godoc.org/github.com/kaspanet/kaspad/rpcmodel#example-UnmarshalCmd) - Demonstrates how to unmarshal a JSON-RPC request and then unmarshal the - concrete request into a concrete command. - -* [Marshal Response](http://godoc.org/github.com/kaspanet/kaspad/rpcmodel#example-MarshalResponse) - Demonstrates how to marshal a JSON-RPC response. - -* [Unmarshal Response](http://godoc.org/github.com/kaspanet/kaspad/rpcmodel#example-package--UnmarshalResponse) - Demonstrates how to unmarshal a JSON-RPC response and then unmarshal the - result field in the response to a concrete type. - diff --git a/infrastructure/network/rpc/model/command_info.go b/infrastructure/network/rpc/model/command_info.go deleted file mode 100644 index f7e442a81..000000000 --- a/infrastructure/network/rpc/model/command_info.go +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright (c) 2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model - -import ( - "fmt" - "reflect" - "strings" -) - -func concreteTypeToMethodWithRLock(rt reflect.Type) (string, bool) { - methodsLock.RLock() - defer methodsLock.RUnlock() - method, ok := concreteTypeToMethod[rt] - return method, ok -} - -func methodToInfoWithRLock(method string) (methodInfo, bool) { - methodsLock.RLock() - defer methodsLock.RUnlock() - info, ok := methodToInfo[method] - return info, ok -} - -func methodConcreteTypeAndInfoWithRLock(method string) (reflect.Type, methodInfo, bool) { - methodsLock.RLock() - defer methodsLock.RUnlock() - rtp, ok := methodToConcreteType[method] - if !ok { - return nil, methodInfo{}, false - } - info := methodToInfo[method] - return rtp, info, ok -} - -// CommandMethod returns the method for the passed command. The provided command -// type must be a registered type. All commands provided by this package are -// registered by default. -func CommandMethod(cmd interface{}) (string, error) { - // Look up the cmd type and error out if not registered. - rt := reflect.TypeOf(cmd) - method, ok := concreteTypeToMethodWithRLock(rt) - if !ok { - str := fmt.Sprintf("%q is not registered", method) - return "", makeError(ErrUnregisteredMethod, str) - } - - return method, nil -} - -// MethodUsageFlags returns the usage flags for the passed command method. The -// provided method must be associated with a registered type. All commands -// provided by this package are registered by default. -func MethodUsageFlags(method string) (UsageFlag, error) { - // Look up details about the provided method and error out if not - // registered. - info, ok := methodToInfoWithRLock(method) - if !ok { - str := fmt.Sprintf("%q is not registered", method) - return 0, makeError(ErrUnregisteredMethod, str) - } - - return info.flags, nil -} - -// subStructUsage returns a string for use in the one-line usage for the given -// sub struct. Note that this is specifically for fields which consist of -// structs (or an array/slice of structs) as opposed to the top-level command -// struct. -// -// Any fields that include a jsonrpcusage struct tag will use that instead of -// being automatically generated. -func subStructUsage(structType reflect.Type) string { - numFields := structType.NumField() - fieldUsages := make([]string, 0, numFields) - for i := 0; i < structType.NumField(); i++ { - rtf := structType.Field(i) - - // When the field has a jsonrpcusage struct tag specified use - // that instead of automatically generating it. - if tag := rtf.Tag.Get("jsonrpcusage"); tag != "" { - fieldUsages = append(fieldUsages, tag) - continue - } - - // Create the name/value entry for the field while considering - // the type of the field. Not all possible types are covered - // here and when one of the types not specifically covered is - // encountered, the field name is simply reused for the value. - fieldName := strings.ToLower(rtf.Name) - fieldValue := fieldName - fieldKind := rtf.Type.Kind() - switch { - case isNumeric(fieldKind): - if fieldKind == reflect.Float32 || fieldKind == reflect.Float64 { - fieldValue = "n.nnn" - } else { - fieldValue = "n" - } - case fieldKind == reflect.String: - fieldValue = `"value"` - - case fieldKind == reflect.Struct: - fieldValue = subStructUsage(rtf.Type) - - case fieldKind == reflect.Array || fieldKind == reflect.Slice: - fieldValue = subArrayUsage(rtf.Type, fieldName) - } - - usage := fmt.Sprintf("%q:%s", fieldName, fieldValue) - fieldUsages = append(fieldUsages, usage) - } - - return fmt.Sprintf("{%s}", strings.Join(fieldUsages, ",")) -} - -// subArrayUsage returns a string for use in the one-line usage for the given -// array or slice. It also contains logic to convert plural field names to -// singular so the generated usage string reads better. -func subArrayUsage(arrayType reflect.Type, fieldName string) string { - // Convert plural field names to singular. Only works for English. - singularFieldName := fieldName - if strings.HasSuffix(fieldName, "ies") { - singularFieldName = strings.TrimSuffix(fieldName, "ies") - singularFieldName = singularFieldName + "y" - } else if strings.HasSuffix(fieldName, "es") { - singularFieldName = strings.TrimSuffix(fieldName, "es") - } else if strings.HasSuffix(fieldName, "s") { - singularFieldName = strings.TrimSuffix(fieldName, "s") - } - - elemType := arrayType.Elem() - switch elemType.Kind() { - case reflect.String: - return fmt.Sprintf("[%q,...]", singularFieldName) - - case reflect.Struct: - return fmt.Sprintf("[%s,...]", subStructUsage(elemType)) - } - - // Fall back to simply showing the field name in array syntax. - return fmt.Sprintf(`[%s,...]`, singularFieldName) -} - -// fieldUsage returns a string for use in the one-line usage for the struct -// field of a command. -// -// Any fields that include a jsonrpcusage struct tag will use that instead of -// being automatically generated. -func fieldUsage(structField reflect.StructField, defaultVal *reflect.Value) string { - // When the field has a jsonrpcusage struct tag specified use that - // instead of automatically generating it. - if tag := structField.Tag.Get("jsonrpcusage"); tag != "" { - return tag - } - - // Indirect the pointer if needed. - fieldType := structField.Type - if fieldType.Kind() == reflect.Ptr { - fieldType = fieldType.Elem() - } - - // When there is a default value, it must also be a pointer due to the - // rules enforced by RegisterCmd. - if defaultVal != nil { - indirect := defaultVal.Elem() - defaultVal = &indirect - } - - // Handle certain types uniquely to provide nicer usage. - fieldName := strings.ToLower(structField.Name) - switch fieldType.Kind() { - case reflect.String: - if defaultVal != nil { - return fmt.Sprintf("%s=%q", fieldName, - defaultVal.Interface()) - } - - return fmt.Sprintf("%q", fieldName) - - case reflect.Array, reflect.Slice: - return subArrayUsage(fieldType, fieldName) - - case reflect.Struct: - return subStructUsage(fieldType) - } - - // Simply return the field name when none of the above special cases - // apply. - if defaultVal != nil { - return fmt.Sprintf("%s=%v", fieldName, defaultVal.Interface()) - } - return fieldName -} - -// methodUsageText returns a one-line usage string for the provided command and -// method info. This is the main work horse for the exported MethodUsageText -// function. -func methodUsageText(rtp reflect.Type, defaults map[int]reflect.Value, method string) string { - // Generate the individual usage for each field in the command. Several - // simplifying assumptions are made here because the RegisterCmd - // function has already rigorously enforced the layout. - rt := rtp.Elem() - numFields := rt.NumField() - reqFieldUsages := make([]string, 0, numFields) - optFieldUsages := make([]string, 0, numFields) - for i := 0; i < numFields; i++ { - rtf := rt.Field(i) - var isOptional bool - if kind := rtf.Type.Kind(); kind == reflect.Ptr { - isOptional = true - } - - var defaultVal *reflect.Value - if defVal, ok := defaults[i]; ok { - defaultVal = &defVal - } - - // Add human-readable usage to the appropriate slice that is - // later used to generate the one-line usage. - usage := fieldUsage(rtf, defaultVal) - if isOptional { - optFieldUsages = append(optFieldUsages, usage) - } else { - reqFieldUsages = append(reqFieldUsages, usage) - } - } - - // Generate and return the one-line usage string. - usageStr := method - if len(reqFieldUsages) > 0 { - usageStr += " " + strings.Join(reqFieldUsages, " ") - } - if len(optFieldUsages) > 0 { - usageStr += fmt.Sprintf(" (%s)", strings.Join(optFieldUsages, " ")) - } - return usageStr -} - -// MethodUsageText returns a one-line usage string for the provided method. The -// provided method must be associated with a registered type. All commands -// provided by this package are registered by default. -func MethodUsageText(method string) (string, error) { - // Look up details about the provided method and error out if not - // registered. - methodsLock.Lock() // Take write lock to protect methodToInfo from concurrent read and write - defer methodsLock.Unlock() - - rtp, ok := methodToConcreteType[method] - info := methodToInfo[method] - if !ok { - str := fmt.Sprintf("%q is not registered", method) - return "", makeError(ErrUnregisteredMethod, str) - } - - // When the usage for this method has already been generated, simply - // return it. - if info.usage != "" { - return info.usage, nil - } - - // Generate and store the usage string for future calls and return it. - usage := methodUsageText(rtp, info.defaults, method) - info.usage = usage - methodToInfo[method] = info - return usage, nil -} diff --git a/infrastructure/network/rpc/model/command_info_test.go b/infrastructure/network/rpc/model/command_info_test.go deleted file mode 100644 index 752a9c3c4..000000000 --- a/infrastructure/network/rpc/model/command_info_test.go +++ /dev/null @@ -1,441 +0,0 @@ -// Copyright (c) 2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model_test - -import ( - "github.com/pkg/errors" - "reflect" - "testing" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// TestCommandMethod tests the CommandMethod function to ensure it retunrs the expected -// methods and errors. -func TestCommandMethod(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - cmd interface{} - method string - err error - }{ - { - name: "unregistered type", - cmd: (*int)(nil), - err: model.Error{ErrorCode: model.ErrUnregisteredMethod}, - }, - { - name: "nil pointer of registered type", - cmd: (*model.GetBlockCmd)(nil), - method: "getBlock", - }, - { - name: "nil instance of registered type", - cmd: &model.GetBlockCountCmd{}, - method: "getBlockCount", - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - method, err := model.CommandMethod(test.cmd) - if reflect.TypeOf(err) != reflect.TypeOf(test.err) { - t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+ - "want %T", i, test.name, err, test.err) - continue - } - if err != nil { - var gotRPCModelErr model.Error - errors.As(err, &gotRPCModelErr) - gotErrorCode := gotRPCModelErr.ErrorCode - var testRPCModelErr model.Error - errors.As(err, &testRPCModelErr) - testErrorCode := testRPCModelErr.ErrorCode - if gotErrorCode != testErrorCode { - t.Errorf("Test #%d (%s) mismatched error code "+ - "- got %v (%v), want %v", i, test.name, - gotErrorCode, err, - testErrorCode) - continue - } - - continue - } - - // Ensure method matches the expected value. - if method != test.method { - t.Errorf("Test #%d (%s) mismatched method - got %v, "+ - "want %v", i, test.name, method, test.method) - continue - } - } -} - -// TestMethodUsageFlags tests the MethodUsage function ensure it returns the -// expected flags and errors. -func TestMethodUsageFlags(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - method string - err error - flags model.UsageFlag - }{ - { - name: "unregistered type", - method: "bogusMethod", - err: model.Error{ErrorCode: model.ErrUnregisteredMethod}, - }, - { - name: "getBlock", - method: "getBlock", - flags: 0, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - flags, err := model.MethodUsageFlags(test.method) - if reflect.TypeOf(err) != reflect.TypeOf(test.err) { - t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+ - "want %T", i, test.name, err, test.err) - continue - } - if err != nil { - var gotRPCModelErr model.Error - errors.As(err, &gotRPCModelErr) - gotErrorCode := gotRPCModelErr.ErrorCode - var testRPCModelErr model.Error - errors.As(err, &testRPCModelErr) - testErrorCode := testRPCModelErr.ErrorCode - if gotErrorCode != testErrorCode { - t.Errorf("Test #%d (%s) mismatched error code "+ - "- got %v (%v), want %v", i, test.name, - gotErrorCode, err, - testErrorCode) - continue - } - - continue - } - - // Ensure flags match the expected value. - if flags != test.flags { - t.Errorf("Test #%d (%s) mismatched flags - got %v, "+ - "want %v", i, test.name, flags, test.flags) - continue - } - } -} - -// TestMethodUsageText tests the MethodUsageText function ensure it returns the -// expected text. -func TestMethodUsageText(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - method string - err error - expected string - }{ - { - name: "unregistered type", - method: "bogusMethod", - err: model.Error{ErrorCode: model.ErrUnregisteredMethod}, - }, - { - name: "getBlockCount", - method: "getBlockCount", - expected: "getBlockCount", - }, - { - name: "getBlock", - method: "getBlock", - expected: `getBlock "hash" (verbose=true verbosetx=false "subnetwork")`, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - usage, err := model.MethodUsageText(test.method) - if reflect.TypeOf(err) != reflect.TypeOf(test.err) { - t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+ - "want %T", i, test.name, err, test.err) - continue - } - if err != nil { - var gotRPCModelErr model.Error - errors.As(err, &gotRPCModelErr) - gotErrorCode := gotRPCModelErr.ErrorCode - var testRPCModelErr model.Error - errors.As(err, &testRPCModelErr) - testErrorCode := testRPCModelErr.ErrorCode - if gotErrorCode != testErrorCode { - t.Errorf("Test #%d (%s) mismatched error code "+ - "- got %v (%v), want %v", i, test.name, - gotErrorCode, err, - testErrorCode) - continue - } - - continue - } - - // Ensure usage matches the expected value. - if usage != test.expected { - t.Errorf("Test #%d (%s) mismatched usage - got %v, "+ - "want %v", i, test.name, usage, test.expected) - continue - } - - // Get the usage again to exercise caching. - usage, err = model.MethodUsageText(test.method) - if err != nil { - t.Errorf("Test #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - - // Ensure usage still matches the expected value. - if usage != test.expected { - t.Errorf("Test #%d (%s) mismatched usage - got %v, "+ - "want %v", i, test.name, usage, test.expected) - continue - } - } -} - -// TestFieldUsage tests the internal fieldUsage function ensure it returns the -// expected text. -func TestFieldUsage(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - field reflect.StructField - defValue *reflect.Value - expected string - }{ - { - name: "jsonrpcusage tag override", - field: func() reflect.StructField { - type s struct { - Test int `jsonrpcusage:"testvalue"` - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: "testvalue", - }, - { - name: "generic interface", - field: func() reflect.StructField { - type s struct { - Test interface{} - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `test`, - }, - { - name: "string without default value", - field: func() reflect.StructField { - type s struct { - Test string - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `"test"`, - }, - { - name: "string with default value", - field: func() reflect.StructField { - type s struct { - Test string - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: func() *reflect.Value { - value := "default" - rv := reflect.ValueOf(&value) - return &rv - }(), - expected: `test="default"`, - }, - { - name: "array of strings", - field: func() reflect.StructField { - type s struct { - Test []string - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `["test",...]`, - }, - { - name: "array of strings with plural field name 1", - field: func() reflect.StructField { - type s struct { - Keys []string - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `["key",...]`, - }, - { - name: "array of strings with plural field name 2", - field: func() reflect.StructField { - type s struct { - Addresses []string - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `["address",...]`, - }, - { - name: "array of strings with plural field name 3", - field: func() reflect.StructField { - type s struct { - Capabilities []string - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `["capability",...]`, - }, - { - name: "array of structs", - field: func() reflect.StructField { - type s2 struct { - TxID string - } - type s struct { - Capabilities []s2 - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `[{"txid":"value"},...]`, - }, - { - name: "array of ints", - field: func() reflect.StructField { - type s struct { - Test []int - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `[test,...]`, - }, - { - name: "sub struct with jsonrpcusage tag override", - field: func() reflect.StructField { - type s2 struct { - Test string `jsonrpcusage:"testusage"` - } - type s struct { - Test s2 - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `{testusage}`, - }, - { - name: "sub struct with string", - field: func() reflect.StructField { - type s2 struct { - TxID string - } - type s struct { - Test s2 - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `{"txid":"value"}`, - }, - { - name: "sub struct with int", - field: func() reflect.StructField { - type s2 struct { - Vout int - } - type s struct { - Test s2 - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `{"vout":n}`, - }, - { - name: "sub struct with float", - field: func() reflect.StructField { - type s2 struct { - Amount float64 - } - type s struct { - Test s2 - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `{"amount":n.nnn}`, - }, - { - name: "sub struct with sub struct", - field: func() reflect.StructField { - type s3 struct { - Amount float64 - } - type s2 struct { - Template s3 - } - type s struct { - Test s2 - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `{"template":{"amount":n.nnn}}`, - }, - { - name: "sub struct with slice", - field: func() reflect.StructField { - type s2 struct { - Capabilities []string - } - type s struct { - Test s2 - } - return reflect.TypeOf((*s)(nil)).Elem().Field(0) - }(), - defValue: nil, - expected: `{"capabilities":["capability",...]}`, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - // Ensure usage matches the expected value. - usage := model.TstFieldUsage(test.field, test.defValue) - if usage != test.expected { - t.Errorf("Test #%d (%s) mismatched usage - got %v, "+ - "want %v", i, test.name, usage, test.expected) - continue - } - } -} diff --git a/infrastructure/network/rpc/model/command_parse.go b/infrastructure/network/rpc/model/command_parse.go deleted file mode 100644 index 6fe148681..000000000 --- a/infrastructure/network/rpc/model/command_parse.go +++ /dev/null @@ -1,544 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model - -import ( - "encoding/json" - "fmt" - "github.com/pkg/errors" - "reflect" - "strconv" - "strings" -) - -// makeParams creates a slice of interface values for the given struct. -func makeParams(rt reflect.Type, rv reflect.Value) []interface{} { - numFields := rt.NumField() - params := make([]interface{}, 0, numFields) - for i := 0; i < numFields; i++ { - rtf := rt.Field(i) - rvf := rv.Field(i) - if rtf.Type.Kind() == reflect.Ptr { - if rvf.IsNil() { - break - } - rvf.Elem() - } - params = append(params, rvf.Interface()) - } - - return params -} - -// MarshalCommand marshals the passed command to a JSON-RPC request byte slice that -// is suitable for transmission to an RPC server. The provided command type -// must be a registered type. All commands provided by this package are -// registered by default. -func MarshalCommand(id interface{}, cmd interface{}) ([]byte, error) { - // Look up the cmd type and error out if not registered. - rt := reflect.TypeOf(cmd) - method, ok := concreteTypeToMethodWithRLock(rt) - if !ok { - str := fmt.Sprintf("%q is not registered", method) - return nil, makeError(ErrUnregisteredMethod, str) - } - - // The provided command must not be nil. - rv := reflect.ValueOf(cmd) - if rv.IsNil() { - str := "the specified command is nil" - return nil, makeError(ErrInvalidType, str) - } - - // Create a slice of interface values in the order of the struct fields - // while respecting pointer fields as optional params and only adding - // them if they are non-nil. - params := makeParams(rt.Elem(), rv.Elem()) - - // Generate and marshal the final JSON-RPC request. - rawCmd, err := NewRequest(id, method, params) - if err != nil { - return nil, err - } - return json.Marshal(rawCmd) -} - -// checkNumParams ensures the supplied number of params is at least the minimum -// required number for the command and less than the maximum allowed. -func checkNumParams(numParams int, info *methodInfo) error { - if numParams < info.numReqParams || numParams > info.maxParams { - if info.numReqParams == info.maxParams { - str := fmt.Sprintf("wrong number of params (expected "+ - "%d, received %d)", info.numReqParams, - numParams) - return makeError(ErrNumParams, str) - } - - str := fmt.Sprintf("wrong number of params (expected "+ - "between %d and %d, received %d)", info.numReqParams, - info.maxParams, numParams) - return makeError(ErrNumParams, str) - } - - return nil -} - -// populateDefaults populates default values into any remaining optional struct -// fields that did not have parameters explicitly provided. The caller should -// have previously checked that the number of parameters being passed is at -// least the required number of parameters to avoid unnecessary work in this -// function, but since required fields never have default values, it will work -// properly even without the check. -func populateDefaults(numParams int, info *methodInfo, rv reflect.Value) { - // When there are no more parameters left in the supplied parameters, - // any remaining struct fields must be optional. Thus, populate them - // with their associated default value as needed. - for i := numParams; i < info.maxParams; i++ { - rvf := rv.Field(i) - if defaultVal, ok := info.defaults[i]; ok { - rvf.Set(defaultVal) - } - } -} - -// UnmarshalCommand unmarshals a JSON-RPC request into a suitable concrete command -// so long as the method type contained within the marshalled request is -// registered. -func UnmarshalCommand(r *Request) (interface{}, error) { - rtp, info, ok := methodConcreteTypeAndInfoWithRLock(r.Method) - if !ok { - str := fmt.Sprintf("%q is not registered", r.Method) - return nil, makeError(ErrUnregisteredMethod, str) - } - rt := rtp.Elem() - rvp := reflect.New(rt) - rv := rvp.Elem() - - // Ensure the number of parameters are correct. - numParams := len(r.Params) - if err := checkNumParams(numParams, &info); err != nil { - return nil, err - } - - // Loop through each of the struct fields and unmarshal the associated - // parameter into them. - for i := 0; i < numParams; i++ { - rvf := rv.Field(i) - // Unmarshal the parameter into the struct field. - concreteVal := rvf.Addr().Interface() - if err := json.Unmarshal(r.Params[i], &concreteVal); err != nil { - // The most common error is the wrong type, so - // explicitly detect that error and make it nicer. - fieldName := strings.ToLower(rt.Field(i).Name) - var jsonErr *json.UnmarshalTypeError - if ok := errors.As(err, &jsonErr); ok { - str := fmt.Sprintf("parameter #%d '%s' must "+ - "be type %s (got %s)", i+1, fieldName, - jsonErr.Type, jsonErr.Value) - return nil, makeError(ErrInvalidType, str) - } - - // Fallback to showing the underlying error. - str := fmt.Sprintf("parameter #%d '%s' failed to "+ - "unmarshal: %s", i+1, fieldName, err) - return nil, makeError(ErrInvalidType, str) - } - } - - // When there are less supplied parameters than the total number of - // params, any remaining struct fields must be optional. Thus, populate - // them with their associated default value as needed. - if numParams < info.maxParams { - populateDefaults(numParams, &info, rv) - } - - return rvp.Interface(), nil -} - -// isNumeric returns whether the passed reflect kind is a signed or unsigned -// integer of any magnitude or a float of any magnitude. -func isNumeric(kind reflect.Kind) bool { - switch kind { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, - reflect.Uint64, reflect.Float32, reflect.Float64: - - return true - } - - return false -} - -// typesMaybeCompatible returns whether the source type can possibly be -// assigned to the destination type. This is intended as a relatively quick -// check to weed out obviously invalid conversions. -func typesMaybeCompatible(dest reflect.Type, src reflect.Type) bool { - // The same types are obviously compatible. - if dest == src { - return true - } - - // When both types are numeric, they are potentially compatible. - srcKind := src.Kind() - destKind := dest.Kind() - if isNumeric(destKind) && isNumeric(srcKind) { - return true - } - - if srcKind == reflect.String { - // Strings can potentially be converted to numeric types. - if isNumeric(destKind) { - return true - } - - switch destKind { - // Strings can potentially be converted to bools by - // strconv.ParseBool. - case reflect.Bool: - return true - - // Strings can be converted to any other type which has as - // underlying type of string. - case reflect.String: - return true - - // Strings can potentially be converted to arrays, slice, - // structs, and maps via json.Unmarshal. - case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map: - return true - } - } - - return false -} - -// baseType returns the type of the argument after indirecting through all -// pointers along with how many indirections were necessary. -func baseType(arg reflect.Type) (reflect.Type, int) { - var numIndirects int - for arg.Kind() == reflect.Ptr { - arg = arg.Elem() - numIndirects++ - } - return arg, numIndirects -} - -// assignField is the main workhorse for the NewCommand function which handles -// assigning the provided source value to the destination field. It supports -// direct type assignments, indirection, conversion of numeric types, and -// unmarshaling of strings into arrays, slices, structs, and maps via -// json.Unmarshal. -func assignField(paramNum int, fieldName string, dest reflect.Value, src reflect.Value) error { - // Just error now when the types have no chance of being compatible. - destBaseType, destIndirects := baseType(dest.Type()) - srcBaseType, srcIndirects := baseType(src.Type()) - if !typesMaybeCompatible(destBaseType, srcBaseType) { - str := fmt.Sprintf("parameter #%d '%s' must be type %s (got "+ - "%s)", paramNum, fieldName, destBaseType, srcBaseType) - return makeError(ErrInvalidType, str) - } - - // Check if it's possible to simply set the dest to the provided source. - // This is the case when the base types are the same or they are both - // pointers that can be indirected to be the same without needing to - // create pointers for the destination field. - if destBaseType == srcBaseType && srcIndirects >= destIndirects { - for i := 0; i < srcIndirects-destIndirects; i++ { - src = src.Elem() - } - dest.Set(src) - return nil - } - - // When the destination has more indirects than the source, the extra - // pointers have to be created. Only create enough pointers to reach - // the same level of indirection as the source so the dest can simply be - // set to the provided source when the types are the same. - destIndirectsRemaining := destIndirects - if destIndirects > srcIndirects { - indirectDiff := destIndirects - srcIndirects - for i := 0; i < indirectDiff; i++ { - dest.Set(reflect.New(dest.Type().Elem())) - dest = dest.Elem() - destIndirectsRemaining-- - } - } - - if destBaseType == srcBaseType { - dest.Set(src) - return nil - } - - // Make any remaining pointers needed to get to the base dest type since - // the above direct assign was not possible and conversions are done - // against the base types. - for i := 0; i < destIndirectsRemaining; i++ { - dest.Set(reflect.New(dest.Type().Elem())) - dest = dest.Elem() - } - - // Indirect through to the base source value. - for src.Kind() == reflect.Ptr { - src = src.Elem() - } - - // Perform supported type conversions. - switch src.Kind() { - // Source value is a signed integer of various magnitude. - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, - reflect.Int64: - - switch dest.Kind() { - // Destination is a signed integer of various magnitude. - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, - reflect.Int64: - - srcInt := src.Int() - if dest.OverflowInt(srcInt) { - str := fmt.Sprintf("parameter #%d '%s' "+ - "overflows destination type %s", - paramNum, fieldName, destBaseType) - return makeError(ErrInvalidType, str) - } - - dest.SetInt(srcInt) - - // Destination is an unsigned integer of various magnitude. - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, - reflect.Uint64: - - srcInt := src.Int() - if srcInt < 0 || dest.OverflowUint(uint64(srcInt)) { - str := fmt.Sprintf("parameter #%d '%s' "+ - "overflows destination type %s", - paramNum, fieldName, destBaseType) - return makeError(ErrInvalidType, str) - } - dest.SetUint(uint64(srcInt)) - - default: - str := fmt.Sprintf("parameter #%d '%s' must be type "+ - "%s (got %s)", paramNum, fieldName, destBaseType, - srcBaseType) - return makeError(ErrInvalidType, str) - } - - // Source value is an unsigned integer of various magnitude. - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, - reflect.Uint64: - - switch dest.Kind() { - // Destination is a signed integer of various magnitude. - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, - reflect.Int64: - - srcUint := src.Uint() - if srcUint > uint64(1<<63)-1 { - str := fmt.Sprintf("parameter #%d '%s' "+ - "overflows destination type %s", - paramNum, fieldName, destBaseType) - return makeError(ErrInvalidType, str) - } - if dest.OverflowInt(int64(srcUint)) { - str := fmt.Sprintf("parameter #%d '%s' "+ - "overflows destination type %s", - paramNum, fieldName, destBaseType) - return makeError(ErrInvalidType, str) - } - dest.SetInt(int64(srcUint)) - - // Destination is an unsigned integer of various magnitude. - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, - reflect.Uint64: - - srcUint := src.Uint() - if dest.OverflowUint(srcUint) { - str := fmt.Sprintf("parameter #%d '%s' "+ - "overflows destination type %s", - paramNum, fieldName, destBaseType) - return makeError(ErrInvalidType, str) - } - dest.SetUint(srcUint) - - default: - str := fmt.Sprintf("parameter #%d '%s' must be type "+ - "%s (got %s)", paramNum, fieldName, destBaseType, - srcBaseType) - return makeError(ErrInvalidType, str) - } - - // Source value is a float. - case reflect.Float32, reflect.Float64: - destKind := dest.Kind() - if destKind != reflect.Float32 && destKind != reflect.Float64 { - str := fmt.Sprintf("parameter #%d '%s' must be type "+ - "%s (got %s)", paramNum, fieldName, destBaseType, - srcBaseType) - return makeError(ErrInvalidType, str) - } - - srcFloat := src.Float() - if dest.OverflowFloat(srcFloat) { - str := fmt.Sprintf("parameter #%d '%s' overflows "+ - "destination type %s", paramNum, fieldName, - destBaseType) - return makeError(ErrInvalidType, str) - } - dest.SetFloat(srcFloat) - - // Source value is a string. - case reflect.String: - switch dest.Kind() { - // String -> bool - case reflect.Bool: - b, err := strconv.ParseBool(src.String()) - if err != nil { - str := fmt.Sprintf("parameter #%d '%s' must "+ - "parse to a %s", paramNum, fieldName, - destBaseType) - return makeError(ErrInvalidType, str) - } - dest.SetBool(b) - - // String -> signed integer of varying size. - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, - reflect.Int64: - - srcInt, err := strconv.ParseInt(src.String(), 0, 0) - if err != nil { - str := fmt.Sprintf("parameter #%d '%s' must "+ - "parse to a %s", paramNum, fieldName, - destBaseType) - return makeError(ErrInvalidType, str) - } - if dest.OverflowInt(srcInt) { - str := fmt.Sprintf("parameter #%d '%s' "+ - "overflows destination type %s", - paramNum, fieldName, destBaseType) - return makeError(ErrInvalidType, str) - } - dest.SetInt(srcInt) - - // String -> unsigned integer of varying size. - case reflect.Uint, reflect.Uint8, reflect.Uint16, - reflect.Uint32, reflect.Uint64: - - srcUint, err := strconv.ParseUint(src.String(), 0, 0) - if err != nil { - str := fmt.Sprintf("parameter #%d '%s' must "+ - "parse to a %s", paramNum, fieldName, - destBaseType) - return makeError(ErrInvalidType, str) - } - if dest.OverflowUint(srcUint) { - str := fmt.Sprintf("parameter #%d '%s' "+ - "overflows destination type %s", - paramNum, fieldName, destBaseType) - return makeError(ErrInvalidType, str) - } - dest.SetUint(srcUint) - - // String -> float of varying size. - case reflect.Float32, reflect.Float64: - srcFloat, err := strconv.ParseFloat(src.String(), 0) - if err != nil { - str := fmt.Sprintf("parameter #%d '%s' must "+ - "parse to a %s", paramNum, fieldName, - destBaseType) - return makeError(ErrInvalidType, str) - } - if dest.OverflowFloat(srcFloat) { - str := fmt.Sprintf("parameter #%d '%s' "+ - "overflows destination type %s", - paramNum, fieldName, destBaseType) - return makeError(ErrInvalidType, str) - } - dest.SetFloat(srcFloat) - - // String -> string (typecast). - case reflect.String: - dest.SetString(src.String()) - - // String -> arrays, slices, structs, and maps via - // json.Unmarshal. - case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map: - concreteVal := dest.Addr().Interface() - err := json.Unmarshal([]byte(src.String()), &concreteVal) - if err != nil { - str := fmt.Sprintf("parameter #%d '%s' must "+ - "be valid JSON which unsmarshals to a %s", - paramNum, fieldName, destBaseType) - return makeError(ErrInvalidType, str) - } - dest.Set(reflect.ValueOf(concreteVal).Elem()) - } - } - - return nil -} - -// NewCommand provides a generic mechanism to create a new command that can marshal -// to a JSON-RPC request while respecting the requirements of the provided -// method. The method must have been registered with the package already along -// with its type definition. All methods associated with the commands exported -// by this package are already registered by default. -// -// The arguments are most efficient when they are the exact same type as the -// underlying field in the command struct associated with the the method, -// however this function also will perform a variety of conversions to make it -// more flexible. This allows, for example, command line args which are strings -// to be passed unaltered. In particular, the following conversions are -// supported: -// -// - Conversion between any size signed or unsigned integer so long as the -// value does not overflow the destination type -// - Conversion between float32 and float64 so long as the value does not -// overflow the destination type -// - Conversion from string to boolean for everything strconv.ParseBool -// recognizes -// - Conversion from string to any size integer for everything -// strconv.ParseInt and strconv.ParseUint recognizes -// - Conversion from string to any size float for everything -// strconv.ParseFloat recognizes -// - Conversion from string to arrays, slices, structs, and maps by treating -// the string as marshalled JSON and calling json.Unmarshal into the -// destination field -func NewCommand(method string, args ...interface{}) (interface{}, error) { - // Look up details about the provided method. Any methods that aren't - // registered are an error. - rtp, info, ok := methodConcreteTypeAndInfoWithRLock(method) - if !ok { - str := fmt.Sprintf("%q is not registered", method) - return nil, makeError(ErrUnregisteredMethod, str) - } - - // Ensure the number of parameters are correct. - numParams := len(args) - if err := checkNumParams(numParams, &info); err != nil { - return nil, err - } - - // Create the appropriate command type for the method. Since all types - // are enforced to be a pointer to a struct at registration time, it's - // safe to indirect to the struct now. - rvp := reflect.New(rtp.Elem()) - rv := rvp.Elem() - rt := rtp.Elem() - - // Loop through each of the struct fields and assign the associated - // parameter into them after checking its type validity. - for i := 0; i < numParams; i++ { - // Attempt to assign each of the arguments to the according - // struct field. - rvf := rv.Field(i) - fieldName := strings.ToLower(rt.Field(i).Name) - err := assignField(i+1, fieldName, rvf, reflect.ValueOf(args[i])) - if err != nil { - return nil, err - } - } - - return rvp.Interface(), nil -} diff --git a/infrastructure/network/rpc/model/command_parse_test.go b/infrastructure/network/rpc/model/command_parse_test.go deleted file mode 100644 index b58d17b17..000000000 --- a/infrastructure/network/rpc/model/command_parse_test.go +++ /dev/null @@ -1,528 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model_test - -import ( - "encoding/json" - "github.com/pkg/errors" - "math" - "reflect" - "testing" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// TestAssignField tests the assignField function handles supported combinations -// properly. -func TestAssignField(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - dest interface{} - src interface{} - expected interface{} - }{ - { - name: "same types", - dest: int8(0), - src: int8(100), - expected: int8(100), - }, - { - name: "same types - more source pointers", - dest: int8(0), - src: func() interface{} { - i := int8(100) - return &i - }(), - expected: int8(100), - }, - { - name: "same types - more dest pointers", - dest: func() interface{} { - i := int8(0) - return &i - }(), - src: int8(100), - expected: int8(100), - }, - { - name: "convertible types - more source pointers", - dest: int16(0), - src: func() interface{} { - i := int8(100) - return &i - }(), - expected: int16(100), - }, - { - name: "convertible types - both pointers", - dest: func() interface{} { - i := int8(0) - return &i - }(), - src: func() interface{} { - i := int16(100) - return &i - }(), - expected: int8(100), - }, - { - name: "convertible types - int16 -> int8", - dest: int8(0), - src: int16(100), - expected: int8(100), - }, - { - name: "convertible types - int16 -> uint8", - dest: uint8(0), - src: int16(100), - expected: uint8(100), - }, - { - name: "convertible types - uint16 -> int8", - dest: int8(0), - src: uint16(100), - expected: int8(100), - }, - { - name: "convertible types - uint16 -> uint8", - dest: uint8(0), - src: uint16(100), - expected: uint8(100), - }, - { - name: "convertible types - float32 -> float64", - dest: float64(0), - src: float32(1.5), - expected: float64(1.5), - }, - { - name: "convertible types - float64 -> float32", - dest: float32(0), - src: float64(1.5), - expected: float32(1.5), - }, - { - name: "convertible types - string -> bool", - dest: false, - src: "true", - expected: true, - }, - { - name: "convertible types - string -> int8", - dest: int8(0), - src: "100", - expected: int8(100), - }, - { - name: "convertible types - string -> uint8", - dest: uint8(0), - src: "100", - expected: uint8(100), - }, - { - name: "convertible types - string -> float32", - dest: float32(0), - src: "1.5", - expected: float32(1.5), - }, - { - name: "convertible types - typecase string -> string", - dest: "", - src: func() interface{} { - type foo string - return foo("foo") - }(), - expected: "foo", - }, - { - name: "convertible types - string -> array", - dest: [2]string{}, - src: `["test","test2"]`, - expected: [2]string{"test", "test2"}, - }, - { - name: "convertible types - string -> slice", - dest: []string{}, - src: `["test","test2"]`, - expected: []string{"test", "test2"}, - }, - { - name: "convertible types - string -> struct", - dest: struct{ A int }{}, - src: `{"A":100}`, - expected: struct{ A int }{100}, - }, - { - name: "convertible types - string -> map", - dest: map[string]float64{}, - src: `{"1Address":1.5}`, - expected: map[string]float64{"1Address": 1.5}, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - dst := reflect.New(reflect.TypeOf(test.dest)).Elem() - src := reflect.ValueOf(test.src) - err := model.TstAssignField(1, "testField", dst, src) - if err != nil { - t.Errorf("Test #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - - // Inidirect through to the base types to ensure their values - // are the same. - for dst.Kind() == reflect.Ptr { - dst = dst.Elem() - } - if !reflect.DeepEqual(dst.Interface(), test.expected) { - t.Errorf("Test #%d (%s) unexpected value - got %v, "+ - "want %v", i, test.name, dst.Interface(), - test.expected) - continue - } - } -} - -// TestAssignFieldErrors tests the assignField function error paths. -func TestAssignFieldErrors(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - dest interface{} - src interface{} - err model.Error - }{ - { - name: "general incompatible int -> string", - dest: string(0), - src: int(0), - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "overflow source int -> dest int", - dest: int8(0), - src: int(128), - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "overflow source int -> dest uint", - dest: uint8(0), - src: int(256), - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "int -> float", - dest: float32(0), - src: int(256), - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "overflow source uint64 -> dest int64", - dest: int64(0), - src: uint64(1 << 63), - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "overflow source uint -> dest int", - dest: int8(0), - src: uint(128), - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "overflow source uint -> dest uint", - dest: uint8(0), - src: uint(256), - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "uint -> float", - dest: float32(0), - src: uint(256), - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "float -> int", - dest: int(0), - src: float32(1.0), - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "overflow float64 -> float32", - dest: float32(0), - src: float64(math.MaxFloat64), - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "invalid string -> bool", - dest: true, - src: "foo", - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "invalid string -> int", - dest: int8(0), - src: "foo", - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "overflow string -> int", - dest: int8(0), - src: "128", - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "invalid string -> uint", - dest: uint8(0), - src: "foo", - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "overflow string -> uint", - dest: uint8(0), - src: "256", - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "invalid string -> float", - dest: float32(0), - src: "foo", - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "overflow string -> float", - dest: float32(0), - src: "1.7976931348623157e+308", - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "invalid string -> array", - dest: [3]int{}, - src: "foo", - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "invalid string -> slice", - dest: []int{}, - src: "foo", - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "invalid string -> struct", - dest: struct{ A int }{}, - src: "foo", - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "invalid string -> map", - dest: map[string]int{}, - src: "foo", - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - dst := reflect.New(reflect.TypeOf(test.dest)).Elem() - src := reflect.ValueOf(test.src) - err := model.TstAssignField(1, "testField", dst, src) - if reflect.TypeOf(err) != reflect.TypeOf(test.err) { - t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+ - "want %T", i, test.name, err, test.err) - continue - } - var gotRPCModelErr model.Error - errors.As(err, &gotRPCModelErr) - gotErrorCode := gotRPCModelErr.ErrorCode - if gotErrorCode != test.err.ErrorCode { - t.Errorf("Test #%d (%s) mismatched error code - got "+ - "%v (%v), want %v", i, test.name, gotErrorCode, - err, test.err.ErrorCode) - continue - } - } -} - -// TestNewCommandErrors ensures the error paths of NewCommand behave as expected. -func TestNewCommandErrors(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - method string - args []interface{} - err model.Error - }{ - { - name: "unregistered command", - method: "bogusCommand", - args: []interface{}{}, - err: model.Error{ErrorCode: model.ErrUnregisteredMethod}, - }, - { - name: "too few parameters to command with required + optional", - method: "getBlock", - args: []interface{}{}, - err: model.Error{ErrorCode: model.ErrNumParams}, - }, - { - name: "too many parameters to command with no optional", - method: "getBlockCount", - args: []interface{}{"123"}, - err: model.Error{ErrorCode: model.ErrNumParams}, - }, - { - name: "incorrect parameter type", - method: "getBlock", - args: []interface{}{1}, - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - _, err := model.NewCommand(test.method, test.args...) - if reflect.TypeOf(err) != reflect.TypeOf(test.err) { - t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+ - "want %T", i, test.name, err, test.err) - continue - } - var gotRPCModelErr model.Error - errors.As(err, &gotRPCModelErr) - gotErrorCode := gotRPCModelErr.ErrorCode - if gotErrorCode != test.err.ErrorCode { - t.Errorf("Test #%d (%s) mismatched error code - got "+ - "%v (%v), want %v", i, test.name, gotErrorCode, - err, test.err.ErrorCode) - continue - } - } -} - -// TestMarshalCommandErrors tests the error paths of the MarshalCommand function. -func TestMarshalCommandErrors(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - id interface{} - cmd interface{} - err model.Error - }{ - { - name: "unregistered type", - id: 1, - cmd: (*int)(nil), - err: model.Error{ErrorCode: model.ErrUnregisteredMethod}, - }, - { - name: "nil instance of registered type", - id: 1, - cmd: (*model.GetBlockCmd)(nil), - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "nil instance of registered type", - id: []int{0, 1}, - cmd: &model.GetBlockCountCmd{}, - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - _, err := model.MarshalCommand(test.id, test.cmd) - if reflect.TypeOf(err) != reflect.TypeOf(test.err) { - t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+ - "want %T", i, test.name, err, test.err) - continue - } - var gotRPCModelErr model.Error - errors.As(err, &gotRPCModelErr) - gotErrorCode := gotRPCModelErr.ErrorCode - if gotErrorCode != test.err.ErrorCode { - t.Errorf("Test #%d (%s) mismatched error code - got "+ - "%v (%v), want %v", i, test.name, gotErrorCode, - err, test.err.ErrorCode) - continue - } - } -} - -// TestUnmarshalCommandErrors tests the error paths of the UnmarshalCommand function. -func TestUnmarshalCommandErrors(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - request model.Request - err model.Error - }{ - { - name: "unregistered type", - request: model.Request{ - JSONRPC: "1.0", - Method: "bogusMethod", - Params: nil, - ID: nil, - }, - err: model.Error{ErrorCode: model.ErrUnregisteredMethod}, - }, - { - name: "incorrect number of params", - request: model.Request{ - JSONRPC: "1.0", - Method: "getBlockCount", - Params: []json.RawMessage{[]byte(`"bogusparam"`)}, - ID: nil, - }, - err: model.Error{ErrorCode: model.ErrNumParams}, - }, - { - name: "invalid type for a parameter", - request: model.Request{ - JSONRPC: "1.0", - Method: "getBlock", - Params: []json.RawMessage{[]byte("1")}, - ID: nil, - }, - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "invalid JSON for a parameter", - request: model.Request{ - JSONRPC: "1.0", - Method: "getBlock", - Params: []json.RawMessage{[]byte(`"1`)}, - ID: nil, - }, - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - _, err := model.UnmarshalCommand(&test.request) - if reflect.TypeOf(err) != reflect.TypeOf(test.err) { - t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+ - "want %T", i, test.name, err, test.err) - continue - } - var gotRPCModelErr model.Error - errors.As(err, &gotRPCModelErr) - gotErrorCode := gotRPCModelErr.ErrorCode - if gotErrorCode != test.err.ErrorCode { - t.Errorf("Test #%d (%s) mismatched error code - got "+ - "%v (%v), want %v", i, test.name, gotErrorCode, - err, test.err.ErrorCode) - continue - } - } -} diff --git a/infrastructure/network/rpc/model/doc.go b/infrastructure/network/rpc/model/doc.go deleted file mode 100644 index 59761965f..000000000 --- a/infrastructure/network/rpc/model/doc.go +++ /dev/null @@ -1,137 +0,0 @@ -/* -Package model provides primitives for working with the kaspa JSON-RPC API. - -Overview - -When communicating via the JSON-RPC protocol, all of the commands need to be -marshalled to and from the the appmessage in the appropriate format. This package -provides data structures and primitives to ease this process. - -In addition, it also provides some additional features such as custom command -registration, command categorization, and reflection-based help generation. - -JSON-RPC Protocol Overview - -This information is not necessary in order to use this package, but it does -provide some intuition into what the marshalling and unmarshalling that is -discussed below is doing under the hood. - -As defined by the JSON-RPC spec, there are effectively two forms of messages on -the appmessage: - - - Request Objects - {"jsonrpc":"1.0","id":"SOMEID","method":"SOMEMETHOD","params":[SOMEPARAMS]} - NOTE: Notifications are the same format except the id field is null. - - - Response Objects - {"result":SOMETHING,"error":null,"id":"SOMEID"} - {"result":null,"error":{"code":SOMEINT,"message":SOMESTRING},"id":"SOMEID"} - -For requests, the params field can vary in what it contains depending on the -method (a.k.a. command) being sent. Each parameter can be as simple as an int -or a complex structure containing many nested fields. The id field is used to -identify a request and will be included in the associated response. - -When working with asynchronous transports, such as websockets, spontaneous -notifications are also possible. As indicated, they are the same as a request -object, except they have the id field set to null. Therefore, servers will -ignore requests with the id field set to null, while clients can choose to -consume or ignore them. - -Marshalling and Unmarshalling - -Based upon the discussion above, it should be easy to see how the types of this -package map into the required parts of the protocol - - - Request Objects (type Request) - - Commands (type Cmd) - - Notifications (type Ntfn) - - Response Objects (type Response) - - Result (type Result) - -To simplify the marshalling of the requests and responses, the MarshalCommand and -MarshalResponse functions are provided. They return the raw bytes ready to be -sent across the appmessage. - -Unmarshalling a received Request object is a two step process: - 1) Unmarshal the raw bytes into a Request struct instance via json.Unmarshal - 2) Use UnmarshalCommand on the Result field of the unmarshalled Request to create - a concrete command or notification instance with all struct fields set - accordingly - -This approach is used since it provides the caller with access to the additional -fields in the request that are not part of the command such as the ID. - -Unmarshalling a received Response object is also a two step process: - 1) Unmarhsal the raw bytes into a Response struct instance via json.Unmarshal - 2) Depending on the ID, unmarshal the Result field of the unmarshalled - Response to create a concrete type instance - -As above, this approach is used since it provides the caller with access to the -fields in the response such as the ID and Error. - -Command Creation - -This package provides two approaches for creating a new command. This first, -and preferred, method is to use one of the NewCmd functions. This allows -static compile-time checking to help ensure the parameters stay in sync with -the struct definitions. - -The second approach is the NewCommand function which takes a method (command) name -and variable arguments. The function includes full checking to ensure the -parameters are accurate according to provided method, however these checks are, -obviously, run-time which means any mistakes won't be found until the code is -actually executed. However, it is quite useful for user-supplied commands -that are intentionally dynamic. - -Custom Command Registration - -The command handling of this package is built around the concept of registered -commands. This is true for the wide variety of commands already provided by the -package, but it also means caller can easily provide custom commands with all -of the same functionality as the built-in commands. Use the RegisterCmd -function for this purpose. - -A list of all registered methods can be obtained with the RegisteredCmdMethods -function. - -Command Inspection - -All registered commands are registered with flags that identify information such -as whether the command applies to a kaspa rpc server or is a -notification along with the method name to use. These flags can be obtained -with the MethodUsageFlags flags, and the method can be obtained with the -CommandMethod function. - -Help Generation - -To facilitate providing consistent help to users of the RPC server, this package -exposes the GenerateHelp and function which uses reflection on registered -commands or notifications, as well as the provided expected result types, to -generate the final help text. - -In addition, the MethodUsageText function is provided to generate consistent -one-line usage for registered commands and notifications using reflection. - -Errors - -There are 2 distinct type of errors supported by this package: - - - General errors related to marshalling or unmarshalling or improper use of - the package (type Error) - - RPC errors which are intended to be returned across the appmessage as a part of - the JSON-RPC response (type RPCError) - -The first category of errors (type Error) typically indicates a programmer error -and can be avoided by properly using the API. Errors of this type will be -returned from the various functions available in this package. They identify -issues such as unsupported field types, attempts to register malformed commands, -and attempting to create a new command with an improper number of parameters. -The specific reason for the error can be detected by type asserting it to a -*model.Error and accessing the ErrorCode field. - -The second category of errors (type RPCError), on the other hand, are useful for -returning errors to RPC clients. Consequently, they are used in the previously -described Response type. -*/ -package model diff --git a/infrastructure/network/rpc/model/error.go b/infrastructure/network/rpc/model/error.go deleted file mode 100644 index df4910ba5..000000000 --- a/infrastructure/network/rpc/model/error.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model - -import ( - "fmt" -) - -// ErrorCode identifies a kind of error. These error codes are NOT used for -// JSON-RPC response errors. -type ErrorCode int - -// These constants are used to identify a specific RuleError. -const ( - // ErrDuplicateMethod indicates a command with the specified method - // already exists. - ErrDuplicateMethod ErrorCode = iota - - // ErrInvalidUsageFlags indicates one or more unrecognized flag bits - // were specified. - ErrInvalidUsageFlags - - // ErrInvalidType indicates a type was passed that is not the required - // type. - ErrInvalidType - - // ErrEmbeddedType indicates the provided command struct contains an - // embedded type which is not not supported. - ErrEmbeddedType - - // ErrUnexportedField indiciates the provided command struct contains an - // unexported field which is not supported. - ErrUnexportedField - - // ErrUnsupportedFieldType indicates the type of a field in the provided - // command struct is not one of the supported types. - ErrUnsupportedFieldType - - // ErrNonOptionalField indicates a non-optional field was specified - // after an optional field. - ErrNonOptionalField - - // ErrNonOptionalDefault indicates a 'jsonrpcdefault' struct tag was - // specified for a non-optional field. - ErrNonOptionalDefault - - // ErrMismatchedDefault indicates a 'jsonrpcdefault' struct tag contains - // a value that doesn't match the type of the field. - ErrMismatchedDefault - - // ErrUnregisteredMethod indicates a method was specified that has not - // been registered. - ErrUnregisteredMethod - - // ErrMissingDescription indicates a description required to generate - // help is missing. - ErrMissingDescription - - // ErrNumParams inidcates the number of params supplied do not - // match the requirements of the associated command. - ErrNumParams - - // numErrorCodes is the maximum error code number used in tests. - numErrorCodes -) - -// Map of ErrorCode values back to their constant names for pretty printing. -var errorCodeStrings = map[ErrorCode]string{ - ErrDuplicateMethod: "ErrDuplicateMethod", - ErrInvalidUsageFlags: "ErrInvalidUsageFlags", - ErrInvalidType: "ErrInvalidType", - ErrEmbeddedType: "ErrEmbeddedType", - ErrUnexportedField: "ErrUnexportedField", - ErrUnsupportedFieldType: "ErrUnsupportedFieldType", - ErrNonOptionalField: "ErrNonOptionalField", - ErrNonOptionalDefault: "ErrNonOptionalDefault", - ErrMismatchedDefault: "ErrMismatchedDefault", - ErrUnregisteredMethod: "ErrUnregisteredMethod", - ErrMissingDescription: "ErrMissingDescription", - ErrNumParams: "ErrNumParams", -} - -// String returns the ErrorCode as a human-readable name. -func (e ErrorCode) String() string { - if s := errorCodeStrings[e]; s != "" { - return s - } - return fmt.Sprintf("Unknown ErrorCode (%d)", int(e)) -} - -// Error identifies a general error. This differs from an RPCError in that this -// error typically is used more by the consumers of the package as opposed to -// RPCErrors which are intended to be returned to the client across the appmessage via -// a JSON-RPC Response. The caller can use type assertions to determine the -// specific error and access the ErrorCode field. -type Error struct { - ErrorCode ErrorCode // Describes the kind of error - Description string // Human readable description of the issue -} - -// Error satisfies the error interface and prints human-readable errors. -func (e Error) Error() string { - return e.Description -} - -// makeError creates an Error given a set of arguments. -func makeError(c ErrorCode, desc string) Error { - return Error{ErrorCode: c, Description: desc} -} diff --git a/infrastructure/network/rpc/model/error_test.go b/infrastructure/network/rpc/model/error_test.go deleted file mode 100644 index 756c99d9c..000000000 --- a/infrastructure/network/rpc/model/error_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model_test - -import ( - "testing" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// TestErrorCodeStringer tests the stringized output for the ErrorCode type. -func TestErrorCodeStringer(t *testing.T) { - t.Parallel() - - tests := []struct { - in model.ErrorCode - want string - }{ - {model.ErrDuplicateMethod, "ErrDuplicateMethod"}, - {model.ErrInvalidUsageFlags, "ErrInvalidUsageFlags"}, - {model.ErrInvalidType, "ErrInvalidType"}, - {model.ErrEmbeddedType, "ErrEmbeddedType"}, - {model.ErrUnexportedField, "ErrUnexportedField"}, - {model.ErrUnsupportedFieldType, "ErrUnsupportedFieldType"}, - {model.ErrNonOptionalField, "ErrNonOptionalField"}, - {model.ErrNonOptionalDefault, "ErrNonOptionalDefault"}, - {model.ErrMismatchedDefault, "ErrMismatchedDefault"}, - {model.ErrUnregisteredMethod, "ErrUnregisteredMethod"}, - {model.ErrNumParams, "ErrNumParams"}, - {model.ErrMissingDescription, "ErrMissingDescription"}, - {0xffff, "Unknown ErrorCode (65535)"}, - } - - // Detect additional error codes that don't have the stringer added. - if len(tests)-1 != int(model.TstNumErrorCodes) { - t.Errorf("It appears an error code was added without adding an " + - "associated stringer test") - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - result := test.in.String() - if result != test.want { - t.Errorf("String #%d\n got: %s want: %s", i, result, - test.want) - continue - } - } -} - -// TestError tests the error output for the Error type. -func TestError(t *testing.T) { - t.Parallel() - - tests := []struct { - in model.Error - want string - }{ - { - model.Error{Description: "some error"}, - "some error", - }, - { - model.Error{Description: "human-readable error"}, - "human-readable error", - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - result := test.in.Error() - if result != test.want { - t.Errorf("Error #%d\n got: %s want: %s", i, result, - test.want) - continue - } - } -} diff --git a/infrastructure/network/rpc/model/example_test.go b/infrastructure/network/rpc/model/example_test.go deleted file mode 100644 index 26175d4ea..000000000 --- a/infrastructure/network/rpc/model/example_test.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model_test - -import ( - "encoding/json" - "fmt" - "github.com/kaspanet/kaspad/util/pointers" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// This example demonstrates how to create and marshal a command into a JSON-RPC -// request. -func ExampleMarshalCommand() { - // Create a new getblock command. Notice the nil parameter indicates - // to use the default parameter for that fields. This is a common - // pattern used in all of the NewCmd functions in this package for - // optional fields. Also, notice the call to pointers.Bool which is a - // convenience function for creating a pointer out of a primitive for - // optional parameters. - blockHash := "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" - gbCmd := model.NewGetBlockCmd(blockHash, pointers.Bool(false), nil, nil) - - // Marshal the command to the format suitable for sending to the RPC - // server. Typically the client would increment the id here which is - // request so the response can be identified. - id := 1 - marshalledBytes, err := model.MarshalCommand(id, gbCmd) - if err != nil { - fmt.Println(err) - return - } - - // Display the marshalled command. Ordinarily this would be sent across - // the appmessage to the RPC server, but for this example, just display it. - fmt.Printf("%s\n", marshalledBytes) - - // Output: - // {"jsonrpc":"1.0","method":"getBlock","params":["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",false],"id":1} -} - -// This example demonstrates how to unmarshal a JSON-RPC request and then -// unmarshal the concrete request into a concrete command. -func ExampleUnmarshalCommand() { - // Ordinarily this would be read from the appmessage, but for this example, - // it is hard coded here for clarity. - data := []byte(`{"jsonrpc":"1.0","method":"getBlock","params":["000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",false],"id":1}`) - - // Unmarshal the raw bytes from the appmessage into a JSON-RPC request. - var request model.Request - if err := json.Unmarshal(data, &request); err != nil { - fmt.Println(err) - return - } - - // Typically there isn't any need to examine the request fields directly - // like this as the caller already knows what response to expect based - // on the command it sent. However, this is done here to demonstrate - // why the unmarshal process is two steps. - if request.ID == nil { - fmt.Println("Unexpected notification") - return - } - if request.Method != "getBlock" { - fmt.Println("Unexpected method") - return - } - - // Unmarshal the request into a concrete command. - cmd, err := model.UnmarshalCommand(&request) - if err != nil { - fmt.Println(err) - return - } - - // Type assert the command to the appropriate type. - gbCmd, ok := cmd.(*model.GetBlockCmd) - if !ok { - fmt.Printf("Incorrect command type: %T\n", cmd) - return - } - - // Display the fields in the concrete command. - fmt.Println("Hash:", gbCmd.Hash) - fmt.Println("Verbose:", *gbCmd.Verbose) - fmt.Println("VerboseTx:", *gbCmd.VerboseTx) - if gbCmd.Subnetwork != nil { - fmt.Println("Subnetwork:", *gbCmd.Subnetwork) - } - - // Output: - // Hash: 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f - // Verbose: false - // VerboseTx: false -} - -// This example demonstrates how to marshal a JSON-RPC response. -func ExampleMarshalResponse() { - // Marshal a new JSON-RPC response. For example, this is a response - // to a getblockheight request. - marshalledBytes, err := model.MarshalResponse(1, 350001, nil) - if err != nil { - fmt.Println(err) - return - } - - // Display the marshalled response. Ordinarily this would be sent - // across the appmessage to the RPC client, but for this example, just display - // it. - fmt.Printf("%s\n", marshalledBytes) - - // Output: - // {"result":350001,"error":null,"id":1} -} - -// This example demonstrates how to unmarshal a JSON-RPC response and then -// unmarshal the result field in the response to a concrete type. -func Example_unmarshalResponse() { - // Ordinarily this would be read from the appmessage, but for this example, - // it is hard coded here for clarity. This is an example response to a - // getblockheight request. - data := []byte(`{"result":350001,"error":null,"id":1}`) - - // Unmarshal the raw bytes from the appmessage into a JSON-RPC response. - var response model.Response - if err := json.Unmarshal(data, &response); err != nil { - fmt.Println("Malformed JSON-RPC response:", err) - return - } - - // Check the response for an error from the server. For example, the - // server might return an error if an invalid/unknown block hash is - // requested. - if response.Error != nil { - fmt.Println(response.Error) - return - } - - // Unmarshal the result into the expected type for the response. - var blockHeight int32 - if err := json.Unmarshal(response.Result, &blockHeight); err != nil { - fmt.Printf("Unexpected result type: %T\n", response.Result) - return - } - fmt.Println("Block height:", blockHeight) - - // Output: - // Block height: 350001 -} diff --git a/infrastructure/network/rpc/model/export_test.go b/infrastructure/network/rpc/model/export_test.go deleted file mode 100644 index d1beeadb4..000000000 --- a/infrastructure/network/rpc/model/export_test.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model - -// TstHighestUsageFlagBit makes the internal highestUsageFlagBit parameter -// available to the test package. -var TstHighestUsageFlagBit = highestUsageFlagBit - -// TstNumErrorCodes makes the internal numErrorCodes parameter available to the -// test package. -var TstNumErrorCodes = numErrorCodes - -// TstAssignField makes the internal assignField function available to the test -// package. -var TstAssignField = assignField - -// TstFieldUsage makes the internal fieldUsage function available to the test -// package. -var TstFieldUsage = fieldUsage - -// TstReflectTypeToJSONType makes the internal reflectTypeToJSONType function -// available to the test package. -var TstReflectTypeToJSONType = reflectTypeToJSONType - -// TstResultStructHelp makes the internal resultStructHelp function available to -// the test package. -var TstResultStructHelp = resultStructHelp - -// TstReflectTypeToJSONExample makes the internal reflectTypeToJSONExample -// function available to the test package. -var TstReflectTypeToJSONExample = reflectTypeToJSONExample - -// TstResultTypeHelp makes the internal resultTypeHelp function available to the -// test package. -var TstResultTypeHelp = resultTypeHelp - -// TstArgHelp makes the internal argHelp function available to the test package. -var TstArgHelp = argHelp - -// TestMethodHelp makes the internal methodHelp function available to the test -// package. -var TestMethodHelp = methodHelp - -// TstIsValidResultType makes the internal isValidResultType function available -// to the test package. -var TstIsValidResultType = isValidResultType diff --git a/infrastructure/network/rpc/model/help.go b/infrastructure/network/rpc/model/help.go deleted file mode 100644 index 9a4c36848..000000000 --- a/infrastructure/network/rpc/model/help.go +++ /dev/null @@ -1,559 +0,0 @@ -// Copyright (c) 2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model - -import ( - "bytes" - "fmt" - "reflect" - "strings" - "text/tabwriter" - - "github.com/kaspanet/kaspad/util" -) - -// baseHelpDescs house the various help labels, types, and example values used -// when generating help. The per-command synopsis, field descriptions, -// conditions, and result descriptions are to be provided by the caller. -var baseHelpDescs = map[string]string{ - // Misc help labels and output. - "help-arguments": "Arguments", - "help-arguments-none": "None", - "help-result": "Result", - "help-result-nothing": "Nothing", - "help-default": "default", - "help-optional": "optional", - "help-required": "required", - - // JSON types. - "json-type-numeric": "numeric", - "json-type-string": "string", - "json-type-bool": "boolean", - "json-type-array": "array of ", - "json-type-object": "object", - "json-type-value": "value", - - // JSON examples. - "json-example-string": "value", - "json-example-bool": "true|false", - "json-example-map-data": "data", - "json-example-unknown": "unknown", -} - -// descLookupFunc is a function which is used to lookup a description given -// a key. -type descLookupFunc func(string) string - -// reflectTypeToJSONType returns a string that represents the JSON type -// associated with the provided Go type. -func reflectTypeToJSONType(xT descLookupFunc, rt reflect.Type) string { - kind := rt.Kind() - if isNumeric(kind) { - return xT("json-type-numeric") - } - - switch kind { - case reflect.String: - return xT("json-type-string") - - case reflect.Bool: - return xT("json-type-bool") - - case reflect.Array, reflect.Slice: - return xT("json-type-array") + reflectTypeToJSONType(xT, - rt.Elem()) - - case reflect.Struct: - return xT("json-type-object") - - case reflect.Map: - return xT("json-type-object") - } - - return xT("json-type-value") -} - -// resultStructHelp returns a slice of strings containing the result help output -// for a struct. Each line makes use of tabs to separate the relevant pieces so -// a tabwriter can be used later to line everything up. The descriptions are -// pulled from the active help descriptions map based on the lowercase version -// of the provided reflect type and json name (or the lowercase version of the -// field name if no json tag was specified). -func resultStructHelp(xT descLookupFunc, rt reflect.Type, indentLevel int) []string { - indent := strings.Repeat(" ", indentLevel) - typeName := util.ToCamelCase(rt.Name()) - - // Generate the help for each of the fields in the result struct. - numField := rt.NumField() - results := make([]string, 0, numField) - for i := 0; i < numField; i++ { - rtf := rt.Field(i) - - // The field name to display is the json name when it's - // available, otherwise use the lowercase field name. - var fieldName string - if tag := rtf.Tag.Get("json"); tag != "" { - fieldName = strings.Split(tag, ",")[0] - } else { - fieldName = util.ToCamelCase(rtf.Name) - } - - // Deference pointer if needed. - rtfType := rtf.Type - if rtfType.Kind() == reflect.Ptr { - rtfType = rtf.Type.Elem() - } - - // Generate the JSON example for the result type of this struct - // field. When it is a complex type, examine the type and - // adjust the opening bracket and brace combination accordingly. - fieldType := reflectTypeToJSONType(xT, rtfType) - fieldDescKey := typeName + "-" + fieldName - fieldExamples, isComplex := reflectTypeToJSONExample(xT, - rtfType, indentLevel, fieldDescKey) - if isComplex { - var brace string - kind := rtfType.Kind() - if kind == reflect.Array || kind == reflect.Slice { - brace = "[{" - } else { - brace = "{" - } - result := fmt.Sprintf("%s\"%s\": %s\t(%s)\t%s", indent, - fieldName, brace, fieldType, xT(fieldDescKey)) - results = append(results, result) - results = append(results, fieldExamples...) - } else { - result := fmt.Sprintf("%s\"%s\": %s,\t(%s)\t%s", indent, - fieldName, fieldExamples[0], fieldType, - xT(fieldDescKey)) - results = append(results, result) - } - } - - return results -} - -// reflectTypeToJSONExample generates example usage in the format used by the -// help output. It handles arrays, slices and structs recursively. The output -// is returned as a slice of lines so the final help can be nicely aligned via -// a tab writer. A bool is also returned which specifies whether or not the -// type results in a complex JSON object since they need to be handled -// differently. -func reflectTypeToJSONExample(xT descLookupFunc, rt reflect.Type, indentLevel int, fieldDescKey string) ([]string, bool) { - // Indirect pointer if needed. - if rt.Kind() == reflect.Ptr { - rt = rt.Elem() - } - kind := rt.Kind() - if isNumeric(kind) { - if kind == reflect.Float32 || kind == reflect.Float64 { - return []string{"n.nnn"}, false - } - - return []string{"n"}, false - } - - switch kind { - case reflect.String: - return []string{`"` + xT("json-example-string") + `"`}, false - - case reflect.Bool: - return []string{xT("json-example-bool")}, false - - case reflect.Struct: - indent := strings.Repeat(" ", indentLevel) - results := resultStructHelp(xT, rt, indentLevel+1) - - // An opening brace is needed for the first indent level. For - // all others, it will be included as a part of the previous - // field. - if indentLevel == 0 { - newResults := make([]string, len(results)+1) - newResults[0] = "{" - copy(newResults[1:], results) - results = newResults - } - - // The closing brace has a comma after it except for the first - // indent level. The final tabs are necessary so the tab writer - // lines things up properly. - closingBrace := indent + "}" - if indentLevel > 0 { - closingBrace += "," - } - results = append(results, closingBrace+"\t\t") - return results, true - - case reflect.Array, reflect.Slice: - results, isComplex := reflectTypeToJSONExample(xT, rt.Elem(), - indentLevel, fieldDescKey) - - // When the result is complex, it is because this is an array of - // objects. - if isComplex { - // When this is at indent level zero, there is no - // previous field to house the opening array bracket, so - // replace the opening object brace with the array - // syntax. Also, replace the final closing object brace - // with the variadiac array closing syntax. - indent := strings.Repeat(" ", indentLevel) - if indentLevel == 0 { - results[0] = indent + "[{" - results[len(results)-1] = indent + "},...]" - return results, true - } - - // At this point, the indent level is greater than 0, so - // the opening array bracket and object brace are - // already a part of the previous field. However, the - // closing entry is a simple object brace, so replace it - // with the variadiac array closing syntax. The final - // tabs are necessary so the tab writer lines things up - // properly. - results[len(results)-1] = indent + "},...],\t\t" - return results, true - } - - // It's an array of primitives, so return the formatted text - // accordingly. - return []string{fmt.Sprintf("[%s,...]", results[0])}, false - - case reflect.Map: - indent := strings.Repeat(" ", indentLevel) - results := make([]string, 0, 3) - - // An opening brace is needed for the first indent level. For - // all others, it will be included as a part of the previous - // field. - if indentLevel == 0 { - results = append(results, indent+"{") - } - - // Maps are a bit special in that they need to have the key, - // value, and description of the object entry specifically - // called out. - innerIndent := strings.Repeat(" ", indentLevel+1) - result := fmt.Sprintf("%s%q: %s, (%s) %s", innerIndent, - xT(fieldDescKey+"--key"), xT(fieldDescKey+"--value"), - reflectTypeToJSONType(xT, rt), xT(fieldDescKey+"--desc")) - results = append(results, result) - results = append(results, innerIndent+"...") - - results = append(results, indent+"}") - return results, true - } - - return []string{xT("json-example-unknown")}, false -} - -// resultTypeHelp generates and returns formatted help for the provided result -// type. -func resultTypeHelp(xT descLookupFunc, rt reflect.Type, fieldDescKey string) string { - // Generate the JSON example for the result type. - results, isComplex := reflectTypeToJSONExample(xT, rt, 0, fieldDescKey) - - // When this is a primitive type, add the associated JSON type and - // result description into the final string, format it accordingly, - // and return it. - if !isComplex { - return fmt.Sprintf("%s (%s) %s", results[0], - reflectTypeToJSONType(xT, rt), xT(fieldDescKey)) - } - - // At this point, this is a complex type that already has the JSON types - // and descriptions in the results. Thus, use a tab writer to nicely - // align the help text. - var formatted bytes.Buffer - w := new(tabwriter.Writer) - w.Init(&formatted, 0, 4, 1, ' ', 0) - for i, text := range results { - if i == len(results)-1 { - fmt.Fprint(w, text) - } else { - fmt.Fprintln(w, text) - } - } - w.Flush() - return formatted.String() -} - -// argTypeHelp returns the type of provided command argument as a string in the -// format used by the help output. In particular, it includes the JSON type -// (boolean, numeric, string, array, object) along with optional and the default -// value if applicable. -func argTypeHelp(xT descLookupFunc, structField reflect.StructField, defaultVal *reflect.Value) string { - // Indirect the pointer if needed and track if it's an optional field. - fieldType := structField.Type - var isOptional bool - if fieldType.Kind() == reflect.Ptr { - fieldType = fieldType.Elem() - isOptional = true - } - - // When there is a default value, it must also be a pointer due to the - // rules enforced by RegisterCmd. - if defaultVal != nil { - indirect := defaultVal.Elem() - defaultVal = &indirect - } - - // Convert the field type to a JSON type. - details := make([]string, 0, 3) - details = append(details, reflectTypeToJSONType(xT, fieldType)) - - // Add optional and default value to the details if needed. - if isOptional { - details = append(details, xT("help-optional")) - - // Add the default value if there is one. This is only checked - // when the field is optional since a non-optional field can't - // have a default value. - if defaultVal != nil { - val := defaultVal.Interface() - if defaultVal.Kind() == reflect.String { - val = fmt.Sprintf(`"%s"`, val) - } - str := fmt.Sprintf("%s=%v", xT("help-default"), val) - details = append(details, str) - } - } else { - details = append(details, xT("help-required")) - } - - return strings.Join(details, ", ") -} - -// argHelp generates and returns formatted help for the provided command. -func argHelp(xT descLookupFunc, rtp reflect.Type, defaults map[int]reflect.Value, method string) string { - // Return now if the command has no arguments. - rt := rtp.Elem() - numFields := rt.NumField() - if numFields == 0 { - return "" - } - - // Generate the help for each argument in the command. Several - // simplifying assumptions are made here because the RegisterCmd - // function has already rigorously enforced the layout. - args := make([]string, 0, numFields) - for i := 0; i < numFields; i++ { - rtf := rt.Field(i) - var defaultVal *reflect.Value - if defVal, ok := defaults[i]; ok { - defaultVal = &defVal - } - - fieldName := util.ToCamelCase(rtf.Name) - helpText := fmt.Sprintf("%d.\t%s\t(%s)\t%s", i+1, fieldName, - argTypeHelp(xT, rtf, defaultVal), - xT(method+"-"+fieldName)) - args = append(args, helpText) - - // For types which require a JSON object, or an array of JSON - // objects, generate the full syntax for the argument. - fieldType := rtf.Type - if fieldType.Kind() == reflect.Ptr { - fieldType = fieldType.Elem() - } - kind := fieldType.Kind() - switch kind { - case reflect.Struct: - fieldDescKey := fmt.Sprintf("%s-%s", method, fieldName) - resultText := resultTypeHelp(xT, fieldType, fieldDescKey) - args = append(args, resultText) - - case reflect.Map: - fieldDescKey := fmt.Sprintf("%s-%s", method, fieldName) - resultText := resultTypeHelp(xT, fieldType, fieldDescKey) - args = append(args, resultText) - - case reflect.Array, reflect.Slice: - fieldDescKey := fmt.Sprintf("%s-%s", method, fieldName) - if rtf.Type.Elem().Kind() == reflect.Struct { - resultText := resultTypeHelp(xT, fieldType, - fieldDescKey) - args = append(args, resultText) - } - } - } - - // Add argument names, types, and descriptions if there are any. Use a - // tab writer to nicely align the help text. - var formatted bytes.Buffer - w := new(tabwriter.Writer) - w.Init(&formatted, 0, 4, 1, ' ', 0) - for _, text := range args { - fmt.Fprintln(w, text) - } - w.Flush() - return formatted.String() -} - -// methodHelp generates and returns the help output for the provided command -// and method info. This is the main work horse for the exported MethodHelp -// function. -func methodHelp(xT descLookupFunc, rtp reflect.Type, defaults map[int]reflect.Value, method string, resultTypes []interface{}) string { - // Start off with the method usage and help synopsis. - help := fmt.Sprintf("%s\n\n%s\n", methodUsageText(rtp, defaults, method), - xT(method+"--synopsis")) - - // Generate the help for each argument in the command. - if argText := argHelp(xT, rtp, defaults, method); argText != "" { - help += fmt.Sprintf("\n%s:\n%s", xT("help-arguments"), - argText) - } else { - help += fmt.Sprintf("\n%s:\n%s\n", xT("help-arguments"), - xT("help-arguments-none")) - } - - // Generate the help text for each result type. - resultTexts := make([]string, 0, len(resultTypes)) - for i := range resultTypes { - rtp := reflect.TypeOf(resultTypes[i]) - fieldDescKey := fmt.Sprintf("%s--result%d", method, i) - if resultTypes[i] == nil { - resultText := xT("help-result-nothing") - resultTexts = append(resultTexts, resultText) - continue - } - - resultText := resultTypeHelp(xT, rtp.Elem(), fieldDescKey) - resultTexts = append(resultTexts, resultText) - } - - // Add result types and descriptions. When there is more than one - // result type, also add the condition which triggers it. - if len(resultTexts) > 1 { - for i, resultText := range resultTexts { - condKey := fmt.Sprintf("%s--condition%d", method, i) - help += fmt.Sprintf("\n%s (%s):\n%s\n", - xT("help-result"), xT(condKey), resultText) - } - } else if len(resultTexts) > 0 { - help += fmt.Sprintf("\n%s:\n%s\n", xT("help-result"), - resultTexts[0]) - } else { - help += fmt.Sprintf("\n%s:\n%s\n", xT("help-result"), - xT("help-result-nothing")) - } - return help -} - -// isValidResultType returns whether the passed reflect kind is one of the -// acceptable types for results. -func isValidResultType(kind reflect.Kind) bool { - if isNumeric(kind) { - return true - } - - switch kind { - case reflect.String, reflect.Struct, reflect.Array, reflect.Slice, - reflect.Bool, reflect.Map: - - return true - } - - return false -} - -// GenerateHelp generates and returns help output for the provided method and -// result types given a map to provide the appropriate keys for the method -// synopsis, field descriptions, conditions, and result descriptions. The -// method must be associated with a registered type. All commands provided by -// this package are registered by default. -// -// The resultTypes must be pointer-to-types which represent the specific types -// of values the command returns. For example, if the command only returns a -// boolean value, there should only be a single entry of (*bool)(nil). Note -// that each type must be a single pointer to the type. Therefore, it is -// recommended to simply pass a nil pointer cast to the appropriate type as -// previously shown. -// -// The provided descriptions map must contain all of the keys or an error will -// be returned which includes the missing key, or the final missing key when -// there is more than one key missing. The generated help in the case of such -// an error will use the key in place of the description. -// -// The following outlines the required keys: -// "--synopsis" Synopsis for the command -// "-" Description for each command argument -// "-" Description for each object field -// "--condition<#>" Description for each result condition -// "--result<#>" Description for each primitive result num -// -// Notice that the "special" keys synopsis, condition<#>, and result<#> are -// preceded by a double dash to ensure they don't conflict with field names. -// -// The condition keys are only required when there is more than on result type, -// and the result key for a given result type is only required if it's not an -// object. -// -// For example, consider the 'help' command itself. There are two possible -// returns depending on the provided parameters. So, the help would be -// generated by calling the function as follows: -// GenerateHelp("help", descs, (*string)(nil), (*string)(nil)). -// -// The following keys would then be required in the provided descriptions map: -// -// "help--synopsis": "Returns a list of all commands or help for ...." -// "help-command": "The command to retrieve help for", -// "help--condition0": "no command provided" -// "help--condition1": "command specified" -// "help--result0": "List of commands" -// "help--result1": "Help for specified command" -func GenerateHelp(method string, descs map[string]string, resultTypes ...interface{}) (string, error) { - // Look up details about the provided method and error out if not - // registered. - rtp, info, ok := methodConcreteTypeAndInfoWithRLock(method) - if !ok { - str := fmt.Sprintf("%q is not registered", method) - return "", makeError(ErrUnregisteredMethod, str) - } - - // Validate each result type is a pointer to a supported type (or nil). - for i, resultType := range resultTypes { - if resultType == nil { - continue - } - - rtp := reflect.TypeOf(resultType) - if rtp.Kind() != reflect.Ptr { - str := fmt.Sprintf("result #%d (%s) is not a pointer", - i, rtp.Kind()) - return "", makeError(ErrInvalidType, str) - } - - elemKind := rtp.Elem().Kind() - if !isValidResultType(elemKind) { - str := fmt.Sprintf("result #%d (%s) is not an allowed "+ - "type", i, elemKind) - return "", makeError(ErrInvalidType, str) - } - } - - // Create a closure for the description lookup function which falls back - // to the base help descriptions map for unrecognized keys and tracks - // and missing keys. - var missingKey string - xT := func(key string) string { - if desc, ok := descs[key]; ok { - return desc - } - if desc, ok := baseHelpDescs[key]; ok { - return desc - } - - missingKey = key - return key - } - - // Generate and return the help for the method. - help := methodHelp(xT, rtp, info.defaults, method, resultTypes) - if missingKey != "" { - return help, makeError(ErrMissingDescription, missingKey) - } - return help, nil -} diff --git a/infrastructure/network/rpc/model/help_test.go b/infrastructure/network/rpc/model/help_test.go deleted file mode 100644 index 928414362..000000000 --- a/infrastructure/network/rpc/model/help_test.go +++ /dev/null @@ -1,740 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model_test - -import ( - "github.com/pkg/errors" - "reflect" - "testing" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// TestHelpReflectInternals ensures the various help functions which deal with -// reflect types work as expected for various Go types. -func TestHelpReflectInternals(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - reflectType reflect.Type - indentLevel int - key string - examples []string - isComplex bool - help string - isInvalid bool - }{ - { - name: "int", - reflectType: reflect.TypeOf(int(0)), - key: "json-type-numeric", - examples: []string{"n"}, - help: "n (json-type-numeric) fdk", - }, - { - name: "*int", - reflectType: reflect.TypeOf((*int)(nil)), - key: "json-type-value", - examples: []string{"n"}, - help: "n (json-type-value) fdk", - isInvalid: true, - }, - { - name: "int8", - reflectType: reflect.TypeOf(int8(0)), - key: "json-type-numeric", - examples: []string{"n"}, - help: "n (json-type-numeric) fdk", - }, - { - name: "int16", - reflectType: reflect.TypeOf(int16(0)), - key: "json-type-numeric", - examples: []string{"n"}, - help: "n (json-type-numeric) fdk", - }, - { - name: "int32", - reflectType: reflect.TypeOf(int32(0)), - key: "json-type-numeric", - examples: []string{"n"}, - help: "n (json-type-numeric) fdk", - }, - { - name: "int64", - reflectType: reflect.TypeOf(int64(0)), - key: "json-type-numeric", - examples: []string{"n"}, - help: "n (json-type-numeric) fdk", - }, - { - name: "uint", - reflectType: reflect.TypeOf(uint(0)), - key: "json-type-numeric", - examples: []string{"n"}, - help: "n (json-type-numeric) fdk", - }, - { - name: "uint8", - reflectType: reflect.TypeOf(uint8(0)), - key: "json-type-numeric", - examples: []string{"n"}, - help: "n (json-type-numeric) fdk", - }, - { - name: "uint16", - reflectType: reflect.TypeOf(uint16(0)), - key: "json-type-numeric", - examples: []string{"n"}, - help: "n (json-type-numeric) fdk", - }, - { - name: "uint32", - reflectType: reflect.TypeOf(uint32(0)), - key: "json-type-numeric", - examples: []string{"n"}, - help: "n (json-type-numeric) fdk", - }, - { - name: "uint64", - reflectType: reflect.TypeOf(uint64(0)), - key: "json-type-numeric", - examples: []string{"n"}, - help: "n (json-type-numeric) fdk", - }, - { - name: "float32", - reflectType: reflect.TypeOf(float32(0)), - key: "json-type-numeric", - examples: []string{"n.nnn"}, - help: "n.nnn (json-type-numeric) fdk", - }, - { - name: "float64", - reflectType: reflect.TypeOf(float64(0)), - key: "json-type-numeric", - examples: []string{"n.nnn"}, - help: "n.nnn (json-type-numeric) fdk", - }, - { - name: "string", - reflectType: reflect.TypeOf(""), - key: "json-type-string", - examples: []string{`"json-example-string"`}, - help: "\"json-example-string\" (json-type-string) fdk", - }, - { - name: "bool", - reflectType: reflect.TypeOf(true), - key: "json-type-bool", - examples: []string{"json-example-bool"}, - help: "json-example-bool (json-type-bool) fdk", - }, - { - name: "array of int", - reflectType: reflect.TypeOf([1]int{0}), - key: "json-type-arrayjson-type-numeric", - examples: []string{"[n,...]"}, - help: "[n,...] (json-type-arrayjson-type-numeric) fdk", - }, - { - name: "slice of int", - reflectType: reflect.TypeOf([]int{0}), - key: "json-type-arrayjson-type-numeric", - examples: []string{"[n,...]"}, - help: "[n,...] (json-type-arrayjson-type-numeric) fdk", - }, - { - name: "struct", - reflectType: reflect.TypeOf(struct{}{}), - key: "json-type-object", - examples: []string{"{", "}\t\t"}, - isComplex: true, - help: "{\n} ", - }, - { - name: "struct indent level 1", - reflectType: reflect.TypeOf(struct{ field int }{}), - indentLevel: 1, - key: "json-type-object", - examples: []string{ - " \"field\": n,\t(json-type-numeric)\t-field", - " },\t\t", - }, - help: "{\n" + - " \"field\": n, (json-type-numeric) -field\n" + - "} ", - isComplex: true, - }, - { - name: "array of struct indent level 0", - reflectType: func() reflect.Type { - type s struct { - field int - } - return reflect.TypeOf([]s{}) - }(), - key: "json-type-arrayjson-type-object", - examples: []string{ - "[{", - " \"field\": n,\t(json-type-numeric)\ts-field", - "},...]", - }, - help: "[{\n" + - " \"field\": n, (json-type-numeric) s-field\n" + - "},...]", - isComplex: true, - }, - { - name: "array of struct indent level 1", - reflectType: func() reflect.Type { - type s struct { - field int - } - return reflect.TypeOf([]s{}) - }(), - indentLevel: 1, - key: "json-type-arrayjson-type-object", - examples: []string{ - " \"field\": n,\t(json-type-numeric)\ts-field", - " },...],\t\t", - }, - help: "[{\n" + - " \"field\": n, (json-type-numeric) s-field\n" + - "},...]", - isComplex: true, - }, - { - name: "map", - reflectType: reflect.TypeOf(map[string]string{}), - key: "json-type-object", - examples: []string{"{", - " \"fdk--key\": fdk--value, (json-type-object) fdk--desc", - " ...", "}", - }, - help: "{\n" + - " \"fdk--key\": fdk--value, (json-type-object) fdk--desc\n" + - " ...\n" + - "}", - isComplex: true, - }, - { - name: "complex", - reflectType: reflect.TypeOf(complex64(0)), - key: "json-type-value", - examples: []string{"json-example-unknown"}, - help: "json-example-unknown (json-type-value) fdk", - isInvalid: true, - }, - } - - xT := func(key string) string { - return key - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - // Ensure the description key is the expected value. - key := model.TstReflectTypeToJSONType(xT, test.reflectType) - if key != test.key { - t.Errorf("Test #%d (%s) unexpected key - got: %v, "+ - "want: %v", i, test.name, key, test.key) - continue - } - - // Ensure the generated example is as expected. - examples, isComplex := model.TstReflectTypeToJSONExample(xT, - test.reflectType, test.indentLevel, "fdk") - if isComplex != test.isComplex { - t.Errorf("Test #%d (%s) unexpected isComplex - got: %v, "+ - "want: %v", i, test.name, isComplex, - test.isComplex) - continue - } - if len(examples) != len(test.examples) { - t.Errorf("Test #%d (%s) unexpected result length - "+ - "got: %v, want: %v", i, test.name, len(examples), - len(test.examples)) - continue - } - for j, example := range examples { - if example != test.examples[j] { - t.Errorf("Test #%d (%s) example #%d unexpected "+ - "example - got: %v, want: %v", i, - test.name, j, example, test.examples[j]) - continue - } - } - - // Ensure the generated result type help is as expected. - helpText := model.TstResultTypeHelp(xT, test.reflectType, "fdk") - if helpText != test.help { - t.Errorf("Test #%d (%s) unexpected result help - "+ - "got: %v, want: %v", i, test.name, helpText, - test.help) - continue - } - - isValid := model.TstIsValidResultType(test.reflectType.Kind()) - if isValid != !test.isInvalid { - t.Errorf("Test #%d (%s) unexpected result type validity "+ - "- got: %v", i, test.name, isValid) - continue - } - } -} - -// TestResultStructHelp ensures the expected help text format is returned for -// various Go struct types. -func TestResultStructHelp(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - reflectType reflect.Type - expected []string - }{ - { - name: "empty struct", - reflectType: func() reflect.Type { - type s struct{} - return reflect.TypeOf(s{}) - }(), - expected: nil, - }, - { - name: "struct with primitive field", - reflectType: func() reflect.Type { - type s struct { - field int - } - return reflect.TypeOf(s{}) - }(), - expected: []string{ - "\"field\": n,\t(json-type-numeric)\ts-field", - }, - }, - { - name: "struct with primitive field and json tag", - reflectType: func() reflect.Type { - type s struct { - Field int `json:"f"` - } - return reflect.TypeOf(s{}) - }(), - expected: []string{ - "\"f\": n,\t(json-type-numeric)\ts-f", - }, - }, - { - name: "struct with array of primitive field", - reflectType: func() reflect.Type { - type s struct { - field []int - } - return reflect.TypeOf(s{}) - }(), - expected: []string{ - "\"field\": [n,...],\t(json-type-arrayjson-type-numeric)\ts-field", - }, - }, - { - name: "struct with sub-struct field", - reflectType: func() reflect.Type { - type s2 struct { - subField int - } - type s struct { - field s2 - } - return reflect.TypeOf(s{}) - }(), - expected: []string{ - "\"field\": {\t(json-type-object)\ts-field", - "{", - " \"subField\": n,\t(json-type-numeric)\ts2-subField", - "}\t\t", - }, - }, - { - name: "struct with sub-struct field pointer", - reflectType: func() reflect.Type { - type s2 struct { - subField int - } - type s struct { - field *s2 - } - return reflect.TypeOf(s{}) - }(), - expected: []string{ - "\"field\": {\t(json-type-object)\ts-field", - "{", - " \"subField\": n,\t(json-type-numeric)\ts2-subField", - "}\t\t", - }, - }, - { - name: "struct with array of structs field", - reflectType: func() reflect.Type { - type s2 struct { - subField int - } - type s struct { - field []s2 - } - return reflect.TypeOf(s{}) - }(), - expected: []string{ - "\"field\": [{\t(json-type-arrayjson-type-object)\ts-field", - "[{", - " \"subField\": n,\t(json-type-numeric)\ts2-subField", - "},...]", - }, - }, - } - - xT := func(key string) string { - return key - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - results := model.TstResultStructHelp(xT, test.reflectType, 0) - if len(results) != len(test.expected) { - t.Errorf("Test #%d (%s) unexpected result length - "+ - "got: %v, want: %v", i, test.name, len(results), - len(test.expected)) - continue - } - for j, result := range results { - if result != test.expected[j] { - t.Errorf("Test #%d (%s) result #%d unexpected "+ - "result - got: %v, want: %v", i, - test.name, j, result, test.expected[j]) - continue - } - } - } -} - -// TestHelpArgInternals ensures the various help functions which deal with -// arguments work as expected for various argument types. -func TestHelpArgInternals(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - method string - reflectType reflect.Type - defaults map[int]reflect.Value - help string - }{ - { - name: "command with no args", - method: "test", - reflectType: func() reflect.Type { - type s struct{} - return reflect.TypeOf((*s)(nil)) - }(), - defaults: nil, - help: "", - }, - { - name: "command with one required arg", - method: "test", - reflectType: func() reflect.Type { - type s struct { - Field int - } - return reflect.TypeOf((*s)(nil)) - }(), - defaults: nil, - help: "1. field (json-type-numeric, help-required) test-field\n", - }, - { - name: "command with one optional arg, no default", - method: "test", - reflectType: func() reflect.Type { - type s struct { - Optional *int - } - return reflect.TypeOf((*s)(nil)) - }(), - defaults: nil, - help: "1. optional (json-type-numeric, help-optional) test-optional\n", - }, - { - name: "command with one optional arg with default", - method: "test", - reflectType: func() reflect.Type { - type s struct { - Optional *string - } - return reflect.TypeOf((*s)(nil)) - }(), - defaults: func() map[int]reflect.Value { - defVal := "test" - return map[int]reflect.Value{ - 0: reflect.ValueOf(&defVal), - } - }(), - help: "1. optional (json-type-string, help-optional, help-default=\"test\") test-optional\n", - }, - { - name: "command with struct field", - method: "test", - reflectType: func() reflect.Type { - type s2 struct { - F int8 - } - type s struct { - Field s2 - } - return reflect.TypeOf((*s)(nil)) - }(), - defaults: nil, - help: "1. field (json-type-object, help-required) test-field\n" + - "{\n" + - " \"f\": n, (json-type-numeric) s2-f\n" + - "} \n", - }, - { - name: "command with map field", - method: "test", - reflectType: func() reflect.Type { - type s struct { - Field map[string]float64 - } - return reflect.TypeOf((*s)(nil)) - }(), - defaults: nil, - help: "1. field (json-type-object, help-required) test-field\n" + - "{\n" + - " \"test-field--key\": test-field--value, (json-type-object) test-field--desc\n" + - " ...\n" + - "}\n", - }, - { - name: "command with slice of primitives field", - method: "test", - reflectType: func() reflect.Type { - type s struct { - Field []int64 - } - return reflect.TypeOf((*s)(nil)) - }(), - defaults: nil, - help: "1. field (json-type-arrayjson-type-numeric, help-required) test-field\n", - }, - { - name: "command with slice of structs field", - method: "test", - reflectType: func() reflect.Type { - type s2 struct { - F int64 - } - type s struct { - Field []s2 - } - return reflect.TypeOf((*s)(nil)) - }(), - defaults: nil, - help: "1. field (json-type-arrayjson-type-object, help-required) test-field\n" + - "[{\n" + - " \"f\": n, (json-type-numeric) s2-f\n" + - "},...]\n", - }, - } - - xT := func(key string) string { - return key - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - help := model.TstArgHelp(xT, test.reflectType, test.defaults, - test.method) - if help != test.help { - t.Errorf("Test #%d (%s) unexpected help - got:\n%v\n"+ - "want:\n%v", i, test.name, help, test.help) - continue - } - } -} - -// TestMethodHelp ensures the method help function works as expected for various -// command structs. -func TestMethodHelp(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - method string - reflectType reflect.Type - defaults map[int]reflect.Value - resultTypes []interface{} - help string - }{ - { - name: "command with no args or results", - method: "test", - reflectType: func() reflect.Type { - type s struct{} - return reflect.TypeOf((*s)(nil)) - }(), - help: "test\n\ntest--synopsis\n\n" + - "help-arguments:\nhelp-arguments-none\n\n" + - "help-result:\nhelp-result-nothing\n", - }, - { - name: "command with no args and one primitive result", - method: "test", - reflectType: func() reflect.Type { - type s struct{} - return reflect.TypeOf((*s)(nil)) - }(), - resultTypes: []interface{}{(*int64)(nil)}, - help: "test\n\ntest--synopsis\n\n" + - "help-arguments:\nhelp-arguments-none\n\n" + - "help-result:\nn (json-type-numeric) test--result0\n", - }, - { - name: "command with no args and two results", - method: "test", - reflectType: func() reflect.Type { - type s struct{} - return reflect.TypeOf((*s)(nil)) - }(), - resultTypes: []interface{}{(*int64)(nil), nil}, - help: "test\n\ntest--synopsis\n\n" + - "help-arguments:\nhelp-arguments-none\n\n" + - "help-result (test--condition0):\nn (json-type-numeric) test--result0\n\n" + - "help-result (test--condition1):\nhelp-result-nothing\n", - }, - { - name: "command with primitive arg and no results", - method: "test", - reflectType: func() reflect.Type { - type s struct { - Field bool - } - return reflect.TypeOf((*s)(nil)) - }(), - help: "test field\n\ntest--synopsis\n\n" + - "help-arguments:\n1. field (json-type-bool, help-required) test-field\n\n" + - "help-result:\nhelp-result-nothing\n", - }, - { - name: "command with primitive optional and no results", - method: "test", - reflectType: func() reflect.Type { - type s struct { - Field *bool - } - return reflect.TypeOf((*s)(nil)) - }(), - help: "test (field)\n\ntest--synopsis\n\n" + - "help-arguments:\n1. field (json-type-bool, help-optional) test-field\n\n" + - "help-result:\nhelp-result-nothing\n", - }, - } - - xT := func(key string) string { - return key - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - help := model.TestMethodHelp(xT, test.reflectType, - test.defaults, test.method, test.resultTypes) - if help != test.help { - t.Errorf("Test #%d (%s) unexpected help - got:\n%v\n"+ - "want:\n%v", i, test.name, help, test.help) - continue - } - } -} - -// TestGenerateHelpErrors ensures the GenerateHelp function returns the expected -// errors. -func TestGenerateHelpErrors(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - method string - resultTypes []interface{} - err model.Error - }{ - { - name: "unregistered command", - method: "boguscommand", - err: model.Error{ErrorCode: model.ErrUnregisteredMethod}, - }, - { - name: "non-pointer result type", - method: "help", - resultTypes: []interface{}{0}, - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "invalid result type", - method: "help", - resultTypes: []interface{}{(*complex64)(nil)}, - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "missing description", - method: "help", - resultTypes: []interface{}{(*string)(nil), nil}, - err: model.Error{ErrorCode: model.ErrMissingDescription}, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - _, err := model.GenerateHelp(test.method, nil, - test.resultTypes...) - if reflect.TypeOf(err) != reflect.TypeOf(test.err) { - t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+ - "want %T", i, test.name, err, test.err) - continue - } - var gotRPCModelErr model.Error - errors.As(err, &gotRPCModelErr) - gotErrorCode := gotRPCModelErr.ErrorCode - if gotErrorCode != test.err.ErrorCode { - t.Errorf("Test #%d (%s) mismatched error code - got "+ - "%v (%v), want %v", i, test.name, gotErrorCode, - err, test.err.ErrorCode) - continue - } - } -} - -// TestGenerateHelp performs a very basic test to ensure GenerateHelp is working -// as expected. The internal are testd much more thoroughly in other tests, so -// there is no need to add more tests here. -func TestGenerateHelp(t *testing.T) { - t.Parallel() - - descs := map[string]string{ - "help--synopsis": "test", - "help-command": "test", - } - help, err := model.GenerateHelp("help", descs) - if err != nil { - t.Fatalf("GenerateHelp: unexpected error: %v", err) - } - wantHelp := "help (\"command\")\n\n" + - "test\n\nArguments:\n1. command (string, optional) test\n\n" + - "Result:\nNothing\n" - if help != wantHelp { - t.Fatalf("GenerateHelp: unexpected help - got\n%v\nwant\n%v", - help, wantHelp) - } -} diff --git a/infrastructure/network/rpc/model/jsonrpc.go b/infrastructure/network/rpc/model/jsonrpc.go deleted file mode 100644 index ad4597f74..000000000 --- a/infrastructure/network/rpc/model/jsonrpc.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model - -import ( - "encoding/json" - "fmt" -) - -// RPCErrorCode represents an error code to be used as a part of an RPCError -// which is in turn used in a JSON-RPC Response object. -// -// A specific type is used to help ensure the wrong errors aren't used. -type RPCErrorCode int - -// RPCError represents an error that is used as a part of a JSON-RPC Response -// object. -type RPCError struct { - Code RPCErrorCode `json:"code,omitempty"` - Message string `json:"message,omitempty"` -} - -// Guarantee RPCError satisifies the builtin error interface. -var _, _ error = RPCError{}, (*RPCError)(nil) - -// Error returns a string describing the RPC error. This satisifies the -// builtin error interface. -func (e RPCError) Error() string { - return fmt.Sprintf("%d: %s", e.Code, e.Message) -} - -// NewRPCError constructs and returns a new JSON-RPC error that is suitable -// for use in a JSON-RPC Response object. -func NewRPCError(code RPCErrorCode, message string) *RPCError { - return &RPCError{ - Code: code, - Message: message, - } -} - -// IsValidIDType checks that the ID field (which can go in any of the JSON-RPC -// requests, responses, or notifications) is valid. JSON-RPC 1.0 allows any -// valid JSON type. JSON-RPC 2.0 (which kaspad follows for some parts) only -// allows string, number, or null, so this function restricts the allowed types -// to that list. This function is only provided in case the caller is manually -// marshalling for some reason. The functions which accept an ID in this -// package already call this function to ensure the provided id is valid. -func IsValidIDType(id interface{}) bool { - switch id.(type) { - case int, int8, int16, int32, int64, - uint, uint8, uint16, uint32, uint64, - float32, float64, - string, - nil: - return true - default: - return false - } -} - -// Request is a type for raw JSON-RPC 1.0 requests. The Method field identifies -// the specific command type which in turns leads to different parameters. -// Callers typically will not use this directly since this package provides a -// statically typed command infrastructure which handles creation of these -// requests, however this struct it being exported in case the caller wants to -// construct raw requests for some reason. -type Request struct { - JSONRPC string `json:"jsonrpc"` - Method string `json:"method"` - Params []json.RawMessage `json:"params"` - ID interface{} `json:"id"` -} - -// NewRequest returns a new JSON-RPC 1.0 request object given the provided id, -// method, and parameters. The parameters are marshalled into a json.RawMessage -// for the Params field of the returned request object. This function is only -// provided in case the caller wants to construct raw requests for some reason. -// -// Typically callers will instead want to create a registered concrete command -// type with the NewCommand or NewCmd functions and call the MarshalCommand -// function with that command to generate the marshalled JSON-RPC request. -func NewRequest(id interface{}, method string, params []interface{}) (*Request, error) { - if !IsValidIDType(id) { - str := fmt.Sprintf("the id of type '%T' is invalid", id) - return nil, makeError(ErrInvalidType, str) - } - - rawParams := make([]json.RawMessage, 0, len(params)) - for _, param := range params { - marshalledParam, err := json.Marshal(param) - if err != nil { - return nil, err - } - rawMessage := json.RawMessage(marshalledParam) - rawParams = append(rawParams, rawMessage) - } - - return &Request{ - JSONRPC: "1.0", - ID: id, - Method: method, - Params: rawParams, - }, nil -} - -// Response is the general form of a JSON-RPC response. The type of the Result -// field varies from one command to the next, so it is implemented as an -// interface. The ID field has to be a pointer for Go to put a null in it when -// empty. -type Response struct { - Result json.RawMessage `json:"result"` - Error *RPCError `json:"error"` - ID *interface{} `json:"id"` -} - -// NewResponse returns a new JSON-RPC response object given the provided id, -// marshalled result, and RPC error. This function is only provided in case the -// caller wants to construct raw responses for some reason. -// -// Typically callers will instead want to create the fully marshalled JSON-RPC -// response to send over the appmessage with the MarshalResponse function. -func NewResponse(id interface{}, marshalledResult []byte, rpcErr *RPCError) (*Response, error) { - if !IsValidIDType(id) { - str := fmt.Sprintf("the id of type '%T' is invalid", id) - return nil, makeError(ErrInvalidType, str) - } - - pid := &id - return &Response{ - Result: marshalledResult, - Error: rpcErr, - ID: pid, - }, nil -} - -// MarshalResponse marshals the passed id, result, and RPCError to a JSON-RPC -// response byte slice that is suitable for transmission to a JSON-RPC client. -func MarshalResponse(id interface{}, result interface{}, rpcErr *RPCError) ([]byte, error) { - marshalledResult, err := json.Marshal(result) - if err != nil { - return nil, err - } - response, err := NewResponse(id, marshalledResult, rpcErr) - if err != nil { - return nil, err - } - return json.Marshal(&response) -} diff --git a/infrastructure/network/rpc/model/jsonrpc_errors.go b/infrastructure/network/rpc/model/jsonrpc_errors.go deleted file mode 100644 index 81a39ea01..000000000 --- a/infrastructure/network/rpc/model/jsonrpc_errors.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model - -// Standard JSON-RPC 2.0 errors. -var ( - ErrRPCInvalidRequest = &RPCError{ - Code: -32600, - Message: "Invalid request", - } - ErrRPCMethodNotFound = &RPCError{ - Code: -32601, - Message: "Method not found", - } - ErrRPCInvalidParams = &RPCError{ - Code: -32602, - Message: "Invalid parameters", - } - ErrRPCInternal = &RPCError{ - Code: -32603, - Message: "Internal error", - } - ErrRPCParse = &RPCError{ - Code: -32700, - Message: "Parse error", - } -) - -// General application defined JSON errors. -const ( - ErrRPCMisc RPCErrorCode = -1 - ErrRPCForbiddenBySafeMode RPCErrorCode = -2 - ErrRPCType RPCErrorCode = -3 - ErrRPCInvalidAddressOrKey RPCErrorCode = -5 - ErrRPCOutOfMemory RPCErrorCode = -7 - ErrRPCInvalidParameter RPCErrorCode = -8 - ErrRPCDatabase RPCErrorCode = -20 - ErrRPCDeserialization RPCErrorCode = -22 - ErrRPCVerify RPCErrorCode = -25 -) - -// Peer-to-peer client errors. -const ( - ErrRPCClientNotConnected RPCErrorCode = -9 - ErrRPCClientInInitialDownload RPCErrorCode = -10 - ErrRPCClientNodeNotAdded RPCErrorCode = -24 -) - -// Specific Errors related to commands. These are the ones a user of the RPC -// server are most likely to see. Generally, the codes should match one of the -// more general errors above. -const ( - ErrRPCBlockNotFound RPCErrorCode = -5 - ErrRPCBlockCount RPCErrorCode = -5 - ErrRPCSelectedTipHash RPCErrorCode = -5 - ErrRPCDifficulty RPCErrorCode = -5 - ErrRPCOutOfRange RPCErrorCode = -1 - ErrRPCNoTxInfo RPCErrorCode = -5 - ErrRPCNoAcceptanceIndex RPCErrorCode = -5 - ErrRPCNoNewestBlockInfo RPCErrorCode = -5 - ErrRPCInvalidTxVout RPCErrorCode = -5 - ErrRPCSubnetworkNotFound RPCErrorCode = -5 - ErrRPCRawTxString RPCErrorCode = -32602 - ErrRPCDecodeHexString RPCErrorCode = -22 - ErrRPCOrphanBlock RPCErrorCode = -6 - ErrRPCBlockInvalid RPCErrorCode = -5 -) - -// Errors that are specific to kaspad. -const ( - ErrRPCUnimplemented RPCErrorCode = -1 -) diff --git a/infrastructure/network/rpc/model/jsonrpc_test.go b/infrastructure/network/rpc/model/jsonrpc_test.go deleted file mode 100644 index d58cd184d..000000000 --- a/infrastructure/network/rpc/model/jsonrpc_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model_test - -import ( - "encoding/json" - "github.com/pkg/errors" - "reflect" - "testing" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// TestIsValidIDType ensures the IsValidIDType function behaves as expected. -func TestIsValidIDType(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - id interface{} - isValid bool - }{ - {"int", int(1), true}, - {"int8", int8(1), true}, - {"int16", int16(1), true}, - {"int32", int32(1), true}, - {"int64", int64(1), true}, - {"uint", uint(1), true}, - {"uint8", uint8(1), true}, - {"uint16", uint16(1), true}, - {"uint32", uint32(1), true}, - {"uint64", uint64(1), true}, - {"string", "1", true}, - {"nil", nil, true}, - {"float32", float32(1), true}, - {"float64", float64(1), true}, - {"bool", true, false}, - {"chan int", make(chan int), false}, - {"complex64", complex64(1), false}, - {"complex128", complex128(1), false}, - {"func", func() {}, false}, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - if model.IsValidIDType(test.id) != test.isValid { - t.Errorf("Test #%d (%s) valid mismatch - got %v, "+ - "want %v", i, test.name, !test.isValid, - test.isValid) - continue - } - } -} - -// TestMarshalResponse ensures the MarshalResponse function works as expected. -func TestMarshalResponse(t *testing.T) { - t.Parallel() - - testID := 1 - tests := []struct { - name string - result interface{} - jsonErr *model.RPCError - expected []byte - }{ - { - name: "ordinary bool result with no error", - result: true, - jsonErr: nil, - expected: []byte(`{"result":true,"error":null,"id":1}`), - }, - { - name: "result with error", - result: nil, - jsonErr: func() *model.RPCError { - return model.NewRPCError(model.ErrRPCBlockNotFound, "123 not found") - }(), - expected: []byte(`{"result":null,"error":{"code":-5,"message":"123 not found"},"id":1}`), - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - _, _ = i, test - marshalled, err := model.MarshalResponse(testID, test.result, test.jsonErr) - if err != nil { - t.Errorf("Test #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - - if !reflect.DeepEqual(marshalled, test.expected) { - t.Errorf("Test #%d (%s) mismatched result - got %s, "+ - "want %s", i, test.name, marshalled, - test.expected) - } - } -} - -// TestMiscErrors tests a few error conditions not covered elsewhere. -func TestMiscErrors(t *testing.T) { - t.Parallel() - - // Force an error in NewRequest by giving it a parameter type that is - // not supported. - _, err := model.NewRequest(nil, "test", []interface{}{make(chan int)}) - if err == nil { - t.Error("NewRequest: did not receive error") - return - } - - // Force an error in MarshalResponse by giving it an id type that is not - // supported. - wantErr := model.Error{ErrorCode: model.ErrInvalidType} - _, err = model.MarshalResponse(make(chan int), nil, nil) - var rpcModelErr model.Error - if ok := errors.As(err, &rpcModelErr); !ok || rpcModelErr.ErrorCode != wantErr.ErrorCode { - t.Errorf("MarshalResult: did not receive expected error - got "+ - "%v (%[1]T), want %v (%[2]T)", err, wantErr) - return - } - - // Force an error in MarshalResponse by giving it a result type that - // can't be marshalled. - _, err = model.MarshalResponse(1, make(chan int), nil) - if jErr := &(json.UnsupportedTypeError{}); !errors.As(err, &jErr) { - wantErr := &json.UnsupportedTypeError{} - t.Errorf("MarshalResult: did not receive expected error - got "+ - "%v (%[1]T), want %T", err, wantErr) - return - } -} - -// TestRPCError tests the error output for the RPCError type. -func TestRPCError(t *testing.T) { - t.Parallel() - - tests := []struct { - in *model.RPCError - want string - }{ - { - model.ErrRPCInvalidRequest, - "-32600: Invalid request", - }, - { - model.ErrRPCMethodNotFound, - "-32601: Method not found", - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - result := test.in.Error() - if result != test.want { - t.Errorf("Error #%d\n got: %s want: %s", i, result, - test.want) - continue - } - } -} diff --git a/infrastructure/network/rpc/model/register.go b/infrastructure/network/rpc/model/register.go deleted file mode 100644 index 7b6226db5..000000000 --- a/infrastructure/network/rpc/model/register.go +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model - -import ( - "encoding/json" - "fmt" - "reflect" - "sort" - "strconv" - "strings" - "sync" -) - -// UsageFlag define flags that specify additional properties about the -// circumstances under which a command can be used. -type UsageFlag uint32 - -const ( - // UFWebsocketOnly indicates that the command can only be used when - // communicating with an RPC server over websockets. This typically - // applies to notifications and notification registration functions - // since neiher makes since when using a single-shot HTTP-POST request. - UFWebsocketOnly UsageFlag = 1 << iota - - // UFNotification indicates that the command is actually a notification. - // This means when it is marshalled, the ID must be nil. - UFNotification - - // highestUsageFlagBit is the maximum usage flag bit and is used in the - // stringer and tests to ensure all of the above constants have been - // tested. - highestUsageFlagBit -) - -// Map of UsageFlag values back to their constant names for pretty printing. -var usageFlagStrings = map[UsageFlag]string{ - UFWebsocketOnly: "UFWebsocketOnly", - UFNotification: "UFNotification", -} - -// String returns the UsageFlag in human-readable form. -func (fl UsageFlag) String() string { - // No flags are set. - if fl == 0 { - return "0x0" - } - - // Add individual bit flags. - s := "" - for flag := UFWebsocketOnly; flag < highestUsageFlagBit; flag <<= 1 { - if fl&flag == flag { - s += usageFlagStrings[flag] + "|" - fl -= flag - } - } - - // Add remaining value as raw hex. - s = strings.TrimRight(s, "|") - if fl != 0 { - s += "|0x" + strconv.FormatUint(uint64(fl), 16) - } - s = strings.TrimLeft(s, "|") - return s -} - -// methodInfo keeps track of information about each registered method such as -// the parameter information. -type methodInfo struct { - maxParams int - numReqParams int - numOptParams int - defaults map[int]reflect.Value - flags UsageFlag - usage string -} - -var ( - // These fields are used to map the registered types to method names. - methodsLock sync.RWMutex - methodToConcreteType = make(map[string]reflect.Type) - methodToInfo = make(map[string]methodInfo) - concreteTypeToMethod = make(map[reflect.Type]string) -) - -// baseKindString returns the base kind for a given reflect.Type after -// indirecting through all pointers. -func baseKindString(rt reflect.Type) string { - numIndirects := 0 - for rt.Kind() == reflect.Ptr { - numIndirects++ - rt = rt.Elem() - } - - return fmt.Sprintf("%s%s", strings.Repeat("*", numIndirects), rt.Kind()) -} - -// isAcceptableKind returns whether or not the passed field type is a supported -// type. It is called after the first pointer indirection, so further pointers -// are not supported. -func isAcceptableKind(kind reflect.Kind) bool { - switch kind { - case reflect.Chan: - fallthrough - case reflect.Complex64: - fallthrough - case reflect.Complex128: - fallthrough - case reflect.Func: - fallthrough - case reflect.Ptr: - fallthrough - case reflect.Interface: - return false - } - - return true -} - -// RegisterCmd registers a new command that will automatically marshal to and -// from JSON-RPC with full type checking and positional parameter support. It -// also accepts usage flags which identify the circumstances under which the -// command can be used. -// -// This package automatically registers all of the exported commands by default -// using this function, however it is also exported so callers can easily -// register custom types. -// -// The type format is very strict since it needs to be able to automatically -// marshal to and from JSON-RPC 1.0. The following enumerates the requirements: -// -// - The provided command must be a single pointer to a struct -// - All fields must be exported -// - The order of the positional parameters in the marshalled JSON will be in -// the same order as declared in the struct definition -// - Struct embedding is not supported -// - Struct fields may NOT be channels, functions, complex, or interface -// - A field in the provided struct with a pointer is treated as optional -// - Multiple indirections (i.e **int) are not supported -// - Once the first optional field (pointer) is encountered, the remaining -// fields must also be optional fields (pointers) as required by positional -// params -// - A field that has a 'jsonrpcdefault' struct tag must be an optional field -// (pointer) -// -// NOTE: This function only needs to be able to examine the structure of the -// passed struct, so it does not need to be an actual instance. Therefore, it -// is recommended to simply pass a nil pointer cast to the appropriate type. -// For example, (*FooCmd)(nil). -func RegisterCmd(method string, cmd interface{}, flags UsageFlag) error { - methodsLock.Lock() - defer methodsLock.Unlock() - - if _, ok := methodToConcreteType[method]; ok { - str := fmt.Sprintf("method %q is already registered", method) - return makeError(ErrDuplicateMethod, str) - } - - // Ensure that no unrecognized flag bits were specified. - if ^(highestUsageFlagBit-1)&flags != 0 { - str := fmt.Sprintf("invalid usage flags specified for method "+ - "%s: %s", method, flags) - return makeError(ErrInvalidUsageFlags, str) - } - - rtp := reflect.TypeOf(cmd) - if rtp.Kind() != reflect.Ptr { - str := fmt.Sprintf("type must be *struct not '%s (%s)'", rtp, - rtp.Kind()) - return makeError(ErrInvalidType, str) - } - rt := rtp.Elem() - if rt.Kind() != reflect.Struct { - str := fmt.Sprintf("type must be *struct not '%s (*%s)'", - rtp, rt.Kind()) - return makeError(ErrInvalidType, str) - } - - // Enumerate the struct fields to validate them and gather parameter - // information. - numFields := rt.NumField() - numOptFields := 0 - defaults := make(map[int]reflect.Value) - for i := 0; i < numFields; i++ { - rtf := rt.Field(i) - if rtf.Anonymous { - str := fmt.Sprintf("embedded fields are not supported "+ - "(field name: %q)", rtf.Name) - return makeError(ErrEmbeddedType, str) - } - if rtf.PkgPath != "" { - str := fmt.Sprintf("unexported fields are not supported "+ - "(field name: %q)", rtf.Name) - return makeError(ErrUnexportedField, str) - } - - // Disallow types that can't be JSON encoded. Also, determine - // if the field is optional based on it being a pointer. - var isOptional bool - switch kind := rtf.Type.Kind(); kind { - case reflect.Ptr: - isOptional = true - kind = rtf.Type.Elem().Kind() - fallthrough - default: - if !isAcceptableKind(kind) { - str := fmt.Sprintf("unsupported field type "+ - "'%s (%s)' (field name %q)", rtf.Type, - baseKindString(rtf.Type), rtf.Name) - return makeError(ErrUnsupportedFieldType, str) - } - } - - // Count the optional fields and ensure all fields after the - // first optional field are also optional. - if isOptional { - numOptFields++ - } else { - if numOptFields > 0 { - str := fmt.Sprintf("all fields after the first "+ - "optional field must also be optional "+ - "(field name %q)", rtf.Name) - return makeError(ErrNonOptionalField, str) - } - } - - // Ensure the default value can be unsmarshalled into the type - // and that defaults are only specified for optional fields. - if tag := rtf.Tag.Get("jsonrpcdefault"); tag != "" { - if !isOptional { - str := fmt.Sprintf("required fields must not "+ - "have a default specified (field name "+ - "%q)", rtf.Name) - return makeError(ErrNonOptionalDefault, str) - } - - rvf := reflect.New(rtf.Type.Elem()) - err := json.Unmarshal([]byte(tag), rvf.Interface()) - if err != nil { - str := fmt.Sprintf("default value of %q is "+ - "the wrong type (field name %q)", tag, - rtf.Name) - return makeError(ErrMismatchedDefault, str) - } - defaults[i] = rvf - } - } - - // Update the registration maps. - methodToConcreteType[method] = rtp - methodToInfo[method] = methodInfo{ - maxParams: numFields, - numReqParams: numFields - numOptFields, - numOptParams: numOptFields, - defaults: defaults, - flags: flags, - } - concreteTypeToMethod[rtp] = method - return nil -} - -// MustRegisterCommand performs the same function as RegisterCmd except it panics -// if there is an error. This should only be called from package init -// functions. -func MustRegisterCommand(method string, cmd interface{}, flags UsageFlag) { - if err := RegisterCmd(method, cmd, flags); err != nil { - panic(fmt.Sprintf("failed to register type %q: %s\n", method, - err)) - } -} - -// RegisteredCmdMethods returns a sorted list of methods for all registered -// commands. -func RegisteredCmdMethods() []string { - methodsLock.Lock() - defer methodsLock.Unlock() - - methods := make([]string, 0, len(methodToInfo)) - for k := range methodToInfo { - methods = append(methods, k) - } - - sort.Strings(methods) - return methods -} diff --git a/infrastructure/network/rpc/model/register_test.go b/infrastructure/network/rpc/model/register_test.go deleted file mode 100644 index fce484eb2..000000000 --- a/infrastructure/network/rpc/model/register_test.go +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model_test - -import ( - "github.com/pkg/errors" - "reflect" - "sort" - "testing" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// TestUsageFlagStringer tests the stringized output for the UsageFlag type. -func TestUsageFlagStringer(t *testing.T) { - t.Parallel() - - tests := []struct { - in model.UsageFlag - want string - }{ - {0, "0x0"}, - {model.UFWebsocketOnly, "UFWebsocketOnly"}, - {model.UFNotification, "UFNotification"}, - {model.UFWebsocketOnly | model.UFNotification, - "UFWebsocketOnly|UFNotification"}, - {model.UFWebsocketOnly | model.UFNotification | (1 << 31), - "UFWebsocketOnly|UFNotification|0x80000000"}, - } - - // Detect additional usage flags that don't have the stringer added. - numUsageFlags := 0 - highestUsageFlagBit := model.TstHighestUsageFlagBit - for highestUsageFlagBit > 1 { - numUsageFlags++ - highestUsageFlagBit >>= 1 - } - if len(tests)-3 != numUsageFlags { - t.Errorf("It appears a usage flag was added without adding " + - "an associated stringer test") - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - result := test.in.String() - if result != test.want { - t.Errorf("String #%d\n got: %s want: %s", i, result, - test.want) - continue - } - } -} - -// TestRegisterCmdErrors ensures the RegisterCmd function returns the expected -// error when provided with invalid types. -func TestRegisterCmdErrors(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - method string - cmdFunc func() interface{} - flags model.UsageFlag - err model.Error - }{ - { - name: "duplicate method", - method: "getBlock", - cmdFunc: func() interface{} { - return struct{}{} - }, - err: model.Error{ErrorCode: model.ErrDuplicateMethod}, - }, - { - name: "invalid usage flags", - method: "registerTestCmd", - cmdFunc: func() interface{} { - return 0 - }, - flags: model.TstHighestUsageFlagBit, - err: model.Error{ErrorCode: model.ErrInvalidUsageFlags}, - }, - { - name: "invalid type", - method: "registerTestCmd", - cmdFunc: func() interface{} { - return 0 - }, - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "invalid type 2", - method: "registerTestCmd", - cmdFunc: func() interface{} { - return &[]string{} - }, - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "embedded field", - method: "registerTestCmd", - cmdFunc: func() interface{} { - type test struct{ int } - return (*test)(nil) - }, - err: model.Error{ErrorCode: model.ErrEmbeddedType}, - }, - { - name: "unexported field", - method: "registerTestCmd", - cmdFunc: func() interface{} { - type test struct{ a int } - return (*test)(nil) - }, - err: model.Error{ErrorCode: model.ErrUnexportedField}, - }, - { - name: "unsupported field type 1", - method: "registerTestCmd", - cmdFunc: func() interface{} { - type test struct{ A **int } - return (*test)(nil) - }, - err: model.Error{ErrorCode: model.ErrUnsupportedFieldType}, - }, - { - name: "unsupported field type 2", - method: "registerTestCmd", - cmdFunc: func() interface{} { - type test struct{ A chan int } - return (*test)(nil) - }, - err: model.Error{ErrorCode: model.ErrUnsupportedFieldType}, - }, - { - name: "unsupported field type 3", - method: "registerTestCmd", - cmdFunc: func() interface{} { - type test struct{ A complex64 } - return (*test)(nil) - }, - err: model.Error{ErrorCode: model.ErrUnsupportedFieldType}, - }, - { - name: "unsupported field type 4", - method: "registerTestCmd", - cmdFunc: func() interface{} { - type test struct{ A complex128 } - return (*test)(nil) - }, - err: model.Error{ErrorCode: model.ErrUnsupportedFieldType}, - }, - { - name: "unsupported field type 5", - method: "registerTestCmd", - cmdFunc: func() interface{} { - type test struct{ A func() } - return (*test)(nil) - }, - err: model.Error{ErrorCode: model.ErrUnsupportedFieldType}, - }, - { - name: "unsupported field type 6", - method: "registerTestCmd", - cmdFunc: func() interface{} { - type test struct{ A interface{} } - return (*test)(nil) - }, - err: model.Error{ErrorCode: model.ErrUnsupportedFieldType}, - }, - { - name: "required after optional", - method: "registerTestCmd", - cmdFunc: func() interface{} { - type test struct { - A *int - B int - } - return (*test)(nil) - }, - err: model.Error{ErrorCode: model.ErrNonOptionalField}, - }, - { - name: "non-optional with default", - method: "registerTestCmd", - cmdFunc: func() interface{} { - type test struct { - A int `jsonrpcdefault:"1"` - } - return (*test)(nil) - }, - err: model.Error{ErrorCode: model.ErrNonOptionalDefault}, - }, - { - name: "mismatched default", - method: "registerTestCmd", - cmdFunc: func() interface{} { - type test struct { - A *int `jsonrpcdefault:"1.7"` - } - return (*test)(nil) - }, - err: model.Error{ErrorCode: model.ErrMismatchedDefault}, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - err := model.RegisterCmd(test.method, test.cmdFunc(), - test.flags) - if reflect.TypeOf(err) != reflect.TypeOf(test.err) { - t.Errorf("Test #%d (%s) wrong error - got %T, "+ - "want %T", i, test.name, err, test.err) - continue - } - var gotRPCModelErr model.Error - errors.As(err, &gotRPCModelErr) - gotErrorCode := gotRPCModelErr.ErrorCode - if gotErrorCode != test.err.ErrorCode { - t.Errorf("Test #%d (%s) mismatched error code - got "+ - "%v, want %v", i, test.name, gotErrorCode, - test.err.ErrorCode) - continue - } - } -} - -// TestMustRegisterCmdPanic ensures the MustRegisterCommand function panics when -// used to register an invalid type. -func TestMustRegisterCmdPanic(t *testing.T) { - t.Parallel() - - // Setup a defer to catch the expected panic to ensure it actually - // paniced. - defer func() { - if err := recover(); err == nil { - t.Error("MustRegisterCommand did not panic as expected") - } - }() - - // Intentionally try to register an invalid type to force a panic. - model.MustRegisterCommand("panicme", 0, 0) -} - -// TestRegisteredCmdMethods tests the RegisteredCmdMethods function ensure it -// works as expected. -func TestRegisteredCmdMethods(t *testing.T) { - t.Parallel() - - // Ensure the registered methods are returned. - methods := model.RegisteredCmdMethods() - if len(methods) == 0 { - t.Fatal("RegisteredCmdMethods: no methods") - } - - // Ensure the returned methods are sorted. - sortedMethods := make([]string, len(methods)) - copy(sortedMethods, methods) - sort.Strings(sortedMethods) - if !reflect.DeepEqual(sortedMethods, methods) { - t.Fatal("RegisteredCmdMethods: methods are not sorted") - } -} diff --git a/infrastructure/network/rpc/model/rpc_commands.go b/infrastructure/network/rpc/model/rpc_commands.go deleted file mode 100644 index 59e729780..000000000 --- a/infrastructure/network/rpc/model/rpc_commands.go +++ /dev/null @@ -1,633 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -// NOTE: This file is intended to house the RPC commands that are supported by -// a kaspa rpc server. - -package model - -import ( - "encoding/json" - "fmt" -) - -// ConnectCmd defines the connect JSON-RPC command. -type ConnectCmd struct { - Address string - IsPermanent *bool `jsonrpcdefault:"false"` -} - -// NewConnectCmd returns a new instance which can be used to issue a connection -// JSON-RPC command. -func NewConnectCmd(address string, isPermanent *bool) *ConnectCmd { - return &ConnectCmd{ - Address: address, - IsPermanent: isPermanent, - } -} - -// DisconnectCmd defines the disconnect JSON-RPC command. -type DisconnectCmd struct { - Address string -} - -// NewDisconnectCmd returns a new instance which can be used to issue an disconnect -// JSON-RPC command. -func NewDisconnectCmd(address string) *DisconnectCmd { - return &DisconnectCmd{ - Address: address, - } -} - -// TransactionInput represents the inputs to a transaction. Specifically a -// transaction hash and output number pair. -type TransactionInput struct { - TxID string `json:"txId"` - Vout uint32 `json:"vout"` -} - -// GetSelectedTipHashCmd defines the getSelectedTipHash JSON-RPC command. -type GetSelectedTipHashCmd struct{} - -// NewGetSelectedTipHashCmd returns a new instance which can be used to issue a -// getSelectedTipHash JSON-RPC command. -func NewGetSelectedTipHashCmd() *GetSelectedTipHashCmd { - return &GetSelectedTipHashCmd{} -} - -// GetBlockCmd defines the getBlock JSON-RPC command. -type GetBlockCmd struct { - Hash string - Verbose *bool `jsonrpcdefault:"true"` - VerboseTx *bool `jsonrpcdefault:"false"` - Subnetwork *string -} - -// NewGetBlockCmd returns a new instance which can be used to issue a getBlock -// JSON-RPC command. -// -// The parameters which are pointers indicate they are optional. Passing nil -// for optional parameters will use the default value. -func NewGetBlockCmd(hash string, verbose, verboseTx *bool, subnetworkID *string) *GetBlockCmd { - return &GetBlockCmd{ - Hash: hash, - Verbose: verbose, - VerboseTx: verboseTx, - Subnetwork: subnetworkID, - } -} - -// GetBlocksCmd defines the getBlocks JSON-RPC command. -type GetBlocksCmd struct { - IncludeRawBlockData bool `json:"includeRawBlockData"` - IncludeVerboseBlockData bool `json:"includeVerboseBlockData"` - LowHash *string `json:"lowHash"` -} - -// NewGetBlocksCmd returns a new instance which can be used to issue a -// GetGetBlocks JSON-RPC command. -func NewGetBlocksCmd(includeRawBlockData bool, includeVerboseBlockData bool, lowHash *string) *GetBlocksCmd { - return &GetBlocksCmd{ - IncludeRawBlockData: includeRawBlockData, - IncludeVerboseBlockData: includeVerboseBlockData, - LowHash: lowHash, - } -} - -// GetBlockDAGInfoCmd defines the getBlockDagInfo JSON-RPC command. -type GetBlockDAGInfoCmd struct{} - -// NewGetBlockDAGInfoCmd returns a new instance which can be used to issue a -// getBlockDagInfo JSON-RPC command. -func NewGetBlockDAGInfoCmd() *GetBlockDAGInfoCmd { - return &GetBlockDAGInfoCmd{} -} - -// GetBlockCountCmd defines the getBlockCount JSON-RPC command. -type GetBlockCountCmd struct{} - -// NewGetBlockCountCmd returns a new instance which can be used to issue a -// getBlockCount JSON-RPC command. -func NewGetBlockCountCmd() *GetBlockCountCmd { - return &GetBlockCountCmd{} -} - -// GetBlockHeaderCmd defines the getBlockHeader JSON-RPC command. -type GetBlockHeaderCmd struct { - Hash string - Verbose *bool `jsonrpcdefault:"true"` -} - -// NewGetBlockHeaderCmd returns a new instance which can be used to issue a -// getBlockHeader JSON-RPC command. -func NewGetBlockHeaderCmd(hash string, verbose *bool) *GetBlockHeaderCmd { - return &GetBlockHeaderCmd{ - Hash: hash, - Verbose: verbose, - } -} - -// TemplateRequest is a request object as defined in BIP22. It is optionally -// provided as an pointer argument to GetBlockTemplateCmd. -type TemplateRequest struct { - Mode string `json:"mode,omitempty"` - - // Optional long polling. - LongPollID string `json:"longPollId,omitempty"` - - // Optional template tweaking. SigOpLimit and MassLimit can be int64 - // or bool. - SigOpLimit interface{} `json:"sigOpLimit,omitempty"` - MassLimit interface{} `json:"massLimit,omitempty"` - MaxVersion uint32 `json:"maxVersion,omitempty"` - - // Basic pool extension from BIP 0023. - Target string `json:"target,omitempty"` - - // Block proposal from BIP 0023. Data is only provided when Mode is - // "proposal". - Data string `json:"data,omitempty"` - WorkID string `json:"workId,omitempty"` - - PayAddress string `json:"payAddress"` -} - -// convertTemplateRequestField potentially converts the provided value as -// needed. -func convertTemplateRequestField(fieldName string, iface interface{}) (interface{}, error) { - switch val := iface.(type) { - case nil: - return nil, nil - case bool: - return val, nil - case float64: - if val == float64(int64(val)) { - return int64(val), nil - } - } - - str := fmt.Sprintf("the %s field must be unspecified, a boolean, or "+ - "a 64-bit integer", fieldName) - return nil, makeError(ErrInvalidType, str) -} - -// UnmarshalJSON provides a custom Unmarshal method for TemplateRequest. This -// is necessary because the SigOpLimit and MassLimit fields can only be specific -// types. -func (t *TemplateRequest) UnmarshalJSON(data []byte) error { - type templateRequest TemplateRequest - - request := (*templateRequest)(t) - if err := json.Unmarshal(data, &request); err != nil { - return err - } - - // The SigOpLimit field can only be nil, bool, or int64. - val, err := convertTemplateRequestField("sigOpLimit", request.SigOpLimit) - if err != nil { - return err - } - request.SigOpLimit = val - - // The MassLimit field can only be nil, bool, or int64. - val, err = convertTemplateRequestField("massLimit", request.MassLimit) - if err != nil { - return err - } - request.MassLimit = val - - return nil -} - -// GetBlockTemplateCmd defines the getBlockTemplate JSON-RPC command. -type GetBlockTemplateCmd struct { - Request *TemplateRequest -} - -// NewGetBlockTemplateCmd returns a new instance which can be used to issue a -// getBlockTemplate JSON-RPC command. -// -// The parameters which are pointers indicate they are optional. Passing nil -// for optional parameters will use the default value. -func NewGetBlockTemplateCmd(request *TemplateRequest) *GetBlockTemplateCmd { - return &GetBlockTemplateCmd{ - Request: request, - } -} - -// GetChainFromBlockCmd defines the getChainFromBlock JSON-RPC command. -type GetChainFromBlockCmd struct { - IncludeBlocks bool `json:"includeBlocks"` - StartHash *string `json:"startHash"` -} - -// NewGetChainFromBlockCmd returns a new instance which can be used to issue a -// GetChainFromBlock JSON-RPC command. -func NewGetChainFromBlockCmd(includeBlocks bool, startHash *string) *GetChainFromBlockCmd { - return &GetChainFromBlockCmd{ - IncludeBlocks: includeBlocks, - StartHash: startHash, - } -} - -// GetDAGTipsCmd defines the getDagTips JSON-RPC command. -type GetDAGTipsCmd struct{} - -// NewGetDAGTipsCmd returns a new instance which can be used to issue a -// getDagTips JSON-RPC command. -func NewGetDAGTipsCmd() *GetDAGTipsCmd { - return &GetDAGTipsCmd{} -} - -// GetConnectionCountCmd defines the getConnectionCount JSON-RPC command. -type GetConnectionCountCmd struct{} - -// NewGetConnectionCountCmd returns a new instance which can be used to issue a -// getConnectionCount JSON-RPC command. -func NewGetConnectionCountCmd() *GetConnectionCountCmd { - return &GetConnectionCountCmd{} -} - -// GetDifficultyCmd defines the getDifficulty JSON-RPC command. -type GetDifficultyCmd struct{} - -// NewGetDifficultyCmd returns a new instance which can be used to issue a -// getDifficulty JSON-RPC command. -func NewGetDifficultyCmd() *GetDifficultyCmd { - return &GetDifficultyCmd{} -} - -// GetInfoCmd defines the getInfo JSON-RPC command. -type GetInfoCmd struct{} - -// NewGetInfoCmd returns a new instance which can be used to issue a -// getInfo JSON-RPC command. -func NewGetInfoCmd() *GetInfoCmd { - return &GetInfoCmd{} -} - -// GetMempoolEntryCmd defines the getMempoolEntry JSON-RPC command. -type GetMempoolEntryCmd struct { - TxID string -} - -// NewGetMempoolEntryCmd returns a new instance which can be used to issue a -// getMempoolEntry JSON-RPC command. -func NewGetMempoolEntryCmd(txHash string) *GetMempoolEntryCmd { - return &GetMempoolEntryCmd{ - TxID: txHash, - } -} - -// GetMempoolInfoCmd defines the getMempoolInfo JSON-RPC command. -type GetMempoolInfoCmd struct{} - -// NewGetMempoolInfoCmd returns a new instance which can be used to issue a -// getmempool JSON-RPC command. -func NewGetMempoolInfoCmd() *GetMempoolInfoCmd { - return &GetMempoolInfoCmd{} -} - -// GetNetworkInfoCmd defines the getNetworkInfo JSON-RPC command. -type GetNetworkInfoCmd struct{} - -// NewGetNetworkInfoCmd returns a new instance which can be used to issue a -// getNetworkInfo JSON-RPC command. -func NewGetNetworkInfoCmd() *GetNetworkInfoCmd { - return &GetNetworkInfoCmd{} -} - -// GetNetTotalsCmd defines the getNetTotals JSON-RPC command. -type GetNetTotalsCmd struct{} - -// NewGetNetTotalsCmd returns a new instance which can be used to issue a -// getNetTotals JSON-RPC command. -func NewGetNetTotalsCmd() *GetNetTotalsCmd { - return &GetNetTotalsCmd{} -} - -// GetConnectedPeerInfoCmd defines the getConnectedPeerInfo JSON-RPC command. -type GetConnectedPeerInfoCmd struct{} - -// NewGetConnectedPeerInfoCmd returns a new instance which can be used to issue a getpeer -// JSON-RPC command. -func NewGetConnectedPeerInfoCmd() *GetConnectedPeerInfoCmd { - return &GetConnectedPeerInfoCmd{} -} - -// GetRawMempoolCmd defines the getmempool JSON-RPC command. -type GetRawMempoolCmd struct { - Verbose *bool `jsonrpcdefault:"false"` -} - -// NewGetRawMempoolCmd returns a new instance which can be used to issue a -// getRawMempool JSON-RPC command. -// -// The parameters which are pointers indicate they are optional. Passing nil -// for optional parameters will use the default value. -func NewGetRawMempoolCmd(verbose *bool) *GetRawMempoolCmd { - return &GetRawMempoolCmd{ - Verbose: verbose, - } -} - -// GetSubnetworkCmd defines the getSubnetwork JSON-RPC command. -type GetSubnetworkCmd struct { - SubnetworkID string -} - -// NewGetSubnetworkCmd returns a new instance which can be used to issue a -// getSubnetworkCmd command. -func NewGetSubnetworkCmd(subnetworkID string) *GetSubnetworkCmd { - return &GetSubnetworkCmd{ - SubnetworkID: subnetworkID, - } -} - -// GetTxOutCmd defines the getTxOut JSON-RPC command. -type GetTxOutCmd struct { - TxID string - Vout uint32 - IncludeMempool *bool `jsonrpcdefault:"true"` -} - -// NewGetTxOutCmd returns a new instance which can be used to issue a getTxOut -// JSON-RPC command. -// -// The parameters which are pointers indicate they are optional. Passing nil -// for optional parameters will use the default value. -func NewGetTxOutCmd(txHash string, vout uint32, includeMempool *bool) *GetTxOutCmd { - return &GetTxOutCmd{ - TxID: txHash, - Vout: vout, - IncludeMempool: includeMempool, - } -} - -// GetTxOutSetInfoCmd defines the getTxOutSetInfo JSON-RPC command. -type GetTxOutSetInfoCmd struct{} - -// NewGetTxOutSetInfoCmd returns a new instance which can be used to issue a -// getTxOutSetInfo JSON-RPC command. -func NewGetTxOutSetInfoCmd() *GetTxOutSetInfoCmd { - return &GetTxOutSetInfoCmd{} -} - -// HelpCmd defines the help JSON-RPC command. -type HelpCmd struct { - Command *string -} - -// NewHelpCmd returns a new instance which can be used to issue a help JSON-RPC -// command. -// -// The parameters which are pointers indicate they are optional. Passing nil -// for optional parameters will use the default value. -func NewHelpCmd(command *string) *HelpCmd { - return &HelpCmd{ - Command: command, - } -} - -// PingCmd defines the ping JSON-RPC command. -type PingCmd struct{} - -// NewPingCmd returns a new instance which can be used to issue a ping JSON-RPC -// command. -func NewPingCmd() *PingCmd { - return &PingCmd{} -} - -// SendRawTransactionCmd defines the sendRawTransaction JSON-RPC command. -type SendRawTransactionCmd struct { - HexTx string - AllowHighFees *bool `jsonrpcdefault:"false"` -} - -// NewSendRawTransactionCmd returns a new instance which can be used to issue a -// sendRawTransaction JSON-RPC command. -// -// The parameters which are pointers indicate they are optional. Passing nil -// for optional parameters will use the default value. -func NewSendRawTransactionCmd(hexTx string, allowHighFees *bool) *SendRawTransactionCmd { - return &SendRawTransactionCmd{ - HexTx: hexTx, - AllowHighFees: allowHighFees, - } -} - -// StopCmd defines the stop JSON-RPC command. -type StopCmd struct{} - -// NewStopCmd returns a new instance which can be used to issue a stop JSON-RPC -// command. -func NewStopCmd() *StopCmd { - return &StopCmd{} -} - -// SubmitBlockOptions represents the optional options struct provided with a -// SubmitBlockCmd command. -type SubmitBlockOptions struct { - // must be provided if server provided a workid with template. - WorkID string `json:"workId,omitempty"` -} - -// SubmitBlockCmd defines the submitBlock JSON-RPC command. -type SubmitBlockCmd struct { - HexBlock string - Options *SubmitBlockOptions -} - -// NewSubmitBlockCmd returns a new instance which can be used to issue a -// submitBlock JSON-RPC command. -// -// The parameters which are pointers indicate they are optional. Passing nil -// for optional parameters will use the default value. -func NewSubmitBlockCmd(hexBlock string, options *SubmitBlockOptions) *SubmitBlockCmd { - return &SubmitBlockCmd{ - HexBlock: hexBlock, - Options: options, - } -} - -// UptimeCmd defines the uptime JSON-RPC command. -type UptimeCmd struct{} - -// NewUptimeCmd returns a new instance which can be used to issue an uptime JSON-RPC command. -func NewUptimeCmd() *UptimeCmd { - return &UptimeCmd{} -} - -// ValidateAddressCmd defines the validateAddress JSON-RPC command. -type ValidateAddressCmd struct { - Address string -} - -// NewValidateAddressCmd returns a new instance which can be used to issue a -// validateAddress JSON-RPC command. -func NewValidateAddressCmd(address string) *ValidateAddressCmd { - return &ValidateAddressCmd{ - Address: address, - } -} - -// NodeSubCmd defines the type used in the `node` JSON-RPC command for the -// sub command field. -type NodeSubCmd string - -const ( - // NConnect indicates the specified host that should be connected to. - NConnect NodeSubCmd = "connect" - - // NRemove indicates the specified peer that should be removed as a - // persistent peer. - NRemove NodeSubCmd = "remove" - - // NDisconnect indicates the specified peer should be disconnected. - NDisconnect NodeSubCmd = "disconnect" -) - -// NodeCmd defines the node JSON-RPC command. -type NodeCmd struct { - SubCmd NodeSubCmd `jsonrpcusage:"\"connect|remove|disconnect\""` - Target string - ConnectSubCmd *string `jsonrpcusage:"\"perm|temp\""` -} - -// NewNodeCmd returns a new instance which can be used to issue a `node` -// JSON-RPC command. -// -// The parameters which are pointers indicate they are optional. Passing nil -// for optional parameters will use the default value. -func NewNodeCmd(subCmd NodeSubCmd, target string, connectSubCmd *string) *NodeCmd { - return &NodeCmd{ - SubCmd: subCmd, - Target: target, - ConnectSubCmd: connectSubCmd, - } -} - -// DebugLevelCmd defines the debugLevel JSON-RPC command. -type DebugLevelCmd struct { - LevelSpec string -} - -// NewDebugLevelCmd returns a new DebugLevelCmd which can be used to issue a -// debugLevel JSON-RPC command. -func NewDebugLevelCmd(levelSpec string) *DebugLevelCmd { - return &DebugLevelCmd{ - LevelSpec: levelSpec, - } -} - -// GetSelectedTipCmd defines the getSelectedTip JSON-RPC command. -type GetSelectedTipCmd struct { - Verbose *bool `jsonrpcdefault:"true"` - VerboseTx *bool `jsonrpcdefault:"false"` -} - -// NewGetSelectedTipCmd returns a new instance which can be used to issue a -// getSelectedTip JSON-RPC command. -func NewGetSelectedTipCmd(verbose, verboseTx *bool) *GetSelectedTipCmd { - return &GetSelectedTipCmd{ - Verbose: verbose, - VerboseTx: verboseTx, - } -} - -// GetCurrentNetCmd defines the getCurrentNet JSON-RPC command. -type GetCurrentNetCmd struct{} - -// NewGetCurrentNetCmd returns a new instance which can be used to issue a -// getCurrentNet JSON-RPC command. -func NewGetCurrentNetCmd() *GetCurrentNetCmd { - return &GetCurrentNetCmd{} -} - -// GetTopHeadersCmd defined the getTopHeaders JSON-RPC command. -type GetTopHeadersCmd struct { - HighHash *string `json:"highHash"` -} - -// NewGetTopHeadersCmd returns a new instance which can be used to issue a -// getTopHeaders JSON-RPC command. -func NewGetTopHeadersCmd(highHash *string) *GetTopHeadersCmd { - return &GetTopHeadersCmd{ - HighHash: highHash, - } -} - -// GetHeadersCmd defines the getHeaders JSON-RPC command. -type GetHeadersCmd struct { - LowHash string `json:"lowHash"` - HighHash string `json:"highHash"` -} - -// NewGetHeadersCmd returns a new instance which can be used to issue a -// getHeaders JSON-RPC command. -func NewGetHeadersCmd(lowHash, highHash string) *GetHeadersCmd { - return &GetHeadersCmd{ - LowHash: lowHash, - HighHash: highHash, - } -} - -// VersionCmd defines the version JSON-RPC command. -type VersionCmd struct{} - -// NewVersionCmd returns a new instance which can be used to issue a JSON-RPC -// version command. -func NewVersionCmd() *VersionCmd { return new(VersionCmd) } - -// GetPeerAddressesCmd defines the getPeerAddresses JSON-RPC command. -type GetPeerAddressesCmd struct { -} - -// NewGetPeerAddressesCmd returns a new instance which can be used to issue a JSON-RPC -// getPeerAddresses command. -func NewGetPeerAddressesCmd() *GetPeerAddressesCmd { return new(GetPeerAddressesCmd) } - -func init() { - // No special flags for commands in this file. - flags := UsageFlag(0) - - MustRegisterCommand("connect", (*ConnectCmd)(nil), flags) - MustRegisterCommand("getSelectedTipHash", (*GetSelectedTipHashCmd)(nil), flags) - MustRegisterCommand("getBlock", (*GetBlockCmd)(nil), flags) - MustRegisterCommand("getBlocks", (*GetBlocksCmd)(nil), flags) - MustRegisterCommand("getBlockDagInfo", (*GetBlockDAGInfoCmd)(nil), flags) - MustRegisterCommand("getBlockCount", (*GetBlockCountCmd)(nil), flags) - MustRegisterCommand("getBlockHeader", (*GetBlockHeaderCmd)(nil), flags) - MustRegisterCommand("getBlockTemplate", (*GetBlockTemplateCmd)(nil), flags) - MustRegisterCommand("getChainFromBlock", (*GetChainFromBlockCmd)(nil), flags) - MustRegisterCommand("getDagTips", (*GetDAGTipsCmd)(nil), flags) - MustRegisterCommand("getConnectionCount", (*GetConnectionCountCmd)(nil), flags) - MustRegisterCommand("getDifficulty", (*GetDifficultyCmd)(nil), flags) - MustRegisterCommand("getInfo", (*GetInfoCmd)(nil), flags) - MustRegisterCommand("getMempoolEntry", (*GetMempoolEntryCmd)(nil), flags) - MustRegisterCommand("getMempoolInfo", (*GetMempoolInfoCmd)(nil), flags) - MustRegisterCommand("getNetworkInfo", (*GetNetworkInfoCmd)(nil), flags) - MustRegisterCommand("getNetTotals", (*GetNetTotalsCmd)(nil), flags) - MustRegisterCommand("getConnectedPeerInfo", (*GetConnectedPeerInfoCmd)(nil), flags) - MustRegisterCommand("getPeerAddresses", (*GetPeerAddressesCmd)(nil), flags) - MustRegisterCommand("getRawMempool", (*GetRawMempoolCmd)(nil), flags) - MustRegisterCommand("getSubnetwork", (*GetSubnetworkCmd)(nil), flags) - MustRegisterCommand("getTxOut", (*GetTxOutCmd)(nil), flags) - MustRegisterCommand("getTxOutSetInfo", (*GetTxOutSetInfoCmd)(nil), flags) - MustRegisterCommand("help", (*HelpCmd)(nil), flags) - MustRegisterCommand("ping", (*PingCmd)(nil), flags) - MustRegisterCommand("disconnect", (*DisconnectCmd)(nil), flags) - MustRegisterCommand("sendRawTransaction", (*SendRawTransactionCmd)(nil), flags) - MustRegisterCommand("stop", (*StopCmd)(nil), flags) - MustRegisterCommand("submitBlock", (*SubmitBlockCmd)(nil), flags) - MustRegisterCommand("uptime", (*UptimeCmd)(nil), flags) - MustRegisterCommand("validateAddress", (*ValidateAddressCmd)(nil), flags) - MustRegisterCommand("debugLevel", (*DebugLevelCmd)(nil), flags) - MustRegisterCommand("node", (*NodeCmd)(nil), flags) - MustRegisterCommand("getSelectedTip", (*GetSelectedTipCmd)(nil), flags) - MustRegisterCommand("getCurrentNet", (*GetCurrentNetCmd)(nil), flags) - MustRegisterCommand("getHeaders", (*GetHeadersCmd)(nil), flags) - MustRegisterCommand("getTopHeaders", (*GetTopHeadersCmd)(nil), flags) - MustRegisterCommand("version", (*VersionCmd)(nil), flags) -} diff --git a/infrastructure/network/rpc/model/rpc_commands_test.go b/infrastructure/network/rpc/model/rpc_commands_test.go deleted file mode 100644 index 422baedb7..000000000 --- a/infrastructure/network/rpc/model/rpc_commands_test.go +++ /dev/null @@ -1,886 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model_test - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "github.com/kaspanet/kaspad/util/pointers" - "reflect" - "testing" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// TestRPCServerCommands tests all of the kaspa rpc server commands marshal and unmarshal -// into valid results include handling of optional fields being omitted in the -// marshalled command, while optional fields with defaults have the default -// assigned on unmarshalled commands. -func TestRPCServerCommands(t *testing.T) { - t.Parallel() - - testID := 1 - tests := []struct { - name string - newCmd func() (interface{}, error) - staticCmd func() interface{} - marshalled string - unmarshalled interface{} - }{ - { - name: "connect", - newCmd: func() (interface{}, error) { - return model.NewCommand("connect", "127.0.0.1") - }, - staticCmd: func() interface{} { - return model.NewConnectCmd("127.0.0.1", nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"connect","params":["127.0.0.1"],"id":1}`, - unmarshalled: &model.ConnectCmd{Address: "127.0.0.1", IsPermanent: pointers.Bool(false)}, - }, - { - name: "getSelectedTipHash", - newCmd: func() (interface{}, error) { - return model.NewCommand("getSelectedTipHash") - }, - staticCmd: func() interface{} { - return model.NewGetSelectedTipHashCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getSelectedTipHash","params":[],"id":1}`, - unmarshalled: &model.GetSelectedTipHashCmd{}, - }, - { - name: "getBlock", - newCmd: func() (interface{}, error) { - return model.NewCommand("getBlock", "123") - }, - staticCmd: func() interface{} { - return model.NewGetBlockCmd("123", nil, nil, nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"getBlock","params":["123"],"id":1}`, - unmarshalled: &model.GetBlockCmd{ - Hash: "123", - Verbose: pointers.Bool(true), - VerboseTx: pointers.Bool(false), - }, - }, - { - name: "getBlock required optional1", - newCmd: func() (interface{}, error) { - // Intentionally use a source param that is - // more pointers than the destination to - // exercise that path. - verbosePtr := pointers.Bool(true) - return model.NewCommand("getBlock", "123", &verbosePtr) - }, - staticCmd: func() interface{} { - return model.NewGetBlockCmd("123", pointers.Bool(true), nil, nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"getBlock","params":["123",true],"id":1}`, - unmarshalled: &model.GetBlockCmd{ - Hash: "123", - Verbose: pointers.Bool(true), - VerboseTx: pointers.Bool(false), - }, - }, - { - name: "getBlock required optional2", - newCmd: func() (interface{}, error) { - return model.NewCommand("getBlock", "123", true, true) - }, - staticCmd: func() interface{} { - return model.NewGetBlockCmd("123", pointers.Bool(true), pointers.Bool(true), nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"getBlock","params":["123",true,true],"id":1}`, - unmarshalled: &model.GetBlockCmd{ - Hash: "123", - Verbose: pointers.Bool(true), - VerboseTx: pointers.Bool(true), - }, - }, - { - name: "getBlock required optional3", - newCmd: func() (interface{}, error) { - return model.NewCommand("getBlock", "123", true, true, "456") - }, - staticCmd: func() interface{} { - return model.NewGetBlockCmd("123", pointers.Bool(true), pointers.Bool(true), pointers.String("456")) - }, - marshalled: `{"jsonrpc":"1.0","method":"getBlock","params":["123",true,true,"456"],"id":1}`, - unmarshalled: &model.GetBlockCmd{ - Hash: "123", - Verbose: pointers.Bool(true), - VerboseTx: pointers.Bool(true), - Subnetwork: pointers.String("456"), - }, - }, - { - name: "getBlocks", - newCmd: func() (interface{}, error) { - return model.NewCommand("getBlocks", true, true, "123") - }, - staticCmd: func() interface{} { - return model.NewGetBlocksCmd(true, true, pointers.String("123")) - }, - marshalled: `{"jsonrpc":"1.0","method":"getBlocks","params":[true,true,"123"],"id":1}`, - unmarshalled: &model.GetBlocksCmd{ - IncludeRawBlockData: true, - IncludeVerboseBlockData: true, - LowHash: pointers.String("123"), - }, - }, - { - name: "getBlockDagInfo", - newCmd: func() (interface{}, error) { - return model.NewCommand("getBlockDagInfo") - }, - staticCmd: func() interface{} { - return model.NewGetBlockDAGInfoCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getBlockDagInfo","params":[],"id":1}`, - unmarshalled: &model.GetBlockDAGInfoCmd{}, - }, - { - name: "getBlockCount", - newCmd: func() (interface{}, error) { - return model.NewCommand("getBlockCount") - }, - staticCmd: func() interface{} { - return model.NewGetBlockCountCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getBlockCount","params":[],"id":1}`, - unmarshalled: &model.GetBlockCountCmd{}, - }, - { - name: "getBlockHeader", - newCmd: func() (interface{}, error) { - return model.NewCommand("getBlockHeader", "123") - }, - staticCmd: func() interface{} { - return model.NewGetBlockHeaderCmd("123", nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"getBlockHeader","params":["123"],"id":1}`, - unmarshalled: &model.GetBlockHeaderCmd{ - Hash: "123", - Verbose: pointers.Bool(true), - }, - }, - { - name: "getBlockTemplate", - newCmd: func() (interface{}, error) { - return model.NewCommand("getBlockTemplate") - }, - staticCmd: func() interface{} { - return model.NewGetBlockTemplateCmd(nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[],"id":1}`, - unmarshalled: &model.GetBlockTemplateCmd{Request: nil}, - }, - { - name: "getBlockTemplate optional - template request", - newCmd: func() (interface{}, error) { - return model.NewCommand("getBlockTemplate", `{"mode":"template","payAddress":"kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3"}`) - }, - staticCmd: func() interface{} { - template := model.TemplateRequest{ - Mode: "template", - PayAddress: "kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3", - } - return model.NewGetBlockTemplateCmd(&template) - }, - marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","payAddress":"kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3"}],"id":1}`, - unmarshalled: &model.GetBlockTemplateCmd{ - Request: &model.TemplateRequest{ - Mode: "template", - PayAddress: "kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3", - }, - }, - }, - { - name: "getBlockTemplate optional - template request with tweaks", - newCmd: func() (interface{}, error) { - return model.NewCommand("getBlockTemplate", `{"mode":"template","sigOpLimit":500,"massLimit":100000000,"maxVersion":1,"payAddress":"kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3"}`) - }, - staticCmd: func() interface{} { - template := model.TemplateRequest{ - Mode: "template", - PayAddress: "kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3", - SigOpLimit: 500, - MassLimit: 100000000, - MaxVersion: 1, - } - return model.NewGetBlockTemplateCmd(&template) - }, - marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","sigOpLimit":500,"massLimit":100000000,"maxVersion":1,"payAddress":"kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3"}],"id":1}`, - unmarshalled: &model.GetBlockTemplateCmd{ - Request: &model.TemplateRequest{ - Mode: "template", - PayAddress: "kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3", - SigOpLimit: int64(500), - MassLimit: int64(100000000), - MaxVersion: 1, - }, - }, - }, - { - name: "getBlockTemplate optional - template request with tweaks 2", - newCmd: func() (interface{}, error) { - return model.NewCommand("getBlockTemplate", `{"mode":"template","payAddress":"kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3","sigOpLimit":true,"massLimit":100000000,"maxVersion":1}`) - }, - staticCmd: func() interface{} { - template := model.TemplateRequest{ - Mode: "template", - PayAddress: "kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3", - SigOpLimit: true, - MassLimit: 100000000, - MaxVersion: 1, - } - return model.NewGetBlockTemplateCmd(&template) - }, - marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","sigOpLimit":true,"massLimit":100000000,"maxVersion":1,"payAddress":"kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3"}],"id":1}`, - unmarshalled: &model.GetBlockTemplateCmd{ - Request: &model.TemplateRequest{ - Mode: "template", - PayAddress: "kaspa:qph364lxa0ul5h0jrvl3u7xu8erc7mu3dv7prcn7x3", - SigOpLimit: true, - MassLimit: int64(100000000), - MaxVersion: 1, - }, - }, - }, - { - name: "getChainFromBlock", - newCmd: func() (interface{}, error) { - return model.NewCommand("getChainFromBlock", true, "123") - }, - staticCmd: func() interface{} { - return model.NewGetChainFromBlockCmd(true, pointers.String("123")) - }, - marshalled: `{"jsonrpc":"1.0","method":"getChainFromBlock","params":[true,"123"],"id":1}`, - unmarshalled: &model.GetChainFromBlockCmd{ - IncludeBlocks: true, - StartHash: pointers.String("123"), - }, - }, - { - name: "getDagTips", - newCmd: func() (interface{}, error) { - return model.NewCommand("getDagTips") - }, - staticCmd: func() interface{} { - return model.NewGetDAGTipsCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getDagTips","params":[],"id":1}`, - unmarshalled: &model.GetDAGTipsCmd{}, - }, - { - name: "getConnectionCount", - newCmd: func() (interface{}, error) { - return model.NewCommand("getConnectionCount") - }, - staticCmd: func() interface{} { - return model.NewGetConnectionCountCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getConnectionCount","params":[],"id":1}`, - unmarshalled: &model.GetConnectionCountCmd{}, - }, - { - name: "getDifficulty", - newCmd: func() (interface{}, error) { - return model.NewCommand("getDifficulty") - }, - staticCmd: func() interface{} { - return model.NewGetDifficultyCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getDifficulty","params":[],"id":1}`, - unmarshalled: &model.GetDifficultyCmd{}, - }, - { - name: "getInfo", - newCmd: func() (interface{}, error) { - return model.NewCommand("getInfo") - }, - staticCmd: func() interface{} { - return model.NewGetInfoCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getInfo","params":[],"id":1}`, - unmarshalled: &model.GetInfoCmd{}, - }, - { - name: "getMempoolEntry", - newCmd: func() (interface{}, error) { - return model.NewCommand("getMempoolEntry", "txhash") - }, - staticCmd: func() interface{} { - return model.NewGetMempoolEntryCmd("txhash") - }, - marshalled: `{"jsonrpc":"1.0","method":"getMempoolEntry","params":["txhash"],"id":1}`, - unmarshalled: &model.GetMempoolEntryCmd{ - TxID: "txhash", - }, - }, - { - name: "getMempoolInfo", - newCmd: func() (interface{}, error) { - return model.NewCommand("getMempoolInfo") - }, - staticCmd: func() interface{} { - return model.NewGetMempoolInfoCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getMempoolInfo","params":[],"id":1}`, - unmarshalled: &model.GetMempoolInfoCmd{}, - }, - { - name: "getNetworkInfo", - newCmd: func() (interface{}, error) { - return model.NewCommand("getNetworkInfo") - }, - staticCmd: func() interface{} { - return model.NewGetNetworkInfoCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getNetworkInfo","params":[],"id":1}`, - unmarshalled: &model.GetNetworkInfoCmd{}, - }, - { - name: "getNetTotals", - newCmd: func() (interface{}, error) { - return model.NewCommand("getNetTotals") - }, - staticCmd: func() interface{} { - return model.NewGetNetTotalsCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getNetTotals","params":[],"id":1}`, - unmarshalled: &model.GetNetTotalsCmd{}, - }, - { - name: "getConnectedPeerInfo", - newCmd: func() (interface{}, error) { - return model.NewCommand("getConnectedPeerInfo") - }, - staticCmd: func() interface{} { - return model.NewGetConnectedPeerInfoCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getConnectedPeerInfo","params":[],"id":1}`, - unmarshalled: &model.GetConnectedPeerInfoCmd{}, - }, - { - name: "getRawMempool", - newCmd: func() (interface{}, error) { - return model.NewCommand("getRawMempool") - }, - staticCmd: func() interface{} { - return model.NewGetRawMempoolCmd(nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"getRawMempool","params":[],"id":1}`, - unmarshalled: &model.GetRawMempoolCmd{ - Verbose: pointers.Bool(false), - }, - }, - { - name: "getRawMempool optional", - newCmd: func() (interface{}, error) { - return model.NewCommand("getRawMempool", false) - }, - staticCmd: func() interface{} { - return model.NewGetRawMempoolCmd(pointers.Bool(false)) - }, - marshalled: `{"jsonrpc":"1.0","method":"getRawMempool","params":[false],"id":1}`, - unmarshalled: &model.GetRawMempoolCmd{ - Verbose: pointers.Bool(false), - }, - }, - { - name: "getSubnetwork", - newCmd: func() (interface{}, error) { - return model.NewCommand("getSubnetwork", "123") - }, - staticCmd: func() interface{} { - return model.NewGetSubnetworkCmd("123") - }, - marshalled: `{"jsonrpc":"1.0","method":"getSubnetwork","params":["123"],"id":1}`, - unmarshalled: &model.GetSubnetworkCmd{ - SubnetworkID: "123", - }, - }, - { - name: "getTxOut", - newCmd: func() (interface{}, error) { - return model.NewCommand("getTxOut", "123", 1) - }, - staticCmd: func() interface{} { - return model.NewGetTxOutCmd("123", 1, nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"getTxOut","params":["123",1],"id":1}`, - unmarshalled: &model.GetTxOutCmd{ - TxID: "123", - Vout: 1, - IncludeMempool: pointers.Bool(true), - }, - }, - { - name: "getTxOut optional", - newCmd: func() (interface{}, error) { - return model.NewCommand("getTxOut", "123", 1, true) - }, - staticCmd: func() interface{} { - return model.NewGetTxOutCmd("123", 1, pointers.Bool(true)) - }, - marshalled: `{"jsonrpc":"1.0","method":"getTxOut","params":["123",1,true],"id":1}`, - unmarshalled: &model.GetTxOutCmd{ - TxID: "123", - Vout: 1, - IncludeMempool: pointers.Bool(true), - }, - }, - { - name: "getTxOutSetInfo", - newCmd: func() (interface{}, error) { - return model.NewCommand("getTxOutSetInfo") - }, - staticCmd: func() interface{} { - return model.NewGetTxOutSetInfoCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getTxOutSetInfo","params":[],"id":1}`, - unmarshalled: &model.GetTxOutSetInfoCmd{}, - }, - { - name: "help", - newCmd: func() (interface{}, error) { - return model.NewCommand("help") - }, - staticCmd: func() interface{} { - return model.NewHelpCmd(nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"help","params":[],"id":1}`, - unmarshalled: &model.HelpCmd{ - Command: nil, - }, - }, - { - name: "help optional", - newCmd: func() (interface{}, error) { - return model.NewCommand("help", "getBlock") - }, - staticCmd: func() interface{} { - return model.NewHelpCmd(pointers.String("getBlock")) - }, - marshalled: `{"jsonrpc":"1.0","method":"help","params":["getBlock"],"id":1}`, - unmarshalled: &model.HelpCmd{ - Command: pointers.String("getBlock"), - }, - }, - { - name: "ping", - newCmd: func() (interface{}, error) { - return model.NewCommand("ping") - }, - staticCmd: func() interface{} { - return model.NewPingCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"ping","params":[],"id":1}`, - unmarshalled: &model.PingCmd{}, - }, - { - name: "disconnect", - newCmd: func() (interface{}, error) { - return model.NewCommand("disconnect", "127.0.0.1") - }, - staticCmd: func() interface{} { - return model.NewDisconnectCmd("127.0.0.1") - }, - marshalled: `{"jsonrpc":"1.0","method":"disconnect","params":["127.0.0.1"],"id":1}`, - unmarshalled: &model.DisconnectCmd{Address: "127.0.0.1"}, - }, - { - name: "sendRawTransaction", - newCmd: func() (interface{}, error) { - return model.NewCommand("sendRawTransaction", "1122") - }, - staticCmd: func() interface{} { - return model.NewSendRawTransactionCmd("1122", nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"sendRawTransaction","params":["1122"],"id":1}`, - unmarshalled: &model.SendRawTransactionCmd{ - HexTx: "1122", - AllowHighFees: pointers.Bool(false), - }, - }, - { - name: "sendRawTransaction optional", - newCmd: func() (interface{}, error) { - return model.NewCommand("sendRawTransaction", "1122", false) - }, - staticCmd: func() interface{} { - return model.NewSendRawTransactionCmd("1122", pointers.Bool(false)) - }, - marshalled: `{"jsonrpc":"1.0","method":"sendRawTransaction","params":["1122",false],"id":1}`, - unmarshalled: &model.SendRawTransactionCmd{ - HexTx: "1122", - AllowHighFees: pointers.Bool(false), - }, - }, - { - name: "stop", - newCmd: func() (interface{}, error) { - return model.NewCommand("stop") - }, - staticCmd: func() interface{} { - return model.NewStopCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"stop","params":[],"id":1}`, - unmarshalled: &model.StopCmd{}, - }, - { - name: "submitBlock", - newCmd: func() (interface{}, error) { - return model.NewCommand("submitBlock", "112233") - }, - staticCmd: func() interface{} { - return model.NewSubmitBlockCmd("112233", nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"submitBlock","params":["112233"],"id":1}`, - unmarshalled: &model.SubmitBlockCmd{ - HexBlock: "112233", - Options: nil, - }, - }, - { - name: "submitBlock optional", - newCmd: func() (interface{}, error) { - return model.NewCommand("submitBlock", "112233", `{"workId":"12345"}`) - }, - staticCmd: func() interface{} { - options := model.SubmitBlockOptions{ - WorkID: "12345", - } - return model.NewSubmitBlockCmd("112233", &options) - }, - marshalled: `{"jsonrpc":"1.0","method":"submitBlock","params":["112233",{"workId":"12345"}],"id":1}`, - unmarshalled: &model.SubmitBlockCmd{ - HexBlock: "112233", - Options: &model.SubmitBlockOptions{ - WorkID: "12345", - }, - }, - }, - { - name: "uptime", - newCmd: func() (interface{}, error) { - return model.NewCommand("uptime") - }, - staticCmd: func() interface{} { - return model.NewUptimeCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"uptime","params":[],"id":1}`, - unmarshalled: &model.UptimeCmd{}, - }, - { - name: "validateAddress", - newCmd: func() (interface{}, error) { - return model.NewCommand("validateAddress", "1Address") - }, - staticCmd: func() interface{} { - return model.NewValidateAddressCmd("1Address") - }, - marshalled: `{"jsonrpc":"1.0","method":"validateAddress","params":["1Address"],"id":1}`, - unmarshalled: &model.ValidateAddressCmd{ - Address: "1Address", - }, - }, - { - name: "debugLevel", - newCmd: func() (interface{}, error) { - return model.NewCommand("debugLevel", "trace") - }, - staticCmd: func() interface{} { - return model.NewDebugLevelCmd("trace") - }, - marshalled: `{"jsonrpc":"1.0","method":"debugLevel","params":["trace"],"id":1}`, - unmarshalled: &model.DebugLevelCmd{ - LevelSpec: "trace", - }, - }, - { - name: "node", - newCmd: func() (interface{}, error) { - return model.NewCommand("node", model.NRemove, "1.1.1.1") - }, - staticCmd: func() interface{} { - return model.NewNodeCmd("remove", "1.1.1.1", nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"node","params":["remove","1.1.1.1"],"id":1}`, - unmarshalled: &model.NodeCmd{ - SubCmd: model.NRemove, - Target: "1.1.1.1", - }, - }, - { - name: "node", - newCmd: func() (interface{}, error) { - return model.NewCommand("node", model.NDisconnect, "1.1.1.1") - }, - staticCmd: func() interface{} { - return model.NewNodeCmd("disconnect", "1.1.1.1", nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"node","params":["disconnect","1.1.1.1"],"id":1}`, - unmarshalled: &model.NodeCmd{ - SubCmd: model.NDisconnect, - Target: "1.1.1.1", - }, - }, - { - name: "node", - newCmd: func() (interface{}, error) { - return model.NewCommand("node", model.NConnect, "1.1.1.1", "perm") - }, - staticCmd: func() interface{} { - return model.NewNodeCmd("connect", "1.1.1.1", pointers.String("perm")) - }, - marshalled: `{"jsonrpc":"1.0","method":"node","params":["connect","1.1.1.1","perm"],"id":1}`, - unmarshalled: &model.NodeCmd{ - SubCmd: model.NConnect, - Target: "1.1.1.1", - ConnectSubCmd: pointers.String("perm"), - }, - }, - { - name: "node", - newCmd: func() (interface{}, error) { - return model.NewCommand("node", model.NConnect, "1.1.1.1", "temp") - }, - staticCmd: func() interface{} { - return model.NewNodeCmd("connect", "1.1.1.1", pointers.String("temp")) - }, - marshalled: `{"jsonrpc":"1.0","method":"node","params":["connect","1.1.1.1","temp"],"id":1}`, - unmarshalled: &model.NodeCmd{ - SubCmd: model.NConnect, - Target: "1.1.1.1", - ConnectSubCmd: pointers.String("temp"), - }, - }, - { - name: "getSelectedTip", - newCmd: func() (interface{}, error) { - return model.NewCommand("getSelectedTip") - }, - staticCmd: func() interface{} { - return model.NewGetSelectedTipCmd(nil, nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"getSelectedTip","params":[],"id":1}`, - unmarshalled: &model.GetSelectedTipCmd{ - Verbose: pointers.Bool(true), - VerboseTx: pointers.Bool(false), - }, - }, - { - name: "getCurrentNet", - newCmd: func() (interface{}, error) { - return model.NewCommand("getCurrentNet") - }, - staticCmd: func() interface{} { - return model.NewGetCurrentNetCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"getCurrentNet","params":[],"id":1}`, - unmarshalled: &model.GetCurrentNetCmd{}, - }, - { - name: "getHeaders", - newCmd: func() (interface{}, error) { - return model.NewCommand("getHeaders", "", "") - }, - staticCmd: func() interface{} { - return model.NewGetHeadersCmd( - "", - "", - ) - }, - marshalled: `{"jsonrpc":"1.0","method":"getHeaders","params":["",""],"id":1}`, - unmarshalled: &model.GetHeadersCmd{ - LowHash: "", - HighHash: "", - }, - }, - { - name: "getHeaders - with arguments", - newCmd: func() (interface{}, error) { - return model.NewCommand("getHeaders", "000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16", "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7") - }, - staticCmd: func() interface{} { - return model.NewGetHeadersCmd( - "000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16", - "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7", - ) - }, - marshalled: `{"jsonrpc":"1.0","method":"getHeaders","params":["000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16","000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7"],"id":1}`, - unmarshalled: &model.GetHeadersCmd{ - LowHash: "000000000000000001f1739002418e2f9a84c47a4fd2a0eb7a787a6b7dc12f16", - HighHash: "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7", - }, - }, - { - name: "getTopHeaders", - newCmd: func() (interface{}, error) { - return model.NewCommand("getTopHeaders") - }, - staticCmd: func() interface{} { - return model.NewGetTopHeadersCmd( - nil, - ) - }, - marshalled: `{"jsonrpc":"1.0","method":"getTopHeaders","params":[],"id":1}`, - unmarshalled: &model.GetTopHeadersCmd{}, - }, - { - name: "getTopHeaders - with high hash", - newCmd: func() (interface{}, error) { - return model.NewCommand("getTopHeaders", "000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7") - }, - staticCmd: func() interface{} { - return model.NewGetTopHeadersCmd( - pointers.String("000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7"), - ) - }, - marshalled: `{"jsonrpc":"1.0","method":"getTopHeaders","params":["000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7"],"id":1}`, - unmarshalled: &model.GetTopHeadersCmd{ - HighHash: pointers.String("000000000000000000ba33b33e1fad70b69e234fc24414dd47113bff38f523f7"), - }, - }, - { - name: "version", - newCmd: func() (interface{}, error) { - return model.NewCommand("version") - }, - staticCmd: func() interface{} { - return model.NewVersionCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"version","params":[],"id":1}`, - unmarshalled: &model.VersionCmd{}, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - // Marshal the command as created by the new static command - // creation function. - marshalled, err := model.MarshalCommand(testID, test.staticCmd()) - if err != nil { - t.Errorf("MarshalCommand #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - - if !bytes.Equal(marshalled, []byte(test.marshalled)) { - t.Errorf("Test #%d (%s) unexpected marshalled data - "+ - "got %s, want %s", i, test.name, marshalled, - test.marshalled) - t.Errorf("\n%s\n%s", marshalled, test.marshalled) - continue - } - - // Ensure the command is created without error via the generic - // new command creation function. - cmd, err := test.newCmd() - if err != nil { - t.Errorf("Test #%d (%s) unexpected NewCommand error: %v ", - i, test.name, err) - } - - // Marshal the command as created by the generic new command - // creation function. - marshalled, err = model.MarshalCommand(testID, cmd) - if err != nil { - t.Errorf("MarshalCommand #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - - if !bytes.Equal(marshalled, []byte(test.marshalled)) { - t.Errorf("Test #%d (%s) unexpected marshalled data - "+ - "got %s, want %s", i, test.name, marshalled, - test.marshalled) - continue - } - - var request model.Request - if err := json.Unmarshal(marshalled, &request); err != nil { - t.Errorf("Test #%d (%s) unexpected error while "+ - "unmarshalling JSON-RPC request: %v", i, - test.name, err) - continue - } - - cmd, err = model.UnmarshalCommand(&request) - if err != nil { - t.Errorf("UnmarshalCommand #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - - if !reflect.DeepEqual(cmd, test.unmarshalled) { - t.Errorf("Test #%d (%s) unexpected unmarshalled command "+ - "- got %s, want %s", i, test.name, - fmt.Sprintf("(%T) %+[1]v", cmd), - fmt.Sprintf("(%T) %+[1]v\n", test.unmarshalled)) - continue - } - } -} - -// TestRPCServerCommandErrors ensures any errors that occur in the command during -// custom mashal and unmarshal are as expected. -func TestRPCServerCommandErrors(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - result interface{} - marshalled string - err error - }{ - { - name: "template request with invalid type", - result: &model.TemplateRequest{}, - marshalled: `{"mode":1}`, - err: &json.UnmarshalTypeError{}, - }, - { - name: "invalid template request sigoplimit field", - result: &model.TemplateRequest{}, - marshalled: `{"sigoplimit":"invalid"}`, - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - { - name: "invalid template request masslimit field", - result: &model.TemplateRequest{}, - marshalled: `{"masslimit":"invalid"}`, - err: model.Error{ErrorCode: model.ErrInvalidType}, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - err := json.Unmarshal([]byte(test.marshalled), &test.result) - if reflect.TypeOf(err) != reflect.TypeOf(test.err) { - t.Errorf("Test #%d (%s) wrong error - got %T (%[2]v), "+ - "want %T", i, test.name, err, test.err) - continue - } - - var testErr model.Error - if errors.As(err, &testErr) { - var gotRPCModelErr model.Error - errors.As(err, &gotRPCModelErr) - gotErrorCode := gotRPCModelErr.ErrorCode - if gotErrorCode != testErr.ErrorCode { - t.Errorf("Test #%d (%s) mismatched error code "+ - "- got %v (%v), want %v", i, test.name, - gotErrorCode, testErr, testErr.ErrorCode) - continue - } - } - } -} diff --git a/infrastructure/network/rpc/model/rpc_results.go b/infrastructure/network/rpc/model/rpc_results.go deleted file mode 100644 index b97ae6d64..000000000 --- a/infrastructure/network/rpc/model/rpc_results.go +++ /dev/null @@ -1,475 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model - -import ( - "encoding/json" - "github.com/kaspanet/kaspad/infrastructure/network/addressmanager" -) - -// GetBlockHeaderVerboseResult models the data from the getblockheader command when -// the verbose flag is set. When the verbose flag is not set, getblockheader -// returns a hex-encoded string. -type GetBlockHeaderVerboseResult struct { - Hash string `json:"hash"` - Confirmations uint64 `json:"confirmations"` - Version int32 `json:"version"` - VersionHex string `json:"versionHex"` - HashMerkleRoot string `json:"hashMerkleRoot"` - AcceptedIDMerkleRoot string `json:"acceptedIdMerkleRoot"` - Time int64 `json:"time"` - Nonce uint64 `json:"nonce"` - Bits string `json:"bits"` - Difficulty float64 `json:"difficulty"` - ParentHashes []string `json:"parentHashes,omitempty"` - SelectedParentHash string `json:"selectedParentHash"` - ChildHashes []string `json:"childHashes,omitempty"` -} - -// GetBlockVerboseResult models the data from the getblock command when the -// verbose flag is set. When the verbose flag is not set, getblock returns a -// hex-encoded string. -type GetBlockVerboseResult struct { - Hash string `json:"hash"` - Confirmations uint64 `json:"confirmations"` - Size int32 `json:"size"` - BlueScore uint64 `json:"blueScore"` - IsChainBlock bool `json:"isChainBlock"` - Version int32 `json:"version"` - VersionHex string `json:"versionHex"` - HashMerkleRoot string `json:"hashMerkleRoot"` - AcceptedIDMerkleRoot string `json:"acceptedIdMerkleRoot"` - UTXOCommitment string `json:"utxoCommitment"` - Tx []string `json:"tx,omitempty"` - RawTx []TxRawResult `json:"rawRx,omitempty"` - Time int64 `json:"time"` - Nonce uint64 `json:"nonce"` - Bits string `json:"bits"` - Difficulty float64 `json:"difficulty"` - ParentHashes []string `json:"parentHashes"` - SelectedParentHash string `json:"selectedParentHash"` - ChildHashes []string `json:"childHashes"` - AcceptedBlockHashes []string `json:"acceptedBlockHashes"` -} - -// CreateMultiSigResult models the data returned from the createmultisig -// command. -type CreateMultiSigResult struct { - Address string `json:"address"` - RedeemScript string `json:"redeemScript"` -} - -// DecodeScriptResult models the data returned from the decodescript command. -type DecodeScriptResult struct { - Asm string `json:"asm"` - Type string `json:"type"` - Address *string `json:"address,omitempty"` - P2sh string `json:"p2sh,omitempty"` -} - -// SoftForkDescription describes the current state of a soft-fork which was -// deployed using a super-majority block signalling. -type SoftForkDescription struct { - ID string `json:"id"` - Version uint32 `json:"version"` - Reject struct { - Status bool `json:"status"` - } `json:"reject"` -} - -// Bip9SoftForkDescription describes the current state of a defined BIP0009 -// version bits soft-fork. -type Bip9SoftForkDescription struct { - Status string `json:"status"` - Bit uint8 `json:"bit"` - StartTime int64 `json:"startTime"` - Timeout int64 `json:"timeout"` - Since int32 `json:"since"` -} - -// GetBlockDAGInfoResult models the data returned from the getblockdaginfo -// command. -type GetBlockDAGInfoResult struct { - DAG string `json:"dag"` - Blocks uint64 `json:"blocks"` - Headers uint64 `json:"headers"` - TipHashes []string `json:"tipHashes"` - Difficulty float64 `json:"difficulty"` - MedianTime int64 `json:"medianTime"` - VerificationProgress float64 `json:"verificationProgress,omitempty"` - Pruned bool `json:"pruned"` - PruneHeight uint64 `json:"pruneHeight,omitempty"` - DAGWork string `json:"dagWork,omitempty"` - SoftForks []*SoftForkDescription `json:"softForks"` - Bip9SoftForks map[string]*Bip9SoftForkDescription `json:"bip9SoftForks"` -} - -// GetBlockTemplateResultTx models the transactions field of the -// getblocktemplate command. -type GetBlockTemplateResultTx struct { - Data string `json:"data"` - ID string `json:"id"` - Depends []int64 `json:"depends"` - Mass uint64 `json:"mass"` - Fee uint64 `json:"fee"` -} - -// GetBlockTemplateResult models the data returned from the getblocktemplate -// command. -type GetBlockTemplateResult struct { - // Base fields from BIP 0022. CoinbaseAux is optional. One of - // CoinbaseTxn or CoinbaseValue must be specified, but not both. - Bits string `json:"bits"` - CurTime int64 `json:"curTime"` - Height uint64 `json:"height"` - ParentHashes []string `json:"parentHashes"` - MassLimit int64 `json:"massLimit,omitempty"` - Transactions []GetBlockTemplateResultTx `json:"transactions"` - HashMerkleRoot string `json:"hashMerkleRoot"` - AcceptedIDMerkleRoot string `json:"acceptedIdMerkleRoot"` - UTXOCommitment string `json:"utxoCommitment"` - Version int32 `json:"version"` - WorkID string `json:"workId,omitempty"` - IsSynced bool `json:"isSynced"` - - // Optional long polling from BIP 0022. - LongPollID string `json:"longPollId,omitempty"` - LongPollURI string `json:"longPollUri,omitempty"` - - // Basic pool extension from BIP 0023. - Target string `json:"target,omitempty"` - Expires int64 `json:"expires,omitempty"` - - // Mutations from BIP 0023. - MaxTime int64 `json:"maxTime,omitempty"` - MinTime int64 `json:"minTime,omitempty"` - Mutable []string `json:"mutable,omitempty"` - NonceRange string `json:"nonceRange,omitempty"` - - // Block proposal from BIP 0023. - Capabilities []string `json:"capabilities,omitempty"` - RejectReason string `json:"rejectReason,omitempty"` -} - -// GetMempoolEntryResult models the data returned from the getMempoolEntry -// command. -type GetMempoolEntryResult struct { - Fee uint64 `json:"fee"` - Time int64 `json:"time"` - RawTx TxRawResult `json:"rawTx"` -} - -// GetMempoolInfoResult models the data returned from the getmempoolinfo -// command. -type GetMempoolInfoResult struct { - Size int64 `json:"size"` - Bytes int64 `json:"bytes"` -} - -// NetworksResult models the networks data from the getnetworkinfo command. -type NetworksResult struct { - Name string `json:"name"` - Limited bool `json:"limited"` - Reachable bool `json:"reachable"` - Proxy string `json:"proxy"` - ProxyRandomizeCredentials bool `json:"proxyRandomizeCredentials"` -} - -// LocalAddressesResult models the localaddresses data from the getnetworkinfo -// command. -type LocalAddressesResult struct { - Address string `json:"address"` - Port uint16 `json:"port"` - Score int32 `json:"score"` -} - -// GetNetworkInfoResult models the data returned from the getnetworkinfo -// command. -type GetNetworkInfoResult struct { - Version int32 `json:"version"` - SubVersion string `json:"subVersion"` - ProtocolVersion int32 `json:"protocolVersion"` - LocalServices string `json:"localServices"` - LocalRelay bool `json:"localRelay"` - TimeOffset int64 `json:"timeOffset"` - Connections int32 `json:"connections"` - NetworkActive bool `json:"networkActive"` - Networks []NetworksResult `json:"networks"` - RelayFee float64 `json:"relayFee"` - IncrementalFee float64 `json:"incrementalFee"` - LocalAddresses []LocalAddressesResult `json:"localAddresses"` - Warnings string `json:"warnings"` -} - -// GetConnectedPeerInfoResult models the data returned from the getConnectedPeerInfo command. -type GetConnectedPeerInfoResult struct { - ID string `json:"id"` - Address string `json:"address"` - LastPingDuration int64 `json:"lastPingDuration"` - SelectedTipHash string `json:"selectedTipHash"` - IsSyncNode bool `json:"isSyncNode"` - IsOutbound bool `json:"isOutbound"` - TimeOffset int64 `json:"timeOffset"` - UserAgent string `json:"userAgent"` - AdvertisedProtocolVersion uint32 `json:"advertisedProtocolVersion"` - TimeConnected int64 `json:"timeConnected"` -} - -// GetPeerAddressesResult models the data returned from the getPeerAddresses command. -type GetPeerAddressesResult struct { - Version int - Key [32]byte - Addresses []*GetPeerAddressesKnownAddressResult - NewBuckets map[string]*GetPeerAddressesNewBucketResult // string is Subnetwork ID - NewBucketFullNodes GetPeerAddressesNewBucketResult - TriedBuckets map[string]*GetPeerAddressesTriedBucketResult // string is Subnetwork ID - TriedBucketFullNodes GetPeerAddressesTriedBucketResult -} - -// GetPeerAddressesKnownAddressResult models a GetPeerAddressesResult known address. -type GetPeerAddressesKnownAddressResult struct { - Addr string - Src string - SubnetworkID string - Attempts int - TimeStamp int64 - LastAttempt int64 - LastSuccess int64 - IsBanned bool - BannedTime int64 -} - -// GetPeerAddressesNewBucketResult models a GetPeerAddressesResult new bucket. -type GetPeerAddressesNewBucketResult [addressmanager.NewBucketCount][]string - -// GetPeerAddressesTriedBucketResult models a GetPeerAddressesResult tried bucket. -type GetPeerAddressesTriedBucketResult [addressmanager.TriedBucketCount][]string - -// GetRawMempoolVerboseResult models the data returned from the getrawmempool -// command when the verbose flag is set. When the verbose flag is not set, -// getrawmempool returns an array of transaction hashes. -type GetRawMempoolVerboseResult struct { - Size int32 `json:"size"` - Fee float64 `json:"fee"` - Time int64 `json:"time"` - Depends []string `json:"depends"` -} - -// ScriptPubKeyResult models the scriptPubKey data of a tx script. It is -// defined separately since it is used by multiple commands. -type ScriptPubKeyResult struct { - Asm string `json:"asm"` - Hex string `json:"hex,omitempty"` - Type string `json:"type"` - Address *string `json:"address,omitempty"` -} - -// GetSubnetworkResult models the data from the getSubnetwork command. -type GetSubnetworkResult struct { - GasLimit *uint64 `json:"gasLimit"` -} - -// GetTxOutResult models the data from the gettxout command. -type GetTxOutResult struct { - SelectedTip string `json:"selectedTip"` - Confirmations *uint64 `json:"confirmations,omitempty"` - IsInMempool bool `json:"isInMempool"` - Value float64 `json:"value"` - ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"` - Coinbase bool `json:"coinbase"` -} - -// GetNetTotalsResult models the data returned from the getnettotals command. -type GetNetTotalsResult struct { - TotalBytesRecv uint64 `json:"totalBytesRecv"` - TotalBytesSent uint64 `json:"totalBytesSent"` - TimeMillis int64 `json:"timeMillis"` -} - -// ScriptSig models a signature script. It is defined separately since it only -// applies to non-coinbase. Therefore the field in the Vin structure needs -// to be a pointer. -type ScriptSig struct { - Asm string `json:"asm"` - Hex string `json:"hex"` -} - -// Vin models parts of the tx data. -type Vin struct { - TxID string `json:"txId"` - Vout uint32 `json:"vout"` - ScriptSig *ScriptSig `json:"scriptSig"` - Sequence uint64 `json:"sequence"` -} - -// MarshalJSON provides a custom Marshal method for Vin. -func (v *Vin) MarshalJSON() ([]byte, error) { - txStruct := struct { - TxID string `json:"txId"` - Vout uint32 `json:"vout"` - ScriptSig *ScriptSig `json:"scriptSig"` - Sequence uint64 `json:"sequence"` - }{ - TxID: v.TxID, - Vout: v.Vout, - ScriptSig: v.ScriptSig, - Sequence: v.Sequence, - } - return json.Marshal(txStruct) -} - -// PrevOut represents previous output for an input Vin. -type PrevOut struct { - Address *string `json:"address,omitempty"` - Value float64 `json:"value"` -} - -// VinPrevOut is like Vin except it includes PrevOut. -type VinPrevOut struct { - Coinbase string `json:"coinbase"` - TxID string `json:"txId"` - Vout uint32 `json:"vout"` - ScriptSig *ScriptSig `json:"scriptSig"` - PrevOut *PrevOut `json:"prevOut"` - Sequence uint64 `json:"sequence"` -} - -// IsCoinBase returns a bool to show if a Vin is a Coinbase one or not. -func (v *VinPrevOut) IsCoinBase() bool { - return len(v.Coinbase) > 0 -} - -// MarshalJSON provides a custom Marshal method for VinPrevOut. -func (v *VinPrevOut) MarshalJSON() ([]byte, error) { - if v.IsCoinBase() { - coinbaseStruct := struct { - Coinbase string `json:"coinbase"` - Sequence uint64 `json:"sequence"` - }{ - Coinbase: v.Coinbase, - Sequence: v.Sequence, - } - return json.Marshal(coinbaseStruct) - } - - txStruct := struct { - TxID string `json:"txId"` - Vout uint32 `json:"vout"` - ScriptSig *ScriptSig `json:"scriptSig"` - PrevOut *PrevOut `json:"prevOut,omitempty"` - Sequence uint64 `json:"sequence"` - }{ - TxID: v.TxID, - Vout: v.Vout, - ScriptSig: v.ScriptSig, - PrevOut: v.PrevOut, - Sequence: v.Sequence, - } - return json.Marshal(txStruct) -} - -// Vout models parts of the tx data -type Vout struct { - Value uint64 `json:"value"` - N uint32 `json:"n"` - ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"` -} - -// GetWorkResult models the data from the getwork command. -type GetWorkResult struct { - Data string `json:"data"` - Hash1 string `json:"hash1"` - Midstate string `json:"midstate"` - Target string `json:"target"` -} - -// InfoDAGResult models the data returned by the kaspa rpc server getinfo command. -type InfoDAGResult struct { - Version string `json:"version"` - ProtocolVersion int32 `json:"protocolVersion"` - Blocks uint64 `json:"blocks"` - Connections int32 `json:"connections"` - Proxy string `json:"proxy"` - Difficulty float64 `json:"difficulty"` - Testnet bool `json:"testnet"` - Devnet bool `json:"devnet"` - RelayFee float64 `json:"relayFee"` - Errors string `json:"errors"` -} - -// TxRawResult models transaction result data. -type TxRawResult struct { - Hex string `json:"hex"` - TxID string `json:"txId"` - Hash string `json:"hash,omitempty"` - Size int32 `json:"size,omitempty"` - Version int32 `json:"version"` - LockTime uint64 `json:"lockTime"` - Subnetwork string `json:"subnetwork"` - Gas uint64 `json:"gas"` - PayloadHash string `json:"payloadHash"` - Payload string `json:"payload"` - Vin []Vin `json:"vin"` - Vout []Vout `json:"vout"` - BlockHash string `json:"blockHash,omitempty"` - AcceptedBy *string `json:"acceptedBy,omitempty"` - IsInMempool bool `json:"isInMempool"` - Time uint64 `json:"time,omitempty"` - BlockTime uint64 `json:"blockTime,omitempty"` -} - -// TxRawDecodeResult models the data from the decoderawtransaction command. -type TxRawDecodeResult struct { - TxID string `json:"txId"` - Version int32 `json:"version"` - Locktime uint64 `json:"lockTime"` - Vin []Vin `json:"vin"` - Vout []Vout `json:"vout"` -} - -// ValidateAddressResult models the data returned by the kaspa rpc server -// validateaddress command. -type ValidateAddressResult struct { - IsValid bool `json:"isValid"` - Address string `json:"address,omitempty"` -} - -// ChainBlock models a block that is part of the selected parent chain. -type ChainBlock struct { - Hash string `json:"hash"` - AcceptedBlocks []AcceptedBlock `json:"acceptedBlocks"` -} - -// AcceptedBlock models a block that is included in the blues of a selected -// chain block. -type AcceptedBlock struct { - Hash string `json:"hash"` - AcceptedTxIDs []string `json:"acceptedTxIds"` -} - -// GetChainFromBlockResult models the data from the getChainFromBlock command. -type GetChainFromBlockResult struct { - RemovedChainBlockHashes []string `json:"removedChainBlockHashes"` - AddedChainBlocks []ChainBlock `json:"addedChainBlocks"` - Blocks []GetBlockVerboseResult `json:"blocks"` -} - -// GetBlocksResult models the data from the getBlocks command. -type GetBlocksResult struct { - Hashes []string `json:"hashes"` - RawBlocks []string `json:"rawBlocks"` - VerboseBlocks []GetBlockVerboseResult `json:"verboseBlocks"` -} - -// VersionResult models objects included in the version response. In the actual -// result, these objects are keyed by the program or API name. -type VersionResult struct { - VersionString string `json:"versionString"` - Major uint32 `json:"major"` - Minor uint32 `json:"minor"` - Patch uint32 `json:"patch"` - Prerelease string `json:"prerelease"` - BuildMetadata string `json:"buildMetadata"` -} diff --git a/infrastructure/network/rpc/model/rpc_results_test.go b/infrastructure/network/rpc/model/rpc_results_test.go deleted file mode 100644 index c1393c8c7..000000000 --- a/infrastructure/network/rpc/model/rpc_results_test.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2014 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model_test - -import ( - "encoding/json" - "github.com/kaspanet/kaspad/util/pointers" - "testing" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// TestRPCServerCustomResults ensures any results that have custom marshalling -// work as intended. -// and unmarshal code of results are as expected. -func TestRPCServerCustomResults(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - result interface{} - expected string - }{ - { - name: "custom vin marshal without coinbase", - result: &model.Vin{ - TxID: "123", - Vout: 1, - ScriptSig: &model.ScriptSig{ - Asm: "0", - Hex: "00", - }, - Sequence: 4294967295, - }, - expected: `{"txId":"123","vout":1,"scriptSig":{"asm":"0","hex":"00"},"sequence":4294967295}`, - }, - { - name: "custom vinprevout marshal with coinbase", - result: &model.VinPrevOut{ - Coinbase: "021234", - Sequence: 4294967295, - }, - expected: `{"coinbase":"021234","sequence":4294967295}`, - }, - { - name: "custom vinprevout marshal without coinbase", - result: &model.VinPrevOut{ - TxID: "123", - Vout: 1, - ScriptSig: &model.ScriptSig{ - Asm: "0", - Hex: "00", - }, - PrevOut: &model.PrevOut{ - Address: pointers.String("addr1"), - Value: 0, - }, - Sequence: 4294967295, - }, - expected: `{"txId":"123","vout":1,"scriptSig":{"asm":"0","hex":"00"},"prevOut":{"address":"addr1","value":0},"sequence":4294967295}`, - }, - { - name: "versionresult", - result: &model.VersionResult{ - VersionString: "1.0.0", - Major: 1, - Minor: 0, - Patch: 0, - Prerelease: "pr", - BuildMetadata: "bm", - }, - expected: `{"versionString":"1.0.0","major":1,"minor":0,"patch":0,"prerelease":"pr","buildMetadata":"bm"}`, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - marshalled, err := json.Marshal(test.result) - if err != nil { - t.Errorf("Test #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - if string(marshalled) != test.expected { - t.Errorf("Test #%d (%s) unexpected marhsalled data - "+ - "got %s, want %s", i, test.name, marshalled, - test.expected) - continue - } - } -} diff --git a/infrastructure/network/rpc/model/rpc_websocket_commands.go b/infrastructure/network/rpc/model/rpc_websocket_commands.go deleted file mode 100644 index 49418ad87..000000000 --- a/infrastructure/network/rpc/model/rpc_websocket_commands.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Copyright (c) 2015-2017 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -// NOTE: This file is intended to house the RPC commands that are supported by -// a kaspa rpc server, but are only available via websockets. - -package model - -// AuthenticateCmd defines the authenticate JSON-RPC command. -type AuthenticateCmd struct { - Username string - Passphrase string -} - -// NewAuthenticateCmd returns a new instance which can be used to issue an -// authenticate JSON-RPC command. -func NewAuthenticateCmd(username, passphrase string) *AuthenticateCmd { - return &AuthenticateCmd{ - Username: username, - Passphrase: passphrase, - } -} - -// NotifyBlocksCmd defines the notifyBlocks JSON-RPC command. -type NotifyBlocksCmd struct{} - -// NewNotifyBlocksCmd returns a new instance which can be used to issue a -// notifyBlocks JSON-RPC command. -func NewNotifyBlocksCmd() *NotifyBlocksCmd { - return &NotifyBlocksCmd{} -} - -// StopNotifyBlocksCmd defines the stopNotifyBlocks JSON-RPC command. -type StopNotifyBlocksCmd struct{} - -// NewStopNotifyBlocksCmd returns a new instance which can be used to issue a -// stopNotifyBlocks JSON-RPC command. -func NewStopNotifyBlocksCmd() *StopNotifyBlocksCmd { - return &StopNotifyBlocksCmd{} -} - -// NotifyChainChangesCmd defines the notifyChainChanges JSON-RPC command. -type NotifyChainChangesCmd struct{} - -// NewNotifyChainChangesCmd returns a new instance which can be used to issue a -// notifyChainChanges JSON-RPC command. -func NewNotifyChainChangesCmd() *NotifyChainChangesCmd { - return &NotifyChainChangesCmd{} -} - -// StopNotifyChainChangesCmd defines the stopNotifyChainChanges JSON-RPC command. -type StopNotifyChainChangesCmd struct{} - -// NewStopNotifyChainChangesCmd returns a new instance which can be used to issue a -// stopNotifyChainChanges JSON-RPC command. -func NewStopNotifyChainChangesCmd() *StopNotifyChainChangesCmd { - return &StopNotifyChainChangesCmd{} -} - -// NotifyNewTransactionsCmd defines the notifyNewTransactions JSON-RPC command. -type NotifyNewTransactionsCmd struct { - Verbose *bool `jsonrpcdefault:"false"` - Subnetwork *string -} - -// NewNotifyNewTransactionsCmd returns a new instance which can be used to issue -// a notifyNewTransactions JSON-RPC command. -// -// The parameters which are pointers indicate they are optional. Passing nil -// for optional parameters will use the default value. -func NewNotifyNewTransactionsCmd(verbose *bool, subnetworkID *string) *NotifyNewTransactionsCmd { - return &NotifyNewTransactionsCmd{ - Verbose: verbose, - Subnetwork: subnetworkID, - } -} - -// SessionCmd defines the session JSON-RPC command. -type SessionCmd struct{} - -// NewSessionCmd returns a new instance which can be used to issue a session -// JSON-RPC command. -func NewSessionCmd() *SessionCmd { - return &SessionCmd{} -} - -// StopNotifyNewTransactionsCmd defines the stopNotifyNewTransactions JSON-RPC command. -type StopNotifyNewTransactionsCmd struct{} - -// NewStopNotifyNewTransactionsCmd returns a new instance which can be used to issue -// a stopNotifyNewTransactions JSON-RPC command. -// -// The parameters which are pointers indicate they are optional. Passing nil -// for optional parameters will use the default value. -func NewStopNotifyNewTransactionsCmd() *StopNotifyNewTransactionsCmd { - return &StopNotifyNewTransactionsCmd{} -} - -// Outpoint describes a transaction outpoint that will be marshalled to and -// from JSON. -type Outpoint struct { - TxID string `json:"txid"` - Index uint32 `json:"index"` -} - -// LoadTxFilterCmd defines the loadTxFilter request parameters to load or -// reload a transaction filter. -type LoadTxFilterCmd struct { - Reload bool - Addresses []string - Outpoints []Outpoint -} - -// NewLoadTxFilterCmd returns a new instance which can be used to issue a -// loadTxFilter JSON-RPC command. -func NewLoadTxFilterCmd(reload bool, addresses []string, outpoints []Outpoint) *LoadTxFilterCmd { - return &LoadTxFilterCmd{ - Reload: reload, - Addresses: addresses, - Outpoints: outpoints, - } -} - -func init() { - // The commands in this file are only usable by websockets. - flags := UFWebsocketOnly - - MustRegisterCommand("authenticate", (*AuthenticateCmd)(nil), flags) - MustRegisterCommand("loadTxFilter", (*LoadTxFilterCmd)(nil), flags) - MustRegisterCommand("notifyBlocks", (*NotifyBlocksCmd)(nil), flags) - MustRegisterCommand("notifyChainChanges", (*NotifyChainChangesCmd)(nil), flags) - MustRegisterCommand("notifyNewTransactions", (*NotifyNewTransactionsCmd)(nil), flags) - MustRegisterCommand("session", (*SessionCmd)(nil), flags) - MustRegisterCommand("stopNotifyBlocks", (*StopNotifyBlocksCmd)(nil), flags) - MustRegisterCommand("stopNotifyChainChanges", (*StopNotifyChainChangesCmd)(nil), flags) - MustRegisterCommand("stopNotifyNewTransactions", (*StopNotifyNewTransactionsCmd)(nil), flags) -} diff --git a/infrastructure/network/rpc/model/rpc_websocket_commands_test.go b/infrastructure/network/rpc/model/rpc_websocket_commands_test.go deleted file mode 100644 index c0f7fe8b6..000000000 --- a/infrastructure/network/rpc/model/rpc_websocket_commands_test.go +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Copyright (c) 2015-2017 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model_test - -import ( - "bytes" - "encoding/json" - "fmt" - "github.com/kaspanet/kaspad/util/pointers" - "reflect" - "testing" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" -) - -// TestRPCServerWebsocketCommands tests all of the kaspa rpc server websocket-specific commands -// marshal and unmarshal into valid results include handling of optional fields -// being omitted in the marshalled command, while optional fields with defaults -// have the default assigned on unmarshalled commands. -func TestRPCServerWebsocketCommands(t *testing.T) { - t.Parallel() - - testID := int(1) - tests := []struct { - name string - newCmd func() (interface{}, error) - staticCmd func() interface{} - marshalled string - unmarshalled interface{} - }{ - { - name: "authenticate", - newCmd: func() (interface{}, error) { - return model.NewCommand("authenticate", "user", "pass") - }, - staticCmd: func() interface{} { - return model.NewAuthenticateCmd("user", "pass") - }, - marshalled: `{"jsonrpc":"1.0","method":"authenticate","params":["user","pass"],"id":1}`, - unmarshalled: &model.AuthenticateCmd{Username: "user", Passphrase: "pass"}, - }, - { - name: "notifyBlocks", - newCmd: func() (interface{}, error) { - return model.NewCommand("notifyBlocks") - }, - staticCmd: func() interface{} { - return model.NewNotifyBlocksCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"notifyBlocks","params":[],"id":1}`, - unmarshalled: &model.NotifyBlocksCmd{}, - }, - { - name: "stopNotifyBlocks", - newCmd: func() (interface{}, error) { - return model.NewCommand("stopNotifyBlocks") - }, - staticCmd: func() interface{} { - return model.NewStopNotifyBlocksCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"stopNotifyBlocks","params":[],"id":1}`, - unmarshalled: &model.StopNotifyBlocksCmd{}, - }, - { - name: "notifyChainChanges", - newCmd: func() (interface{}, error) { - return model.NewCommand("notifyChainChanges") - }, - staticCmd: func() interface{} { - return model.NewNotifyChainChangesCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"notifyChainChanges","params":[],"id":1}`, - unmarshalled: &model.NotifyChainChangesCmd{}, - }, - { - name: "stopNotifyChainChanges", - newCmd: func() (interface{}, error) { - return model.NewCommand("stopNotifyChainChanges") - }, - staticCmd: func() interface{} { - return model.NewStopNotifyChainChangesCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"stopNotifyChainChanges","params":[],"id":1}`, - unmarshalled: &model.StopNotifyChainChangesCmd{}, - }, - { - name: "notifyNewTransactions", - newCmd: func() (interface{}, error) { - return model.NewCommand("notifyNewTransactions") - }, - staticCmd: func() interface{} { - return model.NewNotifyNewTransactionsCmd(nil, nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"notifyNewTransactions","params":[],"id":1}`, - unmarshalled: &model.NotifyNewTransactionsCmd{ - Verbose: pointers.Bool(false), - }, - }, - { - name: "notifyNewTransactions optional", - newCmd: func() (interface{}, error) { - return model.NewCommand("notifyNewTransactions", true) - }, - staticCmd: func() interface{} { - return model.NewNotifyNewTransactionsCmd(pointers.Bool(true), nil) - }, - marshalled: `{"jsonrpc":"1.0","method":"notifyNewTransactions","params":[true],"id":1}`, - unmarshalled: &model.NotifyNewTransactionsCmd{ - Verbose: pointers.Bool(true), - }, - }, - { - name: "notifyNewTransactions optional 2", - newCmd: func() (interface{}, error) { - return model.NewCommand("notifyNewTransactions", true, "0000000000000000000000000000000000000123") - }, - staticCmd: func() interface{} { - return model.NewNotifyNewTransactionsCmd(pointers.Bool(true), pointers.String("0000000000000000000000000000000000000123")) - }, - marshalled: `{"jsonrpc":"1.0","method":"notifyNewTransactions","params":[true,"0000000000000000000000000000000000000123"],"id":1}`, - unmarshalled: &model.NotifyNewTransactionsCmd{ - Verbose: pointers.Bool(true), - Subnetwork: pointers.String("0000000000000000000000000000000000000123"), - }, - }, - { - name: "stopNotifyNewTransactions", - newCmd: func() (interface{}, error) { - return model.NewCommand("stopNotifyNewTransactions") - }, - staticCmd: func() interface{} { - return model.NewStopNotifyNewTransactionsCmd() - }, - marshalled: `{"jsonrpc":"1.0","method":"stopNotifyNewTransactions","params":[],"id":1}`, - unmarshalled: &model.StopNotifyNewTransactionsCmd{}, - }, - { - name: "loadTxFilter", - newCmd: func() (interface{}, error) { - return model.NewCommand("loadTxFilter", false, `["1Address"]`, `[{"txid":"0000000000000000000000000000000000000000000000000000000000000123","index":0}]`) - }, - staticCmd: func() interface{} { - addrs := []string{"1Address"} - ops := []model.Outpoint{{ - TxID: "0000000000000000000000000000000000000000000000000000000000000123", - Index: 0, - }} - return model.NewLoadTxFilterCmd(false, addrs, ops) - }, - marshalled: `{"jsonrpc":"1.0","method":"loadTxFilter","params":[false,["1Address"],[{"txid":"0000000000000000000000000000000000000000000000000000000000000123","index":0}]],"id":1}`, - unmarshalled: &model.LoadTxFilterCmd{ - Reload: false, - Addresses: []string{"1Address"}, - Outpoints: []model.Outpoint{{TxID: "0000000000000000000000000000000000000000000000000000000000000123", Index: 0}}, - }, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - // Marshal the command as created by the new static command - // creation function. - marshalled, err := model.MarshalCommand(testID, test.staticCmd()) - if err != nil { - t.Errorf("MarshalCommand #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - - if !bytes.Equal(marshalled, []byte(test.marshalled)) { - t.Errorf("Test #%d (%s) unexpected marshalled data - "+ - "got %s, want %s", i, test.name, marshalled, - test.marshalled) - continue - } - - // Ensure the command is created without error via the generic - // new command creation function. - cmd, err := test.newCmd() - if err != nil { - t.Errorf("Test #%d (%s) unexpected NewCommand error: %v ", - i, test.name, err) - } - - // Marshal the command as created by the generic new command - // creation function. - marshalled, err = model.MarshalCommand(testID, cmd) - if err != nil { - t.Errorf("MarshalCommand #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - - if !bytes.Equal(marshalled, []byte(test.marshalled)) { - t.Errorf("Test #%d (%s) unexpected marshalled data - "+ - "got %s, want %s", i, test.name, marshalled, - test.marshalled) - continue - } - - var request model.Request - if err := json.Unmarshal(marshalled, &request); err != nil { - t.Errorf("Test #%d (%s) unexpected error while "+ - "unmarshalling JSON-RPC request: %v", i, - test.name, err) - continue - } - - cmd, err = model.UnmarshalCommand(&request) - if err != nil { - t.Errorf("UnmarshalCommand #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - - if !reflect.DeepEqual(cmd, test.unmarshalled) { - t.Errorf("Test #%d (%s) unexpected unmarshalled command "+ - "- got %s, want %s", i, test.name, - fmt.Sprintf("(%T) %+[1]v", cmd), - fmt.Sprintf("(%T) %+[1]v\n", test.unmarshalled)) - continue - } - } -} diff --git a/infrastructure/network/rpc/model/rpc_websocket_notifications.go b/infrastructure/network/rpc/model/rpc_websocket_notifications.go deleted file mode 100644 index 77623ac83..000000000 --- a/infrastructure/network/rpc/model/rpc_websocket_notifications.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Copyright (c) 2015-2017 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -// NOTE: This file is intended to house the RPC websocket notifications that are -// supported by a kaspa rpc server. - -package model - -const ( - // FilteredBlockAddedNtfnMethod is the new method used for - // notifications from the kaspa rpc server that a block has been connected. - FilteredBlockAddedNtfnMethod = "filteredBlockAdded" - - // TxAcceptedNtfnMethod is the method used for notifications from the - // kaspa rpc server that a transaction has been accepted into the mempool. - TxAcceptedNtfnMethod = "txAccepted" - - // TxAcceptedVerboseNtfnMethod is the method used for notifications from - // the kaspa rpc server that a transaction has been accepted into the - // mempool. This differs from TxAcceptedNtfnMethod in that it provides - // more details in the notification. - TxAcceptedVerboseNtfnMethod = "txAcceptedVerbose" - - // RelevantTxAcceptedNtfnMethod is the new method used for notifications - // from the kaspa rpc server that inform a client that a transaction that - // matches the loaded filter was accepted by the mempool. - RelevantTxAcceptedNtfnMethod = "relevantTxAccepted" - - // ChainChangedNtfnMethod is the new method used for notifications - // from the kaspa rpc server that inform a client that the selected chain - // has changed. - ChainChangedNtfnMethod = "chainChanged" -) - -// FilteredBlockAddedNtfn defines the filteredBlockAdded JSON-RPC -// notification. -type FilteredBlockAddedNtfn struct { - BlueScore uint64 - Header string - SubscribedTxs []string -} - -// NewFilteredBlockAddedNtfn returns a new instance which can be used to -// issue a filteredBlockAdded JSON-RPC notification. -func NewFilteredBlockAddedNtfn(blueScore uint64, header string, subscribedTxs []string) *FilteredBlockAddedNtfn { - return &FilteredBlockAddedNtfn{ - BlueScore: blueScore, - Header: header, - SubscribedTxs: subscribedTxs, - } -} - -// ChainChangedNtfn defines the chainChanged JSON-RPC -// notification. -type ChainChangedNtfn struct { - ChainChangedRawParam ChainChangedRawParam -} - -// ChainChangedRawParam is the first parameter -// of ChainChangedNtfn which contains all the -// remove chain block hashes and the added -// chain blocks. -type ChainChangedRawParam struct { - RemovedChainBlockHashes []string `json:"removedChainBlockHashes"` - AddedChainBlocks []ChainBlock `json:"addedChainBlocks"` -} - -// NewChainChangedNtfn returns a new instance which can be used to -// issue a chainChanged JSON-RPC notification. -func NewChainChangedNtfn(removedChainBlockHashes []string, - addedChainBlocks []ChainBlock) *ChainChangedNtfn { - return &ChainChangedNtfn{ChainChangedRawParam: ChainChangedRawParam{ - RemovedChainBlockHashes: removedChainBlockHashes, - AddedChainBlocks: addedChainBlocks, - }} -} - -// BlockDetails describes details of a tx in a block. -type BlockDetails struct { - Height uint64 `json:"height"` - Hash string `json:"hash"` - Index int `json:"index"` - Time int64 `json:"time"` -} - -// TxAcceptedNtfn defines the txAccepted JSON-RPC notification. -type TxAcceptedNtfn struct { - TxID string - Amount float64 -} - -// NewTxAcceptedNtfn returns a new instance which can be used to issue a -// txAccepted JSON-RPC notification. -func NewTxAcceptedNtfn(txHash string, amount float64) *TxAcceptedNtfn { - return &TxAcceptedNtfn{ - TxID: txHash, - Amount: amount, - } -} - -// TxAcceptedVerboseNtfn defines the txAcceptedVerbose JSON-RPC notification. -type TxAcceptedVerboseNtfn struct { - RawTx TxRawResult -} - -// NewTxAcceptedVerboseNtfn returns a new instance which can be used to issue a -// txAcceptedVerbose JSON-RPC notification. -func NewTxAcceptedVerboseNtfn(rawTx TxRawResult) *TxAcceptedVerboseNtfn { - return &TxAcceptedVerboseNtfn{ - RawTx: rawTx, - } -} - -// RelevantTxAcceptedNtfn defines the parameters to the relevantTxAccepted -// JSON-RPC notification. -type RelevantTxAcceptedNtfn struct { - Transaction string `json:"transaction"` -} - -// NewRelevantTxAcceptedNtfn returns a new instance which can be used to issue a -// relevantxaccepted JSON-RPC notification. -func NewRelevantTxAcceptedNtfn(txHex string) *RelevantTxAcceptedNtfn { - return &RelevantTxAcceptedNtfn{Transaction: txHex} -} - -func init() { - // The commands in this file are only usable by websockets and are - // notifications. - flags := UFWebsocketOnly | UFNotification - - MustRegisterCommand(FilteredBlockAddedNtfnMethod, (*FilteredBlockAddedNtfn)(nil), flags) - MustRegisterCommand(TxAcceptedNtfnMethod, (*TxAcceptedNtfn)(nil), flags) - MustRegisterCommand(TxAcceptedVerboseNtfnMethod, (*TxAcceptedVerboseNtfn)(nil), flags) - MustRegisterCommand(RelevantTxAcceptedNtfnMethod, (*RelevantTxAcceptedNtfn)(nil), flags) - MustRegisterCommand(ChainChangedNtfnMethod, (*ChainChangedNtfn)(nil), flags) -} diff --git a/infrastructure/network/rpc/model/rpc_websocket_notifications_test.go b/infrastructure/network/rpc/model/rpc_websocket_notifications_test.go deleted file mode 100644 index 43de84ae0..000000000 --- a/infrastructure/network/rpc/model/rpc_websocket_notifications_test.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright (c) 2014-2017 The btcsuite developers -// Copyright (c) 2015-2017 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model_test - -import ( - "bytes" - "encoding/json" - "fmt" - "reflect" - "testing" - - "github.com/kaspanet/kaspad/util/subnetworkid" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/daghash" -) - -// TestRPCServerWebsocketNotifications tests all of the kaspa rpc server websocket-specific -// notifications marshal and unmarshal into valid results include handling of -// optional fields being omitted in the marshalled command, while optional -// fields with defaults have the default assigned on unmarshalled commands. -func TestRPCServerWebsocketNotifications(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - newNtfn func() (interface{}, error) - staticNtfn func() interface{} - marshalled string - unmarshalled interface{} - }{ - { - name: "filteredBlockAdded", - newNtfn: func() (interface{}, error) { - return model.NewCommand("filteredBlockAdded", 100, "header", []string{"tx0", "tx1"}) - }, - staticNtfn: func() interface{} { - return model.NewFilteredBlockAddedNtfn(100, "header", []string{"tx0", "tx1"}) - }, - marshalled: `{"jsonrpc":"1.0","method":"filteredBlockAdded","params":[100,"header",["tx0","tx1"]],"id":null}`, - unmarshalled: &model.FilteredBlockAddedNtfn{ - BlueScore: 100, - Header: "header", - SubscribedTxs: []string{"tx0", "tx1"}, - }, - }, - { - name: "txAccepted", - newNtfn: func() (interface{}, error) { - return model.NewCommand("txAccepted", "123", 1.5) - }, - staticNtfn: func() interface{} { - return model.NewTxAcceptedNtfn("123", 1.5) - }, - marshalled: `{"jsonrpc":"1.0","method":"txAccepted","params":["123",1.5],"id":null}`, - unmarshalled: &model.TxAcceptedNtfn{ - TxID: "123", - Amount: 1.5, - }, - }, - { - name: "txAcceptedVerbose", - newNtfn: func() (interface{}, error) { - return model.NewCommand("txAcceptedVerbose", `{"hex":"001122","txid":"123","version":1,"locktime":4294967295,"subnetwork":"0000000000000000000000000000000000000000","gas":0,"payloadHash":"","payload":"","vin":null,"vout":null,"isInMempool":false}`) - }, - staticNtfn: func() interface{} { - txResult := model.TxRawResult{ - Hex: "001122", - TxID: "123", - Version: 1, - LockTime: 4294967295, - Subnetwork: subnetworkid.SubnetworkIDNative.String(), - Vin: nil, - Vout: nil, - } - return model.NewTxAcceptedVerboseNtfn(txResult) - }, - marshalled: `{"jsonrpc":"1.0","method":"txAcceptedVerbose","params":[{"hex":"001122","txId":"123","version":1,"lockTime":4294967295,"subnetwork":"0000000000000000000000000000000000000000","gas":0,"payloadHash":"","payload":"","vin":null,"vout":null,"isInMempool":false}],"id":null}`, - unmarshalled: &model.TxAcceptedVerboseNtfn{ - RawTx: model.TxRawResult{ - Hex: "001122", - TxID: "123", - Version: 1, - LockTime: 4294967295, - Subnetwork: subnetworkid.SubnetworkIDNative.String(), - Vin: nil, - Vout: nil, - }, - }, - }, - { - name: "txAcceptedVerbose with subnetwork, gas and paylaod", - newNtfn: func() (interface{}, error) { - return model.NewCommand("txAcceptedVerbose", `{"hex":"001122","txId":"123","version":1,"lockTime":4294967295,"subnetwork":"000000000000000000000000000000000000432d","gas":10,"payloadHash":"bf8ccdb364499a3e628200c3d3512c2c2a43b7a7d4f1a40d7f716715e449f442","payload":"102030","vin":null,"vout":null,"isInMempool":false}`) - }, - staticNtfn: func() interface{} { - txResult := model.TxRawResult{ - Hex: "001122", - TxID: "123", - Version: 1, - LockTime: 4294967295, - Subnetwork: subnetworkid.SubnetworkID{45, 67}.String(), - PayloadHash: daghash.DoubleHashP([]byte("102030")).String(), - Payload: "102030", - Gas: 10, - Vin: nil, - Vout: nil, - } - return model.NewTxAcceptedVerboseNtfn(txResult) - }, - marshalled: `{"jsonrpc":"1.0","method":"txAcceptedVerbose","params":[{"hex":"001122","txId":"123","version":1,"lockTime":4294967295,"subnetwork":"000000000000000000000000000000000000432d","gas":10,"payloadHash":"bf8ccdb364499a3e628200c3d3512c2c2a43b7a7d4f1a40d7f716715e449f442","payload":"102030","vin":null,"vout":null,"isInMempool":false}],"id":null}`, - unmarshalled: &model.TxAcceptedVerboseNtfn{ - RawTx: model.TxRawResult{ - Hex: "001122", - TxID: "123", - Version: 1, - LockTime: 4294967295, - Subnetwork: subnetworkid.SubnetworkID{45, 67}.String(), - PayloadHash: daghash.DoubleHashP([]byte("102030")).String(), - Payload: "102030", - Gas: 10, - Vin: nil, - Vout: nil, - }, - }, - }, - { - name: "relevantTxAccepted", - newNtfn: func() (interface{}, error) { - return model.NewCommand("relevantTxAccepted", "001122") - }, - staticNtfn: func() interface{} { - return model.NewRelevantTxAcceptedNtfn("001122") - }, - marshalled: `{"jsonrpc":"1.0","method":"relevantTxAccepted","params":["001122"],"id":null}`, - unmarshalled: &model.RelevantTxAcceptedNtfn{ - Transaction: "001122", - }, - }, - } - - t.Logf("Running %d tests", len(tests)) - for i, test := range tests { - // Marshal the notification as created by the new static - // creation function. The ID is nil for notifications. - marshalled, err := model.MarshalCommand(nil, test.staticNtfn()) - if err != nil { - t.Errorf("MarshalCommand #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - - if !bytes.Equal(marshalled, []byte(test.marshalled)) { - t.Errorf("Test #%d (%s) unexpected marshalled data - "+ - "got %s, want %s", i, test.name, marshalled, - test.marshalled) - continue - } - - // Ensure the notification is created without error via the - // generic new notification creation function. - cmd, err := test.newNtfn() - if err != nil { - t.Errorf("Test #%d (%s) unexpected NewCommand error: %v ", - i, test.name, err) - } - - // Marshal the notification as created by the generic new - // notification creation function. The ID is nil for - // notifications. - marshalled, err = model.MarshalCommand(nil, cmd) - if err != nil { - t.Errorf("MarshalCommand #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - - if !bytes.Equal(marshalled, []byte(test.marshalled)) { - t.Errorf("Test #%d (%s) unexpected marshalled data - "+ - "got %s, want %s", i, test.name, marshalled, - test.marshalled) - continue - } - - var request model.Request - if err := json.Unmarshal(marshalled, &request); err != nil { - t.Errorf("Test #%d (%s) unexpected error while "+ - "unmarshalling JSON-RPC request: %v", i, - test.name, err) - continue - } - - cmd, err = model.UnmarshalCommand(&request) - if err != nil { - t.Errorf("UnmarshalCommand #%d (%s) unexpected error: %v", i, - test.name, err) - continue - } - - if !reflect.DeepEqual(cmd, test.unmarshalled) { - t.Errorf("Test #%d (%s) unexpected unmarshalled command "+ - "- got %s, want %s", i, test.name, - fmt.Sprintf("(%T) %+[1]v", cmd), - fmt.Sprintf("(%T) %+[1]v\n", test.unmarshalled)) - continue - } - } -} diff --git a/infrastructure/network/rpc/model/rpc_websocket_results.go b/infrastructure/network/rpc/model/rpc_websocket_results.go deleted file mode 100644 index 69a613264..000000000 --- a/infrastructure/network/rpc/model/rpc_websocket_results.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2015-2017 The btcsuite developers -// Copyright (c) 2015-2017 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package model - -// SessionResult models the data from the session command. -type SessionResult struct { - SessionID uint64 `json:"sessionId"` -} diff --git a/infrastructure/network/rpc/rpcserver.go b/infrastructure/network/rpc/rpcserver.go deleted file mode 100644 index 0d06e352a..000000000 --- a/infrastructure/network/rpc/rpcserver.go +++ /dev/null @@ -1,786 +0,0 @@ -// Copyright (c) 2013-2017 The btcsuite developers -// Copyright (c) 2015-2017 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package rpc - -import ( - "crypto/sha256" - "crypto/subtle" - "crypto/tls" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "math/rand" - "net" - "net/http" - "strconv" - "sync" - "sync/atomic" - "time" - - "github.com/kaspanet/kaspad/app/protocol" - "github.com/kaspanet/kaspad/infrastructure/network/addressmanager" - "github.com/kaspanet/kaspad/infrastructure/network/connmanager" - "github.com/kaspanet/kaspad/util/mstime" - - "github.com/pkg/errors" - - "github.com/btcsuite/websocket" - "github.com/kaspanet/kaspad/domain/blockdag" - "github.com/kaspanet/kaspad/domain/blockdag/indexers" - "github.com/kaspanet/kaspad/domain/mempool" - "github.com/kaspanet/kaspad/domain/mining" - "github.com/kaspanet/kaspad/infrastructure/config" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util/fs" - "github.com/kaspanet/kaspad/util/network" -) - -const ( - // rpcAuthTimeoutSeconds is the number of seconds a connection to the - // RPC server is allowed to stay open without authenticating before it - // is closed. - rpcAuthTimeoutSeconds = 10 - - // maxProtocolVersion is the max protocol version the server supports. - maxProtocolVersion = 70002 -) - -type commandHandler func(*Server, interface{}, <-chan struct{}) (interface{}, error) - -// rpcHandlers maps RPC command strings to appropriate handler functions. -// This is set by init because help references rpcHandlers and thus causes -// a dependency loop. -var rpcHandlers map[string]commandHandler -var rpcHandlersBeforeInit = map[string]commandHandler{ - "connect": handleConnect, - "debugLevel": handleDebugLevel, - "getSelectedTip": handleGetSelectedTip, - "getSelectedTipHash": handleGetSelectedTipHash, - "getBlock": handleGetBlock, - "getBlocks": handleGetBlocks, - "getBlockDagInfo": handleGetBlockDAGInfo, - "getBlockCount": handleGetBlockCount, - "getBlockHeader": handleGetBlockHeader, - "getBlockTemplate": handleGetBlockTemplate, - "getChainFromBlock": handleGetChainFromBlock, - "getConnectionCount": handleGetConnectionCount, - "getCurrentNet": handleGetCurrentNet, - "getDifficulty": handleGetDifficulty, - "getHeaders": handleGetHeaders, - "getTopHeaders": handleGetTopHeaders, - "getInfo": handleGetInfo, - "getMempoolInfo": handleGetMempoolInfo, - "getMempoolEntry": handleGetMempoolEntry, - "getConnectedPeerInfo": handleGetConnectedPeerInfo, - "getPeerAddresses": handleGetPeerAddresses, - "getRawMempool": handleGetRawMempool, - "getSubnetwork": handleGetSubnetwork, - "getTxOut": handleGetTxOut, - "help": handleHelp, - "disconnect": handleDisconnect, - "sendRawTransaction": handleSendRawTransaction, - "stop": handleStop, - "submitBlock": handleSubmitBlock, - "uptime": handleUptime, - "version": handleVersion, -} - -// Commands that are currently unimplemented, but should ultimately be. -var rpcUnimplemented = map[string]struct{}{ - "getMempoolEntry": {}, - "getNetworkInfo": {}, - "getNetTotals": {}, -} - -// Commands that are available to a limited user -var rpcLimited = map[string]struct{}{ - // Websockets commands - "loadTxFilter": {}, - "notifyBlocks": {}, - "notifyChainChanges": {}, - "notifyNewTransactions": {}, - "notifyReceived": {}, - "notifySpent": {}, - "session": {}, - - // Websockets AND HTTP/S commands - "help": {}, - - // HTTP/S-only commands - "createRawTransaction": {}, - "decodeRawTransaction": {}, - "decodeScript": {}, - "getSelectedTip": {}, - "getSelectedTipHash": {}, - "getBlock": {}, - "getBlocks": {}, - "getBlockCount": {}, - "getBlockHash": {}, - "getBlockHeader": {}, - "getChainFromBlock": {}, - "getCurrentNet": {}, - "getDifficulty": {}, - "getHeaders": {}, - "getInfo": {}, - "getNetTotals": {}, - "getRawMempool": {}, - "getTxOut": {}, - "sendRawTransaction": {}, - "submitBlock": {}, - "uptime": {}, - "validateAddress": {}, - "version": {}, -} - -// handleUnimplemented is the handler for commands that should ultimately be -// supported but are not yet implemented. -func handleUnimplemented(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - return nil, ErrRPCUnimplemented -} - -// Server provides a concurrent safe RPC server to a kaspa node. -type Server struct { - listeners []net.Listener - started int32 - shutdown int32 - cfg *config.Config - startupTime mstime.Time - authsha [sha256.Size]byte - limitauthsha [sha256.Size]byte - ntfnMgr *wsNotificationManager - numClients int32 - statusLines map[int]string - statusLock sync.RWMutex - wg sync.WaitGroup - gbtWorkState *gbtWorkState - helpCacher *helpCacher - requestProcessShutdown chan struct{} - quit chan int - - dag *blockdag.BlockDAG - txMempool *mempool.TxPool - acceptanceIndex *indexers.AcceptanceIndex - blockTemplateGenerator *mining.BlkTmplGenerator - connectionManager *connmanager.ConnectionManager - addressManager *addressmanager.AddressManager - protocolManager *protocol.Manager -} - -// httpStatusLine returns a response Status-Line (RFC 2616 Section 6.1) -// for the given request and response status code. This function was lifted and -// adapted from the standard library HTTP server code since it's not exported. -func (s *Server) httpStatusLine(req *http.Request, code int) string { - // Fast path: - key := code - proto11 := req.ProtoAtLeast(1, 1) - if !proto11 { - key = -key - } - line, ok := func() (string, bool) { - s.statusLock.RLock() - defer s.statusLock.RUnlock() - line, ok := s.statusLines[key] - return line, ok - }() - if ok { - return line - } - - // Slow path: - proto := "HTTP/1.0" - if proto11 { - proto = "HTTP/1.1" - } - codeStr := strconv.Itoa(code) - text := http.StatusText(code) - if text != "" { - line = proto + " " + codeStr + " " + text + "\r\n" - s.statusLock.Lock() - defer s.statusLock.Unlock() - s.statusLines[key] = line - } else { - text = "status code " + codeStr - line = proto + " " + codeStr + " " + text + "\r\n" - } - - return line -} - -// writeHTTPResponseHeaders writes the necessary response headers prior to -// writing an HTTP body given a request to use for protocol negotiation, headers -// to write, a status code, and a writer. -func (s *Server) writeHTTPResponseHeaders(req *http.Request, headers http.Header, code int, w io.Writer) error { - _, err := io.WriteString(w, s.httpStatusLine(req, code)) - if err != nil { - return err - } - - err = headers.Write(w) - if err != nil { - return err - } - - _, err = io.WriteString(w, "\r\n") - return err -} - -// Stop is used by server.go to stop the rpc listener. -func (s *Server) Stop() error { - if atomic.AddInt32(&s.shutdown, 1) != 1 { - log.Infof("RPC server is already in the process of shutting down") - return nil - } - log.Warnf("RPC server shutting down") - for _, listener := range s.listeners { - err := listener.Close() - if err != nil { - log.Errorf("Problem shutting down rpc: %s", err) - return err - } - } - s.ntfnMgr.Shutdown() - s.ntfnMgr.WaitForShutdown() - close(s.quit) - s.wg.Wait() - log.Infof("RPC server shutdown complete") - return nil -} - -// RequestedProcessShutdown returns a channel that is sent to when an authorized -// RPC client requests the process to shutdown. If the request can not be read -// immediately, it is dropped. -func (s *Server) RequestedProcessShutdown() <-chan struct{} { - return s.requestProcessShutdown -} - -// NotifyNewTransactions notifies both websocket and getBlockTemplate long -// poll clients of the passed transactions. This function should be called -// whenever new transactions are added to the mempool. -func (s *Server) NotifyNewTransactions(txns []*mempool.TxDesc) { - for _, txD := range txns { - // Notify websocket clients about mempool transactions. - s.ntfnMgr.NotifyMempoolTx(txD.Tx, true) - - // Potentially notify any getBlockTemplate long poll clients - // about stale block templates due to the new transaction. - s.gbtWorkState.NotifyMempoolTx(s.txMempool.LastUpdated()) - } -} - -// limitConnections responds with a 503 service unavailable and returns true if -// adding another client would exceed the maximum allow RPC clients. -// -// This function is safe for concurrent access. -func (s *Server) limitConnections(w http.ResponseWriter, remoteAddr string) bool { - if int(atomic.LoadInt32(&s.numClients)+1) > s.cfg.RPCMaxClients { - log.Infof("Max RPC clients exceeded [%d] - "+ - "disconnecting client %s", s.cfg.RPCMaxClients, - remoteAddr) - http.Error(w, "503 Too busy. Try again later.", - http.StatusServiceUnavailable) - return true - } - return false -} - -// incrementClients adds one to the number of connected RPC clients. Note -// this only applies to standard clients. Websocket clients have their own -// limits and are tracked separately. -// -// This function is safe for concurrent access. -func (s *Server) incrementClients() { - atomic.AddInt32(&s.numClients, 1) -} - -// decrementClients subtracts one from the number of connected RPC clients. -// Note this only applies to standard clients. Websocket clients have their own -// limits and are tracked separately. -// -// This function is safe for concurrent access. -func (s *Server) decrementClients() { - atomic.AddInt32(&s.numClients, -1) -} - -// checkAuth checks the HTTP Basic authentication supplied by a wallet -// or RPC client in the HTTP request r. If the supplied authentication -// does not match the username and password expected, a non-nil error is -// returned. -// -// This check is time-constant. -// -// The first bool return value signifies auth success (true if successful) and -// the second bool return value specifies whether the user can change the state -// of the server (true) or whether the user is limited (false). The second is -// always false if the first is. -func (s *Server) checkAuth(r *http.Request, require bool) (bool, bool, error) { - authhdr := r.Header["Authorization"] - if len(authhdr) <= 0 { - if require { - log.Warnf("RPC authentication failure from %s", - r.RemoteAddr) - return false, false, errors.New("auth failure") - } - - return false, false, nil - } - - authsha := sha256.Sum256([]byte(authhdr[0])) - - // Check for limited auth first as in environments with limited users, those - // are probably expected to have a higher volume of calls - limitcmp := subtle.ConstantTimeCompare(authsha[:], s.limitauthsha[:]) - if limitcmp == 1 { - return true, false, nil - } - - // Check for admin-level auth - cmp := subtle.ConstantTimeCompare(authsha[:], s.authsha[:]) - if cmp == 1 { - return true, true, nil - } - - // Request's auth doesn't match either user - log.Warnf("RPC authentication failure from %s", r.RemoteAddr) - return false, false, errors.New("auth failure") -} - -// parsedRPCCmd represents a JSON-RPC request object that has been parsed into -// a known concrete command along with any error that might have happened while -// parsing it. -type parsedRPCCmd struct { - id interface{} - method string - cmd interface{} - err *model.RPCError -} - -// standardCmdResult checks that a parsed command is a standard kaspa JSON-RPC -// command and runs the appropriate handler to reply to the command. Any -// commands which are not recognized or not implemented will return an error -// suitable for use in replies. -func (s *Server) standardCmdResult(cmd *parsedRPCCmd, closeChan <-chan struct{}) (interface{}, error) { - handler, ok := rpcHandlers[cmd.method] - if ok { - goto handled - } - _, ok = rpcUnimplemented[cmd.method] - if ok { - handler = handleUnimplemented - goto handled - } - return nil, model.ErrRPCMethodNotFound -handled: - - return handler(s, cmd.cmd, closeChan) -} - -// parseCmd parses a JSON-RPC request object into known concrete command. The -// err field of the returned parsedRPCCmd struct will contain an RPC error that -// is suitable for use in replies if the command is invalid in some way such as -// an unregistered command or invalid parameters. -func parseCmd(request *model.Request) *parsedRPCCmd { - var parsedCmd parsedRPCCmd - parsedCmd.id = request.ID - parsedCmd.method = request.Method - - cmd, err := model.UnmarshalCommand(request) - if err != nil { - // When the error is because the method is not registered, - // produce a method not found RPC error. - var rpcModelErr model.Error - if ok := errors.As(err, &rpcModelErr); ok && - rpcModelErr.ErrorCode == model.ErrUnregisteredMethod { - - parsedCmd.err = model.ErrRPCMethodNotFound - return &parsedCmd - } - - // Otherwise, some type of invalid parameters is the - // cause, so produce the equivalent RPC error. - parsedCmd.err = model.NewRPCError( - model.ErrRPCInvalidParams.Code, err.Error()) - return &parsedCmd - } - - parsedCmd.cmd = cmd - return &parsedCmd -} - -// createMarshalledReply returns a new marshalled JSON-RPC response given the -// passed parameters. It will automatically convert errors that are not of -// the type *model.RPCError to the appropriate type as needed. -func createMarshalledReply(id, result interface{}, replyErr error) ([]byte, error) { - var jsonErr *model.RPCError - if replyErr != nil { - if jErr, ok := replyErr.(*model.RPCError); ok { - jsonErr = jErr - } else { - jsonErr = internalRPCError(replyErr.Error(), "") - } - } - - return model.MarshalResponse(id, result, jsonErr) -} - -// jsonRPCRead handles reading and responding to RPC messages. -func (s *Server) jsonRPCRead(w http.ResponseWriter, r *http.Request, isAdmin bool) { - if atomic.LoadInt32(&s.shutdown) != 0 { - return - } - - // Read and close the JSON-RPC request body from the caller. - body, err := ioutil.ReadAll(r.Body) - r.Body.Close() - if err != nil { - errCode := http.StatusBadRequest - http.Error(w, fmt.Sprintf("%d error reading JSON message: %s", - errCode, err), errCode) - return - } - - // Unfortunately, the http server doesn't provide the ability to - // change the read deadline for the new connection and having one breaks - // long polling. However, not having a read deadline on the initial - // connection would mean clients can connect and idle forever. Thus, - // hijack the connecton from the HTTP server, clear the read deadline, - // and handle writing the response manually. - hj, ok := w.(http.Hijacker) - if !ok { - errMsg := "webserver doesn't support hijacking" - log.Warnf(errMsg) - errCode := http.StatusInternalServerError - http.Error(w, strconv.Itoa(errCode)+" "+errMsg, errCode) - return - } - conn, buf, err := hj.Hijack() - if err != nil { - log.Warnf("Failed to hijack HTTP connection: %s", err) - errCode := http.StatusInternalServerError - http.Error(w, strconv.Itoa(errCode)+" "+err.Error(), errCode) - return - } - defer conn.Close() - defer buf.Flush() - conn.SetReadDeadline(timeZeroVal) - - // Attempt to parse the raw body into a JSON-RPC request. - var responseID interface{} - var jsonErr error - var result interface{} - var request model.Request - if err := json.Unmarshal(body, &request); err != nil { - jsonErr = &model.RPCError{ - Code: model.ErrRPCParse.Code, - Message: "Failed to parse request: " + err.Error(), - } - } - if jsonErr == nil { - // The JSON-RPC 1.0 spec defines that notifications must have their "id" - // set to null and states that notifications do not have a response. - // - // A JSON-RPC 2.0 notification is a request with "json-rpc":"2.0", and - // without an "id" member. The specification states that notifications - // must not be responded to. JSON-RPC 2.0 permits the null value as a - // valid request id, therefore such requests are not notifications. - // - // Kaspad does not respond to any request without an "id" or "id":null, - // regardless the indicated JSON-RPC protocol version. - if request.ID == nil { - return - } - - // The parse was at least successful enough to have an ID so - // set it for the response. - responseID = request.ID - - // Setup a close notifier. Since the connection is hijacked, - // the CloseNotifer on the ResponseWriter is not available. - closeChan := make(chan struct{}, 1) - spawn("Server.jsonRPCRead-conn.Read", func() { - _, err := conn.Read(make([]byte, 1)) - if err != nil { - close(closeChan) - } - }) - - // Check if the user is limited and set error if method unauthorized - if !isAdmin { - if _, ok := rpcLimited[request.Method]; !ok { - jsonErr = &model.RPCError{ - Code: model.ErrRPCInvalidParams.Code, - Message: "limited user not authorized for this method", - } - } - } - - if jsonErr == nil { - // Attempt to parse the JSON-RPC request into a known concrete - // command. - parsedCmd := parseCmd(&request) - if parsedCmd.err != nil { - jsonErr = parsedCmd.err - } else { - log.Debugf("HTTP server received command <%s> from %s", parsedCmd.method, r.RemoteAddr) - result, jsonErr = s.standardCmdResult(parsedCmd, closeChan) - } - } - } - - // Marshal the response. - msg, err := createMarshalledReply(responseID, result, jsonErr) - if err != nil { - log.Errorf("Failed to marshal reply: %s", err) - return - } - - // Write the response. - err = s.writeHTTPResponseHeaders(r, w.Header(), http.StatusOK, buf) - if err != nil { - log.Error(err) - return - } - if _, err := buf.Write(msg); err != nil { - log.Errorf("Failed to write marshalled reply: %s", err) - } - - // Terminate with newline for historical reasons. - if err := buf.WriteByte('\n'); err != nil { - log.Errorf("Failed to append terminating newline to reply: %s", err) - } -} - -// jsonAuthFail sends a message back to the client if the http auth is rejected. -func jsonAuthFail(w http.ResponseWriter) { - w.Header().Add("WWW-Authenticate", `Basic realm="kaspad RPC"`) - http.Error(w, "401 Unauthorized.", http.StatusUnauthorized) -} - -// Start is used by server.go to start the rpc listener. -func (s *Server) Start() { - if atomic.AddInt32(&s.started, 1) != 1 { - return - } - - log.Trace("Starting RPC server") - rpcServeMux := http.NewServeMux() - httpServer := &http.Server{ - Handler: rpcServeMux, - - // Timeout connections which don't complete the initial - // handshake within the allowed timeframe. - ReadTimeout: time.Second * rpcAuthTimeoutSeconds, - } - rpcServeMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Connection", "close") - w.Header().Set("Content-Type", "application/json") - r.Close = true - - // Limit the number of connections to max allowed. - if s.limitConnections(w, r.RemoteAddr) { - return - } - - // Keep track of the number of connected clients. - s.incrementClients() - defer s.decrementClients() - _, isAdmin, err := s.checkAuth(r, true) - if err != nil { - jsonAuthFail(w) - return - } - - // Read and respond to the request. - s.jsonRPCRead(w, r, isAdmin) - }) - - // Websocket endpoint. - rpcServeMux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { - authenticated, isAdmin, err := s.checkAuth(r, false) - if err != nil { - jsonAuthFail(w) - return - } - - // Attempt to upgrade the connection to a websocket connection - // using the default size for read/write buffers. - ws, err := websocket.Upgrade(w, r, nil, 0, 0) - if err != nil { - if !errors.As(err, &websocket.HandshakeError{}) { - log.Errorf("Unexpected websocket error: %s", - err) - } - http.Error(w, "400 Bad Request.", http.StatusBadRequest) - return - } - s.WebsocketHandler(ws, r.RemoteAddr, authenticated, isAdmin) - }) - - for _, listener := range s.listeners { - s.wg.Add(1) - // Declaring this variable is necessary as it needs be declared in the same - // scope of the anonymous function below it. - listenerCopy := listener - spawn("Server.Start-httpServer.Serve", func() { - log.Infof("RPC server listening on %s", listenerCopy.Addr()) - httpServer.Serve(listenerCopy) - log.Tracef("RPC listener done for %s", listenerCopy.Addr()) - s.wg.Done() - }) - } - - s.ntfnMgr.Start() -} - -// setupRPCListeners returns a slice of listeners that are configured for use -// with the RPC server depending on the configuration settings for listen -// addresses and TLS. -func setupRPCListeners(appCfg *config.Config) ([]net.Listener, error) { - // Setup TLS if not disabled. - listenFunc := net.Listen - if !appCfg.DisableTLS { - // Generate the TLS cert and key file if both don't already - // exist. - if !fs.FileExists(appCfg.RPCKey) && !fs.FileExists(appCfg.RPCCert) { - err := GenCertPair(appCfg.RPCCert, appCfg.RPCKey) - if err != nil { - return nil, err - } - } - keypair, err := tls.LoadX509KeyPair(appCfg.RPCCert, appCfg.RPCKey) - if err != nil { - return nil, err - } - - tlsConfig := tls.Config{ - Certificates: []tls.Certificate{keypair}, - MinVersion: tls.VersionTLS12, - } - - // Change the standard net.Listen function to the tls one. - listenFunc = func(net string, laddr string) (net.Listener, error) { - return tls.Listen(net, laddr, &tlsConfig) - } - } - - netAddrs, err := network.ParseListeners(appCfg.RPCListeners) - if err != nil { - return nil, err - } - - listeners := make([]net.Listener, 0, len(netAddrs)) - for _, addr := range netAddrs { - listener, err := listenFunc(addr.Network(), addr.String()) - if err != nil { - log.Warnf("Can't listen on %s: %s", addr, err) - continue - } - listeners = append(listeners, listener) - } - - return listeners, nil -} - -// NewRPCServer returns a new instance of the rpcServer struct. -func NewRPCServer( - cfg *config.Config, - dag *blockdag.BlockDAG, - txMempool *mempool.TxPool, - acceptanceIndex *indexers.AcceptanceIndex, - blockTemplateGenerator *mining.BlkTmplGenerator, - connectionManager *connmanager.ConnectionManager, - addressManager *addressmanager.AddressManager, - protocolManager *protocol.Manager, -) (*Server, error) { - // Setup listeners for the configured RPC listen addresses and - // TLS settings. - rpcListeners, err := setupRPCListeners(cfg) - if err != nil { - return nil, err - } - if len(rpcListeners) == 0 { - return nil, errors.New("RPCS: No valid listen address") - } - rpc := Server{ - cfg: cfg, - - listeners: rpcListeners, - startupTime: mstime.Now(), - statusLines: make(map[int]string), - gbtWorkState: newGbtWorkState(), - helpCacher: newHelpCacher(), - requestProcessShutdown: make(chan struct{}), - quit: make(chan int), - - dag: dag, - txMempool: txMempool, - acceptanceIndex: acceptanceIndex, - blockTemplateGenerator: blockTemplateGenerator, - connectionManager: connectionManager, - addressManager: addressManager, - protocolManager: protocolManager, - } - if cfg.RPCUser != "" && cfg.RPCPass != "" { - login := cfg.RPCUser + ":" + cfg.RPCPass - auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) - rpc.authsha = sha256.Sum256([]byte(auth)) - } - if cfg.RPCLimitUser != "" && cfg.RPCLimitPass != "" { - login := cfg.RPCLimitUser + ":" + cfg.RPCLimitPass - auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) - rpc.limitauthsha = sha256.Sum256([]byte(auth)) - } - rpc.ntfnMgr = newWsNotificationManager(&rpc) - rpc.dag.Subscribe(rpc.handleBlockDAGNotification) - - return &rpc, nil -} - -// Callback for notifications from blockdag. It notifies clients that are -// long polling for changes or subscribed to websockets notifications. -func (s *Server) handleBlockDAGNotification(notification *blockdag.Notification) { - switch notification.Type { - case blockdag.NTBlockAdded: - data, ok := notification.Data.(*blockdag.BlockAddedNotificationData) - if !ok { - log.Warnf("Block added notification data is of wrong type.") - break - } - block := data.Block - - tipHashes := s.dag.TipHashes() - - // Allow any clients performing long polling via the - // getBlockTemplate RPC to be notified when the new block causes - // their old block template to become stale. - s.gbtWorkState.NotifyBlockAdded(tipHashes) - - // Notify registered websocket clients of incoming block. - s.ntfnMgr.NotifyBlockAdded(block) - case blockdag.NTChainChanged: - data, ok := notification.Data.(*blockdag.ChainChangedNotificationData) - if !ok { - log.Warnf("Chain changed notification data is of wrong type.") - break - } - - // If the acceptance index is off we aren't capable of serving - // ChainChanged notifications. - if s.acceptanceIndex == nil { - break - } - - // Notify registered websocket clients of chain changes. - s.ntfnMgr.NotifyChainChanged(data.RemovedChainBlockHashes, - data.AddedChainBlockHashes) - } -} - -func init() { - rpcHandlers = rpcHandlersBeforeInit - rand.Seed(time.Now().UnixNano()) -} diff --git a/infrastructure/network/rpc/rpcserverhelp.go b/infrastructure/network/rpc/rpcserverhelp.go deleted file mode 100644 index 42b0bfbaa..000000000 --- a/infrastructure/network/rpc/rpcserverhelp.go +++ /dev/null @@ -1,680 +0,0 @@ -// Copyright (c) 2015-2017 The btcsuite developers -// Copyright (c) 2015-2017 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package rpc - -import ( - "sort" - "strings" - "sync" - - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/pkg/errors" -) - -// helpDescsEnUS defines the English descriptions used for the help strings. -var helpDescsEnUS = map[string]string{ - // DebugLevelCmd help. - "debugLevel--synopsis": "Dynamically changes the debug logging level.\n" + - "The levelspec can either a debug level or of the form:\n" + - "=,=,...\n" + - "The valid debug levels are trace, debug, info, warn, error, and critical.\n" + - "The valid subsystems are AMGR, ADXR, KSDB, BMGR, KASD, BDAG, DISC, PEER, RPCS, SCRP, SRVR, and TXMP.\n" + - "Finally the keyword 'show' will return a list of the available subsystems.", - "debugLevel-levelSpec": "The debug level(s) to use or the keyword 'show'", - "debugLevel--condition0": "levelspec!=show", - "debugLevel--condition1": "levelspec=show", - "debugLevel--result0": "The string 'Done.'", - "debugLevel--result1": "The list of subsystems", - - // ConnectCmd help. - "connect--synopsis": "Attempts to connect a peer.", - "connect-address": "IP address and port of the peer to connect", - "connect-isPermanent": "Whether the connection for this address should be permanent", - - // TransactionInput help. - "transactionInput-txId": "The hash of the input transaction", - "transactionInput-vout": "The specific output of the input transaction to redeem", - - // CreateRawTransactionCmd help. - "createRawTransaction--synopsis": "Returns a new transaction spending the provided inputs and sending to the provided addresses.\n" + - "The transaction inputs are not signed in the created transaction, and as such must be signed separately.", - "createRawTransaction-inputs": "The inputs to the transaction", - "createRawTransaction-amounts": "JSON object with the destination addresses as keys and amounts as values", - "createRawTransaction-amounts--key": "address", - "createRawTransaction-amounts--value": "n.nnn", - "createRawTransaction-amounts--desc": "The destination address as the key and the amount in KAS as the value", - "createRawTransaction-lockTime": "Locktime value; a non-zero value will also locktime-activate the inputs", - "createRawTransaction--result0": "Hex-encoded bytes of the serialized transaction", - - // ScriptSig help. - "scriptSig-asm": "Disassembly of the script", - "scriptSig-hex": "Hex-encoded bytes of the script", - - // PrevOut help. - "prevOut-address": "previous output address (if any)", - "prevOut-value": "previous output value", - - // VinPrevOut help. - "vinPrevOut-coinbase": "The hex-encoded bytes of the signature script (coinbase txns only)", - "vinPrevOut-txId": "The hash of the origin transaction (non-coinbase txns only)", - "vinPrevOut-vout": "The index of the output being redeemed from the origin transaction (non-coinbase txns only)", - "vinPrevOut-scriptSig": "The signature script used to redeem the origin transaction as a JSON object (non-coinbase txns only)", - "vinPrevOut-prevOut": "Data from the origin transaction output with index vout.", - "vinPrevOut-sequence": "The script sequence number", - - // Vin help. - "vin-coinbase": "The hex-encoded bytes of the signature script (coinbase txns only)", - "vin-txId": "The hash of the origin transaction (non-coinbase txns only)", - "vin-vout": "The index of the output being redeemed from the origin transaction (non-coinbase txns only)", - "vin-scriptSig": "The signature script used to redeem the origin transaction as a JSON object (non-coinbase txns only)", - "vin-sequence": "The script sequence number", - - // ScriptPubKeyResult help. - "scriptPubKeyResult-asm": "Disassembly of the script", - "scriptPubKeyResult-hex": "Hex-encoded bytes of the script", - "scriptPubKeyResult-type": "The type of the script (e.g. 'pubkeyhash')", - "scriptPubKeyResult-address": "The kaspa address (if any) associated with this script", - - // Vout help. - "vout-value": "The amount in KAS", - "vout-n": "The index of this transaction output", - "vout-scriptPubKey": "The public key script used to pay coins as a JSON object", - - // ChainBlock help. - "chainBlock-hash": "The hash of the chain block", - "chainBlock-acceptedBlocks": "The blocks accepted by this chain block", - - // AcceptedBlock help. - "acceptedBlock-hash": "The hash of the accepted block", - "acceptedBlock-acceptedTxIds": "The transactions in this block accepted by the chain block", - - // GetSelectedTipResult help. - "getSelectedTipResult-hash": "Hex-encoded bytes of the best block hash", - "getSelectedTipResult-height": "Height of the best block", - - // GetSelectedTipCmd help. - "getSelectedTip--synopsis": "Returns information about the selected tip of the blockDAG.", - "getSelectedTip-verbose": "Specifies the block is returned as a JSON object instead of hex-encoded string", - "getSelectedTip-verboseTx": "Specifies that each transaction is returned as a JSON object and only applies if the verbose flag is true", - "getSelectedTip--condition0": "verbose=false", - "getSelectedTip--condition1": "verbose=true", - "getSelectedTip-acceptedTx": "Specifies if the transaction got accepted", - "getSelectedTip--result0": "Hex-encoded bytes of the serialized block", - - // GetSelectedTipHashCmd help. - "getSelectedTipHash--synopsis": "Returns the hash of the of the selected tip of the blockDAG.", - "getSelectedTipHash--result0": "The hex-encoded block hash", - - // GetBlockCmd help. - "getBlock--synopsis": "Returns information about a block given its hash.", - "getBlock-hash": "The hash of the block", - "getBlock-verbose": "Specifies the block is returned as a JSON object instead of hex-encoded string", - "getBlock-verboseTx": "Specifies that each transaction is returned as a JSON object and only applies if the verbose flag is true", - "getBlock-subnetwork": "If passed, the returned block will be a partial block of the specified subnetwork", - "getBlock--condition0": "verbose=false", - "getBlock--condition1": "verbose=true", - "getBlock-acceptedTx": "Specifies if the transaction got accepted", - "getBlock--result0": "Hex-encoded bytes of the serialized block", - - // GetBlocksCmd help. - "getBlocks--synopsis": "Return the blocks starting from lowHash up to the virtual ordered by blue score.", - "getBlocks-includeRawBlockData": "If set to true - the raw block data would be also included.", - "getBlocks-includeVerboseBlockData": "If set to true - the verbose block data would also be included.", - "getBlocks-lowHash": "Hash of the block with the bottom blue score. If this hash is unknown - returns an error.", - "getBlocks--result0": "Blocks starting from lowHash. The result may contains up to 1000 blocks. For the remainder, call the command again with the bluest block's hash.", - - // GetChainFromBlockResult help. - "getBlocksResult-hashes": "List of hashes from StartHash (excluding StartHash) ordered by smallest blue score to greatest.", - "getBlocksResult-rawBlocks": "If includeBlocks=true - contains the block contents. Otherwise - omitted.", - "getBlocksResult-verboseBlocks": "If includeBlocks=true and verboseBlocks=true - each block is returned as a JSON object. Otherwise - hex encoded string.", - - // GetBlockChainInfoCmd help. - "getBlockDagInfo--synopsis": "Returns information about the current blockDAG state and the status of any active soft-fork deployments.", - - // GetBlockDAGInfoResult help. - "getBlockDagInfoResult-dag": "The name of the DAG the daemon is on (testnet, mainnet, etc)", - "getBlockDagInfoResult-blocks": "The number of blocks in the DAG", - "getBlockDagInfoResult-headers": "The number of headers that we've gathered for in the DAG", - "getBlockDagInfoResult-tipHashes": "The block hashes for the tips in the DAG", - "getBlockDagInfoResult-difficulty": "The current DAG difficulty", - "getBlockDagInfoResult-medianTime": "The median time from the PoV of the selected tip in the DAG", - "getBlockDagInfoResult-utxoCommitment": "Commitment to the dag's UTXOSet", - "getBlockDagInfoResult-verificationProgress": "An estimate for how much of the DAG we've verified", - "getBlockDagInfoResult-pruned": "A bool that indicates if the node is pruned or not", - "getBlockDagInfoResult-pruneHeight": "The lowest block retained in the current pruned DAG", - "getBlockDagInfoResult-dagWork": "The total cumulative work in the DAG", - "getBlockDagInfoResult-softForks": "The status of the super-majority soft-forks", - "getBlockDagInfoResult-bip9SoftForks": "JSON object describing active BIP0009 deployments", - "getBlockDagInfoResult-bip9SoftForks--key": "bip9_softforks", - "getBlockDagInfoResult-bip9SoftForks--value": "An object describing a particular BIP009 deployment", - "getBlockDagInfoResult-bip9SoftForks--desc": "The status of any defined BIP0009 soft-fork deployments", - - // SoftForkDescription help. - "softForkDescription-reject": "The current activation status of the softfork", - "softForkDescription-version": "The block version that signals enforcement of this softfork", - "softForkDescription-id": "The string identifier for the soft fork", - "-status": "A bool which indicates if the soft fork is active", - - // TxRawResult help. - "txRawResult-hex": "Hex-encoded transaction", - "txRawResult-txId": "The hash of the transaction", - "txRawResult-version": "The transaction version", - "txRawResult-lockTime": "The transaction lock time", - "txRawResult-subnetwork": "The transaction subnetwork", - "txRawResult-gas": "The transaction gas", - "txRawResult-mass": "The transaction mass", - "txRawResult-payloadHash": "The transaction payload hash", - "txRawResult-payload": "The transaction payload", - "txRawResult-vin": "The transaction inputs as JSON objects", - "txRawResult-vout": "The transaction outputs as JSON objects", - "txRawResult-blockHash": "Hash of the block the transaction is part of", - "txRawResult-isInMempool": "Whether the transaction is in the mempool", - "txRawResult-time": "Transaction time in seconds since 1 Jan 1970 GMT", - "txRawResult-blockTime": "Block time in seconds since the 1 Jan 1970 GMT", - "txRawResult-size": "The size of the transaction in bytes", - "txRawResult-hash": "The hash of the transaction", - "txRawResult-acceptedBy": "The block in which the transaction got accepted in", - - // GetBlockVerboseResult help. - "getBlockVerboseResult-hash": "The hash of the block (same as provided)", - "getBlockVerboseResult-confirmations": "The number of confirmations", - "getBlockVerboseResult-size": "The size of the block", - "getBlockVerboseResult-mass": "The mass of the block", - "getBlockVerboseResult-height": "The height of the block in the block DAG", - "getBlockVerboseResult-version": "The block version", - "getBlockVerboseResult-versionHex": "The block version in hexadecimal", - "getBlockVerboseResult-hashMerkleRoot": "Merkle tree reference to hash of all transactions for the block", - "getBlockVerboseResult-acceptedIdMerkleRoot": "Merkle tree reference to hash all transactions accepted form the block blues", - "getBlockVerboseResult-utxoCommitment": "An ECMH UTXO commitment of this block", - "getBlockVerboseResult-blueScore": "The block blue score", - "getBlockVerboseResult-isChainBlock": "Whether the block is in the selected parent chain", - "getBlockVerboseResult-tx": "The transaction hashes (only when verbosetx=false)", - "getBlockVerboseResult-rawRx": "The transactions as JSON objects (only when verbosetx=true)", - "getBlockVerboseResult-time": "The block time in seconds since 1 Jan 1970 GMT", - "getBlockVerboseResult-nonce": "The block nonce", - "getBlockVerboseResult-bits": "The bits which represent the block difficulty", - "getBlockVerboseResult-difficulty": "The proof-of-work difficulty as a multiple of the minimum difficulty", - "getBlockVerboseResult-parentHashes": "The hashes of the parent blocks", - "getBlockVerboseResult-selectedParentHash": "The selected parent hash", - "getBlockVerboseResult-childHashes": "The hashes of the child blocks (only if there are any)", - "getBlockVerboseResult-acceptedBlockHashes": "The hashes of the blocks accepted by this block", - - // GetBlockCountCmd help. - "getBlockCount--synopsis": "Returns the number of blocks in the block DAG.", - "getBlockCount--result0": "The current block count", - - // GetBlockHeaderCmd help. - "getBlockHeader--synopsis": "Returns information about a block header given its hash.", - "getBlockHeader-hash": "The hash of the block", - "getBlockHeader-verbose": "Specifies the block header is returned as a JSON object instead of hex-encoded string", - "getBlockHeader--condition0": "verbose=false", - "getBlockHeader--condition1": "verbose=true", - "getBlockHeader--result0": "The block header hash", - - // GetBlockHeaderVerboseResult help. - "getBlockHeaderVerboseResult-hash": "The hash of the block (same as provided)", - "getBlockHeaderVerboseResult-confirmations": "The number of confirmations", - "getBlockHeaderVerboseResult-height": "The height of the block in the block DAG", - "getBlockHeaderVerboseResult-version": "The block version", - "getBlockHeaderVerboseResult-versionHex": "The block version in hexadecimal", - "getBlockHeaderVerboseResult-hashMerkleRoot": "Merkle tree reference to hash of all transactions for the block", - "getBlockHeaderVerboseResult-acceptedIdMerkleRoot": "Merkle tree reference to hash all transactions accepted form the block blues", - "getBlockHeaderVerboseResult-time": "The block time in seconds since 1 Jan 1970 GMT", - "getBlockHeaderVerboseResult-nonce": "The block nonce", - "getBlockHeaderVerboseResult-bits": "The bits which represent the block difficulty", - "getBlockHeaderVerboseResult-difficulty": "The proof-of-work difficulty as a multiple of the minimum difficulty", - "getBlockHeaderVerboseResult-parentHashes": "The hashes of the parent blocks", - "getBlockHeaderVerboseResult-selectedParentHash": "The selected parent hash", - "getBlockHeaderVerboseResult-childHashes": "The hashes of the child blocks (only if there are any)", - - // TemplateRequest help. - "templateRequest-mode": "This is 'template', 'proposal', or omitted", - "templateRequest-payAddress": "The address the coinbase pays to", - "templateRequest-longPollId": "The long poll ID of a job to monitor for expiration; required and valid only for long poll requests ", - "templateRequest-sigOpLimit": "Number of signature operations allowed in blocks (this parameter is ignored)", - "templateRequest-massLimit": "Max transaction mass allowed in blocks (this parameter is ignored)", - "templateRequest-maxVersion": "Highest supported block version number (this parameter is ignored)", - "templateRequest-target": "The desired target for the block template (this parameter is ignored)", - "templateRequest-data": "Hex-encoded block data (only for mode=proposal)", - "templateRequest-workId": "The server provided workid if provided in block template (not applicable)", - - // GetBlockTemplateResultTx help. - "getBlockTemplateResultTx-data": "Hex-encoded transaction data (byte-for-byte)", - "getBlockTemplateResultTx-hash": "Hex-encoded transaction hash (little endian if treated as a 256-bit number)", - "getBlockTemplateResultTx-id": "Hex-encoded transaction ID (little endian if treated as a 256-bit number)", - "getBlockTemplateResultTx-depends": "Other transactions before this one (by 1-based index in the 'transactions' list) that must be present in the final block if this one is", - "getBlockTemplateResultTx-mass": "Total mass of all transactions in the block", - "getBlockTemplateResultTx-fee": "Difference in value between transaction inputs and outputs (in sompi)", - "getBlockTemplateResultTx-sigOps": "Total number of signature operations as counted for purposes of block limits", - - // GetBlockTemplateResultAux help. - "getBlockTemplateResultAux-flags": "Hex-encoded byte-for-byte data to include in the coinbase signature script", - - // GetBlockTemplateResult help. - "getBlockTemplateResult-bits": "Hex-encoded compressed difficulty", - "getBlockTemplateResult-curTime": "Current time as seen by the server (recommended for block time); must fall within mintime/maxtime rules", - "getBlockTemplateResult-height": "Height of the block to be solved", - "getBlockTemplateResult-parentHashes": "Hex-encoded big-endian hashes of the parent blocks", - "getBlockTemplateResult-sigOpLimit": "Number of sigops allowed in blocks", - "getBlockTemplateResult-massLimit": "Max transaction mass allowed in blocks", - "getBlockTemplateResult-transactions": "Array of transactions as JSON objects", - "getBlockTemplateResult-hashMerkleRoot": "The root of the merkle tree of all transaction IDs in this block", - "getBlockTemplateResult-acceptedIdMerkleRoot": "The root of the merkle tree of transaction IDs accepted by this block", - "getBlockTemplateResult-utxoCommitment": "An ECMH UTXO commitment of this block", - "getBlockTemplateResult-version": "The block version", - "getBlockTemplateResult-coinbaseAux": "Data that should be included in the coinbase signature script", - "getBlockTemplateResult-coinbaseTxn": "Information about the coinbase transaction", - "getBlockTemplateResult-coinbaseValue": "Total amount available for the coinbase in sompi", - "getBlockTemplateResult-workId": "This value must be returned with result if provided (not provided)", - "getBlockTemplateResult-longPollId": "Identifier for long poll request which allows monitoring for expiration", - "getBlockTemplateResult-longPollUri": "An alternate URI to use for long poll requests if provided (not provided)", - "getBlockTemplateResult-submitOld": "Not applicable", - "getBlockTemplateResult-target": "Hex-encoded big-endian number which valid results must be less than", - "getBlockTemplateResult-expires": "Maximum number of seconds (starting from when the server sent the response) this work is valid for", - "getBlockTemplateResult-maxTime": "Maximum allowed time", - "getBlockTemplateResult-minTime": "Minimum allowed time", - "getBlockTemplateResult-mutable": "List of mutations the server explicitly allows", - "getBlockTemplateResult-nonceRange": "Two concatenated hex-encoded big-endian 64-bit integers which represent the valid ranges of nonces the miner may scan", - "getBlockTemplateResult-capabilities": "List of server capabilities including 'proposal' to indicate support for block proposals", - "getBlockTemplateResult-rejectReason": "Reason the proposal was invalid as-is (only applies to proposal responses)", - "getBlockTemplateResult-isSynced": "Whether this node is synced with the rest of of the network. Miners are generally expected not to mine when isSynced is false", - - // GetBlockTemplateCmd help. - "getBlockTemplate--synopsis": "Returns a JSON object with information necessary to construct a block to mine or accepts a proposal to validate.\n" + - "See BIP0022 and BIP0023 for the full specification.", - "getBlockTemplate-request": "Request object which controls the mode and several parameters", - "getBlockTemplate--condition0": "mode=template", - "getBlockTemplate--condition1": "mode=proposal, rejected", - "getBlockTemplate--condition2": "mode=proposal, accepted", - "getBlockTemplate--result1": "An error string which represents why the proposal was rejected or nothing if accepted", - - // GetChainFromBlockCmd help. - "getChainFromBlock--synopsis": "Return the selected parent chain starting from startHash up to the virtual. If startHash is not in the selected parent chain, it goes down the DAG until it does reach a hash in the selected parent chain while collecting hashes into removedChainBlockHashes.", - "getChainFromBlock-startHash": "Hash of the bottom of the requested chain. If this hash is unknown or is not a chain block - returns an error.", - "getChainFromBlock-includeBlocks": "If set to true - the block contents would be also included.", - "getChainFromBlock--result0": "The selected parent chain. The result may contains up to 1000 blocks. For the remainder, call the command again with the bluest block's hash.", - - // GetChainFromBlockResult help. - "getChainFromBlockResult-removedChainBlockHashes": "List chain-block hashes that were removed from the selected parent chain in top-to-bottom order", - "getChainFromBlockResult-addedChainBlocks": "List of ChainBlocks from Virtual.SelectedTipHashAndBlueScore to StartHash (excluding StartHash) ordered bottom-to-top.", - "getChainFromBlockResult-blocks": "If includeBlocks=true - contains the contents of all chain and accepted blocks in the AddedChainBlocks. Otherwise - omitted.", - - // GetConnectionCountCmd help. - "getConnectionCount--synopsis": "Returns the number of active connections to other peers.", - "getConnectionCount--result0": "The number of connections", - - // GetCurrentNetCmd help. - "getCurrentNet--synopsis": "Get kaspa network the server is running on.", - "getCurrentNet--result0": "The network identifer", - - // GetDifficultyCmd help. - "getDifficulty--synopsis": "Returns the proof-of-work difficulty as a multiple of the minimum difficulty.", - "getDifficulty--result0": "The difficulty", - - // InfoDAGResult help. - "infoDagResult-version": "The version of the server", - "infoDagResult-protocolVersion": "The latest supported protocol version", - "infoDagResult-blocks": "The number of blocks processed", - "infoDagResult-timeOffset": "The time offset", - "infoDagResult-connections": "The number of connected peers", - "infoDagResult-proxy": "The proxy used by the server", - "infoDagResult-difficulty": "The current target difficulty", - "infoDagResult-testnet": "Whether or not server is using testnet", - "infoDagResult-devnet": "Whether or not server is using devnet", - "infoDagResult-relayFee": "The minimum relay fee for non-free transactions in KAS/KB", - "infoDagResult-errors": "Any current errors", - - // GetTopHeadersCmd help. - "getTopHeaders--synopsis": "Returns the top block headers starting with the provided high hash (not inclusive)", - "getTopHeaders-highHash": "Block hash to start including block headers from; if not found, it'll start from the virtual.", - "getTopHeaders--result0": "Serialized block headers of all located blocks, limited to some arbitrary maximum number of hashes (currently 2000, which matches the appmessage protocol headers message, but this is not guaranteed)", - - // GetHeadersCmd help. - "getHeaders--synopsis": "Returns block headers starting with the first known block hash from the request", - "getHeaders-lowHash": "Block hash to start including headers from; if not found, it'll start from the genesis block.", - "getHeaders-highHash": "Block hash to stop including block headers for; if not found, all headers to the latest known block are returned.", - "getHeaders--result0": "Serialized block headers of all located blocks, limited to some arbitrary maximum number of hashes (currently 2000, which matches the appmessage protocol headers message, but this is not guaranteed)", - - // GetInfoCmd help. - "getInfo--synopsis": "Returns a JSON object containing various state info.", - - // getMempoolEntry help. - "getMempoolEntry--synopsis": "Returns mempool data for given transaction", - "getMempoolEntry-txId": "The transaction ID", - - // getMempoolEntryResult help. - "getMempoolEntryResult-fee": "Transaction fee in sompis", - "getMempoolEntryResult-time": "Local time transaction entered pool in seconds since 1 Jan 1970 GMT", - "getMempoolEntryResult-rawTx": "The transaction as a JSON object", - - // GetMempoolInfoCmd help. - "getMempoolInfo--synopsis": "Returns memory pool information", - - // GetMempoolInfoResult help. - "getMempoolInfoResult-bytes": "Size in bytes of the mempool", - "getMempoolInfoResult-size": "Number of transactions in the mempool", - - // GetNetTotalsCmd help. - "getNetTotals--synopsis": "Returns a JSON object containing network traffic statistics.", - - // GetNetTotalsResult help. - "getNetTotalsResult-totalBytesRecv": "Total bytes received", - "getNetTotalsResult-totalBytesSent": "Total bytes sent", - "getNetTotalsResult-timeMillis": "Number of milliseconds since 1 Jan 1970 GMT", - - // GetConnectedPeerInfoResult help. - "getConnectedPeerInfoResult-id": "A unique node ID", - "getConnectedPeerInfoResult-address": "The ip address and port of the peer", - "getConnectedPeerInfoResult-selectedTipHash": "The hash of the selected tip of the peer", - "getConnectedPeerInfoResult-lastPingDuration": "The duration of the last ping to the peer in milliseconds", - "getConnectedPeerInfoResult-isSyncNode": "Whether or not the peer is the sync peer", - "getConnectedPeerInfoResult-isOutbound": "Whether the peer is inbound or outbound", - "getConnectedPeerInfoResult-timeOffset": "The time difference between this node and the peer", - "getConnectedPeerInfoResult-userAgent": "The user agent of the peer", - "getConnectedPeerInfoResult-advertisedProtocolVersion": "The advertised p2p protocol version of the peer", - "getConnectedPeerInfoResult-timeConnected": "The timestamp of when the peer connected to this node", - - // GetConnectedPeerInfoCmd help. - "getConnectedPeerInfo--synopsis": "Returns data about each connected network peer as an array of json objects.", - - // GetPeerAddressesResult help. - "getPeerAddressesResult-version": "Peers state serialization version", - "getPeerAddressesResult-key": "Address manager's key for randomness purposes.", - "getPeerAddressesResult-addresses": "The node's known addresses", - "getPeerAddressesResult-newBuckets": "Peers state subnetwork new buckets", - "getPeerAddressesResult-newBuckets--desc": "New buckets keyed by subnetwork ID", - "getPeerAddressesResult-newBuckets--key": "subnetworkId", - "getPeerAddressesResult-newBuckets--value": "New bucket", - "getPeerAddressesResult-newBucketFullNodes": "Peers state full nodes new bucket", - "getPeerAddressesResult-triedBuckets": "Peers state subnetwork tried buckets", - "getPeerAddressesResult-triedBuckets--desc": "Tried buckets keyed by subnetwork ID", - "getPeerAddressesResult-triedBuckets--key": "subnetworkId", - "getPeerAddressesResult-triedBuckets--value": "Tried bucket", - "getPeerAddressesResult-triedBucketFullNodes": "Peers state tried full nodes bucket", - - "getPeerAddressesKnownAddressResult-addr": "Address", - "getPeerAddressesKnownAddressResult-src": "Address of the peer that handed the address", - "getPeerAddressesKnownAddressResult-subnetworkId": "Address subnetwork ID", - "getPeerAddressesKnownAddressResult-attempts": "Number of attempts to connect to the address", - "getPeerAddressesKnownAddressResult-timeStamp": "Time the address was added", - "getPeerAddressesKnownAddressResult-lastAttempt": "Last attempt to connect to the address", - "getPeerAddressesKnownAddressResult-lastSuccess": "Last successful attempt to connect to the address", - "getPeerAddressesKnownAddressResult-isBanned": "Whether the address was banned", - "getPeerAddressesKnownAddressResult-bannedTime": "Time the address was banned", - - // GetPeerAddressesCmd help. - "getPeerAddresses--synopsis": "Returns the peers state.", - - // GetRawMempoolVerboseResult help. - "getRawMempoolVerboseResult-size": "Transaction size in bytes", - "getRawMempoolVerboseResult-fee": "Transaction fee in kaspa", - "getRawMempoolVerboseResult-time": "Local time transaction entered pool in seconds since 1 Jan 1970 GMT", - "getRawMempoolVerboseResult-height": "Block height when transaction entered the pool", - "getRawMempoolVerboseResult-startingPriority": "Priority when transaction entered the pool", - "getRawMempoolVerboseResult-currentPriority": "Current priority", - "getRawMempoolVerboseResult-depends": "Unconfirmed transactions used as inputs for this transaction", - - // GetRawMempoolCmd help. - "getRawMempool--synopsis": "Returns information about all of the transactions currently in the memory pool.", - "getRawMempool-verbose": "Returns JSON object when true or an array of transaction hashes when false", - "getRawMempool--condition0": "verbose=false", - "getRawMempool--condition1": "verbose=true", - "getRawMempool--result0": "Array of transaction hashes", - - // GetSubnetworkCmd help. - "getSubnetwork--synopsis": "Returns information about a subnetwork given its ID.", - "getSubnetwork-subnetworkId": "The ID of the subnetwork", - - // GetSubnetworkResult help. - "getSubnetworkResult-gasLimit": "The gas limit of the subnetwork", - - // GetTxOutResult help. - "getTxOutResult-selectedTip": "The block hash that contains the transaction output", - "getTxOutResult-confirmations": "The number of confirmations", - "getTxOutResult-isInMempool": "Whether the transaction is in the mempool", - "getTxOutResult-value": "The transaction amount in KAS", - "getTxOutResult-scriptPubKey": "The public key script used to pay coins as a JSON object", - "getTxOutResult-version": "The transaction version", - "getTxOutResult-coinbase": "Whether or not the transaction is a coinbase", - - // GetTxOutCmd help. - "getTxOut--synopsis": "Returns information about an unspent transaction output..", - "getTxOut-txId": "The hash of the transaction", - "getTxOut-vout": "The index of the output", - "getTxOut-includeMempool": "Include the mempool when true", - - // HelpCmd help. - "help--synopsis": "Returns a list of all commands or help for a specified command.", - "help-command": "The command to retrieve help for", - "help--condition0": "no command provided", - "help--condition1": "command specified", - "help--result0": "List of commands", - "help--result1": "Help for specified command", - - // PingCmd help. - "ping--synopsis": "Queues a ping to be sent to each connected peer.\n" + - "Ping times are provided by getConnectedPeerInfo via the pingtime and pingwait fields.", - - // DisconnectCmd help. - "disconnect--synopsis": "Disconnects a peer", - "disconnect-address": "IP address and port of the peer to disconnect", - - // SendRawTransactionCmd help. - "sendRawTransaction--synopsis": "Submits the serialized, hex-encoded transaction to the local peer and relays it to the network.", - "sendRawTransaction-hexTx": "Serialized, hex-encoded signed transaction", - "sendRawTransaction-allowHighFees": "Whether or not to allow insanely high fees (kaspad does not yet implement this parameter, so it has no effect)", - "sendRawTransaction--result0": "The hash of the transaction", - - // StopCmd help. - "stop--synopsis": "Shutdown kaspad.", - "stop--result0": "The string 'kaspad stopping.'", - - // SubmitBlockOptions help. - "submitBlockOptions-workId": "This parameter is currently ignored", - - // SubmitBlockCmd help. - "submitBlock--synopsis": "Attempts to submit a new serialized, hex-encoded block to the network.", - "submitBlock-hexBlock": "Serialized, hex-encoded block", - "submitBlock-options": "This parameter is currently ignored", - "submitBlock--condition0": "Block successfully submitted", - "submitBlock--condition1": "Block rejected", - "submitBlock--result1": "The reason the block was rejected", - - // ValidateAddressResult help. - "validateAddressResult-isValid": "Whether or not the address is valid", - "validateAddressResult-address": "The kaspa address (only when isvalid is true)", - - // ValidateAddressCmd help. - "validateAddress--synopsis": "Verify an address is valid.", - "validateAddress-address": "Kaspa address to validate", - - // -------- Websocket-specific help -------- - - // Session help. - "session--synopsis": "Return details regarding a websocket client's current connection session.", - "sessionResult-sessionId": "The unique session ID for a client's websocket connection.", - - // NotifyBlocksCmd help. - "notifyBlocks--synopsis": "Request notifications for whenever a block is connected to the DAG.", - - // StopNotifyBlocksCmd help. - "stopNotifyBlocks--synopsis": "Cancel registered notifications for whenever a block is connected to the DAG.", - - // NotifyChainChangesCmd help. - "notifyChainChanges--synopsis": "Request notifications for whenever the selected parent chain changes.", - - // StopNotifyChainChangesCmd help. - "stopNotifyChainChanges--synopsis": "Cancel registered notifications for whenever the selected parent chain changes.", - - // NotifyNewTransactionsCmd help. - "notifyNewTransactions--synopsis": "Send either a txaccepted or a txacceptedverbose notification when a new transaction is accepted into the mempool.", - "notifyNewTransactions-verbose": "Specifies which type of notification to receive. If verbose is true, then the caller receives txacceptedverbose, otherwise the caller receives txaccepted", - "notifyNewTransactions-subnetwork": "Specifies which subnetwork to receive full transactions of. Requires verbose=true. Not allowed when node subnetwork is Native. Must be equal to node subnetwork when node is partial.", - - // StopNotifyNewTransactionsCmd help. - "stopNotifyNewTransactions--synopsis": "Stop sending either a txaccepted or a txacceptedverbose notification when a new transaction is accepted into the mempool.", - - // Outpoint help. - "outpoint-txid": "The hex-encoded bytes of the outpoint transaction ID", - "outpoint-index": "The index of the outpoint", - - // LoadTxFilterCmd help. - "loadTxFilter--synopsis": "Load, add to, or reload a websocket client's transaction filter for mempool transactions and new blocks.", - "loadTxFilter-reload": "Load a new filter instead of adding data to an existing one", - "loadTxFilter-addresses": "Array of addresses to add to the transaction filter", - "loadTxFilter-outpoints": "Array of outpoints to add to the transaction filter", - - // Uptime help. - "uptime--synopsis": "Returns the total uptime of the server.", - "uptime--result0": "The number of seconds that the server has been running", - - // Version help. - "version--synopsis": "Returns the JSON-RPC API version (semver)", - "version--result0--desc": "Version objects keyed by the program or API name", - "version--result0--key": "Program or API name", - "version--result0--value": "Object containing the semantic version", - - // VersionResult help. - "versionResult-versionString": "The JSON-RPC API version (semver)", - "versionResult-major": "The major component of the JSON-RPC API version", - "versionResult-minor": "The minor component of the JSON-RPC API version", - "versionResult-patch": "The patch component of the JSON-RPC API version", - "versionResult-prerelease": "Prerelease info about the current build", - "versionResult-buildMetadata": "Metadata about the current build", -} - -// rpcResultTypes specifies the result types that each RPC command can return. -// This information is used to generate the help. Each result type must be a -// pointer to the type (or nil to indicate no return value). -var rpcResultTypes = map[string][]interface{}{ - "connect": nil, - "debugLevel": {(*string)(nil), (*string)(nil)}, - "getSelectedTip": {(*model.GetBlockVerboseResult)(nil)}, - "getSelectedTipHash": {(*string)(nil)}, - "getBlock": {(*string)(nil), (*model.GetBlockVerboseResult)(nil)}, - "getBlocks": {(*model.GetBlocksResult)(nil)}, - "getBlockCount": {(*int64)(nil)}, - "getBlockHeader": {(*string)(nil), (*model.GetBlockHeaderVerboseResult)(nil)}, - "getBlockTemplate": {(*model.GetBlockTemplateResult)(nil), (*string)(nil), nil}, - "getBlockDagInfo": {(*model.GetBlockDAGInfoResult)(nil)}, - "getChainFromBlock": {(*model.GetChainFromBlockResult)(nil)}, - "getConnectionCount": {(*int32)(nil)}, - "getCurrentNet": {(*uint32)(nil)}, - "getDifficulty": {(*float64)(nil)}, - "getTopHeaders": {(*[]string)(nil)}, - "getHeaders": {(*[]string)(nil)}, - "getInfo": {(*model.InfoDAGResult)(nil)}, - "getMempoolInfo": {(*model.GetMempoolInfoResult)(nil)}, - "getMempoolEntry": {(*model.GetMempoolEntryResult)(nil)}, - "getNetTotals": {(*model.GetNetTotalsResult)(nil)}, - "getConnectedPeerInfo": {(*[]model.GetConnectedPeerInfoResult)(nil)}, - "getPeerAddresses": {(*[]model.GetPeerAddressesResult)(nil)}, - "getRawMempool": {(*[]string)(nil), (*model.GetRawMempoolVerboseResult)(nil)}, - "getSubnetwork": {(*model.GetSubnetworkResult)(nil)}, - "getTxOut": {(*model.GetTxOutResult)(nil)}, - "node": nil, - "help": {(*string)(nil), (*string)(nil)}, - "ping": nil, - "disconnect": nil, - "sendRawTransaction": {(*string)(nil)}, - "stop": {(*string)(nil)}, - "submitBlock": {nil, (*string)(nil)}, - "uptime": {(*int64)(nil)}, - "validateAddress": {(*model.ValidateAddressResult)(nil)}, - "version": {(*map[string]model.VersionResult)(nil)}, - - // Websocket commands. - "loadTxFilter": nil, - "session": {(*model.SessionResult)(nil)}, - "notifyBlocks": nil, - "stopNotifyBlocks": nil, - "notifyChainChanges": nil, - "stopNotifyChainChanges": nil, - "notifyNewTransactions": nil, - "stopNotifyNewTransactions": nil, -} - -// helpCacher provides a concurrent safe type that provides help and usage for -// the RPC server commands and caches the results for future calls. -type helpCacher struct { - sync.Mutex - usage string - methodHelp map[string]string -} - -// rpcMethodHelp returns an RPC help string for the provided method. -// -// This function is safe for concurrent access. -func (c *helpCacher) rpcMethodHelp(method string) (string, error) { - c.Lock() - defer c.Unlock() - - // Return the cached method help if it exists. - if help, exists := c.methodHelp[method]; exists { - return help, nil - } - - // Look up the result types for the method. - resultTypes, ok := rpcResultTypes[method] - if !ok { - return "", errors.New("no result types specified for method " + - method) - } - - // Generate, cache, and return the help. - help, err := model.GenerateHelp(method, helpDescsEnUS, resultTypes...) - if err != nil { - return "", err - } - c.methodHelp[method] = help - return help, nil -} - -// rpcUsage returns one-line usage for all support RPC commands. -// -// This function is safe for concurrent access. -func (c *helpCacher) rpcUsage(includeWebsockets bool) (string, error) { - c.Lock() - defer c.Unlock() - - // Return the cached usage if it is available. - if c.usage != "" { - return c.usage, nil - } - - // Generate a list of one-line usage for every command. - usageTexts := make([]string, 0, len(rpcHandlers)) - for k := range rpcHandlers { - usage, err := model.MethodUsageText(k) - if err != nil { - return "", err - } - usageTexts = append(usageTexts, usage) - } - - // Include websockets commands if requested. - if includeWebsockets { - for k := range wsHandlers { - usage, err := model.MethodUsageText(k) - if err != nil { - return "", err - } - usageTexts = append(usageTexts, usage) - } - } - - sort.Strings(usageTexts) - c.usage = strings.Join(usageTexts, "\n") - return c.usage, nil -} - -// newHelpCacher returns a new instance of a help cacher which provides help and -// usage for the RPC server commands and caches the results for future calls. -func newHelpCacher() *helpCacher { - return &helpCacher{ - methodHelp: make(map[string]string), - } -} diff --git a/infrastructure/network/rpc/rpcserverhelp_test.go b/infrastructure/network/rpc/rpcserverhelp_test.go deleted file mode 100644 index 00f83d723..000000000 --- a/infrastructure/network/rpc/rpcserverhelp_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2015 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package rpc - -import ( - "testing" -) - -// TestHelp ensures the help is reasonably accurate by checking that every -// command specified also has result types defined and the one-line usage and -// help text can be generated for them. -func TestHelp(t *testing.T) { - // Ensure there are result types specified for every handler. - for k := range rpcHandlers { - if _, ok := rpcResultTypes[k]; !ok { - t.Errorf("RPC handler defined for method '%v' without "+ - "also specifying result types", k) - continue - } - - } - for k := range wsHandlers { - if _, ok := rpcResultTypes[k]; !ok { - t.Errorf("RPC handler defined for method '%v' without "+ - "also specifying result types", k) - continue - } - - } - - // Ensure the usage for every command can be generated without errors. - helpCacher := newHelpCacher() - if _, err := helpCacher.rpcUsage(true); err != nil { - t.Errorf("Failed to generate one-line usage: %v", err) - } - if _, err := helpCacher.rpcUsage(true); err != nil { - t.Errorf("Failed to generate one-line usage (cached): %v", err) - } - - // Ensure the help for every command can be generated without errors. - for k := range rpcHandlers { - if _, err := helpCacher.rpcMethodHelp(k); err != nil { - t.Errorf("Failed to generate help for method '%v': %v", - k, err) - continue - } - if _, err := helpCacher.rpcMethodHelp(k); err != nil { - t.Errorf("Failed to generate help for method '%v'"+ - "(cached): %v", k, err) - continue - } - } - for k := range wsHandlers { - if _, err := helpCacher.rpcMethodHelp(k); err != nil { - t.Errorf("Failed to generate help for method '%v': %v", - k, err) - continue - } - if _, err := helpCacher.rpcMethodHelp(k); err != nil { - t.Errorf("Failed to generate help for method '%v'"+ - "(cached): %v", k, err) - continue - } - } -} diff --git a/infrastructure/network/rpc/rpcwebsocket.go b/infrastructure/network/rpc/rpcwebsocket.go deleted file mode 100644 index 8678992d4..000000000 --- a/infrastructure/network/rpc/rpcwebsocket.go +++ /dev/null @@ -1,1335 +0,0 @@ -// Copyright (c) 2013-2017 The btcsuite developers -// Copyright (c) 2015-2017 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package rpc - -import ( - "bytes" - "container/list" - "crypto/sha256" - "crypto/subtle" - "encoding/base64" - "encoding/hex" - "encoding/json" - "io" - "sync" - "time" - - "github.com/pkg/errors" - - "github.com/kaspanet/kaspad/util/random" - "github.com/kaspanet/kaspad/util/subnetworkid" - - "golang.org/x/crypto/ripemd160" - - "github.com/btcsuite/websocket" - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/domain/dagconfig" - "github.com/kaspanet/kaspad/domain/txscript" - "github.com/kaspanet/kaspad/infrastructure/network/rpc/model" - "github.com/kaspanet/kaspad/util" - "github.com/kaspanet/kaspad/util/daghash" -) - -const ( - // websocketSendBufferSize is the number of elements the send channel - // can queue before blocking. Note that this only applies to requests - // handled directly in the websocket client input handler or the async - // handler since notifications have their own queuing mechanism - // independent of the send channel buffer. - websocketSendBufferSize = 50 -) - -type semaphore chan struct{} - -func makeSemaphore(n int) semaphore { - return make(chan struct{}, n) -} - -func (s semaphore) acquire() { s <- struct{}{} } -func (s semaphore) release() { <-s } - -// timeZeroVal is simply the zero value for a time.Time and is used to avoid -// creating multiple instances. -var timeZeroVal time.Time - -// wsCommandHandler describes a callback function used to handle a specific -// command. -type wsCommandHandler func(*wsClient, interface{}) (interface{}, error) - -// wsHandlers maps RPC command strings to appropriate websocket handler -// functions. This is set by init because help references wsHandlers and thus -// causes a dependency loop. -var wsHandlers map[string]wsCommandHandler -var wsHandlersBeforeInit = map[string]wsCommandHandler{ - "loadTxFilter": handleLoadTxFilter, - "help": handleWebsocketHelp, - "notifyBlocks": handleNotifyBlocks, - "notifyChainChanges": handleNotifyChainChanges, - "notifyNewTransactions": handleNotifyNewTransactions, - "session": handleSession, - "stopNotifyBlocks": handleStopNotifyBlocks, - "stopNotifyChainChanges": handleStopNotifyChainChanges, - "stopNotifyNewTransactions": handleStopNotifyNewTransactions, -} - -// WebsocketHandler handles a new websocket client by creating a new wsClient, -// starting it, and blocking until the connection closes. Since it blocks, it -// must be run in a separate goroutine. It should be invoked from the websocket -// server handler which runs each new connection in a new goroutine thereby -// satisfying the requirement. -func (s *Server) WebsocketHandler(conn *websocket.Conn, remoteAddr string, - authenticated bool, isAdmin bool) { - - // Clear the read deadline that was set before the websocket hijacked - // the connection. - conn.SetReadDeadline(timeZeroVal) - - // Limit max number of websocket clients. - log.Infof("New websocket client %s", remoteAddr) - if s.ntfnMgr.NumClients()+1 > s.cfg.RPCMaxWebsockets { - log.Infof("Max websocket clients exceeded [%d] - "+ - "disconnecting client %s", s.cfg.RPCMaxWebsockets, - remoteAddr) - conn.Close() - return - } - - // Create a new websocket client to handle the new websocket connection - // and wait for it to shutdown. Once it has shutdown (and hence - // disconnected), remove it and any notifications it registered for. - client, err := newWebsocketClient(s, conn, remoteAddr, authenticated, isAdmin) - if err != nil { - log.Errorf("Failed to serve client %s: %s", remoteAddr, err) - conn.Close() - return - } - s.ntfnMgr.AddClient(client) - client.Start() - client.WaitForShutdown() - s.ntfnMgr.RemoveClient(client) - log.Infof("Disconnected websocket client %s", remoteAddr) -} - -// wsNotificationManager is a connection and notification manager used for -// websockets. It allows websocket clients to register for notifications they -// are interested in. When an event happens elsewhere in the code such as -// transactions being added to the memory pool or block connects/disconnects, -// the notification manager is provided with the relevant details needed to -// figure out which websocket clients need to be notified based on what they -// have registered for and notifies them accordingly. It is also used to keep -// track of all connected websocket clients. -type wsNotificationManager struct { - // server is the RPC server the notification manager is associated with. - server *Server - - // queueNotification queues a notification for handling. - queueNotification chan interface{} - - // notificationMsgs feeds notificationHandler with notifications - // and client (un)registeration requests from a queue as well as - // registeration and unregisteration requests from clients. - notificationMsgs chan interface{} - - // Access channel for current number of connected clients. - numClients chan int - - // Shutdown handling - wg sync.WaitGroup - quit chan struct{} -} - -// queueHandler manages a queue of empty interfaces, reading from in and -// sending the oldest unsent to out. This handler stops when either of the -// in or quit channels are closed, and closes out before returning, without -// waiting to send any variables still remaining in the queue. -func queueHandler(in <-chan interface{}, out chan<- interface{}, quit <-chan struct{}) { - var q []interface{} - var dequeue chan<- interface{} - skipQueue := out - var next interface{} -out: - for { - select { - case n, ok := <-in: - if !ok { - // Sender closed input channel. - break out - } - - // Either send to out immediately if skipQueue is - // non-nil (queue is empty) and reader is ready, - // or append to the queue and send later. - select { - case skipQueue <- n: - default: - q = append(q, n) - dequeue = out - skipQueue = nil - next = q[0] - } - - case dequeue <- next: - copy(q, q[1:]) - q[len(q)-1] = nil // avoid leak - q = q[:len(q)-1] - if len(q) == 0 { - dequeue = nil - skipQueue = out - } else { - next = q[0] - } - - case <-quit: - break out - } - } - close(out) -} - -// queueHandler maintains a queue of notifications and notification handler -// control messages. -func (m *wsNotificationManager) queueHandler() { - queueHandler(m.queueNotification, m.notificationMsgs, m.quit) - m.wg.Done() -} - -// NotifyBlockAdded passes a block newly-added to the blockDAG -// to the notification manager for block and transaction notification -// processing. -func (m *wsNotificationManager) NotifyBlockAdded(block *util.Block) { - // As NotifyBlockAdded will be called by the block manager - // and the RPC server may no longer be running, use a select - // statement to unblock enqueuing the notification once the RPC - // server has begun shutting down. - select { - case m.queueNotification <- (*notificationBlockAdded)(block): - case <-m.quit: - } -} - -// NotifyChainChanged passes changes to the selected parent chain of -// the blockDAG to the notification manager for processing. -func (m *wsNotificationManager) NotifyChainChanged(removedChainBlockHashes []*daghash.Hash, - addedChainBlockHashes []*daghash.Hash) { - n := ¬ificationChainChanged{ - removedChainBlockHashes: removedChainBlockHashes, - addedChainBlocksHashes: addedChainBlockHashes, - } - // As NotifyChainChanged will be called by the DAG manager - // and the RPC server may no longer be running, use a select - // statement to unblock enqueuing the notification once the RPC - // server has begun shutting down. - select { - case m.queueNotification <- n: - case <-m.quit: - } -} - -// NotifyMempoolTx passes a transaction accepted by mempool to the -// notification manager for transaction notification processing. If -// isNew is true, the tx is is a new transaction, rather than one -// added to the mempool during a reorg. -func (m *wsNotificationManager) NotifyMempoolTx(tx *util.Tx, isNew bool) { - n := ¬ificationTxAcceptedByMempool{ - isNew: isNew, - tx: tx, - } - - // As NotifyMempoolTx will be called by mempool and the RPC server - // may no longer be running, use a select statement to unblock - // enqueuing the notification once the RPC server has begun - // shutting down. - select { - case m.queueNotification <- n: - case <-m.quit: - } -} - -// wsClientFilter tracks relevant addresses for each websocket client for -// the `rescanBlocks` extension. It is modified by the `loadTxFilter` command. -// -// NOTE: This extension was ported from github.com/decred/dcrd -type wsClientFilter struct { - mu sync.Mutex - - // Implemented fast paths for address lookup. - pubKeyHashes map[[ripemd160.Size]byte]struct{} - scriptHashes map[[ripemd160.Size]byte]struct{} - compressedPubKeys map[[33]byte]struct{} - uncompressedPubKeys map[[65]byte]struct{} - - // A fallback address lookup map in case a fast path doesn't exist. - // Only exists for completeness. If using this shows up in a profile, - // there's a good chance a fast path should be added. - otherAddresses map[string]struct{} - - // Outpoints of unspent outputs. - unspent map[appmessage.Outpoint]struct{} -} - -// newWSClientFilter creates a new, empty wsClientFilter struct to be used -// for a websocket client. -// -// NOTE: This extension was ported from github.com/decred/dcrd -func newWSClientFilter(addresses []string, unspentOutpoints []appmessage.Outpoint, params *dagconfig.Params) *wsClientFilter { - filter := &wsClientFilter{ - pubKeyHashes: map[[ripemd160.Size]byte]struct{}{}, - scriptHashes: map[[ripemd160.Size]byte]struct{}{}, - compressedPubKeys: map[[33]byte]struct{}{}, - uncompressedPubKeys: map[[65]byte]struct{}{}, - otherAddresses: map[string]struct{}{}, - unspent: make(map[appmessage.Outpoint]struct{}, len(unspentOutpoints)), - } - - for _, s := range addresses { - filter.addAddressStr(s, params) - } - for i := range unspentOutpoints { - filter.addUnspentOutpoint(&unspentOutpoints[i]) - } - - return filter -} - -// addAddress adds an address to a wsClientFilter, treating it correctly based -// on the type of address passed as an argument. -// -// NOTE: This extension was ported from github.com/decred/dcrd -func (f *wsClientFilter) addAddress(a util.Address) { - switch a := a.(type) { - case *util.AddressPubKeyHash: - f.pubKeyHashes[*a.Hash160()] = struct{}{} - return - case *util.AddressScriptHash: - f.scriptHashes[*a.Hash160()] = struct{}{} - return - } - - f.otherAddresses[a.EncodeAddress()] = struct{}{} -} - -// addAddressStr parses an address from a string and then adds it to the -// wsClientFilter using addAddress. -// -// NOTE: This extension was ported from github.com/decred/dcrd -func (f *wsClientFilter) addAddressStr(s string, params *dagconfig.Params) { - // If address can't be decoded, no point in saving it since it should also - // impossible to create the address from an inspected transaction output - // script. - a, err := util.DecodeAddress(s, params.Prefix) - if err != nil { - return - } - f.addAddress(a) -} - -// existsAddress returns true if the passed address has been added to the -// wsClientFilter. -// -// NOTE: This extension was ported from github.com/decred/dcrd -func (f *wsClientFilter) existsAddress(a util.Address) bool { - switch a := a.(type) { - case *util.AddressPubKeyHash: - _, ok := f.pubKeyHashes[*a.Hash160()] - return ok - case *util.AddressScriptHash: - _, ok := f.scriptHashes[*a.Hash160()] - return ok - } - - _, ok := f.otherAddresses[a.EncodeAddress()] - return ok -} - -// addUnspentOutpoint adds an outpoint to the wsClientFilter. -// -// NOTE: This extension was ported from github.com/decred/dcrd -func (f *wsClientFilter) addUnspentOutpoint(op *appmessage.Outpoint) { - f.unspent[*op] = struct{}{} -} - -// existsUnspentOutpointNoLock returns true if the passed outpoint has been added to -// the wsClientFilter. -// -// NOTE: This extension was ported from github.com/decred/dcrd -func (f *wsClientFilter) existsUnspentOutpointNoLock(op *appmessage.Outpoint) bool { - _, ok := f.unspent[*op] - return ok -} - -func (f *wsClientFilter) existsUnspentOutpoint(op *appmessage.Outpoint) bool { - f.mu.Lock() - defer f.mu.Unlock() - return f.existsUnspentOutpointNoLock(op) -} - -// Notification types -type notificationBlockAdded util.Block -type notificationChainChanged struct { - removedChainBlockHashes []*daghash.Hash - addedChainBlocksHashes []*daghash.Hash -} -type notificationTxAcceptedByMempool struct { - isNew bool - tx *util.Tx -} - -// Notification control requests -type notificationRegisterClient wsClient -type notificationUnregisterClient wsClient -type notificationRegisterBlocks wsClient -type notificationUnregisterBlocks wsClient -type notificationRegisterChainChanges wsClient -type notificationUnregisterChainChanges wsClient -type notificationRegisterNewMempoolTxs wsClient -type notificationUnregisterNewMempoolTxs wsClient - -// notificationHandler reads notifications and control messages from the queue -// handler and processes one at a time. -func (m *wsNotificationManager) notificationHandler() { - // clients is a map of all currently connected websocket clients. - clients := make(map[chan struct{}]*wsClient) - - // Maps used to hold lists of websocket clients to be notified on - // certain events. Each websocket client also keeps maps for the events - // which have multiple triggers to make removal from these lists on - // connection close less horrendously expensive. - // - // Where possible, the quit channel is used as the unique id for a client - // since it is quite a bit more efficient than using the entire struct. - blockNotifications := make(map[chan struct{}]*wsClient) - chainChangeNotifications := make(map[chan struct{}]*wsClient) - txNotifications := make(map[chan struct{}]*wsClient) - -out: - for { - select { - case n, ok := <-m.notificationMsgs: - if !ok { - // queueHandler quit. - break out - } - switch n := n.(type) { - case *notificationBlockAdded: - block := (*util.Block)(n) - - if len(blockNotifications) != 0 { - m.notifyFilteredBlockAdded(blockNotifications, - block) - } - - case *notificationChainChanged: - m.notifyChainChanged(chainChangeNotifications, - n.removedChainBlockHashes, n.addedChainBlocksHashes) - - case *notificationTxAcceptedByMempool: - if n.isNew && len(txNotifications) != 0 { - m.notifyForNewTx(txNotifications, n.tx) - } - m.notifyRelevantTxAccepted(n.tx, clients) - - case *notificationRegisterBlocks: - wsc := (*wsClient)(n) - blockNotifications[wsc.quit] = wsc - - case *notificationUnregisterBlocks: - wsc := (*wsClient)(n) - delete(blockNotifications, wsc.quit) - - case *notificationRegisterChainChanges: - wsc := (*wsClient)(n) - chainChangeNotifications[wsc.quit] = wsc - - case *notificationUnregisterChainChanges: - wsc := (*wsClient)(n) - delete(chainChangeNotifications, wsc.quit) - - case *notificationRegisterClient: - wsc := (*wsClient)(n) - clients[wsc.quit] = wsc - - case *notificationUnregisterClient: - wsc := (*wsClient)(n) - // Remove any requests made by the client as well as - // the client itself. - delete(blockNotifications, wsc.quit) - delete(chainChangeNotifications, wsc.quit) - delete(txNotifications, wsc.quit) - delete(clients, wsc.quit) - - case *notificationRegisterNewMempoolTxs: - wsc := (*wsClient)(n) - txNotifications[wsc.quit] = wsc - - case *notificationUnregisterNewMempoolTxs: - wsc := (*wsClient)(n) - delete(txNotifications, wsc.quit) - - default: - log.Warn("Unhandled notification type") - } - - case m.numClients <- len(clients): - - case <-m.quit: - // RPC server shutting down. - break out - } - } - - for _, c := range clients { - c.Disconnect() - } - m.wg.Done() -} - -// NumClients returns the number of clients actively being served. -func (m *wsNotificationManager) NumClients() (n int) { - select { - case n = <-m.numClients: - case <-m.quit: // Use default n (0) if server has shut down. - } - return -} - -// RegisterBlockUpdates requests block update notifications to the passed -// websocket client. -func (m *wsNotificationManager) RegisterBlockUpdates(wsc *wsClient) { - m.queueNotification <- (*notificationRegisterBlocks)(wsc) -} - -// UnregisterBlockUpdates removes block update notifications for the passed -// websocket client. -func (m *wsNotificationManager) UnregisterBlockUpdates(wsc *wsClient) { - m.queueNotification <- (*notificationUnregisterBlocks)(wsc) -} - -// RegisterChainChanges requests chain change notifications to the passed -// websocket client. -func (m *wsNotificationManager) RegisterChainChanges(wsc *wsClient) { - m.queueNotification <- (*notificationRegisterChainChanges)(wsc) -} - -// UnregisterChainChanges removes chain change notifications for the passed -// websocket client. -func (m *wsNotificationManager) UnregisterChainChanges(wsc *wsClient) { - m.queueNotification <- (*notificationUnregisterChainChanges)(wsc) -} - -// notifyChainChanged notifies websocket clients that have registered for -// chain changes. -func (m *wsNotificationManager) notifyChainChanged(clients map[chan struct{}]*wsClient, - removedChainBlockHashes []*daghash.Hash, addedChainBlockHashes []*daghash.Hash) { - - // Collect removed chain hashes. - removedChainHashesStrs := make([]string, len(removedChainBlockHashes)) - for i, hash := range removedChainBlockHashes { - removedChainHashesStrs[i] = hash.String() - } - - // Collect added chain blocks. - addedChainBlocks, err := collectChainBlocks(m.server, addedChainBlockHashes) - if err != nil { - log.Errorf("Failed to collect chain blocks: %s", err) - return - } - - // Create the notification. - ntfn := model.NewChainChangedNtfn(removedChainHashesStrs, addedChainBlocks) - - var marshalledJSON []byte - if len(clients) != 0 { - // Marshal notification - var err error - marshalledJSON, err = model.MarshalCommand(nil, ntfn) - if err != nil { - log.Errorf("Failed to marshal chain changed "+ - "notification: %s", err) - return - } - } - - for _, wsc := range clients { - // Queue notification. - wsc.QueueNotification(marshalledJSON) - } -} - -// subscribedClients returns the set of all websocket client quit channels that -// are registered to receive notifications regarding tx, either due to tx -// spending a watched output or outputting to a watched address. Matching -// client's filters are updated based on this transaction's outputs and output -// addresses that may be relevant for a client. -func (m *wsNotificationManager) subscribedClients(tx *util.Tx, - clients map[chan struct{}]*wsClient) map[chan struct{}]struct{} { - - // Use a map of client quit channels as keys to prevent duplicates when - // multiple inputs and/or outputs are relevant to the client. - subscribed := make(map[chan struct{}]struct{}) - - msgTx := tx.MsgTx() - for _, input := range msgTx.TxIn { - for quitChan, wsc := range clients { - filter := wsc.FilterData() - if filter == nil { - continue - } - if filter.existsUnspentOutpoint(&input.PreviousOutpoint) { - subscribed[quitChan] = struct{}{} - } - } - } - - for i, output := range msgTx.TxOut { - _, addr, err := txscript.ExtractScriptPubKeyAddress( - output.ScriptPubKey, m.server.dag.Params) - if err != nil || addr == nil { - // Clients are not able to subscribe to - // nonstandard or non-address outputs. - continue - } - for quitChan, wsc := range clients { - filter := wsc.FilterData() - if filter == nil { - continue - } - func() { - filter.mu.Lock() - defer filter.mu.Unlock() - if filter.existsAddress(addr) { - subscribed[quitChan] = struct{}{} - op := appmessage.Outpoint{ - TxID: *tx.ID(), - Index: uint32(i), - } - filter.addUnspentOutpoint(&op) - } - }() - } - } - - return subscribed -} - -// notifyFilteredBlockAdded notifies websocket clients that have registered for -// block updates when a block is added to the blockDAG. -func (m *wsNotificationManager) notifyFilteredBlockAdded(clients map[chan struct{}]*wsClient, - block *util.Block) { - - // Create the common portion of the notification that is the same for - // every client. - var w bytes.Buffer - err := block.MsgBlock().Header.Serialize(&w) - if err != nil { - log.Errorf("Failed to serialize header for filtered block "+ - "added notification: %s", err) - return - } - blueScore, err := block.BlueScore() - if err != nil { - log.Errorf("Failed to deserialize blue score for filtered block "+ - "added notification: %s", err) - return - } - ntfn := model.NewFilteredBlockAddedNtfn(blueScore, hex.EncodeToString(w.Bytes()), nil) - - // Search for relevant transactions for each client and save them - // serialized in hex encoding for the notification. - subscribedTxs := make(map[chan struct{}][]string) - for _, tx := range block.Transactions() { - var txHex string - for quitChan := range m.subscribedClients(tx, clients) { - if txHex == "" { - txHex = txHexString(tx.MsgTx()) - } - subscribedTxs[quitChan] = append(subscribedTxs[quitChan], txHex) - } - } - for quitChan, wsc := range clients { - // Add all discovered transactions for this client. For clients - // that have no new-style filter, add the empty string slice. - ntfn.SubscribedTxs = subscribedTxs[quitChan] - - // Marshal and queue notification. - marshalledJSON, err := model.MarshalCommand(nil, ntfn) - if err != nil { - log.Errorf("Failed to marshal filtered block "+ - "connected notification: %s", err) - return - } - wsc.QueueNotification(marshalledJSON) - } -} - -// RegisterNewMempoolTxsUpdates requests notifications to the passed websocket -// client when new transactions are added to the memory pool. -func (m *wsNotificationManager) RegisterNewMempoolTxsUpdates(wsc *wsClient) { - m.queueNotification <- (*notificationRegisterNewMempoolTxs)(wsc) -} - -// UnregisterNewMempoolTxsUpdates removes notifications to the passed websocket -// client when new transaction are added to the memory pool. -func (m *wsNotificationManager) UnregisterNewMempoolTxsUpdates(wsc *wsClient) { - m.queueNotification <- (*notificationUnregisterNewMempoolTxs)(wsc) -} - -// notifyForNewTx notifies websocket clients that have registered for updates -// when a new transaction is added to the memory pool. -func (m *wsNotificationManager) notifyForNewTx(clients map[chan struct{}]*wsClient, tx *util.Tx) { - txIDStr := tx.ID().String() - mtx := tx.MsgTx() - - var amount uint64 - for _, txOut := range mtx.TxOut { - amount += txOut.Value - } - - ntfn := model.NewTxAcceptedNtfn(txIDStr, util.Amount(amount).ToKAS()) - marshalledJSON, err := model.MarshalCommand(nil, ntfn) - if err != nil { - log.Errorf("Failed to marshal tx notification: %s", err.Error()) - return - } - - // To avoid unnecessary marshalling of verbose transactions, only initialize - // marshalledJSONVerboseFull and marshalledJSONVerbosePartial if required. - // Note: both are initialized at the same time - // Note: for simplicity's sake, this operation modifies mtx in place - var marshalledJSONVerboseFull []byte - var marshalledJSONVerbosePartial []byte - initializeMarshalledJSONVerbose := func() bool { - net := m.server.dag.Params - build := func() ([]byte, bool) { - rawTx, err := createTxRawResult(net, mtx, txIDStr, nil, "", nil, true) - if err != nil { - return nil, false - } - verboseNtfn := model.NewTxAcceptedVerboseNtfn(*rawTx) - marshalledJSONVerbose, err := model.MarshalCommand(nil, verboseNtfn) - if err != nil { - log.Errorf("Failed to marshal verbose tx notification: %s", err.Error()) - return nil, false - } - - return marshalledJSONVerbose, true - } - - // First, build the given mtx for a Full version of the transaction - var ok bool - marshalledJSONVerboseFull, ok = build() - if !ok { - return false - } - - // Second, modify the given mtx to make it partial - mtx.Payload = []byte{} - - // Third, build again, now with the modified mtx, for a Partial version - marshalledJSONVerbosePartial, ok = build() - return ok - } - - for _, wsc := range clients { - if wsc.verboseTxUpdates { - if marshalledJSONVerboseFull == nil { - ok := initializeMarshalledJSONVerbose() - if !ok { - return - } - } - - nodeSubnetworkID := m.server.dag.SubnetworkID() - if wsc.subnetworkIDForTxUpdates == nil || wsc.subnetworkIDForTxUpdates.IsEqual(nodeSubnetworkID) { - wsc.QueueNotification(marshalledJSONVerboseFull) - } else { - wsc.QueueNotification(marshalledJSONVerbosePartial) - } - } else { - wsc.QueueNotification(marshalledJSON) - } - } -} - -// txHexString returns the serialized transaction encoded in hexadecimal. -func txHexString(tx *appmessage.MsgTx) string { - buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) - // Ignore Serialize's error, as writing to a bytes.buffer cannot fail. - tx.Serialize(buf) - return hex.EncodeToString(buf.Bytes()) -} - -// notifyRelevantTxAccepted examines the inputs and outputs of the passed -// transaction, notifying websocket clients of outputs spending to a watched -// address and inputs spending a watched outpoint. Any outputs paying to a -// watched address result in the output being watched as well for future -// notifications. -func (m *wsNotificationManager) notifyRelevantTxAccepted(tx *util.Tx, - clients map[chan struct{}]*wsClient) { - - clientsToNotify := m.subscribedClients(tx, clients) - - if len(clientsToNotify) != 0 { - n := model.NewRelevantTxAcceptedNtfn(txHexString(tx.MsgTx())) - marshalled, err := model.MarshalCommand(nil, n) - if err != nil { - log.Errorf("Failed to marshal notification: %s", err) - return - } - for quitChan := range clientsToNotify { - clients[quitChan].QueueNotification(marshalled) - } - } -} - -// AddClient adds the passed websocket client to the notification manager. -func (m *wsNotificationManager) AddClient(wsc *wsClient) { - m.queueNotification <- (*notificationRegisterClient)(wsc) -} - -// RemoveClient removes the passed websocket client and all notifications -// registered for it. -func (m *wsNotificationManager) RemoveClient(wsc *wsClient) { - select { - case m.queueNotification <- (*notificationUnregisterClient)(wsc): - case <-m.quit: - } -} - -// Start starts the goroutines required for the manager to queue and process -// websocket client notifications. -func (m *wsNotificationManager) Start() { - m.wg.Add(2) - spawn("wsNotificationManager.queueHandler", m.queueHandler) - spawn("wsNotificationManager.notificationHandler", m.notificationHandler) -} - -// WaitForShutdown blocks until all notification manager goroutines have -// finished. -func (m *wsNotificationManager) WaitForShutdown() { - m.wg.Wait() -} - -// Shutdown shuts down the manager, stopping the notification queue and -// notification handler goroutines. -func (m *wsNotificationManager) Shutdown() { - close(m.quit) -} - -// newWsNotificationManager returns a new notification manager ready for use. -// See wsNotificationManager for more details. -func newWsNotificationManager(server *Server) *wsNotificationManager { - return &wsNotificationManager{ - server: server, - queueNotification: make(chan interface{}), - notificationMsgs: make(chan interface{}), - numClients: make(chan int), - quit: make(chan struct{}), - } -} - -// wsResponse houses a message to send to a connected websocket client as -// well as a channel to reply on when the message is sent. -type wsResponse struct { - msg []byte - doneChan chan bool -} - -// wsClient provides an abstraction for handling a websocket client. The -// overall data flow is split into 3 main goroutines, a possible 4th goroutine -// for long-running operations (only started if request is made), and a -// websocket manager which is used to allow things such as broadcasting -// requested notifications to all connected websocket clients. Inbound -// messages are read via the inHandler goroutine and generally dispatched to -// their own handler. However, certain potentially long-running operations such -// as rescans, are sent to the asyncHander goroutine and are limited to one at a -// time. There are two outbound message types - one for responding to client -// requests and another for async notifications. Responses to client requests -// use SendMessage which employs a buffered channel thereby limiting the number -// of outstanding requests that can be made. Notifications are sent via -// QueueNotification which implements a queue via notificationQueueHandler to -// ensure sending notifications from other subsystems can't block. Ultimately, -// all messages are sent via the outHandler. -type wsClient struct { - sync.Mutex - - // server is the RPC server that is servicing the client. - server *Server - - // conn is the underlying websocket connection. - conn *websocket.Conn - - // disconnected indicated whether or not the websocket client is - // disconnected. - disconnected bool - - // addr is the remote address of the client. - addr string - - // authenticated specifies whether a client has been authenticated - // and therefore is allowed to communicated over the websocket. - authenticated bool - - // isAdmin specifies whether a client may change the state of the server; - // false means its access is only to the limited set of RPC calls. - isAdmin bool - - // sessionID is a random ID generated for each client when connected. - // These IDs may be queried by a client using the session RPC. A change - // to the session ID indicates that the client reconnected. - sessionID uint64 - - // verboseTxUpdates specifies whether a client has requested verbose - // information about all new transactions. - verboseTxUpdates bool - - // subnetworkIDForTxUpdates specifies whether a client has requested to receive - // new transaction information from a specific subnetwork. - subnetworkIDForTxUpdates *subnetworkid.SubnetworkID - - // filterData is the new generation transaction filter backported from - // github.com/decred/dcrd for the new backported `loadTxFilter` and - // `rescanBlocks` methods. - filterData *wsClientFilter - - // Networking infrastructure. - serviceRequestSem semaphore - ntfnChan chan []byte - sendChan chan wsResponse - quit chan struct{} - wg sync.WaitGroup -} - -// inHandler handles all incoming messages for the websocket connection. It -// must be run as a goroutine. -func (c *wsClient) inHandler() { -out: - for { - // Break out of the loop once the quit channel has been closed. - // Use a non-blocking select here so we fall through otherwise. - select { - case <-c.quit: - break out - default: - } - - _, msg, err := c.conn.ReadMessage() - if err != nil { - // Log the error if it's not due to disconnecting. - if err != io.EOF { - log.Errorf("Websocket receive error from "+ - "%s: %s", c.addr, err) - } - break out - } - - var request model.Request - err = json.Unmarshal(msg, &request) - if err != nil { - if !c.authenticated { - break out - } - - jsonErr := &model.RPCError{ - Code: model.ErrRPCParse.Code, - Message: "Failed to parse request: " + err.Error(), - } - reply, err := createMarshalledReply(nil, nil, jsonErr) - if err != nil { - log.Errorf("Failed to marshal parse failure "+ - "reply: %s", err) - continue - } - c.SendMessage(reply, nil) - continue - } - - // The JSON-RPC 1.0 spec defines that notifications must have their "id" - // set to null and states that notifications do not have a response. - // - // A JSON-RPC 2.0 notification is a request with "json-rpc":"2.0", and - // without an "id" member. The specification states that notifications - // must not be responded to. JSON-RPC 2.0 permits the null value as a - // valid request id, therefore such requests are not notifications. - // - // Kaspad does not respond to any request without an "id" or "id":null, - // regardless the indicated JSON-RPC protocol version. - if request.ID == nil { - if !c.authenticated { - break out - } - continue - } - - cmd := parseCmd(&request) - if cmd.err != nil { - if !c.authenticated { - break out - } - - reply, err := createMarshalledReply(cmd.id, nil, cmd.err) - if err != nil { - log.Errorf("Failed to marshal parse failure "+ - "reply: %s", err) - continue - } - c.SendMessage(reply, nil) - continue - } - log.Debugf("Websocket server received command <%s> from %s", cmd.method, c.addr) - - // Check auth. The client is immediately disconnected if the - // first request of an unauthentiated websocket client is not - // the authenticate request, an authenticate request is received - // when the client is already authenticated, or incorrect - // authentication credentials are provided in the request. - switch authCmd, ok := cmd.cmd.(*model.AuthenticateCmd); { - case c.authenticated && ok: - log.Warnf("Websocket client %s is already authenticated", - c.addr) - break out - case !c.authenticated && !ok: - log.Warnf("Unauthenticated websocket message " + - "received") - break out - case !c.authenticated: - // Check credentials. - login := authCmd.Username + ":" + authCmd.Passphrase - auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) - authSha := sha256.Sum256([]byte(auth)) - cmp := subtle.ConstantTimeCompare(authSha[:], c.server.authsha[:]) - limitcmp := subtle.ConstantTimeCompare(authSha[:], c.server.limitauthsha[:]) - if cmp != 1 && limitcmp != 1 { - log.Warnf("Auth failure.") - break out - } - c.authenticated = true - c.isAdmin = cmp == 1 - - // Marshal and send response. - reply, err := createMarshalledReply(cmd.id, nil, nil) - if err != nil { - log.Errorf("Failed to marshal authenticate reply: "+ - "%s", err.Error()) - continue - } - c.SendMessage(reply, nil) - continue - } - - // Check if the client is using limited RPC credentials and - // error when not authorized to call this RPC. - if !c.isAdmin { - if _, ok := rpcLimited[request.Method]; !ok { - jsonErr := &model.RPCError{ - Code: model.ErrRPCInvalidParams.Code, - Message: "limited user not authorized for this method", - } - // Marshal and send response. - reply, err := createMarshalledReply(request.ID, nil, jsonErr) - if err != nil { - log.Errorf("Failed to marshal parse failure "+ - "reply: %s", err) - continue - } - c.SendMessage(reply, nil) - continue - } - } - - // Asynchronously handle the request. A semaphore is used to - // limit the number of concurrent requests currently being - // serviced. If the semaphore can not be acquired, simply wait - // until a request finished before reading the next RPC request - // from the websocket client. - // - // This could be a little fancier by timing out and erroring - // when it takes too long to service the request, but if that is - // done, the read of the next request should not be blocked by - // this semaphore, otherwise the next request will be read and - // will probably sit here for another few seconds before timing - // out as well. This will cause the total timeout duration for - // later requests to be much longer than the check here would - // imply. - // - // If a timeout is added, the semaphore acquiring should be - // moved inside of the new goroutine with a select statement - // that also reads a time.After channel. This will unblock the - // read of the next request from the websocket client and allow - // many requests to be waited on concurrently. - c.serviceRequestSem.acquire() - spawn("wsClient.inHandler-serviceRequest", func() { - c.serviceRequest(cmd) - c.serviceRequestSem.release() - }) - } - - // Ensure the connection is closed. - c.Disconnect() - c.wg.Done() - log.Tracef("Websocket client input handler done for %s", c.addr) -} - -// serviceRequest services a parsed RPC request by looking up and executing the -// appropriate RPC handler. The response is marshalled and sent to the -// websocket client. -func (c *wsClient) serviceRequest(r *parsedRPCCmd) { - var ( - result interface{} - err error - ) - - // Lookup the websocket extension for the command and if it doesn't - // exist fallback to handling the command as a standard command. - wsHandler, ok := wsHandlers[r.method] - if ok { - result, err = wsHandler(c, r.cmd) - } else { - result, err = c.server.standardCmdResult(r, nil) - } - reply, err := createMarshalledReply(r.id, result, err) - if err != nil { - log.Errorf("Failed to marshal reply for <%s> "+ - "command: %s", r.method, err) - return - } - c.SendMessage(reply, nil) -} - -// notificationQueueHandler handles the queuing of outgoing notifications for -// the websocket client. This runs as a muxer for various sources of input to -// ensure that queuing up notifications to be sent will not block. Otherwise, -// slow clients could bog down the other systems (such as the mempool or block -// manager) which are queuing the data. The data is passed on to outHandler to -// actually be written. It must be run as a goroutine. -func (c *wsClient) notificationQueueHandler() { - ntfnSentChan := make(chan bool, 1) // nonblocking sync - - // pendingNtfns is used as a queue for notifications that are ready to - // be sent once there are no outstanding notifications currently being - // sent. The waiting flag is used over simply checking for items in the - // pending list to ensure cleanup knows what has and hasn't been sent - // to the outHandler. Currently no special cleanup is needed, however - // if something like a done channel is added to notifications in the - // future, not knowing what has and hasn't been sent to the outHandler - // (and thus who should respond to the done channel) would be - // problematic without using this approach. - pendingNtfns := list.New() - waiting := false -out: - for { - select { - // This channel is notified when a message is being queued to - // be sent across the network socket. It will either send the - // message immediately if a send is not already in progress, or - // queue the message to be sent once the other pending messages - // are sent. - case msg := <-c.ntfnChan: - if !waiting { - c.SendMessage(msg, ntfnSentChan) - } else { - pendingNtfns.PushBack(msg) - } - waiting = true - - // This channel is notified when a notification has been sent - // across the network socket. - case <-ntfnSentChan: - // No longer waiting if there are no more messages in - // the pending messages queue. - next := pendingNtfns.Front() - if next == nil { - waiting = false - continue - } - - // Notify the outHandler about the next item to - // asynchronously send. - msg := pendingNtfns.Remove(next).([]byte) - c.SendMessage(msg, ntfnSentChan) - - case <-c.quit: - break out - } - } - - // Drain any wait channels before exiting so nothing is left waiting - // around to send. -cleanup: - for { - select { - case <-c.ntfnChan: - case <-ntfnSentChan: - default: - break cleanup - } - } - c.wg.Done() - log.Tracef("Websocket client notification queue handler done "+ - "for %s", c.addr) -} - -// outHandler handles all outgoing messages for the websocket connection. It -// must be run as a goroutine. It uses a buffered channel to serialize output -// messages while allowing the sender to continue running asynchronously. It -// must be run as a goroutine. -func (c *wsClient) outHandler() { -out: - for { - // Send any messages ready for send until the quit channel is - // closed. - select { - case r := <-c.sendChan: - err := c.conn.WriteMessage(websocket.TextMessage, r.msg) - if err != nil { - c.Disconnect() - break out - } - if r.doneChan != nil { - r.doneChan <- true - } - - case <-c.quit: - break out - } - } - - // Drain any wait channels before exiting so nothing is left waiting - // around to send. -cleanup: - for { - select { - case r := <-c.sendChan: - if r.doneChan != nil { - r.doneChan <- false - } - default: - break cleanup - } - } - c.wg.Done() - log.Tracef("Websocket client output handler done for %s", c.addr) -} - -// SendMessage sends the passed json to the websocket client. It is backed -// by a buffered channel, so it will not block until the send channel is full. -// Note however that QueueNotification must be used for sending async -// notifications instead of the this function. This approach allows a limit to -// the number of outstanding requests a client can make without preventing or -// blocking on async notifications. -func (c *wsClient) SendMessage(marshalledJSON []byte, doneChan chan bool) { - // Don't send the message if disconnected. - if c.Disconnected() { - if doneChan != nil { - doneChan <- false - } - return - } - - c.sendChan <- wsResponse{msg: marshalledJSON, doneChan: doneChan} -} - -// ErrClientQuit describes the error where a client send is not processed due -// to the client having already been disconnected or dropped. -var ErrClientQuit = errors.New("client quit") - -// QueueNotification queues the passed notification to be sent to the websocket -// client. This function, as the name implies, is only intended for -// notifications since it has additional logic to prevent other subsystems, such -// as the memory pool and block manager, from blocking even when the send -// channel is full. -// -// If the client is in the process of shutting down, this function returns -// ErrClientQuit. This is intended to be checked by long-running notification -// handlers to stop processing if there is no more work needed to be done. -func (c *wsClient) QueueNotification(marshalledJSON []byte) error { - // Don't queue the message if disconnected. - if c.Disconnected() { - return ErrClientQuit - } - - c.ntfnChan <- marshalledJSON - return nil -} - -// Disconnected returns whether or not the websocket client is disconnected. -func (c *wsClient) Disconnected() bool { - c.Lock() - defer c.Unlock() - return c.disconnected -} - -// Disconnect disconnects the websocket client. -func (c *wsClient) Disconnect() { - c.Lock() - defer c.Unlock() - - // Nothing to do if already disconnected. - if c.disconnected { - return - } - - log.Tracef("Disconnecting websocket client %s", c.addr) - close(c.quit) - c.conn.Close() - c.disconnected = true -} - -// Start begins processing input and output messages. -func (c *wsClient) Start() { - log.Tracef("Starting websocket client %s", c.addr) - - // Start processing input and output. - c.wg.Add(3) - spawn("wsClient.inHandler", c.inHandler) - spawn("wsClient.notificationQueueHandler", c.notificationQueueHandler) - spawn("wsClient.outHandler", c.outHandler) -} - -// WaitForShutdown blocks until the websocket client goroutines are stopped -// and the connection is closed. -func (c *wsClient) WaitForShutdown() { - c.wg.Wait() -} - -// FilterData returns the websocket client filter data. -func (c *wsClient) FilterData() *wsClientFilter { - c.Lock() - defer c.Unlock() - return c.filterData -} - -// newWebsocketClient returns a new websocket client given the notification -// manager, websocket connection, remote address, and whether or not the client -// has already been authenticated (via HTTP Basic access authentication). The -// returned client is ready to start. Once started, the client will process -// incoming and outgoing messages in separate goroutines complete with queuing -// and asynchrous handling for long-running operations. -func newWebsocketClient(server *Server, conn *websocket.Conn, - remoteAddr string, authenticated bool, isAdmin bool) (*wsClient, error) { - - sessionID, err := random.Uint64() - if err != nil { - return nil, err - } - - client := &wsClient{ - conn: conn, - addr: remoteAddr, - authenticated: authenticated, - isAdmin: isAdmin, - sessionID: sessionID, - server: server, - serviceRequestSem: makeSemaphore(server.cfg.RPCMaxConcurrentReqs), - ntfnChan: make(chan []byte, 1), // nonblocking sync - sendChan: make(chan wsResponse, websocketSendBufferSize), - quit: make(chan struct{}), - } - return client, nil -} - -func init() { - wsHandlers = wsHandlersBeforeInit -} diff --git a/infrastructure/network/rpc/utils.go b/infrastructure/network/rpc/utils.go deleted file mode 100644 index 45dd2a4e4..000000000 --- a/infrastructure/network/rpc/utils.go +++ /dev/null @@ -1,42 +0,0 @@ -package rpc - -import ( - "io/ioutil" - "net" - "os" - "time" - - "github.com/kaspanet/kaspad/infrastructure/config" - - "github.com/kaspanet/kaspad/util" -) - -// GenCertPair generates a key/cert pair to the paths provided. -func GenCertPair(certFile, keyFile string) error { - log.Infof("Generating TLS certificates...") - - org := "kaspad autogenerated cert" - validUntil := time.Now().Add(10 * 365 * 24 * time.Hour) - cert, key, err := util.NewTLSCertPair(org, validUntil, nil) - if err != nil { - return err - } - - // Write cert and key files. - if err = ioutil.WriteFile(certFile, cert, 0666); err != nil { - return err - } - if err = ioutil.WriteFile(keyFile, key, 0600); err != nil { - os.Remove(certFile) - return err - } - - log.Infof("Done generating TLS certificates") - return nil -} - -// KaspadDial connects to the address on the named network using the appropriate -// dial function depending on the address and configuration options. -func KaspadDial(cfg *config.Config, addr net.Addr) (net.Conn, error) { - return cfg.Dial(addr.Network(), addr.String(), config.DefaultConnectTimeout) -} diff --git a/infrastructure/network/rpcclient/grpcclient/grpcclient.go b/infrastructure/network/rpcclient/grpcclient/grpcclient.go new file mode 100644 index 000000000..f5b48ff11 --- /dev/null +++ b/infrastructure/network/rpcclient/grpcclient/grpcclient.go @@ -0,0 +1,105 @@ +package grpcclient + +import ( + "context" + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/server/grpcserver/protowire" + "github.com/pkg/errors" + "google.golang.org/grpc" + "google.golang.org/grpc/encoding/gzip" + "io" + "time" +) + +// GRPCClient is a gRPC-based RPC client +type GRPCClient struct { + stream protowire.RPC_MessageStreamClient +} + +// Connect connects to the RPC server with the given address +func Connect(address string) (*GRPCClient, error) { + const dialTimeout = 30 * time.Second + ctx, cancel := context.WithTimeout(context.Background(), dialTimeout) + defer cancel() + + gRPCConnection, err := grpc.DialContext(ctx, address, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + return nil, errors.Wrapf(err, "error connecting to %s", address) + } + + grpcClient := protowire.NewRPCClient(gRPCConnection) + stream, err := grpcClient.MessageStream(context.Background(), grpc.UseCompressor(gzip.Name)) + if err != nil { + return nil, errors.Wrapf(err, "error getting client stream for %s", address) + } + return &GRPCClient{stream: stream}, nil +} + +// Disconnect disconnects from the RPC server +func (c *GRPCClient) Disconnect() error { + return c.stream.CloseSend() +} + +// AttachRouter attaches the given router to the client and starts +// sending/receiving messages via it +func (c *GRPCClient) AttachRouter(router *router.Router) { + spawn("GRPCClient.AttachRouter-sendLoop", func() { + for { + message, err := router.OutgoingRoute().Dequeue() + if err != nil { + c.handleError(err) + return + } + err = c.send(message) + if err != nil { + c.handleError(err) + return + } + } + }) + spawn("GRPCClient.AttachRouter-receiveLoop", func() { + for { + message, err := c.receive() + if err != nil { + c.handleError(err) + return + } + err = router.EnqueueIncomingMessage(message) + if err != nil { + c.handleError(err) + return + } + } + }) +} + +func (c *GRPCClient) send(requestAppMessage appmessage.Message) error { + request, err := protowire.FromAppMessage(requestAppMessage) + if err != nil { + return errors.Wrapf(err, "error converting the request") + } + return c.stream.Send(request) +} + +func (c *GRPCClient) receive() (appmessage.Message, error) { + response, err := c.stream.Recv() + if err != nil { + return nil, err + } + return response.ToAppMessage() +} + +func (c *GRPCClient) handleError(err error) { + if errors.Is(err, io.EOF) { + return + } + if errors.Is(err, router.ErrRouteClosed) { + err := c.Disconnect() + if err != nil { + panic(err) + } + return + } + panic(err) +} diff --git a/infrastructure/network/rpcclient/grpcclient/log.go b/infrastructure/network/rpcclient/grpcclient/log.go new file mode 100644 index 000000000..6d7c54ebb --- /dev/null +++ b/infrastructure/network/rpcclient/grpcclient/log.go @@ -0,0 +1,9 @@ +package grpcclient + +import ( + "github.com/kaspanet/kaspad/infrastructure/logger" + "github.com/kaspanet/kaspad/util/panics" +) + +var log, _ = logger.Get(logger.SubsystemTags.RPCC) +var spawn = panics.GoroutineWrapperFunc(log) diff --git a/infrastructure/network/rpcclient/grpcclient/post.go b/infrastructure/network/rpcclient/grpcclient/post.go new file mode 100644 index 000000000..8247d708b --- /dev/null +++ b/infrastructure/network/rpcclient/grpcclient/post.go @@ -0,0 +1,64 @@ +package grpcclient + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/infrastructure/network/netadapter/server/grpcserver/protowire" + "github.com/pkg/errors" + "google.golang.org/protobuf/encoding/protojson" +) + +// PostJSON is a helper function that converts the given requestJSON +// to protobuf, sends it to the RPC server, accepts the first response +// that arrives back, and returns the response as JSON +func (c *GRPCClient) PostJSON(requestJSON string) (string, error) { + requestBytes := []byte(requestJSON) + var parsedRequest protowire.KaspadMessage + err := protojson.Unmarshal(requestBytes, &parsedRequest) + if err != nil { + return "", errors.Wrapf(err, "error parsing the request") + } + response, err := c.Post(&parsedRequest) + if err != nil { + return "", err + } + responseBytes, err := protojson.Marshal(response) + if err != nil { + return "", errors.Wrapf(err, "error parsing the response from the RPC server") + } + return string(responseBytes), nil +} + +// PostAppMessage is a helper function that converts the given +// requestAppMessage to protobuf, sends it to the RPC server, +// accepts the first response that arrives back, and returns the +// response as an appMessage +func (c *GRPCClient) PostAppMessage(requestAppMessage appmessage.Message) (appmessage.Message, error) { + request, err := protowire.FromAppMessage(requestAppMessage) + if err != nil { + return nil, errors.Wrapf(err, "error converting the request") + } + response, err := c.Post(request) + if err != nil { + return nil, err + } + responseAppMessage, err := response.ToAppMessage() + if err != nil { + return nil, errors.Wrapf(err, "error converting the response") + } + return responseAppMessage, nil +} + +// Post is a helper function that sends the given request to the +// RPC server, accepts the first response that arrives back, and +// returns the response +func (c *GRPCClient) Post(request *protowire.KaspadMessage) (*protowire.KaspadMessage, error) { + err := c.stream.Send(request) + if err != nil { + return nil, errors.Wrapf(err, "error sending the request to the RPC server") + } + response, err := c.stream.Recv() + if err != nil { + return nil, errors.Wrapf(err, "error receiving the response from the RPC server") + } + return response, nil +} diff --git a/infrastructure/network/rpcclient/log.go b/infrastructure/network/rpcclient/log.go new file mode 100644 index 000000000..3f211c4ea --- /dev/null +++ b/infrastructure/network/rpcclient/log.go @@ -0,0 +1,9 @@ +package rpcclient + +import ( + "github.com/kaspanet/kaspad/infrastructure/logger" + "github.com/kaspanet/kaspad/util/panics" +) + +var log, _ = logger.Get(logger.SubsystemTags.RPCC) +var spawn = panics.GoroutineWrapperFunc(log) diff --git a/infrastructure/network/rpcclient/rpc_connect_to_peer.go b/infrastructure/network/rpcclient/rpc_connect_to_peer.go new file mode 100644 index 000000000..6f481805f --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_connect_to_peer.go @@ -0,0 +1,20 @@ +package rpcclient + +import "github.com/kaspanet/kaspad/app/appmessage" + +// AddPeer sends an RPC request respective to the function's name and returns the RPC server's response +func (c *RPCClient) AddPeer(address string, isPermanent bool) error { + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewAddPeerRequestMessage(address, isPermanent)) + if err != nil { + return err + } + response, err := c.route(appmessage.CmdAddPeerResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return err + } + getMempoolEntryResponse := response.(*appmessage.AddPeerResponseMessage) + if getMempoolEntryResponse.Error != nil { + return c.convertRPCError(getMempoolEntryResponse.Error) + } + return nil +} diff --git a/infrastructure/network/rpcclient/rpc_get_block.go b/infrastructure/network/rpcclient/rpc_get_block.go new file mode 100644 index 000000000..4002cba83 --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_get_block.go @@ -0,0 +1,23 @@ +package rpcclient + +import "github.com/kaspanet/kaspad/app/appmessage" + +// GetBlock sends an RPC request respective to the function's name and returns the RPC server's response +func (c *RPCClient) GetBlock(hash string, subnetworkID string, includeBlockHex bool, + includeBlockVerboseData bool, includeTransactionVerboseData bool) (*appmessage.GetBlockResponseMessage, error) { + + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewGetBlockRequestMessage(hash, subnetworkID, + includeBlockHex, includeBlockVerboseData, includeTransactionVerboseData)) + if err != nil { + return nil, err + } + response, err := c.route(appmessage.CmdGetBlockResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return nil, err + } + GetBlockResponse := response.(*appmessage.GetBlockResponseMessage) + if GetBlockResponse.Error != nil { + return nil, c.convertRPCError(GetBlockResponse.Error) + } + return GetBlockResponse, nil +} diff --git a/infrastructure/network/rpcclient/rpc_get_block_template.go b/infrastructure/network/rpcclient/rpc_get_block_template.go new file mode 100644 index 000000000..ccaaf33ec --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_get_block_template.go @@ -0,0 +1,20 @@ +package rpcclient + +import "github.com/kaspanet/kaspad/app/appmessage" + +// GetBlockTemplate sends an RPC request respective to the function's name and returns the RPC server's response +func (c *RPCClient) GetBlockTemplate(miningAddress string, longPollID string) (*appmessage.GetBlockTemplateResponseMessage, error) { + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewGetBlockTemplateRequestMessage(miningAddress, longPollID)) + if err != nil { + return nil, err + } + response, err := c.route(appmessage.CmdGetBlockTemplateResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return nil, err + } + getBlockTemplateResponse := response.(*appmessage.GetBlockTemplateResponseMessage) + if getBlockTemplateResponse.Error != nil { + return nil, c.convertRPCError(getBlockTemplateResponse.Error) + } + return getBlockTemplateResponse, nil +} diff --git a/infrastructure/network/rpcclient/rpc_get_blocks.go b/infrastructure/network/rpcclient/rpc_get_blocks.go new file mode 100644 index 000000000..a5b853019 --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_get_blocks.go @@ -0,0 +1,22 @@ +package rpcclient + +import "github.com/kaspanet/kaspad/app/appmessage" + +// GetBlocks sends an RPC request respective to the function's name and returns the RPC server's response +func (c *RPCClient) GetBlocks(lowHash string, includeBlockHexes bool, + includeBlockVerboseData bool) (*appmessage.GetBlocksResponseMessage, error) { + + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewGetBlocksRequestMessage(lowHash, includeBlockHexes, includeBlockVerboseData)) + if err != nil { + return nil, err + } + response, err := c.route(appmessage.CmdGetBlocksResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return nil, err + } + GetBlocksResponse := response.(*appmessage.GetBlocksResponseMessage) + if GetBlocksResponse.Error != nil { + return nil, c.convertRPCError(GetBlocksResponse.Error) + } + return GetBlocksResponse, nil +} diff --git a/infrastructure/network/rpcclient/rpc_get_chain_from_block.go b/infrastructure/network/rpcclient/rpc_get_chain_from_block.go new file mode 100644 index 000000000..2e7748f0d --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_get_chain_from_block.go @@ -0,0 +1,20 @@ +package rpcclient + +import "github.com/kaspanet/kaspad/app/appmessage" + +// GetChainFromBlock sends an RPC request respective to the function's name and returns the RPC server's response +func (c *RPCClient) GetChainFromBlock(startHash string, includeBlockVerboseData bool) (*appmessage.GetChainFromBlockResponseMessage, error) { + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewGetChainFromBlockRequestMessage(startHash, includeBlockVerboseData)) + if err != nil { + return nil, err + } + response, err := c.route(appmessage.CmdGetChainFromBlockResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return nil, err + } + GetChainFromBlockResponse := response.(*appmessage.GetChainFromBlockResponseMessage) + if GetChainFromBlockResponse.Error != nil { + return nil, c.convertRPCError(GetChainFromBlockResponse.Error) + } + return GetChainFromBlockResponse, nil +} diff --git a/infrastructure/network/rpcclient/rpc_get_connected_peer_info.go b/infrastructure/network/rpcclient/rpc_get_connected_peer_info.go new file mode 100644 index 000000000..021b5ee70 --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_get_connected_peer_info.go @@ -0,0 +1,20 @@ +package rpcclient + +import "github.com/kaspanet/kaspad/app/appmessage" + +// GetConnectedPeerInfo sends an RPC request respective to the function's name and returns the RPC server's response +func (c *RPCClient) GetConnectedPeerInfo() (*appmessage.GetConnectedPeerInfoResponseMessage, error) { + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewGetConnectedPeerInfoRequestMessage()) + if err != nil { + return nil, err + } + response, err := c.route(appmessage.CmdGetConnectedPeerInfoResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return nil, err + } + getMempoolEntryResponse := response.(*appmessage.GetConnectedPeerInfoResponseMessage) + if getMempoolEntryResponse.Error != nil { + return nil, c.convertRPCError(getMempoolEntryResponse.Error) + } + return getMempoolEntryResponse, nil +} diff --git a/infrastructure/network/rpcclient/rpc_get_mempool_entry.go b/infrastructure/network/rpcclient/rpc_get_mempool_entry.go new file mode 100644 index 000000000..c3ba84a05 --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_get_mempool_entry.go @@ -0,0 +1,20 @@ +package rpcclient + +import "github.com/kaspanet/kaspad/app/appmessage" + +// GetMempoolEntry sends an RPC request respective to the function's name and returns the RPC server's response +func (c *RPCClient) GetMempoolEntry(txID string) (*appmessage.GetMempoolEntryResponseMessage, error) { + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewGetMempoolEntryRequestMessage(txID)) + if err != nil { + return nil, err + } + response, err := c.route(appmessage.CmdGetMempoolEntryResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return nil, err + } + getMempoolEntryResponse := response.(*appmessage.GetMempoolEntryResponseMessage) + if getMempoolEntryResponse.Error != nil { + return nil, c.convertRPCError(getMempoolEntryResponse.Error) + } + return getMempoolEntryResponse, nil +} diff --git a/infrastructure/network/rpcclient/rpc_get_peer_addresses.go b/infrastructure/network/rpcclient/rpc_get_peer_addresses.go new file mode 100644 index 000000000..d41b29472 --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_get_peer_addresses.go @@ -0,0 +1,20 @@ +package rpcclient + +import "github.com/kaspanet/kaspad/app/appmessage" + +// GetPeerAddresses sends an RPC request respective to the function's name and returns the RPC server's response +func (c *RPCClient) GetPeerAddresses() (*appmessage.GetPeerAddressesResponseMessage, error) { + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewGetPeerAddressesRequestMessage()) + if err != nil { + return nil, err + } + response, err := c.route(appmessage.CmdGetPeerAddressesResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return nil, err + } + getPeerAddressesResponse := response.(*appmessage.GetPeerAddressesResponseMessage) + if getPeerAddressesResponse.Error != nil { + return nil, c.convertRPCError(getPeerAddressesResponse.Error) + } + return getPeerAddressesResponse, nil +} diff --git a/infrastructure/network/rpcclient/rpc_get_selected_tip_hash.go b/infrastructure/network/rpcclient/rpc_get_selected_tip_hash.go new file mode 100644 index 000000000..64709a844 --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_get_selected_tip_hash.go @@ -0,0 +1,20 @@ +package rpcclient + +import "github.com/kaspanet/kaspad/app/appmessage" + +// GetSelectedTipHash sends an RPC request respective to the function's name and returns the RPC server's response +func (c *RPCClient) GetSelectedTipHash() (*appmessage.GetSelectedTipHashResponseMessage, error) { + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewGetSelectedTipHashRequestMessage()) + if err != nil { + return nil, err + } + response, err := c.route(appmessage.CmdGetSelectedTipHashResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return nil, err + } + getSelectedTipHashResponse := response.(*appmessage.GetSelectedTipHashResponseMessage) + if getSelectedTipHashResponse.Error != nil { + return nil, c.convertRPCError(getSelectedTipHashResponse.Error) + } + return getSelectedTipHashResponse, nil +} diff --git a/infrastructure/network/rpcclient/rpc_get_subnetwork.go b/infrastructure/network/rpcclient/rpc_get_subnetwork.go new file mode 100644 index 000000000..e8543a4fb --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_get_subnetwork.go @@ -0,0 +1,20 @@ +package rpcclient + +import "github.com/kaspanet/kaspad/app/appmessage" + +// GetSubnetwork sends an RPC request respective to the function's name and returns the RPC server's response +func (c *RPCClient) GetSubnetwork(subnetworkID string) (*appmessage.GetSubnetworkResponseMessage, error) { + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewGetSubnetworkRequestMessage(subnetworkID)) + if err != nil { + return nil, err + } + response, err := c.route(appmessage.CmdGetSubnetworkResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return nil, err + } + getSubnetworkResponse := response.(*appmessage.GetSubnetworkResponseMessage) + if getSubnetworkResponse.Error != nil { + return nil, c.convertRPCError(getSubnetworkResponse.Error) + } + return getSubnetworkResponse, nil +} diff --git a/infrastructure/network/rpcclient/rpc_on_block_added.go b/infrastructure/network/rpcclient/rpc_on_block_added.go new file mode 100644 index 000000000..e82c9b248 --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_on_block_added.go @@ -0,0 +1,38 @@ +package rpcclient + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/pkg/errors" +) + +// RegisterForBlockAddedNotifications 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) RegisterForBlockAddedNotifications(onBlockAdded func(notification *appmessage.BlockAddedNotificationMessage)) error { + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewNotifyBlockAddedRequestMessage()) + if err != nil { + return err + } + response, err := c.route(appmessage.CmdNotifyBlockAddedResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return err + } + notifyBlockAddedResponse := response.(*appmessage.NotifyBlockAddedResponseMessage) + if notifyBlockAddedResponse.Error != nil { + return c.convertRPCError(notifyBlockAddedResponse.Error) + } + spawn("RegisterForBlockAddedNotifications", func() { + for { + notification, err := c.route(appmessage.CmdBlockAddedNotificationMessage).Dequeue() + if err != nil { + if errors.Is(err, routerpkg.ErrRouteClosed) { + break + } + panic(err) + } + blockAddedNotification := notification.(*appmessage.BlockAddedNotificationMessage) + onBlockAdded(blockAddedNotification) + } + }) + return nil +} diff --git a/infrastructure/network/rpcclient/rpc_on_chain_changed.go b/infrastructure/network/rpcclient/rpc_on_chain_changed.go new file mode 100644 index 000000000..328af0231 --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_on_chain_changed.go @@ -0,0 +1,38 @@ +package rpcclient + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/pkg/errors" +) + +// RegisterForChainChangedNotifications 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) RegisterForChainChangedNotifications(onChainChanged func(notification *appmessage.ChainChangedNotificationMessage)) error { + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewNotifyChainChangedRequestMessage()) + if err != nil { + return err + } + response, err := c.route(appmessage.CmdNotifyChainChangedResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return err + } + notifyChainChangedResponse := response.(*appmessage.NotifyChainChangedResponseMessage) + if notifyChainChangedResponse.Error != nil { + return c.convertRPCError(notifyChainChangedResponse.Error) + } + spawn("RegisterForChainChangedNotifications", func() { + for { + notification, err := c.route(appmessage.CmdChainChangedNotificationMessage).Dequeue() + if err != nil { + if errors.Is(err, routerpkg.ErrRouteClosed) { + break + } + panic(err) + } + ChainChangedNotification := notification.(*appmessage.ChainChangedNotificationMessage) + onChainChanged(ChainChangedNotification) + } + }) + return nil +} diff --git a/infrastructure/network/rpcclient/rpc_send_raw_transaction.go b/infrastructure/network/rpcclient/rpc_send_raw_transaction.go new file mode 100644 index 000000000..fbc375936 --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_send_raw_transaction.go @@ -0,0 +1,30 @@ +package rpcclient + +import ( + "bytes" + "encoding/hex" + "github.com/kaspanet/kaspad/app/appmessage" +) + +// 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) { + buf := bytes.NewBuffer(make([]byte, 0, msgTx.SerializeSize())) + if err := msgTx.Serialize(buf); err != nil { + return nil, err + } + transactionHex := hex.EncodeToString(buf.Bytes()) + err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewSubmitTransactionRequestMessage(transactionHex)) + if err != nil { + return nil, err + } + response, err := c.route(appmessage.CmdSubmitTransactionResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return nil, err + } + submitTransactionResponse := response.(*appmessage.SubmitTransactionResponseMessage) + if submitTransactionResponse.Error != nil { + return nil, c.convertRPCError(submitTransactionResponse.Error) + } + + return submitTransactionResponse, nil +} diff --git a/infrastructure/network/rpcclient/rpc_submit_block.go b/infrastructure/network/rpcclient/rpc_submit_block.go new file mode 100644 index 000000000..8e91efcf4 --- /dev/null +++ b/infrastructure/network/rpcclient/rpc_submit_block.go @@ -0,0 +1,29 @@ +package rpcclient + +import ( + "encoding/hex" + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/util" +) + +// SubmitBlock sends an RPC request respective to the function's name and returns the RPC server's response +func (c *RPCClient) SubmitBlock(block *util.Block) error { + blockBytes, err := block.Bytes() + if err != nil { + return err + } + blockHex := hex.EncodeToString(blockBytes) + err = c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewSubmitBlockRequestMessage(blockHex)) + if err != nil { + return err + } + response, err := c.route(appmessage.CmdSubmitBlockResponseMessage).DequeueWithTimeout(c.timeout) + if err != nil { + return err + } + submitBlockResponse := response.(*appmessage.SubmitBlockResponseMessage) + if submitBlockResponse.Error != nil { + return c.convertRPCError(submitBlockResponse.Error) + } + return nil +} diff --git a/infrastructure/network/rpcclient/rpcclient.go b/infrastructure/network/rpcclient/rpcclient.go new file mode 100644 index 000000000..9ed3a05fa --- /dev/null +++ b/infrastructure/network/rpcclient/rpcclient.go @@ -0,0 +1,76 @@ +package rpcclient + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/infrastructure/logger" + routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" + "github.com/kaspanet/kaspad/infrastructure/network/rpcclient/grpcclient" + "github.com/kaspanet/kaspad/util/panics" + "github.com/pkg/errors" + "time" +) + +const defaultTimeout = 30 * time.Second + +// RPCClient is an RPC client +type RPCClient struct { + *grpcclient.GRPCClient + + rpcAddress string + rpcRouter *rpcRouter + + timeout time.Duration +} + +// NewRPCClient creates a new RPC client +func NewRPCClient(rpcAddress string) (*RPCClient, error) { + rpcClient, err := grpcclient.Connect(rpcAddress) + if err != nil { + return nil, errors.Wrapf(err, "error connecting to address %s", rpcClient) + } + rpcRouter, err := buildRPCRouter() + if err != nil { + return nil, errors.Wrapf(err, "error creating the RPC router") + } + rpcClient.AttachRouter(rpcRouter.router) + + log.Infof("Connected to server %s", rpcAddress) + + return &RPCClient{ + GRPCClient: rpcClient, + rpcAddress: rpcAddress, + rpcRouter: rpcRouter, + timeout: defaultTimeout, + }, nil +} + +// SetTimeout sets the timeout by which to wait for RPC responses +func (c *RPCClient) SetTimeout(timeout time.Duration) { + c.timeout = timeout +} + +// Close closes the RPC client +func (c *RPCClient) Close() { + c.rpcRouter.router.Close() +} + +// Address returns the address the RPC client connected to +func (c *RPCClient) Address() string { + return c.rpcAddress +} + +func (c *RPCClient) route(command appmessage.MessageCommand) *routerpkg.Route { + return c.rpcRouter.routes[command] +} + +func (c *RPCClient) convertRPCError(rpcError *appmessage.RPCError) error { + return errors.Errorf("received error response from RPC: %s", rpcError.Message) +} + +// SetLogger uses a specified Logger to output package logging info +func (c *RPCClient) SetLogger(backend *logger.Backend, level logger.Level) { + const logSubsystem = "RPCC" + log = backend.Logger(logSubsystem) + log.SetLevel(level) + spawn = panics.GoroutineWrapperFunc(log) +} diff --git a/infrastructure/network/rpcclient/rpcrouter.go b/infrastructure/network/rpcclient/rpcrouter.go new file mode 100644 index 000000000..e826d22b7 --- /dev/null +++ b/infrastructure/network/rpcclient/rpcrouter.go @@ -0,0 +1,32 @@ +package rpcclient + +import ( + "github.com/kaspanet/kaspad/app/appmessage" + routerpkg "github.com/kaspanet/kaspad/infrastructure/network/netadapter/router" +) + +type rpcRouter struct { + router *routerpkg.Router + routes map[appmessage.MessageCommand]*routerpkg.Route +} + +func buildRPCRouter() (*rpcRouter, error) { + router := routerpkg.NewRouter() + routes := make(map[appmessage.MessageCommand]*routerpkg.Route, len(appmessage.RPCMessageCommandToString)) + for messageType := range appmessage.RPCMessageCommandToString { + route, err := router.AddIncomingRoute([]appmessage.MessageCommand{messageType}) + if err != nil { + return nil, err + } + routes[messageType] = route + } + + return &rpcRouter{ + router: router, + routes: routes, + }, nil +} + +func (r *rpcRouter) outgoingRoute() *routerpkg.Route { + return r.router.OutgoingRoute() +} diff --git a/testing/integration/64_incoming_connections_test.go b/testing/integration/64_incoming_connections_test.go index 3952182f0..edc3055ca 100644 --- a/testing/integration/64_incoming_connections_test.go +++ b/testing/integration/64_incoming_connections_test.go @@ -36,19 +36,19 @@ func Test64IncomingConnections(t *testing.T) { blockAddedWG := sync.WaitGroup{} blockAddedWG.Add(numBullies) for _, bully := range bullies { - err := bully.rpcClient.NotifyBlocks() - if err != nil { - t.Fatalf("Error from NotifyBlocks: %+v", err) - } - blockAdded := false - bully.rpcClient.onBlockAdded = func(header *appmessage.BlockHeader) { + onBlockAdded := func(_ *appmessage.BlockAddedNotificationMessage) { if blockAdded { t.Fatalf("Single bully reported block added twice") } blockAdded = true blockAddedWG.Done() } + + err := bully.rpcClient.RegisterForBlockAddedNotifications(onBlockAdded) + if err != nil { + t.Fatalf("Error from RegisterForBlockAddedNotifications: %+v", err) + } } _ = mineNextBlock(t, victim) diff --git a/testing/integration/basic_sync_test.go b/testing/integration/basic_sync_test.go index d788d0fe0..7488f1195 100644 --- a/testing/integration/basic_sync_test.go +++ b/testing/integration/basic_sync_test.go @@ -17,13 +17,13 @@ func TestIntegrationBasicSync(t *testing.T) { connect(t, appHarness2, appHarness3) app2OnBlockAddedChan := make(chan *appmessage.BlockHeader) - setOnBlockAddedHandler(t, appHarness2, func(header *appmessage.BlockHeader) { - app2OnBlockAddedChan <- header + setOnBlockAddedHandler(t, appHarness2, func(notification *appmessage.BlockAddedNotificationMessage) { + app2OnBlockAddedChan <- ¬ification.Block.Header }) app3OnBlockAddedChan := make(chan *appmessage.BlockHeader) - setOnBlockAddedHandler(t, appHarness3, func(header *appmessage.BlockHeader) { - app3OnBlockAddedChan <- header + setOnBlockAddedHandler(t, appHarness3, func(notification *appmessage.BlockAddedNotificationMessage) { + app3OnBlockAddedChan <- ¬ification.Block.Header }) block := mineNextBlock(t, appHarness1) diff --git a/testing/integration/connect_test.go b/testing/integration/connect_test.go index 7414c01df..9d13cf19f 100644 --- a/testing/integration/connect_test.go +++ b/testing/integration/connect_test.go @@ -6,7 +6,7 @@ import ( ) func connect(t *testing.T, incoming, outgoing *appHarness) { - err := outgoing.rpcClient.ConnectNode(incoming.p2pAddress) + err := outgoing.rpcClient.AddPeer(incoming.p2pAddress, false) if err != nil { t.Fatalf("Error connecting the nodes") } @@ -52,14 +52,14 @@ func isConnected(t *testing.T, appHarness1, appHarness2 *appHarness) bool { var incomingConnected, outgoingConnected bool app1ID, app2ID := appHarness1.app.P2PNodeID().String(), appHarness2.app.P2PNodeID().String() - for _, connectedPeer := range connectedPeerInfo1 { + for _, connectedPeer := range connectedPeerInfo1.Infos { if connectedPeer.ID == app2ID { incomingConnected = true break } } - for _, connectedPeer := range connectedPeerInfo2 { + for _, connectedPeer := range connectedPeerInfo2.Infos { if connectedPeer.ID == app1ID { outgoingConnected = true break diff --git a/testing/integration/ibd_test.go b/testing/integration/ibd_test.go index 4b3b8c82e..1fb26ca67 100644 --- a/testing/integration/ibd_test.go +++ b/testing/integration/ibd_test.go @@ -23,7 +23,7 @@ func TestIBD(t *testing.T) { blockAddedWG := sync.WaitGroup{} blockAddedWG.Add(numBlocks) receivedBlocks := 0 - setOnBlockAddedHandler(t, syncee, func(header *appmessage.BlockHeader) { + setOnBlockAddedHandler(t, syncee, func(_ *appmessage.BlockAddedNotificationMessage) { receivedBlocks++ blockAddedWG.Done() }) @@ -36,16 +36,16 @@ func TestIBD(t *testing.T) { case <-locks.ReceiveFromChanWhenDone(func() { blockAddedWG.Wait() }): } - tip1, err := syncer.rpcClient.GetSelectedTip() + tip1Hash, err := syncer.rpcClient.GetSelectedTipHash() if err != nil { t.Fatalf("Error getting tip for syncer") } - tip2, err := syncee.rpcClient.GetSelectedTip() + tip2Hash, err := syncee.rpcClient.GetSelectedTipHash() if err != nil { t.Fatalf("Error getting tip for syncee") } - if tip1.Hash != tip2.Hash { - t.Errorf("Tips of syncer: '%s' and syncee '%s' are not equal", tip1.Hash, tip2.Hash) + if tip1Hash.SelectedTipHash != tip2Hash.SelectedTipHash { + t.Errorf("Tips of syncer: '%s' and syncee '%s' are not equal", tip1Hash.SelectedTipHash, tip2Hash.SelectedTipHash) } } diff --git a/testing/integration/mining_test.go b/testing/integration/mining_test.go index ec37988e0..9de0c3ef8 100644 --- a/testing/integration/mining_test.go +++ b/testing/integration/mining_test.go @@ -1,11 +1,10 @@ package integration import ( + "github.com/kaspanet/kaspad/domain/mining" "math/rand" "testing" - clientpkg "github.com/kaspanet/kaspad/infrastructure/network/rpc/client" - "github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/util" "github.com/kaspanet/kaspad/util/daghash" @@ -32,14 +31,14 @@ func mineNextBlock(t *testing.T, harness *appHarness) *util.Block { t.Fatalf("Error getting block template: %+v", err) } - block, err := clientpkg.ConvertGetBlockTemplateResultToBlock(blockTemplate) + block, err := mining.ConvertGetBlockTemplateResultToBlock(blockTemplate) if err != nil { t.Fatalf("Error parsing blockTemplate: %s", err) } solveBlock(block) - err = harness.rpcClient.SubmitBlock(block, nil) + err = harness.rpcClient.SubmitBlock(block) if err != nil { t.Fatalf("Error submitting block: %s", err) } diff --git a/testing/integration/notifications_test.go b/testing/integration/notifications_test.go index 03303c7ec..368927955 100644 --- a/testing/integration/notifications_test.go +++ b/testing/integration/notifications_test.go @@ -6,10 +6,9 @@ import ( "github.com/kaspanet/kaspad/app/appmessage" ) -func setOnBlockAddedHandler(t *testing.T, harness *appHarness, handler func(header *appmessage.BlockHeader)) { - err := harness.rpcClient.NotifyBlocks() +func setOnBlockAddedHandler(t *testing.T, harness *appHarness, handler func(notification *appmessage.BlockAddedNotificationMessage)) { + err := harness.rpcClient.RegisterForBlockAddedNotifications(handler) if err != nil { - t.Fatalf("Error from NotifyBlocks: %s", err) + t.Fatalf("Error from RegisterForBlockAddedNotifications: %s", err) } - harness.rpcClient.onBlockAdded = handler } diff --git a/testing/integration/rpc_test.go b/testing/integration/rpc_test.go index 528e39562..3d07a1959 100644 --- a/testing/integration/rpc_test.go +++ b/testing/integration/rpc_test.go @@ -1,37 +1,24 @@ package integration import ( - "github.com/kaspanet/kaspad/app/appmessage" - "github.com/kaspanet/kaspad/util" - - rpcclient "github.com/kaspanet/kaspad/infrastructure/network/rpc/client" + "github.com/kaspanet/kaspad/infrastructure/network/rpcclient" + "time" ) -type rpcClient struct { - *rpcclient.Client - onBlockAdded func(*appmessage.BlockHeader) +const rpcTimeout = 1 * time.Second + +type testRPCClient struct { + *rpcclient.RPCClient } -func newRPCClient(rpcAddress string) (*rpcClient, error) { - client := &rpcClient{} - notificationHandlers := &rpcclient.NotificationHandlers{ - OnFilteredBlockAdded: func(height uint64, header *appmessage.BlockHeader, txs []*util.Tx) { - if client.onBlockAdded != nil { - client.onBlockAdded(header) - } - }, +func newTestRPCClient(rpcAddress string) (*testRPCClient, error) { + rpcClient, err := rpcclient.NewRPCClient(rpcAddress) + if err != nil { + return nil, err } + rpcClient.SetTimeout(rpcTimeout) - connConfig := &rpcclient.ConnConfig{ - Host: rpcAddress, - Endpoint: "ws", - User: rpcUser, - Pass: rpcPass, - DisableTLS: true, - RequestTimeout: defaultTimeout, - } - - var err error - client.Client, err = rpcclient.New(connConfig, notificationHandlers) - return client, err + return &testRPCClient{ + RPCClient: rpcClient, + }, nil } diff --git a/testing/integration/setup_test.go b/testing/integration/setup_test.go index 006dbc79d..e2f018c9e 100644 --- a/testing/integration/setup_test.go +++ b/testing/integration/setup_test.go @@ -11,7 +11,7 @@ import ( type appHarness struct { app *app.App - rpcClient *rpcClient + rpcClient *testRPCClient p2pAddress string rpcAddress string miningAddress string @@ -90,13 +90,14 @@ func standardSetup(t *testing.T) (appHarness1, appHarness2, appHarness3 *appHarn func setRPCClient(t *testing.T, harness *appHarness) { var err error - harness.rpcClient, err = newRPCClient(harness.rpcAddress) + harness.rpcClient, err = newTestRPCClient(harness.rpcAddress) if err != nil { t.Fatalf("Error getting RPC client %+v", err) } } func teardownHarness(t *testing.T, harness *appHarness) { + harness.rpcClient.Close() harness.app.Stop() err := harness.databaseContext.Close() diff --git a/testing/integration/tx_relay_test.go b/testing/integration/tx_relay_test.go index 5aa2e33b5..360347420 100644 --- a/testing/integration/tx_relay_test.go +++ b/testing/integration/tx_relay_test.go @@ -22,8 +22,8 @@ func TestTxRelay(t *testing.T) { connect(t, mediator, payee) payeeBlockAddedChan := make(chan *appmessage.BlockHeader) - setOnBlockAddedHandler(t, payee, func(header *appmessage.BlockHeader) { - payeeBlockAddedChan <- header + setOnBlockAddedHandler(t, payee, func(notification *appmessage.BlockAddedNotificationMessage) { + payeeBlockAddedChan <- ¬ification.Block.Header }) // skip the first block because it's paying to genesis script mineNextBlock(t, payer) @@ -39,10 +39,11 @@ func TestTxRelay(t *testing.T) { } tx := generateTx(t, secondBlock.CoinbaseTransaction().MsgTx(), payer, payee) - txID, err := payer.rpcClient.SendRawTransaction(tx, true) + response, err := payer.rpcClient.SubmitTransaction(tx) if err != nil { t.Fatalf("Error submitting transaction: %+v", err) } + txID := response.TxID txAddedToMempoolChan := make(chan struct{}) @@ -51,9 +52,9 @@ func TestTxRelay(t *testing.T) { defer ticker.Stop() for range ticker.C { - _, err := payee.rpcClient.GetMempoolEntry(txID.String()) + _, err := payee.rpcClient.GetMempoolEntry(txID) if err != nil { - if strings.Contains(err.Error(), "-32603: transaction is not in the pool") { + if strings.Contains(err.Error(), "transaction is not in the pool") { continue }