Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Protobuf] add a way to switch between RLP and Protobuf for substateDB decoding #87

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 11 additions & 19 deletions db/substate_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -72,19 +73,20 @@ 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) {
base, err := newCodeDB(path, o, wo, ro)
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 {
Expand All @@ -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.
Expand All @@ -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("Error decoding block %d, tx %d; %w", block, tx, err)
rpl-ffl marked this conversation as resolved.
Show resolved Hide resolved
}

txSubstate[tx] = sbstt
Expand Down
73 changes: 73 additions & 0 deletions db/substate_decoder.go
Original file line number Diff line number Diff line change
@@ -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(<encoding>) // 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 data into rlp block: %v, tx %v; %w", block, tx, err)
rpl-ffl marked this conversation as resolved.
Show resolved Hide resolved
}

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 data into protobuf block: %v, tx %v; %w", block, tx, err)
rpl-ffl marked this conversation as resolved.
Show resolved Hide resolved
}

return pbSubstate.Decode(lookup, block, tx)
}
12 changes: 2 additions & 10 deletions db/substate_iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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) {
Expand Down
5 changes: 2 additions & 3 deletions protobuf/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion substate/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down