From 8b1f76d93d74dad27b08d1136ccfbff86ebdc002 Mon Sep 17 00:00:00 2001 From: Steven Landers Date: Wed, 8 May 2024 10:56:22 -0400 Subject: [PATCH] [EVM] Prevent registering pointers to pointers (#1638) * small test refactor * add coverage for keeper logic * cleanup * add register step to cw tests * add erc tests * update code id for error * goimports and cleanup --- contracts/test/CW20toERC20PointerTest.js | 20 +- contracts/test/CW721toERC721PointerTest.js | 25 +- contracts/test/ERC20toCW20PointerTest.js | 34 ++- contracts/test/ERC721toCW721PointerTest.js | 36 ++- contracts/test/lib.js | 76 +++++- x/evm/keeper/pointer.go | 87 +++---- x/evm/keeper/pointer_test.go | 263 ++++++++++++++++++--- 7 files changed, 411 insertions(+), 130 deletions(-) diff --git a/contracts/test/CW20toERC20PointerTest.js b/contracts/test/CW20toERC20PointerTest.js index 69d9d0f2d9..87ec4b9e9d 100644 --- a/contracts/test/CW20toERC20PointerTest.js +++ b/contracts/test/CW20toERC20PointerTest.js @@ -1,10 +1,8 @@ -const {fundAddress, storeWasm, instantiateWasm, getSeiAddress, getAdmin, queryWasm, executeWasm, deployEvmContract, setupSigners, - getEvmAddress +const {getAdmin, queryWasm, executeWasm, deployEvmContract, setupSigners, deployErc20PointerForCw20, deployWasm, WASM, + registerPointerForCw20 } = require("./lib") const { expect } = require("chai"); -const {getAdminAddress} = require("@openzeppelin/upgrades-core"); -const CW20_POINTER_WASM = "../example/cosmwasm/cw20/artifacts/cwerc20.wasm"; describe("CW20 to ERC20 Pointer", function () { let accounts; let testToken; @@ -29,8 +27,7 @@ describe("CW20 to ERC20 Pointer", function () { admin = await getAdmin() await setBalance(admin.evmAddress, 1000000000000) - const codeId = await storeWasm(CW20_POINTER_WASM) - cw20Pointer = await instantiateWasm(codeId, accounts[0].seiAddress, "cw20-erc20", {erc20_address: tokenAddr }) + cw20Pointer = await registerPointerForCw20(tokenAddr) }) async function assertUnsupported(addr, operation, args) { @@ -43,6 +40,17 @@ describe("CW20 to ERC20 Pointer", function () { } } + describe("validation", function(){ + it("should not allow a pointer to the pointer", async function(){ + try { + await deployErc20PointerForCw20(hre.ethers.provider, cw20Pointer, 5) + expect.fail(`Expected to be prevented from creating a pointer`); + } catch(e){ + expect(e.message).to.include("contract deployment failed"); + } + }) + }) + describe("query", function(){ it("should return token_info", async function(){ const result = await queryWasm(cw20Pointer, "token_info", {}) diff --git a/contracts/test/CW721toERC721PointerTest.js b/contracts/test/CW721toERC721PointerTest.js index 492b861d3d..d0e0c36d00 100644 --- a/contracts/test/CW721toERC721PointerTest.js +++ b/contracts/test/CW721toERC721PointerTest.js @@ -1,8 +1,8 @@ -const {setupSigners, deployEvmContract, getAdmin, deployWasm, executeWasm, queryWasm} = require("./lib"); +const {setupSigners, deployEvmContract, getAdmin, deployWasm, executeWasm, queryWasm, deployErc20PointerForCw20, + deployErc721PointerForCw721, WASM, registerPointerForCw721 +} = require("./lib"); const {expect} = require("chai"); -const CW721_POINTER_WASM = "../example/cosmwasm/cw721/artifacts/cwerc721.wasm"; - describe("CW721 to ERC721 Pointer", function () { let accounts; let erc721; @@ -13,11 +13,8 @@ describe("CW721 to ERC721 Pointer", function () { accounts = await setupSigners(await hre.ethers.getSigners()) erc721 = await deployEvmContract("MyNFT") admin = await getAdmin() - pointer = await deployWasm(CW721_POINTER_WASM, - accounts[0].seiAddress, - "cw721-erc721", - {erc721_address: await erc721.getAddress() } - ) + + pointer = await registerPointerForCw721(await erc721.getAddress()) await (await erc721.mint(accounts[0].evmAddress, 1)).wait() await (await erc721.mint(accounts[1].evmAddress, 2)).wait() @@ -27,8 +24,18 @@ describe("CW721 to ERC721 Pointer", function () { await (await erc721.setApprovalForAll(admin.evmAddress, true)).wait(); }) - describe("query", function(){ + describe("validation", function(){ + it("should not allow a pointer to the pointer", async function(){ + try { + await deployErc721PointerForCw721(hre.ethers.provider, pointer, 5) + expect.fail(`Expected to be prevented from creating a pointer`); + } catch(e){ + expect(e.message).to.include("contract deployment failed"); + } + }) + }) + describe("query", function(){ it("should query the owner of a token", async function () { const result = await queryWasm(pointer, "owner_of", { token_id: "1" }); expect(result).to.deep.equal({data:{ diff --git a/contracts/test/ERC20toCW20PointerTest.js b/contracts/test/ERC20toCW20PointerTest.js index dc339c08fd..a87bb6c446 100644 --- a/contracts/test/ERC20toCW20PointerTest.js +++ b/contracts/test/ERC20toCW20PointerTest.js @@ -1,21 +1,7 @@ -const {setupSigners, deployErc20PointerForCw20, getAdmin, storeWasm, instantiateWasm} = require("./lib"); +const {setupSigners, deployErc20PointerForCw20, getAdmin, deployWasm, WASM, ABI, registerPointerForCw20 +} = require("./lib"); const {expect} = require("chai"); -const CW20_BASE_WASM_LOCATION = "../contracts/wasm/cw20_base.wasm"; - -const erc20Abi = [ - "function name() view returns (string)", - "function symbol() view returns (string)", - "function decimals() view returns (uint8)", - "function totalSupply() view returns (uint256)", - "function balanceOf(address owner) view returns (uint256 balance)", - "function transfer(address to, uint amount) returns (bool)", - "function allowance(address owner, address spender) view returns (uint256)", - "function approve(address spender, uint256 value) returns (bool)", - "function transferFrom(address from, address to, uint value) returns (bool)" -]; - - describe("ERC20 to CW20 Pointer", function () { let accounts; let pointer; @@ -26,8 +12,7 @@ describe("ERC20 to CW20 Pointer", function () { accounts = await setupSigners(await hre.ethers.getSigners()) admin = await getAdmin() - const codeId = await storeWasm(CW20_BASE_WASM_LOCATION) - cw20Address = await instantiateWasm(codeId, accounts[0].seiAddress, "cw20", { + cw20Address = await deployWasm(WASM.CW20, accounts[0].seiAddress, "cw20", { name: "Test", symbol: "TEST", decimals: 6, @@ -43,10 +28,21 @@ describe("ERC20 to CW20 Pointer", function () { // deploy TestToken const pointerAddr = await deployErc20PointerForCw20(hre.ethers.provider, cw20Address) - const contract = new hre.ethers.Contract(pointerAddr, erc20Abi, hre.ethers.provider); + const contract = new hre.ethers.Contract(pointerAddr, ABI.ERC20, hre.ethers.provider); pointer = contract.connect(accounts[0].signer) }) + describe("validation", function(){ + it("should not allow a pointer to the pointer", async function(){ + try { + await registerPointerForCw20(await pointer.getAddress()) + expect.fail(`Expected to be prevented from creating a pointer`); + } catch(e){ + expect(e.message).to.include("contract deployment failed"); + } + }) + }) + describe("read", function(){ it("get name", async function () { const name = await pointer.name(); diff --git a/contracts/test/ERC721toCW721PointerTest.js b/contracts/test/ERC721toCW721PointerTest.js index 89a9f94d4c..2b0e151a11 100644 --- a/contracts/test/ERC721toCW721PointerTest.js +++ b/contracts/test/ERC721toCW721PointerTest.js @@ -1,27 +1,10 @@ -const {setupSigners, deployErc721PointerForCw721, getAdmin, deployWasm, executeWasm} = require("./lib"); +const {setupSigners, deployErc721PointerForCw721, getAdmin, deployWasm, executeWasm, ABI, registerPointerForCw20, + registerPointerForCw721 +} = require("./lib"); const {expect} = require("chai"); const CW721_BASE_WASM_LOCATION = "../contracts/wasm/cw721_base.wasm"; -const erc721Abi = [ - "event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId)", - "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)", - "event ApprovalForAll(address indexed owner, address indexed operator, bool approved)", - "function name() view returns (string)", - "function symbol() view returns (string)", - "function totalSupply() view returns (uint256)", - "function tokenURI(uint256 tokenId) view returns (string)", - "function balanceOf(address owner) view returns (uint256 balance)", - "function ownerOf(uint256 tokenId) view returns (address owner)", - "function getApproved(uint256 tokenId) view returns (address operator)", - "function isApprovedForAll(address owner, address operator) view returns (bool)", - "function approve(address to, uint256 tokenId) returns (bool)", - "function setApprovalForAll(address operator, bool _approved) returns (bool)", - "function transferFrom(address from, address to, uint256 tokenId) returns (bool)", - "function safeTransferFrom(address from, address to, uint256 tokenId) returns (bool)", - "function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) returns (bool)" -]; - describe("ERC721 to CW721 Pointer", function () { let accounts; let pointerAcc0; @@ -44,11 +27,22 @@ describe("ERC721 to CW721 Pointer", function () { await executeWasm(cw721Address, { mint : { token_id : "3", owner : accounts[1].seiAddress, token_uri: "token uri 3"}}); const pointerAddr = await deployErc721PointerForCw721(hre.ethers.provider, cw721Address) - const contract = new hre.ethers.Contract(pointerAddr, erc721Abi, hre.ethers.provider); + const contract = new hre.ethers.Contract(pointerAddr, ABI.ERC721, hre.ethers.provider); pointerAcc0 = contract.connect(accounts[0].signer) pointerAcc1 = contract.connect(accounts[1].signer) }) + describe("validation", function(){ + it("should not allow a pointer to the pointer", async function(){ + try { + await registerPointerForCw721(await pointerAcc0.getAddress()) + expect.fail(`Expected to be prevented from creating a pointer`); + } catch(e){ + expect(e.message).to.include("contract deployment failed"); + } + }) + }) + describe("read", function(){ it("get name", async function () { const name = await pointerAcc0.name(); diff --git a/contracts/test/lib.js b/contracts/test/lib.js index 58cba67304..4a422413be 100644 --- a/contracts/test/lib.js +++ b/contracts/test/lib.js @@ -2,6 +2,45 @@ const { exec } = require("child_process"); // Importing exec from child_process const adminKeyName = "admin" +const ABI = { + ERC20: [ + "function name() view returns (string)", + "function symbol() view returns (string)", + "function decimals() view returns (uint8)", + "function totalSupply() view returns (uint256)", + "function balanceOf(address owner) view returns (uint256 balance)", + "function transfer(address to, uint amount) returns (bool)", + "function allowance(address owner, address spender) view returns (uint256)", + "function approve(address spender, uint256 value) returns (bool)", + "function transferFrom(address from, address to, uint value) returns (bool)" + ], + ERC721: [ + "event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId)", + "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)", + "event ApprovalForAll(address indexed owner, address indexed operator, bool approved)", + "function name() view returns (string)", + "function symbol() view returns (string)", + "function totalSupply() view returns (uint256)", + "function tokenURI(uint256 tokenId) view returns (string)", + "function balanceOf(address owner) view returns (uint256 balance)", + "function ownerOf(uint256 tokenId) view returns (address owner)", + "function getApproved(uint256 tokenId) view returns (address operator)", + "function isApprovedForAll(address owner, address operator) view returns (bool)", + "function approve(address to, uint256 tokenId) returns (bool)", + "function setApprovalForAll(address operator, bool _approved) returns (bool)", + "function transferFrom(address from, address to, uint256 tokenId) returns (bool)", + "function safeTransferFrom(address from, address to, uint256 tokenId) returns (bool)", + "function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) returns (bool)" + ], +} + +const WASM = { + CW721: "../contracts/wasm/cw721_base.wasm", + CW20: "../contracts/wasm/cw20_base.wasm", + POINTER_CW20: "../example/cosmwasm/cw20/artifacts/cwerc20.wasm", + POINTER_CW721: "../example/cosmwasm/cw721/artifacts/cwerc721.wasm", +} + function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } @@ -116,15 +155,17 @@ async function getPointerForCw721(cw721Address) { return JSON.parse(output); } -async function deployErc20PointerForCw20(provider, cw20Address) { +async function deployErc20PointerForCw20(provider, cw20Address, attempts=10) { const command = `seid tx evm call-precompile pointer addCW20Pointer ${cw20Address} --from=admin -b block` const output = await execute(command); const txHash = output.replace(/.*0x/, "0x").trim() let attempt = 0; - while(attempt < 10) { + while(attempt < attempts) { const receipt = await provider.getTransactionReceipt(txHash); - if(receipt) { + if(receipt && receipt.status === 1) { return (await getPointerForCw20(cw20Address)).pointer + } else if(receipt){ + throw new Error("contract deployment failed") } await sleep(500) attempt++ @@ -139,8 +180,10 @@ async function deployErc721PointerForCw721(provider, cw721Address) { let attempt = 0; while(attempt < 10) { const receipt = await provider.getTransactionReceipt(txHash); - if(receipt) { + if(receipt && receipt.status === 1) { return (await getPointerForCw721(cw721Address)).pointer + } else if(receipt){ + throw new Error("contract deployment failed") } await sleep(500) attempt++ @@ -161,6 +204,27 @@ async function instantiateWasm(codeId, adminAddr, label, args = {}) { return getEventAttribute(response, "instantiate", "_contract_address"); } +async function registerPointerForCw20(erc20Address, fees="20000usei", from=adminKeyName) { + const command = `seid tx evm register-pointer ERC20 ${erc20Address} --from ${from} --fees ${fees} --broadcast-mode block -y -o json` + const output = await execute(command); + const response = JSON.parse(output) + if(response.code !== 0) { + throw new Error("contract deployment failed") + } + return getEventAttribute(response, "pointer_registered", "pointer_address") +} + +async function registerPointerForCw721(erc721Address, fees="20000usei", from=adminKeyName) { + const command = `seid tx evm register-pointer ERC721 ${erc721Address} --from ${from} --fees ${fees} --broadcast-mode block -y -o json` + const output = await execute(command); + const response = JSON.parse(output) + if(response.code !== 0) { + throw new Error("contract deployment failed") + } + return getEventAttribute(response, "pointer_registered", "pointer_address") + +} + async function getSeiAddress(evmAddress) { const command = `seid q evm sei-addr ${evmAddress} -o json` @@ -276,6 +340,8 @@ module.exports = { deployEvmContract, deployErc20PointerForCw20, deployErc721PointerForCw721, + registerPointerForCw20, + registerPointerForCw721, importKey, getNativeAccount, associateKey, @@ -283,4 +349,6 @@ module.exports = { bankSend, evmSend, waitForReceipt, + WASM, + ABI, }; diff --git a/x/evm/keeper/pointer.go b/x/evm/keeper/pointer.go index f5d75a4a8f..e9b2e1a07d 100644 --- a/x/evm/keeper/pointer.go +++ b/x/evm/keeper/pointer.go @@ -6,7 +6,9 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/ethereum/go-ethereum/common" + "github.com/sei-protocol/sei-chain/x/evm/artifacts/cw20" "github.com/sei-protocol/sei-chain/x/evm/artifacts/cw721" "github.com/sei-protocol/sei-chain/x/evm/artifacts/erc20" @@ -15,15 +17,18 @@ import ( "github.com/sei-protocol/sei-chain/x/evm/types" ) +var ErrorPointerToPointerNotAllowed = sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "cannot create a pointer to a pointer") + +// ERC20 -> Native Token func (k *Keeper) SetERC20NativePointer(ctx sdk.Context, token string, addr common.Address) error { - err := k.setPointerInfo(ctx, types.PointerERC20NativeKey(token), addr[:], native.CurrentVersion) - if err != nil { - return err - } - return k.setPointerInfo(ctx, types.PointerReverseRegistryKey(addr), []byte(token), native.CurrentVersion) + return k.SetERC20NativePointerWithVersion(ctx, token, addr, native.CurrentVersion) } +// ERC20 -> Native Token func (k *Keeper) SetERC20NativePointerWithVersion(ctx sdk.Context, token string, addr common.Address, version uint16) error { + if k.cwAddressIsPointer(ctx, token) { + return ErrorPointerToPointerNotAllowed + } err := k.setPointerInfo(ctx, types.PointerERC20NativeKey(token), addr[:], version) if err != nil { return err @@ -31,6 +36,7 @@ func (k *Keeper) SetERC20NativePointerWithVersion(ctx sdk.Context, token string, return k.setPointerInfo(ctx, types.PointerReverseRegistryKey(addr), []byte(token), version) } +// ERC20 -> Native Token func (k *Keeper) GetERC20NativePointer(ctx sdk.Context, token string) (addr common.Address, version uint16, exists bool) { addrBz, version, exists := k.GetPointerInfo(ctx, types.PointerERC20NativeKey(token)) if exists { @@ -39,32 +45,25 @@ func (k *Keeper) GetERC20NativePointer(ctx sdk.Context, token string) (addr comm return } -func (k *Keeper) GetERC20NativeByPointer(ctx sdk.Context, addr common.Address) (token string, version uint16, exists bool) { - tokenBz, version, exists := k.GetPointerInfo(ctx, types.PointerReverseRegistryKey(addr)) - if exists { - token = string(tokenBz) - } - return -} - +// ERC20 -> Native Token func (k *Keeper) DeleteERC20NativePointer(ctx sdk.Context, token string, version uint16) { addr, _, exists := k.GetERC20NativePointer(ctx, token) if exists { k.deletePointerInfo(ctx, types.PointerERC20NativeKey(token), version) k.deletePointerInfo(ctx, types.PointerReverseRegistryKey(addr), version) } - } +// ERC20 -> CW20 func (k *Keeper) SetERC20CW20Pointer(ctx sdk.Context, cw20Address string, addr common.Address) error { - err := k.setPointerInfo(ctx, types.PointerERC20CW20Key(cw20Address), addr[:], cw20.CurrentVersion) - if err != nil { - return err - } - return k.setPointerInfo(ctx, types.PointerReverseRegistryKey(addr), []byte(cw20Address), cw20.CurrentVersion) + return k.SetERC20CW20PointerWithVersion(ctx, cw20Address, addr, cw20.CurrentVersion) } +// ERC20 -> CW20 func (k *Keeper) SetERC20CW20PointerWithVersion(ctx sdk.Context, cw20Address string, addr common.Address, version uint16) error { + if k.cwAddressIsPointer(ctx, cw20Address) { + return ErrorPointerToPointerNotAllowed + } err := k.setPointerInfo(ctx, types.PointerERC20CW20Key(cw20Address), addr[:], version) if err != nil { return err @@ -72,6 +71,7 @@ func (k *Keeper) SetERC20CW20PointerWithVersion(ctx sdk.Context, cw20Address str return k.setPointerInfo(ctx, types.PointerReverseRegistryKey(addr), []byte(cw20Address), version) } +// ERC20 -> CW20 func (k *Keeper) GetERC20CW20Pointer(ctx sdk.Context, cw20Address string) (addr common.Address, version uint16, exists bool) { addrBz, version, exists := k.GetPointerInfo(ctx, types.PointerERC20CW20Key(cw20Address)) if exists { @@ -80,32 +80,25 @@ func (k *Keeper) GetERC20CW20Pointer(ctx sdk.Context, cw20Address string) (addr return } -func (k *Keeper) GetERC20CW20ByPointer(ctx sdk.Context, addr common.Address) (cw20Address string, version uint16, exists bool) { - cw20AddressBz, version, exists := k.GetPointerInfo(ctx, types.PointerReverseRegistryKey(addr)) - if exists { - cw20Address = string(cw20AddressBz) - } - return -} - +// ERC20 -> CW20 func (k *Keeper) DeleteERC20CW20Pointer(ctx sdk.Context, cw20Address string, version uint16) { addr, _, exists := k.GetERC20CW20Pointer(ctx, cw20Address) if exists { k.deletePointerInfo(ctx, types.PointerERC20CW20Key(cw20Address), version) k.deletePointerInfo(ctx, types.PointerReverseRegistryKey(addr), version) } - } +// ERC721 -> CW721 func (k *Keeper) SetERC721CW721Pointer(ctx sdk.Context, cw721Address string, addr common.Address) error { - err := k.setPointerInfo(ctx, types.PointerERC721CW721Key(cw721Address), addr[:], cw721.CurrentVersion) - if err != nil { - return err - } - return k.setPointerInfo(ctx, types.PointerReverseRegistryKey(addr), []byte(cw721Address), cw721.CurrentVersion) + return k.SetERC721CW721PointerWithVersion(ctx, cw721Address, addr, cw721.CurrentVersion) } +// ERC721 -> CW721 func (k *Keeper) SetERC721CW721PointerWithVersion(ctx sdk.Context, cw721Address string, addr common.Address, version uint16) error { + if k.cwAddressIsPointer(ctx, cw721Address) { + return ErrorPointerToPointerNotAllowed + } err := k.setPointerInfo(ctx, types.PointerERC721CW721Key(cw721Address), addr[:], version) if err != nil { return err @@ -113,6 +106,7 @@ func (k *Keeper) SetERC721CW721PointerWithVersion(ctx sdk.Context, cw721Address return k.setPointerInfo(ctx, types.PointerReverseRegistryKey(addr), []byte(cw721Address), version) } +// ERC721 -> CW721 func (k *Keeper) GetERC721CW721Pointer(ctx sdk.Context, cw721Address string) (addr common.Address, version uint16, exists bool) { addrBz, version, exists := k.GetPointerInfo(ctx, types.PointerERC721CW721Key(cw721Address)) if exists { @@ -121,14 +115,7 @@ func (k *Keeper) GetERC721CW721Pointer(ctx sdk.Context, cw721Address string) (ad return } -func (k *Keeper) GetERC721CW721ByPointer(ctx sdk.Context, addr common.Address) (cw721Address string, version uint16, exists bool) { - cw721AddressBz, version, exists := k.GetPointerInfo(ctx, types.PointerReverseRegistryKey(addr)) - if exists { - cw721Address = string(cw721AddressBz) - } - return -} - +// ERC721 -> CW721 func (k *Keeper) DeleteERC721CW721Pointer(ctx sdk.Context, cw721Address string, version uint16) { addr, _, exists := k.GetERC721CW721Pointer(ctx, cw721Address) if exists { @@ -137,7 +124,11 @@ func (k *Keeper) DeleteERC721CW721Pointer(ctx sdk.Context, cw721Address string, } } +// CW20 -> ERC20 func (k *Keeper) SetCW20ERC20Pointer(ctx sdk.Context, erc20Address common.Address, addr string) error { + if k.evmAddressIsPointer(ctx, erc20Address) { + return ErrorPointerToPointerNotAllowed + } err := k.setPointerInfo(ctx, types.PointerCW20ERC20Key(erc20Address), []byte(addr), erc20.CurrentVersion) if err != nil { return err @@ -145,6 +136,7 @@ func (k *Keeper) SetCW20ERC20Pointer(ctx sdk.Context, erc20Address common.Addres return k.setPointerInfo(ctx, types.PointerReverseRegistryKey(common.BytesToAddress([]byte(addr))), erc20Address[:], erc20.CurrentVersion) } +// CW20 -> ERC20 func (k *Keeper) GetCW20ERC20Pointer(ctx sdk.Context, erc20Address common.Address) (addr sdk.AccAddress, version uint16, exists bool) { addrBz, version, exists := k.GetPointerInfo(ctx, types.PointerCW20ERC20Key(erc20Address)) if exists { @@ -153,7 +145,20 @@ func (k *Keeper) GetCW20ERC20Pointer(ctx sdk.Context, erc20Address common.Addres return } +func (k *Keeper) evmAddressIsPointer(ctx sdk.Context, addr common.Address) bool { + _, _, exists := k.GetPointerInfo(ctx, types.PointerReverseRegistryKey(addr)) + return exists +} + +func (k *Keeper) cwAddressIsPointer(ctx sdk.Context, addr string) bool { + _, _, exists := k.GetPointerInfo(ctx, types.PointerReverseRegistryKey(common.BytesToAddress([]byte(addr)))) + return exists +} + func (k *Keeper) SetCW721ERC721Pointer(ctx sdk.Context, erc721Address common.Address, addr string) error { + if k.evmAddressIsPointer(ctx, erc721Address) { + return ErrorPointerToPointerNotAllowed + } err := k.setPointerInfo(ctx, types.PointerCW721ERC721Key(erc721Address), []byte(addr), erc721.CurrentVersion) if err != nil { return err diff --git a/x/evm/keeper/pointer_test.go b/x/evm/keeper/pointer_test.go index 7d21bb1076..b44fde796d 100644 --- a/x/evm/keeper/pointer_test.go +++ b/x/evm/keeper/pointer_test.go @@ -3,41 +3,244 @@ package keeper_test import ( "testing" - testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper" + "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + + testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper" + "github.com/sei-protocol/sei-chain/x/evm/artifacts/cw20" + "github.com/sei-protocol/sei-chain/x/evm/artifacts/cw721" + "github.com/sei-protocol/sei-chain/x/evm/artifacts/native" + evmkeeper "github.com/sei-protocol/sei-chain/x/evm/keeper" ) -func TestERC20NativePointer(t *testing.T) { - k, ctx := testkeeper.MockEVMKeeper() - _, pointer := testkeeper.MockAddressPair() - require.Nil(t, k.SetERC20NativePointer(ctx, "test", pointer)) - require.NotNil(t, k.SetERC20NativePointer(ctx, "test", pointer)) // already set - addr, _, _ := k.GetERC20NativePointer(ctx, "test") - require.Equal(t, pointer, addr) - token, _, _ := k.GetERC20NativeByPointer(ctx, addr) - require.Equal(t, "test", token) +// allows us to permutate different pointer combinations +type handlers struct { + evmSetter func(ctx types.Context, token string, addr common.Address) error + evmGetter func(ctx types.Context, token string) (addr common.Address, version uint16, exists bool) + evmDeleter func(ctx types.Context, token string, version uint16) + cwSetter func(ctx types.Context, erc20Address common.Address, addr string) error + cwGetter func(ctx types.Context, erc20Address common.Address) (addr types.AccAddress, version uint16, exists bool) + cwDeleter func(ctx types.Context, erc20Address common.Address, version uint16) +} + +type seiPointerTest struct { + name string + getHandlers func(k *evmkeeper.Keeper) *handlers + version uint16 } -func TestSetERC20CW20Pointer(t *testing.T) { - k, ctx := testkeeper.MockEVMKeeper() - _, pointer := testkeeper.MockAddressPair() - cw20, _ := testkeeper.MockAddressPair() - require.Nil(t, k.SetERC20CW20Pointer(ctx, cw20.String(), pointer)) - require.NotNil(t, k.SetERC20CW20Pointer(ctx, cw20.String(), pointer)) // already set - addr, _, _ := k.GetERC20CW20Pointer(ctx, cw20.String()) - require.Equal(t, pointer, addr) - cw20Addr, _, _ := k.GetERC20CW20ByPointer(ctx, addr) - require.Equal(t, cw20.String(), cw20Addr) +func TestEVMtoCWPointers(t *testing.T) { + tests := []seiPointerTest{ + { + name: "ERC20NativePointer prevents pointer to cw20 pointer", + getHandlers: func(k *evmkeeper.Keeper) *handlers { + return &handlers{ + evmSetter: k.SetERC20NativePointer, + evmGetter: k.GetERC20NativePointer, + evmDeleter: k.DeleteERC20NativePointer, + cwSetter: k.SetCW20ERC20Pointer, + cwGetter: k.GetCW20ERC20Pointer, + } + }, + version: native.CurrentVersion, + }, + { + name: "ERC20NativePointer prevents pointer to cw721 pointer", + getHandlers: func(k *evmkeeper.Keeper) *handlers { + return &handlers{ + evmSetter: k.SetERC20NativePointer, + evmGetter: k.GetERC20NativePointer, + evmDeleter: k.DeleteERC20NativePointer, + cwSetter: k.SetCW721ERC721Pointer, + cwGetter: k.GetCW721ERC721Pointer, + } + }, + version: native.CurrentVersion, + }, + { + name: "ERC20CW20Pointer prevents pointer to cw721 pointer", + getHandlers: func(k *evmkeeper.Keeper) *handlers { + return &handlers{ + evmSetter: k.SetERC20CW20Pointer, + evmGetter: k.GetERC20CW20Pointer, + evmDeleter: k.DeleteERC20CW20Pointer, + cwSetter: k.SetCW721ERC721Pointer, + cwGetter: k.GetCW721ERC721Pointer, + } + }, + version: cw20.CurrentVersion, + }, + { + name: "ERC20CW20Pointer prevents pointer to cw20 pointer", + getHandlers: func(k *evmkeeper.Keeper) *handlers { + return &handlers{ + evmSetter: k.SetERC20CW20Pointer, + evmGetter: k.GetERC20CW20Pointer, + evmDeleter: k.DeleteERC20CW20Pointer, + cwSetter: k.SetCW20ERC20Pointer, + cwGetter: k.GetCW20ERC20Pointer, + } + }, + version: cw20.CurrentVersion, + }, + { + name: "ERC721CW721Pointer prevents pointer to cw721 pointer", + getHandlers: func(k *evmkeeper.Keeper) *handlers { + return &handlers{ + evmSetter: k.SetERC721CW721Pointer, + evmGetter: k.GetERC721CW721Pointer, + evmDeleter: k.DeleteERC721CW721Pointer, + cwSetter: k.SetCW721ERC721Pointer, + cwGetter: k.GetCW721ERC721Pointer, + } + }, + version: cw721.CurrentVersion, + }, + { + name: "ERC721CW721Pointer prevents pointer to cw20 pointer", + getHandlers: func(k *evmkeeper.Keeper) *handlers { + return &handlers{ + evmSetter: k.SetERC721CW721Pointer, + evmGetter: k.GetERC721CW721Pointer, + evmDeleter: k.DeleteERC721CW721Pointer, + cwSetter: k.SetCW20ERC20Pointer, + cwGetter: k.GetCW20ERC20Pointer, + } + }, + version: cw721.CurrentVersion, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + k, ctx := testkeeper.MockEVMKeeper() + handlers := test.getHandlers(k) + cwAddress, evmAddress := testkeeper.MockAddressPair() + + // create a pointer + require.Nil(t, handlers.evmSetter(ctx, cwAddress.String(), evmAddress)) + + // should exist + addr, _, exists := handlers.evmGetter(ctx, cwAddress.String()) + require.Equal(t, evmAddress, addr) + require.True(t, exists) + require.NotNil(t, handlers.evmSetter(ctx, cwAddress.String(), evmAddress)) + + // should delete + var version uint16 = 1 + if test.version != 0 { + version = test.version + } + handlers.evmDeleter(ctx, cwAddress.String(), version) + _, _, exists = handlers.evmGetter(ctx, cwAddress.String()) + require.False(t, exists) + + // setup target as pointer + require.Nil(t, handlers.cwSetter(ctx, evmAddress, cwAddress.String())) + _, _, exists = handlers.cwGetter(ctx, evmAddress) + require.True(t, exists) + + // should not allow pointer to pointer + require.Error(t, handlers.evmSetter(ctx, cwAddress.String(), evmAddress), evmkeeper.ErrorPointerToPointerNotAllowed) + + }) + } } -func TestERC721CW721Pointer(t *testing.T) { - k, ctx := testkeeper.MockEVMKeeper() - _, pointer := testkeeper.MockAddressPair() - cw721, _ := testkeeper.MockAddressPair() - require.Nil(t, k.SetERC721CW721Pointer(ctx, cw721.String(), pointer)) - require.NotNil(t, k.SetERC721CW721Pointer(ctx, cw721.String(), pointer)) // already set - addr, _, _ := k.GetERC721CW721Pointer(ctx, cw721.String()) - require.Equal(t, pointer, addr) - cw721Addr, _, _ := k.GetERC721CW721ByPointer(ctx, addr) - require.Equal(t, cw721.String(), cw721Addr) +func TestCWtoEVMPointers(t *testing.T) { + tests := []seiPointerTest{ + { + name: "CW20ERC20Pointer prevents pointer to native pointer", + getHandlers: func(k *evmkeeper.Keeper) *handlers { + return &handlers{ + cwSetter: k.SetCW20ERC20Pointer, + cwGetter: k.GetCW20ERC20Pointer, + evmSetter: k.SetERC20NativePointer, + evmGetter: k.GetERC20NativePointer, + } + }, + }, + { + name: "CW20ERC20Pointer prevents pointer to erc20 pointer", + getHandlers: func(k *evmkeeper.Keeper) *handlers { + return &handlers{ + cwSetter: k.SetCW20ERC20Pointer, + cwGetter: k.GetCW20ERC20Pointer, + evmSetter: k.SetERC20CW20Pointer, + evmGetter: k.GetERC20CW20Pointer, + } + }, + }, + { + name: "CW20ERC20Pointer prevents pointer to erc721 pointer", + getHandlers: func(k *evmkeeper.Keeper) *handlers { + return &handlers{ + cwSetter: k.SetCW20ERC20Pointer, + cwGetter: k.GetCW20ERC20Pointer, + evmSetter: k.SetERC721CW721Pointer, + evmGetter: k.GetERC721CW721Pointer, + } + }, + }, + { + name: "CW721ERC721Pointer prevents pointer to native pointer", + getHandlers: func(k *evmkeeper.Keeper) *handlers { + return &handlers{ + cwSetter: k.SetCW721ERC721Pointer, + cwGetter: k.GetCW721ERC721Pointer, + evmSetter: k.SetERC20NativePointer, + evmGetter: k.GetERC20NativePointer, + } + }, + }, + { + name: "CW721ERC721Pointer prevents pointer to erc721 pointer", + getHandlers: func(k *evmkeeper.Keeper) *handlers { + return &handlers{ + cwSetter: k.SetCW721ERC721Pointer, + cwGetter: k.GetCW721ERC721Pointer, + evmSetter: k.SetERC721CW721Pointer, + evmGetter: k.GetERC721CW721Pointer, + } + }, + }, + { + name: "CW721ERC721Pointer prevents pointer to erc20 pointer", + getHandlers: func(k *evmkeeper.Keeper) *handlers { + return &handlers{ + cwSetter: k.SetCW721ERC721Pointer, + cwGetter: k.GetCW721ERC721Pointer, + evmSetter: k.SetERC20CW20Pointer, + evmGetter: k.GetERC20CW20Pointer, + } + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + k, ctx := testkeeper.MockEVMKeeper() + handlers := test.getHandlers(k) + cwAddress, evmAddress := testkeeper.MockAddressPair() + + // create a pointer + require.Nil(t, handlers.cwSetter(ctx, evmAddress, cwAddress.String())) + + // should exist + addr, _, exists := handlers.cwGetter(ctx, evmAddress) + require.Equal(t, cwAddress, addr) + require.True(t, exists) + require.NotNil(t, handlers.cwSetter(ctx, evmAddress, cwAddress.String())) + + // create new address to test prevention logic + cwAddress2, evmAddress2 := testkeeper.MockAddressPair() + + // setup target as pointer (has to be evm so that the target exists) + require.Nil(t, handlers.evmSetter(ctx, cwAddress2.String(), evmAddress2)) + _, _, exists = handlers.evmGetter(ctx, cwAddress2.String()) + require.True(t, exists) + + // should not allow pointer to pointer + require.Error(t, handlers.cwSetter(ctx, evmAddress2, cwAddress2.String()), evmkeeper.ErrorPointerToPointerNotAllowed) + }) + } }