Skip to content

Commit e66d072

Browse files
authored
Add RPC support for Beefy & MMR (#249)
* 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
1 parent f6dea8b commit e66d072

13 files changed

Lines changed: 680 additions & 4 deletions

rpc/beefy/beefy.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Go Substrate RPC Client (GSRPC) provides APIs and types around Polkadot and any Substrate-based chain RPC calls
2+
//
3+
// Copyright 2021 Snowfork
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package beefy
18+
19+
import (
20+
"github.com/centrifuge/go-substrate-rpc-client/v4/client"
21+
)
22+
23+
// Beefy exposes methods for retrieval of chain data
24+
type Beefy struct {
25+
client client.Client
26+
}
27+
28+
// NewBeefy creates a new Chain struct
29+
func NewBeefy(cl client.Client) Beefy {
30+
return Beefy{cl}
31+
}

rpc/beefy/beefy_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package beefy
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/centrifuge/go-substrate-rpc-client/v4/client"
8+
"github.com/centrifuge/go-substrate-rpc-client/v4/config"
9+
)
10+
11+
var beefy Beefy
12+
13+
func TestMain(m *testing.M) {
14+
cl, err := client.Connect(config.Default().RPCURL)
15+
if err != nil {
16+
panic(err)
17+
}
18+
beefy = NewBeefy(cl)
19+
os.Exit(m.Run())
20+
}

