diff --git a/zktrie/LICENSE b/zktrie/LICENSE new file mode 100644 index 0000000..9903b1a --- /dev/null +++ b/zktrie/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Hemi Labs, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/zktrie/go.mod b/zktrie/go.mod new file mode 100644 index 0000000..6640472 --- /dev/null +++ b/zktrie/go.mod @@ -0,0 +1,46 @@ +module github.com/hemilabs/x/zktrie + +go 1.25.1 + +require ( + github.com/btcsuite/btcd v0.25.0 + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 + github.com/davecgh/go-spew v1.1.1 + github.com/ethereum/go-ethereum v1.16.7 + github.com/hemilabs/x/eth-trie v0.0.0-20251105164544-95cd47e9e738 + github.com/holiman/uint256 v1.3.2 +) + +require ( + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/VictoriaMetrics/fastcache v1.13.0 // indirect + github.com/bits-and-blooms/bitset v1.20.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/consensys/gnark-crypto v0.18.0 // indirect + github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/emicklei/dot v1.6.2 // indirect + github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect + github.com/ethereum/go-verkle v0.2.2 // indirect + github.com/ferranbt/fastssz v0.1.4 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/gofrs/flock v0.12.1 // indirect + github.com/golang/snappy v1.0.0 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/minio/sha256-simd v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.37.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/zktrie/go.sum b/zktrie/go.sum new file mode 100644 index 0000000..0d9e5ed --- /dev/null +++ b/zktrie/go.sum @@ -0,0 +1,171 @@ +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= +github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= +github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd v0.25.0 h1:JPbjwvHGpSywBRuorFFqTjaVP4y6Qw69XJ1nQ6MyWJM= +github.com/btcsuite/btcd v0.25.0/go.mod h1:qbPE+pEiR9643E1s1xu57awsRhlCIm1ZIi6FfeRA4KE= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0= +github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c= +github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg= +github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg= +github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= +github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= +github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= +github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ= +github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk= +github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= +github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= +github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= +github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/hemilabs/x/eth-trie v0.0.0-20251105164544-95cd47e9e738 h1:vfq6tWheHCJnVwpGbdQInMJwLrYUpAWaDe+eSxwAsJg= +github.com/hemilabs/x/eth-trie v0.0.0-20251105164544-95cd47e9e738/go.mod h1:Ww/u6/pC+fwKpLDw2HNxLw0PxsLDukGCMXJRe1VBym4= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= +github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= +github.com/prysmaticlabs/gohashtree v0.0.4-beta/go.mod h1:BFdtALS+Ffhg3lGQIHv9HDWuHS8cTvHZzrHWxwOtGOs= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= +github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= +golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/zktrie/zktrie.go b/zktrie/zktrie.go new file mode 100644 index 0000000..5479cf6 --- /dev/null +++ b/zktrie/zktrie.go @@ -0,0 +1,486 @@ +// Copyright (c) 2025 Hemi Labs, Inc. +// Use of this source code is governed by the MIT License, +// which can be found in the LICENSE file. + +package zktrie + +import ( + "encoding/binary" + "errors" + "fmt" + "path/filepath" + "sync" + + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethdb/leveldb" + "github.com/ethereum/go-ethereum/rlp" + "github.com/hemilabs/x/eth-trie/trie" + "github.com/hemilabs/x/eth-trie/trie/trienode" + "github.com/hemilabs/x/eth-trie/triedb" + "github.com/hemilabs/x/eth-trie/triedb/pathdb" + "github.com/holiman/uint256" +) + +var ( + MetadataAddress common.Address + + ErrAddressNotFound = errors.New("address not found") + ErrBlockNotFound = errors.New("block information not found") + ErrOutpointNotFound = errors.New("key not found") +) + +func init() { + const reserved string = "0xffffffffffffffffffffffffffffffffffffffff" + MetadataAddress = common.BytesToAddress([]byte(reserved)) +} + +// txId + index +type Outpoint [32 + 4]byte + +func NewOutpoint(txid [32]byte, index uint32) (op Outpoint) { + copy(op[:32], txid[:]) + binary.BigEndian.PutUint32(op[32:], index) + return op +} + +// TODO: encode with value and then crop to prevent get for balance update? +// blockhash + txId + txInIdx +type SpentOutput [32 + 32 + 4]byte + +func NewSpentOutput(blockHash, txId [32]byte, index uint32) (so SpentOutput) { + copy(so[:32], blockHash[:]) + copy(so[32:64], txId[:]) + binary.BigEndian.PutUint32(so[64:], index) + return so +} + +// blockhash + txId + txOutIdx + value +type SpendableOutput [32 + 32 + 4 + 8]byte + +func (so SpendableOutput) Value() uint64 { + return binary.BigEndian.Uint64(so[68:]) +} + +func NewSpendableOutput(blockHash, txId [32]byte, index uint32, value uint64) (so SpendableOutput) { + copy(so[:32], blockHash[:]) + copy(so[32:64], txId[:]) + binary.BigEndian.PutUint32(so[64:68], index) + binary.BigEndian.PutUint64(so[68:], value) + return so +} + +// prev_stateroot + prev_blockhash + height +type BlockInfo [32 + 32 + 8]byte + +func (bi BlockInfo) PrevStateRoot() common.Hash { + return common.BytesToHash(bi[:32]) +} + +func (bi BlockInfo) PrevBlockHash() chainhash.Hash { + ch, err := chainhash.NewHash(bi[32:64]) + if err != nil { + panic("stored value is not blockhash") + } + return *ch +} + +func (bi BlockInfo) Height() uint64 { + return binary.BigEndian.Uint64(bi[64:]) +} + +func NewBlockInfo(prevStateRoot common.Hash, prevBlockHash chainhash.Hash, height uint64) (bi BlockInfo) { + copy(bi[:32], prevStateRoot[:]) + copy(bi[32:64], prevBlockHash[:]) + binary.BigEndian.PutUint64(bi[64:], height) + return bi +} + +// ZKBlock holds information on a block used for a ZK Trie state transition. +type ZKBlock struct { + blockHash common.Hash + + // Storage information. Automatically managed using the utility methods. + storage map[common.Address]map[common.Hash][]byte +} + +func NewZKBlock(blockHash, prevBlockHash chainhash.Hash, prevStateRoot common.Hash, height uint64) *ZKBlock { + bi := NewBlockInfo(prevStateRoot, prevBlockHash, height) + bh := common.Hash(blockHash) + return &ZKBlock{ + blockHash: bh, + storage: map[common.Address]map[common.Hash][]byte{ + MetadataAddress: { + bh: bi[:], + }, + }, + } +} + +func (b *ZKBlock) NewOut(pkScript []byte, out Outpoint, so SpendableOutput) { + if b.storage == nil { + b.storage = make(map[common.Address]map[common.Hash][]byte) + } + + addr := common.BytesToAddress(pkScript) + if _, ok := b.storage[addr]; !ok { + b.storage[addr] = make(map[common.Hash][]byte) + } + + key := crypto.Keccak256Hash(out[:]) + b.storage[addr][key] = so[:] +} + +func (b *ZKBlock) NewIn(pkScript []byte, out Outpoint, so SpentOutput) { + if b.storage == nil { + b.storage = make(map[common.Address]map[common.Hash][]byte) + } + + addr := common.BytesToAddress(pkScript) + if _, ok := b.storage[addr]; !ok { + b.storage[addr] = make(map[common.Hash][]byte) + } + + key := crypto.Keccak256Hash(out[:]) + b.storage[addr][key] = so[:] +} + +func (b *ZKBlock) GetOutpoint(pkScript []byte, out Outpoint) []byte { + addr := common.BytesToAddress(pkScript) + if b.storage == nil || b.storage[addr] == nil { + return nil + } + key := crypto.Keccak256Hash(out[:]) + return b.storage[addr][key] +} + +func (b *ZKBlock) GetMetadata() BlockInfo { + return BlockInfo(b.storage[MetadataAddress][b.blockHash]) +} + +// ZKTrie is used to perform operation on a ZK trie and its database. +type ZKTrie struct { + mtx sync.RWMutex + stateRoot common.Hash + uncommitedRoots map[common.Hash]struct{} + tdb *triedb.Database +} + +// TODO: set database cache and handles +func NewZKTrie(home string) (*ZKTrie, error) { + db, err := leveldb.New(home, 0, 0, "", false) + if err != nil { + return nil, fmt.Errorf("open database: %w", err) + } + + // You can now use db as a KeyValueStore + var kv ethdb.KeyValueStore = db + + // high-level database wrapper for the given key-value store + disk, err := rawdb.Open(kv, rawdb.OpenOptions{ + Ancient: filepath.Join(home, "ancients"), + }) + if err != nil { + return nil, fmt.Errorf("open rawdb: %w", err) + } + tdb := triedb.NewDatabase(disk, &triedb.Config{ + PathDB: &pathdb.Config{ + // NoAsyncFlush: true, + StateHistory: 1000, // store last 1000 blocks + EnableStateIndexing: true, + }, + }) + + t := &ZKTrie{ + tdb: tdb, + stateRoot: types.EmptyRootHash, + uncommitedRoots: make(map[common.Hash]struct{}), + } + return t, nil +} + +// Close closes the underlying database for ZKTrie. +func (t *ZKTrie) Close() error { + t.mtx.Lock() + defer t.mtx.Unlock() + return t.tdb.Close() +} + +// Recover rollbacks the ZKTrie database to a specified historical point. +func (t *ZKTrie) Recover(stateRoot common.Hash) error { + t.mtx.Lock() + defer t.mtx.Unlock() + if err := t.tdb.Recover(stateRoot); err != nil { + return err + } + t.stateRoot = stateRoot + t.uncommitedRoots = make(map[common.Hash]struct{}) + return nil +} + +// Put performs an insert into the underlying ZKTrie database. +func (t *ZKTrie) Put(key, value []byte) error { + return t.tdb.Disk().Put(key, value) +} + +// Get performs a lookup on the underlying ZKTrie database. +func (t *ZKTrie) Get(key []byte) ([]byte, error) { + return t.tdb.Disk().Get(key) +} + +// Del performs a delete on the underlying ZKTrie database. +func (t *ZKTrie) Del(key []byte) error { + return t.tdb.Disk().Delete(key) +} + +func (t *ZKTrie) inMemory(state common.Hash) bool { + t.mtx.RLock() + defer t.mtx.RUnlock() + _, ok := t.uncommitedRoots[state] + return ok || state.Cmp(t.stateRoot) == 0 +} + +// GetBlockInfo retrieves Block information from the reserved +// metadata state account. +func (t *ZKTrie) GetBlockInfo(blockHash chainhash.Hash, state *common.Hash) (BlockInfo, error) { + var ( + addrHash = crypto.Keccak256Hash(MetadataAddress[:]) + stateRoot = t.currentState() + ) + if state != nil { + stateRoot = *state + } + var b []byte + if t.inMemory(stateRoot) { + r, err := t.tdb.StateReader(stateRoot) + if err != nil { + return BlockInfo{}, err + } + b, err = r.Storage(addrHash, common.BytesToHash(blockHash[:])) + if err != nil { + return BlockInfo{}, err + } + } else { + r, err := t.tdb.HistoricReader(stateRoot) + if err != nil { + return BlockInfo{}, err + } + b, err = r.Storage(MetadataAddress, common.BytesToHash(blockHash[:])) + if err != nil { + return BlockInfo{}, err + } + } + if b == nil { + return BlockInfo{}, ErrBlockNotFound + } + if len(b) != len(BlockInfo{}) { + return BlockInfo{}, fmt.Errorf("unexpected stored value size: %d", len(b)) + } + return BlockInfo(b), nil +} + +func (t *ZKTrie) Sync() error { + return t.tdb.Disk().SyncAncient() +} + +func (t *ZKTrie) SyncProgress() (uint64, error) { + return t.tdb.IndexProgress() +} + +func (t *ZKTrie) GetOutpoint(pkScript []byte, out Outpoint, state *common.Hash) ([]byte, error) { + var ( + addr = common.BytesToAddress(pkScript) + stateRoot = t.currentState() + keyHash = crypto.Keccak256Hash(out[:]) + ) + if state != nil { + stateRoot = *state + } + if t.inMemory(stateRoot) { + r, err := t.tdb.StateReader(stateRoot) + if err != nil { + return nil, err + } + return r.Storage(crypto.Keccak256Hash(addr[:]), keyHash) + } + r, err := t.tdb.HistoricReader(stateRoot) + if err != nil { + return nil, err + } + return r.Storage(addr, keyHash) +} + +func (t *ZKTrie) GetAccount(addr common.Address, state *common.Hash) (*types.SlimAccount, error) { + var stateRoot = t.currentState() + if state != nil { + stateRoot = *state + } + if t.inMemory(stateRoot) { + r, err := t.tdb.StateReader(stateRoot) + if err != nil { + return nil, err + } + return r.Account(crypto.Keccak256Hash(addr[:])) + } + r, err := t.tdb.HistoricReader(stateRoot) + if err != nil { + return nil, err + } + return r.Account(addr) +} + +// currentState gets the most recent State Root. +func (t *ZKTrie) currentState() common.Hash { + t.mtx.RLock() + defer t.mtx.RUnlock() + return t.stateRoot +} + +// InsertBlock performs a state transition for a given block. +func (t *ZKTrie) InsertBlock(block *ZKBlock) (common.Hash, error) { + var ( + stateRoot = block.GetMetadata().PrevStateRoot() + mergeSet = trienode.NewMergedNodeSet() + stateID = trie.StateTrieID(stateRoot) + mutatedAcc = make(map[common.Hash][]byte, len(block.storage)) + originAcc = make(map[common.Address][]byte, len(block.storage)) + mutatedStore = make(map[common.Hash]map[common.Hash][]byte, len(block.storage)) + originStore = make(map[common.Address]map[common.Hash][]byte, len(block.storage)) + ) + stateTrie, err := trie.New(stateID, t.tdb) + if err != nil { + return types.EmptyRootHash, fmt.Errorf("failed to load state trie, err: %w", err) + } + + for addr, storage := range block.storage { + addrHash := crypto.Keccak256Hash(addr.Bytes()) + stateVal, err := stateTrie.Get(addrHash[:]) + if err != nil { + return types.EmptyRootHash, fmt.Errorf("get account state: %w", err) + } + + var ( + sa *types.StateAccount + skipGet bool + ) + if stateVal != nil { + sa, err = types.FullAccount(stateVal) + if err != nil { + panic(fmt.Errorf("decode stored state value: %w", err)) + } + } else { + sa = types.NewEmptyStateAccount() + skipGet = true + } + na := types.StateAccount{ + Balance: sa.Balance, + CodeHash: sa.CodeHash, + Nonce: sa.Nonce, + } + + storeID := trie.StorageTrieID(stateRoot, addrHash, sa.Root) + storeTrie, err := trie.New(storeID, t.tdb) + if err != nil { + return types.EmptyRootHash, fmt.Errorf("failed to load trie, err: %w", err) + } + + mutatedStore[addrHash] = make(map[common.Hash][]byte, len(block.storage[addr])) + originStore[addr] = make(map[common.Hash][]byte, len(block.storage[addr])) + for key, v := range storage { + if len(v) == len(SpendableOutput{}) { + skipGet = true + } + var prev []byte + if !skipGet { + prev, err = storeTrie.Get(key[:]) + if err != nil { + return types.EmptyRootHash, fmt.Errorf("get storage trie value: %w", err) + } + } + if err := storeTrie.Update(key[:], v); err != nil { + return types.EmptyRootHash, fmt.Errorf("update storage trie: %w", err) + } + mutatedStore[addrHash][key] = v + originStore[addr][key] = prev + + switch len(v) { + case len(SpendableOutput{}): + p := uint256.NewInt(binary.BigEndian.Uint64(v[68:76])) + na.Balance.Add(na.Balance, p) + case len(SpentOutput{}): + if prev == nil { + // If out was created and spent in the same block, + // then don't update the balance. + continue + } + pr := prev[68:76] + p := uint256.NewInt(binary.BigEndian.Uint64(pr)) + na.Balance.Sub(na.Balance, p) + case len(BlockInfo{}): + default: + panic("unknown storage value passed") + } + } + + // commit the trie, get storage trie root and node set + newStorageRoot, set := storeTrie.Commit(false) + if err := mergeSet.Merge(set); err != nil { + return types.EmptyRootHash, fmt.Errorf("merge storage nodes: %w", err) + } + na.Root = newStorageRoot + + full, err := rlp.EncodeToBytes(&na) + if err != nil { + return types.EmptyRootHash, err + } + mutatedAcc[addrHash] = full + originAcc[addr] = stateVal + + if err := stateTrie.Update(addrHash[:], full); err != nil { + return types.EmptyRootHash, fmt.Errorf("update accounts trie: %w", err) + } + } + + // commit the trie, get state trie root and node set + newStateRoot, set := stateTrie.Commit(false) + if err := mergeSet.Merge(set); err != nil { + return types.EmptyRootHash, fmt.Errorf("merge account nodes: %w", err) + } + + // StateSet represents a collection of mutated states during a state transition. + s := triedb.StateSet{ + Accounts: mutatedAcc, + AccountsOrigin: originAcc, + Storages: mutatedStore, + StoragesOrigin: originStore, + RawStorageKey: false, + } + + t.mtx.Lock() + defer t.mtx.Unlock() + + // performs a state transition + if err := t.tdb.Update(newStateRoot, stateRoot, block.GetMetadata().Height(), mergeSet, &s); err != nil { + return types.EmptyRootHash, fmt.Errorf("update db: %w", err) + } + + t.stateRoot = newStateRoot + t.uncommitedRoots[newStateRoot] = struct{}{} + return newStateRoot, nil +} + +// Commit commits performed state trasitions from memory to disk. +func (t *ZKTrie) Commit() error { + currState := t.currentState() + t.mtx.Lock() + defer t.mtx.Unlock() + if err := t.tdb.Commit(currState, true); err != nil { + return fmt.Errorf("commit db: %w", err) + } + t.uncommitedRoots = make(map[common.Hash]struct{}) + return nil +} diff --git a/zktrie/zktrie_test.go b/zktrie/zktrie_test.go new file mode 100644 index 0000000..b238c29 --- /dev/null +++ b/zktrie/zktrie_test.go @@ -0,0 +1,229 @@ +// Copyright (c) 2025 Hemi Labs, Inc. +// Use of this source code is governed by the MIT License, +// which can be found in the LICENSE file. + +package zktrie + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "io" + "testing" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +func TestZKTrie(t *testing.T) { + const ( + blockCount uint64 = 11 + storageKeys uint64 = 5 + ) + home := t.TempDir() + + zkt, err := NewZKTrie(home) + if err != nil { + t.Fatal(err) + } + if err := zkt.Put([]byte("hello"), []byte("world")); err != nil { + t.Fatal(err) + } + v, err := zkt.Get([]byte("hello")) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(v, []byte("world")) { + t.Fatalf("got %s, wanted %s", v, []byte("world")) + } + + prevBlock := *chaincfg.TestNet3Params.GenesisHash + prevStateRoot := types.EmptyRootHash + outpoints := make(map[uint64][]Outpoint) + states := make([]common.Hash, blockCount) + for i := range blockCount { + bh := chainhash.Hash(random(32)) + blk := NewZKBlock(bh, prevBlock, prevStateRoot, i) + + // simulate outs + var pkScript [8]byte + binary.BigEndian.PutUint64(pkScript[:], i) + outpoints[i] = make([]Outpoint, 0) + for range storageKeys { + o := NewOutpoint([32]byte(random(32)), 1) + so := NewSpendableOutput(blk.blockHash, [32]byte(random(32)), 1, 100) + blk.NewOut(pkScript[:], o, so) + outpoints[i] = append(outpoints[i], o) + } + + // simulate an in + for ac, keys := range outpoints { + if len(keys) <= 1 { + continue + } + var pkScript [8]byte + binary.BigEndian.PutUint64(pkScript[:], ac) + o := keys[0] + outpoints[ac] = keys[1:] + so := NewSpentOutput(blk.blockHash, [32]byte(random(32)), 1) + blk.NewIn(pkScript[:], o, so) + break + } + + sr, err := zkt.InsertBlock(blk) + if err != nil { + t.Fatal(err) + } + t.Logf("inserted block %d, new state root: %v", blk.GetMetadata().Height(), sr) + prevBlock = bh + prevStateRoot = sr + states[i] = sr + } + + if err := zkt.Commit(); err != nil { + t.Fatal(err) + } + + for _, state := range states { + for ac, keys := range outpoints { + var pkScript [8]byte + binary.BigEndian.PutUint64(pkScript[:], ac) + addr := common.BytesToAddress(pkScript[:]) + sa, err := zkt.GetAccount(addr, &state) + if err != nil { + t.Fatal(err) + } + spew.Dump(sa) + + for _, k := range keys { + v, err := zkt.GetOutpoint(pkScript[:], k, &state) + if err != nil { + t.Fatal(err) + } + t.Logf("address %x, key %x, value %x", ac, k, v) + } + } + } + + md, err := zkt.GetBlockInfo(prevBlock, nil) + if err != nil { + t.Fatal(err) + } + spew.Dump(md) + + if err := zkt.Recover(types.EmptyRootHash); err != nil { + t.Fatal(err) + } +} + +func BenchmarkZKTrie(b *testing.B) { + const ( + newAddressNum uint64 = 10000 + reuseAddressNum uint64 = 10000 + outpointPerReusedAddr uint64 = 5 + outpointPerNewAddr uint64 = 5 + ) + home := b.TempDir() + + zkt, err := NewZKTrie(home) + if err != nil { + b.Fatal(err) + } + + // Pre-insert N outs for reuse + prevBlock := *chaincfg.TestNet3Params.GenesisHash + prevStateRoot := types.EmptyRootHash + outpoints := make(map[uint64][]Outpoint) + + bh := chainhash.Hash(random(32)) + blk := NewZKBlock(bh, prevBlock, prevStateRoot, 0) + + // simulate outs + for i := range reuseAddressNum { + var pkScript [8]byte + binary.BigEndian.PutUint64(pkScript[:], i) + outpoints[i] = make([]Outpoint, outpointPerReusedAddr) + for j := range outpointPerReusedAddr { + o := NewOutpoint([32]byte(random(32)), 1) + so := NewSpendableOutput(blk.blockHash, [32]byte(random(32)), 1, 100) + blk.NewOut(pkScript[:], o, so) + outpoints[i][j] = o + } + } + + sr, err := zkt.InsertBlock(blk) + if err != nil { + b.Fatal(err) + } + b.Logf("inserted block %d, new state root: %v", blk.GetMetadata().Height(), sr) + + if err := zkt.Commit(); err != nil { + b.Fatal(err) + } + + bhIn := chainhash.Hash(random(32)) + blkIn := NewZKBlock(bhIn, bh, sr, 1) + + for i := range reuseAddressNum { + var pkScript [8]byte + binary.BigEndian.PutUint64(pkScript[:], i) + for _, o := range outpoints[i] { + so := NewSpentOutput(blkIn.blockHash, [32]byte(random(32)), 100) + blkIn.NewIn(pkScript[:], o, so) + } + } + + for i := range newAddressNum { + var pkScript [8]byte + binary.BigEndian.PutUint64(pkScript[:], i+reuseAddressNum) + for range outpointPerNewAddr { + o := NewOutpoint([32]byte(random(32)), 1) + so := NewSpendableOutput(blkIn.blockHash, [32]byte(random(32)), 1, 100) + blkIn.NewOut(pkScript[:], o, so) + } + } + + b.Run("Block Insert", func(b *testing.B) { + for b.Loop() { + _, err := zkt.InsertBlock(blkIn) + if err != nil { + b.Fatal(err) + } + // if err := zkt.Commit(); err != nil { + // b.Fatal(err) + // } + // if err := zkt.Recover(sr); err != nil { + // b.Fatal(err) + // } + } + }) + + b.Run("Block Commit And Revert", func(b *testing.B) { + for b.Loop() { + _, err := zkt.InsertBlock(blkIn) + if err != nil { + b.Fatal(err) + } + if err := zkt.Commit(); err != nil { + b.Fatal(err) + } + if err := zkt.Recover(sr); err != nil { + b.Fatal(err) + } + } + }) + +} + +// Random returns a variable number of random bytes. +func random(n int) []byte { + buffer := make([]byte, n) + _, err := io.ReadFull(rand.Reader, buffer) + if err != nil { + panic(err) + } + return buffer +}