Forráskód Böngészése

add precompile light client contracts

fudongbai 5 éve
szülő
commit
1a2da608df

+ 134 - 0
core/vm/contracts_lightclient.go

@@ -0,0 +1,134 @@
+package vm
+
+import (
+	"encoding/binary"
+	"fmt"
+
+	"github.com/ethereum/go-ethereum/core/vm/lightclient"
+	"github.com/ethereum/go-ethereum/params"
+)
+
+const (
+	precompileContractInputMetaDataLength uint64 = 32
+	consensusStateLengthBytesLength       uint64 = 32
+
+	tmHeaderValidateResultMetaDataLength uint64 = 32
+	merkleProofValidateResultLength      uint64 = 32
+)
+
+// input:
+// consensus state length | consensus state | tendermint header |
+// 32 bytes               |                 |                   |
+func decodeTendermintHeaderValidationInput(input []byte) (*lightclient.ConsensusState, *lightclient.Header, error) {
+	csLen := binary.BigEndian.Uint64(input[consensusStateLengthBytesLength-8 : consensusStateLengthBytesLength])
+	if uint64(len(input)) <= consensusStateLengthBytesLength+csLen {
+		return nil, nil, fmt.Errorf("expected payload size %d, actual size: %d", consensusStateLengthBytesLength+csLen, len(input))
+	}
+
+	cs, err := lightclient.DecodeConsensusState(input[consensusStateLengthBytesLength : consensusStateLengthBytesLength+csLen])
+	if err != nil {
+		return nil, nil, err
+	}
+	header, err := lightclient.DecodeHeader(input[consensusStateLengthBytesLength+csLen:])
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return &cs, header, nil
+}
+
+// tmHeaderValidate implemented as a native contract.
+type tmHeaderValidate struct{}
+
+func (c *tmHeaderValidate) RequiredGas(input []byte) uint64 {
+	return params.TendermintHeaderValidateGas
+}
+
+func (c *tmHeaderValidate) 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")
+	}
+
+	payloadLength := binary.BigEndian.Uint64(input[precompileContractInputMetaDataLength-8 : precompileContractInputMetaDataLength])
+	if uint64(len(input)) != payloadLength+precompileContractInputMetaDataLength {
+		return nil, fmt.Errorf("invalid input: input size should be %d, actual the size is %d", payloadLength+precompileContractInputMetaDataLength, len(input))
+	}
+
+	cs, header, err := decodeTendermintHeaderValidationInput(input[precompileContractInputMetaDataLength:])
+	if err != nil {
+		return nil, err
+	}
+
+	validatorSetChanged, err := cs.ApplyHeader(header)
+	if err != nil {
+		return nil, err
+	}
+
+	consensusStateBytes, err := cs.EncodeConsensusState()
+	if err != nil {
+		return nil, err
+	}
+
+	// result
+	// | validatorSetChanged | empty      | consensusStateBytesLength |  new consensusState |
+	// | 1 byte              | 23 bytes   | 8 bytes                   |                     |
+	lengthBytes := make([]byte, tmHeaderValidateResultMetaDataLength)
+	if validatorSetChanged {
+		copy(lengthBytes[:1], []byte{0x01})
+	}
+	consensusStateBytesLength := uint64(len(consensusStateBytes))
+	binary.BigEndian.PutUint64(lengthBytes[tmHeaderValidateResultMetaDataLength-8:], consensusStateBytesLength)
+
+	result = append(lengthBytes, consensusStateBytes...)
+
+	return result, nil
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------------
+
+// tmHeaderValidate implemented as a native contract.
+type iavlMerkleProofValidate struct{}
+
+func (c *iavlMerkleProofValidate) RequiredGas(input []byte) uint64 {
+	return params.IAVLMerkleProofValidateGas
+}
+
+// input:
+// | payload length | payload    |
+// | 32 bytes       |            |
+func (c *iavlMerkleProofValidate) 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-8 : precompileContractInputMetaDataLength])
+	if uint64(len(input)) != payloadLength+precompileContractInputMetaDataLength {
+		return nil, fmt.Errorf("invalid input: input size should be %d, actual the size is %d", payloadLength+precompileContractInputMetaDataLength, len(input))
+	}
+
+	kvmp, err := lightclient.DecodeKeyValueMerkleProof(input[precompileContractInputMetaDataLength:])
+	if err != nil {
+		return nil, err
+	}
+
+	valid := kvmp.Validate()
+	if !valid {
+		return nil, fmt.Errorf("invalid merkle proof")
+	}
+
+	result = make([]byte, merkleProofValidateResultLength)
+	binary.BigEndian.PutUint64(result[merkleProofValidateResultLength-8:], 0x01)
+	return result, nil
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 30 - 0
core/vm/contracts_lightclient_test.go


