Skip to content

Commit

Permalink
feat(internal/client): Implement read only BlockState for ClientAda…
Browse files Browse the repository at this point in the history
…pter (#4549)
  • Loading branch information
dimartiro authored Feb 27, 2025
1 parent 7331c50 commit 6ab8bad
Show file tree
Hide file tree
Showing 28 changed files with 2,811 additions and 95 deletions.
4 changes: 2 additions & 2 deletions dot/state/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var (
arrivalTimePrefix = []byte("arr") // arrivalTimePrefix || hash -> arrivalTime
receiptPrefix = []byte("rcp") // receiptPrefix + hash -> receipt
messageQueuePrefix = []byte("mqp") // messageQueuePrefix + hash -> message queue
justificationPrefix = []byte("jcp") // justificationPrefix + hash -> justification
JustificationPrefix = []byte("jcp") // justificationPrefix + hash -> justification
firstSlotNumberKey = []byte("fsn") // firstSlotNumberKey -> First slot number

errNilBlockTree = errors.New("blocktree is nil")
Expand Down Expand Up @@ -228,7 +228,7 @@ func NewDefaultBlockStateFromGenesis(db database.Database, trs *Tries, header *t
bs.genesisHash = header.Hash()
bs.lastFinalised = header.Hash()

if err := bs.db.Put(highestRoundAndSetIDKey, roundAndSetIDToBytes(0, 0)); err != nil {
if err := bs.db.Put(HighestRoundAndSetIDKey, RoundAndSetIDToBytes(0, 0)); err != nil {
return nil, err
}

Expand Down
6 changes: 3 additions & 3 deletions dot/state/block_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ func (bs *DefaultBlockState) GetMessageQueue(hash common.Hash) ([]byte, error) {

// HasJustification returns if the db contains a Justification at the given hash
func (bs *DefaultBlockState) HasJustification(hash common.Hash) (bool, error) {
return bs.db.Has(prefixKey(hash, justificationPrefix))
return bs.db.Has(prefixKey(hash, JustificationPrefix))
}

// SetJustification sets a Justification in the database
func (bs *DefaultBlockState) SetJustification(hash common.Hash, data []byte) error {
err := bs.db.Put(prefixKey(hash, justificationPrefix), data)
err := bs.db.Put(prefixKey(hash, JustificationPrefix), data)
if err != nil {
return err
}
Expand All @@ -79,7 +79,7 @@ func (bs *DefaultBlockState) SetJustification(hash common.Hash, data []byte) err

// GetJustification retrieves a Justification from the database
func (bs *DefaultBlockState) GetJustification(hash common.Hash) ([]byte, error) {
data, err := bs.db.Get(prefixKey(hash, justificationPrefix))
data, err := bs.db.Get(prefixKey(hash, JustificationPrefix))
if err != nil {
return nil, err
}
Expand Down
18 changes: 9 additions & 9 deletions dot/state/block_finalisation.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ import (
)

var errSetIDLowerThanHighest = errors.New("set id lower than highest")
var highestRoundAndSetIDKey = []byte("hrs")
var HighestRoundAndSetIDKey = []byte("hrs")

// finalisedHashKey = FinalizedBlockHashKey + round + setID (LE encoded)
func finalisedHashKey(round, setID uint64) []byte {
return append(common.FinalizedBlockHashKey, roundAndSetIDToBytes(round, setID)...)
// FinalisedHashKey = FinalizedBlockHashKey + round + setID (LE encoded)
func FinalisedHashKey(round, setID uint64) []byte {
return append(common.FinalizedBlockHashKey, RoundAndSetIDToBytes(round, setID)...)
}

// HasFinalisedBlock returns true if there is a finalised block for a given round and setID, false otherwise
func (bs *DefaultBlockState) HasFinalisedBlock(round, setID uint64) (bool, error) {
return bs.db.Has(finalisedHashKey(round, setID))
return bs.db.Has(FinalisedHashKey(round, setID))
}

// NumberIsFinalised checks if a block number is finalised or not
Expand Down Expand Up @@ -64,7 +64,7 @@ func (bs *DefaultBlockState) GetRoundAndSetID() (uint64, uint64) {

// getFinalisedHash gets the finalised block header by round and setID
func (bs *DefaultBlockState) getFinalisedHash(round, setID uint64) (common.Hash, error) {
h, err := bs.db.Get(finalisedHashKey(round, setID))
h, err := bs.db.Get(FinalisedHashKey(round, setID))
if err != nil {
return common.Hash{}, err
}
Expand All @@ -82,12 +82,12 @@ func (bs *DefaultBlockState) SetHighestRoundAndSetID(round, setID uint64) error
return fmt.Errorf("%w: %d should be greater or equal %d", errSetIDLowerThanHighest, setID, highestSetID)
}

return bs.db.Put(highestRoundAndSetIDKey, roundAndSetIDToBytes(round, setID))
return bs.db.Put(HighestRoundAndSetIDKey, RoundAndSetIDToBytes(round, setID))
}

// GetHighestRoundAndSetID gets the highest round and setID that have been finalised
func (bs *DefaultBlockState) GetHighestRoundAndSetID() (uint64, uint64, error) {
b, err := bs.db.Get(highestRoundAndSetIDKey)
b, err := bs.db.Get(HighestRoundAndSetIDKey)
if err != nil {
return 0, 0, fmt.Errorf("failed to get highest round and setID: %w", err)
}
Expand Down Expand Up @@ -123,7 +123,7 @@ func (bs *DefaultBlockState) GetHighestFinalisedHeader() (*types.Header, error)
}

func (bs *DefaultBlockState) SetFinalizedHashKey(hash common.Hash, round, setID uint64) error {
return bs.db.Put(finalisedHashKey(round, setID), hash[:])
return bs.db.Put(FinalisedHashKey(round, setID), hash[:])
}

// SetFinalisedHash sets the latest finalised block hash
Expand Down
6 changes: 3 additions & 3 deletions dot/state/grandpa.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,17 +534,17 @@ func (s *GrandpaState) GetNextResume() (blockNumber uint, err error) {

func prevotesKey(round, setID uint64) []byte {
prevotesPrefix := []byte("pv")
k := roundAndSetIDToBytes(round, setID)
k := RoundAndSetIDToBytes(round, setID)
return append(prevotesPrefix, k...)
}

func precommitsKey(round, setID uint64) []byte {
precommitsPrefix := []byte("pc")
k := roundAndSetIDToBytes(round, setID)
k := RoundAndSetIDToBytes(round, setID)
return append(precommitsPrefix, k...)
}

func roundAndSetIDToBytes(round, setID uint64) []byte {
func RoundAndSetIDToBytes(round, setID uint64) []byte {
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, round)
buf2 := make([]byte, 8)
Expand Down
2 changes: 1 addition & 1 deletion dot/state/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func (s *Service) Import(header *types.Header, t trie.Trie,
}

// TODO: this is broken, need to know round and setID for the header as well
if err := block.db.Put(finalisedHashKey(0, 0), hash[:]); err != nil {
if err := block.db.Put(FinalisedHashKey(0, 0), hash[:]); err != nil {
return err
}
if err := block.SetHighestRoundAndSetID(0, 0); err != nil {
Expand Down
15 changes: 15 additions & 0 deletions dot/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package types
import (
"fmt"

"github.com/ChainSafe/gossamer/internal/primitives/runtime"
"github.com/ChainSafe/gossamer/pkg/scale"
)

Expand All @@ -15,6 +16,20 @@ type Block struct {
Body Body
}

// NewBlockFromGeneric returns a new Block from a generic block
func NewBlockFromGeneric[N runtime.Number, H runtime.Hash, E runtime.Extrinsic](gb runtime.Block[N, H, E]) (
*Block, error) {
header, err := NewHeaderFromGeneric(gb.Header())
if err != nil {
return nil, err
}

return &Block{
Header: *header,
Body: NewBodyFromGeneric(gb.Extrinsics()),
}, nil
}

// NewBlock returns a new Block
func NewBlock(header Header, body Body) Block {
return Block{
Expand Down
49 changes: 49 additions & 0 deletions dot/types/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
"bytes"
"testing"

"github.com/ChainSafe/gossamer/internal/primitives/core/hash"
"github.com/ChainSafe/gossamer/internal/primitives/runtime"
"github.com/ChainSafe/gossamer/internal/primitives/runtime/generic"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/pkg/scale"

Expand Down Expand Up @@ -142,3 +145,49 @@ func TestScaleUnmarshal(t *testing.T) {
require.EqualError(t, err,
"decoding struct: unmarshalling field at index 0: decoding struct: unmarshalling field at index 4: decoding struct: unmarshalling field at index 1: byte array length 3472328296227680304 exceeds max value of uint32") //nolint
}

func TestNewBlockFromGeneric(t *testing.T) {
parentHash := hash.NewRandomH256()
stateRoot := hash.NewRandomH256()
extrinsicsRoot := hash.NewRandomH256()
blockNumber := uint64(123)

digest := runtime.Digest{
Logs: []runtime.DigestItem{
runtime.NewDigestItem(runtime.Consensus{
ConsensusEngineID: runtime.ConsensusEngineID{'B', 'E', 'E', 'F'},
Bytes: []byte("test"),
}),
runtime.NewDigestItem(runtime.Seal{
ConsensusEngineID: runtime.ConsensusEngineID{'S', 'E', 'A', 'L'},
Bytes: []byte("test"),
}),
runtime.NewDigestItem(runtime.PreRuntime{
ConsensusEngineID: runtime.ConsensusEngineID{'B', 'A', 'B', 'E'},
Bytes: []byte("test"),
}),
runtime.NewDigestItem(runtime.RuntimeEnvironmentUpdated{}),
},
}

gh := generic.NewHeader[uint64, hash.H256, runtime.BlakeTwo256](
blockNumber,
extrinsicsRoot,
stateRoot,
parentHash,
digest,
)

block := generic.NewBlock[runtime.BlakeTwo256, runtime.Extrinsic, uint64, hash.H256](
gh,
[]runtime.Extrinsic{
runtime.OpaqueExtrinsic{Data: []byte{1, 2, 3}},
runtime.OpaqueExtrinsic{Data: []byte{3, 4, 5}},
},
)

newBlock, err := NewBlockFromGeneric(block)
require.NoError(t, err)
require.NotNil(t, newBlock)
require.Equal(t, block.Header().Hash().Bytes(), newBlock.Header.Hash().ToBytes())
}
11 changes: 11 additions & 0 deletions dot/types/body.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,24 @@ import (
"fmt"
"math/big"

"github.com/ChainSafe/gossamer/internal/primitives/runtime"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/pkg/scale"
)

// Body is the extrinsics(not encoded) inside a state block.
type Body []Extrinsic

// NewBodyFromGeneric returns a new Body from a generic extrinsics array.
func NewBodyFromGeneric[E runtime.Extrinsic](extrinsics []E) Body {
var body Body
for _, ext := range extrinsics {
body = append(body, Extrinsic(ext.Bytes()))
}

return body
}

// NewBody returns a Body from an Extrinsic array.
func NewBody(e []Extrinsic) *Body {
body := Body(e)
Expand Down
11 changes: 11 additions & 0 deletions dot/types/body_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package types
import (
"testing"

"github.com/ChainSafe/gossamer/internal/primitives/runtime"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/pkg/scale"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -58,3 +59,13 @@ func TestBodyFromExtrinsicStrings(t *testing.T) {

require.Equal(t, bodyFromByteExtrinsics, bodyFromStringExtrinsics)
}

func TestFromGenericBody(t *testing.T) {
extrinsics := []runtime.Extrinsic{
runtime.OpaqueExtrinsic{Data: []byte{1, 2, 3}},
runtime.OpaqueExtrinsic{Data: []byte{3, 4, 5}},
}
body := NewBodyFromGeneric(extrinsics)

require.Equal(t, *NewBody([]Extrinsic{{1, 2, 3}, {3, 4, 5}}), body)
}
43 changes: 43 additions & 0 deletions dot/types/digest.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"strings"

"github.com/ChainSafe/gossamer/internal/primitives/runtime"
"github.com/ChainSafe/gossamer/pkg/scale"
)

Expand Down Expand Up @@ -98,6 +99,48 @@ func NewDigestItem() DigestItem {
// Digest is slice of DigestItem
type Digest []DigestItem

// NewDigestFromGeneric returns a new Digest from a generic digest
func NewDigestFromGeneric(gd runtime.Digest) (Digest, error) {
newDigest := Digest{}
for _, log := range gd.Logs {
value, err := log.Value()
if err != nil {
return nil, err
}

var digest any

switch v := value.(type) {
case runtime.PreRuntime:
digest = PreRuntimeDigest{
ConsensusEngineID: ConsensusEngineID(v.ConsensusEngineID),
Data: v.Bytes,
}
case runtime.Consensus:
digest = ConsensusDigest{
ConsensusEngineID: ConsensusEngineID(v.ConsensusEngineID),
Data: v.Bytes,
}
case runtime.Seal:
digest = SealDigest{
ConsensusEngineID: ConsensusEngineID(v.ConsensusEngineID),
Data: v.Bytes,
}
case runtime.RuntimeEnvironmentUpdated:
digest = RuntimeEnvironmentUpdated{}
default:
return nil, fmt.Errorf("unsupported type")
}

err = newDigest.Add(digest)
if err != nil {
return nil, err
}
}

return newDigest, nil
}

func (d *Digest) Add(values ...any) (err error) {
for _, value := range values {
item := DigestItem{}
Expand Down
25 changes: 25 additions & 0 deletions dot/types/digest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"bytes"
"testing"

"github.com/ChainSafe/gossamer/internal/primitives/runtime"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/pkg/scale"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -244,3 +245,27 @@ func TestSealDigest(t *testing.T) {
require.NoError(t, err)
require.Equal(t, diValue, vValue)
}

func TestFromGenericDigest(t *testing.T) {
digest := runtime.Digest{
Logs: []runtime.DigestItem{
runtime.NewDigestItem(runtime.Consensus{
ConsensusEngineID: runtime.ConsensusEngineID{'B', 'E', 'E', 'F'},
Bytes: []byte("test"),
}),
runtime.NewDigestItem(runtime.Seal{
ConsensusEngineID: runtime.ConsensusEngineID{'S', 'E', 'A', 'L'},
Bytes: []byte("test"),
}),
runtime.NewDigestItem(runtime.PreRuntime{
ConsensusEngineID: runtime.ConsensusEngineID{'B', 'A', 'B', 'E'},
Bytes: []byte("test"),
}),
runtime.NewDigestItem(runtime.RuntimeEnvironmentUpdated{}),
},
}

d, err := NewDigestFromGeneric(digest)
require.NoError(t, err)
require.NotNil(t, d)
}
21 changes: 21 additions & 0 deletions dot/types/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"errors"
"fmt"

"github.com/ChainSafe/gossamer/internal/primitives/runtime"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/pkg/scale"
)
Expand All @@ -23,6 +24,26 @@ type Header struct {
hash common.Hash
}

// NewHeaderFromGeneric returns a new Header from a generic header
func NewHeaderFromGeneric[N runtime.Number, H runtime.Hash](gh runtime.Header[N, H]) (*Header, error) {
header := &Header{
ParentHash: common.NewHashFromGeneric(gh.ParentHash()),
Number: uint(gh.Number()),
StateRoot: common.NewHashFromGeneric(gh.StateRoot()),
ExtrinsicsRoot: common.NewHashFromGeneric(gh.ExtrinsicsRoot()),
hash: common.NewHashFromGeneric(gh.Hash()),
}

digests, err := NewDigestFromGeneric(gh.Digest())
if err != nil {
return nil, err
}

header.Digest = digests

return header, nil
}

// NewHeader creates a new block header and sets its hash field
func NewHeader(parentHash, stateRoot, extrinsicsRoot common.Hash,
number uint, digest Digest) (blockHeader *Header) {
Expand Down
Loading

0 comments on commit 6ab8bad

Please sign in to comment.