Merge branch 'release/0.9.25'
This commit is contained in:
commit
70867904a0
@ -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) {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
}
|
}
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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
|
|||||||
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 (
|
|||||||
|
|
||||||
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) {
|
|||||||
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(ð.Config{
|
ethereum, err := eth.New(ð.Config{
|
||||||
|
NodeKey: testNodeKey,
|
||||||
DataDir: tmp,
|
DataDir: tmp,
|
||||||
AccountManager: am,
|
AccountManager: am,
|
||||||
MaxPeers: 0,
|
MaxPeers: 0,
|
||||||
@ -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"
|
|||||||
|
|
||||||
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
|
|||||||
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) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 (
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 (
|
|||||||
"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 (
|
|||||||
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
|
|||||||
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
|
|||||||
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:
|
|||||||
|
|
||||||
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 (
|
|||||||
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 {
|
|||||||
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) {
|
|||||||
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 {
|
|||||||
|
|
||||||
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) {
|
|||||||
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 {
|
|||||||
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:
|
|||||||
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 {
|
|||||||
// 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
|
||||||
|
239
jsre/jsre.go
239
jsre/jsre.go
@ -19,9 +19,7 @@ It provides some helper functions to
|
|||||||
- bind native go objects
|
- bind native go objects
|
||||||
*/
|
*/
|
||||||
type JSRE struct {
|
type JSRE struct {
|
||||||
assetPath string
|
assetPath string
|
||||||
vm *otto.Otto
|
|
||||||
|
|
||||||
evalQueue chan *evalReq
|
evalQueue chan *evalReq
|
||||||
stopEventLoop chan bool
|
stopEventLoop chan bool
|
||||||
loopWg sync.WaitGroup
|
loopWg sync.WaitGroup
|
||||||
@ -35,68 +33,37 @@ type jsTimer struct {
|
|||||||
call otto.FunctionCall
|
call otto.FunctionCall
|
||||||
}
|
}
|
||||||
|
|
||||||
// evalResult is a structure to store the result of any serialized vm execution
|
// evalReq is a serialized vm execution request processed by runEventLoop.
|
||||||
type evalResult struct {
|
|
||||||
result otto.Value
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// evalReq is a serialized vm execution request put in evalQueue and processed by runEventLoop
|
|
||||||
type evalReq struct {
|
type evalReq struct {
|
||||||
fn func(res *evalResult)
|
fn func(vm *otto.Otto)
|
||||||
done chan bool
|
done chan bool
|
||||||
res evalResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtime must be stopped with Stop() after use and cannot be used after stopping
|
// runtime must be stopped with Stop() after use and cannot be used after stopping
|
||||||
func New(assetPath string) *JSRE {
|
func New(assetPath string) *JSRE {
|
||||||
re := &JSRE{
|
re := &JSRE{
|
||||||
assetPath: assetPath,
|
assetPath: assetPath,
|
||||||
vm: otto.New(),
|
evalQueue: make(chan *evalReq),
|
||||||
|
stopEventLoop: make(chan bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
// load prettyprint func definition
|
|
||||||
re.vm.Run(pp_js)
|
|
||||||
re.vm.Set("loadScript", re.loadScript)
|
|
||||||
|
|
||||||
re.evalQueue = make(chan *evalReq)
|
|
||||||
re.stopEventLoop = make(chan bool)
|
|
||||||
re.loopWg.Add(1)
|
re.loopWg.Add(1)
|
||||||
go re.runEventLoop()
|
go re.runEventLoop()
|
||||||
|
re.Compile("pp.js", pp_js) // load prettyprint func definition
|
||||||
|
re.Set("loadScript", re.loadScript)
|
||||||
return re
|
return re
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function runs a piece of JS code either in a serialized way (when useEQ is true) or instantly, circumventing the evalQueue
|
// This function runs the main event loop from a goroutine that is started
|
||||||
func (self *JSRE) run(src interface{}, useEQ bool) (value otto.Value, err error) {
|
// when JSRE is created. Use Stop() before exiting to properly stop it.
|
||||||
if useEQ {
|
// The event loop processes vm access requests from the evalQueue in a
|
||||||
done := make(chan bool)
|
// serialized way and calls timer callback functions at the appropriate time.
|
||||||
req := &evalReq{
|
|
||||||
fn: func(res *evalResult) {
|
|
||||||
res.result, res.err = self.vm.Run(src)
|
|
||||||
},
|
|
||||||
done: done,
|
|
||||||
}
|
|
||||||
self.evalQueue <- req
|
|
||||||
<-done
|
|
||||||
return req.res.result, req.res.err
|
|
||||||
} else {
|
|
||||||
return self.vm.Run(src)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
// Exported functions always access the vm through the event queue. You can
|
||||||
This function runs the main event loop from a goroutine that is started
|
// call the functions of the otto vm directly to circumvent the queue. These
|
||||||
when JSRE is created. Use Stop() before exiting to properly stop it.
|
// functions should be used if and only if running a routine that was already
|
||||||
The event loop processes vm access requests from the evalQueue in a
|
// called from JS through an RPC call.
|
||||||
serialized way and calls timer callback functions at the appropriate time.
|
|
||||||
|
|
||||||
Exported functions always access the vm through the event queue. You can
|
|
||||||
call the functions of the otto vm directly to circumvent the queue. These
|
|
||||||
functions should be used if and only if running a routine that was already
|
|
||||||
called from JS through an RPC call.
|
|
||||||
*/
|
|
||||||
func (self *JSRE) runEventLoop() {
|
func (self *JSRE) runEventLoop() {
|
||||||
|
vm := otto.New()
|
||||||
registry := map[*jsTimer]*jsTimer{}
|
registry := map[*jsTimer]*jsTimer{}
|
||||||
ready := make(chan *jsTimer)
|
ready := make(chan *jsTimer)
|
||||||
|
|
||||||
@ -143,10 +110,10 @@ func (self *JSRE) runEventLoop() {
|
|||||||
}
|
}
|
||||||
return otto.UndefinedValue()
|
return otto.UndefinedValue()
|
||||||
}
|
}
|
||||||
self.vm.Set("setTimeout", setTimeout)
|
vm.Set("setTimeout", setTimeout)
|
||||||
self.vm.Set("setInterval", setInterval)
|
vm.Set("setInterval", setInterval)
|
||||||
self.vm.Set("clearTimeout", clearTimeout)
|
vm.Set("clearTimeout", clearTimeout)
|
||||||
self.vm.Set("clearInterval", clearTimeout)
|
vm.Set("clearInterval", clearTimeout)
|
||||||
|
|
||||||
var waitForCallbacks bool
|
var waitForCallbacks bool
|
||||||
|
|
||||||
@ -166,8 +133,7 @@ loop:
|
|||||||
arguments = make([]interface{}, 1)
|
arguments = make([]interface{}, 1)
|
||||||
}
|
}
|
||||||
arguments[0] = timer.call.ArgumentList[0]
|
arguments[0] = timer.call.ArgumentList[0]
|
||||||
_, err := self.vm.Call(`Function.call.call`, nil, arguments...)
|
_, err := vm.Call(`Function.call.call`, nil, arguments...)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("js error:", err, arguments)
|
fmt.Println("js error:", err, arguments)
|
||||||
}
|
}
|
||||||
@ -179,10 +145,10 @@ loop:
|
|||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case evalReq := <-self.evalQueue:
|
case req := <-self.evalQueue:
|
||||||
// run the code, send the result back
|
// run the code, send the result back
|
||||||
evalReq.fn(&evalReq.res)
|
req.fn(vm)
|
||||||
close(evalReq.done)
|
close(req.done)
|
||||||
if waitForCallbacks && (len(registry) == 0) {
|
if waitForCallbacks && (len(registry) == 0) {
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
@ -201,6 +167,14 @@ loop:
|
|||||||
self.loopWg.Done()
|
self.loopWg.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do schedules the given function on the event loop.
|
||||||
|
func (self *JSRE) do(fn func(*otto.Otto)) {
|
||||||
|
done := make(chan bool)
|
||||||
|
req := &evalReq{fn, done}
|
||||||
|
self.evalQueue <- req
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
|
||||||
// stops the event loop before exit, optionally waits for all timers to expire
|
// stops the event loop before exit, optionally waits for all timers to expire
|
||||||
func (self *JSRE) Stop(waitForCallbacks bool) {
|
func (self *JSRE) Stop(waitForCallbacks bool) {
|
||||||
self.stopEventLoop <- waitForCallbacks
|
self.stopEventLoop <- waitForCallbacks
|
||||||
@ -210,119 +184,78 @@ func (self *JSRE) Stop(waitForCallbacks bool) {
|
|||||||
// Exec(file) loads and runs the contents of a file
|
// Exec(file) loads and runs the contents of a file
|
||||||
// if a relative path is given, the jsre's assetPath is used
|
// if a relative path is given, the jsre's assetPath is used
|
||||||
func (self *JSRE) Exec(file string) error {
|
func (self *JSRE) Exec(file string) error {
|
||||||
return self.exec(common.AbsolutePath(self.assetPath, file), true)
|
code, err := ioutil.ReadFile(common.AbsolutePath(self.assetPath, file))
|
||||||
}
|
|
||||||
|
|
||||||
// circumvents the eval queue, see runEventLoop
|
|
||||||
func (self *JSRE) execWithoutEQ(file string) error {
|
|
||||||
return self.exec(common.AbsolutePath(self.assetPath, file), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *JSRE) exec(path string, useEQ bool) error {
|
|
||||||
code, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = self.run(code, useEQ)
|
self.do(func(vm *otto.Otto) { _, err = vm.Run(code) })
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// assigns value v to a variable in the JS environment
|
// Bind assigns value v to a variable in the JS environment
|
||||||
func (self *JSRE) Bind(name string, v interface{}) (err error) {
|
// This method is deprecated, use Set.
|
||||||
self.Set(name, v)
|
func (self *JSRE) Bind(name string, v interface{}) error {
|
||||||
return
|
return self.Set(name, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// runs a piece of JS code
|
// Run runs a piece of JS code.
|
||||||
func (self *JSRE) Run(code string) (otto.Value, error) {
|
func (self *JSRE) Run(code string) (v otto.Value, err error) {
|
||||||
return self.run(code, true)
|
self.do(func(vm *otto.Otto) { v, err = vm.Run(code) })
|
||||||
|
return v, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the value of a variable in the JS environment
|
// Get returns the value of a variable in the JS environment.
|
||||||
func (self *JSRE) Get(ns string) (otto.Value, error) {
|
func (self *JSRE) Get(ns string) (v otto.Value, err error) {
|
||||||
done := make(chan bool)
|
self.do(func(vm *otto.Otto) { v, err = vm.Get(ns) })
|
||||||
req := &evalReq{
|
return v, err
|
||||||
fn: func(res *evalResult) {
|
|
||||||
res.result, res.err = self.vm.Get(ns)
|
|
||||||
},
|
|
||||||
done: done,
|
|
||||||
}
|
|
||||||
self.evalQueue <- req
|
|
||||||
<-done
|
|
||||||
return req.res.result, req.res.err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// assigns value v to a variable in the JS environment
|
// Set assigns value v to a variable in the JS environment.
|
||||||
func (self *JSRE) Set(ns string, v interface{}) error {
|
func (self *JSRE) Set(ns string, v interface{}) (err error) {
|
||||||
done := make(chan bool)
|
self.do(func(vm *otto.Otto) { err = vm.Set(ns, v) })
|
||||||
req := &evalReq{
|
return err
|
||||||
fn: func(res *evalResult) {
|
|
||||||
res.err = self.vm.Set(ns, v)
|
|
||||||
},
|
|
||||||
done: done,
|
|
||||||
}
|
|
||||||
self.evalQueue <- req
|
|
||||||
<-done
|
|
||||||
return req.res.err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// loadScript executes a JS script from inside the currently executing JS code.
|
||||||
Executes a JS script from inside the currently executing JS code.
|
|
||||||
Should only be called from inside an RPC routine.
|
|
||||||
*/
|
|
||||||
func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value {
|
func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value {
|
||||||
file, err := call.Argument(0).ToString()
|
file, err := call.Argument(0).ToString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// TODO: throw exception
|
||||||
return otto.FalseValue()
|
return otto.FalseValue()
|
||||||
}
|
}
|
||||||
if err := self.execWithoutEQ(file); err != nil { // loadScript is only called from inside js
|
file = common.AbsolutePath(self.assetPath, file)
|
||||||
|
source, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: throw exception
|
||||||
|
return otto.FalseValue()
|
||||||
|
}
|
||||||
|
if _, err := compileAndRun(call.Otto, file, source); err != nil {
|
||||||
|
// TODO: throw exception
|
||||||
fmt.Println("err:", err)
|
fmt.Println("err:", err)
|
||||||
return otto.FalseValue()
|
return otto.FalseValue()
|
||||||
}
|
}
|
||||||
|
// TODO: return evaluation result
|
||||||
return otto.TrueValue()
|
return otto.TrueValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
// uses the "prettyPrint" JS function to format a value
|
// PrettyPrint writes v to standard output.
|
||||||
func (self *JSRE) PrettyPrint(v interface{}) (val otto.Value, err error) {
|
func (self *JSRE) PrettyPrint(v interface{}) (val otto.Value, err error) {
|
||||||
var method otto.Value
|
var method otto.Value
|
||||||
v, err = self.ToValue(v)
|
self.do(func(vm *otto.Otto) {
|
||||||
if err != nil {
|
val, err = vm.ToValue(v)
|
||||||
return
|
if err != nil {
|
||||||
}
|
return
|
||||||
method, err = self.vm.Get("prettyPrint")
|
}
|
||||||
if err != nil {
|
method, err = vm.Get("prettyPrint")
|
||||||
return
|
if err != nil {
|
||||||
}
|
return
|
||||||
return method.Call(method, v)
|
}
|
||||||
|
val, err = method.Call(method, val)
|
||||||
|
})
|
||||||
|
return val, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates an otto value from a go type (serialized version)
|
// Eval evaluates JS function and returns result in a pretty printed string format.
|
||||||
func (self *JSRE) ToValue(v interface{}) (otto.Value, error) {
|
|
||||||
done := make(chan bool)
|
|
||||||
req := &evalReq{
|
|
||||||
fn: func(res *evalResult) {
|
|
||||||
res.result, res.err = self.vm.ToValue(v)
|
|
||||||
},
|
|
||||||
done: done,
|
|
||||||
}
|
|
||||||
self.evalQueue <- req
|
|
||||||
<-done
|
|
||||||
return req.res.result, req.res.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// creates an otto value from a go type (non-serialized version)
|
|
||||||
func (self *JSRE) ToVal(v interface{}) otto.Value {
|
|
||||||
|
|
||||||
result, err := self.vm.ToValue(v)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Value unknown:", err)
|
|
||||||
return otto.UndefinedValue()
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// evaluates JS function and returns result in a pretty printed string format
|
|
||||||
func (self *JSRE) Eval(code string) (s string, err error) {
|
func (self *JSRE) Eval(code string) (s string, err error) {
|
||||||
var val otto.Value
|
var val otto.Value
|
||||||
val, err = self.Run(code)
|
val, err = self.Run(code)
|
||||||
@ -336,12 +269,16 @@ func (self *JSRE) Eval(code string) (s string, err error) {
|
|||||||
return fmt.Sprintf("%v", val), nil
|
return fmt.Sprintf("%v", val), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// compiles and then runs a piece of JS code
|
// Compile compiles and then runs a piece of JS code.
|
||||||
func (self *JSRE) Compile(fn string, src interface{}) error {
|
func (self *JSRE) Compile(filename string, src interface{}) (err error) {
|
||||||
script, err := self.vm.Compile(fn, src)
|
self.do(func(vm *otto.Otto) { _, err = compileAndRun(vm, filename, src) })
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
self.run(script, true)
|
func compileAndRun(vm *otto.Otto, filename string, src interface{}) (otto.Value, error) {
|
||||||
return nil
|
script, err := vm.Compile(filename, src)
|
||||||
|
if err != nil {
|
||||||
|
return otto.Value{}, err
|
||||||
|
}
|
||||||
|
return vm.Run(script)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
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
|
|||||||
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) {
|
|||||||
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 (
|
|||||||
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 {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
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 (
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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) {
|
|||||||
// 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
|
|||||||
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:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
|||||||
// 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) {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
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:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
|||||||
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
|
|||||||
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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user