From 3641f97dd5140caf3a6253203e8053e6bf5db9bf Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Tue, 3 Dec 2024 01:43:20 +0700 Subject: [PATCH 1/3] use geth lib with custom-precompiled-contract support and update evmos fork --- go.mod | 6 +++--- go.sum | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 7b5b247a..9e966374 100644 --- a/go.mod +++ b/go.mod @@ -351,9 +351,9 @@ replace ( github.com/CosmWasm/wasmd => github.com/decentrio/wasmd v0.33.0-sdk46.2 github.com/centrifuge/go-substrate-rpc-client/v4 => github.com/availproject/go-substrate-rpc-client/v4 v4.0.12-avail-1.4.0-rc1-5e286e3 github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0 - // use Evmos geth fork - github.com/ethereum/go-ethereum => github.com/evmos/go-ethereum v1.10.26 - github.com/evmos/evmos/v12 => github.com/dymensionxyz/evmos/v12 v12.1.6-dymension-v0.4.3 + // go-ethereum fork with custom-precompiled-contract support + github.com/ethereum/go-ethereum => github.com/EscanBE/go-ethereum-for-evermint v1.10.28 + github.com/evmos/evmos/v12 => github.com/dymensionxyz/evmos/v12 v12.1.7-0.20241202184644-487bdfd58ff9 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 github.com/gorilla/rpc => github.com/dymensionxyz/rpc v1.3.1 github.com/osmosis-labs/osmosis/v15 => github.com/dymensionxyz/osmosis/v15 v15.2.0-dymension-v1.1.2 diff --git a/go.sum b/go.sum index 0823e33e..4b2acb7b 100644 --- a/go.sum +++ b/go.sum @@ -238,6 +238,8 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/EscanBE/go-ethereum-for-evermint v1.10.28 h1:+Iid0JWaULgPbGAB8lGEn1xqsr+GjeGlFzzTMcjgIIE= +github.com/EscanBE/go-ethereum-for-evermint v1.10.28/go.mod h1:/6CsT5Ceen2WPLI/oCA3xMcZ5sWMF/D46SjM/ayY0Oo= github.com/Jorropo/jsync v1.0.1 h1:6HgRolFZnsdfzRUj+ImB9og1JYOxQoReSywkHOGSaUU= github.com/Jorropo/jsync v1.0.1/go.mod h1:jCOZj3vrBCri3bSU3ErUYvevKlnbssrXeCivybS5ABQ= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -581,8 +583,8 @@ github.com/dymensionxyz/dymension-rdk v1.6.1-0.20241119103059-def6322e4345 h1:Fc github.com/dymensionxyz/dymension-rdk v1.6.1-0.20241119103059-def6322e4345/go.mod h1:y89w1OG4C4aF7yyW8bv9PwV3o1KkCx1hyt34ap04Rnk= github.com/dymensionxyz/dymint v1.2.0-rc01.0.20241119162335-82f60adeb0cc h1:jltDUyV834R55beeN4K+p5u/y91agApAuLRd/0s4dkk= github.com/dymensionxyz/dymint v1.2.0-rc01.0.20241119162335-82f60adeb0cc/go.mod h1:ui7okdD4GRAySphS7XNOlbJKXIA2TAH/Wu6S8KV3o0M= -github.com/dymensionxyz/evmos/v12 v12.1.6-dymension-v0.4.3 h1:jSnj5psbuEq5s5MhbwI26I5gKtlb9mi95pv8r0e9ybI= -github.com/dymensionxyz/evmos/v12 v12.1.6-dymension-v0.4.3/go.mod h1:CI6D89pkoiIm4BjoMFNnEaCLdKBEobLuwvhS0c1zh7Y= +github.com/dymensionxyz/evmos/v12 v12.1.7-0.20241202184644-487bdfd58ff9 h1:DgEOLAI18QDrQMSH0DvS+bwwctGZ+roGppDTcVxin8c= +github.com/dymensionxyz/evmos/v12 v12.1.7-0.20241202184644-487bdfd58ff9/go.mod h1:mkPJyB6L8Vciw0nrYMbz8XeNwNPX1Pl9mdS+JJB7Fg8= github.com/dymensionxyz/gerr-cosmos v1.0.0 h1:oi91rgOkpJWr41oX9JOyjvvBnhGY54tj513x8VlDAEc= github.com/dymensionxyz/gerr-cosmos v1.0.0/go.mod h1:n+0olxPogzWqFKba45mCpvrHLGmeS8W9UZjggHnWk6c= github.com/dymensionxyz/rpc v1.3.1 h1:7EXWIobaBes5zldRvTIg7TmNsEKjicrWA/OjCc0NaGs= @@ -609,8 +611,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evmos/go-ethereum v1.10.26 h1:7wlczxUWTwhzJJUyh3Kkqt3/5fdSJzh8c42boc9GuII= -github.com/evmos/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= From 56e328480d86ece21832c83538903e064f66cbdc Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Tue, 3 Dec 2024 02:11:06 +0700 Subject: [PATCH 2/3] add new module cpc and price-feed precompiles --- x/cpc/abi/precompiled_info.go | 24 +++++++++ x/cpc/abi/precompiled_info_test.go | 48 +++++++++++++++++ x/cpc/abi/price_feed.json | 26 ++++++++++ x/cpc/abi/price_feed.sol | 11 ++++ x/cpc/precompiles.go | 39 ++++++++++++++ x/cpc/precompiles_pricefeed.go | 82 ++++++++++++++++++++++++++++++ 6 files changed, 230 insertions(+) create mode 100644 x/cpc/abi/precompiled_info.go create mode 100644 x/cpc/abi/precompiled_info_test.go create mode 100644 x/cpc/abi/price_feed.json create mode 100644 x/cpc/abi/price_feed.sol create mode 100644 x/cpc/precompiles.go create mode 100644 x/cpc/precompiles_pricefeed.go diff --git a/x/cpc/abi/precompiled_info.go b/x/cpc/abi/precompiled_info.go new file mode 100644 index 00000000..e0ca7dba --- /dev/null +++ b/x/cpc/abi/precompiled_info.go @@ -0,0 +1,24 @@ +package abi + +import ( + _ "embed" + "encoding/json" + "github.com/evmos/evmos/v12/x/evm/abi" +) + +var ( + //go:embed price_feed.json + priceFeedJson []byte + + PriceFeedCpcInfo abi.CustomPrecompiledContractInfo +) + +func init() { + var err error + + err = json.Unmarshal(priceFeedJson, &PriceFeedCpcInfo) + if err != nil { + panic(err) + } + PriceFeedCpcInfo.Name = "Price Feed" +} diff --git a/x/cpc/abi/precompiled_info_test.go b/x/cpc/abi/precompiled_info_test.go new file mode 100644 index 00000000..17632350 --- /dev/null +++ b/x/cpc/abi/precompiled_info_test.go @@ -0,0 +1,48 @@ +package abi + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "math" + "math/big" + "testing" +) + +//goland:noinspection GoUnusedGlobalVariable +var ( + bigIntMaxInt64 = new(big.Int).SetUint64(math.MaxInt64) + bigIntMaxInt64Bz = common.BytesToHash(bigIntMaxInt64.Bytes()).Bytes() + bigIntMaxUint64 = new(big.Int).SetUint64(math.MaxUint64) + bigIntMaxUint64Bz = common.BytesToHash(bigIntMaxUint64.Bytes()).Bytes() + bigIntOneBz = common.BytesToHash(big.NewInt(1).Bytes()).Bytes() + text = "hello" + textAbiEncodedBz = []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} + maxUint8Value = uint8(math.MaxUint8) + maxUint8ValueBz = common.BytesToHash([]byte{math.MaxUint8}).Bytes() + _32Bytes = [32]byte{0x1, 0x2, 0x3, 0x32, 0xFF} +) + +func Test_PriceFeed(t *testing.T) { + cpcInfo := PriceFeedCpcInfo + + t.Run("getPrice(string)", func(t *testing.T) { + bz, err := cpcInfo.ABI.Methods["getPrice"].Inputs.Pack(text) + require.NoError(t, err) + + ret, err := cpcInfo.UnpackMethodInput( + "getPrice", + append([]byte{0x52, 0x4f, 0x38, 0x89}, bz...), + ) + require.NoError(t, err) + require.Len(t, ret, 1) + require.Equal(t, text, ret[0].(string)) + + bz, err = cpcInfo.PackMethodOutput("getPrice", bigIntMaxUint64, true) + require.NoError(t, err) + ops, err := cpcInfo.ABI.Methods["getPrice"].Outputs.Unpack(bz) + require.NoError(t, err) + require.Len(t, ops, 2) + require.Equal(t, bigIntMaxUint64, ops[0].(*big.Int)) + require.Equal(t, true, ops[1].(bool)) + }) +} diff --git a/x/cpc/abi/price_feed.json b/x/cpc/abi/price_feed.json new file mode 100644 index 00000000..956b886e --- /dev/null +++ b/x/cpc/abi/price_feed.json @@ -0,0 +1,26 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "tokenName", + "type": "string" + } + ], + "name": "getPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/x/cpc/abi/price_feed.sol b/x/cpc/abi/price_feed.sol new file mode 100644 index 00000000..74305f15 --- /dev/null +++ b/x/cpc/abi/price_feed.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.7.0 <0.9.0; + +interface IDymPriceFeedCPC { + /** + * @dev Returns price for the given token by name. + * The second return value indicating whether the operation succeeded. + */ + function getPrice(string memory tokenName) external view returns (uint256, bool); +} \ No newline at end of file diff --git a/x/cpc/precompiles.go b/x/cpc/precompiles.go new file mode 100644 index 00000000..7c0fc148 --- /dev/null +++ b/x/cpc/precompiles.go @@ -0,0 +1,39 @@ +package cpc + +import ( + "fmt" + "github.com/ethereum/go-ethereum/common" +) + +const ( + cpcAddrNoncePriceFeed byte = iota + 1 +) + +// CpcPriceFeedFixedAddress is the address of the price-feed custom precompiled contract. +var CpcPriceFeedFixedAddress common.Address + +func init() { + generatedCpcAddresses := make(map[common.Address]struct{}) + + // generateCpcAddress generates a custom precompiled contract address based on the contract address nonce. + generateCpcAddress := func(contractAddrNonce byte) common.Address { + if contractAddrNonce == 0 { + panic("contract address nonce cannot be zero") + } + bz := make([]byte, 20) + bz[0] = 0xCC + bz[1] = contractAddrNonce + bz[2] = 0x01 // avoid collision with custom precompiled contracts + bz[19] = contractAddrNonce + + addr := common.BytesToAddress(bz) + if _, ok := generatedCpcAddresses[addr]; ok { + panic(fmt.Sprintf("generated address %s already exists", addr.Hex())) + } + generatedCpcAddresses[addr] = struct{}{} + + return addr + } + + CpcPriceFeedFixedAddress = generateCpcAddress(cpcAddrNoncePriceFeed) +} diff --git a/x/cpc/precompiles_pricefeed.go b/x/cpc/precompiles_pricefeed.go new file mode 100644 index 00000000..fecc760e --- /dev/null +++ b/x/cpc/precompiles_pricefeed.go @@ -0,0 +1,82 @@ +package cpc + +import ( + "github.com/dymensionxyz/rollapp-evm/x/cpc/abi" + "github.com/ethereum/go-ethereum/common" + corevm "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + evmkeeper "github.com/evmos/evmos/v12/x/evm/keeper" + "math/big" +) + +// contract + +var _ evmkeeper.CustomPrecompiledContractI = &priceFeedCustomPrecompiledContract{} + +// priceFeedCustomPrecompiledContract +type priceFeedCustomPrecompiledContract struct { + keeper *evmkeeper.Keeper // FIXME: replace with something that can be used to get the price + executors []evmkeeper.ExtendedCustomPrecompiledContractMethodExecutorI +} + +// NewPreFeedCustomPrecompiledContract creates a new price-feed custom precompiled contract. +func NewPreFeedCustomPrecompiledContract(keeper *evmkeeper.Keeper) evmkeeper.CustomPrecompiledContractI { + contract := &priceFeedCustomPrecompiledContract{ + keeper: keeper, + } + + contract.executors = []evmkeeper.ExtendedCustomPrecompiledContractMethodExecutorI{ + &priceFeedCustomPrecompiledContractRoGetPrice{contract}, + } + + return contract +} + +func (m priceFeedCustomPrecompiledContract) GetName() string { + return abi.PriceFeedCpcInfo.Name +} + +func (m priceFeedCustomPrecompiledContract) GetAddress() common.Address { + return CpcPriceFeedFixedAddress +} + +func (m priceFeedCustomPrecompiledContract) GetMethodExecutors() []evmkeeper.ExtendedCustomPrecompiledContractMethodExecutorI { + return m.executors +} + +// getPrice(string) + +var _ evmkeeper.ExtendedCustomPrecompiledContractMethodExecutorI = &priceFeedCustomPrecompiledContractRoGetPrice{} + +type priceFeedCustomPrecompiledContractRoGetPrice struct { + contract *priceFeedCustomPrecompiledContract +} + +func (e priceFeedCustomPrecompiledContractRoGetPrice) Execute(_ corevm.ContractRef, _ common.Address, input []byte, _ evmkeeper.CpcExecutorEnv) ([]byte, error) { + ips, err := abi.PriceFeedCpcInfo.UnpackMethodInput("getPrice", input) + if err != nil { + return nil, err + } + + denom := ips[0].(string) + var price *big.Int + + { // FIXME: implement it correctly + // generate random price + price = new(big.Int).SetBytes(crypto.Keccak256([]byte(denom))) + } + + return abi.PriceFeedCpcInfo.PackMethodOutput("getPrice", price, true) +} + +func (e priceFeedCustomPrecompiledContractRoGetPrice) Method4BytesSignatures() []byte { + return []byte{0x52, 0x4f, 0x38, 0x89} +} + +func (e priceFeedCustomPrecompiledContractRoGetPrice) RequireGas() uint64 { + return 500_000 +} + +func (e priceFeedCustomPrecompiledContractRoGetPrice) ReadOnly() bool { + return true +} From 734acc98e5035b9901084b6a81ef1d96894d0f4d Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Tue, 3 Dec 2024 02:14:30 +0700 Subject: [PATCH 3/3] register precompiles --- app/app.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/app.go b/app/app.go index a0952dab..37fd8179 100644 --- a/app/app.go +++ b/app/app.go @@ -2,6 +2,7 @@ package app import ( "fmt" + "github.com/dymensionxyz/rollapp-evm/x/cpc" "io" "net/http" "os" @@ -654,6 +655,10 @@ func NewRollapp( ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack) app.IBCKeeper.SetRouter(ibcRouter) + /**** Register custom precompiled contract using keepers ****/ + const enablePrecompilesAtVersion = 0 // future precompiles should be enabled at a higher version and x/evm module params should be updated, the goal is to avoid breaking state when replay blocks + evmkeeper.RegisterCustomPrecompiledContract(cpc.NewPreFeedCustomPrecompiledContract(app.EvmKeeper), enablePrecompilesAtVersion) + /**** Module Options ****/ // NOTE: Any module instantiated in the module manager that is later modified