Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rpc): Implement payment_queryInfo RPC call #1826

Merged
merged 31 commits into from
Oct 15, 2021
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fcfe5f1
chore: adding payment rpc module
EclesioMeloJunior Sep 28, 2021
a8b0897
chore: adding unit test to runtime call
EclesioMeloJunior Sep 30, 2021
368316f
chore: use defined extrinsic
EclesioMeloJunior Sep 30, 2021
68f391a
chore: add Payment Query Info
EclesioMeloJunior Oct 1, 2021
2249715
chore: add unit test to runtime layer
EclesioMeloJunior Oct 1, 2021
78afa32
chore: add unit tests
EclesioMeloJunior Oct 3, 2021
00ebedd
Merge branch 'development' into eclesio/payment-queryinfo-rpc
EclesioMeloJunior Oct 4, 2021
798f650
Merge branch 'development' into eclesio/payment-queryinfo-rpc
EclesioMeloJunior Oct 5, 2021
2ed73a5
Merge branch 'development' into eclesio/payment-queryinfo-rpc
EclesioMeloJunior Oct 5, 2021
9f84411
chore: resolve uint128
EclesioMeloJunior Oct 5, 2021
ec51ea2
Merge branch 'development' into eclesio/payment-queryinfo-rpc
EclesioMeloJunior Oct 5, 2021
04b3235
chore: fix lint warns
EclesioMeloJunior Oct 5, 2021
46f405f
Merge branch 'development' into eclesio/payment-queryinfo-rpc
EclesioMeloJunior Oct 6, 2021
d626e2a
Merge branch 'development' into eclesio/payment-queryinfo-rpc
EclesioMeloJunior Oct 7, 2021
b4c9c16
Merge branch 'development' into eclesio/payment-queryinfo-rpc
EclesioMeloJunior Oct 7, 2021
c637e45
Merge branch 'development' into eclesio/payment-queryinfo-rpc
EclesioMeloJunior Oct 8, 2021
a06b489
chore: put condition back
EclesioMeloJunior Oct 8, 2021
cc4e98a
chore: use pkg/scale uint128
EclesioMeloJunior Oct 8, 2021
49e8758
Merge branch 'development' into eclesio/payment-queryinfo-rpc
EclesioMeloJunior Oct 8, 2021
50bb099
chore: fix unit tests
EclesioMeloJunior Oct 8, 2021
d0c66e8
Merge branch 'eclesio/payment-queryinfo-rpc' of github.com:ChainSafe/…
EclesioMeloJunior Oct 8, 2021
5352f96
chore: remove debug from config.toml
EclesioMeloJunior Oct 8, 2021
6ff3daf
Merge branch 'development' into eclesio/payment-queryinfo-rpc
EclesioMeloJunior Oct 8, 2021
07e9c5c
fix: lint
EclesioMeloJunior Oct 8, 2021
e92f935
chore: remove tests from wasmtime and life
EclesioMeloJunior Oct 8, 2021
591d0aa
Merge branch 'development' into eclesio/payment-queryinfo-rpc
EclesioMeloJunior Oct 13, 2021
cb3d56d
chore: resolving conflicts
EclesioMeloJunior Oct 13, 2021
dcf7f30
chore: remove life impl form payment query info
EclesioMeloJunior Oct 13, 2021
ea9ee3b
chore: simplify chain_test function
EclesioMeloJunior Oct 13, 2021
245148d
chore: ignore unused meth receiver
EclesioMeloJunior Oct 13, 2021
6ff3e44
Merge branch 'development' into eclesio/payment-queryinfo-rpc
EclesioMeloJunior Oct 14, 2021
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
2 changes: 1 addition & 1 deletion chain/dev/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ enabled = true
ws = true
port = 8545
host = "localhost"
modules = ["system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"]
modules = ["system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate", "payment"]
ws-port = 8546
2 changes: 1 addition & 1 deletion chain/dev/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ var (
// DefaultRPCHTTPPort rpc port
DefaultRPCHTTPPort = uint32(8545)
// DefaultRPCModules rpc modules
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"}
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate", "payment"}
// DefaultRPCWSPort rpc websocket port
DefaultRPCWSPort = uint32(8546)
// DefaultRPCEnabled enables the RPC server
Expand Down
2 changes: 1 addition & 1 deletion chain/gssmr/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ discovery-interval = 10
enabled = false
port = 8545
host = "localhost"
modules = ["system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"]
modules = ["system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate", "payment"]
ws-port = 8546
2 changes: 1 addition & 1 deletion chain/gssmr/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ var (
// DefaultRPCHTTPPort rpc port
DefaultRPCHTTPPort = uint32(8545)
// DefaultRPCModules rpc modules
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"}
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate", "payment"}
// DefaultRPCWSPort rpc websocket port
DefaultRPCWSPort = uint32(8546)
)
2 changes: 1 addition & 1 deletion chain/kusama/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ enabled = false
external = false
port = 8545
host = "localhost"
modules = ["system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"]
modules = ["system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate", "payment"]
ws-port = 8546
ws = false
ws-external = false
2 changes: 1 addition & 1 deletion chain/kusama/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ var (
// DefaultRPCHTTPPort rpc port
DefaultRPCHTTPPort = uint32(8545)
// DefaultRPCModules rpc modules
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"}
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate", "payment"}
// DefaultRPCWSPort rpc websocket port
DefaultRPCWSPort = uint32(8546)
)
2 changes: 1 addition & 1 deletion chain/polkadot/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ nomdns = false
enabled = false
port = 8545
host = "localhost"
modules = ["system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"]
modules = ["system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate", "payment"]
ws-port = 8546
2 changes: 1 addition & 1 deletion chain/polkadot/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ var (
// DefaultRPCHTTPPort rpc port
DefaultRPCHTTPPort = uint32(8545)
// DefaultRPCModules rpc modules
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"}
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate", "payment"}
// DefaultRPCWSPort rpc websocket port
DefaultRPCWSPort = uint32(8546)
)
2 changes: 2 additions & 0 deletions dot/rpc/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ func (h *HTTPServer) RegisterModules(mods []string) {
srvc = modules.NewOffchainModule(h.serverConfig.NodeStorage)
case "childstate":
srvc = modules.NewChildStateModule(h.serverConfig.StorageAPI, h.serverConfig.BlockAPI)
case "payment":
srvc = modules.NewPaymentModule(h.serverConfig.BlockAPI)
default:
h.logger.Warn("Unrecognised module", "module", mod)
continue
Expand Down
1 change: 1 addition & 0 deletions dot/rpc/modules/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type BlockAPI interface {
SubChain(start, end common.Hash) ([]common.Hash, error)
RegisterRuntimeUpdatedChannel(ch chan<- runtime.Version) (uint32, error)
UnregisterRuntimeUpdatedChannel(id uint32) bool
GetRuntime(hash *common.Hash) (runtime.Instance, error)
}

// NetworkAPI interface for network state methods
Expand Down
22 changes: 21 additions & 1 deletion dot/rpc/modules/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,21 @@ package modules
import (
"io/ioutil"
"math/big"
"path/filepath"
"testing"

"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/genesis"
"github.com/ChainSafe/gossamer/lib/runtime"
"github.com/ChainSafe/gossamer/lib/runtime/wasmer"
"github.com/ChainSafe/gossamer/lib/trie"
"github.com/ChainSafe/gossamer/lib/utils"
"github.com/ChainSafe/gossamer/pkg/scale"

database "github.com/ChainSafe/chaindb"
rtstorage "github.com/ChainSafe/gossamer/lib/runtime/storage"
log "github.com/ChainSafe/log15"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -350,7 +354,23 @@ func newTestStateService(t *testing.T) *state.Service {
err = stateSrvc.Start()
require.NoError(t, err)

rt, err := stateSrvc.CreateGenesisRuntime(genTrie, gen)
rtCfg := &wasmer.Config{}

rtCfg.Storage, err = rtstorage.NewTrieState(genTrie)
require.NoError(t, err)

nodeStorage := runtime.NodeStorage{}

if stateSrvc != nil {
nodeStorage.BaseDB = stateSrvc.Base
} else {
nodeStorage.BaseDB, err = utils.SetupDatabase(filepath.Join(testDatadirPath, "offline_storage"), false)
require.NoError(t, err)
}

rtCfg.NodeStorage = nodeStorage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
nodeStorage := runtime.NodeStorage{}
if stateSrvc != nil {
nodeStorage.BaseDB = stateSrvc.Base
} else {
nodeStorage.BaseDB, err = utils.SetupDatabase(filepath.Join(testDatadirPath, "offline_storage"), false)
require.NoError(t, err)
}
rtCfg.NodeStorage = nodeStorage
if stateSrvc != nil {
rtCfg.NodeStorage.BaseDB = stateSrvc.Base
} else {
rtCfg.NodeStorage.BaseDB, err = utils.SetupDatabase(filepath.Join(testDatadirPath, "offline_storage"), false)
require.NoError(t, err)
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!


rt, err := wasmer.NewRuntimeFromGenesis(gen, rtCfg)
require.NoError(t, err)

err = loadTestBlocks(t, genesisHeader.Hash(), stateSrvc.Block, rt)
Expand Down
25 changes: 24 additions & 1 deletion dot/rpc/modules/mocks/block_api.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 69 additions & 0 deletions dot/rpc/modules/payment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package modules

import (
"net/http"

"github.com/ChainSafe/gossamer/lib/common"
)

// PaymentQueryInfoRequest represents the request to get the fee of a extrinsic in a given block
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

of an extrinsic

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

type PaymentQueryInfoRequest struct {
// hex SCALE encoded extrinsic
Ext string
// hex optional block hash indicating the state
Hash *common.Hash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use *optional.Hash here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I belive changing to *optional.Hash will add more complexity than just receive a *common.Hash and check if Hash == nil

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also have an open PR that removes all of the optional types, so I think we should avoid using them. If we need an option I think we should use *common.Hash so I am with you @EclesioMeloJunior

}

// PaymentQueryInfoResponse holds the response fields to the query info RPC method
type PaymentQueryInfoResponse struct {
Weight uint64 `json:"weight"`
Class int `json:"class"`
PartialFee string `json:"partialFee"`
}

// PaymentModule holds all the RPC implementation of polkadot payment rpc api
type PaymentModule struct {
blockAPI BlockAPI
}

// NewPaymentModule returns a pointer to PaymentModule
func NewPaymentModule(blockAPI BlockAPI) *PaymentModule {
return &PaymentModule{
blockAPI: blockAPI,
}
}

// QueryInfo query the known data about the fee of an extrinsic at the given block
func (p *PaymentModule) QueryInfo(_ *http.Request, req *PaymentQueryInfoRequest, res *PaymentQueryInfoResponse) error {
var hash common.Hash
if req.Hash == nil {
hash = p.blockAPI.BestBlockHash()
} else {
hash = *req.Hash
}

r, err := p.blockAPI.GetRuntime(&hash)
if err != nil {
return err
}

ext, err := common.HexToBytes(req.Ext)
if err != nil {
return err
}

encQueryInfo, err := r.PaymentQueryInfo(ext)
if err != nil {
return err
}

if encQueryInfo != nil {
*res = PaymentQueryInfoResponse{
Weight: encQueryInfo.Weight,
Class: encQueryInfo.Class,
PartialFee: encQueryInfo.PartialFee.String(),
}
}

return nil
}
142 changes: 142 additions & 0 deletions dot/rpc/modules/payment_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package modules

import (
"errors"
"testing"

"github.com/ChainSafe/gossamer/dot/rpc/modules/mocks"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/pkg/scale"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"

"github.com/ChainSafe/gossamer/lib/common"
mocksruntime "github.com/ChainSafe/gossamer/lib/runtime/mocks"
)

func TestPaymentQueryInfo(t *testing.T) {
state := newTestStateService(t)
bestBlockHash := state.Block.BestBlockHash()

t.Run("When there is no errors", func(t *testing.T) {
mockedQueryInfo := &types.TransactionPaymentQueryInfo{
Weight: 0,
Class: 0,
PartialFee: scale.MaxUint128,
}

expected := PaymentQueryInfoResponse{
Weight: 0,
Class: 0,
PartialFee: scale.MaxUint128.String(),
}

runtimeMock := new(mocksruntime.MockInstance)
runtimeMock.On("PaymentQueryInfo", mock.AnythingOfType("[]uint8")).Return(mockedQueryInfo, nil)

blockAPIMock := new(mocks.MockBlockAPI)
blockAPIMock.On("BestBlockHash").Return(bestBlockHash)

blockAPIMock.On("GetRuntime", mock.AnythingOfType("*common.Hash")).Return(runtimeMock, nil)

mod := &PaymentModule{
blockAPI: blockAPIMock,
}

var req PaymentQueryInfoRequest
req.Ext = "0x0001"
req.Hash = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to explicitly assign nil. The default value will be nil.


var res PaymentQueryInfoResponse
err := mod.QueryInfo(nil, &req, &res)

require.NoError(t, err)
require.Equal(t, expected, res)

// should be called because req.Hash is nil
blockAPIMock.AssertCalled(t, "BestBlockHash")
blockAPIMock.AssertCalled(t, "GetRuntime", mock.AnythingOfType("*common.Hash"))
runtimeMock.AssertCalled(t, "PaymentQueryInfo", mock.AnythingOfType("[]uint8"))
})

t.Run("When could not get runtime", func(t *testing.T) {
blockAPIMock := new(mocks.MockBlockAPI)
blockAPIMock.On("BestBlockHash").Return(bestBlockHash)

blockAPIMock.On("GetRuntime", mock.AnythingOfType("*common.Hash")).
Return(nil, errors.New("mocked problems"))

mod := &PaymentModule{
blockAPI: blockAPIMock,
}

var req PaymentQueryInfoRequest
req.Ext = "0x0011"
req.Hash = nil

var res PaymentQueryInfoResponse
err := mod.QueryInfo(nil, &req, &res)

require.Error(t, err)
require.Equal(t, res, PaymentQueryInfoResponse{})

blockAPIMock.AssertCalled(t, "BestBlockHash")
blockAPIMock.AssertCalled(t, "GetRuntime", mock.AnythingOfType("*common.Hash"))
})

t.Run("When PaymentQueryInfo returns error", func(t *testing.T) {
runtimeMock := new(mocksruntime.MockInstance)
runtimeMock.On("PaymentQueryInfo", mock.AnythingOfType("[]uint8")).Return(nil, errors.New("mocked error"))

blockAPIMock := new(mocks.MockBlockAPI)
blockAPIMock.On("GetRuntime", mock.AnythingOfType("*common.Hash")).Return(runtimeMock, nil)

mod := &PaymentModule{
blockAPI: blockAPIMock,
}

mockedHash := common.NewHash([]byte{0x01, 0x02})
var req PaymentQueryInfoRequest
req.Ext = "0x0000"
req.Hash = &mockedHash

var res PaymentQueryInfoResponse
err := mod.QueryInfo(nil, &req, &res)

require.Error(t, err)
require.Equal(t, res, PaymentQueryInfoResponse{})

// should be called because req.Hash is nil
blockAPIMock.AssertNotCalled(t, "BestBlockHash")
blockAPIMock.AssertCalled(t, "GetRuntime", mock.AnythingOfType("*common.Hash"))
runtimeMock.AssertCalled(t, "PaymentQueryInfo", mock.AnythingOfType("[]uint8"))
})

t.Run("When PaymentQueryInfo returns a nil info", func(t *testing.T) {
runtimeMock := new(mocksruntime.MockInstance)
runtimeMock.On("PaymentQueryInfo", mock.AnythingOfType("[]uint8")).Return(nil, nil)

blockAPIMock := new(mocks.MockBlockAPI)
blockAPIMock.On("GetRuntime", mock.AnythingOfType("*common.Hash")).Return(runtimeMock, nil)

mod := &PaymentModule{
blockAPI: blockAPIMock,
}

mockedHash := common.NewHash([]byte{0x01, 0x02})
var req PaymentQueryInfoRequest
req.Ext = "0x0020"
req.Hash = &mockedHash

var res PaymentQueryInfoResponse
err := mod.QueryInfo(nil, &req, &res)

require.NoError(t, err)
require.Equal(t, res, PaymentQueryInfoResponse{})

// should be called because req.Hash is nil
blockAPIMock.AssertNotCalled(t, "BestBlockHash")
blockAPIMock.AssertCalled(t, "GetRuntime", mock.AnythingOfType("*common.Hash"))
runtimeMock.AssertCalled(t, "PaymentQueryInfo", mock.AnythingOfType("[]uint8"))
})
}
2 changes: 2 additions & 0 deletions dot/rpc/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,13 @@ func (s *Service) BuildMethodNames(rcvr interface{}, name string) {
if mtype.NumIn() != 4 {
continue
}

// First argument must be a pointer and must be http.Request.
reqType := mtype.In(1)
if reqType.Kind() != reflect.Ptr || reqType.Elem() != typeOfRequest {
continue
}

// Second argument must be a pointer and must be exported.
args := mtype.In(2)
if args.Kind() != reflect.Ptr || !isExportedOrBuiltIn(args) {
Expand Down
Loading