Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Wei Tang 2018-06-16 13:24:44 +08:00
commit 3872a67cdd
59 changed files with 698 additions and 613 deletions

View file

@ -1 +1 @@
1.8.11 1.8.12

View file

@ -50,7 +50,7 @@ var (
var KeyStoreType = reflect.TypeOf(&KeyStore{}) var KeyStoreType = reflect.TypeOf(&KeyStore{})
// KeyStoreScheme is the protocol scheme prefixing account and wallet URLs. // KeyStoreScheme is the protocol scheme prefixing account and wallet URLs.
var KeyStoreScheme = "keystore" const KeyStoreScheme = "keystore"
// Maximum time between wallet refreshes (if filesystem notifications don't work). // Maximum time between wallet refreshes (if filesystem notifications don't work).
const walletRefreshCycle = 3 * time.Second const walletRefreshCycle = 3 * time.Second

View file

@ -18,8 +18,8 @@ environment:
install: install:
- git submodule update --init - git submodule update --init
- rmdir C:\go /s /q - rmdir C:\go /s /q
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.10.2.windows-%GETH_ARCH%.zip - appveyor DownloadFile https://storage.googleapis.com/golang/go1.10.3.windows-%GETH_ARCH%.zip
- 7z x go1.10.2.windows-%GETH_ARCH%.zip -y -oC:\ > NUL - 7z x go1.10.3.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
- go version - go version
- gcc --version - gcc --version

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// simple nonconcurrent reference implementation for hashsize segment based // Package bmt is a simple nonconcurrent reference implementation for hashsize segment based
// Binary Merkle tree hash on arbitrary but fixed maximum chunksize // Binary Merkle tree hash on arbitrary but fixed maximum chunksize
// //
// This implementation does not take advantage of any paralellisms and uses // This implementation does not take advantage of any paralellisms and uses

View file

@ -330,6 +330,7 @@ func doLint(cmdline []string) {
configs := []string{ configs := []string{
"--vendor", "--vendor",
"--tests", "--tests",
"--deadline=2m",
"--disable-all", "--disable-all",
"--enable=goimports", "--enable=goimports",
"--enable=varcheck", "--enable=varcheck",

View file

@ -77,9 +77,6 @@ var (
accJSONFlag = flag.String("account.json", "", "Key json file to fund user requests with") accJSONFlag = flag.String("account.json", "", "Key json file to fund user requests with")
accPassFlag = flag.String("account.pass", "", "Decryption password to access faucet funds") accPassFlag = flag.String("account.pass", "", "Decryption password to access faucet funds")
githubUser = flag.String("github.user", "", "GitHub user to authenticate with for Gist access")
githubToken = flag.String("github.token", "", "GitHub personal token to access Gists with")
captchaToken = flag.String("captcha.token", "", "Recaptcha site key to authenticate client side") captchaToken = flag.String("captcha.token", "", "Recaptcha site key to authenticate client side")
captchaSecret = flag.String("captcha.secret", "", "Recaptcha secret key to authenticate server side") captchaSecret = flag.String("captcha.secret", "", "Recaptcha secret key to authenticate server side")
@ -638,59 +635,6 @@ func sendSuccess(conn *websocket.Conn, msg string) error {
return send(conn, map[string]string{"success": msg}, time.Second) return send(conn, map[string]string{"success": msg}, time.Second)
} }
// authGitHub tries to authenticate a faucet request using GitHub gists, returning
// the username, avatar URL and Ethereum address to fund on success.
func authGitHub(url string) (string, string, common.Address, error) {
// Retrieve the gist from the GitHub Gist APIs
parts := strings.Split(url, "/")
req, _ := http.NewRequest("GET", "https://api.github.com/gists/"+parts[len(parts)-1], nil)
if *githubUser != "" {
req.SetBasicAuth(*githubUser, *githubToken)
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return "", "", common.Address{}, err
}
var gist struct {
Owner struct {
Login string `json:"login"`
} `json:"owner"`
Files map[string]struct {
Content string `json:"content"`
} `json:"files"`
}
err = json.NewDecoder(res.Body).Decode(&gist)
res.Body.Close()
if err != nil {
return "", "", common.Address{}, err
}
if gist.Owner.Login == "" {
return "", "", common.Address{}, errors.New("Anonymous Gists not allowed")
}
// Iterate over all the files and look for Ethereum addresses
var address common.Address
for _, file := range gist.Files {
content := strings.TrimSpace(file.Content)
if len(content) == 2+common.AddressLength*2 {
address = common.HexToAddress(content)
}
}
if address == (common.Address{}) {
return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
}
// Validate the user's existence since the API is unhelpful here
if res, err = http.Head("https://github.com/" + gist.Owner.Login); err != nil {
return "", "", common.Address{}, err
}
res.Body.Close()
if res.StatusCode != 200 {
return "", "", common.Address{}, errors.New("Invalid user... boom!")
}
// Everything passed validation, return the gathered infos
return gist.Owner.Login + "@github", fmt.Sprintf("https://github.com/%s.png?size=64", gist.Owner.Login), address, nil
}
// authTwitter tries to authenticate a faucet request using Twitter posts, returning // authTwitter tries to authenticate a faucet request using Twitter posts, returning
// the username, avatar URL and Ethereum address to fund on success. // the username, avatar URL and Ethereum address to fund on success.
func authTwitter(url string) (string, string, common.Address, error) { func authTwitter(url string) (string, string, common.Address, error) {

View file

@ -276,13 +276,3 @@ func (stats serverStats) render() {
} }
table.Render() table.Render()
} }
// protips contains a collection of network infos to report pro-tips
// based on.
type protips struct {
genesis string
network int64
bootFull []string
bootLight []string
ethstats string
}

View file

@ -1139,6 +1139,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
} }
cfg.Genesis = core.DefaultRinkebyGenesisBlock() cfg.Genesis = core.DefaultRinkebyGenesisBlock()
case ctx.GlobalBool(DeveloperFlag.Name): case ctx.GlobalBool(DeveloperFlag.Name):
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 1337
}
// Create new developer account or reuse existing one // Create new developer account or reuse existing one
var ( var (
developer accounts.Account developer accounts.Account

View file

@ -39,6 +39,7 @@ import (
const uintBits = 32 << (uint64(^uint(0)) >> 63) const uintBits = 32 << (uint64(^uint(0)) >> 63)
// Errors
var ( var (
ErrEmptyString = &decError{"empty hex string"} ErrEmptyString = &decError{"empty hex string"}
ErrSyntax = &decError{"invalid hex string"} ErrSyntax = &decError{"invalid hex string"}

View file

@ -22,12 +22,13 @@ import (
"math/big" "math/big"
) )
// Various big integer limit values.
var ( var (
tt255 = BigPow(2, 255) tt255 = BigPow(2, 255)
tt256 = BigPow(2, 256) tt256 = BigPow(2, 256)
tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1)) tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1))
MaxBig256 = new(big.Int).Set(tt256m1)
tt63 = BigPow(2, 63) tt63 = BigPow(2, 63)
MaxBig256 = new(big.Int).Set(tt256m1)
MaxBig63 = new(big.Int).Sub(tt63, big.NewInt(1)) MaxBig63 = new(big.Int).Sub(tt63, big.NewInt(1))
) )

View file

