forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
IRISHUB-238: Add multiply store proof build and verification
- Loading branch information
HaoyangLiu
committed
Aug 30, 2018
1 parent
fd8c1e5
commit 703c643
Showing
8 changed files
with
401 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package context | ||
|
||
import ( | ||
"github.com/pkg/errors" | ||
rpcclient "github.com/tendermint/tendermint/rpc/client" | ||
"strings" | ||
"sync" | ||
) | ||
|
||
// ClientManager is a manager of a set of rpc clients to full nodes. | ||
// This manager can do load balancing upon these rpc clients. | ||
type ClientManager struct { | ||
clients []rpcclient.Client | ||
currentIndex int | ||
mutex sync.Mutex | ||
} | ||
|
||
// NewClientManager create a new ClientManager | ||
func NewClientManager(nodeURIs string) (*ClientManager, error) { | ||
if nodeURIs != "" { | ||
nodeURLArray := strings.Split(nodeURIs, ",") | ||
var clients []rpcclient.Client | ||
for _, url := range nodeURLArray { | ||
client := rpcclient.NewHTTP(url, "/websocket") | ||
clients = append(clients, client) | ||
} | ||
mgr := &ClientManager{ | ||
currentIndex: 0, | ||
clients: clients, | ||
} | ||
return mgr, nil | ||
} | ||
return nil, errors.New("missing node URIs") | ||
} | ||
|
||
func (mgr *ClientManager) getClient() rpcclient.Client { | ||
mgr.mutex.Lock() | ||
defer mgr.mutex.Unlock() | ||
|
||
client := mgr.clients[mgr.currentIndex] | ||
mgr.currentIndex++ | ||
if mgr.currentIndex >= len(mgr.clients) { | ||
mgr.currentIndex = 0 | ||
} | ||
return client | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package context | ||
|
||
import ( | ||
"github.com/stretchr/testify/assert" | ||
"testing" | ||
) | ||
|
||
func TestClientManager(t *testing.T) { | ||
nodeURIs := "10.10.10.10:26657,20.20.20.20:26657,30.30.30.30:26657" | ||
clientMgr, err := NewClientManager(nodeURIs) | ||
assert.Empty(t, err) | ||
endpoint := clientMgr.getClient() | ||
assert.NotEqual(t, endpoint, clientMgr.getClient()) | ||
clientMgr.getClient() | ||
assert.Equal(t, endpoint, clientMgr.getClient()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package store | ||
|
||
import ( | ||
"bytes" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/pkg/errors" | ||
"github.com/tendermint/iavl" | ||
cmn "github.com/tendermint/tendermint/libs/common" | ||
) | ||
|
||
// commitID of substores, such as acc store, gov store | ||
type SubstoreCommitID struct { | ||
Name string `json:"name"` | ||
Version int64 `json:"version"` | ||
CommitHash cmn.HexBytes `json:"commit_hash"` | ||
} | ||
|
||
// proof of store which have multi substores | ||
type MultiStoreProof struct { | ||
CommitIDList []SubstoreCommitID `json:"commit_id_list"` | ||
StoreName string `json:"store_name"` | ||
RangeProof iavl.RangeProof `json:"range_proof"` | ||
} | ||
|
||
// build MultiStoreProof based on iavl proof and storeInfos | ||
func BuildMultiStoreProof(iavlProof []byte, storeName string, storeInfos []storeInfo) ([]byte, error) { | ||
var rangeProof iavl.RangeProof | ||
err := cdc.UnmarshalBinary(iavlProof, &rangeProof) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var multiStoreProof MultiStoreProof | ||
for _, storeInfo := range storeInfos { | ||
|
||
commitID := SubstoreCommitID{ | ||
Name: storeInfo.Name, | ||
Version: storeInfo.Core.CommitID.Version, | ||
CommitHash: storeInfo.Core.CommitID.Hash, | ||
} | ||
multiStoreProof.CommitIDList = append(multiStoreProof.CommitIDList, commitID) | ||
} | ||
multiStoreProof.StoreName = storeName | ||
multiStoreProof.RangeProof = rangeProof | ||
|
||
proof, err := cdc.MarshalBinary(multiStoreProof) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return proof, nil | ||
} | ||
|
||
// verify multiStoreCommitInfo against appHash | ||
func VerifyMultiStoreCommitInfo(storeName string, multiStoreCommitInfo []SubstoreCommitID, appHash []byte) ([]byte, error) { | ||
var substoreCommitHash []byte | ||
var storeInfos []storeInfo | ||
var height int64 | ||
for _, multiStoreCommitID := range multiStoreCommitInfo { | ||
|
||
if multiStoreCommitID.Name == storeName { | ||
substoreCommitHash = multiStoreCommitID.CommitHash | ||
height = multiStoreCommitID.Version | ||
} | ||
storeInfo := storeInfo{ | ||
Name: multiStoreCommitID.Name, | ||
Core: storeCore{ | ||
CommitID: sdk.CommitID{ | ||
Version: multiStoreCommitID.Version, | ||
Hash: multiStoreCommitID.CommitHash, | ||
}, | ||
}, | ||
} | ||
|
||
storeInfos = append(storeInfos, storeInfo) | ||
} | ||
if len(substoreCommitHash) == 0 { | ||
return nil, cmn.NewError("failed to get substore root commit hash by store name") | ||
} | ||
|
||
ci := commitInfo{ | ||
Version: height, | ||
StoreInfos: storeInfos, | ||
} | ||
|
||
if !bytes.Equal(appHash, ci.Hash()) { | ||
return nil, cmn.NewError("the merkle root of multiStoreCommitInfo doesn't equal to appHash") | ||
} | ||
return substoreCommitHash, nil | ||
} | ||
|
||
// verify iavl proof | ||
func VerifyRangeProof(key, value []byte, substoreCommitHash []byte, rangeProof *iavl.RangeProof) error { | ||
|
||
// Validate the proof to ensure data integrity. | ||
err := rangeProof.Verify(substoreCommitHash) | ||
if err != nil { | ||
return errors.Wrap(err, "proof root hash doesn't equal to substore commit root hash") | ||
} | ||
|
||
if len(value) != 0 { | ||
// Validate existence proof | ||
err = rangeProof.VerifyItem(key, value) | ||
if err != nil { | ||
return errors.Wrap(err, "failed in existence verification") | ||
} | ||
} else { | ||
// Validate absence proof | ||
err = rangeProof.VerifyAbsence(key) | ||
if err != nil { | ||
return errors.Wrap(err, "failed in absence verification") | ||
} | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.