Skip to content

Commit

Permalink
Remove unsafe RPC calls (paritytech#469)
Browse files Browse the repository at this point in the history
  • Loading branch information
vgeddes authored Jul 26, 2021
1 parent 4801886 commit 0bff5b9
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 168 deletions.
170 changes: 100 additions & 70 deletions relayer/chain/relaychain/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package relaychain
import (
"context"
"fmt"
"sort"

"github.com/sirupsen/logrus"

Expand Down Expand Up @@ -104,102 +105,131 @@ func (co *Connection) GetMMRLeafForBlock(
return proofResponse, nil
}

func (co *Connection) GetAllParaheadsWithOwn(blockHash types.Hash, ownParachainId uint32) (
[]types.Bytes, int, types.Header, error) {
none := types.NewOptionU32Empty()
encoded, err := types.EncodeToBytes(none)
if err != nil {
co.log.WithError(err).Error("Error")
return nil, 0, types.Header{}, err
}
type ParaHead struct {
LeafIndex int // order in which this head was returned from the storage query
ParaID uint32
Data types.Bytes
}

baseParaHeadsStorageKey, err := types.CreateStorageKey(
co.GetMetadata(),
"Paras",
"Heads", encoded, nil)
if err != nil {
co.log.WithError(err).Error("Failed to create parachain header storage key")
return nil, 0, types.Header{}, err
}
// Offset of encoded para id in storage key.
// The key is of this format:
// ParaId: u32
// Key: hash_twox_128("Paras") + hash_twox_128("Heads") + hash_twox_64(ParaId) + Encode(ParaId)
const ParaIDOffset = 16 + 16 + 8

func (co *Connection) FetchParaHeads(blockHash types.Hash) (map[uint32]ParaHead, error) {

//TODO fix this manual slice.
// The above types.CreateStorageKey does not give the same base key as polkadotjs needs for getKeys.
// It has some extra bytes.
// maybe from the none u32 in golang being wrong, or maybe slightly off CreateStorageKey call? we slice it
// here as a hack.
actualBaseParaHeadsStorageKey := baseParaHeadsStorageKey[:32]
co.log.WithField("actualBaseParaHeadsStorageKey", actualBaseParaHeadsStorageKey.Hex()).Info("actualBaseParaHeadsStorageKey")
keyPrefix := types.CreateStorageKeyPrefix("Paras", "Heads")

keysResponse, err := co.GetAPI().RPC.State.GetKeys(actualBaseParaHeadsStorageKey, blockHash)
keys, err := co.GetAPI().RPC.State.GetKeys(keyPrefix, blockHash)
if err != nil {
co.log.WithError(err).Error("Failed to get all parachain keys")
return nil, 0, types.Header{}, err
return nil, err
}

headersResponse, err := co.GetAPI().RPC.State.QueryStorage(keysResponse, blockHash, blockHash)
co.log.WithFields(logrus.Fields{
"numKeys": len(keys),
"storageKeyPrefix": fmt.Sprintf("%#x", keyPrefix),
"block": blockHash.Hex(),
}).Debug("Found keys for Paras.Heads storage map")

changeSets, err := co.GetAPI().RPC.State.QueryStorageAt(keys, blockHash)
if err != nil {
co.log.WithError(err).Error("Failed to get all parachain headers")
return nil, 0, types.Header{}, err
return nil, err
}

co.log.Info("Got all parachain headers")
var headers []types.Bytes
var ownParachainHeaderPos int
for _, headerResponse := range headersResponse {
for index, change := range headerResponse.Changes {

// TODO fix this manual slice with a proper type decode. only the last few bytes are for the ParaId,
// not sure what the early ones are for.
key := change.StorageKey[40:]
var parachainID types.U32
if err := types.DecodeFromBytes(key, &parachainID); err != nil {
co.log.WithError(err).Error("Failed to decode parachain ID")
return nil, 0, types.Header{}, err
heads := make(map[uint32]ParaHead)

for _, changeSet := range changeSets {
for index, change := range changeSet.Changes {
if change.StorageData.IsNone() {
continue
}

var headerBytes types.Bytes
if err := types.DecodeFromBytes(change.StorageData, &headerBytes); err != nil {
co.log.WithError(err).Error("Failed to decode MMREncodableOpaqueLeaf")
return nil, 0, types.Header{}, err
var paraID uint32
if err := types.DecodeFromBytes(change.StorageKey[ParaIDOffset:], &paraID); err != nil {
co.log.WithError(err).Error("Failed to decode parachain ID")
return nil, err
}
headers = append(headers, headerBytes)

if parachainID == types.U32(ownParachainId) {
ownParachainHeaderPos = index
_, headDataWrapped := change.StorageData.Unwrap()

var headData types.Bytes
if err := types.DecodeFromBytes(headDataWrapped, &headData); err != nil {
co.log.WithError(err).Error("Failed to decode HeadData wrapper")
return nil, err
}

co.log.WithFields(logrus.Fields{
"ParaID": paraID,
"LeafIndex": index,
"HeadData": fmt.Sprintf("%#x", headData),
}).Debug("Processed storage key for head in Paras.Heads")

heads[paraID] = ParaHead{
LeafIndex: index,
ParaID: paraID,
Data: headData,
}
}
}
var ownParachainHeader types.Header
if err := types.DecodeFromBytes(headers[ownParachainHeaderPos], &ownParachainHeader); err != nil {
co.log.WithError(err).Error("Failed to decode Header")
return nil, 0, types.Header{}, err

return heads, nil
}

// ByLeafIndex implements sort.Interface based on the LeafIndex field.
type ByLeafIndex []ParaHead
func (b ByLeafIndex) Len() int { return len(b) }
func (b ByLeafIndex) Less(i, j int) bool { return b[i].LeafIndex < b[j].LeafIndex }
func (b ByLeafIndex) Swap(i, j int) { b[i], b[j] = b[j], b[i] }

// AsProofInput transforms heads into a slice of head datas,
// in the original order they were returned by the Paras.Heads storage query.
func (co *Connection) AsProofInput(heads map[uint32]ParaHead) []types.Bytes {
// make a slice of values in the map
headsAsSlice := make([]ParaHead, 0, len(heads))
for _, v := range heads {
headsAsSlice = append(headsAsSlice, v)
}

co.log.WithField("parachainId", ownParachainId).Info("Decoding header for own parachain")
co.log.WithFields(logrus.Fields{
"headerBytes": fmt.Sprintf("%#x", headers[ownParachainHeaderPos]),
"header.ParentHash": ownParachainHeader.ParentHash.Hex(),
"header.Number": ownParachainHeader.Number,
"header.StateRoot": ownParachainHeader.StateRoot.Hex(),
"header.ExtrinsicsRoot": ownParachainHeader.ExtrinsicsRoot.Hex(),
"header.Digest": ownParachainHeader.Digest,
"parachainId": ownParachainId,
}).Info("Decoded header for parachain")

return headers, ownParachainHeaderPos, ownParachainHeader, nil
// sort by leaf index
sort.Sort(ByLeafIndex(headsAsSlice))

// map over slice to retrieve header data
data := make([]types.Bytes, 0, len(headsAsSlice))
for _, h := range headsAsSlice {
data = append(data, h.Data)
}
return data
}

// Fetch the latest block of a parachain that has been finalized at a relay chain block hash
func (co *Connection) FetchLatestFinalizedParaBlockNumber(relayBlockhash types.Hash, parachainId uint32) (uint64, error) {
_, _, ownParaHead, err := co.GetAllParaheadsWithOwn(relayBlockhash, parachainId)
func (co *Connection) FetchFinalizedParaHead(relayBlockhash types.Hash, paraID uint32) (*types.Header, error) {
encodedParaID, err := types.EncodeToBytes(paraID)
if err != nil {
return nil, err
}

storageKey, err := types.CreateStorageKey(co.GetMetadata(), "Paras", "Heads", encodedParaID, nil)
if err != nil {
return nil, err
}

var headerBytes types.Bytes
ok, err := co.GetAPI().RPC.State.GetStorage(storageKey, &headerBytes, relayBlockhash)
if err != nil {
co.log.WithError(err).Error("Failed to get parachain heads from relay chain")
return 0, err
return nil, err
}

finalizedParaBlockNumber := uint64(ownParaHead.Number)
if !ok {
return nil, fmt.Errorf("parachain head not found")
}

var header types.Header
if err := types.DecodeFromBytes(headerBytes, &header); err != nil {
co.log.WithError(err).Error("Failed to decode Header")
return nil, err
}

return finalizedParaBlockNumber, nil
return &header, nil
}
100 changes: 38 additions & 62 deletions relayer/cmd/subscribe_beefy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cmd

import (
"context"
"fmt"

"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus"
Expand All @@ -14,8 +13,6 @@ import (
"github.com/spf13/cobra"
)

const OUR_PARACHAIN_ID = 200

func subBeefyCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "sub-beefy",
Expand Down Expand Up @@ -102,9 +99,16 @@ func subBeefyJustifications(ctx context.Context, paraID uint32) error {
}
log.WithField("blockHash", nextBlockHash.Hex()).Info("Got blockhash")
GetMMRLeafForBlock(uint64(blockNumber), nextBlockHash, relaychainConn)
allParaheads, ourParahead := GetAllParaheads(nextBlockHash, relaychainConn, paraID)
heads, err := fetchParaHeads(relaychainConn, nextBlockHash)

var ourParahead types.Header
if err := types.DecodeFromBytes(heads[paraID], &ourParahead); err != nil {
log.WithError(err).Error("Failed to decode Header")
return err
}

log.WithFields(logrus.Fields{
"allParaheads": allParaheads,
"allParaheads": heads,
"ourParahead": ourParahead,
}).Info("Got all para heads")

Expand All @@ -114,82 +118,54 @@ func subBeefyJustifications(ctx context.Context, paraID uint32) error {
}
}

func GetAllParaheads(blockHash types.Hash, relaychainConn *relaychain.Connection, ourParachainID uint32) ([]types.Header, types.Header) {
none := types.NewOptionU32Empty()
encoded, err := types.EncodeToBytes(none)
if err != nil {
log.WithError(err).Error("Error")
}
// Copied over from relaychain.Connection
func fetchParaHeads(co *relaychain.Connection, blockHash types.Hash) (map[uint32][]byte, error) {

baseParaHeadsStorageKey, err := types.CreateStorageKey(
relaychainConn.GetMetadata(),
"Paras",
"Heads", encoded, nil)
if err != nil {
log.WithError(err).Error("Failed to create parachain header storage key")
}
keyPrefix := types.CreateStorageKeyPrefix("Paras", "Heads")

//TODO fix this manual slice.
// The above types.CreateStorageKey does not give the same base key as polkadotjs needs for getKeys.
// It has some extra bytes.
// maybe from the none u32 in golang being wrong, or maybe slightly off CreateStorageKey call? we slice it
// here as a hack.
actualBaseParaHeadsStorageKey := baseParaHeadsStorageKey[:32]
log.WithField("actualBaseParaHeadsStorageKey", actualBaseParaHeadsStorageKey.Hex()).Info("actualBaseParaHeadsStorageKey")

keysResponse, err := relaychainConn.GetAPI().RPC.State.GetKeys(actualBaseParaHeadsStorageKey, blockHash)
keys, err := co.GetAPI().RPC.State.GetKeys(keyPrefix, blockHash)
if err != nil {
log.WithError(err).Error("Failed to get all parachain keys")
return nil, err
}

headersResponse, err := relaychainConn.GetAPI().RPC.State.QueryStorage(keysResponse, blockHash, blockHash)
changeSets, err := co.GetAPI().RPC.State.QueryStorageAt(keys, blockHash)
if err != nil {
log.WithError(err).Error("Failed to get all parachain headers")
return nil, err
}

log.Info("Got all parachain headers")
var headers []types.Header
var ourParachainHeader types.Header
for _, headerResponse := range headersResponse {
for _, change := range headerResponse.Changes {

// TODO fix this manual slice with a proper type decode. only the last few bytes are for the ParaId,
// not sure what the early ones are for.
key := change.StorageKey[40:]
var parachainID types.U32
if err := types.DecodeFromBytes(key, &parachainID); err != nil {
log.WithError(err).Error("Failed to decode parachain ID")
}
heads := make(map[uint32][]byte)

log.WithField("parachainId", parachainID).Info("Decoding header for parachain")
var encodableOpaqueHeader types.Bytes
if err := types.DecodeFromBytes(change.StorageData, &encodableOpaqueHeader); err != nil {
log.WithError(err).Error("Failed to decode MMREncodableOpaqueLeaf")
for _, changeSet := range changeSets {
for _, change := range changeSet.Changes {
if change.StorageData.IsNone() {
continue
}

var header types.Header
if err := types.DecodeFromBytes(encodableOpaqueHeader, &header); err != nil {
log.WithError(err).Error("Failed to decode Header")
var paraID uint32

if err := types.DecodeFromBytes(change.StorageKey[40:], &paraID); err != nil {
log.WithError(err).Error("Failed to decode parachain ID")
return nil, err
}
log.WithFields(logrus.Fields{
"headerBytes": fmt.Sprintf("%#x", encodableOpaqueHeader),
"header.ParentHash": header.ParentHash.Hex(),
"header.Number": header.Number,
"header.StateRoot": header.StateRoot.Hex(),
"header.ExtrinsicsRoot": header.ExtrinsicsRoot.Hex(),
"header.Digest": header.Digest,
"parachainId": parachainID,
}).Info("Decoded header for parachain")
headers = append(headers, header)

if parachainID == types.U32(ourParachainID) {
ourParachainHeader = header

_, headDataWrapped := change.StorageData.Unwrap()

var headData types.Bytes
if err := types.DecodeFromBytes(headDataWrapped, &headData); err != nil {
log.WithError(err).Error("Failed to decode HeadData wrapper")
return nil, err
}

heads[paraID] = headData
}
}
return headers, ourParachainHeader

return heads, nil
}


func GetMMRLeafForBlock(blockNumber uint64, blockHash types.Hash, relaychainConn *relaychain.Connection) {
log.WithFields(logrus.Fields{
"blockNumber": blockNumber,
Expand Down
3 changes: 1 addition & 2 deletions relayer/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,14 @@ require (
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sirupsen/logrus v1.7.0
github.com/snowfork/ethashproof v0.0.0-20210708164253-a74e2d12ad79
github.com/snowfork/go-substrate-rpc-client/v3 v3.0.3
github.com/snowfork/go-substrate-rpc-client/v3 v3.0.4
github.com/spf13/afero v1.5.1 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/cobra v1.1.1
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.7.0
github.com/wealdtech/go-merkletree v1.0.0
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/text v0.3.5 // indirect
google.golang.org/protobuf v1.25.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions relayer/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -543,8 +543,8 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/snowfork/ethashproof v0.0.0-20210708164253-a74e2d12ad79 h1:GD0O2pb4qpC4n4ljsNri3snVljfxxPf6Q+k55xbFJVk=
github.com/snowfork/ethashproof v0.0.0-20210708164253-a74e2d12ad79/go.mod h1:Yy1jPDuUtVPG1b3XnRGiEHWPs09Yd5UphOTqPhN9XPU=
github.com/snowfork/go-substrate-rpc-client/v3 v3.0.3 h1:sqmImqfcJVjDXKAJZSiSBVqg8DCrNFnLG0eaFxWJEfA=
github.com/snowfork/go-substrate-rpc-client/v3 v3.0.3/go.mod h1:n+dDFUtmhedjdLsXi8m+rWwwx1SEAEN1Uo2PFePK3bU=
github.com/snowfork/go-substrate-rpc-client/v3 v3.0.4 h1:5Rj2Znuh40xVxngBR7b2MNEG/0+u8S13sVSeN18vxeE=
github.com/snowfork/go-substrate-rpc-client/v3 v3.0.4/go.mod h1:n+dDFUtmhedjdLsXi8m+rWwwx1SEAEN1Uo2PFePK3bU=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
Expand Down
Loading

0 comments on commit 0bff5b9

Please sign in to comment.