Skip to content

Conversation

matthew1001
Copy link
Contributor

@matthew1001 matthew1001 commented May 22, 2025

PR description

This PR follows on from the first Bonsai Archive PR and enhances it to provide full state proofs for Bonsai Archive state.

  • It introduces a new experimental data storage format X_BONSAI_ARCHIVE_PROOFS
    • I would propose that once proven out in experimental mode, the 2 X_BONSAI_ARCHIVE and X_BONSAI_ARCHIVE_PROOFS options are merged into a single BONSAI_ARCHIVE data storage format. Currently I think it will be useful to have separate options while in experimental to make it easier to recreate any issues with and without the state proof behaviour.
  • I have added a page to the Besu wiki which contains the PDF I presented to the Besu community call. The PDF should provide a reasonable amount of design detail, including key/value formats used.

The aim of this feature is to provide feature parity with FOREST DB and allow us to finally start the process of removing FOREST DB from Besu.

Testing

Aside from the updated tests in the PR which exercise the new flat DB format by adding X_BONSAI_ARCHIVE_PROOFS to various existing tests, I have run a number of QBFT chains using a combination of FOREST, BONSAI, and X_BONSAI ARCHIVE_PROOFS nodes and exercised a variety of state update & state proof requests.

I have configured a node to sync with Ethereum mainnet and it is currently at block 7m without issues:

image

In addition to syncing with mainnet, I've created 2 test scripts which exercise eth_getTransactionCount, eth_getStorageAt, and eth_getProof for known accounts/states in the first 3m blocks. The eth_getProof script uses proofs obtained from a FOREST node synced up to 3+m blocks, and then uses eth_getProof against the BONSAI archive node to check that the proofs returned by BONSAI match those returned by FOREST.

Script for eth_getProof testing on mainnet
#!/bin/bash

nonceResponseMatches () {
echo -n "Checking nonce == $2 for account $3, block $4 - "
if [[ "${1^^}" == "${2^^}" ]]; then
  echo OK
else
  echo "Unexpected JSON/RPC response. $1 != $2"
  exit 1
fi
}

balanceResponseMatches () {
echo -n "Checking balance == $2 for account $3, block $4 - "
if [[ "${1^^}" == "${2^^}" ]]; then
  echo OK
else
  echo "Unexpected JSON/RPC response. $1 != $2"
  exit 1
fi
}

storageResponseMatches () {
echo -n "Checking storage slot $1 == $3 for account $4, block $5 - "
if [[ "${2^^}" == "${3^^}" ]]; then
  echo OK
else
  echo "Unexpected JSON/RPC response. $2 != $3"
  exit 1
fi
}

proofResponseMatches () {
echo -n "Checking proof == $2 for account $3, block $4 - "
if [[ "${1^^}" == "${2^^}" ]]; then
  echo OK
else
  echo "Unexpected JSON/RPC response. $1 != $2"
  exit 1
fi
}

# Retrive the transaction count for a number of different accounts in the first 1,000,000 blocks of Ethereum L1
# Some of these have been specfically selected for accounts that change several times in a block, or that change in several contiguous blocks.