+ 138 - 0
core/vm/lightclient/multistoreproof.go

@@ -0,0 +1,138 @@
+package lightclient
+
+import (
+	"bytes"
+	"fmt"
+
+	"github.com/tendermint/iavl"
+	"github.com/tendermint/tendermint/crypto/merkle"
+	cmn "github.com/tendermint/tendermint/libs/common"
+)
+
+// MultiStoreProof defines a collection of store proofs in a multi-store
+type MultiStoreProof struct {
+	StoreInfos []StoreInfo
+}
+
+func NewMultiStoreProof(storeInfos []StoreInfo) *MultiStoreProof {
+	return &MultiStoreProof{StoreInfos: storeInfos}
+}
+
+// ComputeRootHash returns the root hash for a given multi-store proof.
+func (proof *MultiStoreProof) ComputeRootHash() []byte {
+	ci := CommitInfo{
+		Version:    -1, // TODO: Not needed; improve code.
+		StoreInfos: proof.StoreInfos,
+	}
+	return ci.Hash()
+}
+
+// RequireProof return whether proof is require for the subpath
+func RequireProof(subpath string) bool {
+	// XXX: create a better convention.
+	// Currently, only when query subpath is "/store" or "/key", will proof be included in response.
+	// If there are some changes about proof building in iavlstore.go, we must change code here to keep consistency with iavlstore.go:212
+	if subpath == "/store" || subpath == "/key" {
+		return true
+	}
+	return false
+}
+
+//-----------------------------------------------------------------------------
+
+var _ merkle.ProofOperator = MultiStoreProofOp{}
+
+// the multi-store proof operation constant value
+const ProofOpMultiStore = "multistore"
+
+// TODO: document
+type MultiStoreProofOp struct {
+	// Encoded in ProofOp.Key
+	key []byte
+
+	// To encode in ProofOp.Data.
+	Proof *MultiStoreProof `json:"proof"`
+}
+
+func NewMultiStoreProofOp(key []byte, proof *MultiStoreProof) MultiStoreProofOp {
+	return MultiStoreProofOp{
+		key:   key,
+		Proof: proof,
+	}
+}
+
+// MultiStoreProofOpDecoder returns a multi-store merkle proof operator from a
+// given proof operation.
+func MultiStoreProofOpDecoder(pop merkle.ProofOp) (merkle.ProofOperator, error) {
+	if pop.Type != ProofOpMultiStore {
+		return nil, cmn.NewError("unexpected ProofOp.Type; got %v, want %v", pop.Type, ProofOpMultiStore)
+	}
+
+	// XXX: a bit strange as we'll discard this, but it works
+	var op MultiStoreProofOp
+
+	err := Cdc.UnmarshalBinaryLengthPrefixed(pop.Data, &op)
+	if err != nil {
+		return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data into MultiStoreProofOp")
+	}
+
+	return NewMultiStoreProofOp(pop.Key, op.Proof), nil
+}
+
+// ProofOp return a merkle proof operation from a given multi-store proof
+// operation.
+func (op MultiStoreProofOp) ProofOp() merkle.ProofOp {
+	bz := Cdc.MustMarshalBinaryLengthPrefixed(op)
+	return merkle.ProofOp{
+		Type: ProofOpMultiStore,
+		Key:  op.key,
+		Data: bz,
+	}
+}
+
+// String implements the Stringer interface for a mult-store proof operation.
+func (op MultiStoreProofOp) String() string {
+	return fmt.Sprintf("MultiStoreProofOp{%v}", op.GetKey())
+}
+
+// GetKey returns the key for a multi-store proof operation.
+func (op MultiStoreProofOp) GetKey() []byte {
+	return op.key
+}
+
+// Run executes a multi-store proof operation for a given value. It returns
+// the root hash if the value matches all the store's commitID's hash or an
+// error otherwise.
+func (op MultiStoreProofOp) Run(args [][]byte) ([][]byte, error) {
+	if len(args) != 1 {
+		return nil, cmn.NewError("Value size is not 1")
+	}
+
+	value := args[0]
+	root := op.Proof.ComputeRootHash()
+
+	for _, si := range op.Proof.StoreInfos {
+		if si.Name == string(op.key) {
+			if bytes.Equal(value, si.Core.CommitID.Hash) {
+				return [][]byte{root}, nil
+			}
+
+			return nil, cmn.NewError("hash mismatch for substore %v: %X vs %X", si.Name, si.Core.CommitID.Hash, value)
+		}
+	}
+
+	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)
+	prt.RegisterOpDecoder(iavl.ProofOpIAVLValue, iavl.IAVLValueOpDecoder)
+	prt.RegisterOpDecoder(iavl.ProofOpIAVLAbsence, iavl.IAVLAbsenceOpDecoder)
+	prt.RegisterOpDecoder(ProofOpMultiStore, MultiStoreProofOpDecoder)
+	return
+}

