Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polkadot cannot sign DynamicExtrinsic #401

Open
YuXiaoCoder opened this issue Sep 12, 2024 · 3 comments
Open

Polkadot cannot sign DynamicExtrinsic #401

YuXiaoCoder opened this issue Sep 12, 2024 · 3 comments

Comments

@YuXiaoCoder
Copy link

Error message: panic: creating payload: signed extension 'PrevalidateAttests' is not supported

Here's the sample code

func Transfer(keyPair signature.KeyringPair) {
	// This sample shows how to create a transaction to make a transfer from one an account to another.

	// Instantiate the API
	api, err := gsrpc.NewSubstrateAPI("wss://polkadot-rpc.dwellir.com")
	if err != nil {
		panic(err)
	}

	meta, err := api.RPC.State.GetMetadataLatest()
	if err != nil {
		panic(err)
	}

	bob, err := types.NewMultiAddressFromHexAccountID("0x1e635742bc09f1299c2934cbc46266a8c6b5756bc261fc0a970b6a51d1a4d053")
	if err != nil {
		panic(err)
	}

	// 0.1 unit of transfer
	bal, ok := new(big.Int).SetString("1000000000", 10)
	if !ok {
		panic(fmt.Errorf("failed to convert balance"))
	}

	c, err := types.NewCall(meta, "Balances.transfer_keep_alive", bob, types.NewUCompact(bal))
	if err != nil {
		panic(err)
	}

	ext := extrinsic.NewDynamicExtrinsic(&c)

	genesisHash, err := api.RPC.Chain.GetBlockHash(0)
	if err != nil {
		panic(err)
	}

	rv, err := api.RPC.State.GetRuntimeVersionLatest()
	if err != nil {
		panic(err)
	}

	key, err := types.CreateStorageKey(meta, "System", "Account", keyPair.PublicKey)
	if err != nil {
		panic(err)
	}

	var accountInfo types.AccountInfo
	ok, err = api.RPC.State.GetStorageLatest(key, &accountInfo)
	if err != nil || !ok {
		panic(err)
	}

	err = ext.Sign(
		keyPair,
		meta,
		extrinsic.WithEra(types.ExtrinsicEra{IsImmortalEra: true}, genesisHash),
		extrinsic.WithNonce(types.NewUCompactFromUInt(uint64(accountInfo.Nonce))),
		extrinsic.WithTip(types.NewUCompactFromUInt(0)),
		extrinsic.WithSpecVersion(rv.SpecVersion),
		extrinsic.WithTransactionVersion(rv.TransactionVersion),
		extrinsic.WithGenesisHash(genesisHash),
		extrinsic.WithMetadataMode(extensions.CheckMetadataModeDisabled, extensions.CheckMetadataHash{Hash: types.NewEmptyOption[types.H256]()}),
		extrinsic.WithAssetID(types.NewEmptyOption[types.AssetID]()),
	)

	if err != nil {
		panic(err)
	}

	encodedExt, err := codec.EncodeToHex(ext)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Ext - %s\n", encodedExt)

	sub, err := api.RPC.Author.SubmitAndWatchDynamicExtrinsic(ext)
	if err != nil {
		panic(err)
	}
	defer sub.Unsubscribe()

	for {
		select {
		case st := <-sub.Chan():
			extStatus, _ := st.MarshalJSON()
			fmt.Printf("Status for transaction - %s\n", string(extStatus))
		case err := <-sub.Err():
			panic(err)
		}
	}
}
@YuXiaoCoder
Copy link
Author

YuXiaoCoder commented Sep 12, 2024

Using the previous approach is also not feasible

