2022-07-22 16:09:29 +00:00
|
|
|
/*
|
|
|
|
Package neorpc contains a set of types used for JSON-RPC communication with Neo servers.
|
|
|
|
It defines basic request/response types as well as a set of errors and additional
|
|
|
|
parameters used for specific requests/responses.
|
|
|
|
*/
|
|
|
|
package neorpc
|
2020-01-14 12:02:38 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2020-08-06 14:44:08 +00:00
|
|
|
"fmt"
|
2022-07-07 14:41:01 +00:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2020-01-14 12:02:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// JSONRPCVersion is the only JSON-RPC protocol version supported.
|
|
|
|
JSONRPCVersion = "2.0"
|
|
|
|
)
|
|
|
|
|
2022-07-07 14:41:01 +00:00
|
|
|
type (
|
2022-07-22 16:09:29 +00:00
|
|
|
// Request represents JSON-RPC request. It's generic enough to be used in many
|
2022-07-07 15:33:23 +00:00
|
|
|
// generic JSON-RPC communication scenarios, yet at the same time it's
|
|
|
|
// tailored for NeoGo RPC Client needs.
|
2022-07-22 16:09:29 +00:00
|
|
|
Request struct {
|
2022-07-07 15:33:23 +00:00
|
|
|
// JSONRPC is the protocol version, only valid when it contains JSONRPCVersion.
|
|
|
|
JSONRPC string `json:"jsonrpc"`
|
|
|
|
// Method is the method being called.
|
|
|
|
Method string `json:"method"`
|
|
|
|
// Params is a set of method-specific parameters passed to the call. They
|
|
|
|
// can be anything as long as they can be marshaled to JSON correctly and
|
|
|
|
// used by the method implementation on the server side. While JSON-RPC
|
|
|
|
// technically allows it to be an object, all Neo calls expect params
|
|
|
|
// to be an array.
|
2023-04-03 10:34:24 +00:00
|
|
|
Params []any `json:"params"`
|
2022-07-07 15:33:23 +00:00
|
|
|
// ID is an identifier associated with this request. JSON-RPC itself allows
|
|
|
|
// any strings to be used for it as well, but NeoGo RPC client uses numeric
|
|
|
|
// identifiers.
|
|
|
|
ID uint64 `json:"id"`
|
|
|
|
}
|
|
|
|
|
2022-07-22 16:09:29 +00:00
|
|
|
// Header is a generic JSON-RPC 2.0 response header (ID and JSON-RPC version).
|
|
|
|
Header struct {
|
|
|
|
ID json.RawMessage `json:"id"`
|
|
|
|
JSONRPC string `json:"jsonrpc"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// HeaderAndError adds an Error (that can be empty) to the Header, it's used
|
|
|
|
// to construct type-specific responses.
|
|
|
|
HeaderAndError struct {
|
|
|
|
Header
|
|
|
|
Error *Error `json:"error,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Raw represents a standard raw JSON-RPC 2.0
|
|
|
|
// response: http://www.jsonrpc.org/specification#response_object.
|
|
|
|
Response struct {
|
|
|
|
HeaderAndError
|
|
|
|
Result json.RawMessage `json:"result,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Notification is a type used to represent wire format of events, they're
|
|
|
|
// special in that they look like requests but they don't have IDs and their
|
|
|
|
// "method" is actually an event name.
|
|
|
|
Notification struct {
|
2023-04-03 10:34:24 +00:00
|
|
|
JSONRPC string `json:"jsonrpc"`
|
|
|
|
Event EventID `json:"method"`
|
|
|
|
Payload []any `json:"params"`
|
2022-07-22 16:09:29 +00:00
|
|
|
}
|
|
|
|
|
2022-07-07 14:41:01 +00:00
|
|
|
// SignerWithWitness represents transaction's signer with the corresponding witness.
|
|
|
|
SignerWithWitness struct {
|
|
|
|
transaction.Signer
|
|
|
|
transaction.Witness
|
|
|
|
}
|
|
|
|
)
|
2020-10-26 17:22:20 +00:00
|
|
|
|
2022-07-07 14:41:01 +00:00
|
|
|
// signerWithWitnessAux is an auxiliary struct for JSON marshalling. We need it because of
|
|
|
|
// DisallowUnknownFields JSON marshaller setting.
|
|
|
|
type signerWithWitnessAux struct {
|
|
|
|
Account string `json:"account"`
|
2023-07-12 05:33:26 +00:00
|
|
|
Scopes json.RawMessage `json:"scopes"`
|
2022-07-07 14:41:01 +00:00
|
|
|
AllowedContracts []util.Uint160 `json:"allowedcontracts,omitempty"`
|
|
|
|
AllowedGroups []*keys.PublicKey `json:"allowedgroups,omitempty"`
|
|
|
|
Rules []transaction.WitnessRule `json:"rules,omitempty"`
|
|
|
|
InvocationScript []byte `json:"invocation,omitempty"`
|
|
|
|
VerificationScript []byte `json:"verification,omitempty"`
|
2020-01-14 12:02:38 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// MarshalJSON implements the json.Marshaler interface.
|
2022-07-07 14:41:01 +00:00
|
|
|
func (s *SignerWithWitness) MarshalJSON() ([]byte, error) {
|
2023-07-12 05:33:26 +00:00
|
|
|
sc, err := s.Scopes.MarshalJSON()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to marshal scopes: %w", err)
|
|
|
|
}
|
2022-07-07 14:41:01 +00:00
|
|
|
signer := &signerWithWitnessAux{
|
2023-07-12 05:39:15 +00:00
|
|
|
Account: `0x` + s.Account.StringLE(),
|
2023-07-12 05:33:26 +00:00
|
|
|
Scopes: sc,
|
2022-07-07 14:41:01 +00:00
|
|
|
AllowedContracts: s.AllowedContracts,
|
|
|
|
AllowedGroups: s.AllowedGroups,
|
|
|
|
Rules: s.Rules,
|
|
|
|
InvocationScript: s.InvocationScript,
|
|
|
|
VerificationScript: s.VerificationScript,
|
2020-01-14 12:02:38 +00:00
|
|
|
}
|
2022-07-07 14:41:01 +00:00
|
|
|
return json.Marshal(signer)
|
2020-10-26 17:22:20 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
2022-07-07 14:41:01 +00:00
|
|
|
func (s *SignerWithWitness) UnmarshalJSON(data []byte) error {
|
|
|
|
aux := new(signerWithWitnessAux)
|
|
|
|
err := json.Unmarshal(data, aux)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("not a signer: %w", err)
|
2020-10-26 17:22:20 +00:00
|
|
|
}
|
2022-07-07 14:41:01 +00:00
|
|
|
acc, err := util.Uint160DecodeStringLE(strings.TrimPrefix(aux.Account, "0x"))
|
2020-10-26 17:22:20 +00:00
|
|
|
if err != nil {
|
2022-07-07 14:41:01 +00:00
|
|
|
acc, err = address.StringToUint160(aux.Account)
|
2020-10-26 17:22:20 +00:00
|
|
|
}
|
2022-07-07 14:41:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("not a signer: %w", err)
|
2020-10-26 17:22:20 +00:00
|
|
|
}
|
2023-07-12 05:33:26 +00:00
|
|
|
var (
|
|
|
|
jStr string
|
|
|
|
jByte byte
|
|
|
|
scopes transaction.WitnessScope
|
|
|
|
)
|
|
|
|
if len(aux.Scopes) != 0 {
|
|
|
|
if err := json.Unmarshal(aux.Scopes, &jStr); err == nil {
|
|
|
|
scopes, err = transaction.ScopesFromString(jStr)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to retrieve scopes from string: %w", err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err := json.Unmarshal(aux.Scopes, &jByte)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to unmarshal scopes from byte: %w", err)
|
|
|
|
}
|
|
|
|
scopes, err = transaction.ScopesFromByte(jByte)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to retrieve scopes from byte: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-07 14:41:01 +00:00
|
|
|
s.Signer = transaction.Signer{
|
|
|
|
Account: acc,
|
2023-07-12 05:33:26 +00:00
|
|
|
Scopes: scopes,
|
2022-07-07 14:41:01 +00:00
|
|
|
AllowedContracts: aux.AllowedContracts,
|
|
|
|
AllowedGroups: aux.AllowedGroups,
|
|
|
|
Rules: aux.Rules,
|
2020-10-26 17:22:20 +00:00
|
|
|
}
|
2022-07-07 14:41:01 +00:00
|
|
|
s.Witness = transaction.Witness{
|
|
|
|
InvocationScript: aux.InvocationScript,
|
|
|
|
VerificationScript: aux.VerificationScript,
|
2020-10-26 17:22:20 +00:00
|
|
|
}
|
|
|
|
return nil
|
2020-01-14 12:02:38 +00:00
|
|
|
}
|
2022-10-17 10:31:24 +00:00
|
|
|
|
|
|
|
// EventID implements EventContainer interface and returns notification ID.
|
|
|
|
func (n *Notification) EventID() EventID {
|
|
|
|
return n.Event
|
|
|
|
}
|
|
|
|
|
|
|
|
// EventPayload implements EventContainer interface and returns notification
|
|
|
|
// object.
|
2023-04-03 10:34:24 +00:00
|
|
|
func (n *Notification) EventPayload() any {
|
2022-10-17 10:31:24 +00:00
|
|
|
return n.Payload[0]
|
|
|
|
}
|