protocol.go 10.4 KB
Newer Older
1
// Copyright 2016 The go-ethereum Authors
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// 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

import (
20 21
	"crypto/ecdsa"
	"errors"
22 23 24 25 26
	"fmt"
	"io"
	"math/big"

	"github.com/ethereum/go-ethereum/common"
27
	"github.com/ethereum/go-ethereum/core/types"
28
	"github.com/ethereum/go-ethereum/crypto"
29
	vfc "github.com/ethereum/go-ethereum/les/vflux/client"
30
	"github.com/ethereum/go-ethereum/p2p/enode"
31 32 33 34 35
	"github.com/ethereum/go-ethereum/rlp"
)

// Constants to match up protocol versions and messages
const (
36
	lpv2 = 2
37
	lpv3 = 3
38
	lpv4 = 4
39 40
)

41 42
// Supported versions of the les protocol (first is primary)
var (
43 44
	ClientProtocolVersions    = []uint{lpv2, lpv3, lpv4}
	ServerProtocolVersions    = []uint{lpv2, lpv3, lpv4}
45
	AdvertiseProtocolVersions = []uint{lpv2} // clients are searching for the first advertised protocol in the list
46
)
47 48

// Number of implemented message corresponding to different protocol versions.
49
var ProtocolLengths = map[uint]uint64{lpv2: 22, lpv3: 24, lpv4: 24}
50 51 52 53

const (
	NetworkId          = 1
	ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
54 55 56 57 58
	blockSafetyMargin  = 4                // safety margin applied to block ranges specified relative to head block

	txIndexUnlimited    = 0 // this value in the "recentTxLookup" handshake field means the entire tx index history is served
	txIndexDisabled     = 1 // this value means tx index is not served at all
	txIndexRecentOffset = 1 // txIndexRecentOffset + N in the handshake field means then tx index of the last N blocks is supported
59 60 61 62
)

// les protocol message codes
const (
63
	// Protocol messages inherited from LPV1
64 65 66 67 68 69 70 71 72 73
	StatusMsg          = 0x00
	AnnounceMsg        = 0x01
	GetBlockHeadersMsg = 0x02
	BlockHeadersMsg    = 0x03
	GetBlockBodiesMsg  = 0x04
	BlockBodiesMsg     = 0x05
	GetReceiptsMsg     = 0x06
	ReceiptsMsg        = 0x07
	GetCodeMsg         = 0x0a
	CodeMsg            = 0x0b
74
	// Protocol messages introduced in LPV2
75 76 77 78 79 80 81
	GetProofsV2Msg         = 0x0f
	ProofsV2Msg            = 0x10
	GetHelperTrieProofsMsg = 0x11
	HelperTrieProofsMsg    = 0x12
	SendTxV2Msg            = 0x13
	GetTxStatusMsg         = 0x14
	TxStatusMsg            = 0x15
82 83 84
	// Protocol messages introduced in LPV3
	StopMsg   = 0x16
	ResumeMsg = 0x17
85 86
)

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
// GetBlockHeadersData represents a block header query (the request ID is not included)
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)
}

// GetBlockHeadersPacket represents a block header request
type GetBlockHeadersPacket struct {
	ReqID uint64
	Query GetBlockHeadersData
}

// GetBlockBodiesPacket represents a block body request
type GetBlockBodiesPacket struct {
	ReqID  uint64
	Hashes []common.Hash
}

// GetCodePacket represents a contract code request
type GetCodePacket struct {
	ReqID uint64
	Reqs  []CodeReq
}

// GetReceiptsPacket represents a block receipts request
type GetReceiptsPacket struct {
	ReqID  uint64
	Hashes []common.Hash
}

// GetProofsPacket represents a proof request
type GetProofsPacket struct {
	ReqID uint64
	Reqs  []ProofReq
}

// GetHelperTrieProofsPacket represents a helper trie proof request
type GetHelperTrieProofsPacket struct {
	ReqID uint64
	Reqs  []HelperTrieReq
}

// SendTxPacket represents a transaction propagation request
type SendTxPacket struct {
	ReqID uint64
	Txs   []*types.Transaction
}

// GetTxStatusPacket represents a transaction status query
type GetTxStatusPacket struct {
	ReqID  uint64
	Hashes []common.Hash
}

143
type requestInfo struct {
144 145 146
	name                          string
	maxCount                      uint64
	refBasketFirst, refBasketRest float64
147 148
}

149
// reqMapping maps an LES request to one or two vflux service vector entries.
150 151 152 153 154 155 156 157
// If rest != -1 and the request type is used with amounts larger than one then the
// first one of the multi-request is mapped to first while the rest is mapped to rest.
type reqMapping struct {
	first, rest int
}