echo 
echo State proof check for Block 150002, account 0x32Be343B94f860124dC4fEe278FDCBD38C102D88
echo
ACCOUNT="0x32Be343B94f860124dC4fEe278FDCBD38C102D88"
BLOCK="0x249F2"
PROOF=`curl -s -X POST --data '{"jsonrpc":"2.0","method": "eth_getProof","params": [ "'$ACCOUNT'", [], "'$BLOCK'"],"id": 1}' http://127.0.0.1:8545 | jq -r .result.accountProof`
PROOF=`echo $PROOF | sed "s/\"/'/g"`
NONCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method": "eth_getProof","params": [ "'$ACCOUNT'", [], "'$BLOCK'"],"id": 1}' http://127.0.0.1:8545 | jq -r .result.nonce`
nonceResponseMatches $NONCE "0x805" $ACCOUNT $BLOCK
BALANCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method": "eth_getProof","params": [ "'$ACCOUNT'", [], "'$BLOCK'"],"id": 1}' http://127.0.0.1:8545 | jq -r .result.balance`
balanceResponseMatches $BALANCE "0x8bf9c8d1322d1741740" $ACCOUNT $BLOCK
EXPECTEDPROOF=`echo '[ "0xf90211a06d095eaaeacdfffd075684f0578cb572fc88606acd332f7c613de8152a6321a2a0979a68d3084102613fd289c4394e6474b692986bcd03705273f1cca529cb1571a0f54ba75f04abfa8c99886369a13562fb1ccfab979c1332404f1a1039f5603b12a03c117529a1c47692faae3f7d6cfa1d01b1a4345e943609aac9e3123721f1a18fa0d591655460aa3f7fcc03f45e0f0a512a3954aa9cf5105345aa70fd24052c0c96a02b0ef78ba5968e66764694429397e5b52a2e86d8dec695cf0c51cea0e776ebdea03a280eb9719f1b9cc58d59e42ee20f5ee4826922ed73451ea7b265dad0c00c21a02cf89409a859fc6f72aebf9f56d2d4ca8991bf07255442eee7a57e18e7712d35a04b3526fa5187227bda323ff89f6347cfa9e3906b26b101a910c0e74b49f44571a04fc9d79b109dce78daddbd8de09ecbcbcfb404b05d94ec3bb24e670378eae21ba0a37eb851d5af0bedd13eb9262491f300f688bf4547b2e1bc9f5c522c3aa36673a0e21abca7946f0cd6750f41c4994ddbcda5e5702d29391f554dcf290e493f6335a027c1cbda50110c884482266ec4c449691e835b1761da3cfb772db6ee0664f3a4a06aa66a16f28af0b1caca724bc2a3b85896254e08c070c751715b5fc1e44b3150a0aef676628e901f0a8b09782ff39fc2721bb69d8f0a58228393f709dd2e2bfef4a0cdcc4b6cd4ab9bcca73ca22836e19430efead4146e77851add1e9428c867dbdd80", "0xf90211a001883aa7e1f64a4ff0da04f7aa5f219d3ccc56e5fb351038df047d6984f0d956a0272952891fec9bb5663025ea019e3cb71b78362f08bc0d864ca196d17bd3a672a076c4c9fbb7d90cc93601f8c79c30afd037940a257062b3441168a91b6a00473ea08de19c85de6ff38585b5d411f37bba44fb90b5609d5e5ea9509e5ca1b5369b6aa0fb5f0a1a6ced0c97d162fda76900798cadeb14e24590b5e55c5ecb9cc5074f48a0c8a1a880604a20721048d5649030f05093b7b5b52b7fd73f5193b6984d385640a063138db8e2ddf2ac2d98743c9f82624a27b1b51b97a2a1069da4db9055192fbba0a39d481d2355f0451e725b41f0b7641816eccc9dc9e719a3c09765b383609d11a071c1994df821d3a718db10baaecf11a994ce2f32cac10a91ce69c7b5e71136c8a05eb07a4ab7eed93c614a9aca3c1afabbf06eb5dc9c8bc4bbdf5f786b647a3461a01fb3df47069f7eb2640f612343f642b44415094a7e9db11b6bbfc5d9dda4754da0dd76b20038570047e65010f35dc68177021c005d818220bfe1ed10694294d56ba06968f0ff479395925820fc71c7ba5cfeed7ca5f1bbf5566990a045c834c38e14a0b5660ae3d933376c9aa107c864ec2724ea8ecfc3e7a943c198b3486b5b9e358ea0585bb49b9e23c53ed785e27156e86adbb8c913780465700f56d431e0bf0beb04a0cb964717598a937e92f9d6ff514fa6acbf86e88de1333d7c9f1798251c21114880", "0xf90211a0022f94fbe307cbb1e956286f7486e96f668a040b618df1b35b3f1b89e79ae622a091de5983a08e4ce81ffd92f5f3c86973b26c475388fe5a6c3ff473ba37c82d66a01eb1892d9ed86ef3a2f2ce53a0735b1b34c367993e79717e20e9dcd05b235ed2a08f77ab9901240950440678e6d97685a648b3837a004c3914d6a9100465e95875a0685c883719c752347ad096c5df5106dafe3ea0bb71ca7bc123de4bd5fdd71200a0427e40d6e6955dc4e05f009365a06287cf64a87dc55d3105d3cde13ec8126f6ea033c430dc41783155398f4dafa602de2292be02071255f68d417ac84f3cf2392ea03cf7ea6ce6e7b60e886baab8a24aeb83ec56b1b8e5d1faac7dc11c3d8c7cab4fa0f5ebceebf282e4286290bff1d1335b9a0aaa5a5e856e64cb0579ef852b3980e7a06e8e92a67e695493f01f428932f224c0ff6268fcea2f70cdd7ea131d083df74ea0620fe1b7bf2396bd3adcdb6133343193b1319285199b00e88e5fe030b79d050ea08e7eca44e7a52bab6aea332dbbca859542b0f1f306dec9663c685e2b29ea1d76a09b86f98ca6c0baa062d9092c7863bcff0c9f2c6c1a801e24b0bba32e1df475fca0c658bcafc8219b1f779e4f287ad62eabf99cecf2e182d4e1a18ecf5bfaaab026a0e0769df0ef25c8bf2501e4a40369a552582a215357c15ee6b030901b1240e9aca0c52be2cf694dd32f923cd2d2e1270b21d8887e33f34bdf05c69a8f3446a3c00380", "0xf89180a053ddec1f33fa7d5372fc91725332a72af561989fd31982e4b8b2e9ce9809c406808080a07abdc05b292174e3e9de48d893ca5e7d721ba05884fe4e9b3cd59dca47359903a0ba4ba8e064b1fc2273267fd1942f211233edd61388c6c5644d561dd0f2a491518080808080808080a010d3c12c3e1efce1c97c3530a568ebfb38479624a66e6f8cab462b9a90b0b43480", "0xf8749f209f05d2dac481a991ac6466e42d76cfcbfe9d59681d3f255cc95c3b87cbe0b852f8508208058a08bf9c8d1322d1741740a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" ]' | sed "s/\"/'/g"`
proofResponseMatches "$PROOF" "$EXPECTEDPROOF" $ACCOUNT $BLOCK

