From 49a570df26859306ae2589971133f645cfcb05c4 Mon Sep 17 00:00:00 2001 From: yutianwu Date: Thu, 27 Oct 2022 16:16:21 +0800 Subject: [PATCH] support legacy merkle proof validation --- core/vm/contracts_lightclient.go | 48 ++++++--------- core/vm/contracts_lightclient_test.go | 9 +-- core/vm/lightclient/ics23_proof.go | 82 -------------------------- core/vm/lightclient/multistoreproof.go | 15 +++-- core/vm/lightclient/rootmultistore.go | 3 - core/vm/lightclient/types.go | 30 +++------- 6 files changed, 38 insertions(+), 149 deletions(-) diff --git a/core/vm/contracts_lightclient.go b/core/vm/contracts_lightclient.go index 57e654ee64..96cdc41406 100644 --- a/core/vm/contracts_lightclient.go +++ b/core/vm/contracts_lightclient.go @@ -153,43 +153,23 @@ func (c *iavlMerkleProofValidateMoran) Run(input []byte) (result []byte, err err return c.basicIavlMerkleProofValidate.Run(input) } -type iavlMerkleProofValidateBohr struct{} +type iavlMerkleProofValidateBohr struct { + basicIavlMerkleProofValidate +} func (c *iavlMerkleProofValidateBohr) RequiredGas(_ []byte) uint64 { return params.IAVLMerkleProofValidateGas } -// input: -// | version | proof | -// | 8 bytes | | func (c *iavlMerkleProofValidateBohr) Run(input []byte) (result []byte, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("internal error: %v\n", r) - } - }() - - if uint64(len(input)) <= precompileContractInputMetaDataLength { - return nil, fmt.Errorf("invalid input: input should include %d bytes payload length and payload", precompileContractInputMetaDataLength) - } - - payloadLength := binary.BigEndian.Uint64(input[precompileContractInputMetaDataLength-uint64TypeLength : precompileContractInputMetaDataLength]) - if uint64(len(input)) != payloadLength+precompileContractInputMetaDataLength+uint64TypeLength { - return nil, fmt.Errorf("invalid input: input size should be %d, actual the size is %d", payloadLength+precompileContractInputMetaDataLength+uint64TypeLength, len(input)) - } - - version := binary.BigEndian.Uint64(input[precompileContractInputMetaDataLength : precompileContractInputMetaDataLength+uint64TypeLength]) - - kvmp, err := lightclient.DecodeKeyValueMerkleProof(input[precompileContractInputMetaDataLength+uint64TypeLength:]) - if err != nil { - return - } - valid := kvmp.ValidateIcs23(int64(version)) - if !valid { - return nil, fmt.Errorf("invalid merkle proof") + c.basicIavlMerkleProofValidate.proofRuntime = lightclient.Ics23CompatibleProofRuntime() + c.basicIavlMerkleProofValidate.verifiers = []merkle.ProofOpVerifier{ + forbiddenAbsenceOpVerifier, + singleValueOpVerifier, + multiStoreOpVerifier, + forbiddenSimpleValueOpVerifier, } - - return successfulMerkleResult(), nil + return c.basicIavlMerkleProofValidate.Run(input) } func successfulMerkleResult() []byte { @@ -199,7 +179,8 @@ func successfulMerkleResult() []byte { } type basicIavlMerkleProofValidate struct { - verifiers []merkle.ProofOpVerifier + verifiers []merkle.ProofOpVerifier + proofRuntime *merkle.ProofRuntime } func (c *basicIavlMerkleProofValidate) Run(input []byte) (result []byte, err error) { @@ -222,6 +203,11 @@ func (c *basicIavlMerkleProofValidate) Run(input []byte) (result []byte, err err if err != nil { return nil, err } + if c.proofRuntime == nil { + kvmp.SetProofRuntime(lightclient.DefaultProofRuntime()) + } else { + kvmp.SetProofRuntime(c.proofRuntime) + } kvmp.SetVerifiers(c.verifiers) valid := kvmp.Validate() if !valid { diff --git a/core/vm/contracts_lightclient_test.go b/core/vm/contracts_lightclient_test.go index eebcebf55b..b2c6f9330a 100644 --- a/core/vm/contracts_lightclient_test.go +++ b/core/vm/contracts_lightclient_test.go @@ -115,13 +115,13 @@ func TestTmHeaderValidateAndMerkleProofValidate(t *testing.T) { } func TestIcs23Proof(t *testing.T) { - appHash, err := hex.DecodeString("9e29c388c7fa0a27fd22c447d671da114261fa668ccbbc073731e570bf189c92") + appHash, err := hex.DecodeString("ae6d1123fc362b3297bfb19c9f9fabbcbd1e2555b923dead261905b8a2ff6db6") require.NoError(t, err) key, err := hex.DecodeString("77696e64") require.NoError(t, err) value, err := hex.DecodeString("626c6f7773") require.NoError(t, err) - proofBytes, err := hex.DecodeString("0a300a0a69637332333a6961766c120477696e641a1c0a1a0a0477696e641205626c6f77731a0b0801180120012a030002040a9d010a0c69637332333a73696d706c6512036962631a87010a84010a036962631220fb110273d89f0fd5620a0133bdac38249b8d0a2f1ce29d2fc08c3708a24240bd1a090801180120012a0100222708011201011a209db623f0b6cc658e2fb36b66282625a13c51aaa2a7c5e77dec3f8d49fba5cf2f222708011201011a207893b6019c4768f1e82e4e5809ba59e300ee48c0d3ceaafb082d6ca44614ab4e") + proofBytes, err := hex.DecodeString("0a300a0a69637332333a6961766c120477696e641a1c0a1a0a0477696e641205626c6f77731a0b0801180120012a030002040a9d010a0c69637332333a73696d706c6512036962631a87010a84010a036962631220141acb8632cfb808f293f2649cb9aabaca74fc18640900ffd0d48e2994b2a1521a090801180120012a0100222708011201011a205f0ba08283de309300409486e978a3ea59d82bccc838b07c7d39bd87c16a5034222708011201011a20455b81ef5591150bd24d3e57a769f65518b16de93487f0fab02271b3d69e2852") require.NoError(t, err) merkleProofInput := make([]byte, 32+32+len(key)+32+len(value)+32+len(proofBytes)) @@ -141,11 +141,6 @@ func TestIcs23Proof(t *testing.T) { binary.BigEndian.PutUint64(totalLengthPrefix[16:24], 0) binary.BigEndian.PutUint64(totalLengthPrefix[24:], uint64(len(merkleProofInput))) - versionBytes := make([]byte, uint64TypeLength) - binary.BigEndian.PutUint64(versionBytes, 2) - - merkleProofInput = append(versionBytes, merkleProofInput...) - input := append(totalLengthPrefix, merkleProofInput...) validator := iavlMerkleProofValidateBohr{} diff --git a/core/vm/lightclient/ics23_proof.go b/core/vm/lightclient/ics23_proof.go index 931cac76d2..4777caa9d8 100644 --- a/core/vm/lightclient/ics23_proof.go +++ b/core/vm/lightclient/ics23_proof.go @@ -1,11 +1,8 @@ package lightclient import ( - "bytes" "fmt" - "github.com/pkg/errors" - "github.com/bnb-chain/ics23" "github.com/tendermint/tendermint/crypto/merkle" ) @@ -104,82 +101,3 @@ func (op CommitmentOp) ProofOp() merkle.ProofOp { Data: bz, } } - -func Ics23ProofRuntime() (prt *merkle.ProofRuntime) { - prt = merkle.NewProofRuntime() - prt.RegisterOpDecoder(ProofOpIAVLCommitment, CommitmentOpDecoder) - prt.RegisterOpDecoder(ProofOpSimpleMerkleCommitment, CommitmentOpDecoder) - return -} - -func VerifyValue(root []byte, version int64, proof *merkle.Proof, keyPath string, value []byte) error { - poz, err := Ics23ProofRuntime().DecodeProof(proof) - if err != nil { - return fmt.Errorf("decoding proof erorr, err=%s", err.Error()) - } - - if len(poz) != 2 { - return fmt.Errorf("length of proof ops should be 2") - } - - keys, err := merkle.KeyPathToKeys(keyPath) - if err != nil { - return fmt.Errorf("get keys error, err=%s", err.Error()) - } - - if len(keys) != 2 { - return fmt.Errorf("length of keys should be 2") - } - - storeKey, valueKey := keys[0], keys[1] - - iavlPo := poz[0] - if iavlPo.ProofOp().Type != ProofOpIAVLCommitment { - return fmt.Errorf("invalid proof op type, should be %s", ProofOpIAVLCommitment) - } - - if !bytes.Equal(valueKey, iavlPo.GetKey()) { - return fmt.Errorf("invalid proof of key, require %X, got %X", iavlPo.GetKey(), valueKey) - } - - iavlRoot, err := iavlPo.Run([][]byte{value}) - if err != nil { - return fmt.Errorf("invalid iavl proof, err=%s", err.Error()) - } - - if len(iavlRoot) != 1 { - return fmt.Errorf("invalid return of iavl proof") - } - - // calculate store hash - storeInfo := StoreInfo{ - Name: string(storeKey), - Core: StoreCore{ - CommitID: CommitID{ - Version: version, - Hash: iavlRoot[0], - }, - }, - } - storeHash := storeInfo.Hash() - - simplePo := poz[1] - if simplePo.ProofOp().Type != ProofOpSimpleMerkleCommitment { - return fmt.Errorf("invalid proof op type, should be %s", ProofOpSimpleMerkleCommitment) - } - if !bytes.Equal(storeKey, simplePo.GetKey()) { - return fmt.Errorf("invalid proof of key, require %X, got %X", simplePo.GetKey(), storeKey) - } - storeRoot, err := simplePo.Run([][]byte{storeHash}) - if err != nil { - return fmt.Errorf("invalid simple proof, err=%s", err.Error()) - } - if len(storeRoot) != 1 { - return fmt.Errorf("invald return of simple proof") - } - - if !bytes.Equal(root, storeRoot[0]) { - return errors.Errorf("calculated root hash is invalid: expected %+v but got %+v", root, storeRoot[0]) - } - return nil -} diff --git a/core/vm/lightclient/multistoreproof.go b/core/vm/lightclient/multistoreproof.go index 819b7f0011..b7f3618186 100644 --- a/core/vm/lightclient/multistoreproof.go +++ b/core/vm/lightclient/multistoreproof.go @@ -124,10 +124,6 @@ func (op MultiStoreProofOp) Run(args [][]byte) ([][]byte, error) { return nil, cmn.NewError("key %v not found in multistore proof", op.key) } -//----------------------------------------------------------------------------- - -// XXX: This should be managed by the rootMultiStore which may want to register -// more proof ops? func DefaultProofRuntime() (prt *merkle.ProofRuntime) { prt = merkle.NewProofRuntime() prt.RegisterOpDecoder(merkle.ProofOpSimpleValue, merkle.SimpleValueOpDecoder) @@ -136,3 +132,14 @@ func DefaultProofRuntime() (prt *merkle.ProofRuntime) { prt.RegisterOpDecoder(ProofOpMultiStore, MultiStoreProofOpDecoder) return } + +func Ics23CompatibleProofRuntime() (prt *merkle.ProofRuntime) { + prt = merkle.NewProofRuntime() + prt.RegisterOpDecoder(merkle.ProofOpSimpleValue, merkle.SimpleValueOpDecoder) + prt.RegisterOpDecoder(iavl.ProofOpIAVLValue, iavl.IAVLValueOpDecoder) + prt.RegisterOpDecoder(iavl.ProofOpIAVLAbsence, iavl.IAVLAbsenceOpDecoder) + prt.RegisterOpDecoder(ProofOpMultiStore, MultiStoreProofOpDecoder) + prt.RegisterOpDecoder(ProofOpIAVLCommitment, CommitmentOpDecoder) + prt.RegisterOpDecoder(ProofOpSimpleMerkleCommitment, CommitmentOpDecoder) + return +} diff --git a/core/vm/lightclient/rootmultistore.go b/core/vm/lightclient/rootmultistore.go index d0e016c0e7..4c26c7d8f9 100644 --- a/core/vm/lightclient/rootmultistore.go +++ b/core/vm/lightclient/rootmultistore.go @@ -27,7 +27,6 @@ func (cid CommitID) String() string { //---------------------------------------- // CommitInfo -// NOTE: Keep CommitInfo a simple immutable struct. type CommitInfo struct { // Version @@ -39,7 +38,6 @@ type CommitInfo struct { // Hash returns the simple merkle root hash of the stores sorted by name. func (ci CommitInfo) Hash() []byte { - // TODO cache to ci.hash []byte m := make(map[string][]byte, len(ci.StoreInfos)) for _, storeInfo := range ci.StoreInfos { m[storeInfo.Name] = storeInfo.Hash() @@ -71,7 +69,6 @@ type StoreCore struct { // ... maybe add more state } -// Implements merkle.Hasher. func (si StoreInfo) Hash() []byte { // Doesn't write Name, since merkle.SimpleHashFromMap() will // include them via the keys. diff --git a/core/vm/lightclient/types.go b/core/vm/lightclient/types.go index ca668b3ae7..732c02a6aa 100644 --- a/core/vm/lightclient/types.go +++ b/core/vm/lightclient/types.go @@ -216,7 +216,12 @@ type KeyValueMerkleProof struct { AppHash []byte Proof *merkle.Proof - verifiers []merkle.ProofOpVerifier + verifiers []merkle.ProofOpVerifier + proofRuntime *merkle.ProofRuntime +} + +func (kvmp *KeyValueMerkleProof) SetProofRuntime(prt *merkle.ProofRuntime) { + kvmp.proofRuntime = prt } func (kvmp *KeyValueMerkleProof) SetVerifiers(verifiers []merkle.ProofOpVerifier) { @@ -224,35 +229,16 @@ func (kvmp *KeyValueMerkleProof) SetVerifiers(verifiers []merkle.ProofOpVerifier } func (kvmp *KeyValueMerkleProof) Validate() bool { - prt := DefaultProofRuntime() - kp := merkle.KeyPath{} kp = kp.AppendKey([]byte(kvmp.StoreName), merkle.KeyEncodingURL) kp = kp.AppendKey(kvmp.Key, merkle.KeyEncodingURL) if len(kvmp.Value) == 0 { - err := prt.VerifyAbsence(kvmp.Proof, kvmp.AppHash, kp.String(), kvmp.verifiers...) + err := kvmp.proofRuntime.VerifyAbsence(kvmp.Proof, kvmp.AppHash, kp.String(), kvmp.verifiers...) return err == nil } - err := prt.VerifyValue(kvmp.Proof, kvmp.AppHash, kp.String(), kvmp.Value, kvmp.verifiers...) - return err == nil -} - -func (kvmp *KeyValueMerkleProof) ValidateIcs23(version int64) bool { - kp := merkle.KeyPath{} - kp = kp.AppendKey([]byte(kvmp.StoreName), merkle.KeyEncodingURL) - kp = kp.AppendKey(kvmp.Key, merkle.KeyEncodingURL) - - // does not support absence - if len(kvmp.Value) == 0 { - return false - } - - err := VerifyValue(kvmp.AppHash, version, kvmp.Proof, kp.String(), kvmp.Value) - if err != nil { - println(err.Error()) - } + err := kvmp.proofRuntime.VerifyValue(kvmp.Proof, kvmp.AppHash, kp.String(), kvmp.Value, kvmp.verifiers...) return err == nil }