Commit 70867904 authored by obscuren's avatar obscuren

Merge branch 'release/0.9.25'

parents ceea1a70 2c532a72
...@@ -88,6 +88,7 @@ func (js *jsre) adminBindings() { ...@@ -88,6 +88,7 @@ func (js *jsre) adminBindings() {
debug.Set("getBlockRlp", js.getBlockRlp) debug.Set("getBlockRlp", js.getBlockRlp)
debug.Set("setHead", js.setHead) debug.Set("setHead", js.setHead)
debug.Set("processBlock", js.debugBlock) debug.Set("processBlock", js.debugBlock)
debug.Set("seedhash", js.seedHash)
// undocumented temporary // undocumented temporary
debug.Set("waitForBlocks", js.waitForBlocks) debug.Set("waitForBlocks", js.waitForBlocks)
} }
...@@ -118,6 +119,27 @@ func (js *jsre) getBlock(call otto.FunctionCall) (*types.Block, error) { ...@@ -118,6 +119,27 @@ func (js *jsre) getBlock(call otto.FunctionCall) (*types.Block, error) {
return block, nil return block, nil
} }
func (js *jsre) seedHash(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) > 0 {
if call.Argument(0).IsNumber() {
num, _ := call.Argument(0).ToInteger()
hash, err := ethash.GetSeedHash(uint64(num))
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
v, _ := call.Otto.ToValue(fmt.Sprintf("0x%x", hash))
return v
} else {
fmt.Println("arg not a number")
}
} else {
fmt.Println("requires number argument")
}
return otto.UndefinedValue()
}
func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value { func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value {
txs := js.ethereum.TxPool().GetTransactions() txs := js.ethereum.TxPool().GetTransactions()
...@@ -144,7 +166,8 @@ func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value { ...@@ -144,7 +166,8 @@ func (js *jsre) pendingTransactions(call otto.FunctionCall) otto.Value {
} }
} }
return js.re.ToVal(ltxs) v, _ := call.Otto.ToValue(ltxs)
return v
} }
func (js *jsre) resend(call otto.FunctionCall) otto.Value { func (js *jsre) resend(call otto.FunctionCall) otto.Value {
...@@ -175,7 +198,8 @@ func (js *jsre) resend(call otto.FunctionCall) otto.Value { ...@@ -175,7 +198,8 @@ func (js *jsre) resend(call otto.FunctionCall) otto.Value {
} }
js.ethereum.TxPool().RemoveTransactions(types.Transactions{tx.tx}) js.ethereum.TxPool().RemoveTransactions(types.Transactions{tx.tx})
return js.re.ToVal(ret) v, _ := call.Otto.ToValue(ret)
return v
} }
fmt.Println("first argument must be a transaction") fmt.Println("first argument must be a transaction")
...@@ -198,12 +222,13 @@ func (js *jsre) sign(call otto.FunctionCall) otto.Value { ...@@ -198,12 +222,13 @@ func (js *jsre) sign(call otto.FunctionCall) otto.Value {
fmt.Println(err) fmt.Println(err)
return otto.UndefinedValue() return otto.UndefinedValue()
} }
v, err := js.xeth.Sign(signer, data, false) signed, err := js.xeth.Sign(signer, data, false)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return otto.UndefinedValue() return otto.UndefinedValue()
} }
return js.re.ToVal(v) v, _ := call.Otto.ToValue(signed)
return v
} }
func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value { func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value {
...@@ -217,10 +242,11 @@ func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value { ...@@ -217,10 +242,11 @@ func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value {
vm.Debug = true vm.Debug = true
_, err = js.ethereum.BlockProcessor().RetryProcess(block) _, err = js.ethereum.BlockProcessor().RetryProcess(block)
if err != nil { if err != nil {
glog.Infoln(err) fmt.Println(err)
} }
vm.Debug = old vm.Debug = old
fmt.Println("ok")
return otto.UndefinedValue() return otto.UndefinedValue()
} }
...@@ -237,8 +263,8 @@ func (js *jsre) setHead(call otto.FunctionCall) otto.Value { ...@@ -237,8 +263,8 @@ func (js *jsre) setHead(call otto.FunctionCall) otto.Value {
func (js *jsre) downloadProgress(call otto.FunctionCall) otto.Value { func (js *jsre) downloadProgress(call otto.FunctionCall) otto.Value {
current, max := js.ethereum.Downloader().Stats() current, max := js.ethereum.Downloader().Stats()
v, _ := call.Otto.ToValue(fmt.Sprintf("%d/%d", current, max))
return js.re.ToVal(fmt.Sprintf("%d/%d", current, max)) return v
} }
func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value { func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value {
...@@ -248,7 +274,8 @@ func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value { ...@@ -248,7 +274,8 @@ func (js *jsre) getBlockRlp(call otto.FunctionCall) otto.Value {
return otto.UndefinedValue() return otto.UndefinedValue()
} }
encoded, _ := rlp.EncodeToBytes(block) encoded, _ := rlp.EncodeToBytes(block)
return js.re.ToVal(fmt.Sprintf("%x", encoded)) v, _ := call.Otto.ToValue(fmt.Sprintf("%x", encoded))
return v
} }
func (js *jsre) setExtra(call otto.FunctionCall) otto.Value { func (js *jsre) setExtra(call otto.FunctionCall) otto.Value {
...@@ -278,8 +305,9 @@ func (js *jsre) setGasPrice(call otto.FunctionCall) otto.Value { ...@@ -278,8 +305,9 @@ func (js *jsre) setGasPrice(call otto.FunctionCall) otto.Value {
return otto.UndefinedValue() return otto.UndefinedValue()
} }
func (js *jsre) hashrate(otto.FunctionCall) otto.Value { func (js *jsre) hashrate(call otto.FunctionCall) otto.Value {
return js.re.ToVal(js.ethereum.Miner().HashRate()) v, _ := call.Otto.ToValue(js.ethereum.Miner().HashRate())
return v
} }
func (js *jsre) makeDAG(call otto.FunctionCall) otto.Value { func (js *jsre) makeDAG(call otto.FunctionCall) otto.Value {
...@@ -495,15 +523,18 @@ func (js *jsre) newAccount(call otto.FunctionCall) otto.Value { ...@@ -495,15 +523,18 @@ func (js *jsre) newAccount(call otto.FunctionCall) otto.Value {
fmt.Printf("Could not create the account: %v", err) fmt.Printf("Could not create the account: %v", err)
return otto.UndefinedValue() return otto.UndefinedValue()
} }
return js.re.ToVal(acct.Address.Hex()) v, _ := call.Otto.ToValue(acct.Address.Hex())
return v
} }
func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value { func (js *jsre) nodeInfo(call otto.FunctionCall) otto.Value {
return js.re.ToVal(js.ethereum.NodeInfo()) v, _ := call.Otto.ToValue(js.ethereum.NodeInfo())
return v
} }
func (js *jsre) peers(call otto.FunctionCall) otto.Value { func (js *jsre) peers(call otto.FunctionCall) otto.Value {
return js.re.ToVal(js.ethereum.PeersInfo()) v, _ := call.Otto.ToValue(js.ethereum.PeersInfo())
return v
} }
func (js *jsre) importChain(call otto.FunctionCall) otto.Value { func (js *jsre) importChain(call otto.FunctionCall) otto.Value {
...@@ -562,7 +593,8 @@ func (js *jsre) dumpBlock(call otto.FunctionCall) otto.Value { ...@@ -562,7 +593,8 @@ func (js *jsre) dumpBlock(call otto.FunctionCall) otto.Value {
statedb := state.New(block.Root(), js.ethereum.StateDb()) statedb := state.New(block.Root(), js.ethereum.StateDb())
dump := statedb.RawDump() dump := statedb.RawDump()
return js.re.ToVal(dump) v, _ := call.Otto.ToValue(dump)
return v
} }
func (js *jsre) waitForBlocks(call otto.FunctionCall) otto.Value { func (js *jsre) waitForBlocks(call otto.FunctionCall) otto.Value {
...@@ -611,7 +643,8 @@ func (js *jsre) waitForBlocks(call otto.FunctionCall) otto.Value { ...@@ -611,7 +643,8 @@ func (js *jsre) waitForBlocks(call otto.FunctionCall) otto.Value {
return otto.UndefinedValue() return otto.UndefinedValue()
case height = <-wait: case height = <-wait:
} }
return js.re.ToVal(height.Uint64()) v, _ := call.Otto.ToValue(height.Uint64())
return v
} }
func (js *jsre) sleep(call otto.FunctionCall) otto.Value { func (js *jsre) sleep(call otto.FunctionCall) otto.Value {
...@@ -704,8 +737,8 @@ func (js *jsre) register(call otto.FunctionCall) otto.Value { ...@@ -704,8 +737,8 @@ func (js *jsre) register(call otto.FunctionCall) otto.Value {
return otto.UndefinedValue() return otto.UndefinedValue()
} }
return js.re.ToVal(contenthash.Hex()) v, _ := call.Otto.ToValue(contenthash.Hex())
return v
} }
func (js *jsre) registerUrl(call otto.FunctionCall) otto.Value { func (js *jsre) registerUrl(call otto.FunctionCall) otto.Value {
...@@ -764,7 +797,8 @@ func (js *jsre) getContractInfo(call otto.FunctionCall) otto.Value { ...@@ -764,7 +797,8 @@ func (js *jsre) getContractInfo(call otto.FunctionCall) otto.Value {
fmt.Println(err) fmt.Println(err)
return otto.UndefinedValue() return otto.UndefinedValue()
} }
return js.re.ToVal(info) v, _ := call.Otto.ToValue(info)
return v
} }
func (js *jsre) startNatSpec(call otto.FunctionCall) otto.Value { func (js *jsre) startNatSpec(call otto.FunctionCall) otto.Value {
......
...@@ -104,7 +104,7 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, interactive boo ...@@ -104,7 +104,7 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, interactive boo
func (js *jsre) apiBindings(f xeth.Frontend) { func (js *jsre) apiBindings(f xeth.Frontend) {
xe := xeth.New(js.ethereum, f) xe := xeth.New(js.ethereum, f)
ethApi := rpc.NewEthereumApi(xe) ethApi := rpc.NewEthereumApi(xe)
jeth := rpc.NewJeth(ethApi, js.re.ToVal, js.re) jeth := rpc.NewJeth(ethApi, js.re)
js.re.Set("jeth", struct{}{}) js.re.Set("jeth", struct{}{})
t, _ := js.re.Get("jeth") t, _ := js.re.Get("jeth")
......
...@@ -35,6 +35,7 @@ const ( ...@@ -35,6 +35,7 @@ const (
var ( var (
versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`)) versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`))
testNodeKey = crypto.ToECDSA(common.Hex2Bytes("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f"))
testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}` testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}`
) )
...@@ -72,6 +73,7 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) { ...@@ -72,6 +73,7 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
ks := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore")) ks := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore"))
am := accounts.NewManager(ks) am := accounts.NewManager(ks)
ethereum, err := eth.New(&eth.Config{ ethereum, err := eth.New(&eth.Config{
NodeKey: testNodeKey,
DataDir: tmp, DataDir: tmp,
AccountManager: am, AccountManager: am,
MaxPeers: 0, MaxPeers: 0,
...@@ -122,7 +124,7 @@ func TestNodeInfo(t *testing.T) { ...@@ -122,7 +124,7 @@ func TestNodeInfo(t *testing.T) {
} }
defer ethereum.Stop() defer ethereum.Stop()
defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
want := `{"DiscPort":0,"IP":"0.0.0.0","ListenAddr":"","Name":"test","NodeID":"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","NodeUrl":"enode://00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000@0.0.0.0:0","TCPPort":0,"Td":"0"}` want := `{"DiscPort":0,"IP":"0.0.0.0","ListenAddr":"","Name":"test","NodeID":"4cb2fc32924e94277bf94b5e4c983beedb2eabd5a0bc941db32202735c6625d020ca14a5963d1738af43b6ac0a711d61b1a06de931a499fe2aa0b1a132a902b5","NodeUrl":"enode://4cb2fc32924e94277bf94b5e4c983beedb2eabd5a0bc941db32202735c6625d020ca14a5963d1738af43b6ac0a711d61b1a06de931a499fe2aa0b1a132a902b5@0.0.0.0:0","TCPPort":0,"Td":"131072"}`
checkEvalJSON(t, repl, `admin.nodeInfo()`, want) checkEvalJSON(t, repl, `admin.nodeInfo()`, want)
} }
......
...@@ -48,7 +48,7 @@ import _ "net/http/pprof" ...@@ -48,7 +48,7 @@ import _ "net/http/pprof"
const ( const (
ClientIdentifier = "Geth" ClientIdentifier = "Geth"
Version = "0.9.24" Version = "0.9.25"
) )
var ( var (
...@@ -260,6 +260,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso ...@@ -260,6 +260,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.AutoDAGFlag, utils.AutoDAGFlag,
utils.NATFlag, utils.NATFlag,
utils.NatspecEnabledFlag, utils.NatspecEnabledFlag,
utils.NoDiscoverFlag,
utils.NodeKeyFileFlag, utils.NodeKeyFileFlag,
utils.NodeKeyHexFlag, utils.NodeKeyHexFlag,
utils.RPCEnabledFlag, utils.RPCEnabledFlag,
...@@ -532,9 +533,9 @@ func importchain(ctx *cli.Context) { ...@@ -532,9 +533,9 @@ func importchain(ctx *cli.Context) {
} }
// force database flush // force database flush
ethereum.BlockDb().Close() ethereum.BlockDb().Flush()
ethereum.StateDb().Close() ethereum.StateDb().Flush()
ethereum.ExtraDb().Close() ethereum.ExtraDb().Flush()
fmt.Printf("Import done in %v", time.Since(start)) fmt.Printf("Import done in %v", time.Since(start))
...@@ -629,9 +630,9 @@ func upgradeDb(ctx *cli.Context) { ...@@ -629,9 +630,9 @@ func upgradeDb(ctx *cli.Context) {
} }
// force database flush // force database flush
ethereum.BlockDb().Close() ethereum.BlockDb().Flush()
ethereum.StateDb().Close() ethereum.StateDb().Flush()
ethereum.ExtraDb().Close() ethereum.ExtraDb().Flush()
os.Remove(exportFile) os.Remove(exportFile)
......
...@@ -235,6 +235,10 @@ var ( ...@@ -235,6 +235,10 @@ var (
Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:<IP>)", Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:<IP>)",
Value: "any", Value: "any",
} }
NoDiscoverFlag = cli.BoolFlag{
Name: "nodiscover",
Usage: "Disables the peer discovery mechanism (manual peer addition)",
}
WhisperEnabledFlag = cli.BoolFlag{ WhisperEnabledFlag = cli.BoolFlag{
Name: "shh", Name: "shh",
Usage: "Enable whisper", Usage: "Enable whisper",
...@@ -312,6 +316,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config { ...@@ -312,6 +316,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
Port: ctx.GlobalString(ListenPortFlag.Name), Port: ctx.GlobalString(ListenPortFlag.Name),
NAT: GetNAT(ctx), NAT: GetNAT(ctx),
NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name), NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name),
Discovery: !ctx.GlobalBool(NoDiscoverFlag.Name),
NodeKey: GetNodeKey(ctx), NodeKey: GetNodeKey(ctx),
Shh: ctx.GlobalBool(WhisperEnabledFlag.Name), Shh: ctx.GlobalBool(WhisperEnabledFlag.Name),
Dial: true, Dial: true,
...@@ -320,7 +325,6 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config { ...@@ -320,7 +325,6 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
SolcPath: ctx.GlobalString(SolcPathFlag.Name), SolcPath: ctx.GlobalString(SolcPathFlag.Name),
AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name), AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name),
} }
} }
func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Database) { func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Database) {
......
...@@ -34,6 +34,8 @@ var ( ...@@ -34,6 +34,8 @@ var (
"file", // "file", //
"--natspec-dev", // Request to output the contract's Natspec developer documentation. "--natspec-dev", // Request to output the contract's Natspec developer documentation.
"file", "file",
"--add-std",
"1",
} }
) )
......
...@@ -21,7 +21,7 @@ import ( ...@@ -21,7 +21,7 @@ import (
const ( const (
// must be bumped when consensus algorithm is changed, this forces the upgradedb // must be bumped when consensus algorithm is changed, this forces the upgradedb
// command to be run (forces the blocks to be imported again using the new algorithm) // command to be run (forces the blocks to be imported again using the new algorithm)
BlockChainVersion = 2 BlockChainVersion = 3
) )
var receiptsPre = []byte("receipts-") var receiptsPre = []byte("receipts-")
...@@ -159,6 +159,9 @@ func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.Logs, err ...@@ -159,6 +159,9 @@ func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.Logs, err
return nil, ParentError(header.ParentHash) return nil, ParentError(header.ParentHash)
} }
parent := sm.bc.GetBlock(header.ParentHash) parent := sm.bc.GetBlock(header.ParentHash)
if !sm.Pow.Verify(block) {
return nil, ValidationError("Block's nonce is invalid (= %x)", block.Nonce)
}
return sm.processWithParent(block, parent) return sm.processWithParent(block, parent)
} }
...@@ -299,7 +302,7 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header, checkPow b ...@@ -299,7 +302,7 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header, checkPow b
a := new(big.Int).Sub(block.GasLimit, parent.GasLimit) a := new(big.Int).Sub(block.GasLimit, parent.GasLimit)
a.Abs(a) a.Abs(a)
b := new(big.Int).Div(parent.GasLimit, params.GasLimitBoundDivisor) b := new(big.Int).Div(parent.GasLimit, params.GasLimitBoundDivisor)
if !(a.Cmp(b) < 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) { if !(a.Cmp(b) <= 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) {
return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b) return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b)
} }
......
...@@ -750,7 +750,7 @@ out: ...@@ -750,7 +750,7 @@ out:
func blockErr(block *types.Block, err error) { func blockErr(block *types.Block, err error) {
h := block.Header() h := block.Header()
glog.V(logger.Error).Infof("INVALID block #%v (%x)\n", h.Number, h.Hash().Bytes()) glog.V(logger.Error).Infof("Bad block #%v (%x)\n", h.Number, h.Hash().Bytes())
glog.V(logger.Error).Infoln(err) glog.V(logger.Error).Infoln(err)
glog.V(logger.Debug).Infoln(block) glog.V(logger.Debug).Infoln(block)
} }
......
...@@ -25,6 +25,7 @@ var ( ...@@ -25,6 +25,7 @@ var (
ErrInsufficientFunds = errors.New("Insufficient funds for gas * price + value") ErrInsufficientFunds = errors.New("Insufficient funds for gas * price + value")
ErrIntrinsicGas = errors.New("Intrinsic gas too low") ErrIntrinsicGas = errors.New("Intrinsic gas too low")
ErrGasLimit = errors.New("Exceeds block gas limit") ErrGasLimit = errors.New("Exceeds block gas limit")
ErrNegativeValue = errors.New("Negative value")
) )
const txPoolQueueSize = 50 const txPoolQueueSize = 50
...@@ -125,6 +126,10 @@ func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error { ...@@ -125,6 +126,10 @@ func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
return ErrGasLimit return ErrGasLimit
} }
if tx.Amount.Cmp(common.Big0) < 0 {
return ErrNegativeValue
}
total := new(big.Int).Mul(tx.Price, tx.GasLimit) total := new(big.Int).Mul(tx.Price, tx.GasLimit)
total.Add(total, tx.Value()) total.Add(total, tx.Value())
if pool.currentState().GetBalance(from).Cmp(total) < 0 { if pool.currentState().GetBalance(from).Cmp(total) < 0 {
......
...@@ -138,3 +138,17 @@ func TestRemoveTx(t *testing.T) { ...@@ -138,3 +138,17 @@ func TestRemoveTx(t *testing.T) {
t.Error("expected txs to be 0, got", len(pool.txs)) t.Error("expected txs to be 0, got", len(pool.txs))
} }
} }
func TestNegativeValue(t *testing.T) {
pool, key := setupTxPool()
tx := transaction()
tx.Value().Set(big.NewInt(-1))
tx.SignECDSA(key)
from, _ := tx.From()
pool.currentState().AddBalance(from, big.NewInt(1))
err := pool.Add(tx)
if err != ErrNegativeValue {
t.Error("expected", ErrNegativeValue, "got", err)
}
}
...@@ -72,6 +72,7 @@ type Config struct { ...@@ -72,6 +72,7 @@ type Config struct {
MaxPeers int MaxPeers int
MaxPendingPeers int MaxPendingPeers int
Discovery bool
Port string Port string
// Space-separated list of discovery node URLs // Space-separated list of discovery node URLs
...@@ -311,6 +312,7 @@ func New(config *Config) (*Ethereum, error) { ...@@ -311,6 +312,7 @@ func New(config *Config) (*Ethereum, error) {
Name: config.Name, Name: config.Name,
MaxPeers: config.MaxPeers, MaxPeers: config.MaxPeers,
MaxPendingPeers: config.MaxPendingPeers, MaxPendingPeers: config.MaxPendingPeers,
Discovery: config.Discovery,
Protocols: protocols, Protocols: protocols,
NAT: config.NAT, NAT: config.NAT,
NoDial: !config.Dial, NoDial: !config.Dial,
...@@ -449,14 +451,10 @@ func (s *Ethereum) Start() error { ...@@ -449,14 +451,10 @@ func (s *Ethereum) Start() error {
ClientString: s.net.Name, ClientString: s.net.Name,
ProtocolVersion: ProtocolVersion, ProtocolVersion: ProtocolVersion,
}) })
err := s.net.Start()
if s.net.MaxPeers > 0 { if err != nil {
err := s.net.Start() return err
if err != nil {
return err
}
} }
// periodically flush databases // periodically flush databases
go s.syncDatabases() go s.syncDatabases()
......
...@@ -415,7 +415,7 @@ out: ...@@ -415,7 +415,7 @@ out:
peer.Demote() peer.Demote()
break break
} }
if glog.V(logger.Debug) { if glog.V(logger.Debug) && len(blockPack.blocks) > 0 {
glog.Infof("Added %d blocks from: %s\n", len(blockPack.blocks), blockPack.peerId) glog.Infof("Added %d blocks from: %s\n", len(blockPack.blocks), blockPack.peerId)
} }
// Promote the peer and update it's idle state // Promote the peer and update it's idle state
......
...@@ -70,7 +70,6 @@ func (pm *ProtocolManager) processBlocks() error { ...@@ -70,7 +70,6 @@ func (pm *ProtocolManager) processBlocks() error {
// Try to inset the blocks, drop the originating peer if there's an error // Try to inset the blocks, drop the originating peer if there's an error
index, err := pm.chainman.InsertChain(raw) index, err := pm.chainman.InsertChain(raw)
if err != nil { if err != nil {
glog.V(logger.Warn).Infof("Block insertion failed: %v", err)
pm.removePeer(blocks[index].OriginPeer) pm.removePeer(blocks[index].OriginPeer)
pm.downloader.Cancel() pm.downloader.Cancel()
return err return err
......
This diff is collapsed.
package jsre package jsre
import ( import (
"github.com/robertkrimen/otto"
"io/ioutil" "io/ioutil"
"os" "os"
"testing" "testing"
"time" "time"
"github.com/robertkrimen/otto"
) )
type testNativeObjectBinding struct { type testNativeObjectBinding struct{}
toVal func(interface{}) otto.Value
}
type msg struct { type msg struct {
Msg string Msg string
...@@ -21,7 +20,8 @@ func (no *testNativeObjectBinding) TestMethod(call otto.FunctionCall) otto.Value ...@@ -21,7 +20,8 @@ func (no *testNativeObjectBinding) TestMethod(call otto.FunctionCall) otto.Value
if err != nil { if err != nil {
return otto.UndefinedValue() return otto.UndefinedValue()
} }
return no.toVal(&msg{m}) v, _ := call.Otto.ToValue(&msg{m})
return v
} }
func TestExec(t *testing.T) { func TestExec(t *testing.T) {
...@@ -74,7 +74,7 @@ func TestNatto(t *testing.T) { ...@@ -74,7 +74,7 @@ func TestNatto(t *testing.T) {
func TestBind(t *testing.T) { func TestBind(t *testing.T) {
jsre := New("/tmp") jsre := New("/tmp")
jsre.Bind("no", &testNativeObjectBinding{jsre.ToVal}) jsre.Bind("no", &testNativeObjectBinding{})
val, err := jsre.Run(`no.TestMethod("testMsg")`) val, err := jsre.Run(`no.TestMethod("testMsg")`)
if err != nil { if err != nil {
......
...@@ -44,9 +44,10 @@ var ( ...@@ -44,9 +44,10 @@ var (
nodeDBVersionKey = []byte("version") // Version of the database to flush if changes nodeDBVersionKey = []byte("version") // Version of the database to flush if changes
nodeDBItemPrefix = []byte("n:") // Identifier to prefix node entries with nodeDBItemPrefix = []byte("n:") // Identifier to prefix node entries with
nodeDBDiscoverRoot = ":discover" nodeDBDiscoverRoot = ":discover"
nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping" nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping"
nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong" nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong"
nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail"
) )
// newNodeDB creates a new node database for storing and retrieving infos about // newNodeDB creates a new node database for storing and retrieving infos about
...@@ -275,6 +276,16 @@ func (db *nodeDB) updateLastPong(id NodeID, instance time.Time) error { ...@@ -275,6 +276,16 @@ func (db *nodeDB) updateLastPong(id NodeID, instance time.Time) error {
return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix()) return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix())
} }
// findFails retrieves the number of findnode failures since bonding.
func (db *nodeDB) findFails(id NodeID) int {
return int(db.fetchInt64(makeKey(id, nodeDBDiscoverFindFails)))
}
// updateFindFails updates the number of findnode failures since bonding.
func (db *nodeDB) updateFindFails(id NodeID, fails int) error {
return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails))
}
// querySeeds retrieves a batch of nodes to be used as potential seed servers // querySeeds retrieves a batch of nodes to be used as potential seed servers
// during bootstrapping the node into the network. // during bootstrapping the node into the network.
// //
......
...@@ -93,6 +93,7 @@ func TestNodeDBFetchStore(t *testing.T) { ...@@ -93,6 +93,7 @@ func TestNodeDBFetchStore(t *testing.T) {
30303, 30303,
) )
inst := time.Now() inst := time.Now()
num := 314
db, _ := newNodeDB("", Version, NodeID{}) db, _ := newNodeDB("", Version, NodeID{})
defer db.close() defer db.close()
...@@ -117,6 +118,16 @@ func TestNodeDBFetchStore(t *testing.T) { ...@@ -117,6 +118,16 @@ func TestNodeDBFetchStore(t *testing.T) {
if stored := db.lastPong(node.ID); stored.Unix() != inst.Unix() { if stored := db.lastPong(node.ID); stored.Unix() != inst.Unix() {
t.Errorf("pong: value mismatch: have %v, want %v", stored, inst) t.Errorf("pong: value mismatch: have %v, want %v", stored, inst)
} }
// Check fetch/store operations on a node findnode-failure object
if stored := db.findFails(node.ID); stored != 0 {
t.Errorf("find-node fails: non-existing object: %v", stored)
}
if err := db.updateFindFails(node.ID, num); err != nil {
t.Errorf("find-node fails: failed to update: %v", err)
}
if stored := db.findFails(node.ID); stored != num {
t.Errorf("find-node fails: value mismatch: have %v, want %v", stored, num)
}
// Check fetch/store operations on an actual node object // Check fetch/store operations on an actual node object
if stored := db.node(node.ID); stored != nil { if stored := db.node(node.ID); stored != nil {
t.Errorf("node: non-existing object: %v", stored) t.Errorf("node: non-existing object: %v", stored)
......
...@@ -27,6 +27,7 @@ const ( ...@@ -27,6 +27,7 @@ const (
nBuckets = hashBits + 1 // Number of buckets nBuckets = hashBits + 1 // Number of buckets
maxBondingPingPongs = 16 maxBondingPingPongs = 16
maxFindnodeFailures = 5
) )
type Table struct { type Table struct {
...@@ -190,6 +191,12 @@ func (tab *Table) Lookup(targetID NodeID) []*Node { ...@@ -190,6 +191,12 @@ func (tab *Table) Lookup(targetID NodeID) []*Node {
result := tab.closest(target, bucketSize) result := tab.closest(target, bucketSize)
tab.mutex.Unlock() tab.mutex.Unlock()
// If the result set is empty, all nodes were dropped, refresh
if len(result.entries) == 0 {
tab.refresh()
return nil
}
for { for {
// ask the alpha closest nodes that we haven't asked yet // ask the alpha closest nodes that we haven't asked yet
for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ { for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ {
...@@ -198,7 +205,19 @@ func (tab *Table) Lookup(targetID NodeID) []*Node { ...@@ -198,7 +205,19 @@ func (tab *Table) Lookup(targetID NodeID) []*Node {
asked[n.ID] = true asked[n.ID] = true
pendingQueries++ pendingQueries++
go func() { go func() {
r, _ := tab.net.findnode(n.ID, n.addr(), targetID) // Find potential neighbors to bond with
r, err := tab.net.findnode(n.ID, n.addr(), targetID)
if err != nil {
// Bump the failure counter to detect and evacuate non-bonded entries
fails := tab.db.findFails(n.ID) + 1
tab.db.updateFindFails(n.ID, fails)
glog.V(logger.Detail).Infof("Bumping failures for %x: %d", n.ID[:8], fails)
if fails >= maxFindnodeFailures {
glog.V(logger.Detail).Infof("Evacuating node %x: %d findnode failures", n.ID[:8], fails)
tab.del(n)
}
}
reply <- tab.bondall(r) reply <- tab.bondall(r)
}() }()
} }
...@@ -219,30 +238,53 @@ func (tab *Table) Lookup(targetID NodeID) []*Node { ...@@ -219,30 +238,53 @@ func (tab *Table) Lookup(targetID NodeID) []*Node {
return result.entries return result.entries
} }
// refresh performs a lookup for a random target to keep buckets full. // refresh performs a lookup for a random target to keep buckets full, or seeds
// the table if it is empty (initial bootstrap or discarded faulty peers).
func (tab *Table) refresh() { func (tab *Table) refresh() {
// The Kademlia paper specifies that the bucket refresh should seed := true
// perform a refresh in the least recently used bucket. We cannot
// adhere to this because the findnode target is a 512bit value // If the discovery table is empty, seed with previously known nodes
// (not hash-sized) and it is not easily possible to generate a tab.mutex.Lock()
// sha3 preimage that falls into a chosen bucket. for _, bucket := range tab.buckets {
// if len(bucket.entries) > 0 {
// We perform a lookup with a random target instead. seed = false
var target NodeID break
rand.Read(target[:]) }
result := tab.Lookup(target) }
if len(result) == 0 { tab.mutex.Unlock()
// If the table is not empty, try to refresh using the live entries
if !seed {
// The Kademlia paper specifies that the bucket refresh should
// perform a refresh in the least recently used bucket. We cannot
// adhere to this because the findnode target is a 512bit value
// (not hash-sized) and it is not easily possible to generate a
// sha3 preimage that falls into a chosen bucket.
//
// We perform a lookup with a random target instead.
var target NodeID
rand.Read(target[:])
result := tab.Lookup(target)
if len(result) == 0 {
// Lookup failed, seed after all
seed = true
}
}
if seed {
// Pick a batch of previously know seeds to lookup with // Pick a batch of previously know seeds to lookup with
seeds := tab.db.querySeeds(10) seeds := tab.db.querySeeds(10)
for _, seed := range seeds { for _, seed := range seeds {
glog.V(logger.Debug).Infoln("Seeding network with", seed) glog.V(logger.Debug).Infoln("Seeding network with", seed)
} }
// Bootstrap the table with a self lookup nodes := append(tab.nursery, seeds...)
all := tab.bondall(append(tab.nursery, seeds...))
tab.mutex.Lock() // Bond with all the seed nodes (will pingpong only if failed recently)
tab.add(all) bonded := tab.bondall(nodes)
tab.mutex.Unlock() if len(bonded) > 0 {
tab.Lookup(tab.self.ID) tab.Lookup(tab.self.ID)
}
// TODO: the Kademlia paper says that we're supposed to perform // TODO: the Kademlia paper says that we're supposed to perform
// random lookups in all buckets further away than our closest neighbor. // random lookups in all buckets further away than our closest neighbor.
} }
...@@ -305,8 +347,16 @@ func (tab *Table) bondall(nodes []*Node) (result []*Node) { ...@@ -305,8 +347,16 @@ func (tab *Table) bondall(nodes []*Node) (result []*Node) {
// If pinged is true, the remote node has just pinged us and one half // If pinged is true, the remote node has just pinged us and one half
// of the process can be skipped. // of the process can be skipped.
func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) (*Node, error) { func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) (*Node, error) {
var n *Node // Retrieve a previously known node and any recent findnode failures
if n = tab.db.node(id); n == nil { node, fails := tab.db.node(id), 0
if node != nil {
fails = tab.db.findFails(id)
}
// If the node is unknown (non-bonded) or failed (remotely unknown), bond from scratch
var result error
if node == nil || fails > 0 {
glog.V(logger.Detail).Infof("Bonding %x: known=%v, fails=%v", id[:8], node != nil, fails)
tab.bondmu.Lock() tab.bondmu.Lock()
w := tab.bonding[id] w := tab.bonding[id]
if w != nil { if w != nil {
...@@ -325,18 +375,24 @@ func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16 ...@@ -325,18 +375,24 @@ func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16
delete(tab.bonding, id) delete(tab.bonding, id)
tab.bondmu.Unlock() tab.bondmu.Unlock()
} }
n = w.n // Retrieve the bonding results
if w.err != nil { result = w.err
return nil, w.err if result == nil {
node = w.n
} }
} }
tab.mutex.Lock() // Even if bonding temporarily failed, give the node a chance
defer tab.mutex.Unlock() if node != nil {
b := tab.buckets[logdist(tab.self.sha, n.sha)] tab.mutex.Lock()
if !b.bump(n) { defer tab.mutex.Unlock()
tab.pingreplace(n, b)
b := tab.buckets[logdist(tab.self.sha, node.sha)]
if !b.bump(node) {
tab.pingreplace(node, b)
}
tab.db.updateFindFails(id, 0)
} }
return n, nil return node, result
} }
func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) { func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) {
...@@ -414,6 +470,21 @@ outer: ...@@ -414,6 +470,21 @@ outer:
} }
} }
// del removes an entry from the node table (used to evacuate failed/non-bonded
// discovery peers).
func (tab *Table) del(node *Node) {
tab.mutex.Lock()
defer tab.mutex.Unlock()
bucket := tab.buckets[logdist(tab.self.sha, node.sha)]
for i := range bucket.entries {
if bucket.entries[i].ID == node.ID {
bucket.entries = append(bucket.entries[:i], bucket.entries[i+1:]...)
return
}
}
}
func (b *bucket) bump(n *Node) bool { func (b *bucket) bump(n *Node) bool {
for i := range b.entries { for i := range b.entries {
if b.entries[i].ID == n.ID { if b.entries[i].ID == n.ID {
......
...@@ -55,6 +55,10 @@ type Server struct { ...@@ -55,6 +55,10 @@ type Server struct {
// Zero defaults to preset values. // Zero defaults to preset values.
MaxPendingPeers int MaxPendingPeers int
// Discovery specifies whether the peer discovery mechanism should be started
// or not. Disabling is usually useful for protocol debugging (manual topology).
Discovery bool
// Name sets the node name of this server. // Name sets the node name of this server.
// Use common.MakeName to create a name that follows existing conventions. // Use common.MakeName to create a name that follows existing conventions.
Name string Name string
...@@ -237,9 +241,26 @@ func (srv *Server) AddPeer(node *discover.Node) { ...@@ -237,9 +241,26 @@ func (srv *Server) AddPeer(node *discover.Node) {
func (srv *Server) Self() *discover.Node { func (srv *Server) Self() *discover.Node {
srv.lock.Lock() srv.lock.Lock()
defer srv.lock.Unlock() defer srv.lock.Unlock()
// If the server's not running, return an empty node
if !srv.running { if !srv.running {
return &discover.Node{IP: net.ParseIP("0.0.0.0")} return &discover.Node{IP: net.ParseIP("0.0.0.0")}
} }
// If the node is running but discovery is off, manually assemble the node infos
if srv.ntab == nil {
// Inbound connections disabled, use zero address
if srv.listener == nil {
return &discover.Node{IP: net.ParseIP("0.0.0.0"), ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)}
}
// Otherwise inject the listener address too
addr := srv.listener.Addr().(*net.TCPAddr)
return &discover.Node{
ID: discover.PubkeyID(&srv.PrivateKey.PublicKey),
IP: addr.IP,
TCP: uint16(addr.Port),
}
}
// Otherwise return the live node infos
return srv.ntab.Self() return srv.ntab.Self()
} }
...@@ -275,9 +296,6 @@ func (srv *Server) Start() (err error) { ...@@ -275,9 +296,6 @@ func (srv *Server) Start() (err error) {
if srv.PrivateKey == nil { if srv.PrivateKey == nil {
return fmt.Errorf("Server.PrivateKey must be set to a non-nil key") return fmt.Errorf("Server.PrivateKey must be set to a non-nil key")
} }
if srv.MaxPeers <= 0 {
return fmt.Errorf("Server.MaxPeers must be > 0")
}
if srv.newTransport == nil { if srv.newTransport == nil {
srv.newTransport = newRLPX srv.newTransport = newRLPX
} }
...@@ -293,15 +311,22 @@ func (srv *Server) Start() (err error) { ...@@ -293,15 +311,22 @@ func (srv *Server) Start() (err error) {
srv.peerOpDone = make(chan struct{}) srv.peerOpDone = make(chan struct{})
// node table // node table
ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT, srv.NodeDatabase) if srv.Discovery {
if err != nil { ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT, srv.NodeDatabase)
return err if err != nil {
return err
}
srv.ntab = ntab
} }
srv.ntab = ntab
dialer := newDialState(srv.StaticNodes, srv.ntab, srv.MaxPeers/2) dynPeers := srv.MaxPeers / 2
if !srv.Discovery {
dynPeers = 0
}
dialer := newDialState(srv.StaticNodes, srv.ntab, dynPeers)
// handshake // handshake
srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: ntab.Self().ID} srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)}
for _, p := range srv.Protocols { for _, p := range srv.Protocols {
srv.ourHandshake.Caps = append(srv.ourHandshake.Caps, p.cap()) srv.ourHandshake.Caps = append(srv.ourHandshake.Caps, p.cap())
} }
...@@ -457,7 +482,9 @@ running: ...@@ -457,7 +482,9 @@ running:
} }
// Terminate discovery. If there is a running lookup it will terminate soon. // Terminate discovery. If there is a running lookup it will terminate soon.
srv.ntab.Close() if srv.ntab != nil {
srv.ntab.Close()
}
// Disconnect all peers. // Disconnect all peers.
for _, p := range peers { for _, p := range peers {
p.Disconnect(DiscQuitting) p.Disconnect(DiscQuitting)
...@@ -489,7 +516,7 @@ func (srv *Server) encHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) ...@@ -489,7 +516,7 @@ func (srv *Server) encHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn)
return DiscTooManyPeers return DiscTooManyPeers
case peers[c.id] != nil: case peers[c.id] != nil:
return DiscAlreadyConnected return DiscAlreadyConnected
case c.id == srv.ntab.Self().ID: case c.id == srv.Self().ID:
return DiscSelf return DiscSelf
default: default:
return nil return nil
......
...@@ -3,18 +3,18 @@ package rpc ...@@ -3,18 +3,18 @@ package rpc
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/jsre" "github.com/ethereum/go-ethereum/jsre"
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
) )
type Jeth struct { type Jeth struct {
ethApi *EthereumApi ethApi *EthereumApi
toVal func(interface{}) otto.Value
re *jsre.JSRE re *jsre.JSRE
} }
func NewJeth(ethApi *EthereumApi, toVal func(interface{}) otto.Value, re *jsre.JSRE) *Jeth { func NewJeth(ethApi *EthereumApi, re *jsre.JSRE) *Jeth {
return &Jeth{ethApi, toVal, re} return &Jeth{ethApi, re}
} }
func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) { func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment