State Expiry is a mechanism to remove expired data from the blockchain temporarily. Expired data can no longer be accessed temporarily, unless it is revived through an operation called "state revive". By implementing State Expiry, we are able to tackle the state bloat problem, reducing the storage requirements to run a node over the long run.
This repository contains the setup guide to test the State Expiry Proof-of-Concept (PoC) implemented by the NodeReal development team. The POC is based on BEP-206 and BEP-215.
- Golang 1.19 or higher
- nodeJS 18 or higher
- Clone repo and submodules.
git clone --recursive https://github.com/node-real/state-expiry-poc.git- Prepare state expiry binary, and clone BSC repo with
state_expiry_developbranch.
git clone https://github.com/node-real/bsc --branch state_expiry_develop bsc-state-expiry- Compile
gethandbootnode, then copy tobinfolder
cd bsc-state-expiry
make geth && go build -o bootnode ./cmd/bootnode
mkdir -p ../state-expiry-poc/bin && cp build/bin/geth bootnode ../state-expiry-poc/binThe complete flow of testing the state expiry features may look something like:
- Deploy nodes
- Deploy token contract
- Read token balance
- Wait for contract state to be expired
- Read token balance again (expired error is returned)
- Send revive transaction to revive contract state
- Read token balance
- Stop nodes
When a contract is deployed, you should wait for 2 epochs (100 blocks) so that its state is expired. Here's the table reference:
| Epoch | Block Number (Decimal) | Block Number (Hex) |
|---|---|---|
| 0 | 0 | 0x0 |
| 1 | 50 | 0x32 |
| 2 | 100 | 0x64 |
| 3 | 150 | 0x96 |
Now, we need enter state-expiry-poc to run testing.
cd state-expiry-pocAll setup scripts are available in state-expiry-poc/scripts. By default, it will create 3 nodes config, and start them.
# Deploying 3 archive nodes
# modifies the genesis such that the first account of scripts/asset/test_account.json is the initBnbHolder
bash scripts/clusterup_set_first.sh startbash scripts/clusterup_pruning_test.sh startbash scripts/clusterup_set_first.sh stopThen enter state-expiry-poc/test-contract/deploy-BEP20 to run all BEP20 scripts.
It mocks a fake BEP20 token to test contract slots expiry scenarios.
cd test-contract/deploy-BEP20# install dependencies
npm install
# deploy BEP20 Token
npx hardhat run scripts/deploy.js# This will transfer BEP20 Token from scripts/asset/test_account.json first account
# This script only transfer once
npx hardhat run scripts/transfer.js
# read sender & receiver's balance
npx hardhat run scripts/read-balance.jsNow, you could use the default users to transfer tokens, but when you stop using on-chain interactions after a period of time, only readBalance will work because it doesn't trigger on-chain state access.
Using the default config, after about 20 minutes, you cannot directly use the script to transfer tokens, as you may get Access expired state error.
In this case, you need to revive state. For details, see test test_bep20_witness_revive.
Then enter state-expiry-poc/test-script to run all golang scripts.
cd test-script# The script keeps sending transactions until ctrl-c
go run test_bnb_transfer.goWhen your BEP20 token contract got Access expired state error, you should call below script to revive your tx's accessed states.
It will call eth_estimateGasAndReviveState API first and got revive Witness, then it will send a ReviveStateTx with Witness to revive all your expired states, and execute the tx as normal.
# The script will revive sender & receiver state, and exe a new token transfer
go run test_bep20_witness_revive.go# query block height
curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":83}' 127.0.0.1:8502
# query block by number
curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x3", true],"id":83}' 127.0.0.1:8502
# query tx by hash
curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionByHash","params":["0x12beecfb1adb7d874c4714a7871e23cf70baef612235d1276568611460927f18"],"id":83}' 127.0.0.1:8502
# query tx receipt
curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0x782192568c8ee3393e3f3e9b7ac46e231d3cbe0b96941b642e28220ba343209b"],"id":83}' 127.0.0.1:8502The default genesis config is shown below:
{
"config": {
"chainId": 714,
"homesteadBlock": 0,
"eip150Block": 0,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"ramanujanBlock": 0,
"nielsBlock": 0,
"mirrorSyncBlock": 1,
"brunoBlock": 1,
"eulerBlock": 2,
"gibbsBlock": 3,
"moranBlock": 4,
"planckBlock": 5,
"lubanBlock": 6,
"platoBlock": 7,
"claudeBlock": 200,
"elwoodBlock": 400,
"parlia": {
"period": 3,
"epoch": 200,
"stateEpochPeriod": 10
}
}
}The default EpochPeriod is 200, the state will expired in the next 2 epoch, so it will expired in max 20 minutes. claudeBlock is the first hard fork for state expiry, elwoodBlock is the second hard fork and parlia.epoch is the EpochPeriod.