func Transfer(keyingPair signature.KeyringPair) {
	// Instantiate the API
	api, err := gsrpc.NewSubstrateAPI("wss://polkadot-rpc.dwellir.com")
	if err != nil {
		panic(err)
	}

	meta, err := api.RPC.State.GetMetadataLatest()
	if err != nil {
		panic(err)
	}

	bob, err := types.NewMultiAddressFromHexAccountID("0x1e635742bc09f1299c2934cbc46266a8c6b5756bc261fc0a970b6a51d1a4d053")
	if err != nil {
		panic(err)
	}

	// 0.1 unit of transfer
	bal, ok := new(big.Int).SetString("1000000000", 10)
	if !ok {
		panic(fmt.Errorf("failed to convert balance"))
	}

	c, err := types.NewCall(meta, "Balances.transfer_keep_alive", bob, types.NewUCompact(bal))
	if err != nil {
		panic(err)
	}

	// Extrinsic
	ext := types.NewExtrinsic(c)

	genesisHash, err := api.RPC.Chain.GetBlockHash(0)
	if err != nil {
		panic(err)
	}

	rv, err := api.RPC.State.GetRuntimeVersionLatest()
	if err != nil {
		panic(err)
	}

	key, err := types.CreateStorageKey(meta, "System", "Account", keyingPair.PublicKey)
	if err != nil {
		panic(err)
	}

	var accountInfo types.AccountInfo
	ok, err = api.RPC.State.GetStorageLatest(key, &accountInfo)
	if err != nil || !ok {
		panic(err)
	}

	encodedExt, err := codec.EncodeToHex(ext)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Ext - %s\n", encodedExt)

	options := types.SignatureOptions{
		BlockHash:          genesisHash,
		Era:                types.ExtrinsicEra{IsMortalEra: false},
		GenesisHash:        genesisHash,
		Nonce:              types.NewUCompactFromUInt(uint64(accountInfo.Nonce)),
		SpecVersion:        rv.SpecVersion,
		Tip:                types.NewUCompactFromUInt(0),
		TransactionVersion: rv.TransactionVersion,
	}

	err = ext.Sign(keyingPair, options)
	if err != nil {
		panic(err)
	}

	txHash, err := api.RPC.Author.SubmitExtrinsic(ext)
	if err != nil {
		panic(err)
	}
	fmt.Println(txHash.Hex())
}

@cdamian
Copy link
Contributor

cdamian commented Sep 12, 2024

@YuXiaoCoder Please try using the latest version of dynamic extrinsic signing, and by using these extensions when signing in your first example:

err = ext.Sign(
		keyPair,
		meta,
		extrinsic.WithEra(types.ExtrinsicEra{IsImmortalEra: true}, genesisHash),
		extrinsic.WithNonce(types.NewUCompactFromUInt(uint64(accountInfo.Nonce))),
		extrinsic.WithTip(types.NewUCompactFromUInt(0)),
		extrinsic.WithSpecVersion(rv.SpecVersion),
		extrinsic.WithTransactionVersion(rv.TransactionVersion),
		extrinsic.WithGenesisHash(genesisHash),
		extrinsic.WithMetadataMode(extensions.CheckMetadataModeDisabled, extensions.CheckMetadataHash{Hash: types.NewEmptyOption[types.H256]()}),
	)

@1llusion1st
Copy link
Contributor

Hello everyone. My code stopped to work here is an example. (westend network)
Balances.transfer not exists anymore so I've changed it to Balances.transfer_keep_alive
but I've got such error:

api: &{0x40000a4c80 0x40000ae120}
14
Ext - 0x490284009e3ff1495eed8a9793d20e884fdf6fe1e3e2b35490412957f594fd32721ae601011a85a84b149f590291144945b1845e2152a42a70a09731c36c07013ac2e4022010f9306e6b029ff43a6a29738f8b536a5b4fc2deb468fcd9c335ae517ce88a800000690200000403002c6787a3734fef5484c765ffbddeb5bbb947fd0f50d04b25bb266ee474cdb12e070010a5d4e8
panic: Verification Error: Runtime error: Execution failed: Execution aborted due to trap: wasm trap: wasm `unreachable` instruction executed
        WASM backtrace:
        error while executing at wasm backtrace:
            0: 0xaaa23 - westend_runtime.wasm!rust_begin_unwind
            1: 0x1111f - westend_runtime.wasm!core::panicking::panic_fmt::hde8b7aa66e2831e1
            2: 0x305f9c - westend_runtime.wasm!TaggedTransactionQueue_validate_transaction
package main

import (
	"bytes"
	"errors"
	"fmt"
	"math/big"

	gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4"
	"github.com/centrifuge/go-substrate-rpc-client/v4/signature"
	"github.com/centrifuge/go-substrate-rpc-client/v4/types"
	"github.com/centrifuge/go-substrate-rpc-client/v4/types/codec"
	"github.com/centrifuge/go-substrate-rpc-client/v4/types/extrinsic"
	"github.com/centrifuge/go-substrate-rpc-client/v4/types/extrinsic/extensions"
	"github.com/mr-tron/base58"
	"github.com/shopspring/decimal"
	"golang.org/x/crypto/blake2b"
)

