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  }