diff --git a/chain/evm.go b/chain/evm.go new file mode 100644 index 0000000..671b81d --- /dev/null +++ b/chain/evm.go @@ -0,0 +1,126 @@ +package chain + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/CESSProject/cess-go-sdk/core/pattern" + "github.com/CESSProject/cess-go-sdk/utils" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" + "github.com/pkg/errors" +) + +func (c *ChainClient) SendEvmCall(source types.H160, target types.H160, input types.Bytes, value types.U256, gasLimit types.U64, maxFeePerGas types.U256, accessList []pattern.AccessInfo) (string, error) { + c.lock.Lock() + defer func() { + c.lock.Unlock() + if err := recover(); err != nil { + log.Println(utils.RecoverError(err)) + } + }() + + var ( + txhash string + accountInfo types.AccountInfo + ) + + if !c.GetChainState() { + return txhash, fmt.Errorf("chainSDK.UploadDeclaration(): GetChainState(): %v", pattern.ERR_RPC_CONNECTION) + } + + var nonce types.Option[types.U256] + nonce.SetNone() + + var maxPriorityFeePerGas types.Option[types.U256] + maxPriorityFeePerGas.SetNone() + + call, err := types.NewCall(c.metadata, pattern.TX_EVM_CALL, source, target, input, value, gasLimit, maxFeePerGas, maxPriorityFeePerGas, nonce, accessList) + if err != nil { + err = fmt.Errorf("rpc err: [%s] [tx] [%s] NewCall: %v", c.GetCurrentRpcAddr(), pattern.EVM, err) + c.SetChainState(false) + return txhash, err + } + + ext := types.NewExtrinsic(call) + + key, err := types.CreateStorageKey(c.metadata, pattern.SYSTEM, pattern.ACCOUNT, c.keyring.PublicKey) + if err != nil { + err = fmt.Errorf("rpc err: [%s] [tx] [%s] CreateStorageKey: %v", c.GetCurrentRpcAddr(), pattern.EVM, err) + c.SetChainState(false) + return txhash, err + } + + ok, err := c.api.RPC.State.GetStorageLatest(key, &accountInfo) + if err != nil { + err = fmt.Errorf("rpc err: [%s] [tx] [%s] GetStorageLatest: %v", c.GetCurrentRpcAddr(), pattern.EVM, err) + c.SetChainState(false) + return txhash, err + } + + if !ok { + keyStr, _ := utils.NumsToByteStr(key, map[string]bool{}) + return txhash, fmt.Errorf( + "chain rpc.state.GetStorageLatest[%v]: %v", + keyStr, + pattern.ERR_RPC_EMPTY_VALUE, + ) + } + + o := types.SignatureOptions{ + BlockHash: c.genesisHash, + Era: types.ExtrinsicEra{IsMortalEra: false}, + GenesisHash: c.genesisHash, + Nonce: types.NewUCompactFromUInt(uint64(accountInfo.Nonce)), + SpecVersion: c.runtimeVersion.SpecVersion, + Tip: types.NewUCompactFromUInt(0), + TransactionVersion: c.runtimeVersion.TransactionVersion, + } + + err = ext.Sign(c.keyring, o) + if err != nil { + err = fmt.Errorf("rpc err: [%s] [tx] [%s] Sign: %v", c.GetCurrentRpcAddr(), pattern.EVM, err) + c.SetChainState(false) + return txhash, err + } + + sub, err := c.api.RPC.Author.SubmitAndWatchExtrinsic(ext) + if err != nil { + if strings.Contains(err.Error(), pattern.ERR_RPC_PRIORITYTOOLOW) { + o.Nonce = types.NewUCompactFromUInt(uint64(accountInfo.Nonce + 1)) + err = ext.Sign(c.keyring, o) + if err != nil { + return txhash, errors.Wrap(err, "[Sign]") + } + sub, err = c.api.RPC.Author.SubmitAndWatchExtrinsic(ext) + if err != nil { + err = fmt.Errorf("rpc err: [%s] [tx] [%s] SubmitAndWatchExtrinsic: %v", c.GetCurrentRpcAddr(), pattern.EVM, err) + c.SetChainState(false) + return txhash, err + } + } else { + err = fmt.Errorf("rpc err: [%s] [tx] [%s] SubmitAndWatchExtrinsic: %v", c.GetCurrentRpcAddr(), pattern.EVM, err) + c.SetChainState(false) + return txhash, err + } + } + defer sub.Unsubscribe() + + timeout := time.NewTimer(c.packingTime) + defer timeout.Stop() + + for { + select { + case status := <-sub.Chan(): + if status.IsInBlock { + txhash = status.AsInBlock.Hex() + return txhash, err + } + case err = <-sub.Err(): + return txhash, errors.Wrap(err, "[sub]") + case <-timeout.C: + return txhash, pattern.ERR_RPC_TIMEOUT + } + } +} diff --git a/core/pattern/pattern.go b/core/pattern/pattern.go index 9df86cc..e88ea17 100755 --- a/core/pattern/pattern.go +++ b/core/pattern/pattern.go @@ -52,6 +52,8 @@ const ( BALANCES = "Balances" // SYSTEM is a module about the system SYSTEM = "System" + // EVM is a module about the evm contract + EVM = "EVM" // CessTreasury = "CessTreasury" ) @@ -174,6 +176,9 @@ const ( // BALANCES TX_BALANCES_FORCETRANSFER = "Balances" + DOT + "transfer" + + // EVM + TX_EVM_CALL = EVM + DOT + "call" ) // RPC Call @@ -598,3 +603,8 @@ type UserInfo struct { BucketName string FileSize uint64 } + +type AccessInfo struct { + r types.H160 + c []types.H160 +} diff --git a/example/evm/evm.go b/example/evm/evm.go new file mode 100644 index 0000000..c299cee --- /dev/null +++ b/example/evm/evm.go @@ -0,0 +1,84 @@ +package main + +import ( + "context" + "encoding/hex" + "fmt" + "log" + "math/big" + "time" + + cess "github.com/CESSProject/cess-go-sdk" + "github.com/CESSProject/cess-go-sdk/chain" + "github.com/CESSProject/cess-go-sdk/core/pattern" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" +) + +// Substrate well-known mnemonic: +// +// https://github.com/substrate-developer-hub/substrate-developer-hub.github.io/issues/613 +var MY_MNEMONIC = "head achieve piano online exhaust bulk trust vote inflict room keen maximum" + +var RPC_ADDRS = []string{ + //devnet + "wss://devnet-rpc.cess.cloud/ws/", + //testnet + // "wss://testnet-rpc0.cess.cloud/ws/", + // "wss://testnet-rpc1.cess.cloud/ws/", + // "wss://testnet-rpc2.cess.cloud/ws/", +} + +func main() { + // 1. new sdk + sdk, err := NewSDK() + if err != nil { + panic(err) + } + + var ( + source types.H160 + target types.H160 + input types.Bytes + value types.U256 + gasLimit types.U64 + maxFeePerGas types.U256 + accessList []pattern.AccessInfo + ) + + s_h160, err := hex.DecodeString("1e3e1c69dfbd27d398e92da4844a9abdc2786ac0") + if err != nil { + log.Fatalln(err) + } + source = types.NewH160(s_h160) + + t_h160, err := hex.DecodeString("7352188979857675C3aD1AA6662326ebD6DDBf6d") + if err != nil { + log.Fatalln(err) + } + target = types.NewH160(t_h160) + + input_string, err := hex.DecodeString("a9059cbb00000000000000000000000085cdaca43a76c8ab769b974c2cf7306980742a310000000000000000000000000000000000000000000000000de0b6b3a7640000") + input = input_string + + value = types.NewU256(*big.NewInt(0)) + + gasLimit = 3000000 + + maxFeePerGas = types.NewU256(*big.NewInt(500000000)) + + block_hash, err := sdk.SendEvmCall(source, target, input, value, gasLimit, maxFeePerGas, accessList) + if err != nil { + log.Fatalln(err) + } + + fmt.Printf("%s", block_hash) +} + +func NewSDK() (*chain.ChainClient, error) { + return cess.New( + context.Background(), + cess.ConnectRpcAddrs(RPC_ADDRS), + cess.Mnemonic(MY_MNEMONIC), + cess.TransactionTimeout(time.Second*10), + ) +} diff --git a/go.mod b/go.mod index 4654877..87cc581 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( ) require ( + github.com/BurntSushi/toml v1.2.1 // indirect github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -33,6 +34,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-stack/stack v1.8.1 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect @@ -44,9 +46,19 @@ require ( github.com/stretchr/objx v0.5.0 // indirect github.com/vedhavyas/go-subkey/v2 v2.0.0 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect + golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338 // indirect + golang.org/x/mod v0.15.0 // indirect golang.org/x/net v0.21.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.17.0 // indirect + golang.org/x/telemetry v0.0.0-20240209200032-7b892fcb8a78 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.18.1-0.20240412183611-d92ae0781217 // indirect + golang.org/x/tools/gopls v0.15.3 // indirect + golang.org/x/vuln v1.0.1 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + honnef.co/go/tools v0.4.6 // indirect + mvdan.cc/gofumpt v0.6.0 // indirect + mvdan.cc/xurls/v2 v2.5.0 // indirect ) diff --git a/go.sum b/go.sum index d80dd45..5be9d72 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/CESSProject/go-keyring v0.0.0-20220614131247-ee3a8da30fde h1:5MDRjjtg6PEhqyVjupwaapN96cOZiddOGAYwKQeaTu0= github.com/CESSProject/go-keyring v0.0.0-20220614131247-ee3a8da30fde/go.mod h1:RUXBd3ROP98MYepEEa0Y0l/T0vQlIKqFJxI/ocdnRLM= github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= @@ -45,6 +47,8 @@ github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3G github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= @@ -115,6 +119,10 @@ golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338 h1:2O2DON6y3XMJiQRAS1UWU+54aec2uopH3x7MAiqGW6Y= +golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -134,12 +142,23 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240209200032-7b892fcb8a78 h1:vcVnuftN4J4UKLRcgetjzfU9FjjgXUUYUc3JhFplgV4= +golang.org/x/telemetry v0.0.0-20240209200032-7b892fcb8a78/go.mod h1:KG1lNk5ZFNssSZLrpVb4sMXKMpGwGXOxSG3rnu2gZQQ= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.18.1-0.20240412183611-d92ae0781217 h1:uH9jJYgeLCvblH0S+03kFO0qUDxRkbLRLFiKVVDl7ak= +golang.org/x/tools v0.18.1-0.20240412183611-d92ae0781217/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools/gopls v0.15.3 h1:zbdOidFrPTc8Bx0YrN5QKgJ0zCjyGi0L27sKQ/bDG5o= +golang.org/x/tools/gopls v0.15.3/go.mod h1:W/lfb6hIysrnNXreqA2nHP2Qaay881XwhrSRMfsGUdQ= +golang.org/x/vuln v1.0.1 h1:KUas02EjQK5LTuIx1OylBQdKKZ9jeugs+HiqO5HormU= +golang.org/x/vuln v1.0.1/go.mod h1:bb2hMwln/tqxg32BNY4CcxHWtHXuYa3SbIBmtsyjxtM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= @@ -149,3 +168,9 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.4.6 h1:oFEHCKeID7to/3autwsWfnuv69j3NsfcXbvJKuIcep8= +honnef.co/go/tools v0.4.6/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0= +mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo= +mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA= +mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8= +mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=