Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 34 additions & 20 deletions lib/babe/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ var (
errInvalidMandatoryDispatch = errors.New("invalid mandatory dispatch")
errLookupFailed = errors.New("lookup failed")
errValidatorNotFound = errors.New("validator not found")
errBadSigner = errors.New("invalid signing address")
)

func newUnknownError(data scale.VaryingDataTypeValue) error {
Expand Down Expand Up @@ -271,55 +272,68 @@ func (MandatoryDispatch) Index() uint { return 9 }

func (MandatoryDispatch) String() string { return "mandatory dispatch" }

func determineErrType(vdt scale.VaryingDataType) error {
// BadSigner A transaction with a mandatory dispatch
type BadSigner struct{}

// Index returns VDT index
func (BadSigner) Index() uint { return 10 }

func (BadSigner) String() string { return "invalid signing address" }

func determineErrType(vdt scale.VaryingDataType) (err error) {
vdtVal, err := vdt.Value()
if err != nil {
return fmt.Errorf("getting vdt value: %w", err)
}

switch val := vdtVal.(type) {
case Other:
return &DispatchOutcomeError{fmt.Sprintf("unknown error: %s", val)}
err = &DispatchOutcomeError{fmt.Sprintf("unknown error: %s", val)}
case CannotLookup:
return &DispatchOutcomeError{"failed lookup"}
err = &DispatchOutcomeError{"failed lookup"}
case BadOrigin:
return &DispatchOutcomeError{"bad origin"}
err = &DispatchOutcomeError{"bad origin"}
case Module:
return &DispatchOutcomeError{fmt.Sprintf("custom module error: %s", val)}
err = &DispatchOutcomeError{fmt.Sprintf("custom module error: %s", val)}
case Call:
return &TransactionValidityError{errUnexpectedTxCall}
err = &TransactionValidityError{errUnexpectedTxCall}
case Payment:
return &TransactionValidityError{errInvalidPayment}
err = &TransactionValidityError{errInvalidPayment}
case Future:
return &TransactionValidityError{errInvalidTransaction}
err = &TransactionValidityError{errInvalidTransaction}
case Stale:
return &TransactionValidityError{errOutdatedTransaction}
err = &TransactionValidityError{errOutdatedTransaction}
case BadProof:
return &TransactionValidityError{errBadProof}
err = &TransactionValidityError{errBadProof}
case AncientBirthBlock:
return &TransactionValidityError{errAncientBirthBlock}
err = &TransactionValidityError{errAncientBirthBlock}
case ExhaustsResources:
return &TransactionValidityError{errExhaustsResources}
err = &TransactionValidityError{errExhaustsResources}
case InvalidCustom:
return &TransactionValidityError{newUnknownError(val)}
err = &TransactionValidityError{newUnknownError(val)}
case BadMandatory:
return &TransactionValidityError{errMandatoryDispatchError}
err = &TransactionValidityError{errMandatoryDispatchError}
case MandatoryDispatch:
return &TransactionValidityError{errInvalidMandatoryDispatch}
err = &TransactionValidityError{errInvalidMandatoryDispatch}
case ValidityCannotLookup:
return &TransactionValidityError{errLookupFailed}
err = &TransactionValidityError{errLookupFailed}
case NoUnsignedValidator:
return &TransactionValidityError{errValidatorNotFound}
err = &TransactionValidityError{errValidatorNotFound}
case UnknownCustom:
return &TransactionValidityError{newUnknownError(val)}
err = &TransactionValidityError{newUnknownError(val)}
case BadSigner:
err = &TransactionValidityError{errBadSigner}
default:
err = errInvalidResult
}

return errInvalidResult
return err
}

func determineErr(res []byte) error {
dispatchError := scale.MustNewVaryingDataType(other, CannotLookup{}, BadOrigin{}, Module{})
invalid := scale.MustNewVaryingDataType(Call{}, Payment{}, Future{}, Stale{}, BadProof{}, AncientBirthBlock{},
ExhaustsResources{}, invalidCustom, BadMandatory{}, MandatoryDispatch{})
ExhaustsResources{}, invalidCustom, BadMandatory{}, MandatoryDispatch{}, BadSigner{})
unknown := scale.MustNewVaryingDataType(ValidityCannotLookup{}, NoUnsignedValidator{}, unknownCustom)

okRes := scale.NewResult(nil, dispatchError)
Expand Down
14 changes: 10 additions & 4 deletions lib/runtime/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,20 @@ const (
// v0.9.29 polkadot
POLKADOT_RUNTIME_v0929 = "polkadot_runtime-v929"
POLKADOT_RUNTIME_V0929_FP = "polkadot_runtime-v929.compact.wasm"
POLKADOT_RUNTIME_V0929_URL = "https://github.com/paritytech/polkadot/releases/download/v0.9." +
"29/polkadot_runtime-v9290.compact.compressed.wasm?raw=true"
POLKADOT_RUNTIME_V0929_URL = "https://github.com/paritytech/polkadot/releases/download/v0.9.29/" +
"polkadot_runtime-v9290.compact.compressed.wasm?raw=true"

// v0.9.29 westend
WESTEND_RUNTIME_v0929 = "westend_runtime-v929"
WESTEND_RUNTIME_V0929_FP = "westend_runtime-v929.compact.wasm"
WESTEND_RUNTIME_V0929_URL = "https://github.com/paritytech/polkadot/releases/download/v0.9." +
"29/westend_runtime-v9290.compact.compressed.wasm?raw=true"
WESTEND_RUNTIME_V0929_URL = "https://github.com/paritytech/polkadot/releases/download/v0.9.29/" +
"westend_runtime-v9290.compact.compressed.wasm?raw=true"

// v0.9.12 westend used for zero-address bug test
WESTEND_RUNTIME_v0912 = "westend_runtime-v9111"
WESTEND_RUNTIME_V0912_FP = "westend_runtime-v9111.compact.wasm"
WESTEND_RUNTIME_V0912_URL = "https://github.com/paritytech/polkadot/releases/download/v0.9.11/" +
"westend_runtime-v9111.compact.compressed.wasm?raw=true"
)

const (
Expand Down
20 changes: 15 additions & 5 deletions lib/runtime/invalid_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,8 @@ func (i InvalidTransaction) Error() string { //skipcq: GO-W1029

// NewInvalidTransaction is constructor for InvalidTransaction
func NewInvalidTransaction() InvalidTransaction {
vdt, err := scale.NewVaryingDataType(Call{}, Payment{}, Future{}, Stale{}, BadProof{}, AncientBirthBlock{},
ExhaustsResources{}, InvalidCustom(0), BadMandatory{}, MandatoryDispatch{})
if err != nil {
panic(err)
}
vdt := scale.MustNewVaryingDataType(Call{}, Payment{}, Future{}, Stale{}, BadProof{}, AncientBirthBlock{},
ExhaustsResources{}, InvalidCustom(0), BadMandatory{}, MandatoryDispatch{}, BadSigner{})
return InvalidTransaction(vdt)
}

Expand Down Expand Up @@ -188,3 +185,16 @@ func (m MandatoryDispatch) String() string { return m.Error() }
func (MandatoryDispatch) Error() string {
return "invalid mandatory dispatch"
}

// BadSigner A transaction with a mandatory dispatch
type BadSigner struct{}

// Index returns VDT index
func (BadSigner) Index() uint { return 10 }

func (b BadSigner) String() string { return b.Error() }

// Error returns the error message associated with the MandatoryDispatch
func (BadSigner) Error() string {
return "invalid signing address"
}
8 changes: 6 additions & 2 deletions lib/runtime/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func GetRuntime(ctx context.Context, runtime string) (
return runtime, nil
}

basePath := filepath.Join(os.TempDir(), "/gossamer/runtimes/")
basePath := filepath.Join(os.TempDir(), "gossamer", "runtimes")
const perm = os.FileMode(0777)
err = os.MkdirAll(basePath, perm)
if err != nil {
Expand All @@ -78,6 +78,10 @@ func GetRuntime(ctx context.Context, runtime string) (
case WESTEND_RUNTIME_v0929:
runtimeFilename = WESTEND_RUNTIME_V0929_FP
url = WESTEND_RUNTIME_V0929_URL
// only used for TestInstance_BadSignatureExtrinsic_On_WestendBlock8077850
case WESTEND_RUNTIME_v0912:
runtimeFilename = WESTEND_RUNTIME_V0912_FP
url = WESTEND_RUNTIME_V0912_URL
default:
return "", fmt.Errorf("%w: %s", ErrRuntimeUnknown, runtime)
}
Expand All @@ -96,7 +100,7 @@ func GetRuntime(ctx context.Context, runtime string) (
ctx, cancel := context.WithTimeout(ctx, requestTimeout)
defer cancel()

request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
if err != nil {
return "", fmt.Errorf("cannot make HTTP request: %w", err)
}
Expand Down
20 changes: 15 additions & 5 deletions lib/runtime/wazero/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package wazero_runtime

import (
"bytes"
"context"
"crypto/rand"
"encoding/binary"
Expand Down Expand Up @@ -32,7 +33,8 @@ var (
log.AddContext("module", "wazero"),
)

noneEncoded []byte = []byte{0x00}
noneEncoded []byte = []byte{0x00}
allZeroesBytes = [32]byte{}
)

const (
Expand Down Expand Up @@ -710,6 +712,18 @@ func ext_crypto_sr25519_verify_version_2(ctx context.Context, m api.Module, sig
panic("nil runtime context")
}

pubKeyBytes, ok := m.Memory().Read(key, 32)
if !ok {
panic("read overflow")
}

// prevents Polkadot zero-address crash using
// ext_crypto_sr25519_verify_version_1
// https://pacna.org/dot-zero-addr/
if bytes.Equal(pubKeyBytes, allZeroesBytes[:]) {
return ext_crypto_sr25519_verify_version_1(ctx, m, sig, msg, key)
}

sigVerifier := rtCtx.SigVerifier

message := read(m, msg)
Expand All @@ -718,10 +732,6 @@ func ext_crypto_sr25519_verify_version_2(ctx context.Context, m api.Module, sig
panic("read overflow")
}

pubKeyBytes, ok := m.Memory().Read(key, 32)
if !ok {
panic("read overflow")
}
pub, err := sr25519.NewPublicKey(pubKeyBytes)
if err != nil {
logger.Error("invalid sr25519 public key")
Expand Down
132 changes: 132 additions & 0 deletions lib/runtime/wazero/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package wazero_runtime

import (
_ "embed"

"bytes"
"encoding/json"
"math/big"
Expand Down Expand Up @@ -403,6 +405,136 @@ func TestInstance_BabeConfiguration_WestendRuntime_NoAuthorities(t *testing.T) {
require.Equal(t, expected, cfg)
}

func TestInstance_BadSignature_WestendBlock8077850(t *testing.T) {
tests := map[string]struct {
setupRuntime func(t *testing.T) (*Instance, *types.Header)
expectedError []byte
}{
"westend_dev_runtime_should_fail_with_bad_signature": {
expectedError: []byte{1, 0, 0xa},
setupRuntime: func(t *testing.T) (*Instance, *types.Header) {
genesisPath := utils.GetWestendDevRawGenesisPath(t)
gen := genesisFromRawJSON(t, genesisPath)
genTrie, err := runtime.NewTrieFromGenesis(gen)
require.NoError(t, err)

// set state to genesis state
genState := storage.NewTrieState(&genTrie)

cfg := Config{
Storage: genState,
LogLvl: log.Critical,
}

rt, err := NewRuntimeFromGenesis(cfg)
require.NoError(t, err)

// reset state back to parent state before executing
parentState := storage.NewTrieState(&genTrie)
rt.SetContextStorage(parentState)

genesisHeader := &types.Header{
Number: 0,
StateRoot: genTrie.MustHash(),
}

header := &types.Header{
ParentHash: genesisHeader.Hash(),
Number: 1,
Digest: types.NewDigest(),
}

return rt, header
},
},
"westend_0912_runtime_should_fail_with_invalid_payment": {
expectedError: []byte{1, 0, 1},
setupRuntime: func(t *testing.T) (*Instance, *types.Header) {
genesisPath := utils.GetWestendDevRawGenesisPath(t)
gen := genesisFromRawJSON(t, genesisPath)
genTrie, err := runtime.NewTrieFromGenesis(gen)
require.NoError(t, err)

rt := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0912)
parentState := storage.NewTrieState(&genTrie)
rt.SetContextStorage(parentState)

genesisHeader := &types.Header{
Number: 0,
StateRoot: genTrie.MustHash(),
}

header := &types.Header{
ParentHash: genesisHeader.Hash(),
Number: 1,
Digest: types.NewDigest(),
}

return rt, header
},
},
}

for tname, tt := range tests {
tt := tt

t.Run(tname, func(t *testing.T) {
instance, header := tt.setupRuntime(t)

err := instance.InitializeBlock(header)
require.NoError(t, err)

idata := types.NewInherentData()
err = idata.SetInherent(types.Timstap0, uint64(5))
require.NoError(t, err)

err = idata.SetInherent(types.Babeslot, uint64(1))
require.NoError(t, err)

ienc, err := idata.Encode()
require.NoError(t, err)

// Call BlockBuilder_inherent_extrinsics which returns the inherents as encoded extrinsics
inherentExts, err := instance.InherentExtrinsics(ienc)
require.NoError(t, err)

// decode inherent extrinsics
cp := make([]byte, len(inherentExts))
copy(cp, inherentExts)
var inExts [][]byte
err = scale.Unmarshal(cp, &inExts)
require.NoError(t, err)

// apply each inherent extrinsic
for _, inherent := range inExts {
in, err := scale.Marshal(inherent)
require.NoError(t, err)

ret, err := instance.ApplyExtrinsic(in)
require.NoError(t, err)
require.Equal(t, ret, []byte{0, 0})
}

keyring, err := signature.KeyringPairFromSecret(
"0x00000000000000000000000000000000000000000000000000000"+
"00000000000000000000000000000000000000000000000000000"+
"0000000000000000000000", 42)
require.NoError(t, err)

extHex := runtime.NewTestExtrinsic(t, instance, header.ParentHash, header.ParentHash,
0, keyring, "System.remark", []byte{0xab, 0xcd})

res, err := instance.ApplyExtrinsic(common.MustHexToBytes(extHex))
require.NoError(t, err)

// should fail with transaction validity error: invalid payment for runtime 0.9.12
// should fail with transaction validity error: bad signature for runtime version greater than 0.9.12
require.Equal(t, tt.expectedError, res)
})
}

}

func TestInstance_BabeConfiguration_WestendRuntime_WithAuthorities(t *testing.T) {
tt := trie.NewEmptyTrie()

Expand Down