[WIP] Add a service to fill indexing gap for watched addresses #135

Closed
prathamesh0 wants to merge 16 commits from pm-watched-addresses into master
6 changed files with 203 additions and 14 deletions
Showing only changes of commit 6ceb58b3e0 - Show all commits

View File

@ -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));
}
}

View File

@ -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",

View File

@ -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');

View File

@ -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 }

View File

@ -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
}

View File

@ -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)
})
})
})