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

submit batch transaction #304

Closed
bhaagiKenpachi opened this issue Oct 19, 2022 · 23 comments
Closed

submit batch transaction #304

bhaagiKenpachi opened this issue Oct 19, 2022 · 23 comments

Comments

@bhaagiKenpachi
Copy link

I want to submit a batch tx which consists of 2 txs. One tx is system::remark and balances::transfer. Is there any function to submit batch tx?

@cdamian
Copy link
Contributor

cdamian commented Oct 19, 2022

Hi @bhaagiKenpachi,

Please see the below example:

func main() {
	api, err := gsrpc.NewSubstrateAPI("ws://localhost:9946")
	if err != nil {
		panic(err)
	}

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

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

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

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

	var accountInfo types.AccountInfo

	ok, err := api.RPC.State.GetStorageLatest(aliceStorageKey, &accountInfo)
	if err != nil || !ok {
		panic(err)
	}

	call1, err := types.NewCall(meta, "System.remark", []byte{})
	if err != nil {
		panic(err)
	}

	call2, err := types.NewCall(meta, "System.remark", []byte{})
	if err != nil {
		panic(err)
	}

	batchCall, err := types.NewCall(meta, "Utility.batch_all", []types.Call{call1, call2})
	if err != nil {
		panic(err)
	}

	ext := types.NewExtrinsic(batchCall)

	signOpts := types.SignatureOptions{
		BlockHash:          genesisHash, // using genesis since we're using immortal era
		Era:                types.ExtrinsicEra{IsMortalEra: false},
		GenesisHash:        genesisHash,
		Nonce:              types.NewUCompactFromUInt(uint64(accountInfo.Nonce)),
		SpecVersion:        rv.SpecVersion,
		Tip:                types.NewUCompactFromUInt(0),
		TransactionVersion: rv.TransactionVersion,
	}

	if err := ext.Sign(signature.TestKeyringPairAlice, signOpts); err != nil {
		panic(err)
	}

	sub, err := api.RPC.Author.SubmitAndWatchExtrinsic(ext)

	if err != nil {
		panic(err)
	}

	defer sub.Unsubscribe()

	select {
	case <-time.After(1 * time.Minute):
		panic("Timeout reached")
	case st := <-sub.Chan():
		extStatus, _ := st.MarshalJSON()
		fmt.Println("Done with status -", string(out))
	case err := <-sub.Err():
		panic(err)
	}
}

@bhaagiKenpachi
Copy link
Author

@cdamian Thanks for your quick response. Will try and let you know.

@bhaagiKenpachi
Copy link
Author

@cdamian In the code you got hash of block 0. Should I also do the same for any scenario or should I get latest block?

@cdamian
Copy link
Contributor

cdamian commented Oct 20, 2022

@bhaagiKenpachi for the genesis hash you always need the first block.

@bhaagiKenpachi
Copy link
Author

bhaagiKenpachi commented Oct 20, 2022

@cdamian So it should always be genesis hash at blockhash or I should include latest block hash near blockhash?

signOpts := types.SignatureOptions{
		BlockHash:          genesisHash, // using genesis since we're using immortal era
		Era:                types.ExtrinsicEra{IsMortalEra: false},
		GenesisHash:        genesisHash,
		Nonce:              types.NewUCompactFromUInt(uint64(accountInfo.Nonce)),
		SpecVersion:        rv.SpecVersion,
		Tip:                types.NewUCompactFromUInt(0),
		TransactionVersion: rv.TransactionVersion,
	}

is genesis hash field is compulsory in signature options?

@cdamian
Copy link
Contributor

cdamian commented Oct 20, 2022

@bhaagiKenpachi for signature options, yes.

@bhaagiKenpachi
Copy link
Author

@cdamian So it should always be genesis hash at blockhash or I should include latest block hash near blockhash?
What about above question?

signOpts := types.SignatureOptions{
		BlockHash:          genesisHash, // using genesis since we're using immortal era
		Era:                types.ExtrinsicEra{IsMortalEra: false},
		GenesisHash:        genesisHash,
		Nonce:              types.NewUCompactFromUInt(uint64(accountInfo.Nonce)),
		SpecVersion:        rv.SpecVersion,
		Tip:                types.NewUCompactFromUInt(0),
		TransactionVersion: rv.TransactionVersion,
	}

