Skip to content

Commit

Permalink
feat: parachain compatibility (#412)
Browse files Browse the repository at this point in the history
* modules
    * `aura-ext` 
        * block executor
    * `parachain-system`
* api
    *  `collect_collation_info`
    *  `validate_block`
* internal trie representation for `io.Storage` and `io.TransactionBroker`
  • Loading branch information
failfmi authored Jun 14, 2024
1 parent 095050b commit c9a9654
Show file tree
Hide file tree
Showing 175 changed files with 7,388 additions and 267 deletions.
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ start-network:
cd ../../../..; \
WASMTIME_BACKTRACE_DETAILS=1 RUST_LOG=runtime=trace ./target/release/node-template --dev --execution=wasm

parachain-build:
cp $(BUILD_PATH)/parachain.wasm polkadot-sdk/cumulus/polkadot-parachain/src/chain_spec/runtime.wasm; \
cd polkadot-sdk; \
cargo build --release -p polkadot-parachain-bin

# gossamer node configuration
CHAIN_SPEC_PLAIN = ../testdata/chain-spec/plain.json
CHAIN_SPEC_UPDATED = ../testdata/chain-spec/plain-updated.json
Expand Down
4 changes: 2 additions & 2 deletions api/benchmarking/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ type Module struct {
logger log.RuntimeLogger
}

func New(systemIndex sc.U8, modules []primitives.Module, decoder types.RuntimeDecoder, logger log.RuntimeLogger) Module {
func New(systemIndex sc.U8, modules []primitives.Module, decoder types.RuntimeDecoder, storage io.Storage, transactionBroker io.TransactionBroker, logger log.RuntimeLogger) Module {
systemModule := primitives.MustGetModule(systemIndex, modules).(system.Module)

return Module{
modules: modules,
systemModule: systemModule,
decoder: decoder,
transactional: support.NewTransactional[primitives.PostDispatchInfo](logger),
transactional: support.NewTransactional[primitives.PostDispatchInfo](storage, transactionBroker, logger),
memUtils: utils.NewMemoryTranslator(),
hashing: io.NewHashing(),
logger: logger,
Expand Down
92 changes: 92 additions & 0 deletions api/collect_collation_info/module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package collect_collation_info

import (
"bytes"

sc "github.com/LimeChain/goscale"
"github.com/LimeChain/gosemble/constants/metadata"
"github.com/LimeChain/gosemble/frame/parachain_system"
"github.com/LimeChain/gosemble/primitives/hashing"
"github.com/LimeChain/gosemble/primitives/log"
primitives "github.com/LimeChain/gosemble/primitives/types"
"github.com/LimeChain/gosemble/utils"
)

const (
ApiModuleName = "CollectCollationInfo"
apiVersion = 2
)

// Module implements the CollectCollationInfo Runtime API definition.
//
// For more information about API definition, see:
// https://github.com/paritytech/polkadot-sdk/blob/master/cumulus/primitives/core/src/lib.rs#L378
type Module struct {
parachainSystem parachain_system.Module
memUtils utils.WasmMemoryTranslator
logger log.RuntimeLogger
}

func New(parachainSystem parachain_system.Module, logger log.RuntimeLogger) Module {
return Module{
memUtils: utils.NewMemoryTranslator(),
parachainSystem: parachainSystem,
logger: logger,
}
}

// Name returns the name of the api module
func (m Module) Name() string {
return ApiModuleName
}

// Item returns the first 8 bytes of the Blake2b hash of the name and version of the api module.
func (m Module) Item() primitives.ApiItem {
hash := hashing.MustBlake2b8([]byte(ApiModuleName))
return primitives.NewApiItem(hash, apiVersion)
}

// CollectCollationInfo returns the collation information by the header of a built block.
// It takes two arguments:
// - dataPtr: Pointer to the data in the Wasm memory.
// - dataLen: Length of the data.
// which represent the SCALE-encoded block header.
// Returns a pointer-size of the SCALE-encoded collation information.
func (m Module) CollectCollationInfo(dataPtr int32, dataLen int32) int64 {
b := m.memUtils.GetWasmMemorySlice(dataPtr, dataLen)
buffer := bytes.NewBuffer(b)

header, err := primitives.DecodeHeader(buffer)
if err != nil {
m.logger.Critical(err.Error())
}

collationInfo, err := m.parachainSystem.CollectCollationInfo(header)
if err != nil {
m.logger.Critical(err.Error())
}

return m.memUtils.BytesToOffsetAndSize(collationInfo.Bytes())
}

func (m Module) Metadata() primitives.RuntimeApiMetadata {
methods := sc.Sequence[primitives.RuntimeApiMethodMetadata]{
primitives.RuntimeApiMethodMetadata{
Name: "collect_collation_info",
Inputs: sc.Sequence[primitives.RuntimeApiMethodParamMetadata]{
primitives.RuntimeApiMethodParamMetadata{
Name: "header",
Type: sc.ToCompact(metadata.Header),
},
},
Output: sc.ToCompact(metadata.TypesParachainValidationResult),
Docs: sc.Sequence[sc.Str]{"Returns the collation information by the block header."},
},
}

return primitives.RuntimeApiMetadata{
Name: ApiModuleName,
Methods: methods,
Docs: sc.Sequence[sc.Str]{" The collect collation info api."},
}
}
146 changes: 146 additions & 0 deletions api/collect_collation_info/module_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package collect_collation_info

import (
"errors"
"io"
"testing"

"github.com/ChainSafe/gossamer/lib/common"
sc "github.com/LimeChain/goscale"
"github.com/LimeChain/gosemble/constants/metadata"
"github.com/LimeChain/gosemble/mocks"
"github.com/LimeChain/gosemble/primitives/log"
"github.com/LimeChain/gosemble/primitives/parachain"
primitives "github.com/LimeChain/gosemble/primitives/types"
"github.com/stretchr/testify/assert"
)

var (
dataPtr = int32(0)
dataLen = int32(1)
ptrAndSize = int64(6)

parentHash = common.MustHexToHash("0x3aa96b0149b6ca3688878bdbd19464448624136398e3ce45b9e755d3ab61355c").ToBytes()
stateRoot = common.MustHexToHash("0x3aa96b0149b6ca3688878bdbd19464448624136398e3ce45b9e755d3ab61355b").ToBytes()
extrinsicsRoot = common.MustHexToHash("0x3aa96b0149b6ca3688878bdbd19464448624136398e3ce45b9e755d3ab61355a").ToBytes()
header = primitives.Header{
ParentHash: primitives.Blake2bHash{
FixedSequence: sc.BytesToFixedSequenceU8(parentHash)},
Number: 5,
StateRoot: primitives.H256{FixedSequence: sc.BytesToFixedSequenceU8(stateRoot)},
ExtrinsicsRoot: primitives.H256{FixedSequence: sc.BytesToFixedSequenceU8(extrinsicsRoot)},
Digest: primitives.NewDigest(sc.Sequence[primitives.DigestItem]{}),
}
collationInfo = parachain.CollationInfo{
UpwardMessages: sc.Sequence[parachain.UpwardMessage]{},
HorizontalMessages: sc.Sequence[parachain.OutboundHrmpMessage]{},
ValidationCode: sc.Option[sc.Sequence[sc.U8]]{},
ProcessedDownwardMessages: 0,
HrmpWatermark: 0,
HeadData: sc.Sequence[sc.U8]{},
}
errPanic = errors.New("panic")
)

var (
mockParachainSystemModule *mocks.ParachainSystemModule
mockMemoryUtils *mocks.MemoryTranslator
)

func Test_Module_Name(t *testing.T) {
target := setup()

result := target.Name()

assert.Equal(t, ApiModuleName, result)
}

func Test_Module_Item(t *testing.T) {
target := setup()

hexName := common.MustBlake2b8([]byte(ApiModuleName))
expect := primitives.NewApiItem(hexName, apiVersion)

result := target.Item()

assert.Equal(t, expect, result)
}

func Test_Module_CollectCollationInfo(t *testing.T) {
target := setup()

mockMemoryUtils.On("GetWasmMemorySlice", dataPtr, dataLen).Return(header.Bytes())
mockParachainSystemModule.On("CollectCollationInfo", header).Return(collationInfo, nil)
mockMemoryUtils.On("BytesToOffsetAndSize", collationInfo.Bytes()).Return(ptrAndSize)

result := target.CollectCollationInfo(dataPtr, dataLen)

assert.Equal(t, ptrAndSize, result)
mockMemoryUtils.AssertCalled(t, "GetWasmMemorySlice", dataPtr, dataLen)
mockParachainSystemModule.AssertCalled(t, "CollectCollationInfo", header)
mockMemoryUtils.AssertCalled(t, "BytesToOffsetAndSize", collationInfo.Bytes())
}

func Test_Module_CollectCollationInfo_Header_Panics(t *testing.T) {
target := setup()

mockMemoryUtils.On("GetWasmMemorySlice", dataPtr, dataLen).Return([]byte{})

assert.PanicsWithValue(t,
io.EOF.Error(),
func() { target.CollectCollationInfo(dataPtr, dataLen) },
)

mockMemoryUtils.AssertCalled(t, "GetWasmMemorySlice", dataPtr, dataLen)
}

func Test_Module_CollectCollationInfo_Panics(t *testing.T) {
target := setup()

expectedErr := errors.New("panic")

mockMemoryUtils.On("GetWasmMemorySlice", dataPtr, dataLen).Return(header.Bytes())
mockParachainSystemModule.On("CollectCollationInfo", header).Return(collationInfo, expectedErr)

assert.PanicsWithValue(t,
errPanic.Error(),
func() { target.CollectCollationInfo(dataPtr, dataLen) },
)

mockMemoryUtils.AssertCalled(t, "GetWasmMemorySlice", dataPtr, dataLen)
mockParachainSystemModule.On("CollectCollationInfo", header).Return(collationInfo, expectedErr)
}

func Test_Module_Metadata(t *testing.T) {
target := setup()

expect := primitives.RuntimeApiMetadata{
Name: ApiModuleName,
Methods: sc.Sequence[primitives.RuntimeApiMethodMetadata]{
primitives.RuntimeApiMethodMetadata{
Name: "collect_collation_info",
Inputs: sc.Sequence[primitives.RuntimeApiMethodParamMetadata]{
primitives.RuntimeApiMethodParamMetadata{
Name: "header",
Type: sc.ToCompact(metadata.Header),
},
},
Output: sc.ToCompact(metadata.TypesParachainValidationResult),
Docs: sc.Sequence[sc.Str]{"Returns the collation information by the block header."},
},
},
Docs: sc.Sequence[sc.Str]{" The collect collation info api."},
}

assert.Equal(t, expect, target.Metadata())
}

func setup() Module {
mockParachainSystemModule = new(mocks.ParachainSystemModule)
mockMemoryUtils = new(mocks.MemoryTranslator)

target := New(mockParachainSystemModule, log.NewLogger())
target.memUtils = mockMemoryUtils

return target
}
4 changes: 4 additions & 0 deletions api/genesis_builder/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ func (m Module) CreateDefaultConfig() int64 {
m.logger.Critical(err.Error())
}

if len(gcJsonBytes) == 0 {
continue
}

// gcJsonBytes[1:len(gcJsonBytes)-1] trims first and last characters which represent start and end of the json
// CreateDefaultConfig returns a valid json (e.g. {"system":{}}), and here we need it as a json field
gcs = append(gcs, string(gcJsonBytes[1:len(gcJsonBytes)-1]))
Expand Down
12 changes: 12 additions & 0 deletions api/genesis_builder/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ func Test_CreateDefaultConfig(t *testing.T) {
mockMemoryUtils.AssertCalled(t, "BytesToOffsetAndSize", genesisSequence)
}

func Test_CreateDefaultConfig_Empty(t *testing.T) {
genesisSequence := sc.BytesToSequenceU8([]byte("{}"))
setup()
mockModule.On("CreateDefaultConfig").Return([]byte{}, nil)
mockMemoryUtils.On("BytesToOffsetAndSize", genesisSequence.Bytes()).Return(int64(0))

target.CreateDefaultConfig()

mockModule.AssertCalled(t, "CreateDefaultConfig")
mockMemoryUtils.AssertCalled(t, "BytesToOffsetAndSize", genesisSequence.Bytes())
}

func Test_CreateDefaultConfig_Error(t *testing.T) {
setup()
mockModule.On("CreateDefaultConfig").Return(genesis, errors.New("err"))
Expand Down
61 changes: 61 additions & 0 deletions api/metadata/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,24 @@ func (m Module) basicTypes() sc.Sequence[primitives.MetadataType] {
primitives.NewMetadataTypeDefinitionComposite(sc.Sequence[primitives.MetadataTypeDefinitionField]{
primitives.NewMetadataTypeDefinitionField(metadata.TypesFixedSequence32U8)})),

primitives.NewMetadataTypeWithParam(metadata.TypesOptionH256, "Option<H256>", sc.Sequence[sc.Str]{"Option"}, primitives.NewMetadataTypeDefinitionVariant(
sc.Sequence[primitives.MetadataDefinitionVariant]{
primitives.NewMetadataDefinitionVariant(
"None",
sc.Sequence[primitives.MetadataTypeDefinitionField]{},
optionNoneIdx,
""),
primitives.NewMetadataDefinitionVariant(
"Some",
sc.Sequence[primitives.MetadataTypeDefinitionField]{
primitives.NewMetadataTypeDefinitionField(metadata.TypesH256),
},
optionSomeIdx,
""),
}),
primitives.NewMetadataTypeParameter(metadata.TypesH256, "T"),
),

primitives.NewMetadataType(
metadata.TypesCompactU128,
"compact U128",
Expand Down Expand Up @@ -861,6 +879,24 @@ func (m Module) basicTypes() sc.Sequence[primitives.MetadataType] {
primitives.NewMetadataTypeParameter(metadata.TypesSequenceU8, "T"),
),

primitives.NewMetadataTypeWithParam(metadata.TypesOptionU32, "Option<U32>", sc.Sequence[sc.Str]{"Option"}, primitives.NewMetadataTypeDefinitionVariant(
sc.Sequence[primitives.MetadataDefinitionVariant]{
primitives.NewMetadataDefinitionVariant(
"None",
sc.Sequence[primitives.MetadataTypeDefinitionField]{},
optionNoneIdx,
""),
primitives.NewMetadataDefinitionVariant(
"Some",
sc.Sequence[primitives.MetadataTypeDefinitionField]{
primitives.NewMetadataTypeDefinitionField(metadata.PrimitiveTypesU32),
},
optionSomeIdx,
""),
}),
primitives.NewMetadataTypeParameter(metadata.PrimitiveTypesU32, "T"),
),

primitives.NewMetadataType(metadata.TypesSequenceSequenceU8, "[][]byte", primitives.NewMetadataTypeDefinitionSequence(sc.ToCompact(metadata.TypesSequenceU8))),

primitives.NewMetadataType(
Expand Down Expand Up @@ -904,6 +940,31 @@ func (m Module) basicTypes() sc.Sequence[primitives.MetadataType] {
},
),
),
primitives.NewMetadataTypeWithPath(
metadata.TypesParachainOutboundHrmpMessage,
"parachain primitives outbound hrmp messages",
sc.Sequence[sc.Str]{"parachain", "primitives", "OutboundHrmpMessages"},
primitives.NewMetadataTypeDefinitionComposite(sc.Sequence[primitives.MetadataTypeDefinitionField]{
primitives.NewMetadataTypeDefinitionFieldWithNames(metadata.PrimitiveTypesU32, "id", "Id"),
primitives.NewMetadataTypeDefinitionFieldWithNames(metadata.TypesSequenceU8, "data", "Data"),
}),
),
primitives.NewMetadataType(metadata.TypesParachainOutboundHrmpMessages,
"[]OutboundHrmpMessage",
primitives.NewMetadataTypeDefinitionSequence(sc.ToCompact(metadata.TypesParachainOutboundHrmpMessage)),
),
primitives.NewMetadataTypeWithPath(
metadata.TypesParachainValidationResult,
"parachain primitives validationResult", sc.Sequence[sc.Str]{"parachain", "primitives", "ValidationResult"},
primitives.NewMetadataTypeDefinitionComposite(sc.Sequence[primitives.MetadataTypeDefinitionField]{
primitives.NewMetadataTypeDefinitionFieldWithNames(metadata.TypesSequenceSequenceU8, "upward_messages", "UpdwardMessages"),
primitives.NewMetadataTypeDefinitionFieldWithNames(metadata.TypesParachainOutboundHrmpMessages, "horizontal_messages", "HorizontalMessages"),
primitives.NewMetadataTypeDefinitionFieldWithNames(metadata.TypesOptionSequenceU8, "validation_code", "ValidationCode"),
primitives.NewMetadataTypeDefinitionFieldWithNames(metadata.PrimitiveTypesU32, "processed_downward_messages", "ProcessedDownwardMessages"),
primitives.NewMetadataTypeDefinitionFieldWithNames(metadata.PrimitiveTypesU32, "hrmp_watermark", "HrmpWatermark"),
primitives.NewMetadataTypeDefinitionFieldWithNames(metadata.TypesSequenceU8, "head_data", "HeadData"),
}),
),

// 161
primitives.NewMetadataType(
Expand Down
Loading

0 comments on commit c9a9654

Please sign in to comment.