github.com/ebakus/go-ebakus@v1.0.5-0.20200520105415-dbccef9ec421/les/odr_requests.go (about)

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