@ -21,8 +21,8 @@ import (
"strconv" "strconv"
) )
// Integer limit values.
const ( const (
// Integer limit values.
MaxInt8 = 1<<7 - 1 MaxInt8 = 1<<7 - 1
MinInt8 = -1 << 7 MinInt8 = -1 << 7
MaxInt16 = 1<<15 - 1 MaxInt16 = 1<<15 - 1

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // 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/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// package mclock is a wrapper for a monotonic clock source // Package mclock is a wrapper for a monotonic clock source
package mclock package mclock
import ( import (
@ -23,8 +23,10 @@ import (
"github.com/aristanetworks/goarista/monotime" "github.com/aristanetworks/goarista/monotime"
) )
type AbsTime time.Duration // absolute monotonic time // AbsTime represents absolute monotonic time.
type AbsTime time.Duration
// Now returns the current absolute monotonic time.
func Now() AbsTime { func Now() AbsTime {
return AbsTime(monotime.Now()) return AbsTime(monotime.Now())
} }

View file

@ -1,196 +0,0 @@
// Copyright 2015 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 number
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
var tt256 = new(big.Int).Lsh(big.NewInt(1), 256)
var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
var tt255 = new(big.Int).Lsh(big.NewInt(1), 255)
func limitUnsigned256(x *Number) *Number {
x.num.And(x.num, tt256m1)
return x
}
func limitSigned256(x *Number) *Number {
if x.num.Cmp(tt255) < 0 {
return x
}
x.num.Sub(x.num, tt256)
return x
}
// Initialiser is a Number function
type Initialiser func(n int64) *Number
// A Number represents a generic integer with a bounding function limiter. Limit is called after each operations
// to give "fake" bounded integers. New types of Number can be created through NewInitialiser returning a lambda
// with the new Initialiser.
type Number struct {
num *big.Int
limit func(n *Number) *Number
}
// NewInitialiser returns a new initialiser for a new *Number without having to expose certain fields
func NewInitialiser(limiter func(*Number) *Number) Initialiser {
return func(n int64) *Number {
return &Number{big.NewInt(n), limiter}
}
}
// Uint256 returns a Number with a UNSIGNED limiter up to 256 bits
func Uint256(n int64) *Number {
return &Number{big.NewInt(n), limitUnsigned256}
}
// Int256 returns Number with a SIGNED limiter up to 256 bits
func Int256(n int64) *Number {
return &Number{big.NewInt(n), limitSigned256}
}
// Big returns a Number with a SIGNED unlimited size
func Big(n int64) *Number {
return &Number{big.NewInt(n), func(x *Number) *Number { return x }}
}
// Add sets i to sum of x+y
func (i *Number) Add(x, y *Number) *Number {
i.num.Add(x.num, y.num)
return i.limit(i)
}
// Sub sets i to difference of x-y
func (i *Number) Sub(x, y *Number) *Number {
i.num.Sub(x.num, y.num)
return i.limit(i)
}
// Mul sets i to product of x*y
func (i *Number) Mul(x, y *Number) *Number {
i.num.Mul(x.num, y.num)
return i.limit(i)
}
// Div sets i to the quotient prodject of x/y
func (i *Number) Div(x, y *Number) *Number {
i.num.Div(x.num, y.num)
return i.limit(i)
}
// Mod sets i to x % y
func (i *Number) Mod(x, y *Number) *Number {
i.num.Mod(x.num, y.num)
return i.limit(i)
}
// Lsh sets i to x << s
func (i *Number) Lsh(x *Number, s uint) *Number {
i.num.Lsh(x.num, s)
return i.limit(i)
}
// Pow sets i to x^y
func (i *Number) Pow(x, y *Number) *Number {
i.num.Exp(x.num, y.num, big.NewInt(0))
return i.limit(i)
}
// Setters
// Set sets x to i
func (i *Number) Set(x *Number) *Number {
i.num.Set(x.num)
return i.limit(i)
}
// SetBytes sets x bytes to i
func (i *Number) SetBytes(x []byte) *Number {
i.num.SetBytes(x)
return i.limit(i)
}
// Cmp compares x and y and returns:
//
// -1 if x < y
// 0 if x == y
// +1 if x > y
func (i *Number) Cmp(x *Number) int {
return i.num.Cmp(x.num)
}
// Getters
// String returns the string representation of i
func (i *Number) String() string {
return i.num.String()
}
// Bytes returns the byte representation of i
func (i *Number) Bytes() []byte {
return i.num.Bytes()
}
// Uint64 returns the Uint64 representation of x. If x cannot be represented in an int64, the result is undefined.
func (i *Number) Uint64() uint64 {
return i.num.Uint64()
}
// Int64 returns the int64 representation of x. If x cannot be represented in an int64, the result is undefined.
func (i *Number) Int64() int64 {
return i.num.Int64()
}
// Int256 returns the signed version of i
func (i *Number) Int256() *Number {
return Int(0).Set(i)
}
// Uint256 returns the unsigned version of i
func (i *Number) Uint256() *Number {
return Uint(0).Set(i)
}
// FirstBitSet returns the index of the first bit that's set to 1
func (i *Number) FirstBitSet() int {
for j := 0; j < i.num.BitLen(); j++ {
if i.num.Bit(j) > 0 {
return j
}
}
return i.num.BitLen()
}
// Variables
var (
Zero = Uint(0)
One = Uint(1)
Two = Uint(2)
MaxUint256 = Uint(0).SetBytes(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
MinOne = Int(-1)
// "typedefs"
Uint = Uint256
Int = Int256
)

View file

@ -1,108 +0,0 @@
// Copyright 2015 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 number
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
)
func TestSet(t *testing.T) {
a := Uint(0)
b := Uint(10)
a.Set(b)
if a.num.Cmp(b.num) != 0 {
t.Error("didn't compare", a, b)
}
c := Uint(0).SetBytes(common.Hex2Bytes("0a"))
if c.num.Cmp(big.NewInt(10)) != 0 {
t.Error("c set bytes failed.")
}
}
func TestInitialiser(t *testing.T) {
check := false
init := NewInitialiser(func(x *Number) *Number {
check = true
return x
})
a := init(0).Add(init(1), init(2))
if a.Cmp(init(3)) != 0 {
t.Error("expected 3. got", a)
}
if !check {
t.Error("expected limiter to be called")
}
}
func TestGet(t *testing.T) {
a := Uint(10)
if a.Uint64() != 10 {
t.Error("expected to get 10. got", a.Uint64())
}
a = Uint(10)
if a.Int64() != 10 {
t.Error("expected to get 10. got", a.Int64())
}
}
func TestCmp(t *testing.T) {
a := Uint(10)
b := Uint(10)
c := Uint(11)
if a.Cmp(b) != 0 {
t.Error("a b == 0 failed", a, b)
}
if a.Cmp(c) >= 0 {
t.Error("a c < 0 failed", a, c)
}
if c.Cmp(b) <= 0 {
t.Error("c b > 0 failed", c, b)
}
}
func TestMaxArith(t *testing.T) {
a := Uint(0).Add(MaxUint256, One)
if a.Cmp(Zero) != 0 {
t.Error("expected max256 + 1 = 0 got", a)
}
a = Uint(0).Sub(Uint(0), One)
if a.Cmp(MaxUint256) != 0 {
t.Error("expected 0 - 1 = max256 got", a)
}
a = Int(0).Sub(Int(0), One)
if a.Cmp(MinOne) != 0 {
t.Error("expected 0 - 1 = -1 got", a)
}
}
func TestConversion(t *testing.T) {
a := Int(-1)
b := a.Uint256()
if b.Cmp(MaxUint256) != 0 {
t.Error("expected -1 => unsigned to return max. got", b)
}
}

View file

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
) )
// Lengths of hashes and addresses in bytes.
const ( const (
HashLength = 32 HashLength = 32
AddressLength = 20 AddressLength = 20

View file

@ -60,7 +60,7 @@ type Config struct {
Preload []string // Absolute paths to JavaScript files to preload Preload []string // Absolute paths to JavaScript files to preload
} }
// Console is a JavaScript interpreted runtime environment. It is a fully fleged // Console is a JavaScript interpreted runtime environment. It is a fully fledged
// JavaScript console attached to a running node via an external or in-process RPC // JavaScript console attached to a running node via an external or in-process RPC
// client. // client.
type Console struct { type Console struct {

View file

@ -95,7 +95,7 @@ func ensParentNode(name string) (common.Hash, common.Hash) {
} }
} }
func ensNode(name string) common.Hash { func EnsNode(name string) common.Hash {
parentNode, parentLabel := ensParentNode(name) parentNode, parentLabel := ensParentNode(name)
return crypto.Keccak256Hash(parentNode[:], parentLabel[:]) return crypto.Keccak256Hash(parentNode[:], parentLabel[:])
} }
@ -136,7 +136,7 @@ func (self *ENS) getRegistrar(node [32]byte) (*contract.FIFSRegistrarSession, er
// Resolve is a non-transactional call that returns the content hash associated with a name. // Resolve is a non-transactional call that returns the content hash associated with a name.
func (self *ENS) Resolve(name string) (common.Hash, error) { func (self *ENS) Resolve(name string) (common.Hash, error) {
node := ensNode(name) node := EnsNode(name)
resolver, err := self.getResolver(node) resolver, err := self.getResolver(node)
if err != nil { if err != nil {
@ -165,7 +165,7 @@ func (self *ENS) Register(name string) (*types.Transaction, error) {
// SetContentHash sets the content hash associated with a name. Only works if the caller // SetContentHash sets the content hash associated with a name. Only works if the caller
// owns the name, and the associated resolver implements a `setContent` function. // owns the name, and the associated resolver implements a `setContent` function.
func (self *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) { func (self *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) {
node := ensNode(name) node := EnsNode(name)
resolver, err := self.getResolver(node) resolver, err := self.getResolver(node)
if err != nil { if err != nil {

View file

@ -55,7 +55,7 @@ func TestENS(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("can't deploy resolver: %v", err) t.Fatalf("can't deploy resolver: %v", err)
} }
if _, err := ens.SetResolver(ensNode(name), resolverAddr); err != nil { if _, err := ens.SetResolver(EnsNode(name), resolverAddr); err != nil {
t.Fatalf("can't set resolver: %v", err) t.Fatalf("can't set resolver: %v", err)
} }
contractBackend.Commit() contractBackend.Commit()

View file

@ -51,7 +51,7 @@ func NewCompiler(debug bool) *Compiler {
// the compiler. // the compiler.
// //
// feed is the first pass in the compile stage as it // feed is the first pass in the compile stage as it
// collect the used labels in the program and keeps a // collects the used labels in the program and keeps a
// program counter which is used to determine the locations // program counter which is used to determine the locations
// of the jump dests. The labels can than be used in the // of the jump dests. The labels can than be used in the
// second stage to push labels and determine the right // second stage to push labels and determine the right
@ -120,7 +120,7 @@ func (c *Compiler) next() token {
return token return token
} }
// compile line compiles a single line instruction e.g. // compileLine compiles a single line instruction e.g.
// "push 1", "jump @label". // "push 1", "jump @label".
func (c *Compiler) compileLine() error { func (c *Compiler) compileLine() error {
n := c.next() n := c.next()

View file

@ -242,7 +242,7 @@ func lexLabel(l *lexer) stateFn {
} }
// lexInsideString lexes the inside of a string until // lexInsideString lexes the inside of a string until
// until the state function finds the closing quote. // the state function finds the closing quote.
// It returns the lex text state function. // It returns the lex text state function.
func lexInsideString(l *lexer) stateFn { func lexInsideString(l *lexer) stateFn {
if l.acceptRunUntil('"') { if l.acceptRunUntil('"') {

View file

@ -815,13 +815,11 @@ func (pool *TxPool) addTxsLocked(txs []*types.Transaction, local bool) []error {
for i, tx := range txs { for i, tx := range txs {
var replace bool var replace bool
if replace, errs[i] = pool.add(tx, local); errs[i] == nil { if replace, errs[i] = pool.add(tx, local); errs[i] == nil && !replace {
if !replace {
from, _ := types.Sender(pool.signer, tx) // already validated from, _ := types.Sender(pool.signer, tx) // already validated
dirty[from] = struct{}{} dirty[from] = struct{}{}
} }
} }
}
// Only reprocess the internal state if something was actually added // Only reprocess the internal state if something was actually added
if len(dirty) > 0 { if len(dirty) > 0 {
addrs := make([]common.Address, 0, len(dirty)) addrs := make([]common.Address, 0, len(dirty))
@ -1107,7 +1105,7 @@ func (pool *TxPool) demoteUnexecutables() {
log.Trace("Demoting pending transaction", "hash", hash) log.Trace("Demoting pending transaction", "hash", hash)
pool.enqueueTx(hash, tx) pool.enqueueTx(hash, tx)
} }
// If there's a gap in front, warn (should never happen) and postpone all transactions // If there's a gap in front, alert (should never happen) and postpone all transactions
if list.Len() > 0 && list.txs.Get(nonce) == nil { if list.Len() > 0 && list.txs.Get(nonce) == nil {
for _, tx := range list.Cap(0) { for _, tx := range list.Cap(0) {
hash := tx.Hash() hash := tx.Hash()

View file

@ -556,7 +556,7 @@ func opMload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *St
func opMstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opMstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// pop value of the stack // pop value of the stack
mStart, val := stack.pop(), stack.pop() mStart, val := stack.pop(), stack.pop()
memory.Set(mStart.Uint64(), 32, math.PaddedBigBytes(val, 32)) memory.Set32(mStart.Uint64(), val)
evm.interpreter.intPool.put(mStart, val) evm.interpreter.intPool.put(mStart, val)
return nil, nil return nil, nil
@ -570,9 +570,9 @@ func opMstore8(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *
} }
func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
loc := common.BigToHash(stack.pop()) loc := stack.peek()
val := evm.StateDB.GetState(contract.Address(), loc).Big() val := evm.StateDB.GetState(contract.Address(), common.BigToHash(loc))
stack.push(val) loc.SetBytes(val.Bytes())
return nil, nil return nil, nil
} }

View file

@ -425,3 +425,42 @@ func BenchmarkOpIsZero(b *testing.B) {
x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff" x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff"
opBenchmark(b, opIszero, x) opBenchmark(b, opIszero, x)
} }
func TestOpMstore(t *testing.T) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
)
mem.Resize(64)
pc := uint64(0)
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0))
opMstore(&pc, env, nil, mem, stack)
if got := common.Bytes2Hex(mem.Get(0, 32)); got != v {
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
}
stack.pushN(big.NewInt(0x1), big.NewInt(0))
opMstore(&pc, env, nil, mem, stack)
if common.Bytes2Hex(mem.Get(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
t.Fatalf("Mstore failed to overwrite previous value")
}
}
func BenchmarkOpMstore(bench *testing.B) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
)
mem.Resize(64)
pc := uint64(0)
memStart := big.NewInt(0)
value := big.NewInt(0x1337)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
stack.pushN(value, memStart)
opMstore(&pc, env, nil, mem, stack)
}
}

View file

@ -16,7 +16,12 @@
package vm package vm
import "fmt" import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common/math"
)
// Memory implements a simple memory model for the ethereum virtual machine. // Memory implements a simple memory model for the ethereum virtual machine.
type Memory struct { type Memory struct {
@ -30,19 +35,32 @@ func NewMemory() *Memory {
// Set sets offset + size to value // Set sets offset + size to value
func (m *Memory) Set(offset, size uint64, value []byte) { func (m *Memory) Set(offset, size uint64, value []byte) {
// length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory
if size > uint64(len(m.store)) {
panic("INVALID memory: store empty")
}
// It's possible the offset is greater than 0 and size equals 0. This is because // It's possible the offset is greater than 0 and size equals 0. This is because
// the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP) // the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP)
if size > 0 { if size > 0 {
// length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory
if offset+size > uint64(len(m.store)) {
panic("invalid memory: store empty")
}
copy(m.store[offset:offset+size], value) copy(m.store[offset:offset+size], value)
} }
} }
// Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to
// 32 bytes.
func (m *Memory) Set32(offset uint64, val *big.Int) {
// length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory
if offset+32 > uint64(len(m.store)) {
panic("invalid memory: store empty")
}
// Zero the memory area
copy(m.store[offset:offset+32], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
// Fill in relevant bits
math.ReadBits(val, m.store[offset:offset+32])
}
// Resize resizes the memory to size // Resize resizes the memory to size
func (m *Memory) Resize(size uint64) { func (m *Memory) Resize(size uint64) {
if uint64(m.Len()) < size { if uint64(m.Len()) < size {

View file

@ -359,7 +359,7 @@ type BadBlockArgs struct {
RLP string `json:"rlp"` RLP string `json:"rlp"`
} }
// GetBadBLocks returns a list of the last 'bad blocks' that the client has seen on the network // GetBadBlocks returns a list of the last 'bad blocks' that the client has seen on the network
// and returns them as a JSON list of block-hashes // and returns them as a JSON list of block-hashes
func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error) { func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error) {
blocks := api.eth.BlockChain().BadBlocks() blocks := api.eth.BlockChain().BadBlocks()
@ -431,7 +431,7 @@ func storageRangeAt(st state.Trie, start []byte, maxResult int) (StorageRangeRes
return result, nil return result, nil
} }
// GetModifiedAccountsByumber returns all accounts that have changed between the // GetModifiedAccountsByNumber returns all accounts that have changed between the
// two blocks specified. A change is defined as a difference in nonce, balance, // two blocks specified. A change is defined as a difference in nonce, balance,
// code hash, or storage hash. // code hash, or storage hash.
// //

View file

@ -43,6 +43,7 @@ type EthAPIBackend struct {
gpo *gasprice.Oracle gpo *gasprice.Oracle
} }
// ChainConfig returns the active chain configuration.
func (b *EthAPIBackend) ChainConfig() *params.ChainConfig { func (b *EthAPIBackend) ChainConfig() *params.ChainConfig {
return b.eth.chainConfig return b.eth.chainConfig
} }

View file

@ -88,7 +88,7 @@ type Ethereum struct {
gasPrice *big.Int gasPrice *big.Int
etherbase common.Address etherbase common.Address
networkId uint64 networkID uint64
netRPCService *ethapi.PublicNetAPI netRPCService *ethapi.PublicNetAPI
lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase) lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase)
@ -126,7 +126,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
accountManager: ctx.AccountManager, accountManager: ctx.AccountManager,
engine: CreateConsensusEngine(ctx, &config.Ethash, chainConfig, chainDb), engine: CreateConsensusEngine(ctx, &config.Ethash, chainConfig, chainDb),
shutdownChan: make(chan bool), shutdownChan: make(chan bool),
networkId: config.NetworkId, networkID: config.NetworkId,
gasPrice: config.GasPrice, gasPrice: config.GasPrice,
etherbase: config.Etherbase, etherbase: config.Etherbase,
bloomRequests: make(chan chan *bloombits.Retrieval), bloomRequests: make(chan chan *bloombits.Retrieval),
@ -369,7 +369,7 @@ func (s *Ethereum) Engine() consensus.Engine { return s.engine }
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
func (s *Ethereum) IsListening() bool { return true } // Always listening func (s *Ethereum) IsListening() bool { return true } // Always listening
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
func (s *Ethereum) NetVersion() uint64 { return s.networkId } func (s *Ethereum) NetVersion() uint64 { return s.networkID }
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
// Protocols implements node.Service, returning all the currently configured // Protocols implements node.Service, returning all the currently configured

View file

@ -900,7 +900,7 @@ func (d *Downloader) fillHeaderSkeleton(from uint64, skeleton []*types.Header) (
var ( var (
deliver = func(packet dataPack) (int, error) { deliver = func(packet dataPack) (int, error) {
pack := packet.(*headerPack) pack := packet.(*headerPack)
return d.queue.DeliverHeaders(pack.peerId, pack.headers, d.headerProcCh) return d.queue.DeliverHeaders(pack.peerID, pack.headers, d.headerProcCh)
} }
expire = func() map[string]int { return d.queue.ExpireHeaders(d.requestTTL()) } expire = func() map[string]int { return d.queue.ExpireHeaders(d.requestTTL()) }
throttle = func() bool { return false } throttle = func() bool { return false }
@ -930,7 +930,7 @@ func (d *Downloader) fetchBodies(from uint64) error {
var ( var (
deliver = func(packet dataPack) (int, error) { deliver = func(packet dataPack) (int, error) {
pack := packet.(*bodyPack) pack := packet.(*bodyPack)
return d.queue.DeliverBodies(pack.peerId, pack.transactions, pack.uncles) return d.queue.DeliverBodies(pack.peerID, pack.transactions, pack.uncles)
} }
expire = func() map[string]int { return d.queue.ExpireBodies(d.requestTTL()) } expire = func() map[string]int { return d.queue.ExpireBodies(d.requestTTL()) }
fetch = func(p *peerConnection, req *fetchRequest) error { return p.FetchBodies(req) } fetch = func(p *peerConnection, req *fetchRequest) error { return p.FetchBodies(req) }
@ -954,7 +954,7 @@ func (d *Downloader) fetchReceipts(from uint64) error {
var ( var (
deliver = func(packet dataPack) (int, error) { deliver = func(packet dataPack) (int, error) {
pack := packet.(*receiptPack) pack := packet.(*receiptPack)
return d.queue.DeliverReceipts(pack.peerId, pack.receipts) return d.queue.DeliverReceipts(pack.peerID, pack.receipts)
} }
expire = func() map[string]int { return d.queue.ExpireReceipts(d.requestTTL()) } expire = func() map[string]int { return d.queue.ExpireReceipts(d.requestTTL()) }
fetch = func(p *peerConnection, req *fetchRequest) error { return p.FetchReceipts(req) } fetch = func(p *peerConnection, req *fetchRequest) error { return p.FetchReceipts(req) }

View file

@ -596,21 +596,21 @@ func (q *queue) cancel(request *fetchRequest, taskQueue *prque.Prque, pendPool m
// Revoke cancels all pending requests belonging to a given peer. This method is // Revoke cancels all pending requests belonging to a given peer. This method is
// meant to be called during a peer drop to quickly reassign owned data fetches // meant to be called during a peer drop to quickly reassign owned data fetches
// to remaining nodes. // to remaining nodes.
func (q *queue) Revoke(peerId string) { func (q *queue) Revoke(peerID string) {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
if request, ok := q.blockPendPool[peerId]; ok { if request, ok := q.blockPendPool[peerID]; ok {
for _, header := range request.Headers { for _, header := range request.Headers {
q.blockTaskQueue.Push(header, -float32(header.Number.Uint64())) q.blockTaskQueue.Push(header, -float32(header.Number.Uint64()))
} }
delete(q.blockPendPool, peerId) delete(q.blockPendPool, peerID)
} }
if request, ok := q.receiptPendPool[peerId]; ok { if request, ok := q.receiptPendPool[peerID]; ok {
for _, header := range request.Headers { for _, header := range request.Headers {
q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64())) q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64()))
} }
delete(q.receiptPendPool, peerId) delete(q.receiptPendPool, peerID)
} }
} }

View file

@ -34,22 +34,22 @@ type dataPack interface {
// headerPack is a batch of block headers returned by a peer. // headerPack is a batch of block headers returned by a peer.
type headerPack struct { type headerPack struct {
peerId string peerID string
headers []*types.Header headers []*types.Header
} }
func (p *headerPack) PeerId() string { return p.peerId } func (p *headerPack) PeerId() string { return p.peerID }
func (p *headerPack) Items() int { return len(p.headers) } func (p *headerPack) Items() int { return len(p.headers) }
func (p *headerPack) Stats() string { return fmt.Sprintf("%d", len(p.headers)) } func (p *headerPack) Stats() string { return fmt.Sprintf("%d", len(p.headers)) }
// bodyPack is a batch of block bodies returned by a peer. // bodyPack is a batch of block bodies returned by a peer.
type bodyPack struct { type bodyPack struct {
peerId string peerID string
transactions [][]*types.Transaction transactions [][]*types.Transaction
uncles [][]*types.Header uncles [][]*types.Header
} }
func (p *bodyPack) PeerId() string { return p.peerId } func (p *bodyPack) PeerId() string { return p.peerID }
func (p *bodyPack) Items() int { func (p *bodyPack) Items() int {
if len(p.transactions) <= len(p.uncles) { if len(p.transactions) <= len(p.uncles) {
return len(p.transactions) return len(p.transactions)
@ -60,20 +60,20 @@ func (p *bodyPack) Stats() string { return fmt.Sprintf("%d:%d", len(p.transactio
// receiptPack is a batch of receipts returned by a peer. // receiptPack is a batch of receipts returned by a peer.
type receiptPack struct { type receiptPack struct {
peerId string peerID string
receipts [][]*types.Receipt receipts [][]*types.Receipt
} }
func (p *receiptPack) PeerId() string { return p.peerId } func (p *receiptPack) PeerId() string { return p.peerID }
func (p *receiptPack) Items() int { return len(p.receipts) } func (p *receiptPack) Items() int { return len(p.receipts) }
func (p *receiptPack) Stats() string { return fmt.Sprintf("%d", len(p.receipts)) } func (p *receiptPack) Stats() string { return fmt.Sprintf("%d", len(p.receipts)) }
// statePack is a batch of states returned by a peer. // statePack is a batch of states returned by a peer.
type statePack struct { type statePack struct {
peerId string peerID string
states [][]byte states [][]byte
} }
func (p *statePack) PeerId() string { return p.peerId } func (p *statePack) PeerId() string { return p.peerID }
func (p *statePack) Items() int { return len(p.states) } func (p *statePack) Items() int { return len(p.states) }
func (p *statePack) Stats() string { return fmt.Sprintf("%d", len(p.states)) } func (p *statePack) Stats() string { return fmt.Sprintf("%d", len(p.states)) }

View file

@ -88,7 +88,7 @@ type headerFilterTask struct {
time time.Time // Arrival time of the headers time time.Time // Arrival time of the headers
} }
// headerFilterTask represents a batch of block bodies (transactions and uncles) // bodyFilterTask represents a batch of block bodies (transactions and uncles)
// needing fetcher filtering. // needing fetcher filtering.
type bodyFilterTask struct { type bodyFilterTask struct {
peer string // The source peer of block bodies peer string // The source peer of block bodies

View file

@ -258,9 +258,9 @@ Logs:
if len(topics) > len(log.Topics) { if len(topics) > len(log.Topics) {
continue Logs continue Logs
} }
for i, topics := range topics { for i, sub := range topics {
match := len(topics) == 0 // empty rule set == wildcard match := len(sub) == 0 // empty rule set == wildcard
for _, topic := range topics { for _, topic := range sub {
if log.Topics[i] == topic { if log.Topics[i] == topic {
match = true match = true
break break

View file

@ -64,7 +64,7 @@ func errResp(code errCode, format string, v ...interface{}) error {
} }
type ProtocolManager struct { type ProtocolManager struct {
networkId uint64 networkID uint64
fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks) fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks)
acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing) acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing)
@ -98,10 +98,10 @@ type ProtocolManager struct {
// NewProtocolManager returns a new Ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // NewProtocolManager returns a new Ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
// with the Ethereum network. // with the Ethereum network.
func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkId uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkID uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
// Create the protocol manager with the base fields // Create the protocol manager with the base fields
manager := &ProtocolManager{ manager := &ProtocolManager{
networkId: networkId, networkID: networkID,
eventMux: mux, eventMux: mux,
txpool: txpool, txpool: txpool,
blockchain: blockchain, blockchain: blockchain,
@ -263,7 +263,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
number = head.Number.Uint64() number = head.Number.Uint64()
td = pm.blockchain.GetTd(hash, number) td = pm.blockchain.GetTd(hash, number)
) )
if err := p.Handshake(pm.networkId, td, hash, genesis.Hash()); err != nil { if err := p.Handshake(pm.networkID, td, hash, genesis.Hash()); err != nil {
p.Log().Debug("Ethereum handshake failed", "err", err) p.Log().Debug("Ethereum handshake failed", "err", err)
return err return err
} }
@ -779,7 +779,7 @@ type NodeInfo struct {
func (pm *ProtocolManager) NodeInfo() *NodeInfo { func (pm *ProtocolManager) NodeInfo() *NodeInfo {
currentBlock := pm.blockchain.CurrentBlock() currentBlock := pm.blockchain.CurrentBlock()
return &NodeInfo{ return &NodeInfo{
Network: pm.networkId, Network: pm.networkID,
Difficulty: pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()), Difficulty: pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()),
Genesis: pm.blockchain.Genesis().Hash(), Genesis: pm.blockchain.Genesis().Hash(),
Config: pm.blockchain.Config(), Config: pm.blockchain.Config(),

View file

@ -21,6 +21,7 @@
package debug package debug
import ( import (
"bytes"
"errors" "errors"
"io" "io"
"os" "os"
@ -190,9 +191,9 @@ func (*HandlerT) WriteMemProfile(file string) error {
// Stacks returns a printed representation of the stacks of all goroutines. // Stacks returns a printed representation of the stacks of all goroutines.
func (*HandlerT) Stacks() string { func (*HandlerT) Stacks() string {
buf := make([]byte, 1024*1024) buf := new(bytes.Buffer)
buf = buf[:runtime.Stack(buf, true)] pprof.Lookup("goroutine").WriteTo(buf, 2)
return string(buf) return buf.String()
} }
// FreeOSMemory returns unused memory to the OS. // FreeOSMemory returns unused memory to the OS.

View file

@ -313,8 +313,8 @@ web3._extend({
params: 2 params: 2
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'setMutexProfileRate', name: 'setMutexProfileFraction',
call: 'debug_setMutexProfileRate', call: 'debug_setMutexProfileFraction',
params: 1 params: 1
}), }),
new web3._extend.Method({ new web3._extend.Method({

View file

@ -15,7 +15,7 @@ import (
const ( const (
timeFormat = "2006-01-02T15:04:05-0700" timeFormat = "2006-01-02T15:04:05-0700"
termTimeFormat = "01-02|15:04:05" termTimeFormat = "01-02|15:04:05.999999"
floatFormat = 'f' floatFormat = 'f'
termMsgJust = 40 termMsgJust = 40
) )

View file

@ -12,6 +12,7 @@ const timeKey = "t"
const lvlKey = "lvl" const lvlKey = "lvl"
const msgKey = "msg" const msgKey = "msg"
const errorKey = "LOG15_ERROR" const errorKey = "LOG15_ERROR"
const skipLevel = 2
type Lvl int type Lvl int
@ -127,13 +128,13 @@ type logger struct {
h *swapHandler h *swapHandler
} }
func (l *logger) write(msg string, lvl Lvl, ctx []interface{}) { func (l *logger) write(msg string, lvl Lvl, ctx []interface{}, skip int) {
l.h.Log(&Record{ l.h.Log(&Record{
Time: time.Now(), Time: time.Now(),
Lvl: lvl, Lvl: lvl,
Msg: msg, Msg: msg,
Ctx: newContext(l.ctx, ctx), Ctx: newContext(l.ctx, ctx),
Call: stack.Caller(2), Call: stack.Caller(skip),
KeyNames: RecordKeyNames{ KeyNames: RecordKeyNames{
Time: timeKey, Time: timeKey,
Msg: msgKey, Msg: msgKey,
@ -157,27 +158,27 @@ func newContext(prefix []interface{}, suffix []interface{}) []interface{} {
} }
func (l *logger) Trace(msg string, ctx ...interface{}) { func (l *logger) Trace(msg string, ctx ...interface{}) {
l.write(msg, LvlTrace, ctx) l.write(msg, LvlTrace, ctx, skipLevel)
} }
func (l *logger) Debug(msg string, ctx ...interface{}) { func (l *logger) Debug(msg string, ctx ...interface{}) {
l.write(msg, LvlDebug, ctx) l.write(msg, LvlDebug, ctx, skipLevel)
} }
func (l *logger) Info(msg string, ctx ...interface{}) { func (l *logger) Info(msg string, ctx ...interface{}) {
l.write(msg, LvlInfo, ctx) l.write(msg, LvlInfo, ctx, skipLevel)
} }
func (l *logger) Warn(msg string, ctx ...interface{}) { func (l *logger) Warn(msg string, ctx ...interface{}) {
l.write(msg, LvlWarn, ctx) l.write(msg, LvlWarn, ctx, skipLevel)
} }
func (l *logger) Error(msg string, ctx ...interface{}) { func (l *logger) Error(msg string, ctx ...interface{}) {
l.write(msg, LvlError, ctx) l.write(msg, LvlError, ctx, skipLevel)
} }
func (l *logger) Crit(msg string, ctx ...interface{}) { func (l *logger) Crit(msg string, ctx ...interface{}) {
l.write(msg, LvlCrit, ctx) l.write(msg, LvlCrit, ctx, skipLevel)
os.Exit(1) os.Exit(1)
} }

View file

@ -31,31 +31,40 @@ func Root() Logger {
// Trace is a convenient alias for Root().Trace // Trace is a convenient alias for Root().Trace
func Trace(msg string, ctx ...interface{}) { func Trace(msg string, ctx ...interface{}) {
root.write(msg, LvlTrace, ctx) root.write(msg, LvlTrace, ctx, skipLevel)
} }
// Debug is a convenient alias for Root().Debug // Debug is a convenient alias for Root().Debug
func Debug(msg string, ctx ...interface{}) { func Debug(msg string, ctx ...interface{}) {
root.write(msg, LvlDebug, ctx) root.write(msg, LvlDebug, ctx, skipLevel)
} }
// Info is a convenient alias for Root().Info // Info is a convenient alias for Root().Info
func Info(msg string, ctx ...interface{}) { func Info(msg string, ctx ...interface{}) {
root.write(msg, LvlInfo, ctx) root.write(msg, LvlInfo, ctx, skipLevel)
} }
// Warn is a convenient alias for Root().Warn // Warn is a convenient alias for Root().Warn
func Warn(msg string, ctx ...interface{}) { func Warn(msg string, ctx ...interface{}) {
root.write(msg, LvlWarn, ctx) root.write(msg, LvlWarn, ctx, skipLevel)
} }
// Error is a convenient alias for Root().Error // Error is a convenient alias for Root().Error
func Error(msg string, ctx ...interface{}) { func Error(msg string, ctx ...interface{}) {
root.write(msg, LvlError, ctx) root.write(msg, LvlError, ctx, skipLevel)
} }
// Crit is a convenient alias for Root().Crit // Crit is a convenient alias for Root().Crit
func Crit(msg string, ctx ...interface{}) { func Crit(msg string, ctx ...interface{}) {
root.write(msg, LvlCrit, ctx) root.write(msg, LvlCrit, ctx, skipLevel)
os.Exit(1) os.Exit(1)
} }
// Output is a convenient alias for write, allowing for the modification of
// the calldepth (number of stack frames to skip).
// calldepth influences the reported line number of the log message.
// A calldepth of zero reports the immediate caller of Output.
// Non-zero calldepth skips as many stack frames.
func Output(msg string, lvl Lvl, calldepth int, ctx ...interface{}) {
root.write(msg, lvl, ctx, calldepth+skipLevel)
}

View file

@ -47,8 +47,8 @@ func TestTimerStop(t *testing.T) {
func TestTimerFunc(t *testing.T) { func TestTimerFunc(t *testing.T) {
tm := NewTimer() tm := NewTimer()
tm.Time(func() { time.Sleep(50e6) }) tm.Time(func() { time.Sleep(50e6) })
if max := tm.Max(); 35e6 > max || max > 95e6 { if max := tm.Max(); 35e6 > max || max > 145e6 {
t.Errorf("tm.Max(): 35e6 > %v || %v > 95e6\n", max, max) t.Errorf("tm.Max(): 35e6 > %v || %v > 145e6\n", max, max)
} }
} }

View file

@ -480,16 +480,16 @@ func (tab *Table) doRevalidate(done chan<- struct{}) {
b := tab.buckets[bi] b := tab.buckets[bi]
if err == nil { if err == nil {
// The node responded, move it to the front. // The node responded, move it to the front.
log.Debug("Revalidated node", "b", bi, "id", last.ID) log.Trace("Revalidated node", "b", bi, "id", last.ID)
b.bump(last) b.bump(last)
return return
} }
// No reply received, pick a replacement or delete the node if there aren't // No reply received, pick a replacement or delete the node if there aren't
// any replacements. // any replacements.
if r := tab.replace(b, last); r != nil { if r := tab.replace(b, last); r != nil {
log.Debug("Replaced dead node", "b", bi, "id", last.ID, "ip", last.IP, "r", r.ID, "rip", r.IP) log.Trace("Replaced dead node", "b", bi, "id", last.ID, "ip", last.IP, "r", r.ID, "rip", r.IP)
} else { } else {
log.Debug("Removed dead node", "b", bi, "id", last.ID, "ip", last.IP) log.Trace("Removed dead node", "b", bi, "id", last.ID, "ip", last.IP)
} }
} }

View file

@ -33,7 +33,9 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"sync" "sync"
"time"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
) )
@ -217,6 +219,8 @@ func (p *Peer) Drop(err error) {
// this low level call will be wrapped by libraries providing routed or broadcast sends // this low level call will be wrapped by libraries providing routed or broadcast sends
// but often just used to forward and push messages to directly connected peers // but often just used to forward and push messages to directly connected peers
func (p *Peer) Send(msg interface{}) error { func (p *Peer) Send(msg interface{}) error {
defer metrics.GetOrRegisterResettingTimer("peer.send_t", nil).UpdateSince(time.Now())
metrics.GetOrRegisterCounter("peer.send", nil).Inc(1)
code, found := p.spec.GetCode(msg) code, found := p.spec.GetCode(msg)
if !found { if !found {
return errorf(ErrInvalidMsgType, "%v", code) return errorf(ErrInvalidMsgType, "%v", code)

View file

@ -373,15 +373,14 @@ WAIT:
} }
} }
func XTestMultiplePeersDropSelf(t *testing.T) {
func TestMultiplePeersDropSelf(t *testing.T) {
runMultiplePeers(t, 0, runMultiplePeers(t, 0,
fmt.Errorf("subprotocol error"), fmt.Errorf("subprotocol error"),
fmt.Errorf("Message handler error: (msg code 3): dropped"), fmt.Errorf("Message handler error: (msg code 3): dropped"),
) )
} }
func TestMultiplePeersDropOther(t *testing.T) { func XTestMultiplePeersDropOther(t *testing.T) {
runMultiplePeers(t, 1, runMultiplePeers(t, 1,
fmt.Errorf("Message handler error: (msg code 3): dropped"), fmt.Errorf("Message handler error: (msg code 3): dropped"),
fmt.Errorf("subprotocol error"), fmt.Errorf("subprotocol error"),

View file

@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/simulations/pipes"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -159,7 +160,7 @@ func TestProtocolHandshake(t *testing.T) {
wg sync.WaitGroup wg sync.WaitGroup
) )
fd0, fd1, err := tcpPipe() fd0, fd1, err := pipes.TCPPipe()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -601,31 +602,3 @@ func TestHandshakeForwardCompatibility(t *testing.T) {
t.Errorf("ingress-mac('foo') mismatch:\ngot %x\nwant %x", fooIngressHash, wantFooIngressHash) t.Errorf("ingress-mac('foo') mismatch:\ngot %x\nwant %x", fooIngressHash, wantFooIngressHash)
} }
} }
// tcpPipe creates an in process full duplex pipe based on a localhost TCP socket
func tcpPipe() (net.Conn, net.Conn, error) {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return nil, nil, err
}
defer l.Close()
var aconn net.Conn
aerr := make(chan error, 1)
go func() {
var err error
aconn, err = l.Accept()
aerr <- err
}()
dconn, err := net.Dial("tcp", l.Addr().String())
if err != nil {
<-aerr
return nil, nil, err
}
if err := <-aerr; err != nil {
dconn.Close()
return nil, nil, err
}
return aconn, dconn, nil
}

View file

@ -594,13 +594,13 @@ running:
// This channel is used by AddPeer to add to the // This channel is used by AddPeer to add to the
// ephemeral static peer list. Add it to the dialer, // ephemeral static peer list. Add it to the dialer,
// it will keep the node connected. // it will keep the node connected.
srv.log.Debug("Adding static node", "node", n) srv.log.Trace("Adding static node", "node", n)
dialstate.addStatic(n) dialstate.addStatic(n)
case n := <-srv.removestatic: case n := <-srv.removestatic:
// This channel is used by RemovePeer to send a // This channel is used by RemovePeer to send a
// disconnect request to a peer and begin the // disconnect request to a peer and begin the
// stop keeping the node connected // stop keeping the node connected
srv.log.Debug("Removing static node", "node", n) srv.log.Trace("Removing static node", "node", n)
dialstate.removeStatic(n) dialstate.removeStatic(n)
if p, ok := peers[n.ID]; ok { if p, ok := peers[n.ID]; ok {
p.Disconnect(DiscRequested) p.Disconnect(DiscRequested)

View file

@ -28,11 +28,14 @@ import (
"strings" "strings"
"github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/reexec"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
) )
var (
ErrLinuxOnly = errors.New("DockerAdapter can only be used on Linux as it uses the current binary (which must be a Linux binary)")
)
// DockerAdapter is a NodeAdapter which runs simulation nodes inside Docker // DockerAdapter is a NodeAdapter which runs simulation nodes inside Docker
// containers. // containers.
// //
@ -52,7 +55,7 @@ func NewDockerAdapter() (*DockerAdapter, error) {
// It is reasonable to require this because the caller can just // It is reasonable to require this because the caller can just
// compile the current binary in a Docker container. // compile the current binary in a Docker container.
if runtime.GOOS != "linux" { if runtime.GOOS != "linux" {
return nil, errors.New("DockerAdapter can only be used on Linux as it uses the current binary (which must be a Linux binary)") return nil, ErrLinuxOnly
} }
if err := buildDockerImage(); err != nil { if err := buildDockerImage(); err != nil {
@ -95,7 +98,10 @@ func (d *DockerAdapter) NewNode(config *NodeConfig) (Node, error) {
conf.Stack.P2P.NoDiscovery = true conf.Stack.P2P.NoDiscovery = true
conf.Stack.P2P.NAT = nil conf.Stack.P2P.NAT = nil
conf.Stack.NoUSB = true conf.Stack.NoUSB = true
conf.Stack.Logger = log.New("node.id", config.ID.String())
// listen on all interfaces on a given port, which we set when we
// initialise NodeConfig (usually a random port)
conf.Stack.P2P.ListenAddr = fmt.Sprintf(":%d", config.Port)
node := &DockerNode{ node := &DockerNode{
ExecNode: ExecNode{ ExecNode: ExecNode{

View file

@ -17,6 +17,7 @@
package adapters package adapters
import ( import (
"bufio"
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"encoding/json" "encoding/json"
@ -103,9 +104,9 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) {
conf.Stack.P2P.NAT = nil conf.Stack.P2P.NAT = nil
conf.Stack.NoUSB = true conf.Stack.NoUSB = true
// listen on a random localhost port (we'll get the actual port after // listen on a localhost port, which we set when we
// starting the node through the RPC admin.nodeInfo method) // initialise NodeConfig (usually a random port)
conf.Stack.P2P.ListenAddr = "127.0.0.1:0" conf.Stack.P2P.ListenAddr = fmt.Sprintf(":%d", config.Port)
node := &ExecNode{ node := &ExecNode{
ID: config.ID, ID: config.ID,
@ -190,9 +191,23 @@ func (n *ExecNode) Start(snapshots map[string][]byte) (err error) {
n.Cmd = cmd n.Cmd = cmd
// read the WebSocket address from the stderr logs // read the WebSocket address from the stderr logs
wsAddr, err := findWSAddr(stderrR, 10*time.Second) var wsAddr string
if err != nil { wsAddrC := make(chan string)
return fmt.Errorf("error getting WebSocket address: %s", err) go func() {
s := bufio.NewScanner(stderrR)
for s.Scan() {
if strings.Contains(s.Text(), "WebSocket endpoint opened") {
wsAddrC <- wsAddrPattern.FindString(s.Text())
}
}
}()
select {
case wsAddr = <-wsAddrC:
if wsAddr == "" {
return errors.New("failed to read WebSocket address from stderr")
}
case <-time.After(10 * time.Second):
return errors.New("timed out waiting for WebSocket address on stderr")
} }
// create the RPC client and load the node info // create the RPC client and load the node info
@ -318,6 +333,21 @@ type execNodeConfig struct {
PeerAddrs map[string]string `json:"peer_addrs,omitempty"` PeerAddrs map[string]string `json:"peer_addrs,omitempty"`
} }
// ExternalIP gets an external IP address so that Enode URL is usable
func ExternalIP() net.IP {
addrs, err := net.InterfaceAddrs()
if err != nil {
log.Crit("error getting IP address", "err", err)
}
for _, addr := range addrs {
if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() && !ip.IP.IsLinkLocalUnicast() {
return ip.IP
}
}
log.Warn("unable to determine explicit IP address, falling back to loopback")
return net.IP{127, 0, 0, 1}
}
// execP2PNode starts a devp2p node when the current binary is executed with // execP2PNode starts a devp2p node when the current binary is executed with
// argv[0] being "p2p-node", reading the service / ID from argv[1] / argv[2] // argv[0] being "p2p-node", reading the service / ID from argv[1] / argv[2]
// and the node config from the _P2P_NODE_CONFIG environment variable // and the node config from the _P2P_NODE_CONFIG environment variable
@ -341,25 +371,11 @@ func execP2PNode() {
conf.Stack.P2P.PrivateKey = conf.Node.PrivateKey conf.Stack.P2P.PrivateKey = conf.Node.PrivateKey
conf.Stack.Logger = log.New("node.id", conf.Node.ID.String()) conf.Stack.Logger = log.New("node.id", conf.Node.ID.String())
// use explicit IP address in ListenAddr so that Enode URL is usable
externalIP := func() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
log.Crit("error getting IP address", "err", err)
}
for _, addr := range addrs {
if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() {
return ip.IP.String()
}
}
log.Crit("unable to determine explicit IP address")
return ""
}
if strings.HasPrefix(conf.Stack.P2P.ListenAddr, ":") { if strings.HasPrefix(conf.Stack.P2P.ListenAddr, ":") {
conf.Stack.P2P.ListenAddr = externalIP() + conf.Stack.P2P.ListenAddr conf.Stack.P2P.ListenAddr = ExternalIP().String() + conf.Stack.P2P.ListenAddr
} }
if conf.Stack.WSHost == "0.0.0.0" { if conf.Stack.WSHost == "0.0.0.0" {
conf.Stack.WSHost = externalIP() conf.Stack.WSHost = ExternalIP().String()
} }
// initialize the devp2p stack // initialize the devp2p stack

View file

@ -28,12 +28,14 @@ import (
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/simulations/pipes"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
// SimAdapter is a NodeAdapter which creates in-memory simulation nodes and // SimAdapter is a NodeAdapter which creates in-memory simulation nodes and
// connects them using in-memory net.Pipe connections // connects them using net.Pipe
type SimAdapter struct { type SimAdapter struct {
pipe func() (net.Conn, net.Conn, error)
mtx sync.RWMutex mtx sync.RWMutex
nodes map[discover.NodeID]*SimNode nodes map[discover.NodeID]*SimNode
services map[string]ServiceFunc services map[string]ServiceFunc
@ -42,8 +44,18 @@ type SimAdapter struct {
// NewSimAdapter creates a SimAdapter which is capable of running in-memory // NewSimAdapter creates a SimAdapter which is capable of running in-memory
// simulation nodes running any of the given services (the services to run on a // simulation nodes running any of the given services (the services to run on a
// particular node are passed to the NewNode function in the NodeConfig) // particular node are passed to the NewNode function in the NodeConfig)
// the adapter uses a net.Pipe for in-memory simulated network connections
func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter { func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter {
return &SimAdapter{ return &SimAdapter{
pipe: pipes.NetPipe,
nodes: make(map[discover.NodeID]*SimNode),
services: services,
}
}
func NewTCPAdapter(services map[string]ServiceFunc) *SimAdapter {
return &SimAdapter{
pipe: pipes.TCPPipe,
nodes: make(map[discover.NodeID]*SimNode), nodes: make(map[discover.NodeID]*SimNode),
services: services, services: services,
} }
@ -81,7 +93,7 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
MaxPeers: math.MaxInt32, MaxPeers: math.MaxInt32,
NoDiscovery: true, NoDiscovery: true,
Dialer: s, Dialer: s,
EnableMsgEvents: true, EnableMsgEvents: config.EnableMsgEvents,
}, },
NoUSB: true, NoUSB: true,
Logger: log.New("node.id", id.String()), Logger: log.New("node.id", id.String()),
@ -102,7 +114,7 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
} }
// Dial implements the p2p.NodeDialer interface by connecting to the node using // Dial implements the p2p.NodeDialer interface by connecting to the node using
// an in-memory net.Pipe connection // an in-memory net.Pipe
func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) { func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
node, ok := s.GetNode(dest.ID) node, ok := s.GetNode(dest.ID)
if !ok { if !ok {
@ -112,7 +124,14 @@ func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
if srv == nil { if srv == nil {
return nil, fmt.Errorf("node not running: %s", dest.ID) return nil, fmt.Errorf("node not running: %s", dest.ID)
} }
pipe1, pipe2 := net.Pipe() // SimAdapter.pipe is net.Pipe (NewSimAdapter)
pipe1, pipe2, err := s.pipe()
if err != nil {
return nil, err
}
// this is simulated 'listening'
// asynchronously call the dialed destintion node's p2p server
// to set up connection on the 'listening' side
go srv.SetupConn(pipe1, 0, nil) go srv.SetupConn(pipe1, 0, nil)
return pipe2, nil return pipe2, nil
} }
@ -140,8 +159,8 @@ func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) {
} }
// SimNode is an in-memory simulation node which connects to other nodes using // SimNode is an in-memory simulation node which connects to other nodes using
// an in-memory net.Pipe connection (see SimAdapter.Dial), running devp2p // net.Pipe (see SimAdapter.Dial), running devp2p protocols directly over that
// protocols directly over that pipe // pipe
type SimNode struct { type SimNode struct {
lock sync.RWMutex lock sync.RWMutex
ID discover.NodeID ID discover.NodeID
@ -241,7 +260,7 @@ func (sn *SimNode) Start(snapshots map[string][]byte) error {
for _, name := range sn.config.Services { for _, name := range sn.config.Services {
if err := sn.node.Register(newService(name)); err != nil { if err := sn.node.Register(newService(name)); err != nil {
regErr = err regErr = err
return break
} }
} }
}) })
@ -314,3 +333,18 @@ func (sn *SimNode) NodeInfo() *p2p.NodeInfo {
} }
return server.NodeInfo() return server.NodeInfo()
} }
func setSocketBuffer(conn net.Conn, socketReadBuffer int, socketWriteBuffer int) error {
switch v := conn.(type) {
case *net.UnixConn:
err := v.SetReadBuffer(socketReadBuffer)
if err != nil {
return err
}
err = v.SetWriteBuffer(socketWriteBuffer)
if err != nil {
return err
}
}
return nil
}

View file

@ -0,0 +1,259 @@
// Copyright 2017 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 adapters
import (
"bytes"
"encoding/binary"
"fmt"
"testing"
"time"
"github.com/ethereum/go-ethereum/p2p/simulations/pipes"
)
func TestTCPPipe(t *testing.T) {
c1, c2, err := pipes.TCPPipe()
if err != nil {
t.Fatal(err)
}
done := make(chan struct{})
go func() {
msgs := 50
size := 1024
for i := 0; i < msgs; i++ {
msg := make([]byte, size)
_ = binary.PutUvarint(msg, uint64(i))
_, err := c1.Write(msg)
if err != nil {
t.Fatal(err)
}
}
for i := 0; i < msgs; i++ {
msg := make([]byte, size)
_ = binary.PutUvarint(msg, uint64(i))
out := make([]byte, size)
_, err := c2.Read(out)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(msg, out) {
t.Fatalf("expected %#v, got %#v", msg, out)
}
}
done <- struct{}{}
}()
select {
case <-done:
case <-time.After(5 * time.Second):
t.Fatal("test timeout")
}
}
func TestTCPPipeBidirections(t *testing.T) {
c1, c2, err := pipes.TCPPipe()
if err != nil {
t.Fatal(err)
}
done := make(chan struct{})
go func() {
msgs := 50
size := 7
for i := 0; i < msgs; i++ {
msg := []byte(fmt.Sprintf("ping %02d", i))
_, err := c1.Write(msg)
if err != nil {
t.Fatal(err)
}
}
for i := 0; i < msgs; i++ {
expected := []byte(fmt.Sprintf("ping %02d", i))
out := make([]byte, size)
_, err := c2.Read(out)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(expected, out) {
t.Fatalf("expected %#v, got %#v", out, expected)
} else {
msg := []byte(fmt.Sprintf("pong %02d", i))
_, err := c2.Write(msg)
if err != nil {
t.Fatal(err)
}
}
}
for i := 0; i < msgs; i++ {
expected := []byte(fmt.Sprintf("pong %02d", i))
out := make([]byte, size)
_, err := c1.Read(out)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(expected, out) {
t.Fatalf("expected %#v, got %#v", out, expected)
}
}
done <- struct{}{}
}()
select {
case <-done:
case <-time.After(5 * time.Second):
t.Fatal("test timeout")
}
}
func TestNetPipe(t *testing.T) {
c1, c2, err := pipes.NetPipe()
if err != nil {
t.Fatal(err)
}
done := make(chan struct{})
go func() {
msgs := 50
size := 1024
// netPipe is blocking, so writes are emitted asynchronously
go func() {
for i := 0; i < msgs; i++ {
msg := make([]byte, size)
_ = binary.PutUvarint(msg, uint64(i))
_, err := c1.Write(msg)
if err != nil {
t.Fatal(err)
}
}
}()
for i := 0; i < msgs; i++ {
msg := make([]byte, size)
_ = binary.PutUvarint(msg, uint64(i))
out := make([]byte, size)
_, err := c2.Read(out)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(msg, out) {
t.Fatalf("expected %#v, got %#v", msg, out)
}
}
done <- struct{}{}
}()
select {
case <-done:
case <-time.After(5 * time.Second):
t.Fatal("test timeout")
}
}
func TestNetPipeBidirections(t *testing.T) {
c1, c2, err := pipes.NetPipe()
if err != nil {
t.Fatal(err)
}
done := make(chan struct{})
go func() {
msgs := 1000
size := 8
pingTemplate := "ping %03d"
pongTemplate := "pong %03d"
// netPipe is blocking, so writes are emitted asynchronously
go func() {
for i := 0; i < msgs; i++ {
msg := []byte(fmt.Sprintf(pingTemplate, i))
_, err := c1.Write(msg)
if err != nil {
t.Fatal(err)
}
}
}()
// netPipe is blocking, so reads for pong are emitted asynchronously
go func() {
for i := 0; i < msgs; i++ {
expected := []byte(fmt.Sprintf(pongTemplate, i))
out := make([]byte, size)
_, err := c1.Read(out)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(expected, out) {
t.Fatalf("expected %#v, got %#v", expected, out)
}
}
done <- struct{}{}
}()
// expect to read pings, and respond with pongs to the alternate connection
for i := 0; i < msgs; i++ {
expected := []byte(fmt.Sprintf(pingTemplate, i))
out := make([]byte, size)
_, err := c2.Read(out)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(expected, out) {
t.Fatalf("expected %#v, got %#v", expected, out)
} else {
msg := []byte(fmt.Sprintf(pongTemplate, i))
_, err := c2.Write(msg)
if err != nil {
t.Fatal(err)
}
}
}
}()
select {
case <-done:
case <-time.After(5 * time.Second):
t.Fatal("test timeout")
}
}

View file

@ -23,6 +23,7 @@ import (
"fmt" "fmt"
"net" "net"
"os" "os"
"strconv"
"github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/reexec"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -97,6 +98,8 @@ type NodeConfig struct {
// function to sanction or prevent suggesting a peer // function to sanction or prevent suggesting a peer
Reachable func(id discover.NodeID) bool Reachable func(id discover.NodeID) bool
Port uint16
} }
// nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding // nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding
@ -106,6 +109,8 @@ type nodeConfigJSON struct {
PrivateKey string `json:"private_key"` PrivateKey string `json:"private_key"`
Name string `json:"name"` Name string `json:"name"`
Services []string `json:"services"` Services []string `json:"services"`
EnableMsgEvents bool `json:"enable_msg_events"`
Port uint16 `json:"port"`
} }
// MarshalJSON implements the json.Marshaler interface by encoding the config // MarshalJSON implements the json.Marshaler interface by encoding the config
@ -115,6 +120,8 @@ func (n *NodeConfig) MarshalJSON() ([]byte, error) {
ID: n.ID.String(), ID: n.ID.String(),
Name: n.Name, Name: n.Name,
Services: n.Services, Services: n.Services,
Port: n.Port,
EnableMsgEvents: n.EnableMsgEvents,
} }
if n.PrivateKey != nil { if n.PrivateKey != nil {
confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey)) confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey))
@ -152,6 +159,8 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error {
n.Name = confJSON.Name n.Name = confJSON.Name
n.Services = confJSON.Services n.Services = confJSON.Services
n.Port = confJSON.Port
n.EnableMsgEvents = confJSON.EnableMsgEvents
return nil return nil
} }
@ -163,15 +172,38 @@ func RandomNodeConfig() *NodeConfig {
if err != nil { if err != nil {
panic("unable to generate key") panic("unable to generate key")
} }
var id discover.NodeID
pubkey := crypto.FromECDSAPub(&key.PublicKey) id := discover.PubkeyID(&key.PublicKey)
copy(id[:], pubkey[1:]) port, err := assignTCPPort()
if err != nil {
panic("unable to assign tcp port")
}
return &NodeConfig{ return &NodeConfig{
ID: id, ID: id,
Name: fmt.Sprintf("node_%s", id.String()),
PrivateKey: key, PrivateKey: key,
Port: port,
EnableMsgEvents: true,
} }
} }
func assignTCPPort() (uint16, error) {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return 0, err
}
l.Close()
_, port, err := net.SplitHostPort(l.Addr().String())
if err != nil {
return 0, err
}
p, err := strconv.ParseInt(port, 10, 32)
if err != nil {
return 0, err
}
return uint16(p), nil
}
// ServiceContext is a collection of options and methods which can be utilised // ServiceContext is a collection of options and methods which can be utilised
// when starting services // when starting services
type ServiceContext struct { type ServiceContext struct {

View file

@ -561,7 +561,8 @@ func (s *Server) LoadSnapshot(w http.ResponseWriter, req *http.Request) {
// CreateNode creates a node in the network using the given configuration // CreateNode creates a node in the network using the given configuration
func (s *Server) CreateNode(w http.ResponseWriter, req *http.Request) { func (s *Server) CreateNode(w http.ResponseWriter, req *http.Request) {
config := adapters.RandomNodeConfig() config := &adapters.NodeConfig{}
err := json.NewDecoder(req.Body).Decode(config) err := json.NewDecoder(req.Body).Decode(config)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)

View file

@ -348,7 +348,8 @@ func startTestNetwork(t *testing.T, client *Client) []string {
nodeCount := 2 nodeCount := 2
nodeIDs := make([]string, nodeCount) nodeIDs := make([]string, nodeCount)
for i := 0; i < nodeCount; i++ { for i := 0; i < nodeCount; i++ {
node, err := client.CreateNode(nil) config := adapters.RandomNodeConfig()
node, err := client.CreateNode(config)
if err != nil { if err != nil {
t.Fatalf("error creating node: %s", err) t.Fatalf("error creating node: %s", err)
} }
@ -527,7 +528,9 @@ func TestHTTPNodeRPC(t *testing.T) {
// start a node in the network // start a node in the network
client := NewClient(s.URL) client := NewClient(s.URL)
node, err := client.CreateNode(nil)
config := adapters.RandomNodeConfig()
node, err := client.CreateNode(config)
if err != nil { if err != nil {
t.Fatalf("error creating node: %s", err) t.Fatalf("error creating node: %s", err)
} }
@ -589,7 +592,8 @@ func TestHTTPSnapshot(t *testing.T) {
nodeCount := 2 nodeCount := 2
nodes := make([]*p2p.NodeInfo, nodeCount) nodes := make([]*p2p.NodeInfo, nodeCount)
for i := 0; i < nodeCount; i++ { for i := 0; i < nodeCount; i++ {
node, err := client.CreateNode(nil) config := adapters.RandomNodeConfig()
node, err := client.CreateNode(config)
if err != nil { if err != nil {
t.Fatalf("error creating node: %s", err) t.Fatalf("error creating node: %s", err)
} }

View file

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
) )
//a map of mocker names to its function //a map of mocker names to its function
@ -102,8 +103,14 @@ func startStop(net *Network, quit chan struct{}, nodeCount int) {
func probabilistic(net *Network, quit chan struct{}, nodeCount int) { func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
nodes, err := connectNodesInRing(net, nodeCount) nodes, err := connectNodesInRing(net, nodeCount)
if err != nil { if err != nil {
select {
case <-quit:
//error may be due to abortion of mocking; so the quit channel is closed
return
default:
panic("Could not startup node network for mocker") panic("Could not startup node network for mocker")
} }
}
for { for {
select { select {
case <-quit: case <-quit:
@ -143,7 +150,7 @@ func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
log.Debug(fmt.Sprintf("node %v shutting down", nodes[i])) log.Debug(fmt.Sprintf("node %v shutting down", nodes[i]))
err := net.Stop(nodes[i]) err := net.Stop(nodes[i])
if err != nil { if err != nil {
log.Error(fmt.Sprintf("Error stopping node %s", nodes[i])) log.Error("Error stopping node", "node", nodes[i])
wg.Done() wg.Done()
continue continue
} }
@ -151,7 +158,7 @@ func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
time.Sleep(randWait) time.Sleep(randWait)
err := net.Start(id) err := net.Start(id)
if err != nil { if err != nil {
log.Error(fmt.Sprintf("Error starting node %s", id)) log.Error("Error starting node", "node", id)
} }
wg.Done() wg.Done()
}(nodes[i]) }(nodes[i])
@ -165,9 +172,10 @@ func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
func connectNodesInRing(net *Network, nodeCount int) ([]discover.NodeID, error) { func connectNodesInRing(net *Network, nodeCount int) ([]discover.NodeID, error) {
ids := make([]discover.NodeID, nodeCount) ids := make([]discover.NodeID, nodeCount)
for i := 0; i < nodeCount; i++ { for i := 0; i < nodeCount; i++ {
node, err := net.NewNode() conf := adapters.RandomNodeConfig()
node, err := net.NewNodeWithConfig(conf)
if err != nil { if err != nil {
log.Error("Error creating a node! %s", err) log.Error("Error creating a node!", "err", err)
return nil, err return nil, err
} }
ids[i] = node.ID() ids[i] = node.ID()
@ -175,7 +183,7 @@ func connectNodesInRing(net *Network, nodeCount int) ([]discover.NodeID, error)
for _, id := range ids { for _, id := range ids {
if err := net.Start(id); err != nil { if err := net.Start(id); err != nil {
log.Error("Error starting a node! %s", err) log.Error("Error starting a node!", "err", err)
return nil, err return nil, err
} }
log.Debug(fmt.Sprintf("node %v starting up", id)) log.Debug(fmt.Sprintf("node %v starting up", id))
@ -183,7 +191,7 @@ func connectNodesInRing(net *Network, nodeCount int) ([]discover.NodeID, error)
for i, id := range ids { for i, id := range ids {
peerID := ids[(i+1)%len(ids)] peerID := ids[(i+1)%len(ids)]
if err := net.Connect(id, peerID); err != nil { if err := net.Connect(id, peerID); err != nil {
log.Error("Error connecting a node to a peer! %s", err) log.Error("Error connecting a node to a peer!", "err", err)
return nil, err return nil, err
} }
} }

View file

@ -382,6 +382,15 @@ func (net *Network) GetNodeByName(name string) *Node {
return net.getNodeByName(name) return net.getNodeByName(name)
} }
// GetNodes returns the existing nodes
func (net *Network) GetNodes() (nodes []*Node) {
net.lock.Lock()
defer net.lock.Unlock()
nodes = append(nodes, net.Nodes...)
return nodes
}
func (net *Network) getNode(id discover.NodeID) *Node { func (net *Network) getNode(id discover.NodeID) *Node {
i, found := net.nodeMap[id] i, found := net.nodeMap[id]
if !found { if !found {
@ -399,15 +408,6 @@ func (net *Network) getNodeByName(name string) *Node {
return nil return nil
} }
// GetNodes returns the existing nodes
func (net *Network) GetNodes() (nodes []*Node) {
net.lock.Lock()
defer net.lock.Unlock()
nodes = append(nodes, net.Nodes...)
return nodes
}
// GetConn returns the connection which exists between "one" and "other" // GetConn returns the connection which exists between "one" and "other"
// regardless of which node initiated the connection // regardless of which node initiated the connection
func (net *Network) GetConn(oneID, otherID discover.NodeID) *Conn { func (net *Network) GetConn(oneID, otherID discover.NodeID) *Conn {

View file

@ -41,7 +41,8 @@ func TestNetworkSimulation(t *testing.T) {
nodeCount := 20 nodeCount := 20
ids := make([]discover.NodeID, nodeCount) ids := make([]discover.NodeID, nodeCount)
for i := 0; i < nodeCount; i++ { for i := 0; i < nodeCount; i++ {
node, err := network.NewNode() conf := adapters.RandomNodeConfig()
node, err := network.NewNodeWithConfig(conf)
if err != nil { if err != nil {
t.Fatalf("error creating node: %s", err) t.Fatalf("error creating node: %s", err)
} }

View file

@ -0,0 +1,55 @@
// Copyright 2017 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 pipes
import (
"net"
)
// NetPipe wraps net.Pipe in a signature returning an error
func NetPipe() (net.Conn, net.Conn, error) {
p1, p2 := net.Pipe()
return p1, p2, nil
}
// TCPPipe creates an in process full duplex pipe based on a localhost TCP socket
func TCPPipe() (net.Conn, net.Conn, error) {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return nil, nil, err
}
defer l.Close()
var aconn net.Conn
aerr := make(chan error, 1)
go func() {
var err error
aconn, err = l.Accept()
aerr <- err
}()
dconn, err := net.Dial("tcp", l.Addr().String())
if err != nil {
<-aerr
return nil, nil, err
}
if err := <-aerr; err != nil {
dconn.Close()
return nil, nil, err
}
return aconn, dconn, nil
}

View file

@ -23,8 +23,8 @@ import (
const ( const (
VersionMajor = 1 // Major version component of the current release VersionMajor = 1 // Major version component of the current release
VersionMinor = 8 // Minor version component of the current release VersionMinor = 8 // Minor version component of the current release
VersionPatch = 11 // Patch version component of the current release VersionPatch = 12 // Patch version component of the current release
VersionMeta = "stable" // Version metadata to append to the version string VersionMeta = "unstable" // Version metadata to append to the version string
) )
// Version holds the textual version string. // Version holds the textual version string.

View file

@ -61,7 +61,7 @@ const (
// The approach taken here is to maintain a per-subscription linked list buffer // The approach taken here is to maintain a per-subscription linked list buffer
// shrinks on demand. If the buffer reaches the size below, the subscription is // shrinks on demand. If the buffer reaches the size below, the subscription is
// dropped. // dropped.
maxClientSubscriptionBuffer = 8000 maxClientSubscriptionBuffer = 20000
) )
// BatchElem is an element in a batch request. // BatchElem is an element in a batch request.

View file

@ -19,7 +19,6 @@ package whisperv5
import ( import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
"fmt"
"net" "net"
"sync" "sync"
"testing" "testing"
@ -108,8 +107,6 @@ func TestSimulation(t *testing.T) {
func initialize(t *testing.T) { func initialize(t *testing.T) {
var err error var err error
ip := net.IPv4(127, 0, 0, 1)
port0 := 30303
for i := 0; i < NumNodes; i++ { for i := 0; i < NumNodes; i++ {
var node TestNode var node TestNode
@ -128,29 +125,15 @@ func initialize(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed convert the key: %s.", keys[i]) t.Fatalf("failed convert the key: %s.", keys[i])
} }
port := port0 + i
addr := fmt.Sprintf(":%d", port) // e.g. ":30303"
name := common.MakeName("whisper-go", "2.0") name := common.MakeName("whisper-go", "2.0")
var peers []*discover.Node
if i > 0 {
peerNodeId := nodes[i-1].id
peerPort := uint16(port - 1)
peerNode := discover.PubkeyID(&peerNodeId.PublicKey)
peer := discover.NewNode(peerNode, ip, peerPort, peerPort)
peers = append(peers, peer)
}
node.server = &p2p.Server{ node.server = &p2p.Server{
Config: p2p.Config{ Config: p2p.Config{
PrivateKey: node.id, PrivateKey: node.id,
MaxPeers: NumNodes/2 + 1, MaxPeers: NumNodes/2 + 1,
Name: name, Name: name,
Protocols: node.shh.Protocols(), Protocols: node.shh.Protocols(),
ListenAddr: addr, ListenAddr: "127.0.0.1:0",
NAT: nat.Any(), NAT: nat.Any(),
BootstrapNodes: peers,
StaticNodes: peers,
TrustedNodes: peers,
}, },
} }
@ -159,6 +142,15 @@ func initialize(t *testing.T) {
t.Fatalf("failed to start server %d.", i) t.Fatalf("failed to start server %d.", i)
} }
for j := 0; j < i; j++ {
peerNodeId := nodes[j].id
address, _ := net.ResolveTCPAddr("tcp", nodes[j].server.ListenAddr)
peerPort := uint16(address.Port)
peerNode := discover.PubkeyID(&peerNodeId.PublicKey)
peer := discover.NewNode(peerNode, address.IP, peerPort, peerPort)
node.server.AddPeer(peer)
}
nodes[i] = &node nodes[i] = &node
} }
} }

View file

@ -21,12 +21,13 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"fmt" "fmt"
mrand "math/rand" mrand "math/rand"
"net"
"sync" "sync"
"sync/atomic" "sync/atomic"
"testing" "testing"
"time" "time"
"net"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -173,8 +174,6 @@ func initialize(t *testing.T) {
initBloom(t) initBloom(t)
var err error var err error
ip := net.IPv4(127, 0, 0, 1)
port0 := 30303
for i := 0; i < NumNodes; i++ { for i := 0; i < NumNodes; i++ {
var node TestNode var node TestNode
@ -199,17 +198,7 @@ func initialize(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed convert the key: %s.", keys[i]) t.Fatalf("failed convert the key: %s.", keys[i])
} }
port := port0 + i
addr := fmt.Sprintf(":%d", port) // e.g. ":30303"
name := common.MakeName("whisper-go", "2.0") name := common.MakeName("whisper-go", "2.0")
var peers []*discover.Node
if i > 0 {
peerNodeID := nodes[i-1].id
peerPort := uint16(port - 1)
peerNode := discover.PubkeyID(&peerNodeID.PublicKey)
peer := discover.NewNode(peerNode, ip, peerPort, peerPort)
peers = append(peers, peer)
}
node.server = &p2p.Server{ node.server = &p2p.Server{
Config: p2p.Config{ Config: p2p.Config{
@ -217,22 +206,28 @@ func initialize(t *testing.T) {
MaxPeers: NumNodes/2 + 1, MaxPeers: NumNodes/2 + 1,
Name: name, Name: name,
Protocols: node.shh.Protocols(), Protocols: node.shh.Protocols(),
ListenAddr: addr, ListenAddr: "127.0.0.1:0",
NAT: nat.Any(), NAT: nat.Any(),
BootstrapNodes: peers,
StaticNodes: peers,
TrustedNodes: peers,
}, },
} }
go startServer(t, node.server)
nodes[i] = &node nodes[i] = &node
} }
for i := 0; i < NumNodes; i++ {
go startServer(t, nodes[i].server)
}
waitForServersToStart(t) waitForServersToStart(t)
for i := 0; i < NumNodes; i++ {
for j := 0; j < i; j++ {
peerNodeId := nodes[j].id
address, _ := net.ResolveTCPAddr("tcp", nodes[j].server.ListenAddr)
peerPort := uint16(address.Port)
peerNode := discover.PubkeyID(&peerNodeId.PublicKey)
peer := discover.NewNode(peerNode, address.IP, peerPort, peerPort)
nodes[i].server.AddPeer(peer)
}
}
} }
func startServer(t *testing.T, s *p2p.Server) { func startServer(t *testing.T, s *p2p.Server) {