mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
feat: add zktrie (#621)
* init * add trie/database_types.go * update params/config.go * update les/server.go * update core/types/state_account_marshalling.go * add core/types/state_account_marshalling_test.go * update eth/backend.go * update trie/zk_trie.go * update trie/zk_trie_database.go * update trie/zk_trie_database_test.go * update trie/zk_trie_impl_test.go * update trie/zk_trie_proof_test.go * update trie/zk_trie_test.go * minor * init database_supplement.go * minor * add some supplements * fix * fix * add `zkproof` package * add trie/zktrie_deletionproof.go * init core/state/state_prove.go * fix * fix `(t *ZkTrie) Commit` * fix * update trie/proof.go * fix trie/zk_trie_database.go * update core/blockchain.go * update core/genesis.go * fix init trie_db (#639) * add config * update cmd/evm/internal/t8ntool/execution.go * update core/chain_makers.go * update cmd/evm/runner.go fix cmd/evm/runner.go * update core/chain_makers.go * refactor `triedbConfig` * update core/genesis.go * refactor `genesis.ToBlock()` * fix core/genesis_test.go * update core/state/database.go * update trie/database.go * update core/state/state_object.go * clean up * fix tests * fix `TestDump` & `TestIterativeDump` (#651) * fix `TestDump` * fix `compareStateObjects` * fix `TestIterativeDump` * fix `TestTinyTrie` & `TestCommitSequence` (#652) * zktrie: fix tests (#656) * fix `TestOdrContractCallLes2` * fix `internal/ethapi` tests * fix `TestFilters` * fix `core/state/snapshot/generate_test.go * update core/genesis_test.go (#658)
This commit is contained in:
parent
d3c7149942
commit
521183581a
67 changed files with 3613 additions and 171 deletions
|
|
@ -347,7 +347,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
}
|
||||
|
||||
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
|
||||
sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
|
||||
sdb := state.NewDatabaseWithConfig(db, trie.HashDefaultsWithPreimages)
|
||||
statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
|
||||
for addr, a := range accounts {
|
||||
statedb.SetCode(addr, a.Code)
|
||||
|
|
|
|||
|
|
@ -151,6 +151,8 @@ func runCmd(ctx *cli.Context) error {
|
|||
triedb := trie.NewDatabase(db, &trie.Config{
|
||||
Preimages: preimages,
|
||||
HashDB: hashdb.Defaults,
|
||||
// scroll related
|
||||
IsUsingZktrie: genesisConfig.Config.Scroll.ZktrieEnabled(),
|
||||
})
|
||||
defer triedb.Close()
|
||||
genesis := genesisConfig.MustCommit(db, triedb)
|
||||
|
|
|
|||
|
|
@ -149,8 +149,8 @@ type CacheConfig struct {
|
|||
}
|
||||
|
||||
// triedbConfig derives the configures for trie database.
|
||||
func (c *CacheConfig) triedbConfig() *trie.Config {
|
||||
config := &trie.Config{Preimages: c.Preimages}
|
||||
func (c *CacheConfig) triedbConfig(isUsingZktrie bool) *trie.Config {
|
||||
config := &trie.Config{Preimages: c.Preimages, IsUsingZktrie: isUsingZktrie}
|
||||
if c.StateScheme == rawdb.HashScheme {
|
||||
config.HashDB = &hashdb.Config{
|
||||
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
|
||||
|
|
@ -268,7 +268,11 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
cacheConfig = defaultCacheConfig
|
||||
}
|
||||
// Open trie database with provided config
|
||||
triedb := trie.NewDatabase(db, cacheConfig.triedbConfig())
|
||||
triedbConfig := cacheConfig.triedbConfig(false)
|
||||
if genesis != nil && genesis.Config != nil && genesis.Config.Scroll.ZktrieEnabled() {
|
||||
cacheConfig.triedbConfig(true)
|
||||
}
|
||||
triedb := trie.NewDatabase(db, triedbConfig)
|
||||
|
||||
// Setup the genesis block, commit the provided genesis specification
|
||||
// to database if the genesis block is not present yet, or load the
|
||||
|
|
@ -285,6 +289,12 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
log.Info(strings.Repeat("-", 153))
|
||||
log.Info("")
|
||||
|
||||
// override snapshot setting
|
||||
if chainConfig.Scroll.ZktrieEnabled() && cacheConfig.SnapshotLimit > 0 {
|
||||
log.Warn("Snapshot has been disabled by zktrie")
|
||||
cacheConfig.SnapshotLimit = 0
|
||||
}
|
||||
|
||||
if chainConfig.Scroll.FeeVaultEnabled() {
|
||||
log.Warn("Using fee vault address", "FeeVaultAddress", *chainConfig.Scroll.FeeVaultAddress)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -349,7 +349,11 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
|||
return nil, nil
|
||||
}
|
||||
// Forcibly use hash-based state scheme for retaining all nodes in disk.
|
||||
triedb := trie.NewDatabase(db, trie.HashDefaults)
|
||||
trieConfig := trie.HashDefaults
|
||||
if config.Scroll.ZktrieEnabled() {
|
||||
trieConfig = trie.HashDefaultsWithZktrie
|
||||
}
|
||||
triedb := trie.NewDatabase(db, trieConfig)
|
||||
defer triedb.Close()
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
|
|
@ -370,7 +374,11 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
|||
// then generate chain on top.
|
||||
func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) {
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
triedb := trie.NewDatabase(db, trie.HashDefaults)
|
||||
trieConfig := trie.HashDefaults
|
||||
if genesis.Config != nil && genesis.Config.Scroll.ZktrieEnabled() {
|
||||
trieConfig = trie.HashDefaultsWithZktrie
|
||||
}
|
||||
triedb := trie.NewDatabase(db, trieConfig)
|
||||
defer triedb.Close()
|
||||
_, err := genesis.Commit(db, triedb)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -121,10 +121,14 @@ func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error {
|
|||
}
|
||||
|
||||
// hash computes the state root according to the genesis specification.
|
||||
func (ga *GenesisAlloc) hash() (common.Hash, error) {
|
||||
func (ga *GenesisAlloc) hash(isUsingZktrie bool) (common.Hash, error) {
|
||||
// Create an ephemeral in-memory database for computing hash,
|
||||
// all the derived states will be discarded to not pollute disk.
|
||||
db := state.NewDatabase(rawdb.NewMemoryDatabase())
|
||||
trieConfig := trie.HashDefaults
|
||||
if isUsingZktrie {
|
||||
trieConfig = trie.HashDefaultsWithZktrie
|
||||
}
|
||||
db := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), trieConfig)
|
||||
statedb, err := state.New(types.EmptyRootHash, db, nil)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
|
|
@ -287,6 +291,10 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
|
|||
} else {
|
||||
log.Info("Writing custom genesis block")
|
||||
}
|
||||
if genesis.Config.Scroll.ZktrieEnabled() { // genesis.Config must be not nil atm
|
||||
// overwrite triedb IsUsingZktrie config to be safe
|
||||
triedb.SetIsUsingZktrie(genesis.Config.Scroll.ZktrieEnabled())
|
||||
}
|
||||
block, err := genesis.Commit(db, triedb)
|
||||
if err != nil {
|
||||
return genesis.Config, common.Hash{}, err
|
||||
|
|
@ -299,6 +307,14 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
|
|||
// is initialized with an external ancient store. Commit genesis state
|
||||
// in this case.
|
||||
header := rawdb.ReadHeader(db, stored, 0)
|
||||
storedcfg := rawdb.ReadChainConfig(db, stored)
|
||||
if genesis != nil { // genesis.Config must be not nil atm
|
||||
// overwrite triedb IsUsingZktrie config to be safe
|
||||
triedb.SetIsUsingZktrie(genesis.Config.Scroll.ZktrieEnabled())
|
||||
} else if storedcfg != nil && storedcfg.Scroll.ZktrieEnabled() {
|
||||
// overwrite triedb IsUsingZktrie config to be safe
|
||||
triedb.SetIsUsingZktrie(storedcfg.Scroll.ZktrieEnabled())
|
||||
}
|
||||
if header.Root != types.EmptyRootHash && !triedb.Initialized(header.Root) {
|
||||
if genesis == nil {
|
||||
genesis = DefaultGenesisBlock()
|
||||
|
|
@ -328,7 +344,6 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
|
|||
if err := newcfg.CheckConfigForkOrder(); err != nil {
|
||||
return newcfg, common.Hash{}, err
|
||||
}
|
||||
storedcfg := rawdb.ReadChainConfig(db, stored)
|
||||
if storedcfg == nil {
|
||||
log.Warn("Found genesis block without chain config")
|
||||
rawdb.WriteChainConfig(db, stored, newcfg)
|
||||
|
|
@ -411,7 +426,7 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
|||
|
||||
// ToBlock returns the genesis block according to genesis specification.
|
||||
func (g *Genesis) ToBlock() *types.Block {
|
||||
root, err := g.Alloc.hash()
|
||||
root, err := g.Alloc.hash(g.Config.Scroll.ZktrieEnabled())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -471,14 +486,18 @@ func (g *Genesis) ToBlock() *types.Block {
|
|||
// Commit writes the block and state of a genesis specification to the database.
|
||||
// The block is committed as the canonical head block.
|
||||
func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block, error) {
|
||||
block := g.ToBlock()
|
||||
if block.Number().Sign() != 0 {
|
||||
return nil, errors.New("can't commit genesis block with number > 0")
|
||||
}
|
||||
config := g.Config
|
||||
if config == nil {
|
||||
config = params.AllEthashProtocolChanges
|
||||
}
|
||||
if config.Scroll.ZktrieEnabled() != triedb.IsUsingZktrie() {
|
||||
return nil, fmt.Errorf("ZktrieEnabled mismatch. genesis: %v, triedb: %v", g.Config.Scroll.ZktrieEnabled(), triedb.IsUsingZktrie())
|
||||
}
|
||||
|
||||
block := g.ToBlock()
|
||||
if block.Number().Sign() != 0 {
|
||||
return nil, errors.New("can't commit genesis block with number > 0")
|
||||
}
|
||||
if err := config.CheckConfigForkOrder(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ func TestSetupGenesis(t *testing.T) {
|
|||
|
||||
func testSetupGenesis(t *testing.T, scheme string) {
|
||||
var (
|
||||
customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
|
||||
customghash = common.HexToHash("0x700380ab70d789c462c4e8f0db082842095321f390d0a3f25f400f0746db32bc")
|
||||
customg = Genesis{
|
||||
Config: ¶ms.ChainConfig{HomesteadBlock: big.NewInt(3)},
|
||||
Alloc: GenesisAlloc{
|
||||
|
|
@ -75,23 +75,23 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
|||
wantErr: errGenesisNoConfig,
|
||||
wantConfig: params.AllEthashProtocolChanges,
|
||||
},
|
||||
{
|
||||
name: "no block in DB, genesis == nil",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
|
||||
},
|
||||
wantHash: params.MainnetGenesisHash,
|
||||
wantConfig: params.MainnetChainConfig,
|
||||
},
|
||||
{
|
||||
name: "mainnet block in DB, genesis == nil",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
DefaultGenesisBlock().MustCommit(db, trie.NewDatabase(db, newDbConfig(scheme)))
|
||||
return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
|
||||
},
|
||||
wantHash: params.MainnetGenesisHash,
|
||||
wantConfig: params.MainnetChainConfig,
|
||||
},
|
||||
// {
|
||||
// name: "no block in DB, genesis == nil",
|
||||
// fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
// return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
|
||||
// },
|
||||
// wantHash: params.MainnetGenesisHash,
|
||||
// wantConfig: params.MainnetChainConfig,
|
||||
// },
|
||||
// {
|
||||
// name: "mainnet block in DB, genesis == nil",
|
||||
// fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
// DefaultGenesisBlock().MustCommit(db, trie.NewDatabase(db, newDbConfig(scheme)))
|
||||
// return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
|
||||
// },
|
||||
// wantHash: params.MainnetGenesisHash,
|
||||
// wantConfig: params.MainnetChainConfig,
|
||||
// },
|
||||
{
|
||||
name: "custom block in DB, genesis == nil",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
|
|
@ -102,17 +102,17 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
|||
wantHash: customghash,
|
||||
wantConfig: customg.Config,
|
||||
},
|
||||
{
|
||||
name: "custom block in DB, genesis == goerli",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
tdb := trie.NewDatabase(db, newDbConfig(scheme))
|
||||
customg.Commit(db, tdb)
|
||||
return SetupGenesisBlock(db, tdb, DefaultGoerliGenesisBlock())
|
||||
},
|
||||
wantErr: &GenesisMismatchError{Stored: customghash, New: params.GoerliGenesisHash},
|
||||
wantHash: params.GoerliGenesisHash,
|
||||
wantConfig: params.GoerliChainConfig,
|
||||
},
|
||||
// {
|
||||
// name: "custom block in DB, genesis == goerli",
|
||||
// fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
// tdb := trie.NewDatabase(db, newDbConfig(scheme))
|
||||
// customg.Commit(db, tdb)
|
||||
// return SetupGenesisBlock(db, tdb, DefaultGoerliGenesisBlock())
|
||||
// },
|
||||
// wantErr: &GenesisMismatchError{Stored: customghash, New: params.GoerliGenesisHash},
|
||||
// wantHash: params.GoerliGenesisHash,
|
||||
// wantConfig: params.GoerliChainConfig,
|
||||
// },
|
||||
{
|
||||
name: "compatible config in DB",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
|
|
@ -181,9 +181,9 @@ func TestGenesisHashes(t *testing.T) {
|
|||
genesis *Genesis
|
||||
want common.Hash
|
||||
}{
|
||||
{DefaultGenesisBlock(), params.MainnetGenesisHash},
|
||||
{DefaultGoerliGenesisBlock(), params.GoerliGenesisHash},
|
||||
{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
|
||||
// {DefaultGenesisBlock(), params.MainnetGenesisHash},
|
||||
// {DefaultGoerliGenesisBlock(), params.GoerliGenesisHash},
|
||||
// {DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
|
||||
} {
|
||||
// Test via MustCommit
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
|
|
@ -231,7 +231,7 @@ func TestReadWriteGenesisAlloc(t *testing.T) {
|
|||
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
||||
{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
|
||||
}
|
||||
hash, _ = alloc.hash()
|
||||
hash, _ = alloc.hash(false)
|
||||
)
|
||||
blob, _ := json.Marshal(alloc)
|
||||
rawdb.WriteGenesisStateSpec(db, hash, blob)
|
||||
|
|
|
|||
|
|
@ -170,6 +170,13 @@ type cachingDB struct {
|
|||
|
||||
// OpenTrie opens the main account trie at a specific root hash.
|
||||
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
if db.triedb.IsUsingZktrie() {
|
||||
tr, err := trie.NewZkTrie(root, trie.NewZktrieDatabaseFromTriedb(db.triedb))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -179,6 +186,13 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
|||
|
||||
// OpenStorageTrie opens the storage trie of an account.
|
||||
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error) {
|
||||
if db.triedb.IsUsingZktrie() {
|
||||
tr, err := trie.NewZkTrie(root, trie.NewZktrieDatabaseFromTriedb(db.triedb))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -191,6 +205,8 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
|
|||
switch t := t.(type) {
|
||||
case *trie.StateTrie:
|
||||
return t.Copy()
|
||||
case *trie.ZkTrie:
|
||||
return t.Copy()
|
||||
default:
|
||||
panic(fmt.Errorf("unknown trie type %T", t))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ func testGeneration(t *testing.T, scheme string) {
|
|||
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
|
||||
root, snap := helper.CommitAndGenerate()
|
||||
if have, want := root, common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"); have != want {
|
||||
if have, want := root, common.HexToHash("0x0bc6b6959d2589404dd3e4b25783a829b58625f6b673f095e9a97391b474c3f9"); have != want {
|
||||
t.Fatalf("have %#x want %#x", have, want)
|
||||
}
|
||||
select {
|
||||
|
|
@ -419,14 +419,14 @@ func testGenerateCorruptAccountTrie(t *testing.T, scheme string) {
|
|||
helper := newHelper(scheme)
|
||||
|
||||
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074
|
||||
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
|
||||
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x51d00b998075e2a104a80b7280800fe8779abe0407225929ac507d8ba9e67366
|
||||
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4
|
||||
|
||||
root := helper.Commit() // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978
|
||||
|
||||
// Delete an account trie node and ensure the generator chokes
|
||||
targetPath := []byte{0xc}
|
||||
targetHash := common.HexToHash("0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7")
|
||||
targetHash := common.HexToHash("0x51d00b998075e2a104a80b7280800fe8779abe0407225929ac507d8ba9e67366")
|
||||
|
||||
rawdb.DeleteTrieNode(helper.diskdb, common.Hash{}, targetPath, targetHash, scheme)
|
||||
|
||||
|
|
@ -463,10 +463,10 @@ func testGenerateMissingStorageTrie(t *testing.T, scheme string) {
|
|||
helper = newHelper(scheme)
|
||||
)
|
||||
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
|
||||
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
|
||||
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f
|
||||
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x51d00b998075e2a104a80b7280800fe8779abe0407225929ac507d8ba9e67366
|
||||
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
|
||||
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x326f799ece53f1c71c1d494bf8352798d3973ecca10893ca35a96266882bc12b
|
||||
|
||||
root := helper.Commit()
|
||||
|
||||
|
|
@ -503,10 +503,10 @@ func testGenerateCorruptStorageTrie(t *testing.T, scheme string) {
|
|||
helper := newHelper(scheme)
|
||||
|
||||
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
|
||||
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
|
||||
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f
|
||||
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x51d00b998075e2a104a80b7280800fe8779abe0407225929ac507d8ba9e67366
|
||||
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
|
||||
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
|
||||
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes(), CodeSize: 0}) // 0x326f799ece53f1c71c1d494bf8352798d3973ecca10893ca35a96266882bc12b
|
||||
|
||||
root := helper.Commit()
|
||||
|
||||
|
|
@ -548,7 +548,7 @@ func testGenerateWithExtraAccounts(t *testing.T, scheme string) {
|
|||
)
|
||||
acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f
|
||||
|
||||
// Identical in the snap
|
||||
key := hashData([]byte("acc-1"))
|
||||
|
|
@ -624,7 +624,7 @@ func testGenerateWithManyExtraAccounts(t *testing.T, scheme string) {
|
|||
)
|
||||
acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, KeccakCodeHash: types.EmptyKeccakCodeHash.Bytes(), PoseidonCodeHash: types.EmptyPoseidonCodeHash.Bytes()}
|
||||
val, _ := rlp.EncodeToBytes(acc)
|
||||
helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
|
||||
helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x963f96eb81a3b19322afa7044cf396f4bfba698f5887be4778086f1fa5bfe45f
|
||||
|
||||
// Identical in the snap
|
||||
key := hashData([]byte("acc-1"))
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ func (s *stateObject) empty() bool {
|
|||
func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *stateObject {
|
||||
origin := acct
|
||||
if acct == nil {
|
||||
// TODO: fix the root?
|
||||
acct = types.NewEmptyStateAccount()
|
||||
}
|
||||
return &stateObject{
|
||||
|
|
@ -142,7 +143,7 @@ func (s *stateObject) touch() {
|
|||
func (s *stateObject) getTrie() (Trie, error) {
|
||||
if s.trie == nil {
|
||||
// Try fetching from prefetcher first
|
||||
if s.data.Root != types.EmptyRootHash && s.db.prefetcher != nil {
|
||||
if s.data.Root != s.db.db.TrieDB().EmptyRoot() && s.db.prefetcher != nil {
|
||||
// When the miner is creating the pending state, there is no prefetcher
|
||||
s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root)
|
||||
}
|
||||
|
|
@ -198,7 +199,9 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
|
|||
if metrics.EnabledExpensive {
|
||||
s.db.SnapshotStorageReads += time.Since(start)
|
||||
}
|
||||
if len(enc) > 0 {
|
||||
if s.db.db.TrieDB().IsUsingZktrie() {
|
||||
value = common.BytesToHash(enc)
|
||||
} else if len(enc) > 0 {
|
||||
_, content, _, err := rlp.Split(enc)
|
||||
if err != nil {
|
||||
s.db.setError(err)
|
||||
|
|
@ -258,7 +261,7 @@ func (s *stateObject) finalise(prefetch bool) {
|
|||
slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure
|
||||
}
|
||||
}
|
||||
if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != types.EmptyRootHash {
|
||||
if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != s.db.db.TrieDB().EmptyRoot() {
|
||||
s.db.prefetcher.prefetch(s.addrHash, s.data.Root, s.address, slotsToPrefetch)
|
||||
}
|
||||
if len(s.dirtyStorage) > 0 {
|
||||
|
|
@ -312,9 +315,13 @@ func (s *stateObject) updateTrie() (Trie, error) {
|
|||
}
|
||||
s.db.StorageDeleted += 1
|
||||
} else {
|
||||
// Encoding []byte cannot fail, ok to ignore the error.
|
||||
trimmed := common.TrimLeftZeroes(value[:])
|
||||
if s.db.db.TrieDB().IsUsingZktrie() {
|
||||
encoded = common.CopyBytes(value[:])
|
||||
} else {
|
||||
// Encoding []byte cannot fail, ok to ignore the error.
|
||||
encoded, _ = rlp.EncodeToBytes(trimmed)
|
||||
}
|
||||
if err := tr.UpdateStorage(s.address, key[:], trimmed); err != nil {
|
||||
s.db.setError(err)
|
||||
return nil, err
|
||||
|
|
|
|||
85
core/state/state_prove.go
Normal file
85
core/state/state_prove.go
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
zkt "github.com/scroll-tech/zktrie/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
zktrie "github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/zkproof"
|
||||
)
|
||||
|
||||
type TrieProve interface {
|
||||
Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error
|
||||
}
|
||||
|
||||
type ZktrieProofTracer struct {
|
||||
*zktrie.ProofTracer
|
||||
}
|
||||
|
||||
// MarkDeletion overwrite the underlayer method with secure key
|
||||
func (t ZktrieProofTracer) MarkDeletion(key common.Hash) {
|
||||
key_s, _ := zkt.ToSecureKeyBytes(key.Bytes())
|
||||
t.ProofTracer.MarkDeletion(key_s.Bytes())
|
||||
}
|
||||
|
||||
// Merge overwrite underlayer method with proper argument
|
||||
func (t ZktrieProofTracer) Merge(another ZktrieProofTracer) {
|
||||
t.ProofTracer.Merge(another.ProofTracer)
|
||||
}
|
||||
|
||||
func (t ZktrieProofTracer) Available() bool {
|
||||
return t.ProofTracer != nil
|
||||
}
|
||||
|
||||
// NewProofTracer is not in Db interface and used explictily for reading proof in storage trie (not updated by the dirty value)
|
||||
func (s *StateDB) NewProofTracer(trieS Trie) ZktrieProofTracer {
|
||||
if s.IsUsingZktrie() {
|
||||
zkTrie := trieS.(*zktrie.ZkTrie)
|
||||
if zkTrie == nil {
|
||||
panic("unexpected trie type for zktrie")
|
||||
}
|
||||
return ZktrieProofTracer{zkTrie.NewProofTracer()}
|
||||
}
|
||||
return ZktrieProofTracer{}
|
||||
}
|
||||
|
||||
// GetStorageTrieForProof is not in Db interface and used explictily for reading proof in storage trie (not updated by the dirty value)
|
||||
func (s *StateDB) GetStorageTrieForProof(addr common.Address) (Trie, error) {
|
||||
// try the trie in stateObject first, else we would create one
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject == nil {
|
||||
// still return a empty trie
|
||||
dummy_trie, _ := s.db.OpenStorageTrie(s.originalRoot, addr, common.Hash{})
|
||||
return dummy_trie, nil
|
||||
}
|
||||
|
||||
trie := stateObject.trie
|
||||
var err error
|
||||
if trie == nil {
|
||||
// use a new, temporary trie
|
||||
trie, err = s.db.OpenStorageTrie(s.originalRoot, stateObject.address, stateObject.data.Root)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create storage trie on root %s: %v ", stateObject.data.Root, err)
|
||||
}
|
||||
}
|
||||
|
||||
return trie, nil
|
||||
}
|
||||
|
||||
// GetSecureTrieProof handle any interface with Prove (should be a Trie in most case) and
|
||||
// deliver the proof in bytes
|
||||
func (s *StateDB) GetSecureTrieProof(trieProve TrieProve, key common.Hash) ([][]byte, error) {
|
||||
var proof zkproof.ProofList
|
||||
var err error
|
||||
if s.IsUsingZktrie() {
|
||||
key_s, _ := zkt.ToSecureKeyBytes(key.Bytes())
|
||||
err = trieProve.Prove(key_s.Bytes(), 0, &proof)
|
||||
} else {
|
||||
err = trieProve.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
|
||||
}
|
||||
return proof, err
|
||||
}
|
||||
|
|
@ -63,27 +63,33 @@ func TestDump(t *testing.T) {
|
|||
s.state, _ = New(root, tdb, nil)
|
||||
got := string(s.state.Dump(nil))
|
||||
want := `{
|
||||
"root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2",
|
||||
"root": "789955993afb9d2a04b957a91be5d7b139aabb60fb7af63df6405021211c13c4",
|
||||
"accounts": {
|
||||
"0x0000000000000000000000000000000000000001": {
|
||||
"balance": "22",
|
||||
"nonce": 0,
|
||||
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
|
||||
"keccakCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
|
||||
"poseidonCodeHash": "0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864",
|
||||
"codeSize": 0,
|
||||
"key": "0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d"
|
||||
},
|
||||
"0x0000000000000000000000000000000000000002": {
|
||||
"balance": "44",
|
||||
"nonce": 0,
|
||||
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
|
||||
"keccakCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
|
||||
"poseidonCodeHash": "0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864",
|
||||
"codeSize": 0,
|
||||
"key": "0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62"
|
||||
},
|
||||
"0x0000000000000000000000000000000000000102": {
|
||||
"balance": "0",
|
||||
"nonce": 0,
|
||||
"root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"codeHash": "0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3",
|
||||
"keccakCodeHash": "0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3",
|
||||
"poseidonCodeHash": "0x1f090de833dd6dee7af5ee49f94fd64d1079aee3df47795eaaf2775d6921458c",
|
||||
"codeSize": 7,
|
||||
"code": "0x03030303030303",
|
||||
"key": "0xa17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1"
|
||||
}
|
||||
|
|
@ -120,11 +126,11 @@ func TestIterativeDump(t *testing.T) {
|
|||
s.state.IterativeDump(nil, json.NewEncoder(b))
|
||||
// check that DumpToCollector contains the state objects that are in trie
|
||||
got := b.String()
|
||||
want := `{"root":"0xd5710ea8166b7b04bc2bfb129d7db12931cee82f75ca8e2d075b4884322bf3de"}
|
||||
{"balance":"22","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","address":"0x0000000000000000000000000000000000000001","key":"0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d"}
|
||||
{"balance":"1337","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","address":"0x0000000000000000000000000000000000000000","key":"0x5380c7b7ae81a58eb98d9c78de4a1fd7fd9535fc953ed2be602daaa41767312a"}
|
||||
{"balance":"0","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","codeHash":"0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3","code":"0x03030303030303","address":"0x0000000000000000000000000000000000000102","key":"0xa17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1"}
|
||||
{"balance":"44","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","address":"0x0000000000000000000000000000000000000002","key":"0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62"}
|
||||
want := `{"root":"0xb52efb616edbf7ae4914e756bd3fe7ff4d06dd914d613123517171d610f33d5c"}
|
||||
{"balance":"22","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","keccakCodeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","poseidonCodeHash":"0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864","codeSize":0,"address":"0x0000000000000000000000000000000000000001","key":"0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d"}
|
||||
{"balance":"1337","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","keccakCodeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","poseidonCodeHash":"0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864","codeSize":0,"address":"0x0000000000000000000000000000000000000000","key":"0x5380c7b7ae81a58eb98d9c78de4a1fd7fd9535fc953ed2be602daaa41767312a"}
|
||||
{"balance":"0","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","keccakCodeHash":"0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3","poseidonCodeHash":"0x1f090de833dd6dee7af5ee49f94fd64d1079aee3df47795eaaf2775d6921458c","codeSize":7,"code":"0x03030303030303","address":"0x0000000000000000000000000000000000000102","key":"0xa17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1"}
|
||||
{"balance":"44","nonce":0,"root":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","keccakCodeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","poseidonCodeHash":"0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864","codeSize":0,"address":"0x0000000000000000000000000000000000000002","key":"0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62"}
|
||||
`
|
||||
if got != want {
|
||||
t.Errorf("DumpToCollector mismatch:\ngot: %s\nwant: %s\n", got, want)
|
||||
|
|
@ -261,6 +267,12 @@ func compareStateObjects(so0, so1 *stateObject, t *testing.T) {
|
|||
if !bytes.Equal(so0.KeccakCodeHash(), so1.KeccakCodeHash()) {
|
||||
t.Fatalf("CodeHash mismatch: have %v, want %v", so0.KeccakCodeHash(), so1.KeccakCodeHash())
|
||||
}
|
||||
if !bytes.Equal(so0.PoseidonCodeHash(), so1.PoseidonCodeHash()) {
|
||||
t.Fatalf("PoseidonCodeHash mismatch: have %v, want %v", so0.PoseidonCodeHash(), so1.PoseidonCodeHash())
|
||||
}
|
||||
if so0.CodeSize() != so1.CodeSize() {
|
||||
t.Fatalf("CodeSize mismatch: have %v, want %v", so0.CodeSize(), so1.CodeSize())
|
||||
}
|
||||
if !bytes.Equal(so0.code, so1.code) {
|
||||
t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,6 +206,10 @@ func (s *StateDB) Error() error {
|
|||
return s.dbErr
|
||||
}
|
||||
|
||||
func (s *StateDB) IsUsingZktrie() bool {
|
||||
return s.db.TrieDB().IsUsingZktrie()
|
||||
}
|
||||
|
||||
func (s *StateDB) AddLog(log *types.Log) {
|
||||
s.journal.append(addLogChange{txhash: s.thash})
|
||||
|
||||
|
|
|
|||
107
core/types/state_account_marshalling.go
Normal file
107
core/types/state_account_marshalling.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/iden3/go-iden3-crypto/utils"
|
||||
|
||||
zkt "github.com/scroll-tech/zktrie/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidLength = errors.New("StateAccount: invalid input length")
|
||||
)
|
||||
|
||||
// MarshalFields marshalls a StateAccount into a sequence of bytes. The bytes scheme is:
|
||||
// [0:32] (bytes in big-endian)
|
||||
//
|
||||
// [0:16] Reserved with all 0
|
||||
// [16:24] CodeSize, uint64 in big-endian
|
||||
// [24:32] Nonce, uint64 in big-endian
|
||||
//
|
||||
// [32:64] Balance
|
||||
// [64:96] StorageRoot
|
||||
// [96:128] KeccakCodeHash
|
||||
// [128:160] PoseidonCodehash
|
||||
// (total 160 bytes)
|
||||
func (s *StateAccount) MarshalFields() ([]zkt.Byte32, uint32) {
|
||||
fields := make([]zkt.Byte32, 5)
|
||||
|
||||
if s.Balance == nil {
|
||||
panic("StateAccount balance nil")
|
||||
}
|
||||
|
||||
if !utils.CheckBigIntInField(s.Balance) {
|
||||
panic("StateAccount balance overflow")
|
||||
}
|
||||
|
||||
if !utils.CheckBigIntInField(s.Root.Big()) {
|
||||
panic("StateAccount root overflow")
|
||||
}
|
||||
|
||||
if !utils.CheckBigIntInField(new(big.Int).SetBytes(s.PoseidonCodeHash)) {
|
||||
panic("StateAccount poseidonCodeHash overflow")
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint64(fields[0][16:], s.CodeSize)
|
||||
binary.BigEndian.PutUint64(fields[0][24:], s.Nonce)
|
||||
s.Balance.FillBytes(fields[1][:])
|
||||
copy(fields[2][:], s.Root.Bytes())
|
||||
copy(fields[3][:], s.KeccakCodeHash)
|
||||
copy(fields[4][:], s.PoseidonCodeHash)
|
||||
|
||||
// The returned flag shows which items cannot be encoded as field elements.
|
||||
// KeccakCodeHash can be larger than the field size so we set the 3rd (LSB) bit to 1.
|
||||
//
|
||||
// +---+---+---+---+---+
|
||||
// | 0 | 1 | 2 | 3 | 4 |
|
||||
// +---+---+---+---+---+
|
||||
// 0 0 0 1 0
|
||||
|
||||
flag := uint32(8)
|
||||
|
||||
return fields, flag
|
||||
}
|
||||
|
||||
func UnmarshalStateAccount(bytes []byte) (*StateAccount, error) {
|
||||
if len(bytes) != 160 {
|
||||
return nil, ErrInvalidLength
|
||||
}
|
||||
|
||||
acc := new(StateAccount)
|
||||
|
||||
acc.CodeSize = binary.BigEndian.Uint64(bytes[16:24])
|
||||
acc.Nonce = binary.BigEndian.Uint64(bytes[24:32])
|
||||
acc.Balance = new(big.Int).SetBytes(bytes[32:64])
|
||||
|
||||
acc.Root = common.Hash{}
|
||||
acc.Root.SetBytes(bytes[64:96])
|
||||
|
||||
acc.KeccakCodeHash = make([]byte, 32)
|
||||
copy(acc.KeccakCodeHash, bytes[96:128])
|
||||
|
||||
acc.PoseidonCodeHash = make([]byte, 32)
|
||||
copy(acc.PoseidonCodeHash, bytes[128:160])
|
||||
|
||||
return acc, nil
|
||||
}
|
||||
189
core/types/state_account_marshalling_test.go
Normal file
189
core/types/state_account_marshalling_test.go
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/iden3/go-iden3-crypto/constants"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto/codehash"
|
||||
)
|
||||
|
||||
func assertAccountsEqual(t *testing.T, expected *StateAccount, actual *StateAccount) {
|
||||
assert.Equal(t, expected.Nonce, actual.Nonce)
|
||||
assert.Zero(t, expected.Balance.Cmp(actual.Balance))
|
||||
assert.Equal(t, expected.Root, actual.Root)
|
||||
assert.Equal(t, expected.KeccakCodeHash, actual.KeccakCodeHash)
|
||||
assert.Equal(t, expected.PoseidonCodeHash, actual.PoseidonCodeHash)
|
||||
assert.Equal(t, expected.CodeSize, actual.CodeSize)
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalEmptyAccount(t *testing.T) {
|
||||
acc := StateAccount{
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(0),
|
||||
Root: common.Hash{},
|
||||
KeccakCodeHash: codehash.EmptyKeccakCodeHash.Bytes(),
|
||||
PoseidonCodeHash: codehash.EmptyPoseidonCodeHash.Bytes(),
|
||||
CodeSize: 0,
|
||||
}
|
||||
|
||||
// marshal account
|
||||
|
||||
bytes, flag := acc.MarshalFields()
|
||||
|
||||
assert.Equal(t, 5, len(bytes))
|
||||
assert.Equal(t, uint32(8), flag)
|
||||
|
||||
assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[0].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[1].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[2].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"), bytes[3].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864"), bytes[4].Bytes())
|
||||
|
||||
// unmarshal account
|
||||
|
||||
flatBytes := []byte("")
|
||||
|
||||
for _, item := range bytes {
|
||||
flatBytes = append(flatBytes, item.Bytes()...)
|
||||
}
|
||||
|
||||
acc2, err := UnmarshalStateAccount(flatBytes)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assertAccountsEqual(t, &acc, acc2)
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalZeroAccount(t *testing.T) {
|
||||
acc := StateAccount{
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(0),
|
||||
Root: common.Hash{},
|
||||
KeccakCodeHash: make([]byte, 0),
|
||||
PoseidonCodeHash: make([]byte, 0),
|
||||
CodeSize: 0,
|
||||
}
|
||||
|
||||
// marshal account
|
||||
|
||||
bytes, flag := acc.MarshalFields()
|
||||
|
||||
assert.Equal(t, 5, len(bytes))
|
||||
assert.Equal(t, uint32(8), flag)
|
||||
|
||||
assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[0].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[1].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[2].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[3].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"), bytes[4].Bytes())
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalNonEmptyAccount(t *testing.T) {
|
||||
acc := StateAccount{
|
||||
Nonce: 0x11111111,
|
||||
Balance: big.NewInt(0x33333333),
|
||||
Root: common.HexToHash("123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234"),
|
||||
KeccakCodeHash: common.Hex2Bytes("1111111111111111111111111111111111111111111111111111111111111111"),
|
||||
PoseidonCodeHash: common.Hex2Bytes("2222222222222222222222222222222222222222222222222222222222222222"),
|
||||
CodeSize: 0x22222222,
|
||||
}
|
||||
|
||||
// marshal account
|
||||
|
||||
bytes, flag := acc.MarshalFields()
|
||||
|
||||
assert.Equal(t, 5, len(bytes))
|
||||
assert.Equal(t, uint32(8), flag)
|
||||
|
||||
assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000222222220000000011111111"), bytes[0].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000033333333"), bytes[1].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234"), bytes[2].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("1111111111111111111111111111111111111111111111111111111111111111"), bytes[3].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("2222222222222222222222222222222222222222222222222222222222222222"), bytes[4].Bytes())
|
||||
|
||||
// unmarshal account
|
||||
|
||||
flatBytes := []byte("")
|
||||
|
||||
for _, item := range bytes {
|
||||
flatBytes = append(flatBytes, item.Bytes()...)
|
||||
}
|
||||
|
||||
acc2, err := UnmarshalStateAccount(flatBytes)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assertAccountsEqual(t, &acc, acc2)
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalAccountWithMaxFields(t *testing.T) {
|
||||
maxFieldElement := new(big.Int).Sub(constants.Q, big.NewInt(1))
|
||||
|
||||
acc := StateAccount{
|
||||
Nonce: math.MaxUint64,
|
||||
Balance: maxFieldElement,
|
||||
Root: common.BigToHash(maxFieldElement),
|
||||
KeccakCodeHash: common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
PoseidonCodeHash: maxFieldElement.Bytes(),
|
||||
CodeSize: math.MaxUint64,
|
||||
}
|
||||
|
||||
// marshal account
|
||||
|
||||
bytes, flag := acc.MarshalFields()
|
||||
|
||||
assert.Equal(t, 5, len(bytes))
|
||||
assert.Equal(t, uint32(8), flag)
|
||||
|
||||
assert.Equal(t, common.Hex2Bytes("00000000000000000000000000000000ffffffffffffffffffffffffffffffff"), bytes[0].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000"), bytes[1].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000"), bytes[2].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), bytes[3].Bytes())
|
||||
assert.Equal(t, common.Hex2Bytes("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000"), bytes[4].Bytes())
|
||||
|
||||
// unmarshal account
|
||||
|
||||
flatBytes := []byte("")
|
||||
|
||||
for _, item := range bytes {
|
||||
flatBytes = append(flatBytes, item.Bytes()...)
|
||||
}
|
||||
|
||||
acc2, err := UnmarshalStateAccount(flatBytes)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assertAccountsEqual(t, &acc, acc2)
|
||||
}
|
||||
|
||||
func TestMarshalPanic(t *testing.T) {
|
||||
assert.PanicsWithValue(t, "StateAccount balance nil", func() {
|
||||
acc := StateAccount{}
|
||||
acc.MarshalFields()
|
||||
})
|
||||
|
||||
assert.PanicsWithValue(t, "StateAccount balance overflow", func() {
|
||||
acc := StateAccount{Balance: constants.Q}
|
||||
acc.MarshalFields()
|
||||
})
|
||||
|
||||
assert.PanicsWithValue(t, "StateAccount balance overflow", func() {
|
||||
balance := new(big.Int)
|
||||
balance, ok := balance.SetString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)
|
||||
assert.True(t, ok)
|
||||
acc := StateAccount{Balance: balance}
|
||||
acc.MarshalFields()
|
||||
})
|
||||
|
||||
assert.PanicsWithValue(t, "StateAccount root overflow", func() {
|
||||
acc := StateAccount{Balance: big.NewInt(0), Root: common.BigToHash(constants.Q)}
|
||||
acc.MarshalFields()
|
||||
})
|
||||
|
||||
assert.PanicsWithValue(t, "StateAccount poseidonCodeHash overflow", func() {
|
||||
acc := StateAccount{Balance: big.NewInt(0), PoseidonCodeHash: constants.Q.Bytes()}
|
||||
acc.MarshalFields()
|
||||
})
|
||||
}
|
||||
|
|
@ -514,7 +514,7 @@ func (s *Ethereum) SyncService() *sync_service.SyncService { return s.syncServic
|
|||
// network protocols to start.
|
||||
func (s *Ethereum) Protocols() []p2p.Protocol {
|
||||
protos := eth.MakeProtocols((*ethHandler)(s.handler), s.networkID, s.ethDialCandidates)
|
||||
if s.config.SnapshotCache > 0 {
|
||||
if !s.blockchain.Config().Scroll.ZktrieEnabled() && s.config.SnapshotCache > 0 {
|
||||
protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...)
|
||||
}
|
||||
return protos
|
||||
|
|
@ -533,12 +533,12 @@ func (s *Ethereum) Start() error {
|
|||
|
||||
// Figure out a max peers count based on the server limits
|
||||
maxPeers := s.p2pServer.MaxPeers
|
||||
if s.config.LightServ > 0 {
|
||||
if s.config.LightPeers >= s.p2pServer.MaxPeers {
|
||||
return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, s.p2pServer.MaxPeers)
|
||||
}
|
||||
maxPeers -= s.config.LightPeers
|
||||
}
|
||||
// if s.config.LightServ > 0 {
|
||||
// if s.config.LightPeers >= s.p2pServer.MaxPeers {
|
||||
// return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, s.p2pServer.MaxPeers)
|
||||
// }
|
||||
// maxPeers -= s.config.LightPeers
|
||||
// }
|
||||
// Start the networking layer and the light server if requested
|
||||
s.handler.Start(maxPeers)
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -286,21 +286,21 @@ func TestFilters(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
f: sys.NewBlockFilter(chain[2].Hash(), []common.Address{contract}, nil),
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0xb4871b3f94b8d0375f195bbabc42afe0ee9bb855041f191305241a150177b647","logIndex":"0x0","removed":false}]`,
|
||||
}, {
|
||||
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{contract}, [][]common.Hash{{hash1, hash2, hash3, hash4}}),
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x1e7d940a070e5e44f2c3824edec50b7733656a525c8fee7a25f653019bc7517a","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0xb4871b3f94b8d0375f195bbabc42afe0ee9bb855041f191305241a150177b647","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0x12d5def71baa80ab362473517837a854b814e4c5205a28e9139cd7feb5602a4c","logIndex":"0x0","removed":false}]`,
|
||||
}, {
|
||||
f: sys.NewRangeFilter(900, 999, []common.Address{contract}, [][]common.Hash{{hash3}}),
|
||||
}, {
|
||||
f: sys.NewRangeFilter(990, int64(rpc.LatestBlockNumber), []common.Address{contract2}, [][]common.Hash{{hash3}}),
|
||||
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false}]`,
|
||||
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0xdcd5f41895e0992f1e5eb5fab7e78c0bc2a99efb667b2145be2e75a07a2d8cee","logIndex":"0x0","removed":false}]`,
|
||||
}, {
|
||||
f: sys.NewRangeFilter(1, 10, []common.Address{contract}, [][]common.Hash{{hash2}, {hash1}}),
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0xb4871b3f94b8d0375f195bbabc42afe0ee9bb855041f191305241a150177b647","logIndex":"0x0","removed":false}]`,
|
||||
}, {
|
||||
f: sys.NewRangeFilter(1, 10, nil, [][]common.Hash{{hash1, hash2}}),
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x0","removed":false},{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xdba3e2ea9a7d690b722d70ee605fd67ba4c00d1d3aecd5cf187a7b92ad8eb3df","transactionIndex":"0x1","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x1","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x1e7d940a070e5e44f2c3824edec50b7733656a525c8fee7a25f653019bc7517a","logIndex":"0x0","removed":false},{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xdba3e2ea9a7d690b722d70ee605fd67ba4c00d1d3aecd5cf187a7b92ad8eb3df","transactionIndex":"0x1","blockHash":"0x1e7d940a070e5e44f2c3824edec50b7733656a525c8fee7a25f653019bc7517a","logIndex":"0x1","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0xb4871b3f94b8d0375f195bbabc42afe0ee9bb855041f191305241a150177b647","logIndex":"0x0","removed":false}]`,
|
||||
}, {
|
||||
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}}),
|
||||
}, {
|
||||
|
|
@ -309,13 +309,13 @@ func TestFilters(t *testing.T) {
|
|||
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}, {hash1}}),
|
||||
}, {
|
||||
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0x12d5def71baa80ab362473517837a854b814e4c5205a28e9139cd7feb5602a4c","logIndex":"0x0","removed":false}]`,
|
||||
}, {
|
||||
f: sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
|
||||
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
|
||||
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0xdcd5f41895e0992f1e5eb5fab7e78c0bc2a99efb667b2145be2e75a07a2d8cee","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0x12d5def71baa80ab362473517837a854b814e4c5205a28e9139cd7feb5602a4c","logIndex":"0x0","removed":false}]`,
|
||||
}, {
|
||||
f: sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil),
|
||||
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false}]`,
|
||||
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0xdcd5f41895e0992f1e5eb5fab7e78c0bc2a99efb667b2145be2e75a07a2d8cee","logIndex":"0x0","removed":false}]`,
|
||||
}, {
|
||||
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil),
|
||||
}, {
|
||||
|
|
@ -329,10 +329,10 @@ func TestFilters(t *testing.T) {
|
|||
err: "safe header not found",
|
||||
}, {
|
||||
f: sys.NewRangeFilter(int64(rpc.PendingBlockNumber), int64(rpc.PendingBlockNumber), nil, nil),
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696335"],"data":"0x","blockNumber":"0x3e9","transactionHash":"0x4110587c1b8d86edc85dce929a34127f1cb8809515a9f177c91c866de3eb0638","transactionIndex":"0x0","blockHash":"0xc7245899e5817f16fa99cf5ad2d9c1e4b98443a565a673ec9c764640443ef037","logIndex":"0x0","removed":false}]`,
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696335"],"data":"0x","blockNumber":"0x3e9","transactionHash":"0x4110587c1b8d86edc85dce929a34127f1cb8809515a9f177c91c866de3eb0638","transactionIndex":"0x0","blockHash":"0xfaf12ee5c179618e694f8d3f45e0d962e1e64a45a57627792a22379a49270c08","logIndex":"0x0","removed":false}]`,
|
||||
}, {
|
||||
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.PendingBlockNumber), nil, nil),
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696335"],"data":"0x","blockNumber":"0x3e9","transactionHash":"0x4110587c1b8d86edc85dce929a34127f1cb8809515a9f177c91c866de3eb0638","transactionIndex":"0x0","blockHash":"0xc7245899e5817f16fa99cf5ad2d9c1e4b98443a565a673ec9c764640443ef037","logIndex":"0x0","removed":false}]`,
|
||||
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0x12d5def71baa80ab362473517837a854b814e4c5205a28e9139cd7feb5602a4c","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696335"],"data":"0x","blockNumber":"0x3e9","transactionHash":"0x4110587c1b8d86edc85dce929a34127f1cb8809515a9f177c91c866de3eb0638","transactionIndex":"0x0","blockHash":"0xfaf12ee5c179618e694f8d3f45e0d962e1e64a45a57627792a22379a49270c08","logIndex":"0x0","removed":false}]`,
|
||||
}, {
|
||||
f: sys.NewRangeFilter(int64(rpc.PendingBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
|
||||
err: "invalid block range",
|
||||
|
|
|
|||
3
go.mod
3
go.mod
|
|
@ -42,7 +42,7 @@ require (
|
|||
github.com/holiman/bloomfilter/v2 v2.0.3
|
||||
github.com/holiman/uint256 v1.2.3
|
||||
github.com/huin/goupnp v1.3.0
|
||||
github.com/iden3/go-iden3-crypto v0.0.15
|
||||
github.com/iden3/go-iden3-crypto v0.0.12
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.4.0
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
|
||||
github.com/jackpal/go-nat-pmp v1.0.2
|
||||
|
|
@ -57,6 +57,7 @@ require (
|
|||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
|
||||
github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7
|
||||
github.com/rs/cors v1.7.0
|
||||
github.com/scroll-tech/zktrie v0.6.0
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
|
||||
github.com/status-im/keycard-go v0.2.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
|
|
|
|||
10
go.sum
10
go.sum
|
|
@ -154,6 +154,7 @@ github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/dchest/blake512 v1.0.0/go.mod h1:FV1x7xPPLWukZlpDpWQ88rF/SFwZ5qbskrzhLMB92JI=
|
||||
github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI=
|
||||
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
|
||||
|
|
@ -351,8 +352,8 @@ github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFck
|
|||
github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
github.com/iden3/go-iden3-crypto v0.0.15 h1:4MJYlrot1l31Fzlo2sF56u7EVFeHHJkxGXXZCtESgK4=
|
||||
github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E=
|
||||
github.com/iden3/go-iden3-crypto v0.0.12 h1:dXZF+R9iI07DK49LHX/EKC3jTa0O2z+TUyvxjGK7V38=
|
||||
github.com/iden3/go-iden3-crypto v0.0.12/go.mod h1:swXIv0HFbJKobbQBtsB50G7IHr6PbTowutSew/iBEoo=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k=
|
||||
|
|
@ -542,6 +543,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
|
|||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||
github.com/scroll-tech/zktrie v0.6.0 h1:xLrMAO31Yo2BiPg1jtYKzcjpEFnXy8acbB7iIsyshPs=
|
||||
github.com/scroll-tech/zktrie v0.6.0/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
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=
|
||||
|
|
@ -621,6 +624,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
|
@ -696,6 +700,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
|
|
@ -774,6 +779,7 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
|
||||
"hash": "0xcc3b85e906fca96a8337bd17a80a0ae26519c77b2a1e47c59911aebc02a801db",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x1",
|
||||
"parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"parentHash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
|
||||
"stateRoot": "0x1cdafb4045e1cf2b93b5eea1b2425998a9be80c4719265dba102c50bbcb6ece4",
|
||||
"timestamp": "0xa",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x0",
|
||||
"hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"hash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x200",
|
||||
"stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
|
||||
"stateRoot": "0x346d06cdc42cc55d09a235e919de127dde75ba911f8b5d31130935e462e296f0",
|
||||
"timestamp": "0x0",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [],
|
||||
|
|
|
|||
|
|
@ -4,22 +4,22 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"hash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x9",
|
||||
"parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
|
||||
"parentHash": "0x092f6de1438bfcdd6718afa64cf12a13b658cb8a332b730855e3da2885aee687",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
|
||||
"stateRoot": "0x1abc7ad1941fa76fd04eaed1a0658384df02b47feb0561890645296e6579bec8",
|
||||
"timestamp": "0x5a",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
{
|
||||
"blockHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"blockHash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
|
||||
"blockNumber": "0x9",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gas": "0x5208",
|
||||
|
|
@ -33,7 +33,8 @@
|
|||
"type": "0x0",
|
||||
"v": "0x1b",
|
||||
"r": "0xc6028b8e983d62fa8542f8a7633fb23cc941be2c897134352d95a7d9b19feafd",
|
||||
"s": "0xeb6adcaaae3bed489c6cce4435f9db05d23a52820c78bd350e31eec65ed809d"
|
||||
"s": "0xeb6adcaaae3bed489c6cce4435f9db05d23a52820c78bd350e31eec65ed809d",
|
||||
"sender": "0x0000000000000000000000000000000000000000"
|
||||
}
|
||||
],
|
||||
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5",
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
|
||||
"hash": "0x58e8791382f392175cea88e173190ea86e36367acf0f95eb56d2ceb1f650b030",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0xa",
|
||||
"parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"parentHash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
|
||||
"stateRoot": "0xe7adf5abb2eb6302c2af9b8bd1c309b1d28eda30d2b7ba01b151e48b88bd2163",
|
||||
"timestamp": "0x64",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x0",
|
||||
"hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"hash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x200",
|
||||
"stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
|
||||
"stateRoot": "0x346d06cdc42cc55d09a235e919de127dde75ba911f8b5d31130935e462e296f0",
|
||||
"timestamp": "0x0",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [],
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
|
||||
"hash": "0xcc3b85e906fca96a8337bd17a80a0ae26519c77b2a1e47c59911aebc02a801db",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x1",
|
||||
"parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"parentHash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
|
||||
"stateRoot": "0x1cdafb4045e1cf2b93b5eea1b2425998a9be80c4719265dba102c50bbcb6ece4",
|
||||
"timestamp": "0xa",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
|
|
|
|||
|
|
@ -4,22 +4,22 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"hash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x9",
|
||||
"parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
|
||||
"parentHash": "0x092f6de1438bfcdd6718afa64cf12a13b658cb8a332b730855e3da2885aee687",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
|
||||
"stateRoot": "0x1abc7ad1941fa76fd04eaed1a0658384df02b47feb0561890645296e6579bec8",
|
||||
"timestamp": "0x5a",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
{
|
||||
"blockHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"blockHash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
|
||||
"blockNumber": "0x9",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gas": "0x5208",
|
||||
|
|
@ -33,7 +33,8 @@
|
|||
"type": "0x0",
|
||||
"v": "0x1b",
|
||||
"r": "0xc6028b8e983d62fa8542f8a7633fb23cc941be2c897134352d95a7d9b19feafd",
|
||||
"s": "0xeb6adcaaae3bed489c6cce4435f9db05d23a52820c78bd350e31eec65ed809d"
|
||||
"s": "0xeb6adcaaae3bed489c6cce4435f9db05d23a52820c78bd350e31eec65ed809d",
|
||||
"sender": "0x0000000000000000000000000000000000000000"
|
||||
}
|
||||
],
|
||||
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5",
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
|
||||
"hash": "0x58e8791382f392175cea88e173190ea86e36367acf0f95eb56d2ceb1f650b030",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0xa",
|
||||
"parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"parentHash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"size": "0x26a",
|
||||
"stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
|
||||
"stateRoot": "0xe7adf5abb2eb6302c2af9b8bd1c309b1d28eda30d2b7ba01b151e48b88bd2163",
|
||||
"timestamp": "0x64",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactions": [
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@
|
|||
"chainId": "0x7fffffffffffffee",
|
||||
"v": "0x0",
|
||||
"r": "0x0",
|
||||
"s": "0x0"
|
||||
"s": "0x0",
|
||||
"sender": "0x0000000000000000000000000000000000000000"
|
||||
}
|
||||
],
|
||||
"transactionsRoot": "0x98d9f6dd0aa479c0fb448f2627e9f1964aca699fccab8f6e95861547a4699e37",
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
{
|
||||
"blobGasPrice": "0x1",
|
||||
"blobGasUsed": "0x20000",
|
||||
"blockHash": "0xe724dfd4349861f4dceef2bc4df086d0a3d88858214f6bee9fcf1bebd1edc2a6",
|
||||
"blockHash": "0xb39a867bef2962b3e7af21d9a8a8c018c1de5c27be56341261857e24b96b263e",
|
||||
"blockNumber": "0x6",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"effectiveGasPrice": "0x1b09d63b",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5208",
|
||||
"l1Fee": "0x0",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
[
|
||||
{
|
||||
"blockHash": "0x1e7dcf3abe8bf05d32367a5dc387caa32578b15871bf8b3cbeedf2d8d530f844",
|
||||
"blockHash": "0x7afe7ea18e15ea1bf0b10ae924e42a8e01023c4b1f3c8058c45723a1e56b8868",
|
||||
"blockNumber": "0x2",
|
||||
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
|
||||
"cumulativeGasUsed": "0xcf50",
|
||||
"effectiveGasPrice": "0x2db16291",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0xcf50",
|
||||
"l1Fee": "0x0",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
[
|
||||
{
|
||||
"blockHash": "0xffa737e6ce9a9162ffd411dd06169114b3ed5ee9fc1474a2625c92548e4455e0",
|
||||
"blockHash": "0xb2d0c9e52757eee01da7c6a6da6de23a4548c036c97f4f113c5e05d9daf17e1a",
|
||||
"blockNumber": "0x4",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x538d",
|
||||
"effectiveGasPrice": "0x2325c42f",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x538d",
|
||||
"l1Fee": "0x0",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x0",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
[
|
||||
{
|
||||
"blockHash": "0x173dcd9d22ce71929cd17e84ea88702a0f84d6244c6898d2a4f48722e494fe9c",
|
||||
"blockHash": "0xd62e01634cdf11c2b2a854453deccbc7b5b4f6df1c717b932ade30f47db68512",
|
||||
"blockNumber": "0x3",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5e28",
|
||||
"effectiveGasPrice": "0x281c2585",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5e28",
|
||||
"l1Fee": "0x0",
|
||||
"logs": [
|
||||
{
|
||||
"address": "0x0000000000000000000000000000000000031ec7",
|
||||
|
|
@ -19,7 +20,7 @@
|
|||
"blockNumber": "0x3",
|
||||
"transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287",
|
||||
"transactionIndex": "0x0",
|
||||
"blockHash": "0x173dcd9d22ce71929cd17e84ea88702a0f84d6244c6898d2a4f48722e494fe9c",
|
||||
"blockHash": "0xd62e01634cdf11c2b2a854453deccbc7b5b4f6df1c717b932ade30f47db68512",
|
||||
"logIndex": "0x0",
|
||||
"removed": false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
[
|
||||
{
|
||||
"blockHash": "0xa8a067b3cb3b9ddc6cfb8317bfd08b266fcf9994fc870c1f7ed394acecfadf39",
|
||||
"blockHash": "0x34044cfcfe129574966dc82cee6ec504c869d50ec2bd4a16eb330b04a277bcab",
|
||||
"blockNumber": "0x1",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"effectiveGasPrice": "0x342770c0",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5208",
|
||||
"l1Fee": "0x0",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
{
|
||||
"blobGasPrice": "0x1",
|
||||
"blobGasUsed": "0x20000",
|
||||
"blockHash": "0xe724dfd4349861f4dceef2bc4df086d0a3d88858214f6bee9fcf1bebd1edc2a6",
|
||||
"blockHash": "0xb39a867bef2962b3e7af21d9a8a8c018c1de5c27be56341261857e24b96b263e",
|
||||
"blockNumber": "0x6",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"effectiveGasPrice": "0x1b09d63b",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5208",
|
||||
"l1Fee": "0x0",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x0",
|
||||
"hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"hash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
|
||||
"stateRoot": "0x346d06cdc42cc55d09a235e919de127dde75ba911f8b5d31130935e462e296f0",
|
||||
"timestamp": "0x0",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
|
||||
"hash": "0xcc3b85e906fca96a8337bd17a80a0ae26519c77b2a1e47c59911aebc02a801db",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x1",
|
||||
"parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"parentHash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
|
||||
"stateRoot": "0x1cdafb4045e1cf2b93b5eea1b2425998a9be80c4719265dba102c50bbcb6ece4",
|
||||
"timestamp": "0xa",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0xca0ebcce920d2cdfbf9e1dbe90ed3441a1a576f344bd80e60508da814916f4e7"
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"hash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x9",
|
||||
"parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
|
||||
"parentHash": "0x092f6de1438bfcdd6718afa64cf12a13b658cb8a332b730855e3da2885aee687",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
|
||||
"stateRoot": "0x1abc7ad1941fa76fd04eaed1a0658384df02b47feb0561890645296e6579bec8",
|
||||
"timestamp": "0x5a",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5"
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
|
||||
"hash": "0x58e8791382f392175cea88e173190ea86e36367acf0f95eb56d2ceb1f650b030",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0xa",
|
||||
"parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"parentHash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
|
||||
"stateRoot": "0xe7adf5abb2eb6302c2af9b8bd1c309b1d28eda30d2b7ba01b151e48b88bd2163",
|
||||
"timestamp": "0x64",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0xb0893d21a4a44dc26a962a6e91abae66df87fb61ac9c60e936aee89c76331445"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x0",
|
||||
"hash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"hash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
|
||||
"stateRoot": "0x346d06cdc42cc55d09a235e919de127dde75ba911f8b5d31130935e462e296f0",
|
||||
"timestamp": "0x0",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x0da274b315de8e4d5bf8717218ec43540464ef36378cb896469bb731e1d3f3cb",
|
||||
"hash": "0xcc3b85e906fca96a8337bd17a80a0ae26519c77b2a1e47c59911aebc02a801db",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x1",
|
||||
"parentHash": "0xbdc7d83b8f876938810462fe8d053263a482e44201e3883d4ae204ff4de7eff5",
|
||||
"parentHash": "0x6df968f862b75ade64e8dbdd7b28f035580feef62939fb9cbc7cc24e821246c5",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0x92c5c55a698963f5b06e3aee415630f5c48b0760e537af94917ce9c4f42a2e22",
|
||||
"stateRoot": "0x1cdafb4045e1cf2b93b5eea1b2425998a9be80c4719265dba102c50bbcb6ece4",
|
||||
"timestamp": "0xa",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0xca0ebcce920d2cdfbf9e1dbe90ed3441a1a576f344bd80e60508da814916f4e7"
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"hash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0x9",
|
||||
"parentHash": "0x5abd19c39d9f1c6e52998e135ea14e1fbc5db3fa2a108f4538e238ca5c2e68d7",
|
||||
"parentHash": "0x092f6de1438bfcdd6718afa64cf12a13b658cb8a332b730855e3da2885aee687",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0xbd4aa2c2873df709151075250a8c01c9a14d2b0e2f715dbdd16e0ef8030c2cf0",
|
||||
"stateRoot": "0x1abc7ad1941fa76fd04eaed1a0658384df02b47feb0561890645296e6579bec8",
|
||||
"timestamp": "0x5a",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0x0767ed8359337dc6a8fdc77fe52db611bed1be87aac73c4556b1bf1dd3d190a5"
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@
|
|||
"extraData": "0x",
|
||||
"gasLimit": "0x47e7c4",
|
||||
"gasUsed": "0x5208",
|
||||
"hash": "0x97f540a3577c0f645c5dada5da86f38350e8f847e71f21124f917835003e2607",
|
||||
"hash": "0x58e8791382f392175cea88e173190ea86e36367acf0f95eb56d2ceb1f650b030",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "0xa",
|
||||
"parentHash": "0xda97ed946e0d502fb898b0ac881bd44da3c7fee5eaf184431e1ec3d361dad17e",
|
||||
"parentHash": "0x8497ace989616d5495ab931f644ea6990dddf7148999bde6bb2ab4b4c7240320",
|
||||
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
|
||||
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"stateRoot": "0xbb62872e4023fa8a8b17b9cc37031f4817d9595779748d01cba408b495707a91",
|
||||
"stateRoot": "0xe7adf5abb2eb6302c2af9b8bd1c309b1d28eda30d2b7ba01b151e48b88bd2163",
|
||||
"timestamp": "0x64",
|
||||
"totalDifficulty": "0x1",
|
||||
"transactionsRoot": "0xb0893d21a4a44dc26a962a6e91abae66df87fb61ac9c60e936aee89c76331445"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
{
|
||||
"blobGasPrice": "0x1",
|
||||
"blobGasUsed": "0x20000",
|
||||
"blockHash": "0xe724dfd4349861f4dceef2bc4df086d0a3d88858214f6bee9fcf1bebd1edc2a6",
|
||||
"blockHash": "0xb39a867bef2962b3e7af21d9a8a8c018c1de5c27be56341261857e24b96b263e",
|
||||
"blockNumber": "0x6",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"effectiveGasPrice": "0x1b09d63b",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5208",
|
||||
"l1Fee": "0x0",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
{
|
||||
"blockHash": "0x1e7dcf3abe8bf05d32367a5dc387caa32578b15871bf8b3cbeedf2d8d530f844",
|
||||
"blockHash": "0x7afe7ea18e15ea1bf0b10ae924e42a8e01023c4b1f3c8058c45723a1e56b8868",
|
||||
"blockNumber": "0x2",
|
||||
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
|
||||
"cumulativeGasUsed": "0xcf50",
|
||||
"effectiveGasPrice": "0x2db16291",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0xcf50",
|
||||
"l1Fee": "0x0",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
{
|
||||
"blockHash": "0x3fadc5bc916018a326732be829a2565b3acb960a8406f0f151a5e1fa971ea7dd",
|
||||
"blockHash": "0xe1081b33b1e59d80de12700f8c123589b06d5629762a27d31919315e25265c9c",
|
||||
"blockNumber": "0x5",
|
||||
"contractAddress": "0xfdaa97661a584d977b4d3abb5370766ff5b86a18",
|
||||
"cumulativeGasUsed": "0xe01c",
|
||||
"effectiveGasPrice": "0x1ecb3fb4",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0xe01c",
|
||||
"l1Fee": "0x0",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
{
|
||||
"blockHash": "0xffa737e6ce9a9162ffd411dd06169114b3ed5ee9fc1474a2625c92548e4455e0",
|
||||
"blockHash": "0xb2d0c9e52757eee01da7c6a6da6de23a4548c036c97f4f113c5e05d9daf17e1a",
|
||||
"blockNumber": "0x4",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x538d",
|
||||
"effectiveGasPrice": "0x2325c42f",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x538d",
|
||||
"l1Fee": "0x0",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x0",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
{
|
||||
"blockHash": "0xa8a067b3cb3b9ddc6cfb8317bfd08b266fcf9994fc870c1f7ed394acecfadf39",
|
||||
"blockHash": "0x34044cfcfe129574966dc82cee6ec504c869d50ec2bd4a16eb330b04a277bcab",
|
||||
"blockNumber": "0x1",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"effectiveGasPrice": "0x342770c0",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5208",
|
||||
"l1Fee": "0x0",
|
||||
"logs": [],
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"status": "0x1",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
{
|
||||
"blockHash": "0x173dcd9d22ce71929cd17e84ea88702a0f84d6244c6898d2a4f48722e494fe9c",
|
||||
"blockHash": "0xd62e01634cdf11c2b2a854453deccbc7b5b4f6df1c717b932ade30f47db68512",
|
||||
"blockNumber": "0x3",
|
||||
"contractAddress": null,
|
||||
"cumulativeGasUsed": "0x5e28",
|
||||
"effectiveGasPrice": "0x281c2585",
|
||||
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
|
||||
"gasUsed": "0x5e28",
|
||||
"l1Fee": "0x0",
|
||||
"logs": [
|
||||
{
|
||||
"address": "0x0000000000000000000000000000000000031ec7",
|
||||
|
|
@ -18,7 +19,7 @@
|
|||
"blockNumber": "0x3",
|
||||
"transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287",
|
||||
"transactionIndex": "0x0",
|
||||
"blockHash": "0x173dcd9d22ce71929cd17e84ea88702a0f84d6244c6898d2a4f48722e494fe9c",
|
||||
"blockHash": "0xd62e01634cdf11c2b2a854453deccbc7b5b4f6df1c717b932ade30f47db68512",
|
||||
"logIndex": "0x0",
|
||||
"removed": false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package les
|
|||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/mclock"
|
||||
|
|
@ -82,6 +83,11 @@ func NewLesServer(node *node.Node, e ethBackend, config *ethconfig.Config) (*Les
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Currently disable for zktrie
|
||||
if e.BlockChain().Config().Scroll.ZktrieEnabled() {
|
||||
return nil, errors.New("light server not work with zktrie storage")
|
||||
}
|
||||
|
||||
// Calculate the number of threads used to service the light client
|
||||
// requests based on the user-specified value.
|
||||
threads := config.LightServ * 4 / 100
|
||||
|
|
|
|||
|
|
@ -339,6 +339,9 @@ type ChainConfig struct {
|
|||
}
|
||||
|
||||
type ScrollConfig struct {
|
||||
// Use zktrie [optional]
|
||||
UseZktrie bool `json:"useZktrie,omitempty"`
|
||||
|
||||
// Maximum number of transactions per block [optional]
|
||||
MaxTxPerBlock *int `json:"maxTxPerBlock,omitempty"`
|
||||
|
||||
|
|
@ -373,6 +376,10 @@ func (s ScrollConfig) FeeVaultEnabled() bool {
|
|||
return s.FeeVaultAddress != nil
|
||||
}
|
||||
|
||||
func (s ScrollConfig) ZktrieEnabled() bool {
|
||||
return s.UseZktrie
|
||||
}
|
||||
|
||||
func (s ScrollConfig) ShouldIncludeL1Messages() bool {
|
||||
return s.L1Config != nil && s.L1Config.NumL1MessagesPerBlock > 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package trie
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
|
|
@ -33,6 +34,9 @@ type Config struct {
|
|||
Preimages bool // Flag whether the preimage of node key is recorded
|
||||
HashDB *hashdb.Config // Configs for hash-based scheme
|
||||
PathDB *pathdb.Config // Configs for experimental path-based scheme
|
||||
|
||||
// zktrie related stuff
|
||||
IsUsingZktrie bool
|
||||
}
|
||||
|
||||
// HashDefaults represents a config for using hash-based scheme with
|
||||
|
|
@ -42,6 +46,19 @@ var HashDefaults = &Config{
|
|||
HashDB: hashdb.Defaults,
|
||||
}
|
||||
|
||||
// HashDefaultsWithZktrie represents a config based on HashDefaults but with zktrie enabled.
|
||||
var HashDefaultsWithZktrie = &Config{
|
||||
Preimages: false,
|
||||
HashDB: hashdb.Defaults,
|
||||
IsUsingZktrie: true,
|
||||
}
|
||||
|
||||
// HashDefaultsWithPreimages represents a config based on HashDefaults but with Preimages enabled.
|
||||
var HashDefaultsWithPreimages = &Config{
|
||||
Preimages: true,
|
||||
HashDB: hashdb.Defaults,
|
||||
}
|
||||
|
||||
// backend defines the methods needed to access/update trie nodes in different
|
||||
// state scheme.
|
||||
type backend interface {
|
||||
|
|
@ -73,6 +90,9 @@ type backend interface {
|
|||
|
||||
// Close closes the trie database backend and releases all held resources.
|
||||
Close() error
|
||||
|
||||
// database supplementary methods, to get the underlying fields
|
||||
GetLock() *sync.RWMutex
|
||||
}
|
||||
|
||||
// Database is the wrapper of the underlying backend which is shared by different
|
||||
|
|
@ -83,6 +103,10 @@ type Database struct {
|
|||
diskdb ethdb.Database // Persistent database to store the snapshot
|
||||
preimages *preimageStore // The store for caching preimages
|
||||
backend backend // The backend for managing trie nodes
|
||||
|
||||
// zktrie related stuff
|
||||
// TODO: It's a quick&dirty implementation. FIXME later.
|
||||
rawDirties KvMap
|
||||
}
|
||||
|
||||
// NewDatabase initializes the trie database with default settings, note
|
||||
|
|
@ -100,6 +124,8 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database {
|
|||
config: config,
|
||||
diskdb: diskdb,
|
||||
preimages: preimages,
|
||||
// scroll-related
|
||||
rawDirties: make(KvMap),
|
||||
}
|
||||
if config.HashDB != nil && config.PathDB != nil {
|
||||
log.Crit("Both 'hash' and 'path' mode are configured")
|
||||
|
|
@ -112,6 +138,19 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database {
|
|||
return db
|
||||
}
|
||||
|
||||
func (db *Database) IsUsingZktrie() bool {
|
||||
// compatible logic for light mode
|
||||
if db == nil || db.config == nil {
|
||||
return false
|
||||
}
|
||||
return db.config.IsUsingZktrie
|
||||
}
|
||||
|
||||
func (db *Database) SetIsUsingZktrie(isUsingZktrie bool) {
|
||||
// config must not be nil
|
||||
db.config.IsUsingZktrie = isUsingZktrie
|
||||
}
|
||||
|
||||
// Reader returns a reader for accessing all trie nodes with provided state root.
|
||||
// An error will be returned if the requested state is not available.
|
||||
func (db *Database) Reader(blockRoot common.Hash) (Reader, error) {
|
||||
|
|
@ -142,6 +181,25 @@ func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, n
|
|||
// to disk. As a side effect, all pre-images accumulated up to this point are
|
||||
// also written.
|
||||
func (db *Database) Commit(root common.Hash, report bool) error {
|
||||
batch := db.diskdb.NewBatch()
|
||||
|
||||
db.GetLock().Lock()
|
||||
for _, v := range db.rawDirties {
|
||||
batch.Put(v.K, v.V)
|
||||
}
|
||||
for k := range db.rawDirties {
|
||||
delete(db.rawDirties, k)
|
||||
}
|
||||
db.GetLock().Unlock()
|
||||
if err := batch.Write(); err != nil {
|
||||
return err
|
||||
}
|
||||
batch.Reset()
|
||||
|
||||
if (root == common.Hash{}) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if db.preimages != nil {
|
||||
db.preimages.commit(true)
|
||||
}
|
||||
|
|
|
|||
32
trie/database_supplement.go
Normal file
32
trie/database_supplement.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package trie
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/VictoriaMetrics/fastcache"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
||||
)
|
||||
|
||||
func (db *Database) GetLock() *sync.RWMutex {
|
||||
return db.backend.GetLock()
|
||||
}
|
||||
|
||||
func (db *Database) GetCleans() *fastcache.Cache {
|
||||
hdb, ok := db.backend.(*hashdb.Database)
|
||||
if !ok {
|
||||
panic("only hashdb supported")
|
||||
}
|
||||
return hdb.GetCleans()
|
||||
}
|
||||
|
||||
// EmptyRoot indicate what root is for an empty trie, it depends on its underlying implement (zktrie or common trie)
|
||||
func (db *Database) EmptyRoot() common.Hash {
|
||||
if db.IsUsingZktrie() {
|
||||
return common.Hash{}
|
||||
} else {
|
||||
return types.EmptyRootHash
|
||||
}
|
||||
}
|
||||
47
trie/database_types.go
Normal file
47
trie/database_types.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package trie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ErrNotFound is used by the implementations of the interface db.Storage for
|
||||
// when a key is not found in the storage
|
||||
var ErrNotFound = errors.New("key not found")
|
||||
|
||||
// KV contains a key (K) and a value (V)
|
||||
type KV struct {
|
||||
K []byte
|
||||
V []byte
|
||||
}
|
||||
|
||||
// KvMap is a key-value map between a sha256 byte array hash, and a KV struct
|
||||
type KvMap map[[sha256.Size]byte]KV
|
||||
|
||||
// Get retreives the value respective to a key from the KvMap
|
||||
func (m KvMap) Get(k []byte) ([]byte, bool) {
|
||||
v, ok := m[sha256.Sum256(k)]
|
||||
return v.V, ok
|
||||
}
|
||||
|
||||
// Put stores a key and a value in the KvMap
|
||||
func (m KvMap) Put(k, v []byte) {
|
||||
m[sha256.Sum256(k)] = KV{k, v}
|
||||
}
|
||||
|
||||
// Concat concatenates arrays of bytes
|
||||
func Concat(vs ...[]byte) []byte {
|
||||
var b bytes.Buffer
|
||||
for _, v := range vs {
|
||||
b.Write(v)
|
||||
}
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
// Clone clones a byte array into a new byte array
|
||||
func Clone(b0 []byte) []byte {
|
||||
b1 := make([]byte, len(b0))
|
||||
copy(b1, b0)
|
||||
return b1
|
||||
}
|
||||
|
|
@ -115,6 +115,11 @@ func (t *StateTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
|
|||
// key in a trie with the given root hash. VerifyProof returns an error if the
|
||||
// proof contains invalid trie nodes or the wrong value.
|
||||
func VerifyProof(rootHash common.Hash, key []byte, proofDb ethdb.KeyValueReader) (value []byte, err error) {
|
||||
// test the type of proof (for trie or SMT)
|
||||
if buf, _ := proofDb.Get(magicHash); buf != nil {
|
||||
return VerifyProofSMT(rootHash, key, proofDb)
|
||||
}
|
||||
|
||||
key = keybytesToHex(key)
|
||||
wantHash := rootHash
|
||||
for i := 0; ; i++ {
|
||||
|
|
|
|||
|
|
@ -708,15 +708,15 @@ func TestTinyTrie(t *testing.T) {
|
|||
_, accounts := makeAccounts(5)
|
||||
trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
|
||||
trie.MustUpdate(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001337"), accounts[3])
|
||||
if exp, root := common.HexToHash("8c6a85a4d9fda98feff88450299e574e5378e32391f75a055d470ac0653f1005"), trie.Hash(); exp != root {
|
||||
if exp, root := common.HexToHash("fc516c51c03bf9f1a0eec6ed6f6f5da743c2745dcd5670007519e6ec056f95a8"), trie.Hash(); exp != root {
|
||||
t.Errorf("1: got %x, exp %x", root, exp)
|
||||
}
|
||||
trie.MustUpdate(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001338"), accounts[4])
|
||||
if exp, root := common.HexToHash("ec63b967e98a5720e7f720482151963982890d82c9093c0d486b7eb8883a66b1"), trie.Hash(); exp != root {
|
||||
if exp, root := common.HexToHash("5070d3f144546fd13589ad90cd153954643fa4ca6c1a5f08683cbfbbf76e960c"), trie.Hash(); exp != root {
|
||||
t.Errorf("2: got %x, exp %x", root, exp)
|
||||
}
|
||||
trie.MustUpdate(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001339"), accounts[4])
|
||||
if exp, root := common.HexToHash("0608c1d1dc3905fa22204c7a0e43644831c3b6d3def0f274be623a948197e64a"), trie.Hash(); exp != root {
|
||||
if exp, root := common.HexToHash("aa3fba77e50f6e931d8aacde70912be5bff04c7862f518ae06f3418dd4d37be3"), trie.Hash(); exp != root {
|
||||
t.Errorf("3: got %x, exp %x", root, exp)
|
||||
}
|
||||
checktr := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
|
||||
|
|
@ -740,7 +740,7 @@ func TestCommitAfterHash(t *testing.T) {
|
|||
trie.Hash()
|
||||
trie.Commit(false)
|
||||
root := trie.Hash()
|
||||
exp := common.HexToHash("72f9d3f3fe1e1dd7b8936442e7642aef76371472d94319900790053c493f3fe6")
|
||||
exp := common.HexToHash("f0c0681648c93b347479cd58c61995557f01294425bd031ce1943c2799bbd4ec")
|
||||
if exp != root {
|
||||
t.Errorf("got %x, exp %x", root, exp)
|
||||
}
|
||||
|
|
@ -847,9 +847,9 @@ func TestCommitSequence(t *testing.T) {
|
|||
count int
|
||||
expWriteSeqHash []byte
|
||||
}{
|
||||
{20, common.FromHex("873c78df73d60e59d4a2bcf3716e8bfe14554549fea2fc147cb54129382a8066")},
|
||||
{200, common.FromHex("ba03d891bb15408c940eea5ee3d54d419595102648d02774a0268d892add9c8e")},
|
||||
{2000, common.FromHex("f7a184f20df01c94f09537401d11e68d97ad0c00115233107f51b9c287ce60c7")},
|
||||
{20, common.FromHex("7b908cce3bc16abb3eac5dff6c136856526f15225f74ce860a2bec47912a5492")},
|
||||
{200, common.FromHex("55791f6ec2f83fee512a2d3d4b505784fdefaea89974e10440d01d62a18a298a")},
|
||||
{2000, common.FromHex("ccb464abf67804538908c62431b3a6788e8dc6dee62aff9bfe6b10136acfceac")},
|
||||
} {
|
||||
addresses, accounts := makeAccounts(tc.count)
|
||||
// This spongeDb is used to check the sequence of disk-db-writes
|
||||
|
|
|
|||
|
|
@ -36,10 +36,10 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
memcacheCleanHitMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/hit", nil)
|
||||
memcacheCleanMissMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/miss", nil)
|
||||
memcacheCleanReadMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/read", nil)
|
||||
memcacheCleanWriteMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/write", nil)
|
||||
MemcacheCleanHitMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/hit", nil)
|
||||
MemcacheCleanMissMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/miss", nil)
|
||||
MemcacheCleanReadMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/read", nil)
|
||||
MemcacheCleanWriteMeter = metrics.NewRegisteredMeter("hashdb/memcache/clean/write", nil)
|
||||
|
||||
memcacheDirtyHitMeter = metrics.NewRegisteredMeter("hashdb/memcache/dirty/hit", nil)
|
||||
memcacheDirtyMissMeter = metrics.NewRegisteredMeter("hashdb/memcache/dirty/miss", nil)
|
||||
|
|
@ -193,8 +193,8 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
|
|||
// Retrieve the node from the clean cache if available
|
||||
if db.cleans != nil {
|
||||
if enc := db.cleans.Get(nil, hash[:]); enc != nil {
|
||||
memcacheCleanHitMeter.Mark(1)
|
||||
memcacheCleanReadMeter.Mark(int64(len(enc)))
|
||||
MemcacheCleanHitMeter.Mark(1)
|
||||
MemcacheCleanReadMeter.Mark(int64(len(enc)))
|
||||
return enc, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -215,8 +215,8 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
|
|||
if len(enc) != 0 {
|
||||
if db.cleans != nil {
|
||||
db.cleans.Set(hash[:], enc)
|
||||
memcacheCleanMissMeter.Mark(1)
|
||||
memcacheCleanWriteMeter.Mark(int64(len(enc)))
|
||||
MemcacheCleanMissMeter.Mark(1)
|
||||
MemcacheCleanWriteMeter.Mark(int64(len(enc)))
|
||||
}
|
||||
return enc, nil
|
||||
}
|
||||
|
|
@ -554,7 +554,7 @@ func (c *cleaner) Put(key []byte, rlp []byte) error {
|
|||
// Move the flushed node into the clean cache to prevent insta-reloads
|
||||
if c.db.cleans != nil {
|
||||
c.db.cleans.Set(hash[:], rlp)
|
||||
memcacheCleanWriteMeter.Mark(int64(len(rlp)))
|
||||
MemcacheCleanWriteMeter.Mark(int64(len(rlp)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
15
trie/triedb/hashdb/database_supplement.go
Normal file
15
trie/triedb/hashdb/database_supplement.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package hashdb
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/VictoriaMetrics/fastcache"
|
||||
)
|
||||
|
||||
func (db *Database) GetLock() *sync.RWMutex {
|
||||
return &db.lock
|
||||
}
|
||||
|
||||
func (db *Database) GetCleans() *fastcache.Cache {
|
||||
return db.cleans
|
||||
}
|
||||
9
trie/triedb/pathdb/database_supplement.go
Normal file
9
trie/triedb/pathdb/database_supplement.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package pathdb
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
func (db *Database) GetLock() *sync.RWMutex {
|
||||
return &db.lock
|
||||
}
|
||||
287
trie/zk_trie.go
Normal file
287
trie/zk_trie.go
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package trie
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
zktrie "github.com/scroll-tech/zktrie/trie"
|
||||
zkt "github.com/scroll-tech/zktrie/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto/poseidon"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
)
|
||||
|
||||
var magicHash []byte = []byte("THIS IS THE MAGIC INDEX FOR ZKTRIE")
|
||||
|
||||
// wrap zktrie for trie interface
|
||||
type ZkTrie struct {
|
||||
*zktrie.ZkTrie
|
||||
db *ZktrieDatabase
|
||||
}
|
||||
|
||||
func init() {
|
||||
zkt.InitHashScheme(poseidon.HashFixedWithDomain)
|
||||
}
|
||||
|
||||
func sanityCheckByte32Key(b []byte) {
|
||||
if len(b) != 32 && len(b) != 20 {
|
||||
panic(fmt.Errorf("do not support length except for 120bit and 256bit now. data: %v len: %v", b, len(b)))
|
||||
}
|
||||
}
|
||||
|
||||
// NewZkTrie creates a trie
|
||||
// NewZkTrie bypasses all the buffer mechanism in *Database, it directly uses the
|
||||
// underlying diskdb
|
||||
func NewZkTrie(root common.Hash, db *ZktrieDatabase) (*ZkTrie, error) {
|
||||
tr, err := zktrie.NewZkTrie(*zkt.NewByte32FromBytes(root.Bytes()), db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ZkTrie{tr, db}, nil
|
||||
}
|
||||
|
||||
// Get returns the value for key stored in the trie.
|
||||
// The value bytes must not be modified by the caller.
|
||||
func (t *ZkTrie) Get(key []byte) []byte {
|
||||
sanityCheckByte32Key(key)
|
||||
res, err := t.TryGet(key)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (t *ZkTrie) GetAccount(address common.Address) (*types.StateAccount, error) {
|
||||
key := address.Bytes()
|
||||
sanityCheckByte32Key(key)
|
||||
res, err := t.TryGet(key)
|
||||
if res == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return types.UnmarshalStateAccount(res)
|
||||
}
|
||||
|
||||
func (t *ZkTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) {
|
||||
sanityCheckByte32Key(key)
|
||||
enc, err := t.TryGet(key)
|
||||
if err != nil || len(enc) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
_, content, _, err := rlp.Split(enc)
|
||||
return content, err
|
||||
}
|
||||
|
||||
func (t *ZkTrie) UpdateAccount(address common.Address, acc *types.StateAccount) error {
|
||||
return t.TryUpdateAccount(address.Bytes(), acc)
|
||||
}
|
||||
|
||||
// TryUpdateAccount will abstract the write of an account to the
|
||||
// secure trie.
|
||||
func (t *ZkTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error {
|
||||
sanityCheckByte32Key(key)
|
||||
value, flag := acc.MarshalFields()
|
||||
return t.ZkTrie.TryUpdate(key, flag, value)
|
||||
}
|
||||
|
||||
// Update associates key with value in the trie. Subsequent calls to
|
||||
// Get will return value. If value has length zero, any existing value
|
||||
// is deleted from the trie and calls to Get will return nil.
|
||||
//
|
||||
// The value bytes must not be modified by the caller while they are
|
||||
// stored in the trie.
|
||||
func (t *ZkTrie) Update(key, value []byte) {
|
||||
if err := t.TryUpdate(key, value); err != nil {
|
||||
log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: value is restricted to length of bytes32.
|
||||
// we override the underlying zktrie's TryUpdate method
|
||||
func (t *ZkTrie) TryUpdate(key, value []byte) error {
|
||||
sanityCheckByte32Key(key)
|
||||
return t.ZkTrie.TryUpdate(key, 1, []zkt.Byte32{*zkt.NewByte32FromBytes(value)})
|
||||
}
|
||||
|
||||
func (t *ZkTrie) UpdateContractCode(_ common.Address, _ common.Hash, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ZkTrie) UpdateStorage(_ common.Address, key, value []byte) error {
|
||||
v, _ := rlp.EncodeToBytes(value)
|
||||
return t.TryUpdate(key, v)
|
||||
}
|
||||
|
||||
// Delete removes any existing value for key from the trie.
|
||||
func (t *ZkTrie) Delete(key []byte) {
|
||||
sanityCheckByte32Key(key)
|
||||
if err := t.TryDelete(key); err != nil {
|
||||
log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ZkTrie) DeleteAccount(address common.Address) error {
|
||||
key := address.Bytes()
|
||||
sanityCheckByte32Key(key)
|
||||
return t.TryDelete(key)
|
||||
}
|
||||
|
||||
func (t *ZkTrie) DeleteStorage(_ common.Address, key []byte) error {
|
||||
sanityCheckByte32Key(key)
|
||||
return t.TryDelete(key)
|
||||
}
|
||||
|
||||
// GetKey returns the preimage of a hashed key that was
|
||||
// previously used to store a value.
|
||||
func (t *ZkTrie) GetKey(kHashBytes []byte) []byte {
|
||||
// TODO: use a kv cache in memory
|
||||
k, err := zkt.NewBigIntFromHashBytes(kHashBytes)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
|
||||
}
|
||||
if t.db.db.preimages != nil {
|
||||
return t.db.db.preimages.preimage(common.BytesToHash(k.Bytes()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Commit writes all nodes and the secure hash pre-images to the trie's database.
|
||||
// Nodes are stored with their sha3 hash as the key.
|
||||
//
|
||||
// Committing flushes nodes from memory. Subsequent Get calls will load nodes
|
||||
// from the database.
|
||||
//
|
||||
// func (t *ZkTrie) Commit(LeafCallback) (common.Hash, int, error) {
|
||||
// // in current implmentation, every update of trie already writes into database
|
||||
// // so Commmit does nothing
|
||||
// return t.Hash(), 0, nil
|
||||
// }
|
||||
func (t *ZkTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) {
|
||||
// in current implmentation, every update of trie already writes into database
|
||||
// so Commmit does nothing
|
||||
return t.Hash(), nil, nil
|
||||
}
|
||||
|
||||
// Hash returns the root hash of SecureBinaryTrie. It does not write to the
|
||||
// database and can be used even if the trie doesn't have one.
|
||||
func (t *ZkTrie) Hash() common.Hash {
|
||||
var hash common.Hash
|
||||
hash.SetBytes(t.ZkTrie.Hash())
|
||||
return hash
|
||||
}
|
||||
|
||||
// Copy returns a copy of SecureBinaryTrie.
|
||||
func (t *ZkTrie) Copy() *ZkTrie {
|
||||
return &ZkTrie{t.ZkTrie.Copy(), t.db}
|
||||
}
|
||||
|
||||
// NodeIterator returns an iterator that returns nodes of the underlying trie. Iteration
|
||||
// starts at the key after the given start key.
|
||||
func (t *ZkTrie) NodeIterator(start []byte) (NodeIterator, error) {
|
||||
/// FIXME
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// hashKey returns the hash of key as an ephemeral buffer.
|
||||
// The caller must not hold onto the return value because it will become
|
||||
// invalid on the next call to hashKey or secKey.
|
||||
/*func (t *ZkTrie) hashKey(key []byte) []byte {
|
||||
if len(key) != 32 {
|
||||
panic("non byte32 input to hashKey")
|
||||
}
|
||||
low16 := new(big.Int).SetBytes(key[:16])
|
||||
high16 := new(big.Int).SetBytes(key[16:])
|
||||
hash, err := poseidon.Hash([]*big.Int{low16, high16})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return hash.Bytes()
|
||||
}
|
||||
*/
|
||||
|
||||
// Prove constructs a merkle proof for key. The result contains all encoded nodes
|
||||
// on the path to the value at key. The value itself is also included in the last
|
||||
// node and can be retrieved by verifying the proof.
|
||||
//
|
||||
// If the trie does not contain a value for key, the returned proof contains all
|
||||
// nodes of the longest existing prefix of the key (at least the root node), ending
|
||||
// with the node that proves the absence of the key.
|
||||
// func (t *ZkTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error {
|
||||
func (t *ZkTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
|
||||
fromLevel := uint(0)
|
||||
err := t.ZkTrie.Prove(key, fromLevel, func(n *zktrie.Node) error {
|
||||
nodeHash, err := n.NodeHash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n.Type == zktrie.NodeTypeLeaf_New {
|
||||
preImage := t.GetKey(n.NodeKey.Bytes())
|
||||
if len(preImage) > 0 {
|
||||
n.KeyPreimage = &zkt.Byte32{}
|
||||
copy(n.KeyPreimage[:], preImage)
|
||||
//return fmt.Errorf("key preimage not found for [%x] ref %x", n.NodeKey.Bytes(), k.Bytes())
|
||||
}
|
||||
}
|
||||
return proofDb.Put(nodeHash[:], n.Value())
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// we put this special kv pair in db so we can distinguish the type and
|
||||
// make suitable Proof
|
||||
return proofDb.Put(magicHash, zktrie.ProofMagicBytes())
|
||||
}
|
||||
|
||||
// VerifyProof checks merkle proofs. The given proof must contain the value for
|
||||
// key in a trie with the given root hash. VerifyProof returns an error if the
|
||||
// proof contains invalid trie nodes or the wrong value.
|
||||
func VerifyProofSMT(rootHash common.Hash, key []byte, proofDb ethdb.KeyValueReader) (value []byte, err error) {
|
||||
h := zkt.NewHashFromBytes(rootHash.Bytes())
|
||||
k, err := zkt.ToSecureKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proof, n, err := zktrie.BuildZkTrieProof(h, k, len(key)*8, func(key *zkt.Hash) (*zktrie.Node, error) {
|
||||
buf, _ := proofDb.Get(key[:])
|
||||
if buf == nil {
|
||||
return nil, zktrie.ErrKeyNotFound
|
||||
}
|
||||
n, err := zktrie.NewNodeFromBytes(buf)
|
||||
return n, err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
// do not contain the key
|
||||
return nil, err
|
||||
} else if !proof.Existence {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if zktrie.VerifyProofZkTrie(h, proof, n) {
|
||||
return n.Data(), nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("bad proof node %v", proof)
|
||||
}
|
||||
}
|
||||
172
trie/zk_trie_database.go
Normal file
172
trie/zk_trie_database.go
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
package trie
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
|
||||
zktrie "github.com/scroll-tech/zktrie/trie"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
||||
)
|
||||
|
||||
// ZktrieDatabase Database adaptor implements zktrie.ZktrieDatbase
|
||||
// It also reverses the bit order of the key being persisted.
|
||||
// This ensures that the adjacent leaf in zktrie maintains minimal
|
||||
// distance when persisted with dictionary order in LevelDB.
|
||||
// Consequently, this optimizes the snapshot operation, allowing it
|
||||
// to iterate through adjacent leaves at a reduced cost.
|
||||
|
||||
type ZktrieDatabase struct {
|
||||
db *Database
|
||||
prefix []byte
|
||||
}
|
||||
|
||||
func NewZktrieDatabase(diskdb ethdb.Database) *ZktrieDatabase {
|
||||
db := NewDatabase(diskdb, nil)
|
||||
db.config.IsUsingZktrie = true
|
||||
return &ZktrieDatabase{db: db, prefix: []byte{}}
|
||||
}
|
||||
|
||||
// adhoc wrapper...
|
||||
func NewZktrieDatabaseFromTriedb(db *Database) *ZktrieDatabase {
|
||||
db.config.IsUsingZktrie = true
|
||||
return &ZktrieDatabase{db: db, prefix: []byte{}}
|
||||
}
|
||||
|
||||
// Put saves a key:value into the Storage
|
||||
func (l *ZktrieDatabase) Put(k, v []byte) error {
|
||||
k = bitReverse(k)
|
||||
l.db.GetLock().Lock()
|
||||
l.db.rawDirties.Put(Concat(l.prefix, k[:]), v)
|
||||
l.db.GetLock().Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves a value from a key in the Storage
|
||||
func (l *ZktrieDatabase) Get(key []byte) ([]byte, error) {
|
||||
key = bitReverse(key)
|
||||
concatKey := Concat(l.prefix, key[:])
|
||||
l.db.GetLock().RLock()
|
||||
value, ok := l.db.rawDirties.Get(concatKey)
|
||||
l.db.GetLock().RUnlock()
|
||||
if ok {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
if l.db.GetCleans() != nil {
|
||||
if enc := l.db.GetCleans().Get(nil, concatKey); enc != nil {
|
||||
hashdb.MemcacheCleanHitMeter.Mark(1)
|
||||
hashdb.MemcacheCleanReadMeter.Mark(int64(len(enc)))
|
||||
return enc, nil
|
||||
}
|
||||
}
|
||||
|
||||
v, err := l.db.diskdb.Get(concatKey)
|
||||
if err == leveldb.ErrNotFound {
|
||||
return nil, zktrie.ErrKeyNotFound
|
||||
}
|
||||
if l.db.GetCleans() != nil {
|
||||
l.db.GetCleans().Set(concatKey[:], v)
|
||||
hashdb.MemcacheCleanMissMeter.Mark(1)
|
||||
hashdb.MemcacheCleanWriteMeter.Mark(int64(len(v)))
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (l *ZktrieDatabase) UpdatePreimage(preimage []byte, hashField *big.Int) {
|
||||
db := l.db
|
||||
if db.preimages != nil { // Ugly direct check but avoids the below write lock
|
||||
// we must copy the input key
|
||||
db.preimages.insertPreimage(map[common.Hash][]byte{common.BytesToHash(hashField.Bytes()): common.CopyBytes(preimage)})
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate implements the method Iterate of the interface Storage
|
||||
func (l *ZktrieDatabase) Iterate(f func([]byte, []byte) (bool, error)) error {
|
||||
iter := l.db.diskdb.NewIterator(l.prefix, nil)
|
||||
defer iter.Release()
|
||||
for iter.Next() {
|
||||
localKey := bitReverse(iter.Key()[len(l.prefix):])
|
||||
if cont, err := f(localKey, iter.Value()); err != nil {
|
||||
return err
|
||||
} else if !cont {
|
||||
break
|
||||
}
|
||||
}
|
||||
iter.Release()
|
||||
return iter.Error()
|
||||
}
|
||||
|
||||
// Close implements the method Close of the interface Storage
|
||||
func (l *ZktrieDatabase) Close() {
|
||||
// FIXME: is this correct?
|
||||
if err := l.db.diskdb.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// List implements the method List of the interface Storage
|
||||
func (l *ZktrieDatabase) List(limit int) ([]KV, error) {
|
||||
ret := []KV{}
|
||||
err := l.Iterate(func(key []byte, value []byte) (bool, error) {
|
||||
ret = append(ret, KV{K: Clone(key), V: Clone(value)})
|
||||
if len(ret) == limit {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func bitReverseForNibble(b byte) byte {
|
||||
switch b {
|
||||
case 0:
|
||||
return 0
|
||||
case 1:
|
||||
return 8
|
||||
case 2:
|
||||
return 4
|
||||
case 3:
|
||||
return 12
|
||||
case 4:
|
||||
return 2
|
||||
case 5:
|
||||
return 10
|
||||
case 6:
|
||||
return 6
|
||||
case 7:
|
||||
return 14
|
||||
case 8:
|
||||
return 1
|
||||
case 9:
|
||||
return 9
|
||||
case 10:
|
||||
return 5
|
||||
case 11:
|
||||
return 13
|
||||
case 12:
|
||||
return 3
|
||||
case 13:
|
||||
return 11
|
||||
case 14:
|
||||
return 7
|
||||
case 15:
|
||||
return 15
|
||||
default:
|
||||
panic("unexpected input")
|
||||
}
|
||||
}
|
||||
|
||||
func bitReverse(inp []byte) (out []byte) {
|
||||
l := len(inp)
|
||||
out = make([]byte, l)
|
||||
|
||||
for i, b := range inp {
|
||||
out[l-i-1] = bitReverseForNibble(b&15)<<4 + bitReverseForNibble(b>>4)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
63
trie/zk_trie_database_test.go
Normal file
63
trie/zk_trie_database_test.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package trie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// grep from `feat/snap`
|
||||
func reverseBitInPlace(b []byte) {
|
||||
var v [8]uint8
|
||||
for i := 0; i < len(b); i++ {
|
||||
for j := 0; j < 8; j++ {
|
||||
v[j] = (b[i] >> j) & 1
|
||||
}
|
||||
var tmp uint8 = 0
|
||||
for j := 0; j < 8; j++ {
|
||||
tmp |= v[8-j-1] << j
|
||||
}
|
||||
b[i] = tmp
|
||||
}
|
||||
}
|
||||
|
||||
func reverseBytesInPlace(b []byte) {
|
||||
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
}
|
||||
|
||||
func TestBitReverse(t *testing.T) {
|
||||
for _, testBytes := range [][]byte{
|
||||
common.FromHex("7b908cce3bc16abb3eac5dff6c136856526f15225f74ce860a2bec47912a5492"),
|
||||
common.FromHex("fac65cd2ad5e301083d0310dd701b5faaff1364cbe01cdbfaf4ec3609bb4149e"),
|
||||
common.FromHex("55791f6ec2f83fee512a2d3d4b505784fdefaea89974e10440d01d62a18a298a"),
|
||||
common.FromHex("5ab775b64d86a8058bb71c3c765d0f2158c14bbeb9cb32a65eda793a7e95e30f"),
|
||||
common.FromHex("ccb464abf67804538908c62431b3a6788e8dc6dee62aff9bfe6b10136acfceac"),
|
||||
common.FromHex("b908adff17a5aa9d6787324c39014a74b04cef7fba6a92aeb730f48da1ca665d"),
|
||||
} {
|
||||
b1 := bitReverse(testBytes)
|
||||
reverseBitInPlace(testBytes)
|
||||
reverseBytesInPlace(testBytes)
|
||||
if !bytes.Equal(b1, testBytes) {
|
||||
t.Errorf("unexpected bit reversed %x vs %x", b1, testBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBitDoubleReverse(t *testing.T) {
|
||||
for _, testBytes := range [][]byte{
|
||||
common.FromHex("7b908cce3bc16abb3eac5dff6c136856526f15225f74ce860a2bec47912a5492"),
|
||||
common.FromHex("fac65cd2ad5e301083d0310dd701b5faaff1364cbe01cdbfaf4ec3609bb4149e"),
|
||||
common.FromHex("55791f6ec2f83fee512a2d3d4b505784fdefaea89974e10440d01d62a18a298a"),
|
||||
common.FromHex("5ab775b64d86a8058bb71c3c765d0f2158c14bbeb9cb32a65eda793a7e95e30f"),
|
||||
common.FromHex("ccb464abf67804538908c62431b3a6788e8dc6dee62aff9bfe6b10136acfceac"),
|
||||
common.FromHex("b908adff17a5aa9d6787324c39014a74b04cef7fba6a92aeb730f48da1ca665d"),
|
||||
} {
|
||||
b := bitReverse(bitReverse(testBytes))
|
||||
if !bytes.Equal(b, testBytes) {
|
||||
t.Errorf("unexpected double bit reversed %x vs %x", b, testBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
289
trie/zk_trie_impl_test.go
Normal file
289
trie/zk_trie_impl_test.go
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
package trie
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/iden3/go-iden3-crypto/constants"
|
||||
cryptoUtils "github.com/iden3/go-iden3-crypto/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
zktrie "github.com/scroll-tech/zktrie/trie"
|
||||
zkt "github.com/scroll-tech/zktrie/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// we do not need zktrie impl anymore, only made a wrapper for adapting testing
|
||||
type zkTrieImplTestWrapper struct {
|
||||
*zktrie.ZkTrieImpl
|
||||
}
|
||||
|
||||
func newZkTrieImpl(storage *ZktrieDatabase, maxLevels int) (*zkTrieImplTestWrapper, error) {
|
||||
return newZkTrieImplWithRoot(storage, &zkt.HashZero, maxLevels)
|
||||
}
|
||||
|
||||
// NewZkTrieImplWithRoot loads a new ZkTrieImpl. If in the storage already exists one
|
||||
// will open that one, if not, will create a new one.
|
||||
func newZkTrieImplWithRoot(storage *ZktrieDatabase, root *zkt.Hash, maxLevels int) (*zkTrieImplTestWrapper, error) {
|
||||
impl, err := zktrie.NewZkTrieImplWithRoot(storage, root, maxLevels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &zkTrieImplTestWrapper{impl}, nil
|
||||
}
|
||||
|
||||
// AddWord
|
||||
// Deprecated: Add a Bytes32 kv to ZkTrieImpl, only for testing
|
||||
func (mt *zkTrieImplTestWrapper) AddWord(kPreimage, vPreimage *zkt.Byte32) error {
|
||||
k, err := kPreimage.Hash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v, _ := mt.TryGet(k.Bytes()); v != nil {
|
||||
return zktrie.ErrEntryIndexAlreadyExists
|
||||
}
|
||||
|
||||
return mt.ZkTrieImpl.TryUpdate(zkt.NewHashFromBigInt(k), 1, []zkt.Byte32{*vPreimage})
|
||||
}
|
||||
|
||||
// GetLeafNodeByWord
|
||||
// Deprecated: Get a Bytes32 kv to ZkTrieImpl, only for testing
|
||||
func (mt *zkTrieImplTestWrapper) GetLeafNodeByWord(kPreimage *zkt.Byte32) (*zktrie.Node, error) {
|
||||
k, err := kPreimage.Hash()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mt.ZkTrieImpl.GetLeafNode(zkt.NewHashFromBigInt(k))
|
||||
}
|
||||
|
||||
// Deprecated: only for testing
|
||||
func (mt *zkTrieImplTestWrapper) UpdateWord(kPreimage, vPreimage *zkt.Byte32) error {
|
||||
k, err := kPreimage.Hash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mt.ZkTrieImpl.TryUpdate(zkt.NewHashFromBigInt(k), 1, []zkt.Byte32{*vPreimage})
|
||||
}
|
||||
|
||||
// Deprecated: only for testing
|
||||
func (mt *zkTrieImplTestWrapper) DeleteWord(kPreimage *zkt.Byte32) error {
|
||||
k, err := kPreimage.Hash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return mt.ZkTrieImpl.TryDelete(zkt.NewHashFromBigInt(k))
|
||||
}
|
||||
|
||||
func (mt *zkTrieImplTestWrapper) TryGet(key []byte) ([]byte, error) {
|
||||
return mt.ZkTrieImpl.TryGet(zkt.NewHashFromBytes(key))
|
||||
}
|
||||
|
||||
func (mt *zkTrieImplTestWrapper) TryDelete(key []byte) error {
|
||||
return mt.ZkTrieImpl.TryDelete(zkt.NewHashFromBytes(key))
|
||||
}
|
||||
|
||||
// TryUpdateAccount will abstract the write of an account to the trie
|
||||
func (mt *zkTrieImplTestWrapper) TryUpdateAccount(key []byte, acc *types.StateAccount) error {
|
||||
value, flag := acc.MarshalFields()
|
||||
return mt.ZkTrieImpl.TryUpdate(zkt.NewHashFromBytes(key), flag, value)
|
||||
}
|
||||
|
||||
// NewHashFromHex returns a *Hash representation of the given hex string
|
||||
func NewHashFromHex(h string) (*zkt.Hash, error) {
|
||||
return zkt.NewHashFromCheckedBytes(common.FromHex(h))
|
||||
}
|
||||
|
||||
type Fatalable interface {
|
||||
Fatal(args ...interface{})
|
||||
}
|
||||
|
||||
func newTestingMerkle(f Fatalable, numLevels int) *zkTrieImplTestWrapper {
|
||||
mt, err := newZkTrieImpl(NewZktrieDatabase(rawdb.NewMemoryDatabase()), numLevels)
|
||||
if err != nil {
|
||||
f.Fatal(err)
|
||||
return nil
|
||||
}
|
||||
return mt
|
||||
}
|
||||
|
||||
func TestHashParsers(t *testing.T) {
|
||||
h0 := zkt.NewHashFromBigInt(big.NewInt(0))
|
||||
assert.Equal(t, "0", h0.String())
|
||||
h1 := zkt.NewHashFromBigInt(big.NewInt(1))
|
||||
assert.Equal(t, "1", h1.String())
|
||||
h10 := zkt.NewHashFromBigInt(big.NewInt(10))
|
||||
assert.Equal(t, "10", h10.String())
|
||||
|
||||
h7l := zkt.NewHashFromBigInt(big.NewInt(1234567))
|
||||
assert.Equal(t, "1234567", h7l.String())
|
||||
h8l := zkt.NewHashFromBigInt(big.NewInt(12345678))
|
||||
assert.Equal(t, "12345678...", h8l.String())
|
||||
|
||||
b, ok := new(big.Int).SetString("4932297968297298434239270129193057052722409868268166443802652458940273154854", 10) //nolint:lll
|
||||
assert.True(t, ok)
|
||||
h := zkt.NewHashFromBigInt(b)
|
||||
assert.Equal(t, "4932297968297298434239270129193057052722409868268166443802652458940273154854", h.BigInt().String()) //nolint:lll
|
||||
assert.Equal(t, "49322979...", h.String())
|
||||
assert.Equal(t, "0ae794eb9c3d8bbb9002e993fc2ed301dcbd2af5508ed072c375e861f1aa5b26", h.Hex())
|
||||
|
||||
b1, err := zkt.NewBigIntFromHashBytes(b.Bytes())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, new(big.Int).SetBytes(b.Bytes()).String(), b1.String())
|
||||
|
||||
b2, err := zkt.NewHashFromCheckedBytes(b.Bytes())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, b.String(), b2.BigInt().String())
|
||||
|
||||
h2, err := NewHashFromHex(h.Hex())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, h, h2)
|
||||
_, err = NewHashFromHex("0x12")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// check limits
|
||||
a := new(big.Int).Sub(constants.Q, big.NewInt(1))
|
||||
testHashParsers(t, a)
|
||||
a = big.NewInt(int64(1))
|
||||
testHashParsers(t, a)
|
||||
}
|
||||
|
||||
func testHashParsers(t *testing.T, a *big.Int) {
|
||||
require.True(t, cryptoUtils.CheckBigIntInField(a))
|
||||
h := zkt.NewHashFromBigInt(a)
|
||||
assert.Equal(t, a, h.BigInt())
|
||||
hFromBytes, err := zkt.NewHashFromCheckedBytes(h.Bytes())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, h, hFromBytes)
|
||||
assert.Equal(t, a, hFromBytes.BigInt())
|
||||
assert.Equal(t, a.String(), hFromBytes.BigInt().String())
|
||||
hFromHex, err := NewHashFromHex(h.Hex())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, h, hFromHex)
|
||||
|
||||
aBIFromHBytes, err := zkt.NewBigIntFromHashBytes(h.Bytes())
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, a, aBIFromHBytes)
|
||||
assert.Equal(t, new(big.Int).SetBytes(a.Bytes()).String(), aBIFromHBytes.String())
|
||||
}
|
||||
|
||||
func TestMerkleTree_AddUpdateGetWord(t *testing.T) {
|
||||
mt := newTestingMerkle(t, 10)
|
||||
err := mt.AddWord(&zkt.Byte32{1}, &zkt.Byte32{2})
|
||||
assert.Nil(t, err)
|
||||
err = mt.AddWord(&zkt.Byte32{3}, &zkt.Byte32{4})
|
||||
assert.Nil(t, err)
|
||||
err = mt.AddWord(&zkt.Byte32{5}, &zkt.Byte32{6})
|
||||
assert.Nil(t, err)
|
||||
err = mt.AddWord(&zkt.Byte32{5}, &zkt.Byte32{7})
|
||||
assert.Equal(t, zktrie.ErrEntryIndexAlreadyExists, err)
|
||||
|
||||
node, err := mt.GetLeafNodeByWord(&zkt.Byte32{1})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(node.ValuePreimage), 1)
|
||||
assert.Equal(t, (&zkt.Byte32{2})[:], node.ValuePreimage[0][:])
|
||||
node, err = mt.GetLeafNodeByWord(&zkt.Byte32{3})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(node.ValuePreimage), 1)
|
||||
assert.Equal(t, (&zkt.Byte32{4})[:], node.ValuePreimage[0][:])
|
||||
node, err = mt.GetLeafNodeByWord(&zkt.Byte32{5})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(node.ValuePreimage), 1)
|
||||
assert.Equal(t, (&zkt.Byte32{6})[:], node.ValuePreimage[0][:])
|
||||
|
||||
err = mt.UpdateWord(&zkt.Byte32{1}, &zkt.Byte32{7})
|
||||
assert.Nil(t, err)
|
||||
err = mt.UpdateWord(&zkt.Byte32{3}, &zkt.Byte32{8})
|
||||
assert.Nil(t, err)
|
||||
err = mt.UpdateWord(&zkt.Byte32{5}, &zkt.Byte32{9})
|
||||
assert.Nil(t, err)
|
||||
|
||||
node, err = mt.GetLeafNodeByWord(&zkt.Byte32{1})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(node.ValuePreimage), 1)
|
||||
assert.Equal(t, (&zkt.Byte32{7})[:], node.ValuePreimage[0][:])
|
||||
node, err = mt.GetLeafNodeByWord(&zkt.Byte32{3})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(node.ValuePreimage), 1)
|
||||
assert.Equal(t, (&zkt.Byte32{8})[:], node.ValuePreimage[0][:])
|
||||
node, err = mt.GetLeafNodeByWord(&zkt.Byte32{5})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(node.ValuePreimage), 1)
|
||||
assert.Equal(t, (&zkt.Byte32{9})[:], node.ValuePreimage[0][:])
|
||||
_, err = mt.GetLeafNodeByWord(&zkt.Byte32{100})
|
||||
assert.Equal(t, zktrie.ErrKeyNotFound, err)
|
||||
}
|
||||
|
||||
func TestMerkleTree_UpdateAccount(t *testing.T) {
|
||||
mt := newTestingMerkle(t, 10)
|
||||
|
||||
acc1 := &types.StateAccount{
|
||||
Nonce: 1,
|
||||
Balance: big.NewInt(10000000),
|
||||
Root: common.HexToHash("22fb59aa5410ed465267023713ab42554c250f394901455a3366e223d5f7d147"),
|
||||
KeccakCodeHash: common.HexToHash("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(),
|
||||
PoseidonCodeHash: common.HexToHash("0c0a77f6e063b4b62eb7d9ed6f427cf687d8d0071d751850cfe5d136bc60d3ab").Bytes(),
|
||||
CodeSize: 0,
|
||||
}
|
||||
err := mt.TryUpdateAccount(common.HexToAddress("0x05fDbDfaE180345C6Cff5316c286727CF1a43327").Bytes(), acc1)
|
||||
assert.Nil(t, err)
|
||||
|
||||
acc2 := &types.StateAccount{
|
||||
Nonce: 5,
|
||||
Balance: big.NewInt(50000000),
|
||||
Root: common.HexToHash("0"),
|
||||
KeccakCodeHash: common.HexToHash("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(),
|
||||
PoseidonCodeHash: common.HexToHash("05d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").Bytes(),
|
||||
CodeSize: 5,
|
||||
}
|
||||
err = mt.TryUpdateAccount(common.HexToAddress("0x4cb1aB63aF5D8931Ce09673EbD8ae2ce16fD6571").Bytes(), acc2)
|
||||
assert.Nil(t, err)
|
||||
|
||||
bt, err := mt.TryGet(common.HexToAddress("0x05fDbDfaE180345C6Cff5316c286727CF1a43327").Bytes())
|
||||
assert.Nil(t, err)
|
||||
|
||||
acc, err := types.UnmarshalStateAccount(bt)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, acc1.Nonce, acc.Nonce)
|
||||
assert.Equal(t, acc1.Balance.Uint64(), acc.Balance.Uint64())
|
||||
assert.Equal(t, acc1.Root.Bytes(), acc.Root.Bytes())
|
||||
assert.Equal(t, acc1.KeccakCodeHash, acc.KeccakCodeHash)
|
||||
assert.Equal(t, acc1.PoseidonCodeHash, acc.PoseidonCodeHash)
|
||||
assert.Equal(t, acc1.CodeSize, acc.CodeSize)
|
||||
|
||||
bt, err = mt.TryGet(common.HexToAddress("0x4cb1aB63aF5D8931Ce09673EbD8ae2ce16fD6571").Bytes())
|
||||
assert.Nil(t, err)
|
||||
|
||||
acc, err = types.UnmarshalStateAccount(bt)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, acc2.Nonce, acc.Nonce)
|
||||
assert.Equal(t, acc2.Balance.Uint64(), acc.Balance.Uint64())
|
||||
assert.Equal(t, acc2.Root.Bytes(), acc.Root.Bytes())
|
||||
assert.Equal(t, acc2.KeccakCodeHash, acc.KeccakCodeHash)
|
||||
assert.Equal(t, acc2.PoseidonCodeHash, acc.PoseidonCodeHash)
|
||||
assert.Equal(t, acc2.CodeSize, acc.CodeSize)
|
||||
|
||||
bt, err = mt.TryGet(common.HexToAddress("0x8dE13967F19410A7991D63c2c0179feBFDA0c261").Bytes())
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, bt)
|
||||
|
||||
err = mt.TryDelete(common.HexToHash("0x05fDbDfaE180345C6Cff5316c286727CF1a43327").Bytes())
|
||||
assert.Nil(t, err)
|
||||
|
||||
bt, err = mt.TryGet(common.HexToAddress("0x05fDbDfaE180345C6Cff5316c286727CF1a43327").Bytes())
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, bt)
|
||||
|
||||
err = mt.TryDelete(common.HexToAddress("0x4cb1aB63aF5D8931Ce09673EbD8ae2ce16fD6571").Bytes())
|
||||
assert.Nil(t, err)
|
||||
|
||||
bt, err = mt.TryGet(common.HexToAddress("0x4cb1aB63aF5D8931Ce09673EbD8ae2ce16fD6571").Bytes())
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, bt)
|
||||
}
|
||||
284
trie/zk_trie_proof_test.go
Normal file
284
trie/zk_trie_proof_test.go
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package trie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
mrand "math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
zkt "github.com/scroll-tech/zktrie/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||
)
|
||||
|
||||
func init() {
|
||||
mrand.Seed(time.Now().Unix())
|
||||
}
|
||||
|
||||
// makeProvers creates Merkle trie provers based on different implementations to
|
||||
// test all variations.
|
||||
func makeSMTProvers(mt *ZkTrie) []func(key []byte) *memorydb.Database {
|
||||
var provers []func(key []byte) *memorydb.Database
|
||||
|
||||
// Create a direct trie based Merkle prover
|
||||
provers = append(provers, func(key []byte) *memorydb.Database {
|
||||
word := zkt.NewByte32FromBytesPaddingZero(key)
|
||||
k, err := word.Hash()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
proofDB := memorydb.New()
|
||||
err = mt.Prove(common.BytesToHash(k.Bytes()).Bytes(), proofDB)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return proofDB
|
||||
})
|
||||
return provers
|
||||
}
|
||||
|
||||
func verifyValue(proveVal []byte, vPreimage []byte) bool {
|
||||
return bytes.Equal(proveVal, vPreimage)
|
||||
}
|
||||
|
||||
func TestSMTOneElementProof(t *testing.T) {
|
||||
tr, _ := NewZkTrie(common.Hash{}, NewZktrieDatabase(rawdb.NewMemoryDatabase()))
|
||||
mt := &zkTrieImplTestWrapper{tr.Tree()}
|
||||
err := mt.UpdateWord(
|
||||
zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("k"), 32)),
|
||||
zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("v"), 32)),
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
for i, prover := range makeSMTProvers(tr) {
|
||||
keyBytes := bytes.Repeat([]byte("k"), 32)
|
||||
proof := prover(keyBytes)
|
||||
if proof == nil {
|
||||
t.Fatalf("prover %d: nil proof", i)
|
||||
}
|
||||
if proof.Len() != 2 {
|
||||
t.Errorf("prover %d: proof should have 1+1 element (including the magic kv)", i)
|
||||
}
|
||||
val, err := VerifyProof(common.BytesToHash(mt.Root().Bytes()), keyBytes, proof)
|
||||
if err != nil {
|
||||
t.Fatalf("prover %d: failed to verify proof: %v\nraw proof: %x", i, err, proof)
|
||||
}
|
||||
if !verifyValue(val, bytes.Repeat([]byte("v"), 32)) {
|
||||
t.Fatalf("prover %d: verified value mismatch: want 'v' get %x", i, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSMTProof(t *testing.T) {
|
||||
mt, vals := randomZktrie(t, 500)
|
||||
root := mt.Tree().Root()
|
||||
for i, prover := range makeSMTProvers(mt) {
|
||||
for _, kv := range vals {
|
||||
proof := prover(kv.k)
|
||||
if proof == nil {
|
||||
t.Fatalf("prover %d: missing key %x while constructing proof", i, kv.k)
|
||||
}
|
||||
val, err := VerifyProof(common.BytesToHash(root.Bytes()), kv.k, proof)
|
||||
if err != nil {
|
||||
t.Fatalf("prover %d: failed to verify proof for key %x: %v\nraw proof: %x\n", i, kv.k, err, proof)
|
||||
}
|
||||
if !verifyValue(val, zkt.NewByte32FromBytesPaddingZero(kv.v)[:]) {
|
||||
t.Fatalf("prover %d: verified value mismatch for key %x, want %x, get %x", i, kv.k, kv.v, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSMTBadProof(t *testing.T) {
|
||||
mt, vals := randomZktrie(t, 500)
|
||||
root := mt.Tree().Root()
|
||||
for i, prover := range makeSMTProvers(mt) {
|
||||
for _, kv := range vals {
|
||||
proof := prover(kv.k)
|
||||
if proof == nil {
|
||||
t.Fatalf("prover %d: nil proof", i)
|
||||
}
|
||||
it := proof.NewIterator(nil, nil)
|
||||
for i, d := 0, mrand.Intn(proof.Len()); i <= d; i++ {
|
||||
it.Next()
|
||||
}
|
||||
key := it.Key()
|
||||
val, _ := proof.Get(key)
|
||||
proof.Delete(key)
|
||||
it.Release()
|
||||
|
||||
mutateByte(val)
|
||||
proof.Put(crypto.Keccak256(val), val)
|
||||
|
||||
if _, err := VerifyProof(common.BytesToHash(root.Bytes()), kv.k, proof); err == nil {
|
||||
t.Fatalf("prover %d: expected proof to fail for key %x", i, kv.k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that missing keys can also be proven. The test explicitly uses a single
|
||||
// entry trie and checks for missing keys both before and after the single entry.
|
||||
func TestSMTMissingKeyProof(t *testing.T) {
|
||||
tr, _ := NewZkTrie(common.Hash{}, NewZktrieDatabase(rawdb.NewMemoryDatabase()))
|
||||
mt := &zkTrieImplTestWrapper{tr.Tree()}
|
||||
err := mt.UpdateWord(
|
||||
zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("k"), 32)),
|
||||
zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("v"), 32)),
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
|
||||
prover := makeSMTProvers(tr)[0]
|
||||
|
||||
for i, key := range []string{"a", "j", "l", "z"} {
|
||||
keyBytes := bytes.Repeat([]byte(key), 32)
|
||||
proof := prover(keyBytes)
|
||||
|
||||
if proof.Len() != 2 {
|
||||
t.Errorf("test %d: proof should have 2 element (with magic kv)", i)
|
||||
}
|
||||
val, err := VerifyProof(common.BytesToHash(mt.Root().Bytes()), keyBytes, proof)
|
||||
if err != nil {
|
||||
t.Fatalf("test %d: failed to verify proof: %v\nraw proof: %x", i, err, proof)
|
||||
}
|
||||
if val != nil {
|
||||
t.Fatalf("test %d: verified value mismatch: have %x, want nil", i, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func randomZktrie(t *testing.T, n int) (*ZkTrie, map[string]*kv) {
|
||||
tr, err := NewZkTrie(common.Hash{}, NewZktrieDatabase(rawdb.NewMemoryDatabase()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mt := &zkTrieImplTestWrapper{tr.Tree()}
|
||||
vals := make(map[string]*kv)
|
||||
for i := byte(0); i < 100; i++ {
|
||||
|
||||
value := &kv{common.LeftPadBytes([]byte{i}, 32), bytes.Repeat([]byte{i}, 32), false}
|
||||
value2 := &kv{common.LeftPadBytes([]byte{i + 10}, 32), bytes.Repeat([]byte{i}, 32), false}
|
||||
|
||||
err = mt.UpdateWord(zkt.NewByte32FromBytesPaddingZero(value.k), zkt.NewByte32FromBytesPaddingZero(value.v))
|
||||
assert.Nil(t, err)
|
||||
err = mt.UpdateWord(zkt.NewByte32FromBytesPaddingZero(value2.k), zkt.NewByte32FromBytesPaddingZero(value2.v))
|
||||
assert.Nil(t, err)
|
||||
vals[string(value.k)] = value
|
||||
vals[string(value2.k)] = value2
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
value := &kv{randBytes(32), randBytes(20), false}
|
||||
err = mt.UpdateWord(zkt.NewByte32FromBytesPaddingZero(value.k), zkt.NewByte32FromBytesPaddingZero(value.v))
|
||||
assert.Nil(t, err)
|
||||
vals[string(value.k)] = value
|
||||
}
|
||||
|
||||
return tr, vals
|
||||
}
|
||||
|
||||
// Tests that new "proof trace" feature
|
||||
func TestProofWithDeletion(t *testing.T) {
|
||||
tr, _ := NewZkTrie(common.Hash{}, NewZktrieDatabase(rawdb.NewMemoryDatabase()))
|
||||
mt := &zkTrieImplTestWrapper{tr.Tree()}
|
||||
key1 := bytes.Repeat([]byte("l"), 32)
|
||||
key2 := bytes.Repeat([]byte("m"), 32)
|
||||
err := mt.UpdateWord(
|
||||
zkt.NewByte32FromBytesPaddingZero(key1),
|
||||
zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("v"), 32)),
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
err = mt.UpdateWord(
|
||||
zkt.NewByte32FromBytesPaddingZero(key2),
|
||||
zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("n"), 32)),
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proof := memorydb.New()
|
||||
s_key1, err := zkt.ToSecureKeyBytes(key1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proofTracer := tr.NewProofTracer()
|
||||
|
||||
err = proofTracer.Prove(s_key1.Bytes(), 0, proof)
|
||||
assert.NoError(t, err)
|
||||
nd, err := tr.TryGet(key2)
|
||||
assert.NoError(t, err)
|
||||
|
||||
s_key2, err := zkt.ToSecureKeyBytes(bytes.Repeat([]byte("x"), 32))
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = proofTracer.Prove(s_key2.Bytes(), 0, proof)
|
||||
assert.NoError(t, err)
|
||||
//assert.Equal(t, len(sibling1), len(delTracer.GetProofs()))
|
||||
|
||||
siblings, err := proofTracer.GetDeletionProofs()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(siblings))
|
||||
|
||||
proofTracer.MarkDeletion(s_key1.Bytes())
|
||||
siblings, err = proofTracer.GetDeletionProofs()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(siblings))
|
||||
l := len(siblings[0])
|
||||
// a hacking to grep the value part directly from the encoded leaf node,
|
||||
// notice the sibling of key `k*32`` is just the leaf of key `m*32`
|
||||
assert.Equal(t, siblings[0][l-33:l-1], nd)
|
||||
|
||||
// Marking a key that is currently not hit (but terminated by an empty node)
|
||||
// also causes it to be added to the deletion proof
|
||||
proofTracer.MarkDeletion(s_key2.Bytes())
|
||||
siblings, err = proofTracer.GetDeletionProofs()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(siblings))
|
||||
|
||||
key3 := bytes.Repeat([]byte("x"), 32)
|
||||
err = mt.UpdateWord(
|
||||
zkt.NewByte32FromBytesPaddingZero(key3),
|
||||
zkt.NewByte32FromBytesPaddingZero(bytes.Repeat([]byte("z"), 32)),
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proofTracer = tr.NewProofTracer()
|
||||
err = proofTracer.Prove(s_key1.Bytes(), 0, proof)
|
||||
assert.NoError(t, err)
|
||||
err = proofTracer.Prove(s_key2.Bytes(), 0, proof)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proofTracer.MarkDeletion(s_key1.Bytes())
|
||||
siblings, err = proofTracer.GetDeletionProofs()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(siblings))
|
||||
|
||||
proofTracer.MarkDeletion(s_key2.Bytes())
|
||||
siblings, err = proofTracer.GetDeletionProofs()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(siblings))
|
||||
|
||||
// one of the siblings is just leaf for key2, while
|
||||
// another one must be a middle node
|
||||
match1 := bytes.Equal(siblings[0][l-33:l-1], nd)
|
||||
match2 := bytes.Equal(siblings[1][l-33:l-1], nd)
|
||||
assert.True(t, match1 || match2)
|
||||
assert.False(t, match1 && match2)
|
||||
}
|
||||
270
trie/zk_trie_test.go
Normal file
270
trie/zk_trie_test.go
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package trie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
zkt "github.com/scroll-tech/zktrie/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
||||
)
|
||||
|
||||
func newEmptyZkTrie() *ZkTrie {
|
||||
trie, _ := NewZkTrie(
|
||||
common.Hash{},
|
||||
&ZktrieDatabase{
|
||||
db: NewDatabase(rawdb.NewMemoryDatabase(),
|
||||
&Config{Preimages: true}),
|
||||
prefix: []byte{},
|
||||
},
|
||||
)
|
||||
return trie
|
||||
}
|
||||
|
||||
// makeTestSecureTrie creates a large enough secure trie for testing.
|
||||
func makeTestZkTrie() (*ZktrieDatabase, *ZkTrie, map[string][]byte) {
|
||||
// Create an empty trie
|
||||
triedb := NewZktrieDatabase(rawdb.NewMemoryDatabase())
|
||||
trie, _ := NewZkTrie(common.Hash{}, triedb)
|
||||
|
||||
// Fill it with some arbitrary data
|
||||
content := make(map[string][]byte)
|
||||
for i := byte(0); i < 255; i++ {
|
||||
// Map the same data under multiple keys
|
||||
key, val := common.LeftPadBytes([]byte{1, i}, 32), bytes.Repeat([]byte{i}, 32)
|
||||
content[string(key)] = val
|
||||
trie.Update(key, val)
|
||||
|
||||
key, val = common.LeftPadBytes([]byte{2, i}, 32), bytes.Repeat([]byte{i}, 32)
|
||||
content[string(key)] = val
|
||||
trie.Update(key, val)
|
||||
|
||||
// Add some other data to inflate the trie
|
||||
for j := byte(3); j < 13; j++ {
|
||||
key, val = common.LeftPadBytes([]byte{j, i}, 32), bytes.Repeat([]byte{j, i}, 16)
|
||||
content[string(key)] = val
|
||||
trie.Update(key, val)
|
||||
}
|
||||
}
|
||||
trie.Commit(false)
|
||||
|
||||
// Return the generated trie
|
||||
return triedb, trie, content
|
||||
}
|
||||
|
||||
func TestZktrieDelete(t *testing.T) {
|
||||
t.Skip("var-len kv not supported")
|
||||
trie := newEmptyZkTrie()
|
||||
vals := []struct{ k, v string }{
|
||||
{"do", "verb"},
|
||||
{"ether", "wookiedoo"},
|
||||
{"horse", "stallion"},
|
||||
{"shaman", "horse"},
|
||||
{"doge", "coin"},
|
||||
{"ether", ""},
|
||||
{"dog", "puppy"},
|
||||
{"shaman", ""},
|
||||
}
|
||||
for _, val := range vals {
|
||||
if val.v != "" {
|
||||
trie.Update([]byte(val.k), []byte(val.v))
|
||||
} else {
|
||||
trie.Delete([]byte(val.k))
|
||||
}
|
||||
}
|
||||
hash := trie.Hash()
|
||||
exp := common.HexToHash("29b235a58c3c25ab83010c327d5932bcf05324b7d6b1185e650798034783ca9d")
|
||||
if hash != exp {
|
||||
t.Errorf("expected %x got %x", exp, hash)
|
||||
}
|
||||
}
|
||||
|
||||
func TestZktrieGetKey(t *testing.T) {
|
||||
trie := newEmptyZkTrie()
|
||||
key := []byte("0a1b2c3d4e5f6g7h8i9j0a1b2c3d4e5f")
|
||||
value := []byte("9j8i7h6g5f4e3d2c1b0a9j8i7h6g5f4e")
|
||||
trie.Update(key, value)
|
||||
|
||||
kPreimage := zkt.NewByte32FromBytesPaddingZero(key)
|
||||
kHash, err := kPreimage.Hash()
|
||||
assert.Nil(t, err)
|
||||
|
||||
if !bytes.Equal(trie.Get(key), value) {
|
||||
t.Errorf("Get did not return bar")
|
||||
}
|
||||
if k := trie.GetKey(kHash.Bytes()); !bytes.Equal(k, key) {
|
||||
t.Errorf("GetKey returned %q, want %q", k, key)
|
||||
}
|
||||
}
|
||||
|
||||
func TestZkTrieConcurrency(t *testing.T) {
|
||||
// Create an initial trie and copy if for concurrent access
|
||||
_, trie, _ := makeTestZkTrie()
|
||||
|
||||
threads := runtime.NumCPU()
|
||||
tries := make([]*ZkTrie, threads)
|
||||
for i := 0; i < threads; i++ {
|
||||
cpy := *trie
|
||||
tries[i] = &cpy
|
||||
}
|
||||
// Start a batch of goroutines interactng with the trie
|
||||
pend := new(sync.WaitGroup)
|
||||
pend.Add(threads)
|
||||
for i := 0; i < threads; i++ {
|
||||
go func(index int) {
|
||||
defer pend.Done()
|
||||
|
||||
for j := byte(0); j < 255; j++ {
|
||||
// Map the same data under multiple keys
|
||||
key, val := common.LeftPadBytes([]byte{byte(index), 1, j}, 32), bytes.Repeat([]byte{j}, 32)
|
||||
tries[index].Update(key, val)
|
||||
|
||||
key, val = common.LeftPadBytes([]byte{byte(index), 2, j}, 32), bytes.Repeat([]byte{j}, 32)
|
||||
tries[index].Update(key, val)
|
||||
|
||||
// Add some other data to inflate the trie
|
||||
for k := byte(3); k < 13; k++ {
|
||||
key, val = common.LeftPadBytes([]byte{byte(index), k, j}, 32), bytes.Repeat([]byte{k, j}, 16)
|
||||
tries[index].Update(key, val)
|
||||
}
|
||||
}
|
||||
tries[index].Commit(false)
|
||||
}(i)
|
||||
}
|
||||
// Wait for all threads to finish
|
||||
pend.Wait()
|
||||
}
|
||||
|
||||
func tempDBZK(b *testing.B) (string, *Database) {
|
||||
dir, err := ioutil.TempDir("", "zktrie-bench")
|
||||
assert.NoError(b, err)
|
||||
|
||||
diskdb, err := rawdb.NewLevelDBDatabase(dir, 256, 0, "", false)
|
||||
assert.NoError(b, err)
|
||||
config := &Config{
|
||||
Preimages: true,
|
||||
HashDB: &hashdb.Config{CleanCacheSize: 256},
|
||||
IsUsingZktrie: true,
|
||||
}
|
||||
return dir, NewDatabase(diskdb, config)
|
||||
}
|
||||
|
||||
const benchElemCountZk = 10000
|
||||
|
||||
func BenchmarkZkTrieGet(b *testing.B) {
|
||||
dir, tmpdb := tempDBZK(b)
|
||||
zkTrie, _ := NewZkTrie(common.Hash{}, NewZktrieDatabaseFromTriedb(tmpdb))
|
||||
defer func() {
|
||||
ldb := zkTrie.db.db.diskdb
|
||||
ldb.Close()
|
||||
os.RemoveAll(dir)
|
||||
}()
|
||||
|
||||
k := make([]byte, 32)
|
||||
for i := 0; i < benchElemCountZk; i++ {
|
||||
binary.LittleEndian.PutUint64(k, uint64(i))
|
||||
|
||||
err := zkTrie.TryUpdate(k, k)
|
||||
assert.NoError(b, err)
|
||||
}
|
||||
|
||||
zkTrie.db.db.Commit(common.Hash{}, true)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
binary.LittleEndian.PutUint64(k, uint64(i))
|
||||
_, err := zkTrie.TryGet(k)
|
||||
assert.NoError(b, err)
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func BenchmarkZkTrieUpdate(b *testing.B) {
|
||||
dir, tmpdb := tempDBZK(b)
|
||||
zkTrie, _ := NewZkTrie(common.Hash{}, NewZktrieDatabaseFromTriedb(tmpdb))
|
||||
defer func() {
|
||||
ldb := zkTrie.db.db.diskdb
|
||||
ldb.Close()
|
||||
os.RemoveAll(dir)
|
||||
}()
|
||||
|
||||
k := make([]byte, 32)
|
||||
v := make([]byte, 32)
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < benchElemCountZk; i++ {
|
||||
binary.LittleEndian.PutUint64(k, uint64(i))
|
||||
err := zkTrie.TryUpdate(k, k)
|
||||
assert.NoError(b, err)
|
||||
}
|
||||
binary.LittleEndian.PutUint64(k, benchElemCountZk/2)
|
||||
|
||||
//zkTrie.Commit(false)
|
||||
zkTrie.db.db.Commit(common.Hash{}, true)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
binary.LittleEndian.PutUint64(k, uint64(i))
|
||||
binary.LittleEndian.PutUint64(v, 0xffffffff+uint64(i))
|
||||
err := zkTrie.TryUpdate(k, v)
|
||||
assert.NoError(b, err)
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func TestZkTrieDelete(t *testing.T) {
|
||||
key := make([]byte, 32)
|
||||
value := make([]byte, 32)
|
||||
trie1 := newEmptyZkTrie()
|
||||
|
||||
var count int = 6
|
||||
var hashes []common.Hash
|
||||
hashes = append(hashes, trie1.Hash())
|
||||
for i := 0; i < count; i++ {
|
||||
binary.LittleEndian.PutUint64(key, uint64(i))
|
||||
binary.LittleEndian.PutUint64(value, uint64(i))
|
||||
err := trie1.TryUpdate(key, value)
|
||||
assert.NoError(t, err)
|
||||
hashes = append(hashes, trie1.Hash())
|
||||
}
|
||||
|
||||
// binary.LittleEndian.PutUint64(key, uint64(0xffffff))
|
||||
// err := trie1.TryDelete(key)
|
||||
// assert.Equal(t, err, zktrie.ErrKeyNotFound)
|
||||
|
||||
trie1.Commit(false)
|
||||
|
||||
for i := count - 1; i >= 0; i-- {
|
||||
binary.LittleEndian.PutUint64(key, uint64(i))
|
||||
v, err := trie1.TryGet(key)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, v)
|
||||
err = trie1.TryDelete(key)
|
||||
assert.NoError(t, err)
|
||||
hash := trie1.Hash()
|
||||
assert.Equal(t, hashes[i].Hex(), hash.Hex())
|
||||
}
|
||||
}
|
||||
349
trie/zkproof/orderer.go
Normal file
349
trie/zkproof/orderer.go
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
package zkproof
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
type opIterator interface {
|
||||
next() *types.AccountWrapper
|
||||
}
|
||||
|
||||
type opOrderer interface {
|
||||
readonly(bool)
|
||||
absorb(*types.AccountWrapper)
|
||||
absorbStorage(*types.AccountWrapper, *types.StorageWrapper)
|
||||
end_absorb() opIterator
|
||||
}
|
||||
|
||||
type iterateOp []*types.AccountWrapper
|
||||
|
||||
func (ops *iterateOp) next() *types.AccountWrapper {
|
||||
|
||||
sl := *ops
|
||||
|
||||
if len(sl) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
*ops = sl[1:]
|
||||
return sl[0]
|
||||
}
|
||||
|
||||
type simpleOrderer struct {
|
||||
readOnly int
|
||||
savedOp []*types.AccountWrapper
|
||||
}
|
||||
|
||||
func (od *simpleOrderer) SavedOp() []*types.AccountWrapper { return od.savedOp }
|
||||
|
||||
func (od *simpleOrderer) readonly(mode bool) {
|
||||
if mode {
|
||||
od.readOnly += 1
|
||||
} else if od.readOnly == 0 {
|
||||
panic("unexpected readonly mode stack pop")
|
||||
} else {
|
||||
od.readOnly -= 1
|
||||
}
|
||||
}
|
||||
|
||||
func (od *simpleOrderer) absorb(st *types.AccountWrapper) {
|
||||
if od.readOnly > 0 {
|
||||
return
|
||||
}
|
||||
od.savedOp = append(od.savedOp, st)
|
||||
}
|
||||
|
||||
func (od *simpleOrderer) absorbStorage(st *types.AccountWrapper, _ *types.StorageWrapper) {
|
||||
od.absorb(st)
|
||||
}
|
||||
|
||||
func (od *simpleOrderer) end_absorb() opIterator {
|
||||
ret := iterateOp(od.savedOp)
|
||||
return &ret
|
||||
}
|
||||
|
||||
type multiOpIterator []opIterator
|
||||
|
||||
func (opss *multiOpIterator) next() *types.AccountWrapper {
|
||||
|
||||
sl := *opss
|
||||
if len(sl) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
op := sl[0].next()
|
||||
|
||||
for op == nil {
|
||||
|
||||
sl = sl[1:]
|
||||
*opss = sl
|
||||
if len(sl) == 0 {
|
||||
return nil
|
||||
}
|
||||
op = sl[0].next()
|
||||
}
|
||||
return op
|
||||
}
|
||||
|
||||
type rwTblOrderer struct {
|
||||
readOnly int
|
||||
readOnlySnapshot struct {
|
||||
accounts map[string]*types.AccountWrapper
|
||||
storages map[string]map[string]*types.StorageWrapper
|
||||
}
|
||||
initedData map[common.Address]*types.AccountWrapper
|
||||
|
||||
// help to track all accounts being touched, and provide the
|
||||
// completed account status for storage updating
|
||||
traced map[string]*types.AccountWrapper
|
||||
|
||||
opAccNonce map[string]*types.AccountWrapper
|
||||
opAccBalance map[string]*types.AccountWrapper
|
||||
opAccCodeHash map[string]*types.AccountWrapper
|
||||
opStorage map[string]map[string]*types.StorageWrapper
|
||||
}
|
||||
|
||||
func NewSimpleOrderer() *simpleOrderer { return &simpleOrderer{} }
|
||||
|
||||
func NewRWTblOrderer(inited map[common.Address]*types.StateAccount) *rwTblOrderer {
|
||||
|
||||
initedAcc := make(map[common.Address]*types.AccountWrapper)
|
||||
for addr, data := range inited {
|
||||
if data == nil {
|
||||
initedAcc[addr] = &types.AccountWrapper{
|
||||
Address: addr,
|
||||
Balance: (*hexutil.Big)(big.NewInt(0)),
|
||||
}
|
||||
} else {
|
||||
bl := data.Balance
|
||||
if bl == nil {
|
||||
bl = big.NewInt(0)
|
||||
}
|
||||
|
||||
initedAcc[addr] = &types.AccountWrapper{
|
||||
Address: addr,
|
||||
Nonce: data.Nonce,
|
||||
Balance: (*hexutil.Big)(bl),
|
||||
KeccakCodeHash: common.BytesToHash(data.KeccakCodeHash),
|
||||
PoseidonCodeHash: common.BytesToHash(data.PoseidonCodeHash),
|
||||
CodeSize: data.CodeSize,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return &rwTblOrderer{
|
||||
initedData: initedAcc,
|
||||
traced: make(map[string]*types.AccountWrapper),
|
||||
opAccNonce: make(map[string]*types.AccountWrapper),
|
||||
opAccBalance: make(map[string]*types.AccountWrapper),
|
||||
opAccCodeHash: make(map[string]*types.AccountWrapper),
|
||||
opStorage: make(map[string]map[string]*types.StorageWrapper),
|
||||
}
|
||||
}
|
||||
|
||||
func (od *rwTblOrderer) readonly(mode bool) {
|
||||
if mode {
|
||||
if od.readOnly == 0 {
|
||||
od.readOnlySnapshot.accounts = make(map[string]*types.AccountWrapper)
|
||||
od.readOnlySnapshot.storages = make(map[string]map[string]*types.StorageWrapper)
|
||||
}
|
||||
od.readOnly += 1
|
||||
} else if od.readOnly == 0 {
|
||||
panic("unexpected readonly mode stack pop")
|
||||
} else {
|
||||
od.readOnly -= 1
|
||||
if od.readOnly == 0 {
|
||||
for addrS, st := range od.readOnlySnapshot.accounts {
|
||||
od.absorb(st)
|
||||
if m, existed := od.readOnlySnapshot.storages[addrS]; existed {
|
||||
for _, stg := range m {
|
||||
st.Storage = stg
|
||||
od.absorbStorage(st, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (od *rwTblOrderer) absorbStorage(st *types.AccountWrapper, before *types.StorageWrapper) {
|
||||
if st.Storage == nil {
|
||||
panic("do not call absorbStorage ")
|
||||
}
|
||||
|
||||
od.absorb(st)
|
||||
addrStr := st.Address.String()
|
||||
|
||||
if stg := st.Storage; stg != nil {
|
||||
m, existed := od.opStorage[addrStr]
|
||||
if !existed {
|
||||
m = make(map[string]*types.StorageWrapper)
|
||||
od.opStorage[addrStr] = m
|
||||
}
|
||||
|
||||
// key must be unified into 32 bytes
|
||||
keyBytes := hexutil.MustDecode(stg.Key)
|
||||
keyStr := common.BytesToHash(keyBytes).String()
|
||||
|
||||
// trace every "touched" status for readOnly
|
||||
if od.readOnly > 0 {
|
||||
m, existed := od.readOnlySnapshot.storages[addrStr]
|
||||
if !existed {
|
||||
m = make(map[string]*types.StorageWrapper)
|
||||
od.readOnlySnapshot.storages[addrStr] = m
|
||||
}
|
||||
if _, hashTraced := m[keyStr]; !hashTraced {
|
||||
if before != nil {
|
||||
m[keyStr] = before
|
||||
} else {
|
||||
m[keyStr] = stg
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
m[keyStr] = stg
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (od *rwTblOrderer) absorb(st *types.AccountWrapper) {
|
||||
|
||||
initedRef, existed := od.initedData[st.Address]
|
||||
if !existed {
|
||||
panic("encounter unprepared status")
|
||||
}
|
||||
|
||||
addrStr := st.Address.String()
|
||||
|
||||
// trace every "touched" status for readOnly
|
||||
if od.readOnly > 0 {
|
||||
snapShot, existed := od.traced[addrStr]
|
||||
if !existed {
|
||||
snapShot = initedRef
|
||||
}
|
||||
|
||||
if _, hasTraced := od.readOnlySnapshot.accounts[addrStr]; !hasTraced {
|
||||
od.readOnlySnapshot.accounts[addrStr] = copyAccountState(snapShot)
|
||||
}
|
||||
}
|
||||
|
||||
if isDeletedAccount(st) {
|
||||
// for account delete, made a safer data for status
|
||||
st = &types.AccountWrapper{
|
||||
Address: st.Address,
|
||||
Balance: (*hexutil.Big)(big.NewInt(0)),
|
||||
}
|
||||
}
|
||||
|
||||
od.traced[addrStr] = st
|
||||
|
||||
// notice there would be at least one entry for all 3 fields when accessing an address
|
||||
// this may caused extract "read" op in mpt circuit which has no corresponding one in rwtable
|
||||
// we can avoid it unless obtaining more tips from the understanding of opcode
|
||||
// but it would be ok if we have adopted the new lookup way (root_prev, root_cur) under discussion:
|
||||
// https://github.com/privacy-scaling-explorations/zkevm-specs/issues/217
|
||||
|
||||
if traced, existed := od.opAccNonce[addrStr]; !existed {
|
||||
traced = copyAccountState(st)
|
||||
traced.Balance = initedRef.Balance
|
||||
traced.KeccakCodeHash = initedRef.KeccakCodeHash
|
||||
traced.PoseidonCodeHash = initedRef.PoseidonCodeHash
|
||||
traced.CodeSize = initedRef.CodeSize
|
||||
traced.Storage = nil
|
||||
od.opAccNonce[addrStr] = traced
|
||||
} else {
|
||||
traced.Nonce = st.Nonce
|
||||
}
|
||||
|
||||
if traced, existed := od.opAccBalance[addrStr]; !existed {
|
||||
traced = copyAccountState(st)
|
||||
traced.KeccakCodeHash = initedRef.KeccakCodeHash
|
||||
traced.PoseidonCodeHash = initedRef.PoseidonCodeHash
|
||||
traced.CodeSize = initedRef.CodeSize
|
||||
traced.Storage = nil
|
||||
od.opAccBalance[addrStr] = traced
|
||||
} else {
|
||||
traced.Nonce = st.Nonce
|
||||
traced.Balance = st.Balance
|
||||
}
|
||||
|
||||
if traced, existed := od.opAccCodeHash[addrStr]; !existed {
|
||||
traced = copyAccountState(st)
|
||||
traced.Storage = nil
|
||||
od.opAccCodeHash[addrStr] = traced
|
||||
} else {
|
||||
traced.Nonce = st.Nonce
|
||||
traced.Balance = st.Balance
|
||||
traced.KeccakCodeHash = st.KeccakCodeHash
|
||||
traced.PoseidonCodeHash = st.PoseidonCodeHash
|
||||
traced.CodeSize = st.CodeSize
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (od *rwTblOrderer) end_absorb() opIterator {
|
||||
// now sort every map by address / key
|
||||
// inited has collected all address, just sort address once
|
||||
sortedAddrs := make([]string, 0, len(od.traced))
|
||||
for addrs := range od.traced {
|
||||
sortedAddrs = append(sortedAddrs, addrs)
|
||||
}
|
||||
sort.Strings(sortedAddrs)
|
||||
|
||||
var iterNonce []*types.AccountWrapper
|
||||
var iterBalance []*types.AccountWrapper
|
||||
var iterCodeHash []*types.AccountWrapper
|
||||
var iterStorage []*types.AccountWrapper
|
||||
|
||||
for _, addrStr := range sortedAddrs {
|
||||
|
||||
if v, existed := od.opAccNonce[addrStr]; existed {
|
||||
iterNonce = append(iterNonce, v)
|
||||
}
|
||||
|
||||
if v, existed := od.opAccBalance[addrStr]; existed {
|
||||
iterBalance = append(iterBalance, v)
|
||||
}
|
||||
|
||||
if v, existed := od.opAccCodeHash[addrStr]; existed {
|
||||
iterCodeHash = append(iterCodeHash, v)
|
||||
}
|
||||
|
||||
if stgM, existed := od.opStorage[addrStr]; existed {
|
||||
|
||||
tracedStatus := od.traced[addrStr]
|
||||
if tracedStatus == nil {
|
||||
panic("missed traced status found in storage slot")
|
||||
}
|
||||
|
||||
sortedKeys := make([]string, 0, len(stgM))
|
||||
for key := range stgM {
|
||||
sortedKeys = append(sortedKeys, key)
|
||||
}
|
||||
sort.Strings(sortedKeys)
|
||||
|
||||
for _, key := range sortedKeys {
|
||||
st := copyAccountState(tracedStatus)
|
||||
st.Storage = stgM[key]
|
||||
iterStorage = append(iterStorage, st)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var finalRet []opIterator
|
||||
for _, arr := range [][]*types.AccountWrapper{iterNonce, iterBalance, iterCodeHash, iterStorage} {
|
||||
wrappedIter := iterateOp(arr)
|
||||
finalRet = append(finalRet, &wrappedIter)
|
||||
}
|
||||
|
||||
wrappedRet := multiOpIterator(finalRet)
|
||||
return &wrappedRet
|
||||
}
|
||||
59
trie/zkproof/types.go
Normal file
59
trie/zkproof/types.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package zkproof
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
type MPTWitnessType int
|
||||
|
||||
const (
|
||||
MPTWitnessNothing MPTWitnessType = iota
|
||||
MPTWitnessNatural
|
||||
MPTWitnessRWTbl
|
||||
)
|
||||
|
||||
// SMTPathNode represent a node in the SMT Path, all hash is saved by the present of
|
||||
// zktype.Hash
|
||||
type SMTPathNode struct {
|
||||
Value hexutil.Bytes `json:"value"`
|
||||
Sibling hexutil.Bytes `json:"sibling"`
|
||||
}
|
||||
|
||||
// SMTPath is the whole path of SMT
|
||||
type SMTPath struct {
|
||||
KeyPathPart *hexutil.Big `json:"pathPart"` //the path part in key
|
||||
Root hexutil.Bytes `json:"root"`
|
||||
Path []SMTPathNode `json:"path,omitempty"` //path start from top
|
||||
Leaf *SMTPathNode `json:"leaf,omitempty"` //would be omitted for empty leaf, the sibling indicate key
|
||||
}
|
||||
|
||||
// StateAccount is the represent of StateAccount in L2 circuit
|
||||
// Notice in L2 we have different hash scheme against StateAccount.MarshalByte
|
||||
type StateAccount struct {
|
||||
Nonce int `json:"nonce"`
|
||||
Balance *hexutil.Big `json:"balance"` //just the common hex expression of integer (big-endian)
|
||||
KeccakCodeHash hexutil.Bytes `json:"keccakCodeHash,omitempty"`
|
||||
PoseidonCodeHash hexutil.Bytes `json:"poseidonCodeHash,omitempty"`
|
||||
CodeSize uint64 `json:"codeSize,omitempty"`
|
||||
}
|
||||
|
||||
// StateStorage is the represent of a stored key-value pair for specified account
|
||||
type StateStorage struct {
|
||||
Key hexutil.Bytes `json:"key"` //notice this is the preimage of storage key
|
||||
Value hexutil.Bytes `json:"value"`
|
||||
}
|
||||
|
||||
// StorageTrace record the updating on state trie and (if changed) account trie
|
||||
// represent by the [before, after] updating of SMTPath amont tries and Account
|
||||
type StorageTrace struct {
|
||||
// which log the trace is responded for, -1 indicate not caused
|
||||
// by opcode (like gasRefund, coinbase, setNonce, etc)
|
||||
Address hexutil.Bytes `json:"address"`
|
||||
AccountKey hexutil.Bytes `json:"accountKey"`
|
||||
AccountPath [2]*SMTPath `json:"accountPath"`
|
||||
AccountUpdate [2]*StateAccount `json:"accountUpdate"`
|
||||
StateKey hexutil.Bytes `json:"stateKey,omitempty"`
|
||||
CommonStateRoot hexutil.Bytes `json:"commonStateRoot,omitempty"` //CommonStateRoot is used if there is no update on state storage
|
||||
StatePath [2]*SMTPath `json:"statePath,omitempty"`
|
||||
StateUpdate [2]*StateStorage `json:"stateUpdate,omitempty"`
|
||||
}
|
||||
826
trie/zkproof/writer.go
Normal file
826
trie/zkproof/writer.go
Normal file
|
|
@ -0,0 +1,826 @@
|
|||
package zkproof
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
zktrie "github.com/scroll-tech/zktrie/trie"
|
||||
zkt "github.com/scroll-tech/zktrie/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
type ProofList [][]byte
|
||||
|
||||
func (n *ProofList) Put(key []byte, value []byte) error {
|
||||
*n = append(*n, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *ProofList) Delete(key []byte) error {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func addressToKey(addr common.Address) *zkt.Hash {
|
||||
var preImage zkt.Byte32
|
||||
copy(preImage[:], addr.Bytes())
|
||||
|
||||
h, err := preImage.Hash()
|
||||
if err != nil {
|
||||
log.Error("hash failure", "preImage", hexutil.Encode(preImage[:]))
|
||||
return nil
|
||||
}
|
||||
return zkt.NewHashFromBigInt(h)
|
||||
}
|
||||
|
||||
// resume the proof bytes into db and return the leaf node
|
||||
func resumeProofs(proof []hexutil.Bytes, db ethdb.Database) *zktrie.Node {
|
||||
for _, buf := range proof {
|
||||
n, err := zktrie.DecodeSMTProof(buf)
|
||||
if err != nil {
|
||||
log.Warn("decode proof string fail", "error", err)
|
||||
} else if n != nil {
|
||||
hash, err := n.NodeHash()
|
||||
if err != nil {
|
||||
log.Warn("node has no valid node hash", "error", err)
|
||||
} else {
|
||||
//notice: must consistent with trie/merkletree.go
|
||||
bt := hash[:]
|
||||
db.Put(bt, buf)
|
||||
if n.Type == zktrie.NodeTypeLeaf_New || n.Type == zktrie.NodeTypeEmpty_New {
|
||||
return n
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// we have a trick here which suppose the proof array include all middle nodes along the
|
||||
// whole path in sequence, from root to leaf
|
||||
func decodeProofForMPTPath(proof ProofList, path *SMTPath) {
|
||||
var lastNode *zktrie.Node
|
||||
keyPath := big.NewInt(0)
|
||||
path.KeyPathPart = (*hexutil.Big)(keyPath)
|
||||
|
||||
keyCounter := big.NewInt(1)
|
||||
|
||||
for _, buf := range proof {
|
||||
n, err := zktrie.DecodeSMTProof(buf)
|
||||
if err != nil {
|
||||
log.Warn("decode proof string fail", "error", err)
|
||||
} else if n != nil {
|
||||
hash, err := n.NodeHash()
|
||||
if err != nil {
|
||||
log.Warn("node has no valid node hash", "error", err)
|
||||
return
|
||||
}
|
||||
if lastNode == nil {
|
||||
// notice: use little-endian represent inside Hash ([:] or Byte32())
|
||||
path.Root = hash[:]
|
||||
} else {
|
||||
if bytes.Equal(hash[:], lastNode.ChildL[:]) {
|
||||
path.Path = append(path.Path, SMTPathNode{
|
||||
Value: hash[:],
|
||||
Sibling: lastNode.ChildR[:],
|
||||
})
|
||||
} else if bytes.Equal(hash[:], lastNode.ChildR[:]) {
|
||||
path.Path = append(path.Path, SMTPathNode{
|
||||
Value: hash[:],
|
||||
Sibling: lastNode.ChildL[:],
|
||||
})
|
||||
keyPath.Add(keyPath, keyCounter)
|
||||
} else {
|
||||
panic("Unexpected proof form")
|
||||
}
|
||||
keyCounter.Mul(keyCounter, big.NewInt(2))
|
||||
}
|
||||
switch n.Type {
|
||||
case zktrie.NodeTypeBranch_0, zktrie.NodeTypeBranch_1, zktrie.NodeTypeBranch_2, zktrie.NodeTypeBranch_3:
|
||||
lastNode = n
|
||||
case zktrie.NodeTypeLeaf_New:
|
||||
vhash, _ := n.ValueHash()
|
||||
path.Leaf = &SMTPathNode{
|
||||
//here we just return the inner represent of hash (little endian, reversed byte order to common hash)
|
||||
Value: vhash[:],
|
||||
Sibling: n.NodeKey[:],
|
||||
}
|
||||
//sanity check
|
||||
keyPart := keyPath.Bytes()
|
||||
for i, b := range keyPart {
|
||||
ri := len(keyPart) - i
|
||||
cb := path.Leaf.Sibling[ri-1] //notice the output is little-endian
|
||||
if b&cb != b {
|
||||
panic(fmt.Errorf("path key not match: part is %x but key is %x", keyPart, []byte(path.Leaf.Sibling[:])))
|
||||
}
|
||||
}
|
||||
return
|
||||
case zktrie.NodeTypeEmpty_New:
|
||||
return
|
||||
default:
|
||||
panic(fmt.Errorf("unknown node type %d", n.Type))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panic("Unexpected finished here")
|
||||
}
|
||||
|
||||
type zktrieProofWriter struct {
|
||||
db *trie.ZktrieDatabase
|
||||
tracingZktrie *trie.ZkTrie
|
||||
tracingStorageTries map[common.Address]*trie.ZkTrie
|
||||
tracingAccounts map[common.Address]*types.StateAccount
|
||||
}
|
||||
|
||||
func (wr *zktrieProofWriter) TracingAccounts() map[common.Address]*types.StateAccount {
|
||||
return wr.tracingAccounts
|
||||
}
|
||||
|
||||
func NewZkTrieProofWriter(storage *types.StorageTrace) (*zktrieProofWriter, error) {
|
||||
underlayerDb := rawdb.NewMemoryDatabase()
|
||||
zkDb := trie.NewZktrieDatabase(underlayerDb)
|
||||
accounts := make(map[common.Address]*types.StateAccount)
|
||||
|
||||
// resuming proof bytes to underlayerDb
|
||||
for addrs, proof := range storage.Proofs {
|
||||
if n := resumeProofs(proof, underlayerDb); n != nil {
|
||||
addr := common.HexToAddress(addrs)
|
||||
if n.Type == zktrie.NodeTypeEmpty_New {
|
||||
accounts[addr] = nil
|
||||
} else if acc, err := types.UnmarshalStateAccount(n.Data()); err == nil {
|
||||
if bytes.Equal(n.NodeKey[:], addressToKey(addr)[:]) {
|
||||
accounts[addr] = acc
|
||||
} else {
|
||||
// should still mark the address as being trace (data not existed yet)
|
||||
accounts[addr] = nil
|
||||
}
|
||||
|
||||
} else {
|
||||
return nil, fmt.Errorf("decode account bytes fail: %s, raw data [%x]", err, n.Data())
|
||||
}
|
||||
|
||||
} else {
|
||||
return nil, fmt.Errorf("can not resume proof for address %s", addrs)
|
||||
}
|
||||
}
|
||||
|
||||
storages := make(map[common.Address]*trie.ZkTrie)
|
||||
|
||||
for addrs, stgLists := range storage.StorageProofs {
|
||||
addr := common.HexToAddress(addrs)
|
||||
accState, existed := accounts[addr]
|
||||
if !existed {
|
||||
// trace is malformed but currently we just warn about that
|
||||
log.Warn("no account state found for this addr, mal records", "address", addrs)
|
||||
continue
|
||||
} else if accState == nil {
|
||||
// create an empty zktrie for uninit address
|
||||
storages[addr], _ = trie.NewZkTrie(common.Hash{}, zkDb)
|
||||
continue
|
||||
}
|
||||
|
||||
for keys, proof := range stgLists {
|
||||
if n := resumeProofs(proof, underlayerDb); n != nil {
|
||||
var err error
|
||||
storages[addr], err = trie.NewZkTrie(accState.Root, zkDb)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("zktrie create failure for storage in addr <%s>: %s, (root %s)", addrs, err, accState.Root)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("can not resume proof for storage %s@%s", keys, addrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, delProof := range storage.DeletionProofs {
|
||||
n, err := zktrie.DecodeSMTProof(delProof)
|
||||
if err != nil {
|
||||
log.Warn("decode delproof string fail", "error", err, "node", delProof)
|
||||
} else if n != nil {
|
||||
hash, err := n.NodeHash()
|
||||
if err != nil {
|
||||
log.Warn("node has no valid node hash", "error", err)
|
||||
} else {
|
||||
//notice: must consistent with trie/merkletree.go
|
||||
bt := hash[:]
|
||||
underlayerDb.Put(bt, delProof)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zktrie, err := trie.NewZkTrie(
|
||||
storage.RootBefore,
|
||||
trie.NewZktrieDatabase(underlayerDb),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("zktrie create failure: %s", err)
|
||||
}
|
||||
|
||||
// sanity check
|
||||
if !bytes.Equal(zktrie.Hash().Bytes(), storage.RootBefore.Bytes()) {
|
||||
return nil, fmt.Errorf("unmatch init trie hash: expected %x but has %x", storage.RootBefore.Bytes(), zktrie.Hash().Bytes())
|
||||
}
|
||||
|
||||
return &zktrieProofWriter{
|
||||
db: zkDb,
|
||||
tracingZktrie: zktrie,
|
||||
tracingAccounts: accounts,
|
||||
tracingStorageTries: storages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
posSSTOREBefore = 0
|
||||
posCREATE = 0
|
||||
posCREATEAfter = 1
|
||||
posCALL = 2
|
||||
posSTATICCALL = 0
|
||||
|
||||
// posSELFDESTRUCT = 2
|
||||
)
|
||||
|
||||
func getAccountState(l *types.StructLogRes, pos int) *types.AccountWrapper {
|
||||
if exData := l.ExtraData; exData == nil {
|
||||
return nil
|
||||
} else if len(exData.StateList) < pos {
|
||||
return nil
|
||||
} else {
|
||||
return exData.StateList[pos]
|
||||
}
|
||||
}
|
||||
|
||||
func copyAccountState(st *types.AccountWrapper) *types.AccountWrapper {
|
||||
var stg *types.StorageWrapper
|
||||
if st.Storage != nil {
|
||||
stg = &types.StorageWrapper{
|
||||
Key: st.Storage.Key,
|
||||
Value: st.Storage.Value,
|
||||
}
|
||||
}
|
||||
|
||||
return &types.AccountWrapper{
|
||||
Nonce: st.Nonce,
|
||||
Balance: (*hexutil.Big)(big.NewInt(0).Set(st.Balance.ToInt())),
|
||||
KeccakCodeHash: st.KeccakCodeHash,
|
||||
PoseidonCodeHash: st.PoseidonCodeHash,
|
||||
CodeSize: st.CodeSize,
|
||||
Address: st.Address,
|
||||
Storage: stg,
|
||||
}
|
||||
}
|
||||
|
||||
func isDeletedAccount(state *types.AccountWrapper) bool {
|
||||
return state.Nonce == 0 && bytes.Equal(state.KeccakCodeHash.Bytes(), common.Hash{}.Bytes())
|
||||
}
|
||||
|
||||
func getAccountDataFromLogState(state *types.AccountWrapper) *types.StateAccount {
|
||||
if isDeletedAccount(state) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.StateAccount{
|
||||
Nonce: state.Nonce,
|
||||
Balance: (*big.Int)(state.Balance),
|
||||
KeccakCodeHash: state.KeccakCodeHash.Bytes(),
|
||||
PoseidonCodeHash: state.PoseidonCodeHash.Bytes(),
|
||||
CodeSize: state.CodeSize,
|
||||
// Root omitted intentionally
|
||||
}
|
||||
}
|
||||
|
||||
// for sanity check
|
||||
func verifyAccount(addr common.Address, data *types.StateAccount, leaf *SMTPathNode) error {
|
||||
if leaf == nil {
|
||||
if data != nil {
|
||||
return fmt.Errorf("path has no corresponding leaf for account")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
addrKey := addressToKey(addr)
|
||||
if !bytes.Equal(addrKey[:], leaf.Sibling) {
|
||||
if data != nil {
|
||||
return fmt.Errorf("unmatch leaf node in address: %s", addr)
|
||||
}
|
||||
} else if data != nil {
|
||||
arr, flag := data.MarshalFields()
|
||||
h, err := zkt.HandlingElemsAndByte32(flag, arr)
|
||||
//log.Info("sanity check acc before", "addr", addr.String(), "key", leaf.Sibling.Text(16), "hash", h.Text(16))
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("fail to hash account: %v", err)
|
||||
}
|
||||
if !bytes.Equal(h[:], leaf.Value) {
|
||||
return fmt.Errorf("unmatch data in leaf for address %s", addr)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// for sanity check
|
||||
func verifyStorage(key *zkt.Byte32, data *zkt.Byte32, leaf *SMTPathNode) error {
|
||||
emptyData := bytes.Equal(data[:], common.Hash{}.Bytes())
|
||||
|
||||
if leaf == nil {
|
||||
if !emptyData {
|
||||
return fmt.Errorf("path has no corresponding leaf for storage")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
keyHash, err := key.Hash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(zkt.NewHashFromBigInt(keyHash)[:], leaf.Sibling) {
|
||||
if !emptyData {
|
||||
return fmt.Errorf("unmatch leaf node in storage: %x", key[:])
|
||||
}
|
||||
} else {
|
||||
h, err := data.Hash()
|
||||
//log.Info("sanity check acc before", "addr", addr.String(), "key", leaf.Sibling.Text(16), "hash", h.Text(16))
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("fail to hash data: %v", err)
|
||||
}
|
||||
if !bytes.Equal(zkt.NewHashFromBigInt(h)[:], leaf.Value) {
|
||||
return fmt.Errorf("unmatch data in leaf for storage %x", key[:])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// update traced account state, and return the corresponding trace object which
|
||||
// is still opened for more infos
|
||||
// the updated accData state is obtained by a closure which enable it being derived from current status
|
||||
func (w *zktrieProofWriter) traceAccountUpdate(addr common.Address, updateAccData func(*types.StateAccount) *types.StateAccount) (*StorageTrace, error) {
|
||||
out := new(StorageTrace)
|
||||
//account trie
|
||||
out.Address = addr.Bytes()
|
||||
out.AccountPath = [2]*SMTPath{{}, {}}
|
||||
//fill dummy
|
||||
out.AccountUpdate = [2]*StateAccount{}
|
||||
|
||||
accDataBefore, existed := w.tracingAccounts[addr]
|
||||
if !existed {
|
||||
//sanity check
|
||||
panic(fmt.Errorf("code do not add initialized status for account %s", addr))
|
||||
}
|
||||
|
||||
var proof ProofList
|
||||
s_key, _ := zkt.ToSecureKeyBytes(addr.Bytes())
|
||||
if err := w.tracingZktrie.Prove(s_key.Bytes(), &proof); err != nil {
|
||||
return nil, fmt.Errorf("prove BEFORE state fail: %s", err)
|
||||
}
|
||||
|
||||
decodeProofForMPTPath(proof, out.AccountPath[0])
|
||||
if err := verifyAccount(addr, accDataBefore, out.AccountPath[0].Leaf); err != nil {
|
||||
panic(fmt.Errorf("code fail to trace account status correctly: %s", err))
|
||||
}
|
||||
if accDataBefore != nil {
|
||||
// we have ensured the nBefore has a key corresponding to the query one
|
||||
out.AccountKey = out.AccountPath[0].Leaf.Sibling
|
||||
out.AccountUpdate[0] = &StateAccount{
|
||||
Nonce: int(accDataBefore.Nonce),
|
||||
Balance: (*hexutil.Big)(big.NewInt(0).Set(accDataBefore.Balance)),
|
||||
KeccakCodeHash: accDataBefore.KeccakCodeHash,
|
||||
PoseidonCodeHash: accDataBefore.PoseidonCodeHash,
|
||||
CodeSize: accDataBefore.CodeSize,
|
||||
}
|
||||
}
|
||||
|
||||
accData := updateAccData(accDataBefore)
|
||||
if accData != nil {
|
||||
out.AccountUpdate[1] = &StateAccount{
|
||||
Nonce: int(accData.Nonce),
|
||||
Balance: (*hexutil.Big)(big.NewInt(0).Set(accData.Balance)),
|
||||
KeccakCodeHash: accData.KeccakCodeHash,
|
||||
PoseidonCodeHash: accData.PoseidonCodeHash,
|
||||
CodeSize: accData.CodeSize,
|
||||
}
|
||||
}
|
||||
|
||||
if accData != nil {
|
||||
if err := w.tracingZktrie.TryUpdateAccount(addr.Bytes32(), accData); err != nil {
|
||||
return nil, fmt.Errorf("update zktrie account state fail: %s", err)
|
||||
}
|
||||
w.tracingAccounts[addr] = accData
|
||||
} else if accDataBefore != nil {
|
||||
if err := w.tracingZktrie.TryDelete(addr.Bytes32()); err != nil {
|
||||
return nil, fmt.Errorf("delete zktrie account state fail: %s", err)
|
||||
}
|
||||
w.tracingAccounts[addr] = nil
|
||||
} // notice if both before/after is nil, we do not touch zktrie
|
||||
|
||||
proof = ProofList{}
|
||||
if err := w.tracingZktrie.Prove(s_key.Bytes(), &proof); err != nil {
|
||||
return nil, fmt.Errorf("prove AFTER state fail: %s", err)
|
||||
}
|
||||
|
||||
decodeProofForMPTPath(proof, out.AccountPath[1])
|
||||
if err := verifyAccount(addr, accData, out.AccountPath[1].Leaf); err != nil {
|
||||
panic(fmt.Errorf("state AFTER has no valid account: %s", err))
|
||||
}
|
||||
if accData != nil {
|
||||
if out.AccountKey == nil {
|
||||
out.AccountKey = out.AccountPath[1].Leaf.Sibling[:]
|
||||
}
|
||||
//now accountKey must has been filled
|
||||
}
|
||||
|
||||
// notice we have change that no leaf (account data) exist in either before or after,
|
||||
// for that case we had to calculate the nodeKey here
|
||||
if out.AccountKey == nil {
|
||||
word := zkt.NewByte32FromBytesPaddingZero(addr.Bytes())
|
||||
k, err := word.Hash()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("unexpected hash error for address: %s", err))
|
||||
}
|
||||
kHash := zkt.NewHashFromBigInt(k)
|
||||
out.AccountKey = hexutil.Bytes(kHash[:])
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// update traced storage state, and return the corresponding trace object
|
||||
func (w *zktrieProofWriter) traceStorageUpdate(addr common.Address, key, value []byte) (*StorageTrace, error) {
|
||||
trie := w.tracingStorageTries[addr]
|
||||
if trie == nil {
|
||||
return nil, fmt.Errorf("no trace storage trie for %s", addr)
|
||||
}
|
||||
|
||||
statePath := [2]*SMTPath{{}, {}}
|
||||
stateUpdate := [2]*StateStorage{}
|
||||
|
||||
storeKey := zkt.NewByte32FromBytesPaddingZero(common.BytesToHash(key).Bytes())
|
||||
storeValueBefore := trie.Get(storeKey[:])
|
||||
storeValue := zkt.NewByte32FromBytes(value)
|
||||
valZero := zkt.Byte32{}
|
||||
|
||||
if storeValueBefore != nil && !bytes.Equal(storeValueBefore[:], common.Hash{}.Bytes()) {
|
||||
stateUpdate[0] = &StateStorage{
|
||||
Key: storeKey.Bytes(),
|
||||
Value: storeValueBefore,
|
||||
}
|
||||
}
|
||||
|
||||
var storageBeforeProof, storageAfterProof ProofList
|
||||
s_key, _ := zkt.ToSecureKeyBytes(storeKey.Bytes())
|
||||
if err := trie.Prove(s_key.Bytes(), &storageBeforeProof); err != nil {
|
||||
return nil, fmt.Errorf("prove BEFORE storage state fail: %s", err)
|
||||
}
|
||||
|
||||
decodeProofForMPTPath(storageBeforeProof, statePath[0])
|
||||
if err := verifyStorage(storeKey, zkt.NewByte32FromBytes(storeValueBefore), statePath[0].Leaf); err != nil {
|
||||
panic(fmt.Errorf("storage BEFORE has no valid data: %s (%v)", err, statePath[0]))
|
||||
}
|
||||
|
||||
if !bytes.Equal(storeValue.Bytes(), common.Hash{}.Bytes()) {
|
||||
if err := trie.TryUpdate(storeKey.Bytes(), storeValue.Bytes()); err != nil {
|
||||
return nil, fmt.Errorf("update zktrie storage fail: %s", err)
|
||||
}
|
||||
stateUpdate[1] = &StateStorage{
|
||||
Key: storeKey.Bytes(),
|
||||
Value: storeValue.Bytes(),
|
||||
}
|
||||
} else {
|
||||
if err := trie.TryDelete(storeKey.Bytes()); err != nil {
|
||||
return nil, fmt.Errorf("delete zktrie storage fail: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := trie.Prove(s_key.Bytes(), &storageAfterProof); err != nil {
|
||||
return nil, fmt.Errorf("prove AFTER storage state fail: %s", err)
|
||||
}
|
||||
decodeProofForMPTPath(storageAfterProof, statePath[1])
|
||||
if err := verifyStorage(storeKey, storeValue, statePath[1].Leaf); err != nil {
|
||||
panic(fmt.Errorf("storage AFTER has no valid data: %s (%v)", err, statePath[1]))
|
||||
}
|
||||
|
||||
out, err := w.traceAccountUpdate(addr,
|
||||
func(acc *types.StateAccount) *types.StateAccount {
|
||||
if acc == nil {
|
||||
// in case we read an unexist account
|
||||
if !bytes.Equal(valZero.Bytes(), value) {
|
||||
panic(fmt.Errorf("write to an unexist account [%s] which is not allowed", addr))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//sanity check
|
||||
if accRootFromState := zkt.ReverseByteOrder(statePath[0].Root); !bytes.Equal(acc.Root[:], accRootFromState) {
|
||||
panic(fmt.Errorf("unexpected storage root before: [%s] vs [%x]", acc.Root, accRootFromState))
|
||||
}
|
||||
return &types.StateAccount{
|
||||
Nonce: acc.Nonce,
|
||||
Balance: acc.Balance,
|
||||
Root: common.BytesToHash(zkt.ReverseByteOrder(statePath[1].Root)),
|
||||
KeccakCodeHash: acc.KeccakCodeHash,
|
||||
PoseidonCodeHash: acc.PoseidonCodeHash,
|
||||
CodeSize: acc.CodeSize,
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("update account %s in SSTORE fail: %s", addr, err)
|
||||
}
|
||||
|
||||
if stateUpdate[1] != nil {
|
||||
out.StateKey = statePath[1].Leaf.Sibling
|
||||
} else if stateUpdate[0] != nil {
|
||||
out.StateKey = statePath[0].Leaf.Sibling
|
||||
} else {
|
||||
// it occurs when we are handling SLOAD with non-exist value
|
||||
// still no pretty idea, had to touch the internal behavior in zktrie ....
|
||||
if h, err := storeKey.Hash(); err != nil {
|
||||
return nil, fmt.Errorf("hash storekey fail: %s", err)
|
||||
} else {
|
||||
out.StateKey = zkt.NewHashFromBigInt(h)[:]
|
||||
}
|
||||
stateUpdate[1] = &StateStorage{
|
||||
Key: storeKey.Bytes(),
|
||||
Value: valZero.Bytes(),
|
||||
}
|
||||
stateUpdate[0] = stateUpdate[1]
|
||||
}
|
||||
|
||||
out.StatePath = statePath
|
||||
out.StateUpdate = stateUpdate
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (w *zktrieProofWriter) HandleNewState(accountState *types.AccountWrapper) (*StorageTrace, error) {
|
||||
if accountState.Storage != nil {
|
||||
storeAddr := hexutil.MustDecode(accountState.Storage.Key)
|
||||
storeValue := hexutil.MustDecode(accountState.Storage.Value)
|
||||
return w.traceStorageUpdate(accountState.Address, storeAddr, storeValue)
|
||||
} else {
|
||||
var stateRoot common.Hash
|
||||
accData := getAccountDataFromLogState(accountState)
|
||||
|
||||
out, err := w.traceAccountUpdate(accountState.Address, func(accBefore *types.StateAccount) *types.StateAccount {
|
||||
if accBefore != nil {
|
||||
stateRoot = accBefore.Root
|
||||
}
|
||||
// we need to restore stateRoot from before
|
||||
if accData != nil {
|
||||
accData.Root = stateRoot
|
||||
}
|
||||
return accData
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("update account state %s fail: %s", accountState.Address, err)
|
||||
}
|
||||
|
||||
hash := zkt.NewHashFromBytes(stateRoot[:])
|
||||
out.CommonStateRoot = hash[:]
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
|
||||
func handleLogs(od opOrderer, currentContract common.Address, logs []*types.StructLogRes) {
|
||||
logStack := []int{0}
|
||||
contractStack := map[int]common.Address{}
|
||||
callEnterAddress := currentContract
|
||||
|
||||
// now trace every OP which could cause changes on state:
|
||||
for i, sLog := range logs {
|
||||
//trace log stack by depth rather than scanning specified op
|
||||
if sl := len(logStack); sl < sLog.Depth {
|
||||
logStack = append(logStack, i)
|
||||
//update currentContract according to previous op
|
||||
contractStack[sl] = currentContract
|
||||
currentContract = callEnterAddress
|
||||
} else if sl > sLog.Depth {
|
||||
logStack = logStack[:sl-1]
|
||||
currentContract = contractStack[sLog.Depth]
|
||||
resumePos := logStack[len(logStack)-1]
|
||||
calledLog := logs[resumePos]
|
||||
|
||||
//no need to handle fail calling
|
||||
if calledLog.ExtraData != nil {
|
||||
if !calledLog.ExtraData.CallFailed {
|
||||
//reentry the last log which "cause" the calling, some handling may needed
|
||||
switch calledLog.Op {
|
||||
case "CREATE", "CREATE2":
|
||||
//addr, accDataBefore := getAccountDataFromProof(calledLog, posCALLBefore)
|
||||
od.absorb(getAccountState(calledLog, posCREATEAfter))
|
||||
}
|
||||
} else {
|
||||
od.readonly(false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logStack[sl-1] = i
|
||||
}
|
||||
//sanity check
|
||||
if len(logStack) != sLog.Depth {
|
||||
panic("tracking log stack failure")
|
||||
}
|
||||
callEnterAddress = currentContract
|
||||
|
||||
//check extra status for current op if it is a call
|
||||
if extraData := sLog.ExtraData; extraData != nil {
|
||||
if extraData.CallFailed || len(sLog.ExtraData.Caller) < 2 {
|
||||
// no enough caller data (2) is being capture indicate we are in an immediate failure
|
||||
// i.e. it fail before stack entry (like no enough balance for a "call with value"),
|
||||
// or we just not handle this calling op correctly yet
|
||||
|
||||
// for a failed option, now we just purpose nothing happens (FIXME: it is inconsentent with mpt_table)
|
||||
// except for CREATE, for which the callee's nonce would be increased
|
||||
switch sLog.Op {
|
||||
case "CREATE", "CREATE2":
|
||||
st := copyAccountState(extraData.Caller[0])
|
||||
st.Nonce += 1
|
||||
od.absorb(st)
|
||||
}
|
||||
}
|
||||
|
||||
if extraData.CallFailed {
|
||||
od.readonly(true)
|
||||
}
|
||||
// now trace caller's status first
|
||||
if caller := extraData.Caller; len(caller) >= 2 {
|
||||
od.absorb(caller[1])
|
||||
}
|
||||
}
|
||||
|
||||
switch sLog.Op {
|
||||
case "SELFDESTRUCT":
|
||||
// NOTE: this op code has been disabled so we treat it as nothing now
|
||||
|
||||
//in SELFDESTRUCT, a call on target address is made so the balance would be updated
|
||||
//in the last item
|
||||
//stateTarget := getAccountState(sLog, posSELFDESTRUCT)
|
||||
//od.absorb(stateTarget)
|
||||
//then build an "deleted state", only address and other are default
|
||||
//od.absorb(&types.AccountWrapper{Address: currentContract})
|
||||
|
||||
case "CREATE", "CREATE2":
|
||||
// notice in immediate failure we have no enough tracing in extraData
|
||||
if len(sLog.ExtraData.StateList) >= 2 {
|
||||
state := getAccountState(sLog, posCREATE)
|
||||
od.absorb(state)
|
||||
//update contract to CREATE addr
|
||||
callEnterAddress = state.Address
|
||||
}
|
||||
|
||||
case "CALL", "CALLCODE":
|
||||
// notice in immediate failure we have no enough tracing in extraData
|
||||
if len(sLog.ExtraData.StateList) >= 3 {
|
||||
state := getAccountState(sLog, posCALL)
|
||||
od.absorb(state)
|
||||
callEnterAddress = state.Address
|
||||
}
|
||||
case "STATICCALL":
|
||||
//static call has no update on target address (and no immediate failure?)
|
||||
callEnterAddress = getAccountState(sLog, posSTATICCALL).Address
|
||||
case "DELEGATECALL":
|
||||
|
||||
case "SLOAD":
|
||||
accountState := getAccountState(sLog, posSSTOREBefore)
|
||||
od.absorbStorage(accountState, nil)
|
||||
case "SSTORE":
|
||||
log.Debug("build SSTORE", "pc", sLog.Pc, "key", sLog.Stack[len(sLog.Stack)-1])
|
||||
accountState := copyAccountState(getAccountState(sLog, posSSTOREBefore))
|
||||
// notice the log only provide the value BEFORE store and it is not suitable for our protocol,
|
||||
// here we change it into value AFTER update
|
||||
before := accountState.Storage
|
||||
accountState.Storage = &types.StorageWrapper{
|
||||
Key: sLog.Stack[len(sLog.Stack)-1],
|
||||
Value: sLog.Stack[len(sLog.Stack)-2],
|
||||
}
|
||||
od.absorbStorage(accountState, before)
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func HandleTx(od opOrderer, txResult *types.ExecutionResult) {
|
||||
// the from state is read before tx is handled and nonce is added, we combine both
|
||||
preTxSt := copyAccountState(txResult.From)
|
||||
preTxSt.Nonce += 1
|
||||
od.absorb(preTxSt)
|
||||
|
||||
if txResult.Failed {
|
||||
od.readonly(true)
|
||||
}
|
||||
|
||||
var toAddr common.Address
|
||||
if state := txResult.AccountCreated; state != nil {
|
||||
od.absorb(state)
|
||||
toAddr = state.Address
|
||||
} else {
|
||||
toAddr = txResult.To.Address
|
||||
}
|
||||
|
||||
handleLogs(od, toAddr, txResult.StructLogs)
|
||||
if txResult.Failed {
|
||||
od.readonly(false)
|
||||
}
|
||||
|
||||
for _, state := range txResult.AccountsAfter {
|
||||
// special case: for suicide, the state has been captured in SELFDESTRUCT
|
||||
// and we skip it here
|
||||
if isDeletedAccount(state) {
|
||||
log.Debug("skip suicide address", "address", state.Address)
|
||||
continue
|
||||
}
|
||||
|
||||
od.absorb(state)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const defaultOrdererScheme = MPTWitnessRWTbl
|
||||
|
||||
var usedOrdererScheme = defaultOrdererScheme
|
||||
|
||||
func SetOrderScheme(t MPTWitnessType) { usedOrdererScheme = t }
|
||||
|
||||
// HandleBlockTrace only for backward compatibility
|
||||
func HandleBlockTrace(block *types.BlockTrace) ([]*StorageTrace, error) {
|
||||
return HandleBlockTraceEx(block, usedOrdererScheme)
|
||||
}
|
||||
|
||||
func HandleBlockTraceEx(block *types.BlockTrace, ordererScheme MPTWitnessType) ([]*StorageTrace, error) {
|
||||
writer, err := NewZkTrieProofWriter(block.StorageTrace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var od opOrderer
|
||||
switch ordererScheme {
|
||||
case MPTWitnessNothing:
|
||||
panic("should not come here when scheme is 0")
|
||||
case MPTWitnessNatural:
|
||||
od = &simpleOrderer{}
|
||||
case MPTWitnessRWTbl:
|
||||
od = NewRWTblOrderer(writer.tracingAccounts)
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized scheme %d", ordererScheme)
|
||||
}
|
||||
|
||||
for _, tx := range block.ExecutionResults {
|
||||
HandleTx(od, tx)
|
||||
}
|
||||
|
||||
// notice some coinbase addr (like all zero) is in fact not exist and should not be update
|
||||
// TODO: not a good solution, just for patch ...
|
||||
if coinbaseData := writer.tracingAccounts[block.Coinbase.Address]; coinbaseData != nil {
|
||||
od.absorb(block.Coinbase)
|
||||
}
|
||||
|
||||
opDisp := od.end_absorb()
|
||||
var outTrace []*StorageTrace
|
||||
|
||||
for op := opDisp.next(); op != nil; op = opDisp.next() {
|
||||
trace, err := writer.HandleNewState(op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outTrace = append(outTrace, trace)
|
||||
}
|
||||
|
||||
finalHash := writer.tracingZktrie.Hash()
|
||||
if !bytes.Equal(finalHash.Bytes(), block.StorageTrace.RootAfter.Bytes()) {
|
||||
return outTrace, fmt.Errorf("unmatch hash: [%x] vs [%x]", finalHash.Bytes(), block.StorageTrace.RootAfter.Bytes())
|
||||
}
|
||||
|
||||
return outTrace, nil
|
||||
|
||||
}
|
||||
|
||||
func FillBlockTraceForMPTWitness(order MPTWitnessType, block *types.BlockTrace) error {
|
||||
if order == MPTWitnessNothing {
|
||||
return nil
|
||||
}
|
||||
|
||||
trace, err := HandleBlockTraceEx(block, order)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg, err := json.Marshal(trace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawmsg := json.RawMessage(msg)
|
||||
|
||||
block.MPTWitness = &rawmsg
|
||||
return nil
|
||||
}
|
||||
183
trie/zktrie_deletionproof.go
Normal file
183
trie/zktrie_deletionproof.go
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
package trie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
zktrie "github.com/scroll-tech/zktrie/trie"
|
||||
zkt "github.com/scroll-tech/zktrie/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
// Pick Node from its hash directly from database, notice it has different
|
||||
// interface with the function of same name in `trie`
|
||||
func (t *ZkTrie) TryGetNode(nodeHash *zkt.Hash) (*zktrie.Node, error) {
|
||||
if bytes.Equal(nodeHash[:], zkt.HashZero[:]) {
|
||||
return zktrie.NewEmptyNode(), nil
|
||||
}
|
||||
nBytes, err := t.db.Get(nodeHash[:])
|
||||
if err == zktrie.ErrKeyNotFound {
|
||||
return nil, zktrie.ErrKeyNotFound
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return zktrie.NewNodeFromBytes(nBytes)
|
||||
}
|
||||
|
||||
type ProofTracer struct {
|
||||
*ZkTrie
|
||||
deletionTracer map[zkt.Hash]struct{}
|
||||
rawPaths map[string][]*zktrie.Node
|
||||
emptyTermPaths map[string][]*zktrie.Node
|
||||
}
|
||||
|
||||
// NewProofTracer create a proof tracer object
|
||||
func (t *ZkTrie) NewProofTracer() *ProofTracer {
|
||||
return &ProofTracer{
|
||||
ZkTrie: t,
|
||||
// always consider 0 is "deleted"
|
||||
deletionTracer: map[zkt.Hash]struct{}{zkt.HashZero: {}},
|
||||
rawPaths: make(map[string][]*zktrie.Node),
|
||||
emptyTermPaths: make(map[string][]*zktrie.Node),
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merge the input tracer into current and return current tracer
|
||||
func (t *ProofTracer) Merge(another *ProofTracer) *ProofTracer {
|
||||
|
||||
// sanity checking
|
||||
if !bytes.Equal(t.Hash().Bytes(), another.Hash().Bytes()) {
|
||||
panic("can not merge two proof tracer base on different trie")
|
||||
}
|
||||
|
||||
for k := range another.deletionTracer {
|
||||
t.deletionTracer[k] = struct{}{}
|
||||
}
|
||||
|
||||
for k, v := range another.rawPaths {
|
||||
t.rawPaths[k] = v
|
||||
}
|
||||
|
||||
for k, v := range another.emptyTermPaths {
|
||||
t.emptyTermPaths[k] = v
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// GetDeletionProofs generate current deletionTracer and collect deletion proofs
|
||||
// which is possible to be used from all rawPaths, which enabling witness generator
|
||||
// to predict the final state root after executing any deletion
|
||||
// along any of the rawpath, no matter of the deletion occurs in any position of the mpt ops
|
||||
// Note the collected sibling node has no key along with it since witness generator would
|
||||
// always decode the node for its purpose
|
||||
func (t *ProofTracer) GetDeletionProofs() ([][]byte, error) {
|
||||
|
||||
retMap := map[zkt.Hash][]byte{}
|
||||
|
||||
// check each path: reversively, skip the final leaf node
|
||||
for _, path := range t.rawPaths {
|
||||
|
||||
checkPath := path[:len(path)-1]
|
||||
for i := len(checkPath); i > 0; i-- {
|
||||
n := checkPath[i-1]
|
||||
_, deletedL := t.deletionTracer[*n.ChildL]
|
||||
_, deletedR := t.deletionTracer[*n.ChildR]
|
||||
if deletedL && deletedR {
|
||||
nodeHash, _ := n.NodeHash()
|
||||
t.deletionTracer[*nodeHash] = struct{}{}
|
||||
} else {
|
||||
var siblingHash *zkt.Hash
|
||||
if deletedL {
|
||||
siblingHash = n.ChildR
|
||||
} else if deletedR {
|
||||
siblingHash = n.ChildL
|
||||
}
|
||||
if siblingHash != nil {
|
||||
sibling, err := t.TryGetNode(siblingHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sibling.Type != zktrie.NodeTypeEmpty_New {
|
||||
retMap[*siblingHash] = sibling.Value()
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ret [][]byte
|
||||
for _, bt := range retMap {
|
||||
ret = append(ret, bt)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
|
||||
}
|
||||
|
||||
// MarkDeletion mark a key has been involved into deletion
|
||||
func (t *ProofTracer) MarkDeletion(key []byte) {
|
||||
if path, existed := t.emptyTermPaths[string(key)]; existed {
|
||||
// copy empty node terminated path for final scanning
|
||||
t.rawPaths[string(key)] = path
|
||||
} else if path, existed = t.rawPaths[string(key)]; existed {
|
||||
// sanity check
|
||||
leafNode := path[len(path)-1]
|
||||
|
||||
if leafNode.Type != zktrie.NodeTypeLeaf_New {
|
||||
panic("all path recorded in proofTrace should be ended with leafNode")
|
||||
}
|
||||
|
||||
nodeHash, _ := leafNode.NodeHash()
|
||||
t.deletionTracer[*nodeHash] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Prove act the same as zktrie.Prove, while also collect the raw path
|
||||
// for collecting deletion proofs in a post-work
|
||||
func (t *ProofTracer) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error {
|
||||
var mptPath []*zktrie.Node
|
||||
err := t.ZkTrie.ProveWithDeletion(key, fromLevel,
|
||||
func(n *zktrie.Node) error {
|
||||
nodeHash, err := n.NodeHash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch n.Type {
|
||||
case zktrie.NodeTypeLeaf_New:
|
||||
preImage := t.GetKey(n.NodeKey.Bytes())
|
||||
if len(preImage) > 0 {
|
||||
n.KeyPreimage = &zkt.Byte32{}
|
||||
copy(n.KeyPreimage[:], preImage)
|
||||
}
|
||||
case zktrie.NodeTypeBranch_0, zktrie.NodeTypeBranch_1,
|
||||
zktrie.NodeTypeBranch_2, zktrie.NodeTypeBranch_3:
|
||||
mptPath = append(mptPath, n)
|
||||
case zktrie.NodeTypeEmpty_New:
|
||||
// empty node is considered as "unhit" but it should be also being added
|
||||
// into a temporary slot for possibly being marked as deletion later
|
||||
mptPath = append(mptPath, n)
|
||||
t.emptyTermPaths[string(key)] = mptPath
|
||||
default:
|
||||
panic(fmt.Errorf("unexpected node type %d", n.Type))
|
||||
}
|
||||
|
||||
return proofDb.Put(nodeHash[:], n.Value())
|
||||
},
|
||||
func(n *zktrie.Node, _ *zktrie.Node) {
|
||||
// only "hit" path (i.e. the leaf node corresponding the input key can be found)
|
||||
// would be add into tracer
|
||||
mptPath = append(mptPath, n)
|
||||
t.rawPaths[string(key)] = mptPath
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// we put this special kv pair in db so we can distinguish the type and
|
||||
// make suitable Proof
|
||||
return proofDb.Put(magicHash, zktrie.ProofMagicBytes())
|
||||
}
|
||||
Loading…
Reference in a new issue