Skip to content

Commit

Permalink
Merge pull request #576 from Juliusan/bugfixes
Browse files Browse the repository at this point in the history
BugFix for "ApplyBlock: b state index #x can't be applied to the empty state"
  • Loading branch information
jorgemmsilva authored Nov 5, 2021
2 parents a8a3c3d + e4e8cac commit ea1c106
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 13 deletions.
23 changes: 14 additions & 9 deletions packages/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
type virtualStateAccess struct {
chainID *iscp.ChainID
db kvstore.KVStore
empty bool
kvs *buffered.BufferedKVStoreAccess
committedHash hashing.HashValue
appliedBlockHashes []hashing.HashValue
Expand All @@ -39,7 +38,6 @@ func newVirtualState(db kvstore.KVStore, chainID *iscp.ChainID) *virtualStateAcc
ret := &virtualStateAccess{
db: db,
kvs: buffered.NewBufferedKVStoreAccess(kv.NewHiveKVStoreReader(sub)),
empty: true,
appliedBlockHashes: make([]hashing.HashValue, 0),
}
if chainID != nil {
Expand All @@ -50,8 +48,8 @@ func newVirtualState(db kvstore.KVStore, chainID *iscp.ChainID) *virtualStateAcc

func newZeroVirtualState(db kvstore.KVStore, chainID *iscp.ChainID) (VirtualStateAccess, Block) {
ret := newVirtualState(db, chainID)
originBlock := newOriginBlock()
if err := ret.ApplyBlock(originBlock); err != nil {
originBlock, err := ret.applyOriginBlock()
if err != nil {
panic(err)
}
_, _ = ret.ExtractBlock() // clear the update log
Expand All @@ -77,7 +75,6 @@ func (vs *virtualStateAccess) Copy() VirtualStateAccess {
db: vs.db,
committedHash: vs.committedHash,
appliedBlockHashes: make([]hashing.HashValue, len(vs.appliedBlockHashes)),
empty: vs.empty,
kvs: vs.kvs.Copy(),
}
copy(ret.appliedBlockHashes, vs.appliedBlockHashes)
Expand Down Expand Up @@ -133,19 +130,27 @@ func (vs *virtualStateAccess) PreviousStateHash() hashing.HashValue {

// ApplyBlock applies a block of state updates. Checks consistency of the block and previous state. Updates state hash
func (vs *virtualStateAccess) ApplyBlock(b Block) error {
if vs.empty && b.BlockIndex() != 0 {
return vs.applyAnyBlock(b, false)
}

func (vs *virtualStateAccess) applyOriginBlock() (Block, error) {
originBlock := newOriginBlock()
return originBlock, vs.applyAnyBlock(originBlock, true)
}

func (vs *virtualStateAccess) applyAnyBlock(b Block, empty bool) error {
if empty && b.BlockIndex() != 0 {
return xerrors.Errorf("ApplyBlock: b state index #%d can't be applied to the empty state", b.BlockIndex())
}
if !vs.empty && vs.BlockIndex()+1 != b.BlockIndex() {
if !empty && vs.BlockIndex()+1 != b.BlockIndex() {
return xerrors.Errorf("ApplyBlock: b state index #%d can't be applied to the state with index #%d",
b.BlockIndex(), vs.BlockIndex())
}
if !vs.empty && vs.Timestamp().After(b.Timestamp()) {
if !empty && vs.Timestamp().After(b.Timestamp()) {
return xerrors.New("ApplyBlock: inconsistent timestamps")
}
vs.ApplyStateUpdates(b.(*blockImpl).stateUpdate)
vs.appliedBlockHashes = append(vs.appliedBlockHashes, hashing.HashData(b.EssenceBytes()))
vs.empty = false
return nil
}

Expand Down
70 changes: 66 additions & 4 deletions packages/state/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,11 @@ func TestOriginHashes(t *testing.T) {
require.EqualValues(t, calcOriginStateHash(), z.StateCommitment())
})
t.Run("origin state construct", func(t *testing.T) {
origBlock := newOriginBlock()
emptyState := newVirtualState(mapdb.NewMapDB(), nil)
origBlock, err := emptyState.applyOriginBlock()
require.EqualValues(t, 0, origBlock.BlockIndex())
require.True(t, origBlock.Timestamp().IsZero())
require.EqualValues(t, hashing.NilHash, origBlock.PreviousStateHash())

emptyState := newVirtualState(mapdb.NewMapDB(), nil)
err := emptyState.ApplyBlock(origBlock)
require.NoError(t, err)
require.EqualValues(t, emptyState.StateCommitment(), calcOriginStateHash())
require.EqualValues(t, hashing.NilHash, emptyState.PreviousStateHash())
Expand Down Expand Up @@ -166,6 +164,70 @@ func TestStateWithDB(t *testing.T) {

require.EqualValues(t, vs1.StateCommitment(), vs2.StateCommitment())
})
t.Run("apply block after loading", func(t *testing.T) {
store := mapdb.NewMapDB()
chainID := iscp.RandomChainID([]byte("1"))
_, exists, err := LoadSolidState(store, chainID)
require.NoError(t, err)
require.False(t, exists)

vsOrig, err := CreateOriginState(store, chainID)
require.NoError(t, err)

time1 := time.Now()
su := NewStateUpdateWithBlocklogValues(1, time1, hashing.NilHash)
su.Mutations().Set("key", []byte("value"))
block1, err := newBlock(su.Mutations())
require.NoError(t, err)

err = vsOrig.ApplyBlock(block1)
require.NoError(t, err)
require.EqualValues(t, 1, vsOrig.BlockIndex())
require.True(t, time1.Equal(vsOrig.Timestamp()))

time2 := time.Now()
su = NewStateUpdateWithBlocklogValues(2, time2, vsOrig.PreviousStateHash())
su.Mutations().Set("other_key", []byte("other_value"))
block2, err := newBlock(su.Mutations())
require.NoError(t, err)

err = vsOrig.ApplyBlock(block2)
require.NoError(t, err)
require.EqualValues(t, 2, vsOrig.BlockIndex())
require.True(t, time2.Equal(vsOrig.Timestamp()))

err = vsOrig.Commit(block1, block2)
require.NoError(t, err)
require.EqualValues(t, 2, vsOrig.BlockIndex())
require.True(t, time2.Equal(vsOrig.Timestamp()))

vsLoaded, exists, err := LoadSolidState(store, chainID)
require.NoError(t, err)
require.True(t, exists)

require.EqualValues(t, vsOrig.StateCommitment(), vsLoaded.StateCommitment())
require.EqualValues(t, vsOrig.BlockIndex(), vsLoaded.BlockIndex())
require.EqualValues(t, vsOrig.Timestamp(), vsLoaded.Timestamp())
require.EqualValues(t, 2, vsLoaded.BlockIndex())

time3 := time.Now()
su = NewStateUpdateWithBlocklogValues(3, time3, vsLoaded.PreviousStateHash())
su.Mutations().Set("more_keys", []byte("more_values"))
block3, err := newBlock(su.Mutations())
require.NoError(t, err)

err = vsOrig.ApplyBlock(block3)
require.NoError(t, err)
require.EqualValues(t, 3, vsOrig.BlockIndex())
require.True(t, time3.Equal(vsOrig.Timestamp()))

err = vsLoaded.ApplyBlock(block3)
require.NoError(t, err)
require.EqualValues(t, 3, vsLoaded.BlockIndex())
require.True(t, time3.Equal(vsLoaded.Timestamp()))

require.EqualValues(t, vsOrig.StateCommitment(), vsLoaded.StateCommitment())
})
t.Run("state reader", func(t *testing.T) {
store := mapdb.NewMapDB()
chainID := iscp.RandomChainID([]byte("1"))
Expand Down

0 comments on commit ea1c106

Please sign in to comment.