centrifuge / go-substrate-rpc-client

Substrate RPC client for go aka GSRPC
Apache License 2.0
204 stars 179 forks source link

SubmitExtrinsic Verification Error #284

Open jekabolt opened 2 years ago

jekabolt commented 2 years ago

Im using chain DataHighway Tanganika Kusama Parachain using DataHighway Collator v3.6.0-b938f49-x86_64-linux-gnu

trying to SubmitExtrinsic

my code below

func TestTransfer(t *testing.T) {
    // 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://tanganika.datahighway.com")
    if err != nil {
        panic(err)
    }

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

    kpFrom, err := signature.KeyringPairFromSecret("secret1", 33)
    assert.NoError(t, err)

    kpTo, err := signature.KeyringPairFromSecret("secret2", 33)
    assert.NoError(t, err)

    addrTo, err := types.NewMultiAddressFromHexAccountID(types.HexEncodeToString(kpTo.PublicKey))

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

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

    // Create the 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", kpFrom.PublicKey)
    if err != nil {
        panic(err)
    }

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

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

    // Sign the transaction using Alice's default account
    err = ext.Sign(kpFrom, o)
    if err != nil {
        panic(err)
    }

    enc, err := types.EncodeToHex(ext)
    if err != nil {
        panic(err)
    }
    fmt.Println("tx --- ", enc)
    // Send the extrinsic
    _, err = api.RPC.Author.SubmitExtrinsic(ext)
    if err != nil {
        t.Errorf("SubmitExtrinsic %v", err)
    }

}

Im getting

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

            0: 0x324fa5 - <unknown>!rust_begin_unwind
            1: 0x35bc - <unknown>!core::panicking::panic_fmt::h8f11323637b4db3b
            2: 0x21812f - <unknown>!TaggedTransactionQueue_validate_transaction

any ideas what is wrong

sheenhx commented 2 years ago

We solved this by decode the correct tx signed message from PolkadotJS. And we found out the go-rpc code writes the TX message in a wrong sequence

0x390284005675bfca5d558c0df741ce6017beb274d97f262144441f8d622f7e62bb0e411c0168d01fbd9c7849bfdb51cde9d7a3494e1e7af22160f2a361f3672168f5d9085db91a24ea9d9a9d3c9bdaa8f7a347c28a0e4f930aa7e8f0af484547086b9aca89002400000f00009425ad96a3583bfe7de1f0a722ee7bbe9ddcaf3b4b79468ca74fe1b6b99c540c0284d717

https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Ftanganika.datahighway.com#/extrinsics/decode

After sorting it right, we can submit this extrinsic correctly.

jekabolt commented 2 years ago

@sheenhx Hey I didn't get what do you mean I found that SectionIndex & MethodIndex are in wrong places and for browser generated tx they push SectionIndex in front of args

func NewCall(m *types.Metadata, call string, args ...interface{}) (types.Call, error) {
    c, err := m.FindCallIndex(call)
    if err != nil {
        return types.Call{}, err
    }
    c = types.CallIndex{
        SectionIndex: c.MethodIndex,
        MethodIndex:  c.SectionIndex,
    }

    var a []byte
    ci, err := types.Encode(c.SectionIndex)
    if err != nil {
        return types.Call{}, err
    }
    a = append(a, ci...)

    for _, arg := range args {
        e, err := types.Encode(arg)
        if err != nil {
            return types.Call{}, err
        }
        a = append(a, e...)
    }

    return types.Call{
        CallIndex: c,
        Args:      a,
    }, nil
}
image

here I got two nearly identical txs

0x4d028400d624d7a4c7ad34cdd007428709b610d529649e273567c4d7ea37854cec44152601f0a0af61cfc657295bc22cb93e07272b00679edbc21d3cfeb9d188782d5fcd6a9f448bb998109b98a3461e037a915f6596e71b2f646bcb48bc14ba6cb0d49389002800000f00005675bfca5d558c0df741ce6017beb274d97f262144441f8d622f7e62bb0e411c13000064a7b3b6e00d ```
this one from browser 

0x4d028400d624d7a4c7ad34cdd007428709b610d529649e273567c4d7ea37854cec441526011232c17b4a23edfeb8cda558721c8892af458ceccf0e17a618d31f6c7a66d43d64ddcaed7fa1cbe0593fd340958c7129ac9ca10eac86ff8f8fff7715cd5cf884002800000f00005675bfca5d558c0df741ce6017beb274d97f262144441f8d622f7e62bb0e411c13000064a7b3b6e00d```

and one I generate from code only difference in them is sig field and if I try to submit em via browser it gives error :

1010: Invalid Transaction: Transaction has a bad signature
jekabolt commented 2 years ago

@sheenhx I took payload from generated tx

payload 0x000f00005675bfca5d558c0df741ce6017beb274d97f262144441f8d622f7e62bb0e411c13000064a7b3b6e00d0028000400000001000000eacdd2d5b42de9769ccbb6e8d9013ab0d90ab105bf601d4aac53e874c145ec21eacdd2d5b42de9769ccbb6e8d9013ab0d90ab105bf601d4aac53e874c145