is genesis hash field is compulsory in signature options? ( YES)

@bhaagiKenpachi
Copy link
Author

@cdamian So it should always be genesis hash at blockhash or I should include latest block hash near blockhash?
What about above question?

@cdamian
Copy link
Contributor

cdamian commented Oct 20, 2022

@bhaagiKenpachi genesis hash is mandatory in the signature opts.

So it should always be genesis hash at blockhash or I should include latest block hash near blockhash?

If you are using immortal eras, that one should be the genesis hash. If you were using mortal eras then a different block hash would have to be used.

@bhaagiKenpachi
Copy link
Author

@cdamian I want to go with mortal tx for now. Can you suggest the what can be the changes?

@cdamian
Copy link
Contributor

cdamian commented Oct 20, 2022

@bhaagiKenpachi mortal eras are a WIP for us at this point - #286. We have yet to test the changes in this PR and eventually merge it. You are more than welcome to try it out and also contribute to it.

@bhaagiKenpachi
Copy link
Author

@cdamian why did you use storage here?

@cdamian
Copy link
Contributor

cdamian commented Oct 20, 2022

@cdamian why did you use storage here?

To retrieve the account info that is required in the signature opts.

@bhaagiKenpachi
Copy link
Author

@cdamian I have sent friend request on discord. Can you please accept?. For faster communication.

@mikiquantum
Copy link
Contributor

@bhaagiKenpachi lets try to keep this conversation as public as possible in case that other people run into the same questions later on.
For further support, please use the public channel on discord. Thanks!

@bhaagiKenpachi
Copy link
Author

bhaagiKenpachi commented Oct 21, 2022

@cdamian

package main

import (
	"fmt"
	gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4"
	"github.com/centrifuge/go-substrate-rpc-client/v4/signature"
	gsrpcTypes "github.com/centrifuge/go-substrate-rpc-client/v4/types"
	"github.com/centrifuge/go-substrate-rpc-client/v4/types/codec"
	"time"
)

const (
	endpoint = "ws://localhost:9944"
	westend  = "wss://westend-rpc.polkadot.io"
)

func main() {
	api, err := gsrpc.NewSubstrateAPI(westend)
	if err != nil {
		fmt.Errorf("error %w", err)
	}

	meta, err := api.RPC.State.GetMetadataLatest()

	if err != nil {
		fmt.Errorf("error %w", err)
	}

	genesisHash, err := api.RPC.Chain.GetBlockHash(0)
	if err != nil {
		fmt.Errorf("error %w", err)
	}

	kp, err := signature.KeyringPairFromSecret(mnemonic, 42)

	aliceStorageKey, err := gsrpcTypes.CreateStorageKey(meta, "System", "Account", kp.PublicKey)

	if err != nil {
		panic(err)
	}

	var accountInfo gsrpcTypes.AccountInfo
	ok, err := api.RPC.State.GetStorageLatest(aliceStorageKey, &accountInfo)

	if err != nil || !ok {
		panic(err)
	}

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

	memo := gsrpcTypes.NewData([]byte("memo:ADD:DOT.DOT:dojima1nh4y3gqxsn7ymm9t45zwsz3h8p9tm7pev8my62"))
	memoBytes, err := codec.Encode(memo)
	if err != nil {
		panic(err)
	}

	call1, err := gsrpcTypes.NewCall(meta, "System.remark", memoBytes)
	if err != nil {
		panic(err)
	}

	//keyringPair := signature.KeyringPair{Address: kp.Public().Hex(), PublicKey: kp.Public().Encode(), URI: mnemonic}

	if err != nil {
		panic(err)
	}
	dest, err := gsrpcTypes.NewAddressFromHexAccountID("0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48")

	if err != nil {
		panic(err)
	}

	call2, err := gsrpcTypes.NewCall(meta, "Balances.transfer", dest, gsrpcTypes.NewUCompactFromUInt(10000000000))
	if err != nil {
		panic(err)
	}

	batchCall, err := gsrpcTypes.NewCall(meta, "Utility.batch_all", []gsrpcTypes.Call{call1, call2})
	if err != nil {
		panic(err)
	}

	ext := gsrpcTypes.NewExtrinsic(batchCall)

	signOpts := gsrpcTypes.SignatureOptions{
		BlockHash:          genesisHash, // using genesis since we're using immortal era
		Era:                gsrpcTypes.ExtrinsicEra{IsMortalEra: false},
		GenesisHash:        genesisHash,
		Nonce:              gsrpcTypes.NewUCompactFromUInt(uint64(accountInfo.Nonce)),
		SpecVersion:        rv.SpecVersion,
		Tip:                gsrpcTypes.NewUCompactFromUInt(0),
		TransactionVersion: rv.TransactionVersion,
	}

	if err := ext.Sign(kp, signOpts); err != nil {
		panic(err)
	}

	sub, err := api.RPC.Author.SubmitAndWatchExtrinsic(ext)

	if err != nil {
		panic(err)
	}

	defer sub.Unsubscribe()

	select {
	case <-time.After(1 * time.Minute):
		panic("Timeout reached")
	case st := <-sub.Chan():
		extStatus, _ := st.MarshalJSON()
		fmt.Println("Done with status -", string(extStatus))
	case err := <-sub.Err():
		panic(err)
	}
}