+ 86 - 0
core/vm/lightclient/rootmultistore.go

@@ -0,0 +1,86 @@
+package lightclient
+
+import (
+	"fmt"
+
+	"github.com/tendermint/tendermint/crypto/merkle"
+	"github.com/tendermint/tendermint/crypto/tmhash"
+)
+
+//----------------------------------------
+// CommitID
+
+// CommitID contains the tree version number and its merkle root.
+type CommitID struct {
+	Version int64
+	Hash    []byte
+}
+
+func (cid CommitID) IsZero() bool { //nolint
+	return cid.Version == 0 && len(cid.Hash) == 0
+}
+
+func (cid CommitID) String() string {
+	return fmt.Sprintf("CommitID{%v:%X}", cid.Hash, cid.Version)
+}
+
+//----------------------------------------
+// CommitInfo
+
+// NOTE: Keep CommitInfo a simple immutable struct.
+type CommitInfo struct {
+
+	// Version
+	Version int64
+
+	// Store info for
+	StoreInfos []StoreInfo
+}
+
+// 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()
+	}
+	return merkle.SimpleHashFromMap(m)
+}
+
+func (ci CommitInfo) CommitID() CommitID {
+	return CommitID{
+		Version: ci.Version,
+		Hash:    ci.Hash(),
+	}
+}
+
+//----------------------------------------
+// StoreInfo
+
+// StoreInfo contains the name and core reference for an
+// underlying store.  It is the leaf of the rootMultiStores top
+// level simple merkle tree.
+type StoreInfo struct {
+	Name string
+	Core StoreCore
+}
+
+type StoreCore struct {
+	// StoreType StoreType
+	CommitID CommitID
+	// ... 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.
+	bz, _ := Cdc.MarshalBinaryLengthPrefixed(si.Core) // Does not error
+	hasher := tmhash.New()
+	_, err := hasher.Write(bz)
+	if err != nil {
+		// TODO: Handle with #870
+		panic(err)
+	}
+	return hasher.Sum(nil)
+}

+ 292 - 0
core/vm/lightclient/types.go

