diff --git a/test/contract/contracts/SLVToken.sol b/test/contract/contracts/SLVToken.sol index 00b7249b..9c6d89b0 100644 --- a/test/contract/contracts/SLVToken.sol +++ b/test/contract/contracts/SLVToken.sol @@ -7,9 +7,7 @@ contract SLVToken is ERC20 { uint256 private countA; uint256 private countB; - constructor() ERC20("Silver", "SLV") { - _mint(msg.sender, 1000000000000000000000); - } + constructor() ERC20("Silver", "SLV") {} function incrementCountA() public { countA = countA + 1; @@ -20,4 +18,8 @@ contract SLVToken is ERC20 { } receive() external payable {} + + function destroy() public { + selfdestruct(payable(msg.sender)); + } } diff --git a/test/contract/package.json b/test/contract/package.json index 98733292..096d3b4e 100644 --- a/test/contract/package.json +++ b/test/contract/package.json @@ -15,7 +15,8 @@ "dependencies": { "@openzeppelin/contracts": "^4.0.0", "fastify": "^3.14.2", - "hardhat": "^2.2.0" + "hardhat": "^2.2.0", + "solidity-create2-deployer": "^0.4.0" }, "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.0.2", diff --git a/test/contract/src/index.js b/test/contract/src/index.js index 35048d98..d827aa77 100644 --- a/test/contract/src/index.js +++ b/test/contract/src/index.js @@ -1,7 +1,13 @@ const fastify = require('fastify')({ logger: true }); const hre = require("hardhat"); -const { getStorageSlotKey } = require('./utils'); +const { + deployContract, + isDeployed +} = require("solidity-create2-deployer"); +const { getStorageSlotKey, deployCreate2Factory } = require('./utils'); + +const CREATE2_FACTORY_ADDRESS = '0x4a27c059FD7E383854Ea7DE6Be9c390a795f6eE3' // readiness check fastify.get('/v1/healthz', async (req, reply) => { @@ -77,6 +83,20 @@ fastify.get('/v1/deploySLVContract', async (req, reply) => { } }); +fastify.get('/v1/destroySLVContract', async (req, reply) => { + const addr = req.query.addr; + + const SLVToken = await hre.ethers.getContractFactory("SLVToken"); + const token = SLVToken.attach(addr); + + const tx = await token.destroy(); + const receipt = await tx.wait(); + + return { + blockNumber: receipt.blockNumber, + } +}) + fastify.get('/v1/incrementCountA', async (req, reply) => { const addr = req.query.addr; @@ -116,6 +136,43 @@ fastify.get('/v1/getStorageKey', async (req, reply) => { } }); +fastify.get('/v1/create2Contract', async (req, reply) => { + const contract = req.query.contract; + const salt = req.query.salt; + + const provider = hre.ethers.provider; + const signer = await hre.ethers.getSigner(); + const isFactoryDeployed = await isDeployed(CREATE2_FACTORY_ADDRESS, provider); + + if (!isFactoryDeployed) { + await deployCreate2Factory(provider, signer) + } + + const contractFactory = await hre.ethers.getContractFactory(contract); + const bytecode = contractFactory.bytecode; + const constructorTypes = []; + const constructorArgs = []; + + const { txHash, address, receipt } = await deployContract({ + salt, + contractBytecode: bytecode, + constructorTypes: constructorTypes, + constructorArgs: constructorArgs, + signer + }); + + const success = await isDeployed(address, provider); + + if (success) { + return { + address, + txHash, + blockNumber: receipt.blockNumber, + blockHash: receipt.blockHash, + } + } +}); + async function main() { try { await fastify.listen(3000, '0.0.0.0'); diff --git a/test/contract/src/utils.js b/test/contract/src/utils.js index ecfa9e2e..e8f7bfa1 100644 --- a/test/contract/src/utils.js +++ b/test/contract/src/utils.js @@ -1,5 +1,8 @@ const { artifacts } = require("hardhat") const { utils, BigNumber } = require("ethers") +const { deployFactory } = require("solidity-create2-deployer"); + +const CREATE2_FACTORY_ACCOUNT = '0x2287Fa6efdEc6d8c3E0f4612ce551dEcf89A357A'; async function getStorageLayout(contractName) { const artifact = await artifacts.readArtifact(contractName); @@ -34,4 +37,18 @@ async function getStorageSlotKey(contractName, variableName) { return key }; -module.exports = { getStorageSlotKey } +async function deployCreate2Factory(provider, signer) { + // Send eth to account as required to deploy create2 factory contract. + let tx = { + to: CREATE2_FACTORY_ACCOUNT, + value: utils.parseEther('0.01') + } + + const txResponse = await signer.sendTransaction(tx); + await txResponse.wait() + + // Deploy create2 factory contract. + await deployFactory(provider) +} + +module.exports = { getStorageSlotKey, deployCreate2Factory } diff --git a/test/helper.go b/test/helper.go index c8ff01fe..0bca98de 100644 --- a/test/helper.go +++ b/test/helper.go @@ -109,6 +109,19 @@ func DeploySLVContract() (*ContractDeployed, error) { return &contract, nil } +func DestroySLVContract(addr string) (*ContractDestroyed, error) { + res, err := http.Get(fmt.Sprintf("%s/v1/destroySLVContract?addr=%s", srvUrl, addr)) + if err != nil { + return nil, err + } + defer res.Body.Close() + + var data ContractDestroyed + decoder := json.NewDecoder(res.Body) + + return &data, decoder.Decode(&data) +} + func IncrementCount(addr string, count string) (*CountIncremented, error) { res, err := http.Get(fmt.Sprintf("%s/v1/incrementCount%s?addr=%s", srvUrl, count, addr)) if err != nil { @@ -156,3 +169,21 @@ func ClearWatchedAddresses(gethRPCClient *rpc.Client) error { return nil } + +func Create2Contract(contractName string, salt string) (*ContractDeployed, error) { + res, err := http.Get(fmt.Sprintf("%s/v1/create2Contract?contract=%s&salt=%s", srvUrl, contractName, salt)) + if err != nil { + return nil, err + } + defer res.Body.Close() + + var contract ContractDeployed + + decoder := json.NewDecoder(res.Body) + err = decoder.Decode(&contract) + if err != nil { + return nil, err + } + + return &contract, nil +} diff --git a/test/watched_address_gap_filling_service_integration_test.go b/test/watched_address_gap_filling_service_integration_test.go index 34617b81..d3e0eac8 100644 --- a/test/watched_address_gap_filling_service_integration_test.go +++ b/test/watched_address_gap_filling_service_integration_test.go @@ -60,6 +60,8 @@ var _ = Describe("Watched address gap filling service integration test", func() updatedCountB2 = big.NewInt(1) SLV2CountBIncrementedAt *integration.CountIncremented + + contract1Salt = "contract1SLV" ) BeforeEach(func() { @@ -89,7 +91,7 @@ var _ = Describe("Watched address gap filling service integration test", func() Expect(ipldErr).ToNot(HaveOccurred()) // Deploy two SLV contracts and update storage slots - SLV1, contractErr = integration.DeploySLVContract() + SLV1, contractErr = integration.Create2Contract("SLVToken", contract1Salt) Expect(contractErr).ToNot(HaveOccurred()) _, txErr = integration.IncrementCount(SLV1.Address, "A") @@ -114,8 +116,12 @@ var _ = Describe("Watched address gap filling service integration test", func() }) defer It("test cleanup", func() { + // Destroy create2 contract + _, err := integration.DestroySLVContract(SLV1.Address) + Expect(err).ToNot(HaveOccurred()) + // Clear out watched addresses - err := integration.ClearWatchedAddresses(gethRPCClient) + err = integration.ClearWatchedAddresses(gethRPCClient) Expect(err).ToNot(HaveOccurred()) }) @@ -206,24 +212,30 @@ var _ = Describe("Watched address gap filling service integration test", func() }) It("indexes missing past state on watching a contract from an earlier 'created_at'", func() { - // Clear out watched addresses - err := integration.ClearWatchedAddresses(gethRPCClient) - Expect(err).ToNot(HaveOccurred()) + // Remove SLV2 (created_at -> countB incremented) watched contract + args := []sdtypes.WatchAddressArg{ + { + Address: SLV2.Address, + CreatedAt: uint64(SLV2CountBIncrementedAt.BlockNumber), + }, + } + ipldErr := ipldRPCClient.Call(nil, ipldMethod, statediff.Remove, args) + Expect(ipldErr).ToNot(HaveOccurred()) // Watch SLV2 (created_at -> deployment) contract - args := []sdtypes.WatchAddressArg{ + args = []sdtypes.WatchAddressArg{ { Address: SLV2.Address, CreatedAt: uint64(SLV2.BlockNumber), }, } - ipldErr := ipldRPCClient.Call(nil, ipldMethod, statediff.Add, args) + ipldErr = ipldRPCClient.Call(nil, ipldMethod, statediff.Add, args) Expect(ipldErr).ToNot(HaveOccurred()) // Sleep for service interval + few extra seconds time.Sleep(time.Duration(serviceInterval+2) * time.Second) - // WatchedAddresses = [SLV2] + // WatchedAddresses = [GLD1, SLV1, SLV2] // SLV2, countA countA2Storage, err := ipldClient.StorageAt(ctx, common.HexToAddress(SLV2.Address), common.HexToHash(countAIndex), nil) Expect(err).ToNot(HaveOccurred()) @@ -237,4 +249,73 @@ var _ = Describe("Watched address gap filling service integration test", func() Expect(countB2.String()).To(Equal(updatedCountB2.String())) }) }) + + Context("destroyed contract redeployed and watched", func() { + It("returns zero value for destroyed contract", func() { + _, err := integration.DestroySLVContract(SLV1.Address) + Expect(err).ToNot(HaveOccurred()) + + updatedCountA1 = big.NewInt(0) + + operation := statediff.Remove + args := []sdtypes.WatchAddressArg{ + { + Address: SLV1.Address, + CreatedAt: uint64(SLV1.BlockNumber), + }, + } + ipldErr := ipldRPCClient.Call(nil, ipldMethod, operation, args) + Expect(ipldErr).ToNot(HaveOccurred()) + + // WatchedAddresses = [GLD1, SLV2] + // SLV1, countA + countA1Storage, err := ipldClient.StorageAt(ctx, common.HexToAddress(SLV1.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA1 := new(big.Int).SetBytes(countA1Storage) + Expect(countA1.String()).To(Equal(updatedCountA1.String())) + oldCountA1.Set(updatedCountA1) + }) + + It("indexes state for redeployed contract", func() { + // Redeploy contract + SLV1, contractErr = integration.Create2Contract("SLVToken", contract1Salt) + Expect(contractErr).ToNot(HaveOccurred()) + + // Add to watched address + operation := statediff.Add + args := []sdtypes.WatchAddressArg{ + { + Address: SLV1.Address, + CreatedAt: uint64(SLV1.BlockNumber), + }, + } + ipldErr := ipldRPCClient.Call(nil, ipldMethod, operation, args) + Expect(ipldErr).ToNot(HaveOccurred()) + + // Sleep for service interval + few extra seconds + time.Sleep(time.Duration(serviceInterval+2) * time.Second) + + // WatchedAddresses = [GLD1, SLV2, SLV1] + // SLV1, countA + + // TODO: Fix old storage value fetched after redeploying to same address. + // countA1Storage, err := ipldClient.StorageAt(ctx, common.HexToAddress(SLV1.Address), common.HexToHash(countAIndex), nil) + // Expect(err).ToNot(HaveOccurred()) + // countA1 := new(big.Int).SetBytes(countA1Storage) + // Expect(countA1.String()).To(Equal(updatedCountA1.String())) + // oldCountA1.Set(updatedCountA1) + + // Update storage slots + _, txErr = integration.IncrementCount(SLV1.Address, "A") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + updatedCountA1.Add(updatedCountA1, big.NewInt(1)) + + countA1AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(SLV1.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA1AfterIncrement := new(big.Int).SetBytes(countA1AfterIncrementStorage) + Expect(countA1AfterIncrement.String()).To(Equal(updatedCountA1.String())) + oldCountA1.Set(updatedCountA1) + }) + }) })