Skip to content

Commit

Permalink
ICS 23 Implementation (#4515)
Browse files Browse the repository at this point in the history
* add mapping

* rm unused mapping/*, rm interfaces

* rm unused code

* mv mapping -> state, rm x/ibc

* rm GetIfExists

* add key

* rm custom encoding/decoding in enum/bool

* fix lint

* rm tests

* add commitment

* newtyped remote values

* newtyped context

* revert context newtype

* add README, keypath

* reflect downstream ics

* add merkle

* add test for proving

* soft coded root keypath

* add update

* remove RootUpdate

* separate keypath and keuprefix

* add codec

* separate root/path

* add path to codec

* add docs in progre

* add comments

* add comments

* Apply suggestions from code review

Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com>

* add comments in progress

* add comments

* fix comment

* add comments in progress

* recover IntEncoding scheme for integer

* add uint tests, don't use codec in custom types

* finalize merge

* add godoc

* reformat test

* rm XXX

* add godoc

* add query

* update query.go

* update query.go

* add Query to boolean.go

* fix key

* godoc cleanup

* godoc cleanup

* godoc cleanup

* godoc cleanup

* godoc cleanup

* merge from ics04 branch

* merge from ics04 branch

* fix lint

* Apply suggestions from code review

Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com>

* applying review in progress

* apply review - make querier interface

* fix dependency

* revise querier interface to work both on cli & store

* rm commented lines

* rename Path -> Prefix

* Store accessor upstream changes (#5119)

* Store accessor upstream changes (#5119)

* add comments, reformat merkle querier

* rm merkle/utils

* ICS 23 upstream changes (#5120)

* ICS 23 upstream changes (#5120)

* update Value

* remove Mapping

* remove store accessors

* refactor ICS23

* cleanup types

* implement batch verification

* gosimple suggestion

* alias

* add tests

* ICS 24 Implementation (#5229)

* add validation functions

* validate path in ics-23

* address @fede comments

* move errors into host package

* flatten ICS23 structure

* fix ApplyPrefix

* complete types testing

* delete empty test file

* remove ibc errors from core error package

* start batch-verify tests

* minor changes on commitment types

* use testsuite

* upstream changes

* context changes
  • Loading branch information
mossid authored and jackzampolin committed Jan 15, 2020
1 parent 2ecbfe9 commit 220a482
Show file tree
Hide file tree
Showing 11 changed files with 719 additions and 16 deletions.
47 changes: 47 additions & 0 deletions client/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,53 @@ func NewCLIContextWithFrom(from string) CLIContext {
return NewCLIContextWithInputAndFrom(os.Stdin, from)
}

// NewCLIContextIBC takes additional arguements
func NewCLIContextIBC(from string, chainID string, nodeURI string) CLIContext {
var rpc rpcclient.Client

genOnly := viper.GetBool(flags.FlagGenerateOnly)
fromAddress, fromName, err := GetFromFields(os.Stdin, from, genOnly)
if err != nil {
fmt.Printf("failed to get from fields: %v", err)
os.Exit(1)
}

if !genOnly {
if nodeURI != "" {
rpc = rpcclient.NewHTTP(nodeURI, "/websocket")
}
}

ctx := CLIContext{
Client: rpc,
ChainID: chainID,
Output: os.Stdout,
NodeURI: nodeURI,
From: from,
OutputFormat: viper.GetString(cli.OutputFlag),
Height: viper.GetInt64(flags.FlagHeight),
HomeDir: viper.GetString(flags.FlagHome),
TrustNode: viper.GetBool(flags.FlagTrustNode),
UseLedger: viper.GetBool(flags.FlagUseLedger),
BroadcastMode: viper.GetString(flags.FlagBroadcastMode),
Simulate: viper.GetBool(flags.FlagDryRun),
GenerateOnly: genOnly,
FromAddress: fromAddress,
FromName: fromName,
Indent: viper.GetBool(flags.FlagIndentResponse),
SkipConfirm: viper.GetBool(flags.FlagSkipConfirmation),
}

// create a verifier for the specific chain ID and RPC client
verifier, err := CreateVerifier(ctx, DefaultVerifierCacheSize)
if err != nil && viper.IsSet(flags.FlagTrustNode) {
fmt.Printf("failed to create verifier: %s\n", err)
os.Exit(1)
}

return ctx.WithVerifier(verifier)
}

// NewCLIContext returns a new initialized CLIContext with parameters from the
// command line using Viper.
func NewCLIContext() CLIContext { return NewCLIContextWithFrom(viper.GetString(flags.FlagFrom)) }
Expand Down
113 changes: 97 additions & 16 deletions client/context/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package context
import (
"fmt"
"strings"
"time"

"github.com/pkg/errors"

Expand All @@ -12,6 +13,7 @@ import (
tmliteErr "github.com/tendermint/tendermint/lite/errors"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
tmtypes "github.com/tendermint/tendermint/types"

"github.com/cosmos/cosmos-sdk/store/rootmulti"
Expand All @@ -28,6 +30,55 @@ func (ctx CLIContext) GetNode() (rpcclient.Client, error) {
return ctx.Client, nil
}

// WaitForNBlocks blocks until the node defined on the context has advanced N blocks
func (ctx CLIContext) WaitForNBlocks(n int64) {
node, err := ctx.GetNode()
if err != nil {
panic(err)
}

resBlock, err := node.Block(nil)
var height int64
if err != nil || resBlock.Block == nil {
// wait for the first block to exist
ctx.waitForHeight(1)
height = 1 + n
} else {
height = resBlock.Block.Height + n
}
ctx.waitForHeight(height)
}

func (ctx CLIContext) waitForHeight(height int64) {
node, err := ctx.GetNode()
if err != nil {
panic(err)
}

for {
// get url, try a few times
var resBlock *ctypes.ResultBlock
var err error
INNER:
for i := 0; i < 5; i++ {
resBlock, err = node.Block(nil)
if err == nil {
break INNER
}
time.Sleep(time.Millisecond * 200)
}
if err != nil {
panic(err)
}

if resBlock.Block != nil && resBlock.Block.Height >= height {
return
}

time.Sleep(time.Millisecond * 100)
}
}

// Query performs a query to a Tendermint node with the provided path.
// It returns the result and height of the query upon success or an error if
// the query fails.
Expand All @@ -49,6 +100,12 @@ func (ctx CLIContext) QueryStore(key cmn.HexBytes, storeName string) ([]byte, in
return ctx.queryStore(key, storeName, "key")
}

// QueryABCI performs a query to a Tendermint node with the provide RequestQuery.
// It returns the ResultQuery obtained from the query.
func (ctx CLIContext) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) {
return ctx.queryABCI(req)
}

// QuerySubspace performs a query to a Tendermint node with the provided
// store name and subspace. It returns key value pair and height of the query
// upon success or an error if the query fails.
Expand All @@ -72,40 +129,64 @@ func (ctx CLIContext) GetFromName() string {
return ctx.FromName
}

// query performs a query to a Tendermint node with the provided store name
// and path. It returns the result and height of the query upon success
// or an error if the query fails. In addition, it will verify the returned
// proof if TrustNode is disabled. If proof verification fails or the query
// height is invalid, an error will be returned.
func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height int64, err error) {
func (ctx CLIContext) queryABCI(req abci.RequestQuery) (resp abci.ResponseQuery, err error) {

node, err := ctx.GetNode()
if err != nil {
return res, height, err
return resp, err
}

// When a client did not provide a query height, manually query for it so it can
// be injected downstream into responses.
if ctx.Height == 0 {
status, err := node.Status()
if err != nil {
return resp, err
}
ctx = ctx.WithHeight(status.SyncInfo.LatestBlockHeight)
}

opts := rpcclient.ABCIQueryOptions{
Height: ctx.Height,
Prove: !ctx.TrustNode,
Prove: req.Prove || !ctx.TrustNode,
}

result, err := node.ABCIQueryWithOptions(path, key, opts)
result, err := node.ABCIQueryWithOptions(req.Path, req.Data, opts)
if err != nil {
return res, height, err
return
}

resp := result.Response
resp = result.Response
if !resp.IsOK() {
return res, resp.Height, errors.New(resp.Log)
err = errors.New(resp.Log)
return
}

// data from trusted node or subspace query doesn't need verification
if ctx.TrustNode || !isQueryStoreWithProof(path) {
return resp.Value, resp.Height, nil
if ctx.TrustNode || !isQueryStoreWithProof(req.Path) {
return resp, nil
}

err = ctx.verifyProof(path, resp)
err = ctx.verifyProof(req.Path, resp)
if err != nil {
return
}

return
}

// query performs a query to a Tendermint node with the provided store name
// and path. It returns the result and height of the query upon success
// or an error if the query fails. In addition, it will verify the returned
// proof if TrustNode is disabled. If proof verification fails or the query
// height is invalid, an error will be returned.
func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, height int64, err error) {
resp, err := ctx.queryABCI(abci.RequestQuery{
Path: path,
Data: key,
})
if err != nil {
return res, resp.Height, err
return
}

return resp.Value, resp.Height, nil
Expand Down
18 changes: 18 additions & 0 deletions x/ibc/23-commitment/codec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package commitment

import (
"github.com/cosmos/cosmos-sdk/codec"
)

// RegisterCodec registers types declared in this package
func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterInterface((*RootI)(nil), nil)
cdc.RegisterInterface((*PrefixI)(nil), nil)
cdc.RegisterInterface((*PathI)(nil), nil)
cdc.RegisterInterface((*ProofI)(nil), nil)

cdc.RegisterConcrete(Root{}, "ibc/commitment/merkle/Root", nil)
cdc.RegisterConcrete(Prefix{}, "ibc/commitment/merkle/Prefix", nil)
cdc.RegisterConcrete(Path{}, "ibc/commitment/merkle/Path", nil)
cdc.RegisterConcrete(Proof{}, "ibc/commitment/merkle/Proof", nil)
}
37 changes: 37 additions & 0 deletions x/ibc/23-commitment/commitment_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package commitment_test

import (
"testing"

"github.com/stretchr/testify/suite"

"github.com/cosmos/cosmos-sdk/store/iavl"
"github.com/cosmos/cosmos-sdk/store/rootmulti"
storetypes "github.com/cosmos/cosmos-sdk/store/types"

dbm "github.com/tendermint/tm-db"
)

type MerkleTestSuite struct {
suite.Suite

store *rootmulti.Store
storeKey *storetypes.KVStoreKey
iavlStore *iavl.Store
}

func (suite *MerkleTestSuite) SetupTest() {
db := dbm.NewMemDB()
suite.store = rootmulti.NewStore(db)

suite.storeKey = storetypes.NewKVStoreKey("iavlStoreKey")

suite.store.MountStoreWithDB(suite.storeKey, storetypes.StoreTypeIAVL, nil)
suite.store.LoadVersion(0)

suite.iavlStore = suite.store.GetCommitStore(suite.storeKey).(*iavl.Store)
}

func TestMerkleTestSuite(t *testing.T) {
suite.Run(t, new(MerkleTestSuite))
}
Loading

0 comments on commit 220a482

Please sign in to comment.