github.com/core-coin/go-core/v2@v2.1.9/les/odr_requests.go (about)

     1  // Copyright 2016 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-core library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package les
    18  
    19  import (
    20  	"encoding/binary"
    21  	"errors"
    22  	"fmt"
    23  
    24  	"github.com/core-coin/go-core/v2/xcbdb"
    25  
    26  	"github.com/core-coin/go-core/v2/common"
    27  	"github.com/core-coin/go-core/v2/core/rawdb"
    28  	"github.com/core-coin/go-core/v2/core/types"
    29  	"github.com/core-coin/go-core/v2/crypto"
    30  	"github.com/core-coin/go-core/v2/light"
    31  	"github.com/core-coin/go-core/v2/log"
    32  	"github.com/core-coin/go-core/v2/rlp"
    33  	"github.com/core-coin/go-core/v2/trie"
    34  )
    35  
    36  var (
    37  	errInvalidMessageType  = errors.New("invalid message type")
    38  	errInvalidEntryCount   = errors.New("invalid number of response entries")
    39  	errHeaderUnavailable   = errors.New("header unavailable")
    40  	errTxHashMismatch      = errors.New("transaction hash mismatch")
    41  	errUncleHashMismatch   = errors.New("uncle hash mismatch")
    42  	errReceiptHashMismatch = errors.New("receipt hash mismatch")
    43  	errDataHashMismatch    = errors.New("data hash mismatch")
    44  	errCHTHashMismatch     = errors.New("cht hash mismatch")
    45  	errCHTNumberMismatch   = errors.New("cht number mismatch")
    46  	errUselessNodes        = errors.New("useless nodes in merkle proof nodeset")
    47  )
    48  
    49  type LesOdrRequest interface {
    50  	GetCost(*serverPeer) uint64
    51  	CanSend(*serverPeer) bool
    52  	Request(uint64, *serverPeer) error
    53  	Validate(xcbdb.Database, *Msg) error
    54  }
    55  
    56  func LesRequest(req light.OdrRequest) LesOdrRequest {
    57  	switch r := req.(type) {
    58  	case *light.BlockRequest:
    59  		return (*BlockRequest)(r)
    60  	case *light.ReceiptsRequest:
    61  		return (*ReceiptsRequest)(r)
    62  	case *light.TrieRequest:
    63  		return (*TrieRequest)(r)
    64  	case *light.CodeRequest:
    65  		return (*CodeRequest)(r)
    66  	case *light.ChtRequest:
    67  		return (*ChtRequest)(r)
    68  	case *light.BloomRequest:
    69  		return (*BloomRequest)(r)
    70  	case *light.TxStatusRequest:
    71  		return (*TxStatusRequest)(r)
    72  	default:
    73  		return nil
    74  	}
    75  }
    76  
    77  // BlockRequest is the ODR request type for block bodies
    78  type BlockRequest light.BlockRequest
    79  
    80  // GetCost returns the cost of the given ODR request according to the serving
    81  // peer's cost table (implementation of LesOdrRequest)
    82  func (r *BlockRequest) GetCost(peer *serverPeer) uint64 {
    83  	return peer.getRequestCost(GetBlockBodiesMsg, 1)
    84  }
    85  
    86  // CanSend tells if a certain peer is suitable for serving the given request
    87  func (r *BlockRequest) CanSend(peer *serverPeer) bool {
    88  	return peer.HasBlock(r.Hash, r.Number, false)
    89  }
    90  
    91  // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
    92  func (r *BlockRequest) Request(reqID uint64, peer *serverPeer) error {
    93  	peer.Log().Debug("Requesting block body", "hash", r.Hash)
    94  	return peer.requestBodies(reqID, []common.Hash{r.Hash})
    95  }
    96  
    97  // Valid processes an ODR request reply message from the LES network
    98  // returns true and stores results in memory if the message was a valid reply
    99  // to the request (implementation of LesOdrRequest)
   100  func (r *BlockRequest) Validate(db xcbdb.Database, msg *Msg) error {
   101  	log.Debug("Validating block body", "hash", r.Hash)
   102  
   103  	// Ensure we have a correct message with a single block body
   104  	if msg.MsgType != MsgBlockBodies {
   105  		return errInvalidMessageType
   106  	}
   107  	bodies := msg.Obj.([]*types.Body)
   108  	if len(bodies) != 1 {
   109  		return errInvalidEntryCount
   110  	}
   111  	body := bodies[0]
   112  
   113  	// Retrieve our stored header and validate block content against it
   114  	if r.Header == nil {
   115  		r.Header = rawdb.ReadHeader(db, r.Hash, r.Number)
   116  	}
   117  	if r.Header == nil {
   118  		return errHeaderUnavailable
   119  	}
   120  	if r.Header.TxHash != types.DeriveSha(types.Transactions(body.Transactions), new(trie.Trie)) {
   121  		return errTxHashMismatch
   122  	}
   123  	if r.Header.UncleHash != types.CalcUncleHash(body.Uncles) {
   124  		return errUncleHashMismatch
   125  	}
   126  	// Validations passed, encode and store RLP
   127  	data, err := rlp.EncodeToBytes(body)
   128  	if err != nil {
   129  		return err
   130  	}
   131  	r.Rlp = data
   132  	return nil
   133  }
   134  
   135  // ReceiptsRequest is the ODR request type for block receipts by block hash
   136  type ReceiptsRequest light.ReceiptsRequest
   137  
   138  // GetCost returns the cost of the given ODR request according to the serving
   139  // peer's cost table (implementation of LesOdrRequest)
   140  func (r *ReceiptsRequest) GetCost(peer *serverPeer) uint64 {
   141  	return peer.getRequestCost(GetReceiptsMsg, 1)
   142  }
   143  
   144  // CanSend tells if a certain peer is suitable for serving the given request
   145  func (r *ReceiptsRequest) CanSend(peer *serverPeer) bool {
   146  	return peer.HasBlock(r.Hash, r.Number, false)
   147  }
   148  
   149  // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
   150  func (r *ReceiptsRequest) Request(reqID uint64, peer *serverPeer) error {
   151  	peer.Log().Debug("Requesting block receipts", "hash", r.Hash)
   152  	return peer.requestReceipts(reqID, []common.Hash{r.Hash})
   153  }
   154  
   155  // Valid processes an ODR request reply message from the LES network
   156  // returns true and stores results in memory if the message was a valid reply
   157  // to the request (implementation of LesOdrRequest)
   158  func (r *ReceiptsRequest) Validate(db xcbdb.Database, msg *Msg) error {
   159  	log.Debug("Validating block receipts", "hash", r.Hash)
   160  
   161  	// Ensure we have a correct message with a single block receipt
   162  	if msg.MsgType != MsgReceipts {
   163  		return errInvalidMessageType
   164  	}
   165  	receipts := msg.Obj.([]types.Receipts)
   166  	if len(receipts) != 1 {
   167  		return errInvalidEntryCount
   168  	}
   169  	receipt := receipts[0]
   170  
   171  	// Retrieve our stored header and validate receipt content against it
   172  	if r.Header == nil {
   173  		r.Header = rawdb.ReadHeader(db, r.Hash, r.Number)
   174  	}
   175  	if r.Header == nil {
   176  		return errHeaderUnavailable
   177  	}
   178  	if r.Header.ReceiptHash != types.DeriveSha(receipt, new(trie.Trie)) {
   179  		return errReceiptHashMismatch
   180  	}
   181  	// Validations passed, store and return
   182  	r.Receipts = receipt
   183  	return nil
   184  }
   185  
   186  type ProofReq struct {
   187  	BHash       common.Hash
   188  	AccKey, Key []byte
   189  	FromLevel   uint
   190  }
   191  
   192  // ODR request type for state/storage trie entries, see LesOdrRequest interface
   193  type TrieRequest light.TrieRequest
   194  
   195  // GetCost returns the cost of the given ODR request according to the serving
   196  // peer's cost table (implementation of LesOdrRequest)
   197  func (r *TrieRequest) GetCost(peer *serverPeer) uint64 {
   198  	return peer.getRequestCost(GetProofsV2Msg, 1)
   199  }
   200  
   201  // CanSend tells if a certain peer is suitable for serving the given request
   202  func (r *TrieRequest) CanSend(peer *serverPeer) bool {
   203  	return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber, true)
   204  }
   205  
   206  // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
   207  func (r *TrieRequest) Request(reqID uint64, peer *serverPeer) error {
   208  	peer.Log().Debug("Requesting trie proof", "root", r.Id.Root, "key", r.Key)
   209  	req := ProofReq{
   210  		BHash:  r.Id.BlockHash,
   211  		AccKey: r.Id.AccKey,
   212  		Key:    r.Key,
   213  	}
   214  	return peer.requestProofs(reqID, []ProofReq{req})
   215  }
   216  
   217  // Valid processes an ODR request reply message from the LES network
   218  // returns true and stores results in memory if the message was a valid reply
   219  // to the request (implementation of LesOdrRequest)
   220  func (r *TrieRequest) Validate(db xcbdb.Database, msg *Msg) error {
   221  	log.Debug("Validating trie proof", "root", r.Id.Root, "key", r.Key)
   222  
   223  	if msg.MsgType != MsgProofsV2 {
   224  		return errInvalidMessageType
   225  	}
   226  	proofs := msg.Obj.(light.NodeList)
   227  	// Verify the proof and store if checks out
   228  	nodeSet := proofs.NodeSet()
   229  	reads := &readTraceDB{db: nodeSet}
   230  	if _, err := trie.VerifyProof(r.Id.Root, r.Key, reads); err != nil {
   231  		return fmt.Errorf("merkle proof verification failed: %v", err)
   232  	}
   233  	// check if all nodes have been read by VerifyProof
   234  	if len(reads.reads) != nodeSet.KeyCount() {
   235  		return errUselessNodes
   236  	}
   237  	r.Proof = nodeSet
   238  	return nil
   239  }
   240  
   241  type CodeReq struct {
   242  	BHash  common.Hash
   243  	AccKey []byte
   244  }
   245  
   246  // ODR request type for node data (used for retrieving contract code), see LesOdrRequest interface
   247  type CodeRequest light.CodeRequest
   248  
   249  // GetCost returns the cost of the given ODR request according to the serving
   250  // peer's cost table (implementation of LesOdrRequest)
   251  func (r *CodeRequest) GetCost(peer *serverPeer) uint64 {
   252  	return peer.getRequestCost(GetCodeMsg, 1)
   253  }
   254  
   255  // CanSend tells if a certain peer is suitable for serving the given request
   256  func (r *CodeRequest) CanSend(peer *serverPeer) bool {
   257  	return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber, true)
   258  }
   259  
   260  // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
   261  func (r *CodeRequest) Request(reqID uint64, peer *serverPeer) error {
   262  	peer.Log().Debug("Requesting code data", "hash", r.Hash)
   263  	req := CodeReq{
   264  		BHash:  r.Id.BlockHash,
   265  		AccKey: r.Id.AccKey,
   266  	}
   267  	return peer.requestCode(reqID, []CodeReq{req})
   268  }
   269  
   270  // Valid processes an ODR request reply message from the LES network
   271  // returns true and stores results in memory if the message was a valid reply
   272  // to the request (implementation of LesOdrRequest)
   273  func (r *CodeRequest) Validate(db xcbdb.Database, msg *Msg) error {
   274  	log.Debug("Validating code data", "hash", r.Hash)
   275  
   276  	// Ensure we have a correct message with a single code element
   277  	if msg.MsgType != MsgCode {
   278  		return errInvalidMessageType
   279  	}
   280  	reply := msg.Obj.([][]byte)
   281  	if len(reply) != 1 {
   282  		return errInvalidEntryCount
   283  	}
   284  	data := reply[0]
   285  
   286  	// Verify the data and store if checks out
   287  	if hash := crypto.SHA3Hash(data); r.Hash != hash {
   288  		return errDataHashMismatch
   289  	}
   290  	r.Data = data
   291  	return nil
   292  }
   293  
   294  const (
   295  	// helper trie type constants
   296  	htCanonical = iota // Canonical hash trie
   297  	htBloomBits        // BloomBits trie
   298  
   299  	// applicable for all helper trie requests
   300  	auxRoot = 1
   301  	// applicable for htCanonical
   302  	auxHeader = 2
   303  )
   304  
   305  type HelperTrieReq struct {
   306  	Type              uint
   307  	TrieIdx           uint64
   308  	Key               []byte
   309  	FromLevel, AuxReq uint
   310  }
   311  
   312  type HelperTrieResps struct { // describes all responses, not just a single one
   313  	Proofs  light.NodeList
   314  	AuxData [][]byte
   315  }
   316  
   317  // ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
   318  type ChtRequest light.ChtRequest
   319  
   320  // GetCost returns the cost of the given ODR request according to the serving
   321  // peer's cost table (implementation of LesOdrRequest)
   322  func (r *ChtRequest) GetCost(peer *serverPeer) uint64 {
   323  	return peer.getRequestCost(GetHelperTrieProofsMsg, 1)
   324  }
   325  
   326  // CanSend tells if a certain peer is suitable for serving the given request
   327  func (r *ChtRequest) CanSend(peer *serverPeer) bool {
   328  	peer.lock.RLock()
   329  	defer peer.lock.RUnlock()
   330  
   331  	return peer.headInfo.Number >= r.Config.ChtConfirms && r.ChtNum <= (peer.headInfo.Number-r.Config.ChtConfirms)/r.Config.ChtSize
   332  }
   333  
   334  // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
   335  func (r *ChtRequest) Request(reqID uint64, peer *serverPeer) error {
   336  	peer.Log().Debug("Requesting CHT", "cht", r.ChtNum, "block", r.BlockNum)
   337  	var encNum [8]byte
   338  	binary.BigEndian.PutUint64(encNum[:], r.BlockNum)
   339  	req := HelperTrieReq{
   340  		Type:    htCanonical,
   341  		TrieIdx: r.ChtNum,
   342  		Key:     encNum[:],
   343  		AuxReq:  auxHeader,
   344  	}
   345  	return peer.requestHelperTrieProofs(reqID, []HelperTrieReq{req})
   346  }
   347  
   348  // Valid processes an ODR request reply message from the LES network
   349  // returns true and stores results in memory if the message was a valid reply
   350  // to the request (implementation of LesOdrRequest)
   351  func (r *ChtRequest) Validate(db xcbdb.Database, msg *Msg) error {
   352  	log.Debug("Validating CHT", "cht", r.ChtNum, "block", r.BlockNum)
   353  
   354  	if msg.MsgType != MsgHelperTrieProofs {
   355  		return errInvalidMessageType
   356  	}
   357  	resp := msg.Obj.(HelperTrieResps)
   358  	if len(resp.AuxData) != 1 {
   359  		return errInvalidEntryCount
   360  	}
   361  	nodeSet := resp.Proofs.NodeSet()
   362  	headerEnc := resp.AuxData[0]
   363  	if len(headerEnc) == 0 {
   364  		return errHeaderUnavailable
   365  	}
   366  	header := new(types.Header)
   367  	if err := rlp.DecodeBytes(headerEnc, header); err != nil {
   368  		return errHeaderUnavailable
   369  	}
   370  	// Verify the CHT
   371  	var (
   372  		node      light.ChtNode
   373  		encNumber [8]byte
   374  	)
   375  	binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
   376  
   377  	reads := &readTraceDB{db: nodeSet}
   378  	value, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads)
   379  	if err != nil {
   380  		return fmt.Errorf("merkle proof verification failed: %v", err)
   381  	}
   382  	if len(reads.reads) != nodeSet.KeyCount() {
   383  		return errUselessNodes
   384  	}
   385  	if err := rlp.DecodeBytes(value, &node); err != nil {
   386  		return err
   387  	}
   388  	if node.Hash != header.Hash() {
   389  		return errCHTHashMismatch
   390  	}
   391  	if r.BlockNum != header.Number.Uint64() {
   392  		return errCHTNumberMismatch
   393  	}
   394  	// Verifications passed, store and return
   395  	r.Header = header
   396  	r.Proof = nodeSet
   397  	r.Td = node.Td
   398  	return nil
   399  }
   400  
   401  type BloomReq struct {
   402  	BloomTrieNum, BitIdx, SectionIndex, FromLevel uint64
   403  }
   404  
   405  // ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
   406  type BloomRequest light.BloomRequest
   407  
   408  // GetCost returns the cost of the given ODR request according to the serving
   409  // peer's cost table (implementation of LesOdrRequest)
   410  func (r *BloomRequest) GetCost(peer *serverPeer) uint64 {
   411  	return peer.getRequestCost(GetHelperTrieProofsMsg, len(r.SectionIndexList))
   412  }
   413  
   414  // CanSend tells if a certain peer is suitable for serving the given request
   415  func (r *BloomRequest) CanSend(peer *serverPeer) bool {
   416  	peer.lock.RLock()
   417  	defer peer.lock.RUnlock()
   418  
   419  	if peer.version < lpv2 {
   420  		return false
   421  	}
   422  	return peer.headInfo.Number >= r.Config.BloomTrieConfirms && r.BloomTrieNum <= (peer.headInfo.Number-r.Config.BloomTrieConfirms)/r.Config.BloomTrieSize
   423  }
   424  
   425  // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
   426  func (r *BloomRequest) Request(reqID uint64, peer *serverPeer) error {
   427  	peer.Log().Debug("Requesting BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIndexList)
   428  	reqs := make([]HelperTrieReq, len(r.SectionIndexList))
   429  
   430  	var encNumber [10]byte
   431  	binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx))
   432  
   433  	for i, sectionIdx := range r.SectionIndexList {
   434  		binary.BigEndian.PutUint64(encNumber[2:], sectionIdx)
   435  		reqs[i] = HelperTrieReq{
   436  			Type:    htBloomBits,
   437  			TrieIdx: r.BloomTrieNum,
   438  			Key:     common.CopyBytes(encNumber[:]),
   439  		}
   440  	}
   441  	return peer.requestHelperTrieProofs(reqID, reqs)
   442  }
   443  
   444  // Valid processes an ODR request reply message from the LES network
   445  // returns true and stores results in memory if the message was a valid reply
   446  // to the request (implementation of LesOdrRequest)
   447  func (r *BloomRequest) Validate(db xcbdb.Database, msg *Msg) error {
   448  	log.Debug("Validating BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIndexList)
   449  
   450  	// Ensure we have a correct message with a single proof element
   451  	if msg.MsgType != MsgHelperTrieProofs {
   452  		return errInvalidMessageType
   453  	}
   454  	resps := msg.Obj.(HelperTrieResps)
   455  	proofs := resps.Proofs
   456  	nodeSet := proofs.NodeSet()
   457  	reads := &readTraceDB{db: nodeSet}
   458  
   459  	r.BloomBits = make([][]byte, len(r.SectionIndexList))
   460  
   461  	// Verify the proofs
   462  	var encNumber [10]byte
   463  	binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx))
   464  
   465  	for i, idx := range r.SectionIndexList {
   466  		binary.BigEndian.PutUint64(encNumber[2:], idx)
   467  		value, err := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads)
   468  		if err != nil {
   469  			return err
   470  		}
   471  		r.BloomBits[i] = value
   472  	}
   473  
   474  	if len(reads.reads) != nodeSet.KeyCount() {
   475  		return errUselessNodes
   476  	}
   477  	r.Proofs = nodeSet
   478  	return nil
   479  }
   480  
   481  // TxStatusRequest is the ODR request type for transaction status
   482  type TxStatusRequest light.TxStatusRequest
   483  
   484  // GetCost returns the cost of the given ODR request according to the serving
   485  // peer's cost table (implementation of LesOdrRequest)
   486  func (r *TxStatusRequest) GetCost(peer *serverPeer) uint64 {
   487  	return peer.getRequestCost(GetTxStatusMsg, len(r.Hashes))
   488  }
   489  
   490  // CanSend tells if a certain peer is suitable for serving the given request
   491  func (r *TxStatusRequest) CanSend(peer *serverPeer) bool {
   492  	return peer.version >= lpv2
   493  }
   494  
   495  // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
   496  func (r *TxStatusRequest) Request(reqID uint64, peer *serverPeer) error {
   497  	peer.Log().Debug("Requesting transaction status", "count", len(r.Hashes))
   498  	return peer.requestTxStatus(reqID, r.Hashes)
   499  }
   500  
   501  // Valid processes an ODR request reply message from the LES network
   502  // returns true and stores results in memory if the message was a valid reply
   503  // to the request (implementation of LesOdrRequest)
   504  func (r *TxStatusRequest) Validate(db xcbdb.Database, msg *Msg) error {
   505  	log.Debug("Validating transaction status", "count", len(r.Hashes))
   506  
   507  	// Ensure we have a correct message with a single block body
   508  	if msg.MsgType != MsgTxStatus {
   509  		return errInvalidMessageType
   510  	}
   511  	status := msg.Obj.([]light.TxStatus)
   512  	if len(status) != len(r.Hashes) {
   513  		return errInvalidEntryCount
   514  	}
   515  	r.Status = status
   516  	return nil
   517  }
   518  
   519  // readTraceDB stores the keys of database reads. We use this to check that received node
   520  // sets contain only the trie nodes necessary to make proofs pass.
   521  type readTraceDB struct {
   522  	db    xcbdb.KeyValueReader
   523  	reads map[string]struct{}
   524  }
   525  
   526  // Get returns a stored node
   527  func (db *readTraceDB) Get(k []byte) ([]byte, error) {
   528  	if db.reads == nil {
   529  		db.reads = make(map[string]struct{})
   530  	}
   531  	db.reads[string(k)] = struct{}{}
   532  	return db.db.Get(k)
   533  }
   534  
   535  // Has returns true if the node set contains the given key
   536  func (db *readTraceDB) Has(key []byte) (bool, error) {
   537  	_, err := db.Get(key)
   538  	return err == nil, nil
   539  }