Make trie.Database.Commit() write out preimages in deterministic order

Iteration order over db.preimages was not deterministic, sorting the
batch contents when the batch is written wasn't enough to work around
this if preimages didn't fit into a single batch. So now Commit() will
pre-sort preimages to ensure their order is always deterministic, even
when split across multiple batches.
This commit is contained in:
Vadim Macagon 2019-01-30 16:03:14 +07:00
parent 8d42b72c43
commit 500eef96c8

View file

@ -17,8 +17,10 @@
package trie package trie
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"sort"
"sync" "sync"
"time" "time"
@ -615,9 +617,26 @@ func (db *Database) Commit(node common.Hash, report bool) error {
start := time.Now() start := time.Now()
batch := db.diskdb.NewBatch() batch := db.diskdb.NewBatch()
type kvPair struct {
key []byte
value []byte
}
// Sort preimages to ensure they are written out in deterministic order
orderedPreimages := make([]kvPair, 0, len(db.preimages))
for hash := range db.preimages {
orderedPreimages = append(orderedPreimages, kvPair{
key: common.CopyBytes(hash[:]),
value: db.preimages[hash],
})
}
sort.Slice(orderedPreimages, func(j, k int) bool {
return bytes.Compare(orderedPreimages[j].key, orderedPreimages[k].key) < 0
})
// Move all of the accumulated preimages into a write batch // Move all of the accumulated preimages into a write batch
for hash, preimage := range db.preimages { for _, preimage := range orderedPreimages {
if err := batch.Put(db.secureKey(hash[:]), preimage); err != nil { if err := batch.Put(db.secureKey(preimage.key), preimage.value); err != nil {
log.Error("Failed to commit preimage from trie database", "err", err) log.Error("Failed to commit preimage from trie database", "err", err)
db.lock.RUnlock() db.lock.RUnlock()
return err return err