var (
	// requests describes the available LES request types and their initializing amounts
158
	// in the vfc.ValueTracker reference basket. Initial values are estimates
159 160 161 162 163 164 165 166 167 168 169
	// based on the same values as the server's default cost estimates (reqAvgTimeCost).
	requests = map[uint64]requestInfo{
		GetBlockHeadersMsg:     {"GetBlockHeaders", MaxHeaderFetch, 10, 1000},
		GetBlockBodiesMsg:      {"GetBlockBodies", MaxBodyFetch, 1, 0},
		GetReceiptsMsg:         {"GetReceipts", MaxReceiptFetch, 1, 0},
		GetCodeMsg:             {"GetCode", MaxCodeFetch, 1, 0},
		GetProofsV2Msg:         {"GetProofsV2", MaxProofsFetch, 10, 0},
		GetHelperTrieProofsMsg: {"GetHelperTrieProofs", MaxHelperTrieProofsFetch, 10, 100},
		SendTxV2Msg:            {"SendTxV2", MaxTxSend, 1, 0},
		GetTxStatusMsg:         {"GetTxStatus", MaxTxStatus, 10, 0},
	}
170
	requestList    []vfc.RequestInfo
171 172 173
	requestMapping map[uint32]reqMapping
)

174
// init creates a request list and mapping between protocol message codes and vflux
175 176 177 178 179 180
// service vector indices.
func init() {
	requestMapping = make(map[uint32]reqMapping)
	for code, req := range requests {
		cost := reqAvgTimeCost[code]
		rm := reqMapping{len(requestList), -1}
181
		requestList = append(requestList, vfc.RequestInfo{
182 183 184 185 186 187
			Name:       req.name + ".first",
			InitAmount: req.refBasketFirst,
			InitValue:  float64(cost.baseCost + cost.reqCost),
		})
		if req.refBasketRest != 0 {
			rm.rest = len(requestList)
188
			requestList = append(requestList, vfc.RequestInfo{
189 190 191 192 193 194 195
				Name:       req.name + ".rest",
				InitAmount: req.refBasketRest,
				InitValue:  float64(cost.reqCost),
			})
		}
		requestMapping[uint32(code)] = rm
	}
196 197
}

198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
type errCode int

const (
	ErrMsgTooLarge = iota
	ErrDecode
	ErrInvalidMsgCode
	ErrProtocolVersionMismatch
	ErrNetworkIdMismatch
	ErrGenesisBlockMismatch
	ErrNoStatusMsg
	ErrExtraStatusMsg
	ErrSuspendedPeer
	ErrUselessPeer
	ErrRequestRejected
	ErrUnexpectedResponse
	ErrInvalidResponse
	ErrTooManyTimeouts
215
	ErrMissingKey
216
	ErrForkIDRejected
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
)

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",
238
	ErrMissingKey:              "Key missing from list",
239
	ErrForkIDRejected:          "ForkID rejected",
240 241
}

242 243 244 245 246 247 248 249 250
// 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
}

251 252 253 254 255 256 257 258
// sanityCheck verifies that the values are reasonable, as a DoS protection
func (a *announceData) sanityCheck() error {
	if tdlen := a.Td.BitLen(); tdlen > 100 {
		return fmt.Errorf("too large block TD: bitlen %d", tdlen)
	}
	return nil
}

259 260
// sign adds a signature to the block announcement by the given privKey
func (a *announceData) sign(privKey *ecdsa.PrivateKey) {
261
	rlp, _ := rlp.EncodeToBytes(blockInfo{a.Hash, a.Number, a.Td})
262 263 264 265 266
	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
267
func (a *announceData) checkSignature(id enode.ID, update keyValueMap) error {
268
	var sig []byte
269
	if err := update.get("sign", &sig); err != nil {
270 271
		return err
	}
272
	rlp, _ := rlp.EncodeToBytes(blockInfo{a.Hash, a.Number, a.Td})
273
	recPubkey, err := crypto.SigToPub(crypto.Keccak256(rlp), sig)
274 275 276
	if err != nil {
		return err
	}
277
	if id == enode.PubkeyToIDV4(recPubkey) {
278 279
		return nil
	}
280
	return errors.New("wrong signature")
281 282
}

283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
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
}

// 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 {
310 311 312 313 314 315 316 317 318 319 320 321
	_, size, err := s.Kind()
	switch {
	case err != nil:
		return err
	case size == 32:
		hn.Number = 0
		return s.Decode(&hn.Hash)
	case size <= 8:
		hn.Hash = common.Hash{}
		return s.Decode(&hn.Number)
	default:
		return fmt.Errorf("invalid input size %d for origin", size)
322 323 324 325 326 327 328
	}
}

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