diff --git a/test/contract/contracts/SLVToken.sol b/test/contract/contracts/SLVToken.sol new file mode 100644 index 00000000..9c6d89b0 --- /dev/null +++ b/test/contract/contracts/SLVToken.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract SLVToken is ERC20 { + uint256 private countA; + uint256 private countB; + + constructor() ERC20("Silver", "SLV") {} + + function incrementCountA() public { + countA = countA + 1; + } + + function incrementCountB() public { + countB = countB + 1; + } + + receive() external payable {} + + function destroy() public { + selfdestruct(payable(msg.sender)); + } +} diff --git a/test/contract/hardhat.config.js b/test/contract/hardhat.config.js index b474482c..c452b963 100644 --- a/test/contract/hardhat.config.js +++ b/test/contract/hardhat.config.js @@ -17,7 +17,18 @@ task("accounts", "Prints the list of accounts", async () => { * @type import('hardhat/config').HardhatUserConfig */ module.exports = { - solidity: "0.8.0", + solidity: { + version: "0.8.0", + settings: { + outputSelection: { + '*': { + '*': [ + 'abi', 'storageLayout', + ] + } + } + } + }, networks: { local: { url: 'http://127.0.0.1:8545', diff --git a/test/contract/src/index.js b/test/contract/src/index.js index b20d54a2..ff06cf88 100644 --- a/test/contract/src/index.js +++ b/test/contract/src/index.js @@ -1,6 +1,7 @@ const fastify = require('fastify')({ logger: true }); const hre = require("hardhat"); +const { getStorageSlotKey } = require('./utils'); // readiness check fastify.get('/v1/healthz', async (req, reply) => { @@ -63,6 +64,72 @@ fastify.get('/v1/sendEth', async (req, reply) => { } }); +fastify.get('/v1/deploySLVContract', async (req, reply) => { + const SLVToken = await hre.ethers.getContractFactory("SLVToken"); + const token = await SLVToken.deploy(); + const receipt = await token.deployTransaction.wait(); + + return { + address: token.address, + txHash: token.deployTransaction.hash, + blockNumber: receipt.blockNumber, + blockHash: receipt.blockHash, + } +}); + +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; + + const SLVToken = await hre.ethers.getContractFactory("SLVToken"); + const token = await SLVToken.attach(addr); + + const tx = await token.incrementCountA(); + const receipt = await tx.wait(); + + return { + blockNumber: receipt.blockNumber, + } +}); + +fastify.get('/v1/incrementCountB', async (req, reply) => { + const addr = req.query.addr; + + const SLVToken = await hre.ethers.getContractFactory("SLVToken"); + const token = await SLVToken.attach(addr); + + const tx = await token.incrementCountB(); + const receipt = await tx.wait(); + + return { + blockNumber: receipt.blockNumber, + } +}); + +fastify.get('/v1/getStorageKey', async (req, reply) => { + const contract = req.query.contract; + const label = req.query.label; + + const key = await getStorageSlotKey(contract, label) + + return { + key + } +}); + 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 new file mode 100644 index 00000000..be7ed0ef --- /dev/null +++ b/test/contract/src/utils.js @@ -0,0 +1,36 @@ +const { artifacts } = require("hardhat") +const { utils, BigNumber } = require("ethers") + + +async function getStorageLayout(contractName) { + const artifact = await artifacts.readArtifact(contractName); + const buildInfo = await artifacts.getBuildInfo(`${artifact.sourceName}:${artifact.contractName}`); + + if (!buildInfo) { + throw new Error('storageLayout not present in compiler output.'); + } + + const output = buildInfo.output; + const { storageLayout } = output.contracts[artifact.sourceName][artifact.contractName]; + + if (!storageLayout) { + throw new Error('Contract hasn\'t been compiled.'); + } + + return storageLayout; +}; + +async function getStorageSlotKey(contractName, variableName) { + storageLayout = await getStorageLayout(contractName) + + const { storage } = storageLayout; + const targetState = storage.find((state) => state.label === variableName); + + // Throw if state variable could not be found in storage layout. + if (!targetState) { + throw new Error('Variable not present in storage layout.'); + } + + key = utils.hexlify(BigNumber.from(targetState.slot)); + return key +}; diff --git a/test/helper.go b/test/helper.go index 5cd8ee2a..41167622 100644 --- a/test/helper.go +++ b/test/helper.go @@ -5,6 +5,9 @@ import ( "fmt" "math/big" "net/http" + + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/statediff/types" ) type ContractDeployed struct { @@ -27,6 +30,14 @@ type Tx struct { BlockHash string `json:"blockHash"` } +type StorageKey struct { + Key string `json:"key"` +} + +type CountIncremented struct { + BlockNumber int64 `json:"blockNumber"` +} + const srvUrl = "http://localhost:3000" func DeployContract() (*ContractDeployed, error) { @@ -77,3 +88,82 @@ func SendEth(to string, value string) (*Tx, error) { return &tx, nil } + +func DeploySLVContract() (*ContractDeployed, error) { + res, err := http.Get(fmt.Sprintf("%s/v1/deploySLVContract", srvUrl)) + 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 +} + +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 { + return nil, err + } + + var blockNumber CountIncremented + + decoder := json.NewDecoder(res.Body) + err = decoder.Decode(&blockNumber) + if err != nil { + return nil, err + } + + return &blockNumber, nil +} + +func GetStorageSlotKey(contract string, label string) (*StorageKey, error) { + res, err := http.Get(fmt.Sprintf("%s/v1/getStorageKey?contract=%s&label=%s", srvUrl, contract, label)) + if err != nil { + return nil, err + } + defer res.Body.Close() + + var key StorageKey + + decoder := json.NewDecoder(res.Body) + err = decoder.Decode(&key) + if err != nil { + return nil, err + } + + return &key, nil +} + +func ClearWatchedAddresses(gethRPCClient *rpc.Client) error { + gethMethod := "statediff_watchAddress" + args := []types.WatchAddressArg{} + + // Clear watched addresses + gethErr := gethRPCClient.Call(nil, gethMethod, types.Clear, args) + if gethErr != nil { + return gethErr + } + + return nil +} diff --git a/test/watch_address_integration_test.go b/test/watch_address_integration_test.go new file mode 100644 index 00000000..8c0f11f4 --- /dev/null +++ b/test/watch_address_integration_test.go @@ -0,0 +1,674 @@ +package integration_test + +import ( + "context" + "math/big" + "os" + "strconv" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/statediff/types" + sdtypes "github.com/ethereum/go-ethereum/statediff/types" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + integration "github.com/vulcanize/ipld-eth-server/test" +) + +var ( + gethMethod = "statediff_watchAddress" + ipldMethod = "vdb_watchAddress" + + sleepInterval = 2 * time.Second +) + +var _ = Describe("WatchAddress integration test", func() { + dbWrite, err := strconv.ParseBool(os.Getenv("DB_WRITE")) + Expect(err).To(BeNil()) + + watchedAddressServiceEnabled, err := strconv.ParseBool(os.Getenv("WATCHED_ADDRESS_GAP_FILLER_ENABLED")) + Expect(err).To(BeNil()) + + gethHttpPath := "http://127.0.0.1:8545" + gethRPCClient, err := rpc.Dial(gethHttpPath) + Expect(err).ToNot(HaveOccurred()) + + ipldEthHttpPath := "http://127.0.0.1:8081" + ipldClient, err := ethclient.Dial(ipldEthHttpPath) + Expect(err).ToNot(HaveOccurred()) + ipldRPCClient, err := rpc.Dial(ipldEthHttpPath) + Expect(err).ToNot(HaveOccurred()) + + var ( + ctx = context.Background() + + txErr error + contractErr error + incErr error + + contract1 *integration.ContractDeployed + contract2 *integration.ContractDeployed + contract3 *integration.ContractDeployed + + countAIndex string + + prevBalance1 *big.Int + prevBalance2 *big.Int + prevBalance3 *big.Int + + actualBalance1 *big.Int + actualBalance2 *big.Int + actualBalance3 *big.Int + + prevCountA1 *big.Int + prevCountA2 *big.Int + prevCountA3 *big.Int + + actualCountA1 *big.Int + actualCountA2 *big.Int + actualCountA3 *big.Int + ) + + BeforeEach(func() { + if !dbWrite || watchedAddressServiceEnabled { + Skip("skipping WatchAddress API integration tests") + } + }) + + It("test init", func() { + // Deploy three contracts + contract1, contractErr = integration.DeploySLVContract() + Expect(contractErr).ToNot(HaveOccurred()) + + contract2, contractErr = integration.DeploySLVContract() + Expect(contractErr).ToNot(HaveOccurred()) + + contract3, contractErr = integration.DeploySLVContract() + Expect(contractErr).ToNot(HaveOccurred()) + + // Get the storage slot key + storageSlotAKey, err := integration.GetStorageSlotKey("SLVToken", "countA") + Expect(err).ToNot(HaveOccurred()) + countAIndex = storageSlotAKey.Key + + // Clear out watched addresses + err = integration.ClearWatchedAddresses(gethRPCClient) + Expect(err).ToNot(HaveOccurred()) + + // Get initial balances for all the contracts + // Contract 1 + actualBalance1 = big.NewInt(0) + initBalance1, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract1.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(initBalance1.String()).To(Equal(actualBalance1.String())) + prevBalance1 = big.NewInt(0) + + // Contract 2 + actualBalance2 = big.NewInt(0) + initBalance2, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract2.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(initBalance2.String()).To(Equal(actualBalance2.String())) + prevBalance2 = big.NewInt(0) + + // Contract 3 + actualBalance3 = big.NewInt(0) + initBalance3, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract3.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(initBalance3.String()).To(Equal(actualBalance3.String())) + prevBalance3 = big.NewInt(0) + + // Get initial storage values for the contracts + // Contract 1, countA + actualCountA1 = big.NewInt(0) + ipldCountA1Storage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract1.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + ipldCountA1 := new(big.Int).SetBytes(ipldCountA1Storage) + Expect(ipldCountA1.String()).To(Equal(actualCountA1.String())) + prevCountA1 = big.NewInt(0) + + // Contract 2, countA + actualCountA2 = big.NewInt(0) + ipldCountA2Storage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract2.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + ipldCountA2 := new(big.Int).SetBytes(ipldCountA2Storage) + Expect(ipldCountA2.String()).To(Equal(actualCountA2.String())) + prevCountA2 = big.NewInt(0) + + // Contract 3, countA + actualCountA3 = big.NewInt(0) + ipldCountA3Storage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract3.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + ipldCountA3 := new(big.Int).SetBytes(ipldCountA3Storage) + Expect(ipldCountA3.String()).To(Equal(actualCountA2.String())) + prevCountA3 = big.NewInt(0) + }) + + defer It("test cleanup", func() { + // Clear out watched addresses + err := integration.ClearWatchedAddresses(gethRPCClient) + Expect(err).ToNot(HaveOccurred()) + }) + + Context("no contracts being watched", func() { + It("indexes state for all the contracts", func() { + // WatchedAddresses = [] + // Send eth to all three contract accounts + // Contract 1 + _, txErr = integration.SendEth(contract1.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance1.Add(actualBalance1, big.NewInt(10000000000000000)) + + balance1AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract1.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance1AfterTransfer.String()).To(Equal(actualBalance1.String())) + prevBalance1.Set(actualBalance1) + + // Contract 2 + _, txErr = integration.SendEth(contract2.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance2.Add(actualBalance2, big.NewInt(10000000000000000)) + + balance2AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract2.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance2AfterTransfer.String()).To(Equal(actualBalance2.String())) + prevBalance2.Set(actualBalance2) + + // Contract 3 + _, txErr = integration.SendEth(contract3.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance3.Add(actualBalance3, big.NewInt(10000000000000000)) + + balance3AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract3.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance3AfterTransfer.String()).To(Equal(actualBalance3.String())) + prevBalance3.Set(actualBalance3) + + // Increment counts + // Contract 1, countA + _, incErr = integration.IncrementCount(contract1.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA1.Add(actualCountA1, big.NewInt(1)) + + countA1AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract1.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA1AfterIncrement := new(big.Int).SetBytes(countA1AfterIncrementStorage) + Expect(countA1AfterIncrement.String()).To(Equal(actualCountA1.String())) + prevCountA1.Set(actualCountA1) + + // Contract 2, countA + _, incErr = integration.IncrementCount(contract2.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA2.Add(actualCountA2, big.NewInt(1)) + + countA2AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract2.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA2AfterIncrement := new(big.Int).SetBytes(countA2AfterIncrementStorage) + Expect(countA2AfterIncrement.String()).To(Equal(actualCountA2.String())) + prevCountA2.Set(actualCountA2) + + // Contract 3, countA + _, incErr = integration.IncrementCount(contract3.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA3.Add(actualCountA3, big.NewInt(1)) + + countA3AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract3.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA3AfterIncrement := new(big.Int).SetBytes(countA3AfterIncrementStorage) + Expect(countA3AfterIncrement.String()).To(Equal(actualCountA3.String())) + prevCountA3.Set(actualCountA3) + }) + }) + + Context("one contract being watched", func() { + It("indexes state only for the watched contract", func() { + operation := types.Add + args := []types.WatchAddressArg{ + { + Address: contract1.Address, + CreatedAt: uint64(contract1.BlockNumber), + }, + } + ipldErr := ipldRPCClient.Call(nil, ipldMethod, operation, args) + Expect(ipldErr).ToNot(HaveOccurred()) + + // WatchedAddresses = [Contract1] + // Send eth to all three contract accounts + // Contract 1 + _, txErr = integration.SendEth(contract1.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance1.Add(actualBalance1, big.NewInt(10000000000000000)) + + balance1AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract1.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance1AfterTransfer.String()).To(Equal(actualBalance1.String())) + prevBalance1.Set(actualBalance1) + + // Contract 2 + _, txErr = integration.SendEth(contract2.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance2.Add(actualBalance2, big.NewInt(10000000000000000)) + + balance2AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract2.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance2AfterTransfer.String()).To(Equal(prevBalance2.String())) + + // Contract 3 + _, txErr = integration.SendEth(contract3.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance3.Add(actualBalance3, big.NewInt(10000000000000000)) + + balance3AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract3.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance3AfterTransfer.String()).To(Equal(prevBalance3.String())) + + // Increment counts + // Contract 1, countA + _, incErr = integration.IncrementCount(contract1.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA1.Add(actualCountA1, big.NewInt(1)) + + countA1AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract1.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA1AfterIncrement := new(big.Int).SetBytes(countA1AfterIncrementStorage) + Expect(countA1AfterIncrement.String()).To(Equal(actualCountA1.String())) + prevCountA1.Set(actualCountA1) + + // Contract 2, countA + _, incErr = integration.IncrementCount(contract2.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA2.Add(actualCountA2, big.NewInt(1)) + + countA2AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract2.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA2AfterIncrement := new(big.Int).SetBytes(countA2AfterIncrementStorage) + Expect(countA2AfterIncrement.String()).To(Equal(prevCountA2.String())) + + // Contract 3, countA + _, incErr = integration.IncrementCount(contract3.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA3.Add(actualCountA3, big.NewInt(1)) + + countA3AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract3.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA3AfterIncrement := new(big.Int).SetBytes(countA3AfterIncrementStorage) + Expect(countA3AfterIncrement.String()).To(Equal(prevCountA3.String())) + }) + }) + + Context("contract added to a non-empty watch-list", func() { + It("indexes state only for the watched contracts", func() { + operation := types.Add + args := []types.WatchAddressArg{ + { + Address: contract2.Address, + CreatedAt: uint64(contract2.BlockNumber), + }, + } + ipldErr := ipldRPCClient.Call(nil, ipldMethod, operation, args) + Expect(ipldErr).ToNot(HaveOccurred()) + + // WatchedAddresses = [Contract1, Contract2] + // Send eth to all three contract accounts + // Contract 1 + _, txErr = integration.SendEth(contract1.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance1.Add(actualBalance1, big.NewInt(10000000000000000)) + + balance1AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract1.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance1AfterTransfer.String()).To(Equal(actualBalance1.String())) + prevBalance1.Set(actualBalance1) + + // Contract 2 + _, txErr = integration.SendEth(contract2.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance2.Add(actualBalance2, big.NewInt(10000000000000000)) + + balance2AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract2.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance2AfterTransfer.String()).To(Equal(actualBalance2.String())) + prevBalance2.Set(actualBalance2) + + // Contract 3 + _, txErr = integration.SendEth(contract3.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance3.Add(actualBalance3, big.NewInt(10000000000000000)) + + balance3AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract3.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance3AfterTransfer.String()).To(Equal(prevBalance3.String())) + + // Increment counts + // Contract 1, countA + _, incErr = integration.IncrementCount(contract1.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA1.Add(actualCountA1, big.NewInt(1)) + + countA1AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract1.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA1AfterIncrement := new(big.Int).SetBytes(countA1AfterIncrementStorage) + Expect(countA1AfterIncrement.String()).To(Equal(actualCountA1.String())) + prevCountA1.Set(actualCountA1) + + // Contract 2, countA + _, incErr = integration.IncrementCount(contract2.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA2.Add(actualCountA2, big.NewInt(1)) + + countA2AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract2.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA2AfterIncrement := new(big.Int).SetBytes(countA2AfterIncrementStorage) + Expect(countA2AfterIncrement.String()).To(Equal(actualCountA2.String())) + prevCountA2.Set(actualCountA2) + + // Contract 3, countA + _, incErr = integration.IncrementCount(contract3.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA3.Add(actualCountA3, big.NewInt(1)) + + countA3AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract3.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA3AfterIncrement := new(big.Int).SetBytes(countA3AfterIncrementStorage) + Expect(countA3AfterIncrement.String()).To(Equal(prevCountA3.String())) + }) + }) + + Context("contract removed from the watch-list", func() { + It("indexes state only for the watched contract", func() { + operation := types.Remove + args := []types.WatchAddressArg{ + { + Address: contract1.Address, + CreatedAt: uint64(contract1.BlockNumber), + }, + } + ipldErr := ipldRPCClient.Call(nil, ipldMethod, operation, args) + Expect(ipldErr).ToNot(HaveOccurred()) + + // WatchedAddresses = [Contract2] + // Send eth to all three contract accounts + // Contract 1 + _, txErr = integration.SendEth(contract1.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance1.Add(actualBalance1, big.NewInt(10000000000000000)) + + balance1AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract1.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance1AfterTransfer.String()).To(Equal(prevBalance1.String())) + + // Contract 2 + _, txErr = integration.SendEth(contract2.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance2.Add(actualBalance2, big.NewInt(10000000000000000)) + + balance2AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract2.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance2AfterTransfer.String()).To(Equal(actualBalance2.String())) + prevBalance2.Set(actualBalance2) + + // Contract 3 + _, txErr = integration.SendEth(contract3.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance3.Add(actualBalance3, big.NewInt(10000000000000000)) + + balance3AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract3.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance3AfterTransfer.String()).To(Equal(prevBalance3.String())) + + // Increment counts + // Contract 1, countA + _, incErr = integration.IncrementCount(contract1.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA1.Add(actualCountA1, big.NewInt(1)) + + countA1AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract1.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA1AfterIncrement := new(big.Int).SetBytes(countA1AfterIncrementStorage) + Expect(countA1AfterIncrement.String()).To(Equal(prevCountA1.String())) + + // Contract 2, countA + _, incErr = integration.IncrementCount(contract2.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA2.Add(actualCountA2, big.NewInt(1)) + + countA2AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract2.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA2AfterIncrement := new(big.Int).SetBytes(countA2AfterIncrementStorage) + Expect(countA2AfterIncrement.String()).To(Equal(actualCountA2.String())) + prevCountA2.Set(actualCountA2) + + // Contract 3, countA + _, incErr = integration.IncrementCount(contract3.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA3.Add(actualCountA3, big.NewInt(1)) + + countA3AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract3.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA3AfterIncrement := new(big.Int).SetBytes(countA3AfterIncrementStorage) + Expect(countA3AfterIncrement.String()).To(Equal(prevCountA3.String())) + }) + }) + + Context("list of watched addresses set", func() { + It("indexes state only for the watched contracts", func() { + operation := types.Set + args := []types.WatchAddressArg{ + { + Address: contract1.Address, + CreatedAt: uint64(contract1.BlockNumber), + }, + { + Address: contract3.Address, + CreatedAt: uint64(contract3.BlockNumber), + }, + } + ipldErr := ipldRPCClient.Call(nil, ipldMethod, operation, args) + Expect(ipldErr).ToNot(HaveOccurred()) + + // WatchedAddresses = [Contract1, Contract3] + // Send eth to all three contract accounts + // Contract 1 + _, txErr = integration.SendEth(contract1.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance1.Add(actualBalance1, big.NewInt(10000000000000000)) + + balance1AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract1.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance1AfterTransfer.String()).To(Equal(actualBalance1.String())) + prevBalance1.Set(actualBalance1) + + // Contract 2 + _, txErr = integration.SendEth(contract2.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance2.Add(actualBalance2, big.NewInt(10000000000000000)) + + balance2AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract2.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance2AfterTransfer.String()).To(Equal(prevBalance2.String())) + + // Contract 3 + _, txErr = integration.SendEth(contract3.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance3.Add(actualBalance3, big.NewInt(10000000000000000)) + + balance3AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract3.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance3AfterTransfer.String()).To(Equal(actualBalance3.String())) + prevBalance3.Set(actualBalance3) + + // Increment counts + // Contract 1, countA + _, incErr = integration.IncrementCount(contract1.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA1.Add(actualCountA1, big.NewInt(1)) + + countA1AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract1.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA1AfterIncrement := new(big.Int).SetBytes(countA1AfterIncrementStorage) + Expect(countA1AfterIncrement.String()).To(Equal(actualCountA1.String())) + prevCountA1.Set(actualCountA1) + + // Contract 2, countA + _, incErr = integration.IncrementCount(contract2.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA2.Add(actualCountA2, big.NewInt(1)) + + countA2AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract2.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA2AfterIncrement := new(big.Int).SetBytes(countA2AfterIncrementStorage) + Expect(countA2AfterIncrement.String()).To(Equal(prevCountA2.String())) + + // Contract 3, countA + _, incErr = integration.IncrementCount(contract3.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA3.Add(actualCountA3, big.NewInt(1)) + + countA3AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract3.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA3AfterIncrement := new(big.Int).SetBytes(countA3AfterIncrementStorage) + Expect(countA3AfterIncrement.String()).To(Equal(actualCountA3.String())) + prevCountA3.Set(actualCountA3) + }) + }) + + Context("list of watched addresses cleared", func() { + It("indexes state for all the contracts", func() { + operation := types.Clear + args := []types.WatchAddressArg{} + ipldErr := ipldRPCClient.Call(nil, ipldMethod, operation, args) + Expect(ipldErr).ToNot(HaveOccurred()) + + // WatchedAddresses = [] + // Send eth to all three contract accounts + // Contract 1 + _, txErr = integration.SendEth(contract1.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance1.Add(actualBalance1, big.NewInt(10000000000000000)) + + balance1AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract1.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance1AfterTransfer.String()).To(Equal(actualBalance1.String())) + prevBalance1.Set(actualBalance1) + + // Contract 2 + _, txErr = integration.SendEth(contract2.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance2.Add(actualBalance2, big.NewInt(10000000000000000)) + + balance2AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract2.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance2AfterTransfer.String()).To(Equal(actualBalance2.String())) + prevBalance2.Set(actualBalance2) + + // Contract 3 + _, txErr = integration.SendEth(contract3.Address, "0.01") + time.Sleep(sleepInterval) + Expect(txErr).ToNot(HaveOccurred()) + actualBalance3.Add(actualBalance3, big.NewInt(10000000000000000)) + + balance3AfterTransfer, err := ipldClient.BalanceAt(ctx, common.HexToAddress(contract3.Address), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(balance3AfterTransfer.String()).To(Equal(actualBalance3.String())) + prevBalance3.Set(actualBalance3) + + // Increment counts + // Contract 1, countA + _, incErr = integration.IncrementCount(contract1.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA1.Add(actualCountA1, big.NewInt(1)) + + countA1AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract1.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA1AfterIncrement := new(big.Int).SetBytes(countA1AfterIncrementStorage) + Expect(countA1AfterIncrement.String()).To(Equal(actualCountA1.String())) + prevCountA1.Set(actualCountA1) + + // Contract 2, countA + _, incErr = integration.IncrementCount(contract2.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA2.Add(actualCountA2, big.NewInt(1)) + + countA2AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract2.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA2AfterIncrement := new(big.Int).SetBytes(countA2AfterIncrementStorage) + Expect(countA2AfterIncrement.String()).To(Equal(actualCountA2.String())) + prevCountA2.Set(actualCountA2) + + // Contract 3, countA + _, incErr = integration.IncrementCount(contract3.Address, "A") + time.Sleep(sleepInterval) + Expect(incErr).ToNot(HaveOccurred()) + actualCountA3.Add(actualCountA3, big.NewInt(1)) + + countA3AfterIncrementStorage, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract3.Address), common.HexToHash(countAIndex), nil) + Expect(err).ToNot(HaveOccurred()) + countA3AfterIncrement := new(big.Int).SetBytes(countA3AfterIncrementStorage) + Expect(countA3AfterIncrement.String()).To(Equal(actualCountA3.String())) + prevCountA3.Set(actualCountA3) + }) + }) + + Context("with invalid args", func() { + It("returns an error on invalid operation arg", func() { + operation := "WrongOp" + args := []sdtypes.WatchAddressArg{} + + gethErr := gethRPCClient.Call(nil, gethMethod, operation, args) + Expect(gethErr).To(HaveOccurred()) + + ipldErr := ipldRPCClient.Call(nil, ipldMethod, operation, args) + Expect(ipldErr).To(HaveOccurred()) + + Expect(ipldErr).To(Equal(gethErr)) + }) + + It("returns an error on args of invalid type", func() { + operation := types.Add + args := []string{"WrongArg"} + + gethErr := gethRPCClient.Call(nil, gethMethod, operation, args) + Expect(gethErr).To(HaveOccurred()) + + ipldErr := ipldRPCClient.Call(nil, ipldMethod, operation, args) + Expect(ipldErr).To(HaveOccurred()) + + Expect(ipldErr).To(Equal(gethErr)) + }) + }) +})