rpc/beefy/get_finalized_head.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Go Substrate RPC Client (GSRPC) provides APIs and types around Polkadot and any Substrate-based chain RPC calls
2+
//
3+
// Copyright 2021 Snowfork
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package beefy
18+
19+
import (
20+
"github.com/centrifuge/go-substrate-rpc-client/v4/types"
21+
)
22+
23+
// GetFinalizedHead returns the hash of the latest BEEFY block
24+
func (b *Beefy) GetFinalizedHead() (types.Hash, error) {
25+
var res string
26+
27+
err := b.client.Call(&res, "beefy_getFinalizedHead")
28+
if err != nil {
29+
return types.Hash{}, err
30+
}
31+
32+
return types.NewHashFromHexString(res)
33+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Go Substrate RPC Client (GSRPC) provides APIs and types around Polkadot and any Substrate-based chain RPC calls
2+
//
3+
// Copyright 2019 Centrifuge GmbH
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package beefy
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
)
24+
25+
func TestBeefy_GetFinalizedHead(t *testing.T) {
26+
t.Skip("API only available in Polkadot")
27+
_, err := beefy.GetFinalizedHead()
28+
assert.NoError(t, err)
29+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Go Substrate RPC Client (GSRPC) provides APIs and types around Polkadot and any Substrate-based chain RPC calls
2+
//
3+
// Copyright 2021 Snowfork
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package beefy
18+
19+
import (
20+
"context"
21+
"sync"
22+
23+
"github.com/centrifuge/go-substrate-rpc-client/v4/config"
24+
gethrpc "github.com/centrifuge/go-substrate-rpc-client/v4/gethrpc"
25+
"github.com/centrifuge/go-substrate-rpc-client/v4/types"
26+
)
27+
28+
// JustificationsSubscription is a subscription established through one of the Client's subscribe methods.
29+
type JustificationsSubscription struct {
30+
sub *gethrpc.ClientSubscription
31+
channel chan types.SignedCommitment
32+
quitOnce sync.Once // ensures quit is closed once
33+
}
34+
35+
// Chan returns the subscription channel.
36+
//
37+
// The channel is closed when Unsubscribe is called on the subscription.
38+
func (s *JustificationsSubscription) Chan() <-chan types.SignedCommitment {
39+
return s.channel
40+
}
41+
42+
// Err returns the subscription error channel. The intended use of Err is to schedule
43+
// resubscription when the client connection is closed unexpectedly.
44+
//
45+
// The error channel receives a value when the subscription has ended due
46+
// to an error. The received error is nil if Close has been called
47+
// on the underlying client and no other error has occurred.
48+
//
49+
// The error channel is closed when Unsubscribe is called on the subscription.
50+
func (s *JustificationsSubscription) Err() <-chan error {
51+
return s.sub.Err()
52+
}
53+
54+
// Unsubscribe unsubscribes the notification and closes the error channel.
55+
// It can safely be called more than once.
56+
func (s *JustificationsSubscription) Unsubscribe() {
57+
s.sub.Unsubscribe()
58+
s.quitOnce.Do(func() {
59+
close(s.channel)
60+
})
61+
}
62+
63+
// SubscribeJustifications subscribes beefy justifications, returning a subscription that will
64+
// receive server notifications containing the Header.
65+
func (b *Beefy) SubscribeJustifications() (*JustificationsSubscription, error) {
66+
ctx, cancel := context.WithTimeout(context.Background(), config.Default().SubscribeTimeout)
67+
defer cancel()
68+
69+
ch := make(chan types.SignedCommitment)
70+
71+
sub, err := b.client.Subscribe(ctx, "beefy", "subscribeJustifications", "unsubscribeJustifications",
72+
"justifications", ch)
73+
if err != nil {
74+
return nil, err
75+
}
76+
77+
return &JustificationsSubscription{sub: sub, channel: ch}, nil
78+
}

rpc/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ package rpc
1919
import (
2020
"github.com/centrifuge/go-substrate-rpc-client/v4/client"
2121
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/author"
22+
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/beefy"
2223
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/chain"
24+
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/mmr"
2325
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/offchain"
2426
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/state"
2527
"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/system"
@@ -28,7 +30,9 @@ import (
2830

2931
type RPC struct {
3032
Author author.Author
33+
Beefy beefy.Beefy
3134
Chain chain.Chain
35+
MMR mmr.MMR
3236
Offchain offchain.Offchain
3337
State state.State
3438
System system.System
@@ -47,7 +51,9 @@ func NewRPC(cl client.Client) (*RPC, error) {
4751

4852
return &RPC{
4953
Author: author.NewAuthor(cl),
54+
Beefy: beefy.NewBeefy(cl),
5055
Chain: chain.NewChain(cl),
56+
MMR: mmr.NewMMR(cl),
5157
Offchain: offchain.NewOffchain(cl),
5258
State: st,
5359
System: system.NewSystem(cl),

rpc/mmr/generate_proof.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package mmr
2+
3+
import (
4+
"github.com/centrifuge/go-substrate-rpc-client/v4/client"
5+
"github.com/centrifuge/go-substrate-rpc-client/v4/types"
6+
)
7+
8+
// GenerateProof retrieves a MMR proof and leaf for the specified leave index, at the given blockHash (useful to query a
9+
// proof at an earlier block, likely with antoher MMR root)
10+
func (c *MMR) GenerateProof(leafIndex uint64, blockHash types.Hash) (types.GenerateMMRProofResponse, error) {
11+
return c.generateProof(leafIndex, &blockHash)
12+
}
13+
14+
// GenerateProofLatest retrieves the latest MMR proof and leaf for the specified leave index
15+
func (c *MMR) GenerateProofLatest(leafIndex uint64) (types.GenerateMMRProofResponse, error) {
16+
return c.generateProof(leafIndex, nil)
17+
}
18+
19+
func (c *MMR) generateProof(leafIndex uint64, blockHash *types.Hash) (types.GenerateMMRProofResponse, error) {
20+
var res types.GenerateMMRProofResponse
21+
err := client.CallWithBlockHash(c.client, &res, "mmr_generateProof", blockHash, leafIndex)
22+
if err != nil {
23+
return types.GenerateMMRProofResponse{}, err
24+
}
25+
26+
return res, nil
27+
}

rpc/mmr/mmr.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package mmr
2+
3+
import "github.com/centrifuge/go-substrate-rpc-client/v4/client"
4+
5+
// MMR exposes methods for retrieval of MMR data
6+
type MMR struct {
7+
client client.Client
8+
}
9+
10+
// NewMMR creates a new MMR struct
11+
func NewMMR(c client.Client) MMR {
12+
return MMR{client: c}
13+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Go Substrate RPC Client (GSRPC) provides APIs and types around Polkadot and any Substrate-based chain RPC calls
2+
//
3+
// Copyright 2019 Centrifuge GmbH
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package teste2e
18+
19+
import (
20+
"fmt"
21+
"testing"
22+
"time"
23+
24+
gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4"
25+
"github.com/centrifuge/go-substrate-rpc-client/v4/config"
26+
"github.com/stretchr/testify/assert"
27+
)
28+
29+
func TestBeefy_SubscribeJustifications(t *testing.T) {
30+
if testing.Short() {
31+
t.Skip("skipping end-to-end test in short mode.")
32+
}
33+
34+
api, err := gsrpc.NewSubstrateAPI(config.Default().RPCURL)
35+
assert.NoError(t, err)
36+
37+
sub, err := api.RPC.Beefy.SubscribeJustifications()
38+
assert.NoError(t, err)
39+
defer sub.Unsubscribe()
40+
41+
timeout := time.After(300 * time.Second)
42+
received := 0
43+
44+
for {
45+
select {
46+
case commitment := <-sub.Chan():
47+
fmt.Printf("%#v\n", commitment)
48+
49+
received++
50+
51+
if received >= 2 {
52+
return
53+
}
54+
case <-timeout:
55+
assert.FailNow(t, "timeout reached without getting 2 notifications from subscription")
56+
return
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)