github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/crypto/merkle/proof_op.go (about)

     1  package merkle
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  
     8  	cmtcrypto "github.com/badrootd/celestia-core/proto/tendermint/crypto"
     9  )
    10  
    11  //----------------------------------------
    12  // ProofOp gets converted to an instance of ProofOperator:
    13  
    14  // ProofOperator is a layer for calculating intermediate Merkle roots
    15  // when a series of Merkle trees are chained together.
    16  // Run() takes leaf values from a tree and returns the Merkle
    17  // root for the corresponding tree. It takes and returns a list of bytes
    18  // to allow multiple leaves to be part of a single proof, for instance in a range proof.
    19  // ProofOp() encodes the ProofOperator in a generic way so it can later be
    20  // decoded with OpDecoder.
    21  type ProofOperator interface {
    22  	Run([][]byte) ([][]byte, error)
    23  	GetKey() []byte
    24  	ProofOp() cmtcrypto.ProofOp
    25  }
    26  
    27  //----------------------------------------
    28  // Operations on a list of ProofOperators
    29  
    30  // ProofOperators is a slice of ProofOperator(s).
    31  // Each operator will be applied to the input value sequentially
    32  // and the last Merkle root will be verified with already known data
    33  type ProofOperators []ProofOperator
    34  
    35  func (poz ProofOperators) VerifyValue(root []byte, keypath string, value []byte) (err error) {
    36  	return poz.Verify(root, keypath, [][]byte{value})
    37  }
    38  
    39  func (poz ProofOperators) Verify(root []byte, keypath string, args [][]byte) (err error) {
    40  	keys, err := KeyPathToKeys(keypath)
    41  	if err != nil {
    42  		return
    43  	}
    44  
    45  	for i, op := range poz {
    46  		key := op.GetKey()
    47  		if len(key) != 0 {
    48  			if len(keys) == 0 {
    49  				return fmt.Errorf("key path has insufficient # of parts: expected no more keys but got %+v", string(key))
    50  			}
    51  			lastKey := keys[len(keys)-1]
    52  			if !bytes.Equal(lastKey, key) {
    53  				return fmt.Errorf("key mismatch on operation #%d: expected %+v but got %+v", i, string(lastKey), string(key))
    54  			}
    55  			keys = keys[:len(keys)-1]
    56  		}
    57  		args, err = op.Run(args)
    58  		if err != nil {
    59  			return
    60  		}
    61  	}
    62  	if !bytes.Equal(root, args[0]) {
    63  		return fmt.Errorf("calculated root hash is invalid: expected %X but got %X", root, args[0])
    64  	}
    65  	if len(keys) != 0 {
    66  		return errors.New("keypath not consumed all")
    67  	}
    68  	return nil
    69  }
    70  
    71  // VerifyFromKeys performs the same verification logic as the normal Verify
    72  // method, except it does not perform any processing on the keypath. This is
    73  // useful when using keys that have split or escape points as a part of the key.
    74  func (poz ProofOperators) VerifyFromKeys(root []byte, keys [][]byte, args [][]byte) (err error) {
    75  	for i, op := range poz {
    76  		key := op.GetKey()
    77  		if len(key) != 0 {
    78  			if len(keys) == 0 {
    79  				return fmt.Errorf("key path has insufficient # of parts: expected no more keys but got %+v", string(key))
    80  			}
    81  			lastKey := keys[len(keys)-1]
    82  			if !bytes.Equal(lastKey, key) {
    83  				return fmt.Errorf("key mismatch on operation #%d: expected %+v but got %+v", i, string(lastKey), string(key))
    84  			}
    85  			keys = keys[:len(keys)-1]
    86  		}
    87  		args, err = op.Run(args)
    88  		if err != nil {
    89  			return
    90  		}
    91  	}
    92  	if !bytes.Equal(root, args[0]) {
    93  		return fmt.Errorf("calculated root hash is invalid: expected %X but got %X", root, args[0])
    94  	}
    95  	if len(keys) != 0 {
    96  		return fmt.Errorf("keypath not consumed all: %s", string(bytes.Join(keys, []byte("/"))))
    97  	}
    98  	return nil
    99  }
   100  
   101  //----------------------------------------
   102  // ProofRuntime - main entrypoint
   103  
   104  type OpDecoder func(cmtcrypto.ProofOp) (ProofOperator, error)
   105  
   106  type ProofRuntime struct {
   107  	decoders map[string]OpDecoder
   108  }
   109  
   110  func NewProofRuntime() *ProofRuntime {
   111  	return &ProofRuntime{
   112  		decoders: make(map[string]OpDecoder),
   113  	}
   114  }
   115  
   116  func (prt *ProofRuntime) RegisterOpDecoder(typ string, dec OpDecoder) {
   117  	_, ok := prt.decoders[typ]
   118  	if ok {
   119  		panic("already registered for type " + typ)
   120  	}
   121  	prt.decoders[typ] = dec
   122  }
   123  
   124  func (prt *ProofRuntime) Decode(pop cmtcrypto.ProofOp) (ProofOperator, error) {
   125  	decoder := prt.decoders[pop.Type]
   126  	if decoder == nil {
   127  		return nil, fmt.Errorf("unrecognized proof type %v", pop.Type)
   128  	}
   129  	return decoder(pop)
   130  }
   131  
   132  func (prt *ProofRuntime) DecodeProof(proof *cmtcrypto.ProofOps) (ProofOperators, error) {
   133  	poz := make(ProofOperators, 0, len(proof.Ops))
   134  	for _, pop := range proof.Ops {
   135  		operator, err := prt.Decode(pop)
   136  		if err != nil {
   137  			return nil, fmt.Errorf("decoding a proof operator: %w", err)
   138  		}
   139  		poz = append(poz, operator)
   140  	}
   141  	return poz, nil
   142  }
   143  
   144  func (prt *ProofRuntime) VerifyValue(proof *cmtcrypto.ProofOps, root []byte, keypath string, value []byte) (err error) {
   145  	return prt.Verify(proof, root, keypath, [][]byte{value})
   146  }
   147  
   148  func (prt *ProofRuntime) VerifyValueFromKeys(proof *cmtcrypto.ProofOps, root []byte, keys [][]byte, value []byte) (err error) {
   149  	return prt.VerifyFromKeys(proof, root, keys, [][]byte{value})
   150  }
   151  
   152  // TODO In the long run we'll need a method of classification of ops,
   153  // whether existence or absence or perhaps a third?
   154  func (prt *ProofRuntime) VerifyAbsence(proof *cmtcrypto.ProofOps, root []byte, keypath string) (err error) {
   155  	return prt.Verify(proof, root, keypath, nil)
   156  }
   157  
   158  func (prt *ProofRuntime) Verify(proof *cmtcrypto.ProofOps, root []byte, keypath string, args [][]byte) (err error) {
   159  	poz, err := prt.DecodeProof(proof)
   160  	if err != nil {
   161  		return fmt.Errorf("decoding proof: %w", err)
   162  	}
   163  	return poz.Verify(root, keypath, args)
   164  }
   165  
   166  // VerifyFromKeys performs the same verification logic as the normal Verify
   167  // method, except it does not perform any processing on the keypath. This is
   168  // useful when using keys that have split or escape points as a part of the key.
   169  func (prt *ProofRuntime) VerifyFromKeys(proof *cmtcrypto.ProofOps, root []byte, keys [][]byte, args [][]byte) (err error) {
   170  	poz, err := prt.DecodeProof(proof)
   171  	if err != nil {
   172  		return fmt.Errorf("decoding proof: %w", err)
   173  	}
   174  	return poz.VerifyFromKeys(root, keys, args)
   175  }
   176  
   177  // DefaultProofRuntime only knows about value proofs.
   178  // To use e.g. IAVL proofs, register op-decoders as
   179  // defined in the IAVL package.
   180  func DefaultProofRuntime() (prt *ProofRuntime) {
   181  	prt = NewProofRuntime()
   182  	prt.RegisterOpDecoder(ProofOpValue, ValueOpDecoder)
   183  	return
   184  }