func main() {
	opts := types.SerDeOptions{NoPalletIndices: true}
	types.SetSerDeOptions(opts)

	a, err := gsrpc.NewSubstrateAPI("wss://westend-rpc.polkadot.io")
	if err != nil {
		panic(err)
	}

	fmt.Printf("api: %v\n", a)

	to := "5D4vieYSoM8BARF3uD2dfuWqGtQTsh7X4DJBfsxxLPsvzCAT"

	krFrom, err := signature.KeyringPairFromSecret("0xbe767cd48f3fda1535ca470506f1a5db5f8c12466951cd2164ef205bc2a628df", uint16(42))
	if err != nil {
		panic(err)
	}
	toNetwork, toAddrPublic, err := DecodeAddress(to)
	if err != nil || toNetwork != uint8(42) {
		panic(err)
	}

	// Decimals for mainnet - 10, testnet - 12
	decimals := 12

	coef := decimal.NewFromInt(10).Pow(decimal.NewFromInt32(int32(decimals)))

	amount := decimal.RequireFromString("2.0")
	amountAsBitInt := amount.Mul(coef).BigInt()

	amountAsBitInt = big.NewInt(1000000000000)

	meta, err := a.RPC.State.GetMetadataLatest()
	if err != nil {
		panic(err)
	}

	fmt.Println(meta.Version)

	rv, err := a.RPC.State.GetRuntimeVersionLatest()
	if err != nil {
		panic(err)
	}

	toMultiAddress, err := types.NewMultiAddressFromAccountID(toAddrPublic)
	if err != nil {
		panic(err)
	}

	c, err := types.NewCall(meta,
		"Balances.transfer_keep_alive", toMultiAddress, types.NewUCompact(amountAsBitInt))
	if err != nil {
		panic(err)
	}

	ext := extrinsic.NewDynamicExtrinsic(&c)

	genesisHash, err := a.RPC.Chain.GetBlockHash(0)
	if err != nil {
		panic(err)
	}

	key, err := types.CreateStorageKey(meta, "System", "Account", krFrom.PublicKey)
	if err != nil {
		panic(err)
	}

	var accountInfo types.AccountInfo
	ok, err := a.RPC.State.GetStorageLatest(key, &accountInfo)
	if err != nil || !ok {
		if err != nil {
			panic(err)
		} else {
			panic(errors.New("no account"))
		}
	}

	nonce := uint32(accountInfo.Nonce)

	// Sign the transaction using Alice's default account
	err = ext.Sign(
		krFrom, meta,
		extrinsic.WithGenesisHash(genesisHash),
		extrinsic.WithNonce(types.NewUCompactFromUInt(uint64(nonce))),
		extrinsic.WithTip(types.NewUCompactFromUInt(0)),
		extrinsic.WithSpecVersion(rv.SpecVersion),
		extrinsic.WithTransactionVersion(rv.TransactionVersion),
		extrinsic.WithMetadataMode(extensions.CheckMetadataModeDisabled, extensions.CheckMetadataHash{Hash: types.NewEmptyOption[types.H256]()}),
		extrinsic.WithAssetID(types.NewEmptyOption[types.AssetID]()),
		extrinsic.WithEra(types.ExtrinsicEra{IsMortalEra: true}, genesisHash))
	if err != nil {
		panic(err)
	}

	encodedExt, err := codec.EncodeToHex(ext)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Ext - %s\n", encodedExt)

	sub, err := a.RPC.Author.SubmitAndWatchDynamicExtrinsic(ext)
	if err != nil {
		panic(err)
	}

	defer sub.Unsubscribe()

	extInfo := <-sub.Chan()

	tx, _ := extInfo.MarshalJSON()

	fmt.Printf("submitted extrinsic: %s\n", tx)
}

func DecodeAddress(address string) (network byte, public []byte, err error) {
	decoded, err := base58.Decode(address)
	if err != nil {
		return 0, nil, err
	}
	if len(decoded) < (1 + 32 + 2) {
		return 0, nil, fmt.Errorf("wrong address")
	}
	networkWithPublic := decoded[:1+32]
	//fmt.Println("networkWithPublic: ", hex.EncodeToString(networkWithPublic))
	origCheckSum := decoded[1+32:]
	//fmt.Println("origCheckSum: ", hex.EncodeToString(origCheckSum))
	cs, err := ss58Checksum(networkWithPublic)
	if err != nil {
		return 0, nil, err
	}
	//fmt.Println("checsum:", hex.EncodeToString(cs))
	checkSumToCompare := cs[:2]

	//fmt.Println("checsumToCompare: ", hex.EncodeToString(checkSumToCompare))
	if !bytes.Equal(origCheckSum, checkSumToCompare) {
		return 0, nil, fmt.Errorf("wrong checksum")
	}

	return decoded[0], decoded[1 : 1+32], nil
}

func ss58Checksum(data []byte) ([]byte, error) {
	hasher, err := blake2b.New(64, nil)
	if err != nil {
		return nil, err
	}

	_, err = hasher.Write([]byte(ss58Prefix))
	if err != nil {
		return nil, err
	}

	_, err = hasher.Write(data)
	if err != nil {
		return nil, err
	}

	return hasher.Sum(nil), nil
}

const (
	ss58Prefix = "SS58PRE"
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants