Skip to content

Commit

Permalink
Add RPC support for Beefy & MMR (#249)
Browse files Browse the repository at this point in the history
* Add client support for Beefy & MMR

* Fix lint errors

* Fix tests

* Add more unit tests

* Add OptionalSignedCommitment type

* Remove obsolete JSON marshalling code

* update things

* fix bugs

* fixlint
  • Loading branch information
vgeddes authored May 25, 2022
1 parent f6dea8b commit e66d072
Show file tree
Hide file tree
Showing 13 changed files with 680 additions and 4 deletions.
31 changes: 31 additions & 0 deletions rpc/beefy/beefy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Go Substrate RPC Client (GSRPC) provides APIs and types around Polkadot and any Substrate-based chain RPC calls
//
// Copyright 2021 Snowfork
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package beefy

import (
"github.com/centrifuge/go-substrate-rpc-client/v4/client"
)

// Beefy exposes methods for retrieval of chain data
type Beefy struct {
client client.Client
}

// NewBeefy creates a new Chain struct
func NewBeefy(cl client.Client) Beefy {
return Beefy{cl}
}
20 changes: 20 additions & 0 deletions rpc/beefy/beefy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package beefy

import (
"os"
"testing"

"github.com/centrifuge/go-substrate-rpc-client/v4/client"
"github.com/centrifuge/go-substrate-rpc-client/v4/config"
)

var beefy Beefy

func TestMain(m *testing.M) {
cl, err := client.Connect(config.Default().RPCURL)
if err != nil {
panic(err)
}
beefy = NewBeefy(cl)
os.Exit(m.Run())
}
33 changes: 33 additions & 0 deletions rpc/beefy/get_finalized_head.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Go Substrate RPC Client (GSRPC) provides APIs and types around Polkadot and any Substrate-based chain RPC calls
//
// Copyright 2021 Snowfork
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package beefy

import (
"github.com/centrifuge/go-substrate-rpc-client/v4/types"
)

// GetFinalizedHead returns the hash of the latest BEEFY block
func (b *Beefy) GetFinalizedHead() (types.Hash, error) {
var res string

err := b.client.Call(&res, "beefy_getFinalizedHead")
if err != nil {
return types.Hash{}, err
}

return types.NewHashFromHexString(res)
}
29 changes: 29 additions & 0 deletions rpc/beefy/get_finalized_head_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Go Substrate RPC Client (GSRPC) provides APIs and types around Polkadot and any Substrate-based chain RPC calls
//
// Copyright 2019 Centrifuge GmbH
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package beefy

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestBeefy_GetFinalizedHead(t *testing.T) {
t.Skip("API only available in Polkadot")
_, err := beefy.GetFinalizedHead()
assert.NoError(t, err)
}
78 changes: 78 additions & 0 deletions rpc/beefy/subscribe_justifications.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Go Substrate RPC Client (GSRPC) provides APIs and types around Polkadot and any Substrate-based chain RPC calls
//
// Copyright 2021 Snowfork
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package beefy

import (
"context"
"sync"

"github.com/centrifuge/go-substrate-rpc-client/v4/config"
gethrpc "github.com/centrifuge/go-substrate-rpc-client/v4/gethrpc"
"github.com/centrifuge/go-substrate-rpc-client/v4/types"
)

// JustificationsSubscription is a subscription established through one of the Client's subscribe methods.
type JustificationsSubscription struct {
sub *gethrpc.ClientSubscription
channel chan types.SignedCommitment
quitOnce sync.Once // ensures quit is closed once
}

// Chan returns the subscription channel.
//
// The channel is closed when Unsubscribe is called on the subscription.
func (s *JustificationsSubscription) Chan() <-chan types.SignedCommitment {
return s.channel
}

// Err returns the subscription error channel. The intended use of Err is to schedule
// resubscription when the client connection is closed unexpectedly.
//
// The error channel receives a value when the subscription has ended due
// to an error. The received error is nil if Close has been called
// on the underlying client and no other error has occurred.
//
// The error channel is closed when Unsubscribe is called on the subscription.
func (s *JustificationsSubscription) Err() <-chan error {
return s.sub.Err()
}

// Unsubscribe unsubscribes the notification and closes the error channel.
// It can safely be called more than once.
func (s *JustificationsSubscription) Unsubscribe() {
s.sub.Unsubscribe()
s.quitOnce.Do(func() {
close(s.channel)
})
}

// SubscribeJustifications subscribes beefy justifications, returning a subscription that will
// receive server notifications containing the Header.
func (b *Beefy) SubscribeJustifications() (*JustificationsSubscription, error) {
ctx, cancel := context.WithTimeout(context.Background(), config.Default().SubscribeTimeout)
defer cancel()

ch := make(chan types.SignedCommitment)

sub, err := b.client.Subscribe(ctx, "beefy", "subscribeJustifications", "unsubscribeJustifications",
"justifications", ch)
if err != nil {
return nil, err
}

return &JustificationsSubscription{sub: sub, channel: ch}, nil
}
6 changes: 6 additions & 0 deletions rpc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ package rpc
import (
"github.com/centrifuge/go-substrate-rpc-client/v4/client"
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/author"
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/beefy"
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/chain"
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/mmr"
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/offchain"
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/state"
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/system"
Expand All @@ -28,7 +30,9 @@ import (

type RPC struct {
Author author.Author
Beefy beefy.Beefy
Chain chain.Chain
MMR mmr.MMR
Offchain offchain.Offchain
State state.State
System system.System
Expand All @@ -47,7 +51,9 @@ func NewRPC(cl client.Client) (*RPC, error) {

return &RPC{
Author: author.NewAuthor(cl),
Beefy: beefy.NewBeefy(cl),
Chain: chain.NewChain(cl),
MMR: mmr.NewMMR(cl),
Offchain: offchain.NewOffchain(cl),
State: st,
System: system.NewSystem(cl),
Expand Down
27 changes: 27 additions & 0 deletions rpc/mmr/generate_proof.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package mmr

import (
"github.com/centrifuge/go-substrate-rpc-client/v4/client"
"github.com/centrifuge/go-substrate-rpc-client/v4/types"
)

// GenerateProof retrieves a MMR proof and leaf for the specified leave index, at the given blockHash (useful to query a
// proof at an earlier block, likely with antoher MMR root)
func (c *MMR) GenerateProof(leafIndex uint64, blockHash types.Hash) (types.GenerateMMRProofResponse, error) {
return c.generateProof(leafIndex, &blockHash)
}

// GenerateProofLatest retrieves the latest MMR proof and leaf for the specified leave index
func (c *MMR) GenerateProofLatest(leafIndex uint64) (types.GenerateMMRProofResponse, error) {
return c.generateProof(leafIndex, nil)
}

func (c *MMR) generateProof(leafIndex uint64, blockHash *types.Hash) (types.GenerateMMRProofResponse, error) {
var res types.GenerateMMRProofResponse
err := client.CallWithBlockHash(c.client, &res, "mmr_generateProof", blockHash, leafIndex)
if err != nil {
return types.GenerateMMRProofResponse{}, err
}

return res, nil
}
13 changes: 13 additions & 0 deletions rpc/mmr/mmr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package mmr

import "github.com/centrifuge/go-substrate-rpc-client/v4/client"

// MMR exposes methods for retrieval of MMR data
type MMR struct {
client client.Client
}

// NewMMR creates a new MMR struct
func NewMMR(c client.Client) MMR {
return MMR{client: c}
}
59 changes: 59 additions & 0 deletions teste2e/beefy_subscribe_justifications_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Go Substrate RPC Client (GSRPC) provides APIs and types around Polkadot and any Substrate-based chain RPC calls
//
// Copyright 2019 Centrifuge GmbH
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package teste2e

import (
"fmt"
"testing"
"time"

gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4"
"github.com/centrifuge/go-substrate-rpc-client/v4/config"
"github.com/stretchr/testify/assert"
)

func TestBeefy_SubscribeJustifications(t *testing.T) {
if testing.Short() {
t.Skip("skipping end-to-end test in short mode.")
}

api, err := gsrpc.NewSubstrateAPI(config.Default().RPCURL)
assert.NoError(t, err)

sub, err := api.RPC.Beefy.SubscribeJustifications()
assert.NoError(t, err)
defer sub.Unsubscribe()

timeout := time.After(300 * time.Second)
received := 0

for {
select {
case commitment := <-sub.Chan():
fmt.Printf("%#v\n", commitment)

received++

if received >= 2 {
return
}
case <-timeout:
assert.FailNow(t, "timeout reached without getting 2 notifications from subscription")
return
}
}
}
Loading

0 comments on commit e66d072

Please sign in to comment.