diff --git a/dot/sync/test_helpers.go b/dot/sync/test_helpers.go index 4345afcb7f..b424b277c5 100644 --- a/dot/sync/test_helpers.go +++ b/dot/sync/test_helpers.go @@ -32,9 +32,9 @@ import ( "github.com/ChainSafe/gossamer/lib/runtime" rtstorage "github.com/ChainSafe/gossamer/lib/runtime/storage" "github.com/ChainSafe/gossamer/lib/runtime/wasmer" - "github.com/ChainSafe/gossamer/lib/scale" "github.com/ChainSafe/gossamer/lib/transaction" "github.com/ChainSafe/gossamer/lib/trie" + "github.com/ChainSafe/gossamer/pkg/scale" log "github.com/ChainSafe/log15" "github.com/stretchr/testify/require" @@ -166,10 +166,11 @@ func BuildBlock(t *testing.T, instance runtime.Instance, parent *types.Header, e require.NoError(t, err) // decode inherent extrinsics - exts, err := scale.Decode(inherentExts, [][]byte{}) + var exts [][]byte + err = scale.Unmarshal(inherentExts, &exts) require.NoError(t, err) - inExt := exts.([][]byte) + inExt := exts var body *types.Body if ext != nil { @@ -191,7 +192,7 @@ func BuildBlock(t *testing.T, instance runtime.Instance, parent *types.Header, e // apply each inherent extrinsic for _, ext := range inExt { - in, err := scale.Encode(ext) //nolint + in, err := scale.Marshal(ext) //nolint require.NoError(t, err) ret, err := instance.ApplyExtrinsic(in) diff --git a/lib/babe/build.go b/lib/babe/build.go index 66b181caf8..3c2d3ecbd2 100644 --- a/lib/babe/build.go +++ b/lib/babe/build.go @@ -29,8 +29,8 @@ import ( "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/lib/runtime" - "github.com/ChainSafe/gossamer/lib/scale" "github.com/ChainSafe/gossamer/lib/transaction" + "github.com/ChainSafe/gossamer/pkg/scale" ) const ( @@ -305,14 +305,15 @@ func (b *BlockBuilder) buildBlockInherents(slot Slot) ([][]byte, error) { } // decode inherent extrinsics - exts, err := scale.Decode(inherentExts, [][]byte{}) + var exts [][]byte + err = scale.Unmarshal(inherentExts, &exts) if err != nil { return nil, err } // apply each inherent extrinsic - for _, ext := range exts.([][]byte) { - in, err := scale.Encode(ext) + for _, ext := range exts { + in, err := scale.Marshal(ext) if err != nil { return nil, err } @@ -328,7 +329,7 @@ func (b *BlockBuilder) buildBlockInherents(slot Slot) ([][]byte, error) { } } - return exts.([][]byte), nil + return exts, nil } func (b *BlockBuilder) addToQueue(txs []*transaction.ValidTransaction) { @@ -352,12 +353,18 @@ func ExtrinsicsToBody(inherents [][]byte, txs []*transaction.ValidTransaction) ( extrinsics := types.BytesArrayToExtrinsics(inherents) for _, tx := range txs { - decExt, err := scale.Decode(tx.Extrinsic, []byte{}) + var decExt []byte + err := scale.Unmarshal(tx.Extrinsic, &decExt) if err != nil { return nil, err } - extrinsics = append(extrinsics, decExt.([]byte)) + extrinsics = append(extrinsics, decExt) } - return types.NewBodyFromExtrinsics(extrinsics) + enc, err := scale.Marshal(extrinsics) + if err != nil { + return nil, err + } + body := types.Body(enc) + return &body, nil } diff --git a/lib/babe/build_test.go b/lib/babe/build_test.go index f71e94b0c0..b75825f719 100644 --- a/lib/babe/build_test.go +++ b/lib/babe/build_test.go @@ -27,8 +27,8 @@ import ( "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/lib/crypto/sr25519" "github.com/ChainSafe/gossamer/lib/runtime" - "github.com/ChainSafe/gossamer/lib/scale" "github.com/ChainSafe/gossamer/lib/transaction" + "github.com/ChainSafe/gossamer/pkg/scale" log "github.com/ChainSafe/log15" cscale "github.com/centrifuge/go-substrate-rpc-client/v3/scale" @@ -90,11 +90,13 @@ func createTestExtrinsic(t *testing.T, rt runtime.Instance, genHash common.Hash, rawMeta, err := rt.Metadata() require.NoError(t, err) - decoded, err := scale.Decode(rawMeta, []byte{}) + //decoded, err := scale.Decode(rawMeta, []byte{}) + var decoded []byte + err = scale.Unmarshal(rawMeta, &decoded) require.NoError(t, err) meta := &ctypes.Metadata{} - err = ctypes.DecodeFromBytes(decoded.([]byte), meta) + err = ctypes.DecodeFromBytes(decoded, meta) require.NoError(t, err) rv, err := rt.Version() @@ -313,11 +315,12 @@ func TestBuildAndApplyExtrinsic(t *testing.T) { // build extrinsic rawMeta, err := babeService.rt.Metadata() require.NoError(t, err) - decoded, err := scale.Decode(rawMeta, []byte{}) + var decoded []byte + err = scale.Unmarshal(rawMeta, []byte{}) require.NoError(t, err) meta := &ctypes.Metadata{} - err = ctypes.DecodeFromBytes(decoded.([]byte), meta) + err = ctypes.DecodeFromBytes(decoded, meta) require.NoError(t, err) rv, err := babeService.rt.Version() diff --git a/lib/babe/errors.go b/lib/babe/errors.go index d8f4b41d94..e74f565ac2 100644 --- a/lib/babe/errors.go +++ b/lib/babe/errors.go @@ -17,8 +17,7 @@ import ( "errors" "fmt" - "github.com/ChainSafe/gossamer/lib/common/optional" - "github.com/ChainSafe/gossamer/lib/scale" + "github.com/ChainSafe/gossamer/pkg/scale" ) var ( @@ -61,6 +60,19 @@ var ( errNilRuntime = errors.New("runtime is nil") errInvalidResult = errors.New("invalid error value") errNoEpochData = errors.New("no epoch data found for upcoming epoch") + + other Other + invalidCustom InvalidCustom + unknownCustom UnknownCustom + + dispatchError = scale.MustNewVaryingDataType(other, CannotLookup{}, BadOrigin{}, Module{}) + invalid = scale.MustNewVaryingDataType(Call{}, Payment{}, Future{}, Stale{}, BadProof{}, AncientBirthBlock{}, + ExhaustsResources{}, invalidCustom, BadMandatory{}, MandatoryDispatch{}) + unknown = scale.MustNewVaryingDataType(ValidityCannotLookup{}, NoUnsignedValidator{}, unknownCustom) + + okRes = scale.NewResult(nil, dispatchError) + errRes = scale.NewResult(invalid, unknown) + result = scale.NewResult(okRes, errRes) ) // A DispatchOutcomeError is outcome of dispatching the extrinsic @@ -81,88 +93,207 @@ func (e TransactionValidityError) Error() string { return fmt.Sprintf("transaction validity error: %s", e.msg) } -func determineCustomModuleErr(res []byte) error { - if len(res) < 3 { - return errInvalidResult - } - errMsg, err := optional.NewBytes(false, nil).DecodeBytes(res[2:]) - if err != nil { - return err - } - return fmt.Errorf("index: %d code: %d message: %s", res[0], res[1], errMsg.String()) +// UnmarshalError occurs when unmarshalling fails +type UnmarshalError struct { + msg string } -func determineDispatchErr(res []byte) error { - switch res[0] { - case 0: - unKnownError, _ := scale.Decode(res[1:], []byte{}) - return &DispatchOutcomeError{fmt.Sprintf("unknown error: %s", string(unKnownError.([]byte)))} - case 1: - return &DispatchOutcomeError{"failed lookup"} - case 2: - return &DispatchOutcomeError{"bad origin"} - case 3: - return &DispatchOutcomeError{fmt.Sprintf("custom module error: %s", determineCustomModuleErr(res[1:]))} - } - return errInvalidResult +func (e UnmarshalError) Error() string { + return fmt.Sprintf("unmarshal error: %s", e.msg) } -func determineInvalidTxnErr(res []byte) error { - switch res[0] { - case 0: +// Other Some error occurred +type Other string + +// Index Returns VDT index +func (err Other) Index() uint { return 0 } + +// CannotLookup Failed to lookup some data +type CannotLookup struct{} + +// Index Returns VDT index +func (err CannotLookup) Index() uint { return 1 } + +// BadOrigin A bad origin +type BadOrigin struct{} + +// Index Returns VDT index +func (err BadOrigin) Index() uint { return 2 } + +// Module A custom error in a module +type Module struct { + Idx uint8 + Err uint8 + Message *string +} + +// Index Returns VDT index +func (err Module) Index() uint { return 3 } + +func (err Module) string() string { + return fmt.Sprintf("index: %d code: %d message: %x", err.Idx, err.Err, *err.Message) +} + +// ValidityCannotLookup Could not lookup some information that is required to validate the transaction +type ValidityCannotLookup struct{} + +// Index Returns VDT index +func (err ValidityCannotLookup) Index() uint { return 0 } + +// NoUnsignedValidator No validator found for the given unsigned transaction +type NoUnsignedValidator struct{} + +// Index Returns VDT index +func (err NoUnsignedValidator) Index() uint { return 1 } + +// UnknownCustom Any other custom unknown validity that is not covered +type UnknownCustom uint8 + +// Index Returns VDT index +func (err UnknownCustom) Index() uint { return 2 } + +// Call The call of the transaction is not expected +type Call struct{} + +// Index Returns VDT index +func (err Call) Index() uint { return 0 } + +// Payment General error to do with the inability to pay some fees (e.g. account balance too low) +type Payment struct{} + +// Index Returns VDT index +func (err Payment) Index() uint { return 1 } + +// Future General error to do with the transaction not yet being valid (e.g. nonce too high) +type Future struct{} + +// Index Returns VDT index +func (err Future) Index() uint { return 2 } + +// Stale General error to do with the transaction being outdated (e.g. nonce too low) +type Stale struct{} + +// Index Returns VDT index +func (err Stale) Index() uint { return 3 } + +// BadProof General error to do with the transaction’s proofs (e.g. signature) +type BadProof struct{} + +// Index Returns VDT index +func (err BadProof) Index() uint { return 4 } + +// AncientBirthBlock The transaction birth block is ancient +type AncientBirthBlock struct{} + +// Index Returns VDT index +func (err AncientBirthBlock) Index() uint { return 5 } + +// ExhaustsResources The transaction would exhaust the resources of current block +type ExhaustsResources struct{} + +// Index Returns VDT index +func (err ExhaustsResources) Index() uint { return 6 } + +// InvalidCustom Any other custom invalid validity that is not covered +type InvalidCustom uint8 + +// Index Returns VDT index +func (err InvalidCustom) Index() uint { return 7 } + +// BadMandatory An extrinsic with a Mandatory dispatch resulted in Error +type BadMandatory struct{} + +// Index Returns VDT index +func (err BadMandatory) Index() uint { return 8 } + +// MandatoryDispatch A transaction with a mandatory dispatch +type MandatoryDispatch struct{} + +// Index Returns VDT index +func (err MandatoryDispatch) Index() uint { return 9 } + +func determineErrType(vdt scale.VaryingDataType) error { + switch val := vdt.Value().(type) { + case Other: + return &DispatchOutcomeError{fmt.Sprintf("unknown error: %s", val)} + case CannotLookup: + return &DispatchOutcomeError{"failed lookup"} + case BadOrigin: + return &DispatchOutcomeError{"bad origin"} + case Module: + return &DispatchOutcomeError{fmt.Sprintf("custom module error: %s", val.string())} + case Call: return &TransactionValidityError{"call of the transaction is not expected"} - case 1: + case Payment: return &TransactionValidityError{"invalid payment"} - case 2: + case Future: return &TransactionValidityError{"invalid transaction"} - case 3: + case Stale: return &TransactionValidityError{"outdated transaction"} - case 4: + case BadProof: return &TransactionValidityError{"bad proof"} - case 5: + case AncientBirthBlock: return &TransactionValidityError{"ancient birth block"} - case 6: + case ExhaustsResources: return &TransactionValidityError{"exhausts resources"} - case 7: - return &TransactionValidityError{fmt.Sprintf("unknown error: %d", res[1])} - case 8: + case InvalidCustom: + return &TransactionValidityError{fmt.Sprintf("unknown error: %d", val)} + case BadMandatory: return &TransactionValidityError{"mandatory dispatch error"} - case 9: + case MandatoryDispatch: return &TransactionValidityError{"invalid mandatory dispatch"} - } - return errInvalidResult -} - -func determineUnknownTxnErr(res []byte) error { - switch res[0] { - case 0: + case ValidityCannotLookup: return &TransactionValidityError{"lookup failed"} - case 1: + case NoUnsignedValidator: return &TransactionValidityError{"validator not found"} - case 2: - return &TransactionValidityError{fmt.Sprintf("unknown error: %d", res[1])} + case UnknownCustom: + return &TransactionValidityError{fmt.Sprintf("unknown error: %d", val)} } + return errInvalidResult } func determineErr(res []byte) error { - switch res[0] { - case 0: // DispatchOutcome - switch res[1] { - case 0: - return nil - case 1: - return determineDispatchErr(res[2:]) + err := scale.Unmarshal(res, &result) + if err != nil { + return &UnmarshalError{err.Error()} + } + + ok, err := result.Unwrap() + if err != nil { + switch o := err.(type) { + case scale.WrappedErr: + errResult := o.Err.(scale.Result) + ok, err = errResult.Unwrap() + if err != nil { + switch err := err.(type) { + case scale.WrappedErr: + return determineErrType(err.Err.(scale.VaryingDataType)) + default: + return errInvalidResult + } + } else { + return determineErrType(ok.(scale.VaryingDataType)) + } default: return errInvalidResult } - case 1: // TransactionValidityError - switch res[1] { - case 0: - return determineInvalidTxnErr(res[2:]) - case 1: - return determineUnknownTxnErr(res[2:]) + } else { + switch o := ok.(type) { + case scale.Result: + _, err = o.Unwrap() + if err != nil { + switch err := err.(type) { + case scale.WrappedErr: + return determineErrType(err.Err.(scale.VaryingDataType)) + default: + return errInvalidResult + } + } else { + return nil + } + default: + return errInvalidResult } } - return errInvalidResult } diff --git a/lib/babe/errors_test.go b/lib/babe/errors_test.go index 8ec2923c6d..c495cde251 100644 --- a/lib/babe/errors_test.go +++ b/lib/babe/errors_test.go @@ -32,6 +32,16 @@ func TestApplyExtrinsicErrors(t *testing.T) { test: []byte{0, 1, 0, 0x04, 65}, expected: "dispatch outcome error: unknown error: A", }, + { + name: "Dispatch failed lookup", + test: []byte{0, 1, 1}, + expected: "dispatch outcome error: failed lookup", + }, + { + name: "Dispatch bad origin", + test: []byte{0, 1, 2}, + expected: "dispatch outcome error: bad origin", + }, { name: "Invalid txn payment error", test: []byte{1, 0, 1}, diff --git a/lib/transaction/types.go b/lib/transaction/types.go index 9c743c84a3..6d538ab37f 100644 --- a/lib/transaction/types.go +++ b/lib/transaction/types.go @@ -17,10 +17,7 @@ package transaction import ( - "encoding/binary" - "github.com/ChainSafe/gossamer/dot/types" - "github.com/ChainSafe/gossamer/lib/scale" ) // Validity struct see: https://github.com/paritytech/substrate/blob/5420de3face1349a97eb954ae71c5b0b940c31de/core/sr-primitives/src/transaction_validity.rs#L178 @@ -56,35 +53,3 @@ func NewValidTransaction(extrinsic types.Extrinsic, validity *Validity) *ValidTr Validity: validity, } } - -// Encode SCALE encodes the transaction -func (vt *ValidTransaction) Encode() ([]byte, error) { - enc := []byte(vt.Extrinsic) - - buf := make([]byte, 8) - binary.LittleEndian.PutUint64(buf, vt.Validity.Priority) - enc = append(enc, buf...) - - d, err := scale.Encode(vt.Validity.Requires) - if err != nil { - return nil, err - } - enc = append(enc, d...) - - d, err = scale.Encode(vt.Validity.Provides) - if err != nil { - return nil, err - } - enc = append(enc, d...) - - binary.LittleEndian.PutUint64(buf, vt.Validity.Longevity) - enc = append(enc, buf...) - - if vt.Validity.Propagate { - enc = append(enc, 1) - } else { - enc = append(enc, 0) - } - - return enc, nil -} diff --git a/lib/transaction/types_test.go b/lib/transaction/types_test.go index b1e9062a27..d59545dfc8 100644 --- a/lib/transaction/types_test.go +++ b/lib/transaction/types_test.go @@ -3,6 +3,7 @@ package transaction import ( "testing" + "github.com/ChainSafe/gossamer/pkg/scale" "github.com/stretchr/testify/require" ) @@ -18,7 +19,7 @@ func TestValidTransaction_Encode(t *testing.T) { extrinsic := []byte("nootwashere") vt := NewValidTransaction(extrinsic, validity) - enc, err := vt.Encode() + enc, err := scale.Marshal(vt) require.NoError(t, err) if len(enc) == 0 { diff --git a/pkg/scale/decode.go b/pkg/scale/decode.go index f08029485e..68f43405b6 100644 --- a/pkg/scale/decode.go +++ b/pkg/scale/decode.go @@ -240,6 +240,7 @@ func (ds *decodeState) decodeResult(dstv reflect.Value) (err error) { switch rb { case 0x00: tempElem := reflect.New(reflect.TypeOf(res.ok)) + tempElem.Elem().Set(reflect.ValueOf(res.ok)) err = ds.unmarshal(tempElem.Elem()) if err != nil { return @@ -251,6 +252,7 @@ func (ds *decodeState) decodeResult(dstv reflect.Value) (err error) { dstv.Set(reflect.ValueOf(res)) case 0x01: tempElem := reflect.New(reflect.TypeOf(res.err)) + tempElem.Elem().Set(reflect.ValueOf(res.err)) err = ds.unmarshal(tempElem.Elem()) if err != nil { return