and signature

0x1232c17b4a23edfeb8cda558721c8892af458ceccf0e17a618d31f6c7a66d43d64ddcaed7fa1cbe0593fd340958c7129ac9ca10eac86ff8f8fff7715cd5cf884

they are totally fine and pass

ok, err := signature.Verify(b, sig[:], signature.TestKeyringPairAlice.URI)

and browser verification

jekabolt commented 2 years ago

How can I resolve this error?

Thanks

CocDap commented 1 year ago

I have the same issue. Any solutions? I am using the latest version 4.1.0

My blockchain node version: polkadot-v0.9.36 This is my code example:

package main

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

)

func main(){
    err := SendData();

    if err != nil {
        fmt.Println("error:",err)

    }

}

func SendData() error {
        //Alice private key 
    nemo := "0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a"

    api, err := gsrpc.NewSubstrateAPI("ws://127.0.0.1:9944")
    if err != nil {
        return err
    }
    fmt.Println("connected")

    keypair, err := signature.KeyringPairFromSecret(nemo, 42)
    fmt.Println("keypair", keypair.Address, keypair.PublicKey, keypair.URI)
    if err != nil {
        fmt.Println("keypair", err)
    }
    meta, err := api.RPC.State.GetMetadataLatest()
    if err != nil {
        return err
    }

    amount := types.NewUCompactFromUInt(2000000000000000000)

    bob, err := types.NewMultiAddressFromHexAccountID("0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48")
    c, err := types.NewCall(meta, "Balances.transfer",bob, amount)
    if err != nil {
        fmt.Println("err call", err)
        return err
    }
    fmt.Println("done1")
    // Create the extrinsic
    ext := types.NewExtrinsic(c)
    fmt.Println("ext:",ext.Version)

    genesisHash, err := api.RPC.Chain.GetBlockHash(0)
    if err != nil {
        fmt.Println("genesisHash, err", err)
        return err
    }
    fmt.Println("done 2 genesisHash", genesisHash)
    rv, err := api.RPC.State.GetRuntimeVersionLatest()
    if err != nil {
        fmt.Println("GetRuntimeVersionLatest, err", err)
        return err
    }
    fmt.Println("done 3")
    key, err := types.CreateStorageKey(meta, "System", "Account", keypair.PublicKey)
    if err != nil {
        return err
    }
    fmt.Println("done 4 key ", key)
    var accountInfo types.AccountInfo
    ok, err := api.RPC.State.GetStorageLatest(key, &accountInfo)
    fmt.Println("done 5", ok, err, accountInfo)
    if err != nil || !ok {
        return err
    }

    nonce := uint32(accountInfo.Nonce)
    o := types.SignatureOptions{
        BlockHash:          genesisHash,
        Era:                types.ExtrinsicEra{IsMortalEra: false},
        GenesisHash:        genesisHash,
        Nonce:              types.NewUCompactFromUInt(uint64(nonce)),
        SpecVersion:        rv.SpecVersion,
        Tip:                types.NewUCompactFromUInt(100),
        TransactionVersion: rv.TransactionVersion,
    }
    fmt.Println("before sign:",o)
    // Sign the transaction using Alice's default account
    err = ext.Sign(keypair, o)
    if err != nil {
        return err
    }
    fmt.Println("before submission")
    // Send the extrinsic
    txnHash, err := api.RPC.Author.SubmitAndWatchExtrinsic(ext)
    if err != nil {
        return err
    }
    fmt.Println("after submission txnHash", txnHash)
    return nil
}

My go.mod

module test-sign-transaction

go 1.18

require github.com/centrifuge/go-substrate-rpc-client/v4 v4.1.0

require (
    github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect
    github.com/cosmos/go-bip39 v1.0.0 // indirect
    github.com/deckarep/golang-set v1.8.0 // indirect
    github.com/decred/base58 v1.0.4 // indirect
    github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
    github.com/ethereum/go-ethereum v1.10.20 // indirect
    github.com/go-stack/stack v1.8.1 // indirect
    github.com/gorilla/websocket v1.5.0 // indirect
    github.com/gtank/merlin v0.1.1 // indirect
    github.com/gtank/ristretto255 v0.1.2 // indirect
    github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect
    github.com/pierrec/xxHash v0.1.5 // indirect
    github.com/rs/cors v1.8.2 // indirect
    github.com/vedhavyas/go-subkey v1.0.4 // indirect
    golang.org/x/crypto v0.7.0 // indirect
    golang.org/x/sys v0.6.0 // indirect
    gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
)
cdamian commented 11 months ago

It could be that other chains use different kind of tips for signature options, or different signatures, one example can be found here - https://github.com/centrifuge/go-substrate-rpc-client/blob/master/registry/retriever/extrinsic_retriever_live_test.go#L24

The polkadot JS endpoint that you provided, https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Ftanganika.datahighway.com#/extrinsics/decode, does not seem to work. Is there another endpoint that we can use to check this particular issue?