feat&perf: reversed bit of key when persisting zktrie (#410)

* bit reverse when persisting zktrie

* fix according to review

* Update version.go

---------

Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
This commit is contained in:
Ho 2023-07-31 16:13:12 +08:00 committed by GitHub
parent a677ee3ccf
commit 134e1b8f92
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 132 additions and 4 deletions

View file

@ -23,8 +23,8 @@ import (
const (
VersionMajor = 4 // Major version component of the current release
VersionMinor = 2 // Minor version component of the current release
VersionPatch = 11 // Patch version component of the current release
VersionMinor = 3 // Minor version component of the current release
VersionPatch = 0 // Patch version component of the current release
VersionMeta = "sepolia" // Version metadata to append to the version string
)

View file

@ -11,7 +11,13 @@ import (
"github.com/scroll-tech/go-ethereum/ethdb"
)
// ZktrieDatabase Database adaptor imple zktrie.ZktrieDatbase
// 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
@ -29,6 +35,7 @@ func NewZktrieDatabaseFromTriedb(db *Database) *ZktrieDatabase {
// Put saves a key:value into the Storage
func (l *ZktrieDatabase) Put(k, v []byte) error {
k = bitReverse(k)
l.db.lock.Lock()
l.db.rawDirties.Put(Concat(l.prefix, k[:]), v)
l.db.lock.Unlock()
@ -37,6 +44,7 @@ func (l *ZktrieDatabase) Put(k, v []byte) error {
// 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.lock.RLock()
value, ok := l.db.rawDirties.Get(concatKey)
@ -78,7 +86,7 @@ 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 := iter.Key()[len(l.prefix):]
localKey := bitReverse(iter.Key()[len(l.prefix):])
if cont, err := f(localKey, iter.Value()); err != nil {
return err
} else if !cont {
@ -109,3 +117,54 @@ func (l *ZktrieDatabase) List(limit int) ([]KV, error) {
})
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
}

View file

@ -0,0 +1,69 @@
package trie
import (
"bytes"
"testing"
"github.com/scroll-tech/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)
}
}
}