github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/neorpc/types.go (about) 1 /* 2 Package neorpc contains a set of types used for JSON-RPC communication with Neo servers. 3 It defines basic request/response types as well as a set of errors and additional 4 parameters used for specific requests/responses. 5 */ 6 package neorpc 7 8 import ( 9 "encoding/json" 10 "fmt" 11 "strings" 12 13 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 14 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 15 "github.com/nspcc-dev/neo-go/pkg/encoding/address" 16 "github.com/nspcc-dev/neo-go/pkg/util" 17 ) 18 19 const ( 20 // JSONRPCVersion is the only JSON-RPC protocol version supported. 21 JSONRPCVersion = "2.0" 22 ) 23 24 type ( 25 // Request represents JSON-RPC request. It's generic enough to be used in many 26 // generic JSON-RPC communication scenarios, yet at the same time it's 27 // tailored for NeoGo RPC Client needs. 28 Request struct { 29 // JSONRPC is the protocol version, only valid when it contains JSONRPCVersion. 30 JSONRPC string `json:"jsonrpc"` 31 // Method is the method being called. 32 Method string `json:"method"` 33 // Params is a set of method-specific parameters passed to the call. They 34 // can be anything as long as they can be marshaled to JSON correctly and 35 // used by the method implementation on the server side. While JSON-RPC 36 // technically allows it to be an object, all Neo calls expect params 37 // to be an array. 38 Params []any `json:"params"` 39 // ID is an identifier associated with this request. JSON-RPC itself allows 40 // any strings to be used for it as well, but NeoGo RPC client uses numeric 41 // identifiers. 42 ID uint64 `json:"id"` 43 } 44 45 // Header is a generic JSON-RPC 2.0 response header (ID and JSON-RPC version). 46 Header struct { 47 ID json.RawMessage `json:"id"` 48 JSONRPC string `json:"jsonrpc"` 49 } 50 51 // HeaderAndError adds an Error (that can be empty) to the Header, it's used 52 // to construct type-specific responses. 53 HeaderAndError struct { 54 Header 55 Error *Error `json:"error,omitempty"` 56 } 57 58 // Response represents a standard raw JSON-RPC 2.0 59 // response: http://www.jsonrpc.org/specification#response_object. 60 Response struct { 61 HeaderAndError 62 Result json.RawMessage `json:"result,omitempty"` 63 } 64 65 // Notification is a type used to represent wire format of events, they're 66 // special in that they look like requests but they don't have IDs and their 67 // "method" is actually an event name. 68 Notification struct { 69 JSONRPC string `json:"jsonrpc"` 70 Event EventID `json:"method"` 71 Payload []any `json:"params"` 72 } 73 74 // SignerWithWitness represents transaction's signer with the corresponding witness. 75 SignerWithWitness struct { 76 transaction.Signer 77 transaction.Witness 78 } 79 ) 80 81 // signerWithWitnessAux is an auxiliary struct for JSON marshalling. We need it because of 82 // DisallowUnknownFields JSON marshaller setting. 83 type signerWithWitnessAux struct { 84 Account string `json:"account"` 85 Scopes json.RawMessage `json:"scopes"` 86 AllowedContracts []util.Uint160 `json:"allowedcontracts,omitempty"` 87 AllowedGroups []*keys.PublicKey `json:"allowedgroups,omitempty"` 88 Rules []transaction.WitnessRule `json:"rules,omitempty"` 89 InvocationScript []byte `json:"invocation,omitempty"` 90 VerificationScript []byte `json:"verification,omitempty"` 91 } 92 93 // MarshalJSON implements the json.Marshaler interface. 94 func (s *SignerWithWitness) MarshalJSON() ([]byte, error) { 95 sc, err := s.Scopes.MarshalJSON() 96 if err != nil { 97 return nil, fmt.Errorf("failed to marshal scopes: %w", err) 98 } 99 signer := &signerWithWitnessAux{ 100 Account: `0x` + s.Account.StringLE(), 101 Scopes: sc, 102 AllowedContracts: s.AllowedContracts, 103 AllowedGroups: s.AllowedGroups, 104 Rules: s.Rules, 105 InvocationScript: s.InvocationScript, 106 VerificationScript: s.VerificationScript, 107 } 108 return json.Marshal(signer) 109 } 110 111 // UnmarshalJSON implements the json.Unmarshaler interface. 112 func (s *SignerWithWitness) UnmarshalJSON(data []byte) error { 113 aux := new(signerWithWitnessAux) 114 err := json.Unmarshal(data, aux) 115 if err != nil { 116 return fmt.Errorf("not a signer: %w", err) 117 } 118 if len(aux.AllowedContracts) > transaction.MaxAttributes { 119 return fmt.Errorf("invalid number of AllowedContracts: got %d, allowed %d at max", len(aux.AllowedContracts), transaction.MaxAttributes) 120 } 121 if len(aux.AllowedGroups) > transaction.MaxAttributes { 122 return fmt.Errorf("invalid number of AllowedGroups: got %d, allowed %d at max", len(aux.AllowedGroups), transaction.MaxAttributes) 123 } 124 if len(aux.Rules) > transaction.MaxAttributes { 125 return fmt.Errorf("invalid number of Rules: got %d, allowed %d at max", len(aux.Rules), transaction.MaxAttributes) 126 } 127 acc, err := util.Uint160DecodeStringLE(strings.TrimPrefix(aux.Account, "0x")) 128 if err != nil { 129 acc, err = address.StringToUint160(aux.Account) 130 } 131 if err != nil { 132 return fmt.Errorf("not a signer: %w", err) 133 } 134 var ( 135 jStr string 136 jByte byte 137 scopes transaction.WitnessScope 138 ) 139 if len(aux.Scopes) != 0 { 140 if err := json.Unmarshal(aux.Scopes, &jStr); err == nil { 141 scopes, err = transaction.ScopesFromString(jStr) 142 if err != nil { 143 return fmt.Errorf("failed to retrieve scopes from string: %w", err) 144 } 145 } else { 146 err := json.Unmarshal(aux.Scopes, &jByte) 147 if err != nil { 148 return fmt.Errorf("failed to unmarshal scopes from byte: %w", err) 149 } 150 scopes, err = transaction.ScopesFromByte(jByte) 151 if err != nil { 152 return fmt.Errorf("failed to retrieve scopes from byte: %w", err) 153 } 154 } 155 } 156 s.Signer = transaction.Signer{ 157 Account: acc, 158 Scopes: scopes, 159 AllowedContracts: aux.AllowedContracts, 160 AllowedGroups: aux.AllowedGroups, 161 Rules: aux.Rules, 162 } 163 s.Witness = transaction.Witness{ 164 InvocationScript: aux.InvocationScript, 165 VerificationScript: aux.VerificationScript, 166 } 167 return nil 168 } 169 170 // EventID implements EventContainer interface and returns notification ID. 171 func (n *Notification) EventID() EventID { 172 return n.Event 173 } 174 175 // EventPayload implements EventContainer interface and returns notification 176 // object. 177 func (n *Notification) EventPayload() any { 178 return n.Payload[0] 179 }