From c825e9fc7cde7984e2ff3201073b87311b9319cb Mon Sep 17 00:00:00 2001 From: Cosmin Damian <17934949+cdamian@users.noreply.github.com> Date: Wed, 3 Jul 2024 17:35:23 +0200 Subject: [PATCH] extrinsic: Add more comments --- types/extrinsic/extrinsic.go | 15 ++++++++++----- types/extrinsic/options.go | 9 +++++++++ types/extrinsic/payload.go | 28 ++++++++++++++++++++++++++-- types/extrinsic/signature.go | 13 +++++-------- 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/types/extrinsic/extrinsic.go b/types/extrinsic/extrinsic.go index d87679bb2..c500185f0 100644 --- a/types/extrinsic/extrinsic.go +++ b/types/extrinsic/extrinsic.go @@ -11,15 +11,19 @@ import ( "math/big" ) +// DynamicExtrinsic is an extrinsic type that can be used on chains that +// have a custom signed extension logic. type DynamicExtrinsic struct { - // Version is the encoded version flag (which encodes the raw transaction version and signing information in one byte) - Version byte + // Version is the encoded version flag (which encodes the raw transaction version + // and signing information in one byte). + Version byte + // Signature is the extrinsic signature. Signature *Signature // Method is the call this extrinsic wraps Method *types.Call } -// NewExtrinsic creates a new Extrinsic from the provided Call +// NewDynamicExtrinsic creates a new DynamicExtrinsic from the provided Call. func NewDynamicExtrinsic(c *types.Call) DynamicExtrinsic { return DynamicExtrinsic{ Version: types.ExtrinsicVersion4, @@ -27,7 +31,7 @@ func NewDynamicExtrinsic(c *types.Call) DynamicExtrinsic { } } -// MarshalJSON returns a JSON encoded byte array of Extrinsic +// MarshalJSON returns a JSON encoded byte array of DynamicExtrinsic. func (e DynamicExtrinsic) MarshalJSON() ([]byte, error) { s, err := codec.EncodeToHex(e) if err != nil { @@ -46,7 +50,7 @@ func (e DynamicExtrinsic) Type() uint8 { return e.Version & types.ExtrinsicUnmaskVersion } -// Sign adds a signature to the extrinsic +// Sign adds a signature to the extrinsic. func (e *DynamicExtrinsic) Sign(signer signature.KeyringPair, meta *types.Metadata, opts ...SigningOption) error { if e.Type() != types.ExtrinsicVersion4 { return fmt.Errorf("unsupported extrinsic version: %v (isSigned: %v, type: %v)", e.Version, e.IsSigned(), e.Type()) @@ -103,6 +107,7 @@ func (e DynamicExtrinsic) Encode(encoder scale.Encoder) error { return fmt.Errorf("unsupported extrinsic version: %v (isSigned: %v, type: %v)", e.Version, e.IsSigned(), e.Type()) } + var bb = bytes.Buffer{} tempEnc := scale.NewEncoder(&bb) diff --git a/types/extrinsic/options.go b/types/extrinsic/options.go index 1f33a946e..2c66b3f65 100644 --- a/types/extrinsic/options.go +++ b/types/extrinsic/options.go @@ -5,10 +5,13 @@ import ( "github.com/centrifuge/go-substrate-rpc-client/v4/types/extrinsic/extensions" ) +// SignedFieldValues is a map that holds a value for a particular SignedFieldName. type SignedFieldValues map[SignedFieldName]any +// SigningOption is the type used for providing values to a SignedFieldValues map. type SigningOption func(vals SignedFieldValues) +// WithEra returns a SigningOption that is used to add the era and block hash to a Payload. func WithEra(era types.ExtrinsicEra, blockHash types.Hash) SigningOption { return func(vals SignedFieldValues) { vals[EraSignedField] = era @@ -16,12 +19,14 @@ func WithEra(era types.ExtrinsicEra, blockHash types.Hash) SigningOption { } } +// WithNonce returns a SigningOption that is used to add the nonce to a Payload. func WithNonce(nonce types.UCompact) SigningOption { return func(vals SignedFieldValues) { vals[NonceSignedField] = nonce } } +// WithMetadataMode returns a SigningOption that is used to add the check metadata mode and hash to a Payload. func WithMetadataMode(mode extensions.CheckMetadataMode, metadataHash extensions.CheckMetadataHash) SigningOption { return func(vals SignedFieldValues) { vals[CheckMetadataHashModeSignedField] = mode @@ -29,24 +34,28 @@ func WithMetadataMode(mode extensions.CheckMetadataMode, metadataHash extensions } } +// WithTip returns a SigningOption that is used to add the tip to a Payload. func WithTip(tip types.UCompact) SigningOption { return func(vals SignedFieldValues) { vals[TipSignedField] = tip } } +// WithSpecVersion returns a SigningOption that is used to add the spec version to a Payload. func WithSpecVersion(specVersion types.U32) SigningOption { return func(vals SignedFieldValues) { vals[SpecVersionSignedField] = specVersion } } +// WithTransactionVersion returns a SigningOption that is used to add the transaction version to a Payload. func WithTransactionVersion(transactionVersion types.U32) SigningOption { return func(vals SignedFieldValues) { vals[TransactionVersionSignedField] = transactionVersion } } +// WithGenesisHash returns a SigningOption that is used to add the genesis hash to a Payload. func WithGenesisHash(genesisHash types.Hash) SigningOption { return func(vals SignedFieldValues) { vals[GenesisHashSignedField] = genesisHash diff --git a/types/extrinsic/payload.go b/types/extrinsic/payload.go index a085c2179..ca7204b7a 100644 --- a/types/extrinsic/payload.go +++ b/types/extrinsic/payload.go @@ -11,18 +11,33 @@ import ( "github.com/centrifuge/go-substrate-rpc-client/v4/types/extrinsic/extensions" ) +// SignedField represents a field used in the Payload. type SignedField struct { - Name SignedFieldName - Value any + // Name of the field, this is omitted when the extrinsic is signed. + Name SignedFieldName + + // Value of the field used when the extrinsic is signed. + Value any + + // Mutated is used to keep track of changes done to the field. Mutated bool } +// Payload holds the encoded types.Call and the fields that are used for generating +// the DynamicExtrinsic payload and signature. +// +// Notes - the ordering of the SignedFields and SignedExtraFields is the order in which they are provided in +// the metadata. type Payload struct { EncodedCall types.BytesBare SignedFields []*SignedField SignedExtraFields []*SignedField } +// Encode the call bytes and the signed fields in the order that is provided during creation +// from the metadata. +// +// The function also performs an extra check to ensure that all required fields were mutated. func (p *Payload) Encode(encoder scale.Encoder) error { if err := encoder.Encode(p.EncodedCall); err != nil { return fmt.Errorf("unable to encode method: %w", err) @@ -51,6 +66,8 @@ func (p *Payload) Encode(encoder scale.Encoder) error { return nil } +// MutateSignedFields is mutating the payload's SignedFields and SignedExtraFields +// based on the provided SignedFieldValues. func (p *Payload) MutateSignedFields(vals SignedFieldValues) error { if p == nil { return errors.New("payload is nil") @@ -92,6 +109,7 @@ func (p *Payload) Sign(signer signature.KeyringPair) (types.Signature, error) { return types.NewSignature(sig), err } +// SignedFieldName is the type used for representing a field name. type SignedFieldName string const ( @@ -107,8 +125,10 @@ const ( GenesisHashSignedField SignedFieldName = "genesis_hash" ) +// PayloadMutatorFn is the type used for mutating the Payload during creation. type PayloadMutatorFn func(payload *Payload) +// PayloadMutatorFns is a map that holds a PayloadMutatorFn for each supported signed extension. var PayloadMutatorFns = map[extensions.SignedExtensionName]PayloadMutatorFn{ extensions.CheckMortalitySignedExtension: func(payload *Payload) { payload.SignedFields = append(payload.SignedFields, &SignedField{ @@ -187,6 +207,10 @@ var PayloadMutatorFns = map[extensions.SignedExtensionName]PayloadMutatorFn{ extensions.PreBalanceTransferExtensionSignedExtension: func(payload *Payload) {}, } +// createPayload iterates over all signed extensions provided in the metadata and +// attempts to load and use a PayloadMutatorFn for each one. +// +// If a PayloadMutatorFn is not found for a specific signed extension, it means that it is not currently supported. func createPayload(meta *types.Metadata, encodedCall []byte) (*Payload, error) { payload := &Payload{ EncodedCall: encodedCall, diff --git a/types/extrinsic/signature.go b/types/extrinsic/signature.go index 8ae6e0bc8..b97d3e64e 100644 --- a/types/extrinsic/signature.go +++ b/types/extrinsic/signature.go @@ -6,12 +6,17 @@ import ( "github.com/centrifuge/go-substrate-rpc-client/v4/types" ) +// Signature holds all the relevant fields for an extrinsic signature. type Signature struct { Signer types.MultiAddress Signature types.MultiSignature SignedFields []*SignedField } +// Encode is encoding the Signer, Signature, and SignedFields. +// +// Note - the ordering of the SignedFields is the order in which they are provided in +// the metadata. func (s Signature) Encode(encoder scale.Encoder) error { if err := encoder.Encode(s.Signer); err != nil { return err @@ -29,11 +34,3 @@ func (s Signature) Encode(encoder scale.Encoder) error { return nil } - -func createSignature(signer types.MultiAddress, sig types.MultiSignature, signedFields []*SignedField) *Signature { - return &Signature{ - Signer: signer, - Signature: sig, - SignedFields: signedFields, - } -}