[shard] Wrapper on decoding ShardState (#1886)

* [shard] Wrapper on decoding ShardState

* [shard] Use EncodeWrapper

* [shard] Add comment
pull/1890/head
Edgar Aroutiounian 5 years ago committed by GitHub
parent b8fa1b4d94
commit aaf289c070
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      block/v0/header.go
  2. 7
      block/v1/header.go
  3. 7
      block/v2/header.go
  4. 7
      block/v3/header.go
  5. 10
      core/blockchain.go
  6. 21
      core/rawdb/accessors_chain.go
  7. 79
      shard/shard_state.go
  8. 72
      shard/shard_state_test.go

@ -418,12 +418,7 @@ func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
// GetShardState returns the deserialized shard state object.
func (h *Header) GetShardState() (shard.State, error) {
shardState := shard.State{}
err := rlp.DecodeBytes(h.ShardState(), &shardState)
if err != nil {
return nil, err
}
return shardState, nil
return shard.DecodeWrapper(h.ShardState())
}
// Copy returns a copy of the given header.

@ -406,12 +406,7 @@ func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
// GetShardState returns the deserialized shard state object.
func (h *Header) GetShardState() (shard.State, error) {
shardState := shard.State{}
err := rlp.DecodeBytes(h.ShardState(), &shardState)
if err != nil {
return nil, err
}
return shardState, nil
return shard.DecodeWrapper(h.ShardState())
}
// Copy returns a copy of the given header.

@ -402,12 +402,7 @@ func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
// GetShardState returns the deserialized shard state object.
func (h *Header) GetShardState() (shard.State, error) {
shardState := shard.State{}
err := rlp.DecodeBytes(h.ShardState(), &shardState)
if err != nil {
return nil, err
}
return shardState, nil
return shard.DecodeWrapper(h.ShardState())
}
// Copy returns a copy of the given header.

@ -406,12 +406,7 @@ func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
// GetShardState returns the deserialized shard state object.
func (h *Header) GetShardState() (shard.State, error) {
shardState := shard.State{}
err := rlp.DecodeBytes(h.ShardState(), &shardState)
if err != nil {
return nil, err
}
return shardState, nil
return shard.DecodeWrapper(h.ShardState())
}
// Copy returns a copy of the given header.

@ -1916,7 +1916,9 @@ func (bc *BlockChain) WriteShardState(
epoch *big.Int, shardState shard.State,
) error {
shardState = shardState.DeepCopy()
err := rawdb.WriteShardState(bc.db, epoch, shardState)
err := rawdb.WriteShardState(
bc.db, epoch, shardState, bc.Config().IsStaking(epoch),
)
if err != nil {
return err
}
@ -1929,11 +1931,11 @@ func (bc *BlockChain) WriteShardState(
func (bc *BlockChain) WriteShardStateBytes(db rawdb.DatabaseWriter,
epoch *big.Int, shardState []byte,
) (*shard.State, error) {
decodeShardState := shard.State{}
if err := rlp.DecodeBytes(shardState, &decodeShardState); err != nil {
decodeShardState, err := shard.DecodeWrapper(shardState)
if err != nil {
return nil, err
}
err := rawdb.WriteShardStateBytes(db, epoch, shardState)
err = rawdb.WriteShardStateBytes(db, epoch, shardState)
if err != nil {
return nil, err
}

@ -417,27 +417,28 @@ func FindCommonAncestor(db DatabaseReader, a, b *block.Header) *block.Header {
// ReadShardState retrieves sharding state.
func ReadShardState(
db DatabaseReader, epoch *big.Int,
) (shardState shard.State, err error) {
var data []byte
data, err = db.Get(shardStateKey(epoch))
) (shard.State, error) {
data, err := db.Get(shardStateKey(epoch))
if err != nil {
return nil, ctxerror.New(MsgNoShardStateFromDB,
"epoch", epoch,
).WithCause(err)
}
if err = rlp.DecodeBytes(data, &shardState); err != nil {
ss, err2 := shard.DecodeWrapper(data)
if err2 != nil {
return nil, ctxerror.New("cannot decode sharding state",
"epoch", epoch,
).WithCause(err)
}
return shardState, nil
return ss, nil
}
// WriteShardState stores sharding state into database.
func WriteShardState(
db DatabaseWriter, epoch *big.Int, shardState shard.State,
) (err error) {
data, err := rlp.EncodeToBytes(shardState)
db DatabaseWriter, epoch *big.Int,
shardState shard.State, isStaking bool,
) error {
data, err := shard.EncodeWrapper(shardState, isStaking)
if err != nil {
return ctxerror.New("cannot encode sharding state",
"epoch", epoch,
@ -447,9 +448,7 @@ func WriteShardState(
}
// WriteShardStateBytes stores sharding state into database.
func WriteShardStateBytes(
db DatabaseWriter, epoch *big.Int, data []byte,
) (err error) {
func WriteShardStateBytes(db DatabaseWriter, epoch *big.Int, data []byte) (err error) {
if err = db.Put(shardStateKey(epoch), data); err != nil {
return ctxerror.New("cannot write sharding state",
"epoch", epoch,

@ -8,6 +8,7 @@ import (
"sort"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/bls/ffi/go/bls"
common2 "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/internal/ctxerror"
@ -45,6 +46,84 @@ type Committee struct {
Slots SlotList `json:"subcommittee"`
}
/* Legacy
These are the pre-staking used data-structures, needed to maintain
compatibilty for RLP decode/encode
*/
// StateLegacy ..
type StateLegacy []CommitteeLegacy
// SlotLegacy represents node id (BLS address)
type SlotLegacy struct {
EcdsaAddress common.Address `json:"ecdsa-address"`
BlsPublicKey BlsPublicKey `json:"bls-pubkey"`
}
// SlotListLegacy is a list of SlotList.
type SlotListLegacy []SlotLegacy
// CommitteeLegacy contains the active nodes in one shard
type CommitteeLegacy struct {
ShardID uint32 `json:"shard-id"`
Slots SlotListLegacy `json:"subcommittee"`
}
// DecodeWrapper ..
func DecodeWrapper(shardState []byte) (State, error) {
oldSS := StateLegacy{}
newSS := State{}
var (
err1 error
err2 error
)
err1 = rlp.DecodeBytes(shardState, &newSS)
if err1 == nil {
return newSS, nil
}
err2 = rlp.DecodeBytes(shardState, &oldSS)
if err2 == nil {
newSS = make(State, len(oldSS))
for i := range oldSS {
newSS[i] = Committee{ShardID: oldSS[i].ShardID, Slots: SlotList{}}
for _, slot := range oldSS[i].Slots {
newSS[i].Slots = append(newSS[i].Slots, Slot{
slot.EcdsaAddress, slot.BlsPublicKey, nil,
})
}
}
return newSS, nil
}
return nil, err2
}
// EncodeWrapper ..
func EncodeWrapper(shardState State, isStaking bool) ([]byte, error) {
var (
data []byte
err error
)
if isStaking {
data, err = rlp.EncodeToBytes(shardState)
} else {
shardStateLegacy := make(StateLegacy, len(shardState))
for i := range shardState {
shardStateLegacy[i] = CommitteeLegacy{
ShardID: shardState[i].ShardID, Slots: SlotListLegacy{},
}
for _, slot := range shardState[i].Slots {
shardStateLegacy[i].Slots = append(shardStateLegacy[i].Slots, SlotLegacy{
slot.EcdsaAddress, slot.BlsPublicKey,
})
}
}
data, err = rlp.EncodeToBytes(shardStateLegacy)
}
return data, err
}
// JSON produces a non-pretty printed JSON string of the SuperCommittee
func (ss State) JSON() string {
type t struct {

@ -2,9 +2,12 @@ package shard
import (
"bytes"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/numeric"
)
var (
@ -18,6 +21,11 @@ var (
blsPubKey22 = [48]byte{}
)
const (
json1 = `[{"shard-id":0,"member-count":4,"subcommittee":[{"bls-pubkey":"72616e646f6d206b65792031000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":null,"ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"},{"bls-pubkey":"72616e646f6d206b65792032000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":null,"ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"},{"bls-pubkey":"72616e646f6d206b65792033000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":null,"ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"},{"bls-pubkey":"72616e646f6d206b65792034000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":null,"ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"}]},{"shard-id":1,"member-count":2,"subcommittee":[{"bls-pubkey":"72616e646f6d206b65792035000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":null,"ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"},{"bls-pubkey":"72616e646f6d206b65792036000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":null,"ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"}]}]`
json2 = `[{"shard-id":0,"member-count":5,"subcommittee":[{"bls-pubkey":"72616e646f6d206b65792031000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":null,"ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"},{"bls-pubkey":"72616e646f6d206b65792032000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":null,"ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"},{"bls-pubkey":"72616e646f6d206b65792033000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":"10.000000000000000000","ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"},{"bls-pubkey":"72616e646f6d206b65792035000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":null,"ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"},{"bls-pubkey":"72616e646f6d206b65792031000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":"45.123000000000000000","ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"}]},{"shard-id":1,"member-count":4,"subcommittee":[{"bls-pubkey":"72616e646f6d206b65792032000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":"10.000000000000000000","ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"},{"bls-pubkey":"72616e646f6d206b65792033000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":null,"ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"},{"bls-pubkey":"72616e646f6d206b65792034000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":null,"ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"},{"bls-pubkey":"72616e646f6d206b65792035000000000000000000000000000000000000000000000000000000000000000000000000","total-stake":"45.123000000000000000","ecdsa-address":"one1qqqqqqqqqqqqqqqqqqqqqqq423ktdf2pznf238"}]}]`
)
func init() {
copy(blsPubKey1[:], []byte("random key 1"))
copy(blsPubKey2[:], []byte("random key 2"))
@ -92,3 +100,67 @@ func TestHash(t *testing.T) {
t.Error("shardState1 and shardState2 should have equal hash")
}
}
func TestCompatibilityOldShardStateIntoNew(t *testing.T) {
junkA := common.BigToAddress(big.NewInt(23452345345345))
stake1 := numeric.NewDec(10)
stake2 := numeric.MustNewDecFromStr("45.123")
preStakingState := StateLegacy{
CommitteeLegacy{ShardID: 0, Slots: SlotListLegacy{
SlotLegacy{junkA, blsPubKey1},
SlotLegacy{junkA, blsPubKey2},
SlotLegacy{junkA, blsPubKey3},
SlotLegacy{junkA, blsPubKey4},
}},
CommitteeLegacy{ShardID: 1, Slots: SlotListLegacy{
SlotLegacy{junkA, blsPubKey5},
SlotLegacy{junkA, blsPubKey6},
}},
}
postStakingState := State{
Committee{ShardID: 0, Slots: SlotList{
Slot{junkA, blsPubKey1, nil},
Slot{junkA, blsPubKey2, nil},
Slot{junkA, blsPubKey3, &stake1},
Slot{junkA, blsPubKey5, nil},
Slot{junkA, blsPubKey1, &stake2},
}},
Committee{ShardID: 1, Slots: SlotList{
Slot{junkA, blsPubKey2, &stake1},
Slot{junkA, blsPubKey3, nil},
Slot{junkA, blsPubKey4, nil},
Slot{junkA, blsPubKey5, &stake2},
}},
}
preStakingStateBytes, _ := rlp.EncodeToBytes(preStakingState)
postStakingStateBytes, _ := rlp.EncodeToBytes(postStakingState)
decodeJunk := []byte{0x10, 0x12, 0x04, 0x4}
// Decode old shard state into new shard state
a, err1 := DecodeWrapper(preStakingStateBytes)
b, err2 := DecodeWrapper(postStakingStateBytes)
_, err3 := DecodeWrapper(decodeJunk)
if err1 != nil {
t.Errorf("Could not decode old format")
}
if err2 != nil {
t.Errorf("Could not decode new format")
}
if a.JSON() != json1 {
t.Error("old shard state into new shard state as JSON is not equal")
}
if b.JSON() != json2 {
t.Error("new shard state into new shard state as JSON is not equal")
}
if err3 == nil {
t.Errorf("Should have caused error %v", err3)
}
}

Loading…
Cancel
Save