@@ -0,0 +1,292 @@
+package lightclient
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+
+	"github.com/tendermint/tendermint/crypto/ed25519"
+	"github.com/tendermint/tendermint/crypto/merkle"
+	lerr "github.com/tendermint/tendermint/lite/errors"
+	tmtypes "github.com/tendermint/tendermint/types"
+)
+
+const (
+	chainIDLength              uint64 = 32
+	heightLength               uint64 = 8
+	validatorSetHashLength     uint64 = 32
+	validatorPubkeyLength      uint64 = 32
+	validatorVotingPowerLength uint64 = 8
+	appHashLength              uint64 = 32
+	storeNameLengthBytesLength uint64 = 32
+	keyLengthBytesLength       uint64 = 32
+	valueLengthBytesLength     uint64 = 32
+	maxConsensusStateLength    uint64 = 32 * (128 - 1) // maximum validator quantity 99
+)
+
+type ConsensusState struct {
+	ChainID             string
+	Height              uint64
+	AppHash             []byte
+	CurValidatorSetHash []byte
+	NextValidatorSet    *tmtypes.ValidatorSet
+}
+
+// input:
+// | chainID   | height   | appHash  | curValidatorSetHash | [{validator pubkey, voting power}] |
+// | 32 bytes  | 8 bytes  | 32 bytes | 32 bytes            | [{32 bytes, 8 bytes}]              |
+func DecodeConsensusState(input []byte) (ConsensusState, error) {
+
+	minimumLength := chainIDLength + heightLength + appHashLength + validatorSetHashLength
+	singleValidatorBytesLength := validatorPubkeyLength + validatorVotingPowerLength
+
+	inputLen := uint64(len(input))
+	if inputLen <= minimumLength || (inputLen-minimumLength)%singleValidatorBytesLength != 0 {
+		return ConsensusState{}, fmt.Errorf("expected input size %d+%d*N, actual input size: %d", minimumLength, singleValidatorBytesLength, inputLen)
+	}
+	pos := uint64(0)
+
+	chainID := string(bytes.Trim(input[pos:pos+chainIDLength], "\x00"))
+	pos += chainIDLength
+
+	height := binary.BigEndian.Uint64(input[pos : pos+heightLength])
+	pos += heightLength
+
+	appHash := input[pos : pos+appHashLength]
+	pos += appHashLength
+
+	curValidatorSetHash := input[pos : pos+validatorSetHashLength]
+	pos += validatorSetHashLength
+
+	nextValidatorSetLength := (inputLen - minimumLength) / singleValidatorBytesLength
+	validatorSetBytes := input[pos:]
+	var validatorSet []*tmtypes.Validator
+	for index := uint64(0); index < nextValidatorSetLength; index++ {
+		validatorAndPowerBytes := validatorSetBytes[singleValidatorBytesLength*index : singleValidatorBytesLength*(index+1)]
+		var pubkey ed25519.PubKeyEd25519
+		copy(pubkey[:], validatorAndPowerBytes[:validatorPubkeyLength])
+		votingPower := int64(binary.BigEndian.Uint64(validatorAndPowerBytes[validatorPubkeyLength:]))
+
+		validator := tmtypes.NewValidator(pubkey, votingPower)
+		validatorSet = append(validatorSet, validator)
+	}
+
+	consensusState := ConsensusState{
+		ChainID:             chainID,
+		Height:              height,
+		AppHash:             appHash,
+		CurValidatorSetHash: curValidatorSetHash,
+		NextValidatorSet: &tmtypes.ValidatorSet{
+			Validators: validatorSet,
+		},
+	}
+
+	return consensusState, nil
+}
+
+// output:
+// | chainID   | height   | appHash  | curValidatorSetHash | [{validator pubkey, voting power}] |
+// | 32 bytes  | 8 bytes  | 32 bytes | 32 bytes            | [{32 bytes, 8 bytes}]              |
+func (cs ConsensusState) EncodeConsensusState() ([]byte, error) {
+	validatorSetLength := uint64(len(cs.NextValidatorSet.Validators))
+	serializeLength := chainIDLength + heightLength + appHashLength + validatorSetHashLength + validatorSetLength*(validatorPubkeyLength+validatorVotingPowerLength)
+	if serializeLength > maxConsensusStateLength {
+		return nil, fmt.Errorf("too many validators %d, consensus state bytes should not exceed %d", len(cs.NextValidatorSet.Validators), maxConsensusStateLength)
+	}
+
+	encodingBytes := make([]byte, serializeLength)
+
+	pos := uint64(0)
+	if uint64(len(cs.ChainID)) > chainIDLength {
+		return nil, fmt.Errorf("chainID length should be no more than 32")
+	}
+	copy(encodingBytes[pos:pos+chainIDLength], cs.ChainID)
+	pos += chainIDLength
+
+	binary.BigEndian.PutUint64(encodingBytes[pos:pos+heightLength], uint64(cs.Height))
+	pos += heightLength
+
+	copy(encodingBytes[pos:pos+appHashLength], cs.AppHash)
+	pos += appHashLength
+
+	copy(encodingBytes[pos:pos+validatorSetHashLength], cs.CurValidatorSetHash)
+	pos += validatorSetHashLength
+
+	for index := uint64(0); index < validatorSetLength; index++ {
+		validator := cs.NextValidatorSet.Validators[index]
+		pubkey, ok := validator.PubKey.(ed25519.PubKeyEd25519)
+		if !ok {
+			return nil, fmt.Errorf("invalid pubkey type")
+		}
+
+		copy(encodingBytes[pos:pos+validatorPubkeyLength], pubkey[:])
+		pos += validatorPubkeyLength
+
+		binary.BigEndian.PutUint64(encodingBytes[pos:pos+validatorVotingPowerLength], uint64(validator.VotingPower))
+		pos += validatorVotingPowerLength
+	}
+
+	return encodingBytes, nil
+}
+
+func (cs *ConsensusState) ApplyHeader(header *Header) (bool, error) {
+	if uint64(header.Height) < cs.Height {
+		return false, fmt.Errorf("header height < consensus height (%d < %d)", header.Height, cs.Height)
+	}
+
+	if err := header.Validate(cs.ChainID); err != nil {
+		return false, err
+	}
+
+	trustedNextHash := cs.NextValidatorSet.Hash()
+	if cs.Height == uint64(header.Height-1) {
+		if !bytes.Equal(trustedNextHash, header.ValidatorsHash) {
+			return false, lerr.ErrUnexpectedValidators(header.ValidatorsHash, trustedNextHash)
+		}
+		err := cs.NextValidatorSet.VerifyCommit(cs.ChainID, header.Commit.BlockID, header.Height, header.Commit)
+		if err != nil {
+			return false, err
+		}
+	} else {
+		err := cs.NextValidatorSet.VerifyFutureCommit(header.ValidatorSet, cs.ChainID,
+			header.Commit.BlockID, header.Height, header.Commit)
+		if err != nil {
+			return false, err
+		}
+	}
+	validatorSetChanged := false
+	if !bytes.Equal(cs.CurValidatorSetHash, header.ValidatorsHash) || !bytes.Equal(cs.NextValidatorSet.Hash(), header.NextValidatorsHash) {
+		validatorSetChanged = true
+	}
+	// update consensus state
+	cs.Height = uint64(header.Height)
+	cs.AppHash = header.AppHash
+	cs.CurValidatorSetHash = header.ValidatorsHash
+	cs.NextValidatorSet = header.NextValidatorSet
+
+	return validatorSetChanged, nil
+}
+
+type Header struct {
+	tmtypes.SignedHeader
+	ValidatorSet     *tmtypes.ValidatorSet `json:"validator_set"`
+	NextValidatorSet *tmtypes.ValidatorSet `json:"next_validator_set"`
+}
+
+func (h *Header) Validate(chainID string) error {
+	if err := h.SignedHeader.ValidateBasic(chainID); err != nil {
+		return err
+	}
+	if h.ValidatorSet == nil {
+		return fmt.Errorf("invalid header: validator set is nil")
+	}
+	if h.NextValidatorSet == nil {
+		return fmt.Errorf("invalid header: next validator set is nil")
+	}
+	if !bytes.Equal(h.ValidatorsHash, h.ValidatorSet.Hash()) {
+		return fmt.Errorf("invalid header: validator set does not match hash")
+	}
+	if !bytes.Equal(h.NextValidatorsHash, h.NextValidatorSet.Hash()) {
+		return fmt.Errorf("invalid header: next validator set does not match hash")
+	}
+	return nil
+}
+
+func (h *Header) EncodeHeader() ([]byte, error) {
+	bz, err := Cdc.MarshalBinaryLengthPrefixed(h)
+	if err != nil {
+		return nil, err
+	}
+	return bz, nil
+}
+
+func DecodeHeader(input []byte) (*Header, error) {
+	var header Header
+	err := Cdc.UnmarshalBinaryLengthPrefixed(input, &header)
+	if err != nil {
+		return nil, err
+	}
+	return &header, nil
+}
+
+type KeyValueMerkleProof struct {
+	Key       []byte
+	Value     []byte
+	StoreName string
+	AppHash   []byte
+	Proof     *merkle.Proof
+}
+
+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())
+		if err != nil {
+			return false
+		}
+		return true
+	}
+
+	err := prt.VerifyValue(kvmp.Proof, kvmp.AppHash, kp.String(), kvmp.Value)
+	if err != nil {
+		return false
+	}
+	return true
+}
+
+// input:
+// | storeName | key length | key | value length | value | appHash  | proof |
+// | 32 bytes  | 32 bytes   |     | 32 bytes     |       | 32 bytes |       |
+func DecodeKeyValueMerkleProof(input []byte) (*KeyValueMerkleProof, error) {
+	inputLength := uint64(len(input))
+	pos := uint64(0)
+
+	if inputLength <= storeNameLengthBytesLength+keyLengthBytesLength+valueLengthBytesLength+appHashLength {
+		return nil, fmt.Errorf("input length should be no less than %d", storeNameLengthBytesLength+keyLengthBytesLength+valueLengthBytesLength+appHashLength)
+	}
+	storeName := string(bytes.Trim(input[pos:pos+storeNameLengthBytesLength], "\x00"))
+	pos += storeNameLengthBytesLength
+
+	keyLength := binary.BigEndian.Uint64(input[pos+keyLengthBytesLength-8 : pos+keyLengthBytesLength])
+	pos += keyLengthBytesLength
+
+	if inputLength <= storeNameLengthBytesLength+keyLengthBytesLength+keyLength+valueLengthBytesLength {
+		return nil, fmt.Errorf("invalid input, keyLength %d is too long", keyLength)
+	}
+	key := input[pos : pos+keyLength]
+	pos += keyLength
+
+	valueLength := binary.BigEndian.Uint64(input[pos+valueLengthBytesLength-8 : pos+valueLengthBytesLength])
+	pos += valueLengthBytesLength
+
+	if inputLength <= storeNameLengthBytesLength+keyLengthBytesLength+keyLength+valueLengthBytesLength+valueLength+appHashLength {
+		return nil, fmt.Errorf("invalid input, valueLength %d is too long", valueLength)
+	}
+	value := input[pos : pos+valueLength]
+	pos += valueLength
+
+	appHash := input[pos : pos+appHashLength]
+	pos += appHashLength
+
+	proofBytes := input[pos:]
+	var merkleProof merkle.Proof
+	err := merkleProof.Unmarshal(proofBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	keyValueMerkleProof := &KeyValueMerkleProof{
+		Key:       key,
+		Value:     value,
+		StoreName: storeName,
+		AppHash:   appHash,
+		Proof:     &merkleProof,
+	}
+
+	return keyValueMerkleProof, nil
+}

+ 86 - 0
core/vm/lightclient/utils.go

@@ -0,0 +1,86 @@
+package lightclient
+
+import (
+	"fmt"
+
+	rpcclient "github.com/tendermint/tendermint/rpc/client"
+	tmtypes "github.com/tendermint/tendermint/types"
+)
+
+func GetInitConsensusState(node rpcclient.Client, height int64) (*ConsensusState, error) {
+	status, err := node.Status()
+	if err != nil {
+		return nil, err
+	}
+
+	nextValHeight := height + 1
+	nextValidatorSet, err := node.Validators(&nextValHeight)
+	if err != nil {
+		return nil, err
+	}
+
+	header, err := node.Block(&height)
+	if err != nil {
+		return nil, err
+	}
+
+	appHash := header.BlockMeta.Header.AppHash
+	curValidatorSetHash := header.BlockMeta.Header.ValidatorsHash
+
+	cs := &ConsensusState{
+		ChainID:             status.NodeInfo.Network,
+		Height:              uint64(height),
+		AppHash:             appHash,
+		CurValidatorSetHash: curValidatorSetHash,
+		NextValidatorSet: &tmtypes.ValidatorSet{
+			Validators: nextValidatorSet.Validators,
+		},
+	}
+	return cs, nil
+}
+
+func QueryTendermintHeader(node rpcclient.Client, height int64) (*Header, error) {
+	nextHeight := height + 1
+
+	commit, err := node.Commit(&height)
+	if err != nil {
+		return nil, err
+	}
+
+	validators, err := node.Validators(&height)
+	if err != nil {
+		return nil, err
+	}
+
+	nextvalidators, err := node.Validators(&nextHeight)
+	if err != nil {
+		return nil, err
+	}
+
+	header := &Header{
+		SignedHeader:     commit.SignedHeader,
+		ValidatorSet:     tmtypes.NewValidatorSet(validators.Validators),
+		NextValidatorSet: tmtypes.NewValidatorSet(nextvalidators.Validators),
+	}
+
+	return header, nil
+}
+
+func QueryKeyWithProof(node rpcclient.Client, key []byte, storeName string, height int64) ([]byte, []byte, []byte, error) {
+	opts := rpcclient.ABCIQueryOptions{
+		Height: height,
+		Prove:  true,
+	}
+
+	path := fmt.Sprintf("/store/%s/%s", storeName, "key")
+	result, err := node.ABCIQueryWithOptions(path, key, opts)
+	if err != nil {
+		return nil, nil, nil, err
+	}
+	proofBytes, err := result.Response.Proof.Marshal()
+	if err != nil {
+		return nil, nil, nil, err
+	}
+
+	return key, result.Response.Value, proofBytes, nil
+}

+ 16 - 0
core/vm/lightclient/wire.go

@@ -0,0 +1,16 @@
+package lightclient
+
+import (
+	"github.com/tendermint/go-amino"
+	cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
+)
+
+type Codec = amino.Codec
+
+var Cdc *Codec
+
+func init() {
+	cdc := amino.NewCodec()
+	cryptoAmino.RegisterAmino(cdc)
+	Cdc = cdc.Seal()
+}

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott