github.com/stafiprotocol/go-substrate-rpc-client@v1.4.7/types/extrinsic_multi.go (about) 1 // Go Substrate RPC Client (GSRPC) provides APIs and types around Polkadot and any Substrate-based chain RPC calls 2 // 3 // Copyright 2020 Stafi Protocol 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package types 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "fmt" 23 "math/big" 24 "strings" 25 26 "github.com/stafiprotocol/go-substrate-rpc-client/pkg/scale" 27 "github.com/stafiprotocol/go-substrate-rpc-client/signature" 28 ) 29 30 // Extrinsic is a piece of Args bundled into a block that expresses something from the "external" (i.e. off-chain) 31 // world. There are, broadly speaking, two types of extrinsic: transactions (which tend to be signed) and 32 // inherents (which don't). 33 type ExtrinsicMulti struct { 34 // Version is the encoded version flag (which encodes the raw transaction version and signing information in one byte) 35 Version byte 36 // Signature is the ExtrinsicSignatureV4, it's presence depends on the Version flag 37 Signature ExtrinsicMultiSignatureV4 38 // Method is the call this extrinsic wraps 39 Method Call 40 } 41 42 // NewExtrinsic creates a new Extrinsic from the provided Call 43 func NewExtrinsicMulti(c Call) ExtrinsicMulti { 44 return ExtrinsicMulti{ 45 Version: ExtrinsicVersion4, 46 Method: c, 47 } 48 } 49 50 // UnmarshalJSON fills Extrinsic with the JSON encoded byte array given by bz 51 func (e *ExtrinsicMulti) UnmarshalJSON(bz []byte) error { 52 var tmp string 53 if err := json.Unmarshal(bz, &tmp); err != nil { 54 return err 55 } 56 57 // HACK 11 Jan 2019 - before https://github.com/paritytech/substrate/pull/1388 58 // extrinsics didn't have the length, cater for both approaches. This is very 59 // inconsistent with any other `Vec<u8>` implementation 60 var l UCompact 61 err := DecodeFromHexString(tmp, &l) 62 if err != nil { 63 return err 64 } 65 66 prefix, err := EncodeToHexString(l) 67 if err != nil { 68 return err 69 } 70 71 // determine whether length prefix is there 72 if strings.HasPrefix(tmp, prefix) { 73 return DecodeFromHexString(tmp, e) 74 } 75 76 // not there, prepend with compact encoded length prefix 77 dec, err := HexDecodeString(tmp) 78 if err != nil { 79 return err 80 } 81 length := NewUCompactFromUInt(uint64(len(dec))) 82 bprefix, err := EncodeToBytes(length) 83 if err != nil { 84 return err 85 } 86 prefixed := append(bprefix, dec...) 87 return DecodeFromBytes(prefixed, e) 88 } 89 90 // MarshalJSON returns a JSON encoded byte array of Extrinsic 91 func (e ExtrinsicMulti) MarshalJSON() ([]byte, error) { 92 s, err := EncodeToHexString(e) 93 if err != nil { 94 return nil, err 95 } 96 return json.Marshal(s) 97 } 98 99 // IsSigned returns true if the extrinsic is signed 100 func (e ExtrinsicMulti) IsSigned() bool { 101 return e.Version&ExtrinsicBitSigned == ExtrinsicBitSigned 102 } 103 104 // Type returns the raw transaction version (not flagged with signing information) 105 func (e ExtrinsicMulti) Type() uint8 { 106 return e.Version & ExtrinsicUnmaskVersion 107 } 108 109 // Sign adds a signature to the extrinsic 110 func (e *ExtrinsicMulti) Sign(signer signature.KeyringPair, o SignatureOptions) error { 111 if e.Type() != ExtrinsicVersion4 { 112 return fmt.Errorf("unsupported extrinsic version: %v (isSigned: %v, type: %v)", e.Version, e.IsSigned(), e.Type()) 113 } 114 115 mb, err := EncodeToBytes(e.Method) 116 if err != nil { 117 return err 118 } 119 120 era := o.Era 121 if !o.Era.IsMortalEra { 122 era = ExtrinsicEra{IsImmortalEra: true} 123 } 124 125 payload := ExtrinsicPayloadV4{ 126 ExtrinsicPayloadV3: ExtrinsicPayloadV3{ 127 Method: mb, 128 Era: era, 129 Nonce: o.Nonce, 130 Tip: o.Tip, 131 SpecVersion: o.SpecVersion, 132 GenesisHash: o.GenesisHash, 133 BlockHash: o.BlockHash, 134 }, 135 TransactionVersion: o.TransactionVersion, 136 } 137 138 sig, err := payload.Sign(signer) 139 if err != nil { 140 return err 141 } 142 143 signerPubKey := NewMultiAddressFromAccountID(signer.PublicKey) 144 extSig := ExtrinsicMultiSignatureV4{ 145 Signer: signerPubKey, 146 Signature: MultiSignature{IsSr25519: true, AsSr25519: sig}, 147 Era: era, 148 Nonce: o.Nonce, 149 Tip: o.Tip, 150 } 151 152 e.Signature = extSig 153 154 // mark the extrinsic as signed 155 e.Version |= ExtrinsicBitSigned 156 157 return nil 158 } 159 160 func (e *ExtrinsicMulti) Decode(decoder scale.Decoder) error { 161 // compact length encoding (1, 2, or 4 bytes) (may not be there for Extrinsics older than Jan 11 2019) 162 _, err := decoder.DecodeUintCompact() 163 if err != nil { 164 return err 165 } 166 167 // version, signature bitmask (1 byte) 168 err = decoder.Decode(&e.Version) 169 if err != nil { 170 return err 171 } 172 173 // signature 174 if e.IsSigned() { 175 if e.Type() != ExtrinsicVersion4 { 176 return fmt.Errorf("unsupported extrinsic version: %v (isSigned: %v, type: %v)", e.Version, e.IsSigned(), 177 e.Type()) 178 } 179 180 err = decoder.Decode(&e.Signature) 181 if err != nil { 182 return err 183 } 184 } 185 186 // call 187 err = decoder.Decode(&e.Method) 188 if err != nil { 189 return err 190 } 191 192 return nil 193 } 194 195 func (e ExtrinsicMulti) Encode(encoder scale.Encoder) error { 196 if e.Type() != ExtrinsicVersion4 { 197 return fmt.Errorf("unsupported extrinsic version: %v (isSigned: %v, type: %v)", e.Version, e.IsSigned(), 198 e.Type()) 199 } 200 201 // create a temporary buffer that will receive the plain encoded transaction (version, signature (optional), 202 // method/call) 203 var bb = bytes.Buffer{} 204 tempEnc := scale.NewEncoder(&bb) 205 206 // encode the version of the extrinsic 207 err := tempEnc.Encode(e.Version) 208 if err != nil { 209 return err 210 } 211 212 // encode the signature if signed 213 if e.IsSigned() { 214 err = tempEnc.Encode(e.Signature) 215 if err != nil { 216 return err 217 } 218 } 219 220 // encode the method 221 err = tempEnc.Encode(e.Method) 222 if err != nil { 223 return err 224 } 225 226 // take the temporary buffer to determine length, write that as prefix 227 eb := bb.Bytes() 228 err = encoder.EncodeUintCompact(*big.NewInt(0).SetUint64(uint64(len(eb)))) 229 if err != nil { 230 return err 231 } 232 233 // write the actual encoded transaction 234 err = encoder.Write(eb) 235 if err != nil { 236 return err 237 } 238 239 return nil 240 }