echo 
echo State proof check for Block 150003, account 0x32Be343B94f860124dC4fEe278FDCBD38C102D88
echo
BLOCK="0x249F3"
PROOF=`curl -s -X POST --data '{"jsonrpc":"2.0","method": "eth_getProof","params": [ "'$ACCOUNT'", [], "'$BLOCK'"],"id": 1}' http://127.0.0.1:8545 | jq -r .result.accountProof`
PROOF=`echo $PROOF | sed "s/\"/'/g"`
NONCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method": "eth_getProof","params": [ "'$ACCOUNT'", [], "'$BLOCK'"],"id": 1}' http://127.0.0.1:8545 | jq -r .result.nonce`
nonceResponseMatches $NONCE "0x807" $ACCOUNT $BLOCK
BALANCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method": "eth_getProof","params": [ "'$ACCOUNT'", [], "'$BLOCK'"],"id": 1}' http://127.0.0.1:8545 | jq -r .result.balance`
balanceResponseMatches $BALANCE "0x87590bb2b291a6d1240" $ACCOUNT $BLOCK
EXPECTEDPROOF=`echo '[ "0xf90211a06d095eaaeacdfffd075684f0578cb572fc88606acd332f7c613de8152a6321a2a0979a68d3084102613fd289c4394e6474b692986bcd03705273f1cca529cb1571a0f54ba75f04abfa8c99886369a13562fb1ccfab979c1332404f1a1039f5603b12a03c117529a1c47692faae3f7d6cfa1d01b1a4345e943609aac9e3123721f1a18fa00c6adfb8616db3857cb955fab23c6108e1f7ecb578a7456c8f8d078f9df31864a02b0ef78ba5968e66764694429397e5b52a2e86d8dec695cf0c51cea0e776ebdea03a280eb9719f1b9cc58d59e42ee20f5ee4826922ed73451ea7b265dad0c00c21a02cf89409a859fc6f72aebf9f56d2d4ca8991bf07255442eee7a57e18e7712d35a04b3526fa5187227bda323ff89f6347cfa9e3906b26b101a910c0e74b49f44571a0281cbb03a1415785a4df047741da8276d2e61b94d69fcce846697243993da320a0a37eb851d5af0bedd13eb9262491f300f688bf4547b2e1bc9f5c522c3aa36673a0e21abca7946f0cd6750f41c4994ddbcda5e5702d29391f554dcf290e493f6335a027c1cbda50110c884482266ec4c449691e835b1761da3cfb772db6ee0664f3a4a04de04af90a2179450e00b67ad50d33057629fb9f5cc8db32bd871c0b7b1177eda0aef676628e901f0a8b09782ff39fc2721bb69d8f0a58228393f709dd2e2bfef4a0a8ce2e50d3902840eec1ccb52d2e0e139fd1b45731f7740244eb5ab192d9b24d80", "0xf90211a001883aa7e1f64a4ff0da04f7aa5f219d3ccc56e5fb351038df047d6984f0d956a0272952891fec9bb5663025ea019e3cb71b78362f08bc0d864ca196d17bd3a672a076c4c9fbb7d90cc93601f8c79c30afd037940a257062b3441168a91b6a00473ea08de19c85de6ff38585b5d411f37bba44fb90b5609d5e5ea9509e5ca1b5369b6aa0fb5f0a1a6ced0c97d162fda76900798cadeb14e24590b5e55c5ecb9cc5074f48a0c8a1a880604a20721048d5649030f05093b7b5b52b7fd73f5193b6984d385640a063138db8e2ddf2ac2d98743c9f82624a27b1b51b97a2a1069da4db9055192fbba0a39d481d2355f0451e725b41f0b7641816eccc9dc9e719a3c09765b383609d11a071c1994df821d3a718db10baaecf11a994ce2f32cac10a91ce69c7b5e71136c8a05eb07a4ab7eed93c614a9aca3c1afabbf06eb5dc9c8bc4bbdf5f786b647a3461a01fb3df47069f7eb2640f612343f642b44415094a7e9db11b6bbfc5d9dda4754da0dd76b20038570047e65010f35dc68177021c005d818220bfe1ed10694294d56ba06968f0ff479395925820fc71c7ba5cfeed7ca5f1bbf5566990a045c834c38e14a0b5660ae3d933376c9aa107c864ec2724ea8ecfc3e7a943c198b3486b5b9e358ea0585bb49b9e23c53ed785e27156e86adbb8c913780465700f56d431e0bf0beb04a07f9a4cfe156c4d3f7598b72007fc4255c11c1f299614215048843706f8e63a8c80", "0xf90211a0022f94fbe307cbb1e956286f7486e96f668a040b618df1b35b3f1b89e79ae622a0d94d9397680e42b81f3465da02e570cc92aaa5ca80f5ff7fc40c8ad762ffeb91a01eb1892d9ed86ef3a2f2ce53a0735b1b34c367993e79717e20e9dcd05b235ed2a08f77ab9901240950440678e6d97685a648b3837a004c3914d6a9100465e95875a0685c883719c752347ad096c5df5106dafe3ea0bb71ca7bc123de4bd5fdd71200a0427e40d6e6955dc4e05f009365a06287cf64a87dc55d3105d3cde13ec8126f6ea033c430dc41783155398f4dafa602de2292be02071255f68d417ac84f3cf2392ea03cf7ea6ce6e7b60e886baab8a24aeb83ec56b1b8e5d1faac7dc11c3d8c7cab4fa0f5ebceebf282e4286290bff1d1335b9a0aaa5a5e856e64cb0579ef852b3980e7a06e8e92a67e695493f01f428932f224c0ff6268fcea2f70cdd7ea131d083df74ea0620fe1b7bf2396bd3adcdb6133343193b1319285199b00e88e5fe030b79d050ea08e7eca44e7a52bab6aea332dbbca859542b0f1f306dec9663c685e2b29ea1d76a09b86f98ca6c0baa062d9092c7863bcff0c9f2c6c1a801e24b0bba32e1df475fca0c658bcafc8219b1f779e4f287ad62eabf99cecf2e182d4e1a18ecf5bfaaab026a0e0769df0ef25c8bf2501e4a40369a552582a215357c15ee6b030901b1240e9aca0c52be2cf694dd32f923cd2d2e1270b21d8887e33f34bdf05c69a8f3446a3c00380", "0xf89180a053ddec1f33fa7d5372fc91725332a72af561989fd31982e4b8b2e9ce9809c406808080a0f8f540a67329a72e2174bd24e84b8829d5a742c3cab29b491c2e536b3bbcbc9ca0ba4ba8e064b1fc2273267fd1942f211233edd61388c6c5644d561dd0f2a491518080808080808080a010d3c12c3e1efce1c97c3530a568ebfb38479624a66e6f8cab462b9a90b0b43480", "0xf8749f209f05d2dac481a991ac6466e42d76cfcbfe9d59681d3f255cc95c3b87cbe0b852f8508208078a087590bb2b291a6d1240a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" ]' | sed "s/\"/'/g"`
proofResponseMatches "$PROOF" "$EXPECTEDPROOF" $ACCOUNT $BLOCK

