aboutsummaryrefslogblamecommitdiffstats
path: root/les/protocol.go
blob: e1c4625bcef796e9a55d03aad4fc67ad847f6113 (plain) (tree)
1
                                         


















                                                                                  



                         




                                                
                                              

                                                          





                                                       
                

 

                                                            


                                                                                                                       
 

                                                                              
                                                         
















                                                                                              

                                 




                                 







                                              


















                                  
                     




















                                                                






                                                                                      

 






                                                                                          

 

























                                                                                              





                                                                                      











































                                                                                                               





                                                                     


                            

                                              
 
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

// Package les implements the Light Ethereum Subprotocol.
package les

import (
    "bytes"
    "crypto/ecdsa"
    "crypto/elliptic"
    "errors"
    "fmt"
    "io"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/crypto/secp256k1"
    "github.com/ethereum/go-ethereum/rlp"
)

// Constants to match up protocol versions and messages
const (
    lpv1 = 1
    lpv2 = 2
)

// Supported versions of the les protocol (first is primary)
var (
    ClientProtocolVersions    = []uint{lpv2, lpv1}
    ServerProtocolVersions    = []uint{lpv2, lpv1}
    AdvertiseProtocolVersions = []uint{lpv2} // clients are searching for the first advertised protocol in the list
)

// Number of implemented message corresponding to different protocol versions.
var ProtocolLengths = map[uint]uint64{lpv1: 15, lpv2: 22}

const (
    NetworkId          = 1
    ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
)

// les protocol message codes
const (
    // Protocol messages belonging to LPV1
    StatusMsg          = 0x00
    AnnounceMsg        = 0x01
    GetBlockHeadersMsg = 0x02
    BlockHeadersMsg    = 0x03
    GetBlockBodiesMsg  = 0x04
    BlockBodiesMsg     = 0x05
    GetReceiptsMsg     = 0x06
    ReceiptsMsg        = 0x07
    GetProofsV1Msg     = 0x08
    ProofsV1Msg        = 0x09
    GetCodeMsg         = 0x0a
    CodeMsg            = 0x0b
    SendTxMsg          = 0x0c
    GetHeaderProofsMsg = 0x0d
    HeaderProofsMsg    = 0x0e
    // Protocol messages belonging to LPV2
    GetProofsV2Msg         = 0x0f
    ProofsV2Msg            = 0x10
    GetHelperTrieProofsMsg = 0x11
    HelperTrieProofsMsg    = 0x12
    SendTxV2Msg            = 0x13
    GetTxStatusMsg         = 0x14
    TxStatusMsg            = 0x15
)

type errCode int

const (
    ErrMsgTooLarge = iota
    ErrDecode
    ErrInvalidMsgCode
    ErrProtocolVersionMismatch
    ErrNetworkIdMismatch
    ErrGenesisBlockMismatch
    ErrNoStatusMsg
    ErrExtraStatusMsg
    ErrSuspendedPeer
    ErrUselessPeer
    ErrRequestRejected
    ErrUnexpectedResponse
    ErrInvalidResponse
    ErrTooManyTimeouts
    ErrMissingKey
)

func (e errCode) String() string {
    return errorToString[int(e)]
}

// XXX change once legacy code is out
var errorToString = map[int]string{
    ErrMsgTooLarge:             "Message too long",
    ErrDecode:                  "Invalid message",
    ErrInvalidMsgCode:          "Invalid message code",
    ErrProtocolVersionMismatch: "Protocol version mismatch",
    ErrNetworkIdMismatch:       "NetworkId mismatch",
    ErrGenesisBlockMismatch:    "Genesis block mismatch",
    ErrNoStatusMsg:             "No status message",
    ErrExtraStatusMsg:          "Extra status message",
    ErrSuspendedPeer:           "Suspended peer",
    ErrRequestRejected:         "Request rejected",
    ErrUnexpectedResponse:      "Unexpected response",
    ErrInvalidResponse:         "Invalid response",
    ErrTooManyTimeouts:         "Too many request timeouts",
    ErrMissingKey:              "Key missing from list",
}

type announceBlock struct {
    Hash   common.Hash // Hash of one particular block being announced
    Number uint64      // Number of one particular block being announced
    Td     *big.Int    // Total difficulty of one particular block being announced
}

// announceData is the network packet for the block announcements.
type announceData struct {
    Hash       common.Hash // Hash of one particular block being announced
    Number     uint64      // Number of one particular block being announced
    Td         *big.Int    // Total difficulty of one particular block being announced
    ReorgDepth uint64
    Update     keyValueList
}

// sign adds a signature to the block announcement by the given privKey
func (a *announceData) sign(privKey *ecdsa.PrivateKey) {
    rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td})
    sig, _ := crypto.Sign(crypto.Keccak256(rlp), privKey)
    a.Update = a.Update.add("sign", sig)
}

// checkSignature verifies if the block announcement has a valid signature by the given pubKey
func (a *announceData) checkSignature(pubKey *ecdsa.PublicKey) error {
    var sig []byte
    if err := a.Update.decode().get("sign", &sig); err != nil {
        return err
    }
    rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td})
    recPubkey, err := secp256k1.RecoverPubkey(crypto.Keccak256(rlp), sig)
    if err != nil {
        return err
    }
    pbytes := elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y)
    if bytes.Equal(pbytes, recPubkey) {
        return nil
    } else {
        return errors.New("Wrong signature")
    }
}

type blockInfo struct {
    Hash   common.Hash // Hash of one particular block being announced
    Number uint64      // Number of one particular block being announced
    Td     *big.Int    // Total difficulty of one particular block being announced
}

// getBlockHeadersData represents a block header query.
type getBlockHeadersData struct {
    Origin  hashOrNumber // Block from which to retrieve headers
    Amount  uint64       // Maximum number of headers to retrieve
    Skip    uint64       // Blocks to skip between consecutive headers
    Reverse bool         // Query direction (false = rising towards latest, true = falling towards genesis)
}

// hashOrNumber is a combined field for specifying an origin block.
type hashOrNumber struct {
    Hash   common.Hash // Block hash from which to retrieve headers (excludes Number)
    Number uint64      // Block hash from which to retrieve headers (excludes Hash)
}

// EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the
// two contained union fields.
func (hn *hashOrNumber) EncodeRLP(w io.Writer) error {
    if hn.Hash == (common.Hash{}) {
        return rlp.Encode(w, hn.Number)
    }
    if hn.Number != 0 {
        return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number)
    }
    return rlp.Encode(w, hn.Hash)
}

// DecodeRLP is a specialized decoder for hashOrNumber to decode the contents
// into either a block hash or a block number.
func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error {
    _, size, _ := s.Kind()
    origin, err := s.Raw()
    if err == nil {
        switch {
        case size == 32:
            err = rlp.DecodeBytes(origin, &hn.Hash)
        case size <= 8:
            err = rlp.DecodeBytes(origin, &hn.Number)
        default:
            err = fmt.Errorf("invalid input size %d for origin", size)
        }
    }
    return err
}

// CodeData is the network response packet for a node data retrieval.
type CodeData []struct {
    Value []byte
}

type proofsData [][]rlp.RawValue

type txStatus struct {
    Status core.TxStatus
    Lookup *core.TxLookupEntry `rlp:"nil"`
    Error  string
}