panic: Verification Error: Runtime error: Execution failed: Execution aborted due to trap: wasm trap: wasm unreachable` instruction executed
WASM backtrace:

0: 0x4f6cd - <unknown>!rust_begin_unwind
1: 0x5777 - <unknown>!core::panicking::panic_fmt::h18b15be283411c65
2: 0x33227e - <unknown>!TaggedTransactionQueue_validate_transaction

goroutine 1 [running]:
main.main()
polka-scripts/polka_go/tx/main.go:106 +0x9f8

`

@cdamian
Copy link
Contributor

cdamian commented Oct 21, 2022

@bhaagiKenpachi try using U128 instead of UCompact in call2.

Also, please make sure you keep any credentials such as seeds/mnemonics safe, you don't have to share those for debugging since we can use our own accounts for testing.

@bhaagiKenpachi
Copy link
Author

@cdamian call2, err := gsrpcTypes.NewCall(meta, "Balances.transfer", dest, gsrpcTypes.NewU128(*big.NewInt(100000000)))

Still same error

Error: Runtime error: Execution failed: Execution aborted due to trap: wasm trap: wasm unreachable instruction executed
WASM backtrace:

0: 0x4f6cd - <unknown>!rust_begin_unwind
1: 0x5777 - <unknown>!core::panicking::panic_fmt::h18b15be283411c65
2: 0x33227e - <unknown>!TaggedTransactionQueue_validate_transaction

panic: runtime error: invalid memory address or nil pointer dereference
panic: runtime error: invalid memory address or nil pointer dereference

@bhaagiKenpachi
Copy link
Author

@cdamian When I'm trying to submit above code transaction. Im getting "invalid transaction" error. Do you know what are the cases that we get above error.

@cdamian
Copy link
Contributor

cdamian commented Oct 31, 2022

@bhaagiKenpachi In the last example that you posted above, use a multi address for the destination account:

gsrpcTypes.NewMultiAddressFromHexAccountID()

@cdamian
Copy link
Contributor

cdamian commented Oct 31, 2022

https://gist.github.com/cdamian/cdfe38c7d858f8c899977fcc9ef41b99 - this works on our chain, adding the remark and transferring some amount from Alice to Bob.

@cdamian
Copy link
Contributor

cdamian commented Oct 31, 2022

Furthermore, if you are unsure about specific extrinsic related details, you can always use something like - https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fwestend.api.onfinality.io%2Fpublic-ws#/extrinsics to confirm the args, signature and so on.

@bhaagiKenpachi
Copy link
Author

@cdamian Its working with gsrpcTypes.NewAddressFromHexAccountID. I was giving wrong input to get error. After providing correct input its working.

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