echo 
echo State proof check for Block 1000000, account 0x39fA8c5f2793459D6622857E7D9FbB4BD91766d3
echo
ACCOUNT="0x39fA8c5f2793459D6622857E7D9FbB4BD91766d3"
BLOCK="0xF4240"
PROOF=`curl -s -X POST --data '{"jsonrpc":"2.0","method": "eth_getProof","params": [ "'$ACCOUNT'", [], "'$BLOCK'"],"id": 1}' http://127.0.0.1:8545 | jq -r .result.accountProof`
PROOF=`echo $PROOF | sed "s/\"/'/g"`
NONCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method": "eth_getProof","params": [ "'$ACCOUNT'", [], "'$BLOCK'"],"id": 1}' http://127.0.0.1:8545 | jq -r .result.nonce`
nonceResponseMatches $NONCE "0x16" $ACCOUNT $BLOCK
BALANCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method": "eth_getProof","params": [ "'$ACCOUNT'", [], "'$BLOCK'"],"id": 1}' http://127.0.0.1:8545 | jq -r .result.balance`
balanceResponseMatches $BALANCE "0x15bac8c01b85fe4028" $ACCOUNT $BLOCK
EXPECTEDPROOF=`echo '[ "0xf90211a0c07e1b91223de332e1acdb7f51e34500bfc537de6a665034980459d1006605d7a07837218303eb727116ab34525441391c77fd65c8d6a3ec9d8b381822b83105a9a04f268ab2a53def380ada5b2912cb7217c50776ec90b773b53a794d9f423de013a02caeadaf17f2da451ba9bdf2ce9aca9ef14a990a0436156434c3f1f8d1f3c104a0c57482cbb52a96ee1fef320372d955a7a26bc63c8764cf8ccb174668669b441ba00baf463eb991fee008ab3f2e9191b8f18996d5ea3009f55a4cc8eaba9e0e3232a0310c4e4fa0a2fae15f82f855430662909cd36a105f4efbc82637a3c6e92f25b9a02121a1c9cb3ebd9130794ac5564122b3b1e6d29fec8e9d6e24398f084099cbdaa07b0918b6950b34282e6e0de398937ba77f5e4d1f2cc4fae5c840dcdb057df967a0e91917e14dab27f7793d1007aafb301f7c122f803f87c6e3ff382c17ad5f3b80a076c2dc94f6f18780bedabab5bea13e50e82ad48c6201de4e4a84e61cc4f10641a0182895c59c2705482eb700ad0b744a816da4c069091ebd3636cee60a53abb4f3a007afa3ee53d006bb9af6c8bc30e6abc02ab5a9d65edc2a62eb92fad2cb5cb19aa0ad87389f6f88f690811537e80f0f66b902109a2117693790957023ef76c55401a00ebef05bd0a10b5dedf2cbad85f50ce54fc9369b0afb5d1b7ecc3ee528da3f58a0931ea32e24b1747103be9909335432fe7315b23ecad64224ced0c5c948e6db8a80", "0xf90211a0c03e0c7cc5c0c6efc27834246c2ed8a59b057568ec21b4c7a4c94ce94d335436a0124683984ed87d8bad85bf0d5ba28509ee9d905eba60f978139515083b670f52a08ab95651f6405b5abd6756c306159425770b7163e0f10cbc1784b35b1a701930a04efc4743cad2db14e9c09e0bd205ba0a17de62b7f1a5843085529c2b4f166b75a05840981283f5e03d6f0077cfce4ea610e2e7c0eb8d3456c0648cf48b0c36fce0a072ae2d685e01d0ba25a7751e53c183c8a1e7d19264b14c81c77545aa483fc3a2a08fc6f340b05dc44fe58525604828031bcf227f61ba6a426d8e61b15876b7bddea0de3c91481c004312c774d43f009e1e2e747d52d07e30a5b4ebe6d4ae3a75f61ba0c703a7d2c3e12be1205a9b26b7db60837acbc63db7d26ba5e2c38f1a80d0677ea0bf3c789d8cccce4415c76bd72df374c0fa9b6fbbabffc58d2af99e12ef910257a06861b6a8a36817075dcc8f589966629287ce6f105e9ba92d0f65a9438250ae6ba012233fcf9b325f7d6411b8e997e61b766ada1ffdbbf88a1eae5ae16448ff6939a00d0d8800d6e13085494c0728cb576d49d2d70591257acdc10883cff1ae325bfca023ef7222872c1c6144b1841c3c2d8099774f1ba8d0e93eebd320805165a8d9c9a03a5bcdeb62e7b5a401d341438d7df8d478c0abada0357eff5e1acc475babecc9a047013c0d2e777a88fa3ef5ac5bb3d4dc1a6bc86272ccfb2387a5b5c3939a1f7880", "0xf90211a0a4d78502f7918e97c8c28fe271014726c3c2e191aea5a0a7bfe3c0b4ba496d33a0ebb3c34d26940a7f2137522ee2dc07b80596b4dffb0dec8a091d38f612f26a41a0e00a4a51c376943087b19ddad567da4e630c11f690b4d02d626ff109873ee419a0f2af0d9202ed43387023b43e8d1637e341e47b260afdef715e6fb28a8deef98ea0ba0e400f0c333955f65b91a4bc5554b2e20d7172bc8f70e5fb19e738ae022d20a0c6783bba73ff902e70ba7d0f26c29c49e9b1c6f94bdab9dcdd1eafb50bab5983a0f8ae64a9ca0fd6afebe0f2aea32726a3e13ea1501589ed7be73afb1a8f33b7dca0eb00787f4dac6a6473fa970a474197fb03505d0c9c2dbd8c21f19d69362a7fd7a08704d663a39ad9952da1ca0a642e10a52aeaa519d1d849a747b69d03567a0607a05678f5fd1caeada275bd3dd9c0ef3fb5eab96f2c8f81ac026b21e96e0e87305ca0815bfc20e92c88b409517320b2563fdb762e56d39aa8350bd99ae1b8289ce0cca08ecc7b9e54c44d99f5187cb8f0bdebafef7aaa49e3c09b16f612b557286a2617a03399e1628f1cb073038e877ae8ee14eea91cacddb946a4e124ddb602c5e277e5a02417a18fdfb3a43552ec260bc21e45f6ee03dbb3a52063593b03bbf8fa441263a0a38d0adf4e6cecbc0b0e09b921e90678cd365f068087a305a82f9c33c239ddc0a089feafebdd194fd78d1638e57d17a120c61a35710add3b2488dce66ed238e4ef80", "0xf9011180808080a06bd7f1dd56aac09dbe6b64752e0f3d686be5483f182fe8f51874b4b0fe86fb29a00aa8c9bfc05183cdee2bf7832bd6557d2dd040dafc056bf7288ea510f04afaf980a076d2a6dac166edd8418d90a2b9f1d05e290482d9683423ef0870b18abdfcceec80a04ab7910a13f05a5d9ff1b4946e402b91645246b123ea7e75b26085196d4c70c3a0d0406467e0536d2da0a773c1c4b21e57e1371510b3bc31616ca329cbe24843c18080a01ee655920fa772f97a7e8efa32e31de54f46168a03810ee7b3156465820d7feba044144bda0a998a5d36505d9ac5c4804f21522d9a288c3e4759101b4123992108a02f41006f70f4269034754873b225a7061a7896168e2b6a4f95d8c7e566dbec2780", "0xf8719f20ee3524a882a21ddf3073af6c0d567c6150d4a0ad787103a5282ade764735b84ff84d168915bac8c01b85fe4028a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" ]' | sed "s/\"/'/g"`
proofResponseMatches "$PROOF" "$EXPECTEDPROOF" $ACCOUNT $BLOCK

