diff --git a/db/substate_db.go b/db/substate_db.go index 9f3c524..32e20fb 100644 --- a/db/substate_db.go +++ b/db/substate_db.go @@ -4,11 +4,9 @@ import ( "encoding/binary" "fmt" - pb "github.com/Fantom-foundation/Substate/protobuf" "github.com/Fantom-foundation/Substate/rlp" "github.com/Fantom-foundation/Substate/substate" trlp "github.com/Fantom-foundation/Substate/types/rlp" - "github.com/golang/protobuf/proto" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/opt" "github.com/syndtr/goleveldb/leveldb/util" @@ -45,6 +43,9 @@ type SubstateDB interface { // GetLastSubstate returns last substate (block and transaction wise) inside given DB. GetLastSubstate() (*substate.Substate, error) + + // SetDecoder sets the decoder func to the provided encoding + SetDecoder(encoding string) *substateDB } // NewDefaultSubstateDB creates new instance of SubstateDB with default options. @@ -59,11 +60,11 @@ func NewSubstateDB(path string, o *opt.Options, wo *opt.WriteOptions, ro *opt.Re } func MakeDefaultSubstateDB(db *leveldb.DB) SubstateDB { - return &substateDB{&codeDB{&baseDB{backend: db}}} + return &substateDB{&codeDB{&baseDB{backend: db}}, nil} } func MakeDefaultSubstateDBFromBaseDB(db BaseDB) SubstateDB { - return &substateDB{&codeDB{&baseDB{backend: db.getBackend()}}} + return &substateDB{&codeDB{&baseDB{backend: db.getBackend()}}, nil} } // NewReadOnlySubstateDB creates a new instance of read-only SubstateDB. @@ -72,7 +73,7 @@ func NewReadOnlySubstateDB(path string) (SubstateDB, error) { } func MakeSubstateDB(db *leveldb.DB, wo *opt.WriteOptions, ro *opt.ReadOptions) SubstateDB { - return &substateDB{&codeDB{&baseDB{backend: db, wo: wo, ro: ro}}} + return &substateDB{&codeDB{&baseDB{backend: db, wo: wo, ro: ro}}, nil} } func newSubstateDB(path string, o *opt.Options, wo *opt.WriteOptions, ro *opt.ReadOptions) (*substateDB, error) { @@ -80,11 +81,12 @@ func newSubstateDB(path string, o *opt.Options, wo *opt.WriteOptions, ro *opt.Re if err != nil { return nil, err } - return &substateDB{base}, nil + return &substateDB{base, nil}, nil } type substateDB struct { *codeDB + decodeSubstate decoderFunc } func (db *substateDB) GetFirstSubstate() *substate.Substate { @@ -110,12 +112,7 @@ func (db *substateDB) GetSubstate(block uint64, tx int) (*substate.Substate, err return nil, fmt.Errorf("cannot get substate block: %v, tx: %v from db; %w", block, tx, err) } - pbSubstate := &pb.Substate{} - if err := proto.Unmarshal(val, pbSubstate); err != nil { - return nil, err - } - - return pbSubstate.Decode(db.GetCode, block, tx) + return db.DecodeSubstate(val, block, tx) } // GetBlockSubstates returns substates for given block if exists within DB. @@ -140,14 +137,9 @@ func (db *substateDB) GetBlockSubstates(block uint64) (map[int]*substate.Substat return nil, fmt.Errorf("record-replay: GetBlockSubstates(%v) iterated substates from block %v", block, b) } - pbSubstate := &pb.Substate{} - if err := proto.Unmarshal(value, pbSubstate); err != nil { - return nil, err - } - - sbstt, err := pbSubstate.Decode(db.GetCode, block, tx) + sbstt, err := db.DecodeSubstate(value, block, tx) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to decode substate, block: %d, tx: %d; %w", block, tx, err) } txSubstate[tx] = sbstt diff --git a/db/substate_decoder.go b/db/substate_decoder.go new file mode 100644 index 0000000..48f46c7 --- /dev/null +++ b/db/substate_decoder.go @@ -0,0 +1,73 @@ +package db + +import ( + "fmt" + + pb "github.com/Fantom-foundation/Substate/protobuf" + "github.com/Fantom-foundation/Substate/rlp" + "github.com/Fantom-foundation/Substate/substate" + "github.com/Fantom-foundation/Substate/types" + "github.com/golang/protobuf/proto" +) + +// SetDecoder sets the runtime parsing behavior of substateDB +// intended usage: +// +// db := &substateDB{..} // initializing db +// .SetDecoder() // end of init, or right before decoding +func (db *substateDB) SetDecoder(encoding string) *substateDB { + db.decodeSubstate = getDecoderFunc(encoding, db.GetCode) + return db +} + +type substateDecoder interface { + DecodeSubstate(bytes []byte, block uint64, tx int) (*substate.Substate, error) +} + +// DecodeSubstate implemented by substateDB +func (db *substateDB) DecodeSubstate(bytes []byte, block uint64, tx int) (*substate.Substate, error) { + if db.decodeSubstate == nil { + db.SetDecoder("default") + } + return db.decodeSubstate(bytes, block, tx) +} + +// decoderFunc aliases the common function used to decode substate +type decoderFunc func([]byte, uint64, int) (*substate.Substate, error) + +type codeLookup = func(types.Hash) ([]byte, error) + +func getDecoderFunc(encoding string, lookup codeLookup) decoderFunc { + switch encoding { + case "protobuf", "pb": + return func(bytes []byte, block uint64, tx int) (*substate.Substate, error) { + return decodeProtobuf(bytes, lookup, block, tx) + } + default: + fallthrough + case "rlp": + return func(bytes []byte, block uint64, tx int) (*substate.Substate, error) { + return decodeRlp(bytes, lookup, block, tx) + } + } +} + +// decodeRlp decodes into substate the provided rlp-encoded bytecode +func decodeRlp(bytes []byte, lookup codeLookup, block uint64, tx int) (*substate.Substate, error) { + rlpSubstate, err := rlp.Decode(bytes) + if err != nil { + return nil, fmt.Errorf("cannot decode substate data from rlp block: %v, tx %v; %w", block, tx, err) + } + + return rlpSubstate.ToSubstate(lookup, block, tx) +} + +// decodeProtobuf decodes into substate the provided protobuf-encoded bytecode +func decodeProtobuf(bytes []byte, lookup codeLookup, block uint64, tx int) (*substate.Substate, error) { + pbSubstate := &pb.Substate{} + if err := proto.Unmarshal(bytes, pbSubstate); err != nil { + return nil, fmt.Errorf("cannot decode substate data from protobuf block: %v, tx %v; %w", block, tx, err) + } + + return pbSubstate.Decode(lookup, block, tx) +} diff --git a/db/substate_iterator.go b/db/substate_iterator.go index a041d70..54f196f 100644 --- a/db/substate_iterator.go +++ b/db/substate_iterator.go @@ -3,11 +3,8 @@ package db import ( "fmt" - "github.com/golang/protobuf/proto" - "github.com/syndtr/goleveldb/leveldb/util" - - pb "github.com/Fantom-foundation/Substate/protobuf" "github.com/Fantom-foundation/Substate/substate" + "github.com/syndtr/goleveldb/leveldb/util" ) func newSubstateIterator(db *substateDB, start []byte) *substateIterator { @@ -34,12 +31,7 @@ func (i *substateIterator) decode(data rawEntry) (*substate.Substate, error) { return nil, fmt.Errorf("invalid substate key: %v; %w", key, err) } - pbSubstate := &pb.Substate{} - if err := proto.Unmarshal(value, pbSubstate); err != nil { - return nil, err - } - - return pbSubstate.Decode(i.db.GetCode, block, tx) + return i.db.DecodeSubstate(value, block, tx) } func (i *substateIterator) start(numWorkers int) { diff --git a/protobuf/decode.go b/protobuf/decode.go index 6d7b7f4..1b9677b 100644 --- a/protobuf/decode.go +++ b/protobuf/decode.go @@ -9,7 +9,6 @@ import ( "github.com/Fantom-foundation/Substate/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/holiman/uint256" "github.com/syndtr/goleveldb/leveldb" ) @@ -92,9 +91,9 @@ func (entry *Substate_AllocEntry) decode() ([]byte, *Substate_Account, error) { return entry.GetAddress(), entry.GetAccount(), nil } -func (acct *Substate_Account) decode() (uint64, *uint256.Int, []byte, types.Hash, error) { +func (acct *Substate_Account) decode() (uint64, *big.Int, []byte, types.Hash, error) { return acct.GetNonce(), - types.BytesToUint256(acct.GetBalance()), + types.BytesToBigInt(acct.GetBalance()), acct.GetCode(), types.BytesToHash(acct.GetCodeHash()), nil diff --git a/substate/account.go b/substate/account.go index d79530c..594c09f 100644 --- a/substate/account.go +++ b/substate/account.go @@ -8,7 +8,6 @@ import ( "github.com/Fantom-foundation/Substate/types" "github.com/Fantom-foundation/Substate/types/hash" - "github.com/holiman/uint256" ) // Account holds any information about account used in a transaction.