github.com/Finschia/finschia-sdk@v0.48.1/store/internal/proofs/create.go (about)

     1  package proofs
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sort"
     7  
     8  	ics23 "github.com/confio/ics23/go"
     9  
    10  	sdkmaps "github.com/Finschia/finschia-sdk/store/internal/maps"
    11  )
    12  
    13  var (
    14  	ErrEmptyKey       = errors.New("key is empty")
    15  	ErrEmptyKeyInData = errors.New("data contains empty key")
    16  )
    17  
    18  // TendermintSpec constrains the format from ics23-tendermint (crypto/merkle SimpleProof)
    19  var TendermintSpec = &ics23.ProofSpec{
    20  	LeafSpec: &ics23.LeafOp{
    21  		Prefix:       []byte{0},
    22  		Hash:         ics23.HashOp_SHA256,
    23  		PrehashValue: ics23.HashOp_SHA256,
    24  		Length:       ics23.LengthOp_VAR_PROTO,
    25  	},
    26  	InnerSpec: &ics23.InnerSpec{
    27  		ChildOrder:      []int32{0, 1},
    28  		MinPrefixLength: 1,
    29  		MaxPrefixLength: 1,  // fixed prefix + one child
    30  		ChildSize:       32, // (no length byte)
    31  		Hash:            ics23.HashOp_SHA256,
    32  	},
    33  }
    34  
    35  /*
    36  CreateMembershipProof will produce a CommitmentProof that the given key (and queries value) exists in the iavl tree.
    37  If the key doesn't exist in the tree, this will return an error.
    38  */
    39  func CreateMembershipProof(data map[string][]byte, key []byte) (*ics23.CommitmentProof, error) {
    40  	if len(key) == 0 {
    41  		return nil, ErrEmptyKey
    42  	}
    43  	exist, err := createExistenceProof(data, key)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	proof := &ics23.CommitmentProof{
    48  		Proof: &ics23.CommitmentProof_Exist{
    49  			Exist: exist,
    50  		},
    51  	}
    52  	return proof, nil
    53  }
    54  
    55  /*
    56  CreateNonMembershipProof will produce a CommitmentProof that the given key doesn't exist in the iavl tree.
    57  If the key exists in the tree, this will return an error.
    58  */
    59  func CreateNonMembershipProof(data map[string][]byte, key []byte) (*ics23.CommitmentProof, error) {
    60  	if len(key) == 0 {
    61  		return nil, ErrEmptyKey
    62  	}
    63  	// ensure this key is not in the store
    64  	if _, ok := data[string(key)]; ok {
    65  		return nil, fmt.Errorf("cannot create non-membership proof if key is in map")
    66  	}
    67  
    68  	keys := SortedKeys(data)
    69  	rightidx := sort.SearchStrings(keys, string(key))
    70  
    71  	var err error
    72  	nonexist := &ics23.NonExistenceProof{
    73  		Key: key,
    74  	}
    75  
    76  	// include left proof unless key is left of entire map
    77  	if rightidx >= 1 {
    78  		leftkey := keys[rightidx-1]
    79  		nonexist.Left, err = createExistenceProof(data, []byte(leftkey))
    80  		if err != nil {
    81  			return nil, err
    82  		}
    83  	}
    84  
    85  	// include right proof unless key is right of entire map
    86  	if rightidx < len(keys) {
    87  		rightkey := keys[rightidx]
    88  		nonexist.Right, err = createExistenceProof(data, []byte(rightkey))
    89  		if err != nil {
    90  			return nil, err
    91  		}
    92  
    93  	}
    94  
    95  	proof := &ics23.CommitmentProof{
    96  		Proof: &ics23.CommitmentProof_Nonexist{
    97  			Nonexist: nonexist,
    98  		},
    99  	}
   100  	return proof, nil
   101  }
   102  
   103  func createExistenceProof(data map[string][]byte, key []byte) (*ics23.ExistenceProof, error) {
   104  	for k := range data {
   105  		if k == "" {
   106  			return nil, ErrEmptyKeyInData
   107  		}
   108  	}
   109  	value, ok := data[string(key)]
   110  	if !ok {
   111  		return nil, fmt.Errorf("cannot make existence proof if key is not in map")
   112  	}
   113  
   114  	_, ics23, _ := sdkmaps.ProofsFromMap(data)
   115  	proof := ics23[string(key)]
   116  	if proof == nil {
   117  		return nil, fmt.Errorf("returned no proof for key")
   118  	}
   119  
   120  	return ConvertExistenceProof(proof, key, value)
   121  }