exit 0
Script for testing historic account states on mainnet#!/bin/bash nonceResponseMatches () { echo -n "Checking nonce == $2 for account $3, block $4 - " if [[ "${1^^}" == "${2^^}" ]]; then echo OK else echo "Unexpected JSON/RPC response. $1 != $2" exit 1 fi } storageResponseMatches () { echo -n "Checking storage slot $1 == $3 for account $4, block $5 - " if [[ "${2^^}" == "${3^^}" ]]; then echo OK else echo "Unexpected JSON/RPC response. $2 != $3" exit 1 fi } # Retrive the transaction count for a number of different accounts in the first 1,000,000 blocks of Ethereum L1 # Some of these have been specfically selected for accounts that change several times in a block, or that change in several contiguous blocks. # Block 150003 - 2 transactions from the same sender. Check nonce for blocks 150002, 150003, and 150004 ACCOUNT="0x32Be343B94f860124dC4fEe278FDCBD38C102D88" BLOCK="0x249F2" NONCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["'$ACCOUNT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` nonceResponseMatches $NONCE "0x805" $ACCOUNT $BLOCK BLOCK="0x249F3" NONCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["'$ACCOUNT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` nonceResponseMatches $NONCE "0x807" $ACCOUNT $BLOCK BLOCK="0x249F4" NONCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["'$ACCOUNT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` nonceResponseMatches $NONCE "0x807" $ACCOUNT $BLOCK # Blocks 138719 and 138720 - transactions from the same sender in 2 contiguous blocks. Check blocks 138718, 138719, 138720 and 138721 ACCOUNT="0x1DCb8d1F0FCc8CbC8C2d76528E877F915e299fbE" BLOCK="0x21DDE" NONCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["'$ACCOUNT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` nonceResponseMatches $NONCE "0x59" $ACCOUNT $BLOCK BLOCK="0x21DDF" NONCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["'$ACCOUNT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` nonceResponseMatches $NONCE "0x5a" $ACCOUNT $BLOCK BLOCK="0x21DE0" NONCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["'$ACCOUNT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` nonceResponseMatches $NONCE "0x5b" $ACCOUNT $BLOCK BLOCK="0x21DE1" NONCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["'$ACCOUNT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` nonceResponseMatches $NONCE "0x5b" $ACCOUNT $BLOCK # Some storage lookups for slots that are known to change # Blocks 2018260, 2020000, 2300000 for a specific smart contract ACCOUNT="0x684282178b1d61164FEbCf9609cA195BeF9A33B5" BLOCK="0x1ECBD4" SLOT="0x5" STORAGE_VAL=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["'$ACCOUNT'","'$SLOT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` storageResponseMatches "$SLOT" $STORAGE_VAL "0x0000000000000000000000000000000000000000000000000000000000000003" $ACCOUNT $BLOCK SLOT="0x7" STORAGE_VAL=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["'$ACCOUNT'","'$SLOT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` storageResponseMatches "$SLOT" $STORAGE_VAL "0x0000000000000000000000000000000000000000000000000000000000000001" $ACCOUNT $BLOCK SLOT="0xa" STORAGE_VAL=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["'$ACCOUNT'","'$SLOT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` storageResponseMatches "$SLOT" $STORAGE_VAL "0x0000000000000000000000000000000000000000000000000000000000000000" $ACCOUNT $BLOCK BLOCK="0x1ED2A0" SLOT="0x5" STORAGE_VAL=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["'$ACCOUNT'","'$SLOT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` storageResponseMatches "$SLOT" $STORAGE_VAL "0x0000000000000000000000000000000000000000000000000000000000000004" $ACCOUNT $BLOCK SLOT="0x7" STORAGE_VAL=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["'$ACCOUNT'","'$SLOT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` storageResponseMatches "$SLOT" $STORAGE_VAL "0x0000000000000000000000000000000000000000000000000000000000000001" $ACCOUNT $BLOCK SLOT="0xa" STORAGE_VAL=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["'$ACCOUNT'","'$SLOT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` storageResponseMatches "$SLOT" $STORAGE_VAL "0x0000000000000000000000000000000000000000000000000000000000000000" $ACCOUNT $BLOCK BLOCK="0x231860" SLOT="0x5" STORAGE_VAL=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["'$ACCOUNT'","'$SLOT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` storageResponseMatches "$SLOT" $STORAGE_VAL "0x0000000000000000000000000000000000000000000000000000000000000005" $ACCOUNT $BLOCK SLOT="0x7" STORAGE_VAL=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["'$ACCOUNT'","'$SLOT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` storageResponseMatches "$SLOT" $STORAGE_VAL "0x0000000000000000000000000000000000000000000000000000000000000001" $ACCOUNT $BLOCK SLOT="0xa" STORAGE_VAL=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["'$ACCOUNT'","'$SLOT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` storageResponseMatches "$SLOT" $STORAGE_VAL "0x0000000000000000000000000000000000000000000000000000000000000001" $ACCOUNT $BLOCK # Some other random checks for nonce on accounts at later blocks # Block 2000000 ACCOUNT="0x32Be343B94f860124dC4fEe278FDCBD38C102D88" BLOCK="0x1E8480" NONCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["'$ACCOUNT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` nonceResponseMatches $NONCE "0x1EFC6" $ACCOUNT $BLOCK # Block 3000000 ACCOUNT="0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8" BLOCK="0x2DC6C0" NONCE=`curl -s -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["'$ACCOUNT'","'$BLOCK'"],"id":1}' http://127.0.0.1:8545 | jq .result -r` nonceResponseMatches $NONCE "0x10AA05" $ACCOUNT $BLOCK exit 0

jframe and others added 30 commits October 8, 2024 08:38
Signed-off-by: Jason Frame <jason.frame@consensys.net>
…se constructor that reuses worldStateStorage so that we don't lose values in the EvmToolSpecTests

Signed-off-by: Jason Frame <jason.frame@consensys.net>
Signed-off-by: Jason Frame <jason.frame@consensys.net>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
…d state, and freeze it

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
…ten for blocks and move account state to new DB segment

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
…t block state has been frozen for

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
…age from the freezer segment

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
…or DB mode

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
… time. Add more tests

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
… a time

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
…Use the term archive, not freezer

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
…to fail the block

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
@matthew1001 matthew1001 changed the title Archive proofs Bonsai Archive (with state proofs) May 22, 2025
Copy link
Contributor

@mirgee mirgee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few early questions and observations - still getting up to speed on the storage subsystem, so some of these may miss the mark. Also I understand it’s still a WIP, so feel free to disregard anything off-base @matthew1001 . I’ll probably have more feedback as I go through the rest in more detail in effort to fully understand the implementation.

I can also add that, speaking for Absa, we wouldn't oppose merging BONSAI_ARCHIVE and BONSAI_ARCHIVE_PROOFS into a single storage format functionally equivalent to the latter. For our use-cases, full auditability and data integrity are non-negotiable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe as part of the data storage config validation we should also assert that the checkpoint interval is positive?


if (DataStorageFormat.X_BONSAI_ARCHIVE_PROOFS != dataStorageFormat) {
if (unstableOptions.archiveTrieNodeCheckpointInterval
!= DEFAULT_ARCHIVE_CHECKPOINT_INTERVAL) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obviously this is not ideal as the exception will not be triggered if the checkpoint interval is set to the default value. There are other issues with the CLI arg parsing in general but it can and should be addressed separately.

DataStorageFormat.BONSAI.equals(config.getDataStorageFormat()),
"Subcommand only works with data-storage-format=BONSAI");
config.getDataStorageFormat().isBonsaiFormat(),
"Subcommand only works with data-storage-format=BONSAI or X_BONSAI_ARCHIVE");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Subcommand only works with data-storage-format=BONSAI or X_BONSAI_ARCHIVE");
"Subcommand only works with data-storage-format=BONSAI or X_BONSAI_ARCHIVE or X_BONSAI_ARCHIVE_PROOFS");

Comment on lines +81 to +83
: (dataStorageFormat == X_BONSAI_ARCHIVE
? BaseVersionedStorageFormat.BONSAI_ARCHIVE_WITH_RECEIPT_COMPACTION
: BaseVersionedStorageFormat.FOREST_WITH_RECEIPT_COMPACTION);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
: (dataStorageFormat == X_BONSAI_ARCHIVE
? BaseVersionedStorageFormat.BONSAI_ARCHIVE_WITH_RECEIPT_COMPACTION
: BaseVersionedStorageFormat.FOREST_WITH_RECEIPT_COMPACTION);
: dataStorageFormat == X_BONSAI_ARCHIVE
? BaseVersionedStorageFormat.BONSAI_ARCHIVE_WITH_RECEIPT_COMPACTION
: dataStorageFormat == X_BONSAI_ARCHIVE_PROOFS
? BaseVersionedStorageFormat.BONSAI_ARCHIVE_PROOFS_WITH_RECEIPT_COMPACTION
: BaseVersionedStorageFormat.FOREST_WITH_RECEIPT_COMPACTION;

Comment on lines +106 to +108
: (dataStorageFormat == X_BONSAI_ARCHIVE
? BaseVersionedStorageFormat.BONSAI_ARCHIVE_WITH_RECEIPT_COMPACTION
: BaseVersionedStorageFormat.FOREST_WITH_RECEIPT_COMPACTION);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
: (dataStorageFormat == X_BONSAI_ARCHIVE
? BaseVersionedStorageFormat.BONSAI_ARCHIVE_WITH_RECEIPT_COMPACTION
: BaseVersionedStorageFormat.FOREST_WITH_RECEIPT_COMPACTION);
: dataStorageFormat == X_BONSAI_ARCHIVE
? BaseVersionedStorageFormat.BONSAI_ARCHIVE_WITH_RECEIPT_COMPACTION
: dataStorageFormat == X_BONSAI_ARCHIVE_PROOFS
? BaseVersionedStorageFormat.BONSAI_ARCHIVE_PROOFS_WITH_RECEIPT_COMPACTION
: BaseVersionedStorageFormat.FOREST_WITH_RECEIPT_COMPACTION;

* The FlatDbMode enum represents the different modes of the flat database. It has two modes:
* PARTIAL and FULL.
* The FlatDbMode enum represents the different modes of the flat database. It has three modes:
* PARTIAL, FULL, and ARCHIVE.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to be the case, as both archive storage mode variants have their own partial and full flat db strategy?

Comment on lines +231 to +240
executeAsync.accept(
() -> {
if (archiveMutex.tryLock()) {
try {
moveBlockStateToArchive();
} finally {
archiveMutex.unlock();
}
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might it be more efficient to run a batch job every couple of blocks to avoid the overhead of starting tasks just to find that the lock is taken?

Comment on lines +234 to +238
try {
moveBlockStateToArchive();
} finally {
archiveMutex.unlock();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be useful to log any exceptions thrown from moveBlockStateToArchive.

.forEach(
(address, storageSlotKey) -> {
storageSlotKey.forEach(
(slotKey, slotValue) -> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it guaranteed at this point that for the returned LogTuples it is prior != updated, or must the unchanged values be filtered explicitly?

@@ -134,6 +134,10 @@ public Optional<Bytes> getAccountStateTrieNode(
if (nodeHash.equals(MerkleTrie.EMPTY_TRIE_NODE_HASH)) {
return Optional.of(MerkleTrie.EMPTY_TRIE_NODE);
} else {
/*if (Optional.ofNullable(accountNodes.getIfPresent(nodeHash)).isPresent()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would assume returning preloaded storage slots might be problematic also, isn't it?

// then start from 200 and replay trie logs backwards to 120. We will then PUT new trie nodes
// as if we were at block 120, and after those puts (during mutablestate.persist()) the
// WORLD_BLOCK_NUMBER_KEY, WORLD_BLOCK_HASH_KEY, and WORLD_ROOT_HASH_KEY will be set to 120.
((BonsaiArchiveWorldState) mutableState).createCheckpointState(checkpointBlock);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this process safe in the face of checkpoint interval changing over time?

Changing the checkpoint interval can lead to "stale" flatdb entries or rolling back from a block number for which a checkpoint was never "built" - which at least superficially seems problematic...

Copy link

github-actions bot commented Jul 3, 2025

This pr is stale because it has been open for 30 days with no activity.

@matthew1001
Copy link
Contributor Author

matthew1001 commented Jul 4, 2025

Superseded by #8918

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants