Merge branch 'release/0.9.23'

This commit is contained in:
obscuren 2015-05-21 18:58:57 +02:00
commit 2c1c78a6d9
43 changed files with 12316 additions and 954 deletions

@ -8,6 +8,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -35,6 +36,7 @@ func (js *jsre) adminBindings() {
eth := ethO.Object() eth := ethO.Object()
eth.Set("pendingTransactions", js.pendingTransactions) eth.Set("pendingTransactions", js.pendingTransactions)
eth.Set("resend", js.resend) eth.Set("resend", js.resend)
eth.Set("sign", js.sign)
js.re.Set("admin", struct{}{}) js.re.Set("admin", struct{}{})
t, _ := js.re.Get("admin") t, _ := js.re.Get("admin")
@ -72,6 +74,9 @@ func (js *jsre) adminBindings() {
miner.Set("hashrate", js.hashrate) miner.Set("hashrate", js.hashrate)
miner.Set("setExtra", js.setExtra) miner.Set("setExtra", js.setExtra)
miner.Set("setGasPrice", js.setGasPrice) miner.Set("setGasPrice", js.setGasPrice)
miner.Set("startAutoDAG", js.startAutoDAG)
miner.Set("stopAutoDAG", js.stopAutoDAG)
miner.Set("makeDAG", js.makeDAG)
admin.Set("debug", struct{}{}) admin.Set("debug", struct{}{})
t, _ = admin.Get("debug") t, _ = admin.Get("debug")
@ -177,6 +182,30 @@ func (js *jsre) resend(call otto.FunctionCall) otto.Value {
return otto.FalseValue() return otto.FalseValue()
} }
func (js *jsre) sign(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) != 2 {
fmt.Println("requires 2 arguments: eth.sign(signer, data)")
return otto.UndefinedValue()
}
signer, err := call.Argument(0).ToString()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
data, err := call.Argument(1).ToString()
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
v, err := js.xeth.Sign(signer, data, false)
if err != nil {
fmt.Println(err)
return otto.UndefinedValue()
}
return js.re.ToVal(v)
}
func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value { func (js *jsre) debugBlock(call otto.FunctionCall) otto.Value {
block, err := js.getBlock(call) block, err := js.getBlock(call)
if err != nil { if err != nil {
@ -253,6 +282,30 @@ func (js *jsre) hashrate(otto.FunctionCall) otto.Value {
return js.re.ToVal(js.ethereum.Miner().HashRate()) return js.re.ToVal(js.ethereum.Miner().HashRate())
} }
func (js *jsre) makeDAG(call otto.FunctionCall) otto.Value {
blockNumber, err := call.Argument(1).ToInteger()
if err != nil {
fmt.Println(err)
return otto.FalseValue()
}
err = ethash.MakeDAG(uint64(blockNumber), "")
if err != nil {
return otto.FalseValue()
}
return otto.TrueValue()
}
func (js *jsre) startAutoDAG(otto.FunctionCall) otto.Value {
js.ethereum.StartAutoDAG()
return otto.TrueValue()
}
func (js *jsre) stopAutoDAG(otto.FunctionCall) otto.Value {
js.ethereum.StopAutoDAG()
return otto.TrueValue()
}
func (js *jsre) backtrace(call otto.FunctionCall) otto.Value { func (js *jsre) backtrace(call otto.FunctionCall) otto.Value {
tracestr, err := call.Argument(0).ToString() tracestr, err := call.Argument(0).ToString()
if err != nil { if err != nil {
@ -291,6 +344,9 @@ func (js *jsre) startMining(call otto.FunctionCall) otto.Value {
threads = int64(js.ethereum.MinerThreads) threads = int64(js.ethereum.MinerThreads)
} }
// switch on DAG autogeneration when miner starts
js.ethereum.StartAutoDAG()
err = js.ethereum.StartMining(int(threads)) err = js.ethereum.StartMining(int(threads))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -302,6 +358,7 @@ func (js *jsre) startMining(call otto.FunctionCall) otto.Value {
func (js *jsre) stopMining(call otto.FunctionCall) otto.Value { func (js *jsre) stopMining(call otto.FunctionCall) otto.Value {
js.ethereum.StopMining() js.ethereum.StopMining()
js.ethereum.StopAutoDAG()
return otto.TrueValue() return otto.TrueValue()
} }
@ -383,7 +440,7 @@ func (js *jsre) unlock(call otto.FunctionCall) otto.Value {
var passphrase string var passphrase string
if arg.IsUndefined() { if arg.IsUndefined() {
fmt.Println("Please enter a passphrase now.") fmt.Println("Please enter a passphrase now.")
passphrase, err = readPassword("Passphrase: ", true) passphrase, err = utils.PromptPassword("Passphrase: ", true)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return otto.FalseValue() return otto.FalseValue()
@ -410,12 +467,12 @@ func (js *jsre) newAccount(call otto.FunctionCall) otto.Value {
if arg.IsUndefined() { if arg.IsUndefined() {
fmt.Println("The new account will be encrypted with a passphrase.") fmt.Println("The new account will be encrypted with a passphrase.")
fmt.Println("Please enter a passphrase now.") fmt.Println("Please enter a passphrase now.")
auth, err := readPassword("Passphrase: ", true) auth, err := utils.PromptPassword("Passphrase: ", true)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return otto.FalseValue() return otto.FalseValue()
} }
confirm, err := readPassword("Repeat Passphrase: ", false) confirm, err := utils.PromptPassword("Repeat Passphrase: ", false)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return otto.FalseValue() return otto.FalseValue()

@ -1 +1 @@
{"code":"605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056","info":{"abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"compilerVersion":"0.9.17","developerDoc":{"methods":{}},"language":"Solidity","languageVersion":"0","source":"contract test {\n /// @notice Will multiply `a` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply `a` by 7."}}}}} {"code":"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056","info":{"abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"compilerVersion":"0.9.23","developerDoc":{"methods":{}},"language":"Solidity","languageVersion":"0","source":"contract test {\n /// @notice Will multiply `a` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply `a` by 7."}}}}}

@ -71,7 +71,7 @@ type jsre struct {
prompter prompter
} }
func newJSRE(ethereum *eth.Ethereum, libPath, solcPath, corsDomain string, interactive bool, f xeth.Frontend) *jsre { func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, interactive bool, f xeth.Frontend) *jsre {
js := &jsre{ethereum: ethereum, ps1: "> "} js := &jsre{ethereum: ethereum, ps1: "> "}
// set default cors domain used by startRpc from CLI flag // set default cors domain used by startRpc from CLI flag
js.corsDomain = corsDomain js.corsDomain = corsDomain
@ -81,7 +81,6 @@ func newJSRE(ethereum *eth.Ethereum, libPath, solcPath, corsDomain string, inter
js.xeth = xeth.New(ethereum, f) js.xeth = xeth.New(ethereum, f)
js.wait = js.xeth.UpdateState() js.wait = js.xeth.UpdateState()
// update state in separare forever blocks // update state in separare forever blocks
js.xeth.SetSolc(solcPath)
js.re = re.New(libPath) js.re = re.New(libPath)
js.apiBindings(f) js.apiBindings(f)
js.adminBindings() js.adminBindings()

@ -24,7 +24,7 @@ import (
const ( const (
testSolcPath = "" testSolcPath = ""
solcVersion = "0.9.17" solcVersion = "0.9.23"
testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674" testKey = "e6fab74a43941f82d89cb7faa408e227cdad3153c4720e540e855c19b15e6674"
testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
@ -34,6 +34,7 @@ const (
) )
var ( var (
versionRE = regexp.MustCompile(strconv.Quote(`"compilerVersion":"` + solcVersion + `"`))
testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}` testGenesis = `{"` + testAddress[2:] + `": {"balance": "` + testBalance + `"}}`
) )
@ -75,6 +76,7 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
AccountManager: am, AccountManager: am,
MaxPeers: 0, MaxPeers: 0,
Name: "test", Name: "test",
SolcPath: testSolcPath,
}) })
if err != nil { if err != nil {
t.Fatal("%v", err) t.Fatal("%v", err)
@ -101,7 +103,7 @@ func testJEthRE(t *testing.T) (string, *testjethre, *eth.Ethereum) {
t.Errorf("Error creating DocServer: %v", err) t.Errorf("Error creating DocServer: %v", err)
} }
tf := &testjethre{ds: ds, stateDb: ethereum.ChainManager().State().Copy()} tf := &testjethre{ds: ds, stateDb: ethereum.ChainManager().State().Copy()}
repl := newJSRE(ethereum, assetPath, testSolcPath, "", false, tf) repl := newJSRE(ethereum, assetPath, "", false, tf)
tf.jsre = repl tf.jsre = repl
return tmp, tf, ethereum return tmp, tf, ethereum
} }
@ -172,6 +174,8 @@ func TestBlockChain(t *testing.T) {
tmpfile := filepath.Join(extmp, "export.chain") tmpfile := filepath.Join(extmp, "export.chain")
tmpfileq := strconv.Quote(tmpfile) tmpfileq := strconv.Quote(tmpfile)
ethereum.ChainManager().Reset()
checkEvalJSON(t, repl, `admin.export(`+tmpfileq+`)`, `true`) checkEvalJSON(t, repl, `admin.export(`+tmpfileq+`)`, `true`)
if _, err := os.Stat(tmpfile); err != nil { if _, err := os.Stat(tmpfile); err != nil {
t.Fatal(err) t.Fatal(err)
@ -226,11 +230,11 @@ func TestSignature(t *testing.T) {
defer ethereum.Stop() defer ethereum.Stop()
defer os.RemoveAll(tmp) defer os.RemoveAll(tmp)
val, err := repl.re.Run(`eth.sign({from: "` + testAddress + `", data: "` + testHash + `"})`) val, err := repl.re.Run(`eth.sign("` + testAddress + `", "` + testHash + `")`)
// This is a very preliminary test, lacking actual signature verification // This is a very preliminary test, lacking actual signature verification
if err != nil { if err != nil {
t.Errorf("Error runnig js: %v", err) t.Errorf("Error running js: %v", err)
return return
} }
output := val.String() output := val.String()
@ -244,7 +248,6 @@ func TestSignature(t *testing.T) {
} }
func TestContract(t *testing.T) { func TestContract(t *testing.T) {
t.Skip()
tmp, repl, ethereum := testJEthRE(t) tmp, repl, ethereum := testJEthRE(t)
if err := ethereum.Start(); err != nil { if err := ethereum.Start(); err != nil {
@ -257,7 +260,9 @@ func TestContract(t *testing.T) {
var txc uint64 var txc uint64
coinbase := common.HexToAddress(testAddress) coinbase := common.HexToAddress(testAddress)
resolver.New(repl.xeth).CreateContracts(coinbase) resolver.New(repl.xeth).CreateContracts(coinbase)
// time.Sleep(1000 * time.Millisecond)
// checkEvalJSON(t, repl, `eth.getBlock("pending", true).transactions.length`, `2`)
source := `contract test {\n` + source := `contract test {\n` +
" /// @notice Will multiply `a` by 7." + `\n` + " /// @notice Will multiply `a` by 7." + `\n` +
` function multiply(uint a) returns(uint d) {\n` + ` function multiply(uint a) returns(uint d) {\n` +
@ -277,10 +282,9 @@ func TestContract(t *testing.T) {
// if solc is found with right version, test it, otherwise read from file // if solc is found with right version, test it, otherwise read from file
sol, err := compiler.New("") sol, err := compiler.New("")
if err != nil { if err != nil {
t.Logf("solc not found: skipping compiler test") t.Logf("solc not found: mocking contract compilation step")
} else if sol.Version() != solcVersion { } else if sol.Version() != solcVersion {
err = fmt.Errorf("solc wrong version found (%v, expect %v): skipping compiler test", sol.Version(), solcVersion) t.Logf("WARNING: solc different version found (%v, test written for %v, may need to update)", sol.Version(), solcVersion)
t.Log(err)
} }
if err != nil { if err != nil {
@ -293,10 +297,10 @@ func TestContract(t *testing.T) {
t.Errorf("%v", err) t.Errorf("%v", err)
} }
} else { } else {
checkEvalJSON(t, repl, `contract = eth.compile.solidity(source)`, string(contractInfo)) checkEvalJSON(t, repl, `contract = eth.compile.solidity(source).test`, string(contractInfo))
} }
checkEvalJSON(t, repl, `contract.code`, `"605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056"`) checkEvalJSON(t, repl, `contract.code`, `"0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"`)
checkEvalJSON( checkEvalJSON(
t, repl, t, repl,
@ -306,15 +310,16 @@ func TestContract(t *testing.T) {
callSetup := `abiDef = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]'); callSetup := `abiDef = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]');
Multiply7 = eth.contract(abiDef); Multiply7 = eth.contract(abiDef);
multiply7 = new Multiply7(contractaddress); multiply7 = Multiply7.at(contractaddress);
` `
// time.Sleep(1500 * time.Millisecond)
_, err = repl.re.Run(callSetup) _, err = repl.re.Run(callSetup)
if err != nil { if err != nil {
t.Errorf("unexpected error registering, got %v", err) t.Errorf("unexpected error setting up contract, got %v", err)
} }
// updatespec // checkEvalJSON(t, repl, `eth.getBlock("pending", true).transactions.length`, `3`)
// why is this sometimes failing? // why is this sometimes failing?
// checkEvalJSON(t, repl, `multiply7.multiply.call(6)`, `42`) // checkEvalJSON(t, repl, `multiply7.multiply.call(6)`, `42`)
expNotice := "" expNotice := ""
@ -322,20 +327,23 @@ multiply7 = new Multiply7(contractaddress);
t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm) t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm)
} }
// why 0?
checkEvalJSON(t, repl, `eth.getBlock("pending", true).transactions.length`, `0`)
txc, repl.xeth = repl.xeth.ApplyTestTxs(repl.stateDb, coinbase, txc) txc, repl.xeth = repl.xeth.ApplyTestTxs(repl.stateDb, coinbase, txc)
checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`) checkEvalJSON(t, repl, `admin.contractInfo.start()`, `true`)
checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary, gas: "1000000", gasPrice: "100000" })`, `undefined`) checkEvalJSON(t, repl, `multiply7.multiply.sendTransaction(6, { from: primary, gas: "1000000", gasPrice: "100000" })`, `undefined`)
expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x4a6c99e127191d2ee302e42182c338344b39a37a47cdbb17ab0f26b6802eb4d1'): {"params":[{"to":"0x5dcaace5982778b409c524873b319667eba5d074","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}` expNotice = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x87e2802265838c7f14bb69eecd2112911af6767907a702eeaa445239fb20711b'): {"params":[{"to":"0x5dcaace5982778b409c524873b319667eba5d074","data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}]}`
if repl.lastConfirm != expNotice { if repl.lastConfirm != expNotice {
t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm) t.Errorf("incorrect confirmation message: expected %v, got %v", expNotice, repl.lastConfirm)
} }
var contenthash = `"0x86d2b7cf1e72e9a7a3f8d96601f0151742a2f780f1526414304fbe413dc7f9bd"`
if sol != nil {
modContractInfo := versionRE.ReplaceAll(contractInfo, []byte(`"compilerVersion":"`+sol.Version()+`"`))
_ = modContractInfo
// contenthash = crypto.Sha3(modContractInfo)
}
checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`) checkEvalJSON(t, repl, `filename = "/tmp/info.json"`, `"/tmp/info.json"`)
checkEvalJSON(t, repl, `contenthash = admin.contractInfo.register(primary, contractaddress, contract, filename)`, `"0x0d067e2dd99a4d8f0c0279738b17130dd415a89f24a23f0e7cf68c546ae3089d"`) checkEvalJSON(t, repl, `contenthash = admin.contractInfo.register(primary, contractaddress, contract, filename)`, contenthash)
checkEvalJSON(t, repl, `admin.contractInfo.registerUrl(primary, contenthash, "file://"+filename)`, `true`) checkEvalJSON(t, repl, `admin.contractInfo.registerUrl(primary, contenthash, "file://"+filename)`, `true`)
if err != nil { if err != nil {
t.Errorf("unexpected error registering, got %v", err) t.Errorf("unexpected error registering, got %v", err)

@ -21,7 +21,6 @@
package main package main
import ( import (
"bufio"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -44,13 +43,12 @@ import (
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/mattn/go-colorable" "github.com/mattn/go-colorable"
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
"github.com/peterh/liner"
) )
import _ "net/http/pprof" import _ "net/http/pprof"
const ( const (
ClientIdentifier = "Geth" ClientIdentifier = "Geth"
Version = "0.9.22" Version = "0.9.23"
) )
var ( var (
@ -101,7 +99,15 @@ The output of this command is supposed to be machine-readable.
Usage: "import ethereum presale wallet", Usage: "import ethereum presale wallet",
}, },
}, },
}, Description: `
get wallet import /path/to/my/presale.wallet
will prompt for your password and imports your ether presale account.
It can be used non-interactively with the --password option taking a
passwordfile as argument containing the wallet password in plaintext.
`},
{ {
Action: accountList, Action: accountList,
Name: "account", Name: "account",
@ -111,7 +117,7 @@ The output of this command is supposed to be machine-readable.
Manage accounts lets you create new accounts, list all existing accounts, Manage accounts lets you create new accounts, list all existing accounts,
import a private key into a new account. import a private key into a new account.
'account help' shows a list of subcommands or help for one subcommand. ' help' shows a list of subcommands or help for one subcommand.
It supports interactive mode, when you are prompted for password as well as It supports interactive mode, when you are prompted for password as well as
non-interactive mode where passwords are supplied via a given password file. non-interactive mode where passwords are supplied via a given password file.
@ -230,6 +236,11 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
Name: "upgradedb", Name: "upgradedb",
Usage: "upgrade chainblock database", Usage: "upgrade chainblock database",
}, },
{
Action: removeDb,
Name: "removedb",
Usage: "Remove blockchain and state databases",
},
} }
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
utils.IdentityFlag, utils.IdentityFlag,
@ -246,6 +257,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.GasPriceFlag, utils.GasPriceFlag,
utils.MinerThreadsFlag, utils.MinerThreadsFlag,
utils.MiningEnabledFlag, utils.MiningEnabledFlag,
utils.AutoDAGFlag,
utils.NATFlag, utils.NATFlag,
utils.NatspecEnabledFlag, utils.NatspecEnabledFlag,
utils.NodeKeyFileFlag, utils.NodeKeyFileFlag,
@ -323,7 +335,6 @@ func console(ctx *cli.Context) {
repl := newJSRE( repl := newJSRE(
ethereum, ethereum,
ctx.String(utils.JSpathFlag.Name), ctx.String(utils.JSpathFlag.Name),
ctx.String(utils.SolcPathFlag.Name),
ctx.GlobalString(utils.RPCCORSDomainFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
true, true,
nil, nil,
@ -345,7 +356,6 @@ func execJSFiles(ctx *cli.Context) {
repl := newJSRE( repl := newJSRE(
ethereum, ethereum,
ctx.String(utils.JSpathFlag.Name), ctx.String(utils.JSpathFlag.Name),
ctx.String(utils.SolcPathFlag.Name),
ctx.GlobalString(utils.RPCCORSDomainFlag.Name), ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
false, false,
nil, nil,
@ -361,12 +371,20 @@ func execJSFiles(ctx *cli.Context) {
func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (passphrase string) { func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (passphrase string) {
var err error var err error
// Load startup keys. XXX we are going to need a different format // Load startup keys. XXX we are going to need a different format
// Attempt to unlock the account
passphrase = getPassPhrase(ctx, "", false)
if len(account) == 0 { if len(account) == 0 {
utils.Fatalf("Invalid account address '%s'", account) utils.Fatalf("Invalid account address '%s'", account)
} }
err = am.Unlock(common.HexToAddress(account), passphrase) // Attempt to unlock the account 3 times
attempts := 3
for tries := 0; tries < attempts; tries++ {
msg := fmt.Sprintf("Unlocking account %s...%s | Attempt %d/%d", account[:8], account[len(account)-6:], tries+1, attempts)
passphrase = getPassPhrase(ctx, msg, false)
err = am.Unlock(common.HexToAddress(account), passphrase)
if err == nil {
break
}
}
if err != nil { if err != nil {
utils.Fatalf("Unlock account failed '%v'", err) utils.Fatalf("Unlock account failed '%v'", err)
} }
@ -381,15 +399,18 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) {
am := eth.AccountManager() am := eth.AccountManager()
account := ctx.GlobalString(utils.UnlockedAccountFlag.Name) account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
if len(account) > 0 { accounts := strings.Split(account, " ")
if account == "primary" { for _, account := range accounts {
primaryAcc, err := am.Primary() if len(account) > 0 {
if err != nil { if account == "primary" {
utils.Fatalf("no primary account: %v", err) primaryAcc, err := am.Primary()
if err != nil {
utils.Fatalf("no primary account: %v", err)
}
account = primaryAcc.Hex()
} }
account = primaryAcc.Hex() unlockAccount(ctx, am, account)
} }
unlockAccount(ctx, am, account)
} }
// Start auxiliary services if enabled. // Start auxiliary services if enabled.
if ctx.GlobalBool(utils.RPCEnabledFlag.Name) { if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
@ -421,12 +442,12 @@ func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase
passfile := ctx.GlobalString(utils.PasswordFileFlag.Name) passfile := ctx.GlobalString(utils.PasswordFileFlag.Name)
if len(passfile) == 0 { if len(passfile) == 0 {
fmt.Println(desc) fmt.Println(desc)
auth, err := readPassword("Passphrase: ", true) auth, err := utils.PromptPassword("Passphrase: ", true)
if err != nil { if err != nil {
utils.Fatalf("%v", err) utils.Fatalf("%v", err)
} }
if confirmation { if confirmation {
confirm, err := readPassword("Repeat Passphrase: ", false) confirm, err := utils.PromptPassword("Repeat Passphrase: ", false)
if err != nil { if err != nil {
utils.Fatalf("%v", err) utils.Fatalf("%v", err)
} }
@ -543,6 +564,25 @@ func exportchain(ctx *cli.Context) {
return return
} }
func removeDb(ctx *cli.Context) {
confirm, err := utils.PromptConfirm("Remove local databases?")
if err != nil {
utils.Fatalf("%v", err)
}
if confirm {
fmt.Println("Removing chain and state databases...")
start := time.Now()
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "blockchain"))
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "state"))
fmt.Printf("Removed in %v\n", time.Since(start))
} else {
fmt.Println("Operation aborted")
}
}
func upgradeDb(ctx *cli.Context) { func upgradeDb(ctx *cli.Context) {
fmt.Println("Upgrade blockchain DB") fmt.Println("Upgrade blockchain DB")
@ -574,6 +614,7 @@ func upgradeDb(ctx *cli.Context) {
ethereum.ExtraDb().Close() ethereum.ExtraDb().Close()
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "blockchain")) os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "blockchain"))
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "state"))
ethereum, err = eth.New(cfg) ethereum, err = eth.New(cfg)
if err != nil { if err != nil {
@ -665,18 +706,3 @@ func hashish(x string) bool {
_, err := strconv.Atoi(x) _, err := strconv.Atoi(x)
return err != nil return err != nil
} }
func readPassword(prompt string, warnTerm bool) (string, error) {
if liner.TerminalSupported() {
lr := liner.NewLiner()
defer lr.Close()
return lr.PasswordPrompt(prompt)
}
if warnTerm {
fmt.Println("!! Unsupported terminal, password will be echoed.")
}
fmt.Print(prompt)
input, err := bufio.NewReader(os.Stdin).ReadString('\n')
fmt.Println()
return input, err
}

@ -22,11 +22,13 @@
package utils package utils
import ( import (
"bufio"
"fmt" "fmt"
"io" "io"
"os" "os"
"os/signal" "os/signal"
"regexp" "regexp"
"strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
@ -35,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/peterh/liner"
) )
var interruptCallbacks = []func(os.Signal){} var interruptCallbacks = []func(os.Signal){}
@ -71,18 +74,45 @@ func openLogFile(Datadir string, filename string) *os.File {
return file return file
} }
func confirm(message string) bool { func PromptConfirm(prompt string) (bool, error) {
fmt.Println(message, "Are you sure? (y/n)") var (
var r string input string
fmt.Scanln(&r) err error
for ; ; fmt.Scanln(&r) { )
if r == "n" || r == "y" { prompt = prompt + " [y/N] "
break
} else { if liner.TerminalSupported() {
fmt.Printf("Yes or no? (%s)", r) lr := liner.NewLiner()
} defer lr.Close()
input, err = lr.Prompt(prompt)
} else {
fmt.Print(prompt)
input, err = bufio.NewReader(os.Stdin).ReadString('\n')
fmt.Println()
} }
return r == "y"
if len(input) > 0 && strings.ToUpper(input[:1]) == "Y" {
return true, nil
} else {
return false, nil
}
return false, err
}
func PromptPassword(prompt string, warnTerm bool) (string, error) {
if liner.TerminalSupported() {
lr := liner.NewLiner()
defer lr.Close()
return lr.PasswordPrompt(prompt)
}
if warnTerm {
fmt.Println("!! Unsupported terminal, password will be echoed.")
}
fmt.Print(prompt)
input, err := bufio.NewReader(os.Stdin).ReadString('\n')
fmt.Println()
return input, err
} }
func initDataDir(Datadir string) { func initDataDir(Datadir string) {

@ -112,6 +112,10 @@ var (
Name: "mine", Name: "mine",
Usage: "Enable mining", Usage: "Enable mining",
} }
AutoDAGFlag = cli.BoolFlag{
Name: "autodag",
Usage: "Enable automatic DAG pregeneration",
}
EtherbaseFlag = cli.StringFlag{ EtherbaseFlag = cli.StringFlag{
Name: "etherbase", Name: "etherbase",
Usage: "Public address for block mining rewards. By default the address of your primary account is used", Usage: "Public address for block mining rewards. By default the address of your primary account is used",
@ -313,6 +317,8 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
Dial: true, Dial: true,
BootNodes: ctx.GlobalString(BootnodesFlag.Name), BootNodes: ctx.GlobalString(BootnodesFlag.Name),
GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)), GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)),
SolcPath: ctx.GlobalString(SolcPathFlag.Name),
AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name),
} }
} }
@ -336,8 +342,8 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Dat
} }
eventMux := new(event.TypeMux) eventMux := new(event.TypeMux)
chainManager := core.NewChainManager(blockDb, stateDb, eventMux)
pow := ethash.New() pow := ethash.New()
chainManager := core.NewChainManager(blockDb, stateDb, pow, eventMux)
txPool := core.NewTxPool(eventMux, chainManager.State, chainManager.GasLimit) txPool := core.NewTxPool(eventMux, chainManager.State, chainManager.GasLimit)
blockProcessor := core.NewBlockProcessor(stateDb, extraDb, pow, txPool, chainManager, eventMux) blockProcessor := core.NewBlockProcessor(stateDb, extraDb, pow, txPool, chainManager, eventMux)
chainManager.SetProcessor(blockProcessor) chainManager.SetProcessor(blockProcessor)

@ -18,7 +18,8 @@ import (
) )
const ( const (
flair = "Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com> (c) 2014-2015" // flair = "Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com> (c) 2014-2015"
flair = ""
languageVersion = "0" languageVersion = "0"
) )
@ -91,7 +92,7 @@ func (sol *Solidity) Version() string {
return sol.version return sol.version
} }
func (sol *Solidity) Compile(source string) (contract *Contract, err error) { func (sol *Solidity) Compile(source string) (contracts map[string]*Contract, err error) {
if len(source) == 0 { if len(source) == 0 {
err = fmt.Errorf("empty source") err = fmt.Errorf("empty source")
@ -122,58 +123,61 @@ func (sol *Solidity) Compile(source string) (contract *Contract, err error) {
err = fmt.Errorf("solc error: missing code output") err = fmt.Errorf("solc error: missing code output")
return return
} }
if len(matches) > 1 {
err = fmt.Errorf("multi-contract sources are not supported")
return
}
_, file := filepath.Split(matches[0])
base := strings.Split(file, ".")[0]
codeFile := filepath.Join(wd, base+".binary") contracts = make(map[string]*Contract)
abiDefinitionFile := filepath.Join(wd, base+".abi") for _, path := range matches {
userDocFile := filepath.Join(wd, base+".docuser") _, file := filepath.Split(path)
developerDocFile := filepath.Join(wd, base+".docdev") base := strings.Split(file, ".")[0]
code, err := ioutil.ReadFile(codeFile) codeFile := filepath.Join(wd, base+".binary")
if err != nil { abiDefinitionFile := filepath.Join(wd, base+".abi")
err = fmt.Errorf("error reading compiler output for code: %v", err) userDocFile := filepath.Join(wd, base+".docuser")
return developerDocFile := filepath.Join(wd, base+".docdev")
}
abiDefinitionJson, err := ioutil.ReadFile(abiDefinitionFile)
if err != nil {
err = fmt.Errorf("error reading compiler output for abiDefinition: %v", err)
return
}
var abiDefinition interface{}
err = json.Unmarshal(abiDefinitionJson, &abiDefinition)
userDocJson, err := ioutil.ReadFile(userDocFile) var code, abiDefinitionJson, userDocJson, developerDocJson []byte
if err != nil { code, err = ioutil.ReadFile(codeFile)
err = fmt.Errorf("error reading compiler output for userDoc: %v", err) if err != nil {
return err = fmt.Errorf("error reading compiler output for code: %v", err)
} return
var userDoc interface{} }
err = json.Unmarshal(userDocJson, &userDoc) abiDefinitionJson, err = ioutil.ReadFile(abiDefinitionFile)
if err != nil {
err = fmt.Errorf("error reading compiler output for abiDefinition: %v", err)
return
}
var abiDefinition interface{}
err = json.Unmarshal(abiDefinitionJson, &abiDefinition)
developerDocJson, err := ioutil.ReadFile(developerDocFile) userDocJson, err = ioutil.ReadFile(userDocFile)
if err != nil { if err != nil {
err = fmt.Errorf("error reading compiler output for developerDoc: %v", err) err = fmt.Errorf("error reading compiler output for userDoc: %v", err)
return return
} }
var developerDoc interface{} var userDoc interface{}
err = json.Unmarshal(developerDocJson, &developerDoc) err = json.Unmarshal(userDocJson, &userDoc)
contract = &Contract{ developerDocJson, err = ioutil.ReadFile(developerDocFile)
Code: string(code), if err != nil {
Info: ContractInfo{ err = fmt.Errorf("error reading compiler output for developerDoc: %v", err)
Source: source, return
Language: "Solidity", }
LanguageVersion: languageVersion, var developerDoc interface{}
CompilerVersion: sol.version, err = json.Unmarshal(developerDocJson, &developerDoc)
AbiDefinition: abiDefinition,
UserDoc: userDoc, contract := &Contract{
DeveloperDoc: developerDoc, Code: "0x" + string(code),
}, Info: ContractInfo{
Source: source,
Language: "Solidity",
LanguageVersion: languageVersion,
CompilerVersion: sol.version,
AbiDefinition: abiDefinition,
UserDoc: userDoc,
DeveloperDoc: developerDoc,
},
}
contracts[base] = contract
} }
return return

@ -9,7 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
const solcVersion = "0.9.17" const solcVersion = "0.9.23"
var ( var (
source = ` source = `
@ -20,37 +20,45 @@ contract test {
} }
} }
` `
code = "605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056" code = "0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"
info = `{"source":"\ncontract test {\n /// @notice Will multiply ` + "`a`" + ` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","language":"Solidity","languageVersion":"0","compilerVersion":"0.9.17","abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}},"developerDoc":{"methods":{}}}` info = `{"source":"\ncontract test {\n /// @notice Will multiply ` + "`a`" + ` by 7.\n function multiply(uint a) returns(uint d) {\n return a * 7;\n }\n}\n","language":"Solidity","languageVersion":"0","compilerVersion":"0.9.23","abiDefinition":[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}],"userDoc":{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}},"developerDoc":{"methods":{}}}`
infohash = common.HexToHash("0x834075768a68e500e459b9c3213750c84de3df47156500cb01bb664d3f88c60a") infohash = common.HexToHash("0xea782f674eb898e477c20e8a7cf11c2c28b09fa68b5278732104f7a101aed255")
) )
func TestCompiler(t *testing.T) { func TestCompiler(t *testing.T) {
sol, err := New("") sol, err := New("")
if err != nil { if err != nil {
t.Skip("no solc installed") t.Skip("solc not found: skip")
} else if sol.Version() != solcVersion {
t.Logf("WARNING: a newer version of solc found (%v, expect %v)", sol.Version(), solcVersion)
} }
contract, err := sol.Compile(source) contracts, err := sol.Compile(source)
if err != nil { if err != nil {
t.Errorf("error compiling source. result %v: %v", contract, err) t.Errorf("error compiling source. result %v: %v", contracts, err)
return return
} }
/*
if contract.Code != code { if len(contracts) != 1 {
t.Errorf("wrong code, expected\n%s, got\n%s", code, contract.Code) t.Errorf("one contract expected, got\n%s", len(contracts))
} }
*/
if contracts["test"].Code != code {
t.Errorf("wrong code, expected\n%s, got\n%s", code, contracts["test"].Code)
}
} }
func TestCompileError(t *testing.T) { func TestCompileError(t *testing.T) {
sol, err := New("") sol, err := New("")
if err != nil || sol.version != solcVersion { if err != nil || sol.version != solcVersion {
t.Skip("no solc installed") t.Skip("solc not found: skip")
} else if sol.Version() != solcVersion {
t.Logf("WARNING: a newer version of solc found (%v, expect %v)", sol.Version(), solcVersion)
} }
contract, err := sol.Compile(source[2:]) contracts, err := sol.Compile(source[2:])
if err == nil { if err == nil {
t.Errorf("error expected compiling source. got none. result %v", contract) t.Errorf("error expected compiling source. got none. result %v", contracts)
return return
} }
} }
@ -78,11 +86,11 @@ func TestExtractInfo(t *testing.T) {
os.Remove(filename) os.Remove(filename)
cinfohash, err := ExtractInfo(contract, filename) cinfohash, err := ExtractInfo(contract, filename)
if err != nil { if err != nil {
t.Errorf("%v", err) t.Errorf("error extracting info: %v", err)
} }
got, err := ioutil.ReadFile(filename) got, err := ioutil.ReadFile(filename)
if err != nil { if err != nil {
t.Errorf("%v", err) t.Errorf("error reading '%v': %v", filename, err)
} }
if string(got) != info { if string(got) != info {
t.Errorf("incorrect info.json extracted, expected:\n%s\ngot\n%s", info, string(got)) t.Errorf("incorrect info.json extracted, expected:\n%s\ngot\n%s", info, string(got))

@ -85,6 +85,9 @@ func (bc *BlockCache) Get(hash common.Hash) *types.Block {
} }
func (bc *BlockCache) Has(hash common.Hash) bool { func (bc *BlockCache) Has(hash common.Hash) bool {
bc.mu.RLock()
defer bc.mu.RUnlock()
_, ok := bc.blocks[hash] _, ok := bc.blocks[hash]
return ok return ok
} }

@ -24,6 +24,8 @@ const (
BlockChainVersion = 2 BlockChainVersion = 2
) )
var receiptsPre = []byte("receipts-")
type BlockProcessor struct { type BlockProcessor struct {
db common.Database db common.Database
extraDb common.Database extraDb common.Database
@ -189,7 +191,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
state := state.New(parent.Root(), sm.db) state := state.New(parent.Root(), sm.db)
// Block validation // Block validation
if err = sm.ValidateHeader(block.Header(), parent.Header()); err != nil { if err = sm.ValidateHeader(block.Header(), parent.Header(), false); err != nil {
return return
} }
@ -263,13 +265,27 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
putTx(sm.extraDb, tx, block, uint64(i)) putTx(sm.extraDb, tx, block, uint64(i))
} }
receiptsRlp := block.Receipts().RlpEncode()
sm.extraDb.Put(append(receiptsPre, block.Hash().Bytes()...), receiptsRlp)
return state.Logs(), nil return state.Logs(), nil
} }
func (self *BlockProcessor) GetBlockReceipts(bhash common.Hash) (receipts types.Receipts, err error) {
var rdata []byte
rdata, err = self.extraDb.Get(append(receiptsPre, bhash[:]...))
if err == nil {
err = rlp.DecodeBytes(rdata, &receipts)
}
return
}
// Validates the current block. Returns an error if the block was invalid, // Validates the current block. Returns an error if the block was invalid,
// an uncle or anything that isn't on the current block chain. // an uncle or anything that isn't on the current block chain.
// Validation validates easy over difficult (dagger takes longer time = difficult) // Validation validates easy over difficult (dagger takes longer time = difficult)
func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error { func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header, checkPow bool) error {
if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 { if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
return fmt.Errorf("Block extra data too long (%d)", len(block.Extra)) return fmt.Errorf("Block extra data too long (%d)", len(block.Extra))
} }
@ -300,9 +316,11 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error {
return BlockEqualTSErr //ValidationError("Block timestamp equal or less than previous block (%v - %v)", block.Time, parent.Time) return BlockEqualTSErr //ValidationError("Block timestamp equal or less than previous block (%v - %v)", block.Time, parent.Time)
} }
// Verify the nonce of the block. Return an error if it's not valid if checkPow {
if !sm.Pow.Verify(types.NewBlockWithHeader(block)) { // Verify the nonce of the block. Return an error if it's not valid
return ValidationError("Block's nonce is invalid (= %x)", block.Nonce) if !sm.Pow.Verify(types.NewBlockWithHeader(block)) {
return ValidationError("Block's nonce is invalid (= %x)", block.Nonce)
}
} }
return nil return nil
@ -351,6 +369,13 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
uncles.Add(hash) uncles.Add(hash)
if ancestors.Has(hash) { if ancestors.Has(hash) {
branch := fmt.Sprintf(" O - %x\n |\n", block.Hash())
ancestors.Each(func(item interface{}) bool {
branch += fmt.Sprintf(" O - %x\n |\n", hash)
return true
})
glog.Infoln(branch)
return UncleError("uncle[%d](%x) is ancestor", i, hash[:4]) return UncleError("uncle[%d](%x) is ancestor", i, hash[:4])
} }
@ -358,7 +383,7 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
return UncleError("uncle[%d](%x)'s parent unknown (%x)", i, hash[:4], uncle.ParentHash[0:4]) return UncleError("uncle[%d](%x)'s parent unknown (%x)", i, hash[:4], uncle.ParentHash[0:4])
} }
if err := sm.ValidateHeader(uncle, ancestorHeaders[uncle.ParentHash]); err != nil { if err := sm.ValidateHeader(uncle, ancestorHeaders[uncle.ParentHash], true); err != nil {
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err)) return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
} }
} }

@ -14,7 +14,7 @@ func proc() (*BlockProcessor, *ChainManager) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
var mux event.TypeMux var mux event.TypeMux
chainMan := NewChainManager(db, db, &mux) chainMan := NewChainManager(db, db, thePow(), &mux)
return NewBlockProcessor(db, db, ezp.New(), nil, chainMan, &mux), chainMan return NewBlockProcessor(db, db, ezp.New(), nil, chainMan, &mux), chainMan
} }
@ -24,13 +24,13 @@ func TestNumber(t *testing.T) {
block1.Header().Number = big.NewInt(3) block1.Header().Number = big.NewInt(3)
block1.Header().Time-- block1.Header().Time--
err := bp.ValidateHeader(block1.Header(), chain.Genesis().Header()) err := bp.ValidateHeader(block1.Header(), chain.Genesis().Header(), false)
if err != BlockNumberErr { if err != BlockNumberErr {
t.Errorf("expected block number error %v", err) t.Errorf("expected block number error %v", err)
} }
block1 = chain.NewBlock(common.Address{}) block1 = chain.NewBlock(common.Address{})
err = bp.ValidateHeader(block1.Header(), chain.Genesis().Header()) err = bp.ValidateHeader(block1.Header(), chain.Genesis().Header(), false)
if err == BlockNumberErr { if err == BlockNumberErr {
t.Errorf("didn't expect block number error") t.Errorf("didn't expect block number error")
} }

@ -109,7 +109,7 @@ func makeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Dat
// Effectively a fork factory // Effectively a fork factory
func newChainManager(block *types.Block, eventMux *event.TypeMux, db common.Database) *ChainManager { func newChainManager(block *types.Block, eventMux *event.TypeMux, db common.Database) *ChainManager {
genesis := GenesisBlock(db) genesis := GenesisBlock(db)
bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: eventMux} bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: eventMux, pow: FakePow{}}
bc.txState = state.ManageState(state.New(genesis.Root(), db)) bc.txState = state.ManageState(state.New(genesis.Root(), db))
bc.futureBlocks = NewBlockCache(1000) bc.futureBlocks = NewBlockCache(1000)
if block == nil { if block == nil {

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
"runtime"
"sync" "sync"
"time" "time"
@ -15,6 +16,7 @@ import (
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -83,8 +85,9 @@ type ChainManager struct {
eventMux *event.TypeMux eventMux *event.TypeMux
genesisBlock *types.Block genesisBlock *types.Block
// Last known total difficulty // Last known total difficulty
mu sync.RWMutex mu sync.RWMutex
tsmu sync.RWMutex chainmu sync.RWMutex
tsmu sync.RWMutex
td *big.Int td *big.Int
currentBlock *types.Block currentBlock *types.Block
@ -99,9 +102,11 @@ type ChainManager struct {
quit chan struct{} quit chan struct{}
wg sync.WaitGroup wg sync.WaitGroup
pow pow.PoW
} }
func NewChainManager(blockDb, stateDb common.Database, mux *event.TypeMux) *ChainManager { func NewChainManager(blockDb, stateDb common.Database, pow pow.PoW, mux *event.TypeMux) *ChainManager {
bc := &ChainManager{ bc := &ChainManager{
blockDb: blockDb, blockDb: blockDb,
stateDb: stateDb, stateDb: stateDb,
@ -109,6 +114,7 @@ func NewChainManager(blockDb, stateDb common.Database, mux *event.TypeMux) *Chai
eventMux: mux, eventMux: mux,
quit: make(chan struct{}), quit: make(chan struct{}),
cache: NewBlockCache(blockCacheLimit), cache: NewBlockCache(blockCacheLimit),
pow: pow,
} }
bc.setLastState() bc.setLastState()
@ -342,7 +348,7 @@ func (self *ChainManager) Export(w io.Writer) error {
last := self.currentBlock.NumberU64() last := self.currentBlock.NumberU64()
for nr := uint64(0); nr <= last; nr++ { for nr := uint64(1); nr <= last; nr++ {
block := self.GetBlockByNumber(nr) block := self.GetBlockByNumber(nr)
if block == nil { if block == nil {
return fmt.Errorf("export failed on #%d: not found", nr) return fmt.Errorf("export failed on #%d: not found", nr)
@ -406,9 +412,11 @@ func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) (
} }
func (self *ChainManager) GetBlock(hash common.Hash) *types.Block { func (self *ChainManager) GetBlock(hash common.Hash) *types.Block {
if block := self.cache.Get(hash); block != nil { /*
return block if block := self.cache.Get(hash); block != nil {
} return block
}
*/
data, _ := self.blockDb.Get(append(blockHashPre, hash[:]...)) data, _ := self.blockDb.Get(append(blockHashPre, hash[:]...))
if len(data) == 0 { if len(data) == 0 {
@ -518,6 +526,9 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
self.wg.Add(1) self.wg.Add(1)
defer self.wg.Done() defer self.wg.Done()
self.chainmu.Lock()
defer self.chainmu.Unlock()
// A queued approach to delivering events. This is generally faster than direct delivery and requires much less mutex acquiring. // A queued approach to delivering events. This is generally faster than direct delivery and requires much less mutex acquiring.
var ( var (
queue = make([]interface{}, len(chain)) queue = make([]interface{}, len(chain))
@ -525,10 +536,19 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
stats struct{ queued, processed, ignored int } stats struct{ queued, processed, ignored int }
tstart = time.Now() tstart = time.Now()
) )
// check the nonce in parallel to the block processing
// this speeds catching up significantly
nonceErrCh := make(chan error)
go func() {
nonceErrCh <- verifyNonces(self.pow, chain)
}()
for i, block := range chain { for i, block := range chain {
if block == nil { if block == nil {
continue continue
} }
// Setting block.Td regardless of error (known for example) prevents errors down the line // Setting block.Td regardless of error (known for example) prevents errors down the line
// in the protocol handler // in the protocol handler
block.Td = new(big.Int).Set(CalcTD(block, self.GetBlock(block.ParentHash()))) block.Td = new(big.Int).Set(CalcTD(block, self.GetBlock(block.ParentHash())))
@ -542,7 +562,6 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
continue continue
} }
block.Td = new(big.Int)
// Do not penelise on future block. We'll need a block queue eventually that will queue // Do not penelise on future block. We'll need a block queue eventually that will queue
// future block for future use // future block for future use
if err == BlockFutureErr { if err == BlockFutureErr {
@ -559,68 +578,67 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
continue continue
} }
h := block.Header() blockErr(block, err)
glog.V(logger.Error).Infof("INVALID block #%v (%x)\n", h.Number, h.Hash().Bytes())
glog.V(logger.Error).Infoln(err)
glog.V(logger.Debug).Infoln(block)
return i, err return i, err
} }
self.mu.Lock() cblock := self.currentBlock
{ // Write block to database. Eventually we'll have to improve on this and throw away blocks that are
cblock := self.currentBlock // not in the canonical chain.
// Write block to database. Eventually we'll have to improve on this and throw away blocks that are self.write(block)
// not in the canonical chain. // Compare the TD of the last known block in the canonical chain to make sure it's greater.
self.write(block) // At this point it's possible that a different chain (fork) becomes the new canonical chain.
// Compare the TD of the last known block in the canonical chain to make sure it's greater. if block.Td.Cmp(self.td) > 0 {
// At this point it's possible that a different chain (fork) becomes the new canonical chain. // chain fork
if block.Td.Cmp(self.td) > 0 { if block.ParentHash() != cblock.Hash() {
// chain fork // during split we merge two different chains and create the new canonical chain
if block.ParentHash() != cblock.Hash() { self.merge(cblock, block)
// during split we merge two different chains and create the new canonical chain
self.merge(cblock, block)
queue[i] = ChainSplitEvent{block, logs} queue[i] = ChainSplitEvent{block, logs}
queueEvent.splitCount++ queueEvent.splitCount++
}
self.setTotalDifficulty(block.Td)
self.insert(block)
jsonlogger.LogJson(&logger.EthChainNewHead{
BlockHash: block.Hash().Hex(),
BlockNumber: block.Number(),
ChainHeadHash: cblock.Hash().Hex(),
BlockPrevHash: block.ParentHash().Hex(),
})
self.setTransState(state.New(block.Root(), self.stateDb))
self.txState.SetState(state.New(block.Root(), self.stateDb))
queue[i] = ChainEvent{block, block.Hash(), logs}
queueEvent.canonicalCount++
if glog.V(logger.Debug) {
glog.Infof("[%v] inserted block #%d (%d TXs %d UNCs) (%x...)\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4])
}
} else {
if glog.V(logger.Detail) {
glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...)\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4])
}
queue[i] = ChainSideEvent{block, logs}
queueEvent.sideCount++
} }
self.futureBlocks.Delete(block.Hash())
self.setTotalDifficulty(block.Td)
self.insert(block)
jsonlogger.LogJson(&logger.EthChainNewHead{
BlockHash: block.Hash().Hex(),
BlockNumber: block.Number(),
ChainHeadHash: cblock.Hash().Hex(),
BlockPrevHash: block.ParentHash().Hex(),
})
self.setTransState(state.New(block.Root(), self.stateDb))
self.txState.SetState(state.New(block.Root(), self.stateDb))
queue[i] = ChainEvent{block, block.Hash(), logs}
queueEvent.canonicalCount++
if glog.V(logger.Debug) {
glog.Infof("[%v] inserted block #%d (%d TXs %d UNCs) (%x...)\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4])
}
} else {
if glog.V(logger.Detail) {
glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...)\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4])
}
queue[i] = ChainSideEvent{block, logs}
queueEvent.sideCount++
} }
self.mu.Unlock() self.futureBlocks.Delete(block.Hash())
stats.processed++ stats.processed++
} }
// check and wait for the nonce error channel and
// make sure no nonce error was thrown in the process
err := <-nonceErrCh
if err != nil {
return 0, err
}
if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) { if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) {
tend := time.Since(tstart) tend := time.Since(tstart)
start, end := chain[0], chain[len(chain)-1] start, end := chain[0], chain[len(chain)-1]
@ -719,3 +737,63 @@ out:
} }
} }
} }
func blockErr(block *types.Block, err error) {
h := block.Header()
glog.V(logger.Error).Infof("INVALID block #%v (%x)\n", h.Number, h.Hash().Bytes())
glog.V(logger.Error).Infoln(err)
glog.V(logger.Debug).Infoln(block)
}
// verifyNonces verifies nonces of the given blocks in parallel and returns
// an error if one of the blocks nonce verifications failed.
func verifyNonces(pow pow.PoW, blocks []*types.Block) error {
// Spawn a few workers. They listen for blocks on the in channel
// and send results on done. The workers will exit in the
// background when in is closed.
var (
in = make(chan *types.Block)
done = make(chan error, runtime.GOMAXPROCS(0))
)
defer close(in)
for i := 0; i < cap(done); i++ {
go verifyNonce(pow, in, done)
}
// Feed blocks to the workers, aborting at the first invalid nonce.
var (
running, i int
block *types.Block
sendin = in
)
for i < len(blocks) || running > 0 {
if i == len(blocks) {
// Disable sending to in.
sendin = nil
} else {
block = blocks[i]
i++
}
select {
case sendin <- block:
running++
case err := <-done:
running--
if err != nil {
return err
}
}
}
return nil
}
// verifyNonce is a worker for the verifyNonces method. It will run until
// in is closed.
func verifyNonce(pow pow.PoW, in <-chan *types.Block, done chan<- error) {
for block := range in {
if !pow.Verify(block) {
done <- ValidationError("Block(#%v) nonce is invalid (= %x)", block.Number(), block.Nonce)
} else {
done <- nil
}
}
}

@ -9,11 +9,13 @@ import (
"strconv" "strconv"
"testing" "testing"
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -21,6 +23,11 @@ func init() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
} }
func thePow() pow.PoW {
pow, _ := ethash.NewForTesting()
return pow
}
// Test fork of length N starting from block i // Test fork of length N starting from block i
func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big.Int)) { func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big.Int)) {
// switch databases to process the new chain // switch databases to process the new chain
@ -259,7 +266,7 @@ func TestChainInsertions(t *testing.T) {
} }
var eventMux event.TypeMux var eventMux event.TypeMux
chainMan := NewChainManager(db, db, &eventMux) chainMan := NewChainManager(db, db, thePow(), &eventMux)
txPool := NewTxPool(&eventMux, chainMan.State, func() *big.Int { return big.NewInt(100000000) }) txPool := NewTxPool(&eventMux, chainMan.State, func() *big.Int { return big.NewInt(100000000) })
blockMan := NewBlockProcessor(db, db, nil, txPool, chainMan, &eventMux) blockMan := NewBlockProcessor(db, db, nil, txPool, chainMan, &eventMux)
chainMan.SetProcessor(blockMan) chainMan.SetProcessor(blockMan)
@ -305,7 +312,7 @@ func TestChainMultipleInsertions(t *testing.T) {
} }
} }
var eventMux event.TypeMux var eventMux event.TypeMux
chainMan := NewChainManager(db, db, &eventMux) chainMan := NewChainManager(db, db, thePow(), &eventMux)
txPool := NewTxPool(&eventMux, chainMan.State, func() *big.Int { return big.NewInt(100000000) }) txPool := NewTxPool(&eventMux, chainMan.State, func() *big.Int { return big.NewInt(100000000) })
blockMan := NewBlockProcessor(db, db, nil, txPool, chainMan, &eventMux) blockMan := NewBlockProcessor(db, db, nil, txPool, chainMan, &eventMux)
chainMan.SetProcessor(blockMan) chainMan.SetProcessor(blockMan)
@ -334,7 +341,7 @@ func TestGetAncestors(t *testing.T) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
var eventMux event.TypeMux var eventMux event.TypeMux
chainMan := NewChainManager(db, db, &eventMux) chainMan := NewChainManager(db, db, thePow(), &eventMux)
chain, err := loadChain("valid1", t) chain, err := loadChain("valid1", t)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -372,7 +379,7 @@ func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block
func chm(genesis *types.Block, db common.Database) *ChainManager { func chm(genesis *types.Block, db common.Database) *ChainManager {
var eventMux event.TypeMux var eventMux event.TypeMux
bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: &eventMux} bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}}
bc.cache = NewBlockCache(100) bc.cache = NewBlockCache(100)
bc.futureBlocks = NewBlockCache(100) bc.futureBlocks = NewBlockCache(100)
bc.processor = bproc{} bc.processor = bproc{}
@ -383,6 +390,7 @@ func chm(genesis *types.Block, db common.Database) *ChainManager {
} }
func TestReorgLongest(t *testing.T) { func TestReorgLongest(t *testing.T) {
t.Skip("skipped while cache is removed")
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
genesis := GenesisBlock(db) genesis := GenesisBlock(db)
bc := chm(genesis, db) bc := chm(genesis, db)
@ -402,6 +410,7 @@ func TestReorgLongest(t *testing.T) {
} }
func TestReorgShortest(t *testing.T) { func TestReorgShortest(t *testing.T) {
t.Skip("skipped while cache is removed")
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
genesis := GenesisBlock(db) genesis := GenesisBlock(db)
bc := chm(genesis, db) bc := chm(genesis, db)

@ -38,6 +38,12 @@ func (self *Execution) Create(caller vm.ContextRef) (ret []byte, err error, acco
code := self.input code := self.input
self.input = nil self.input = nil
ret, err = self.exec(nil, code, caller) ret, err = self.exec(nil, code, caller)
// Here we get an error if we run into maximum stack depth,
// See: https://github.com/ethereum/yellowpaper/pull/131
// and YP definitions for CREATE instruction
if err != nil {
return nil, err, nil
}
account = self.env.State().GetStateObject(*self.address) account = self.env.State().GetStateObject(*self.address)
return return
} }

@ -49,6 +49,18 @@ func (self *Memory) Get(offset, size int64) (cpy []byte) {
return return
} }
func (self *Memory) GetPtr(offset, size int64) []byte {
if size == 0 {
return nil
}
if len(self.store) > int(offset) {
return self.store[offset : offset+size]
}
return nil
}
func (m *Memory) Len() int { func (m *Memory) Len() int {
return len(m.store) return len(m.store)
} }

@ -695,7 +695,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
self.Printf("resume %x (%v)", context.Address(), context.Gas) self.Printf("resume %x (%v)", context.Address(), context.Gas)
case RETURN: case RETURN:
offset, size := stack.pop(), stack.pop() offset, size := stack.pop(), stack.pop()
ret := mem.Get(offset.Int64(), size.Int64()) ret := mem.GetPtr(offset.Int64(), size.Int64())
self.Printf(" => [%v, %v] (%d) 0x%x", offset, size, len(ret), ret).Endl() self.Printf(" => [%v, %v] (%d) 0x%x", offset, size, len(ret), ret).Endl()

@ -14,6 +14,7 @@ import (
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -30,6 +31,14 @@ import (
"github.com/ethereum/go-ethereum/whisper" "github.com/ethereum/go-ethereum/whisper"
) )
const (
epochLength = 30000
ethashRevision = 23
autoDAGcheckInterval = 10 * time.Hour
autoDAGepochHeight = epochLength / 2
)
var ( var (
jsonlogger = logger.NewJsonLogger() jsonlogger = logger.NewJsonLogger()
@ -59,6 +68,7 @@ type Config struct {
LogJSON string LogJSON string
VmDebug bool VmDebug bool
NatSpec bool NatSpec bool
AutoDAG bool
MaxPeers int MaxPeers int
MaxPendingPeers int MaxPendingPeers int
@ -79,6 +89,7 @@ type Config struct {
GasPrice *big.Int GasPrice *big.Int
MinerThreads int MinerThreads int
AccountManager *accounts.Manager AccountManager *accounts.Manager
SolcPath string
// NewDB is used to create databases. // NewDB is used to create databases.
// If nil, the default is to create leveldb databases on disk. // If nil, the default is to create leveldb databases on disk.
@ -181,6 +192,8 @@ type Ethereum struct {
pow *ethash.Ethash pow *ethash.Ethash
protocolManager *ProtocolManager protocolManager *ProtocolManager
downloader *downloader.Downloader downloader *downloader.Downloader
SolcPath string
solc *compiler.Solidity
net *p2p.Server net *p2p.Server
eventMux *event.TypeMux eventMux *event.TypeMux
@ -193,6 +206,8 @@ type Ethereum struct {
MinerThreads int MinerThreads int
NatSpec bool NatSpec bool
DataDir string DataDir string
AutoDAG bool
autodagquit chan bool
etherbase common.Address etherbase common.Address
clientVersion string clientVersion string
ethVersionId int ethVersionId int
@ -209,7 +224,7 @@ func New(config *Config) (*Ethereum, error) {
// Let the database take 3/4 of the max open files (TODO figure out a way to get the actual limit of the open files) // Let the database take 3/4 of the max open files (TODO figure out a way to get the actual limit of the open files)
const dbCount = 3 const dbCount = 3
ethdb.OpenFileLimit = 256 / (dbCount + 1) ethdb.OpenFileLimit = 128 / (dbCount + 1)
newdb := config.NewDB newdb := config.NewDB
if newdb == nil { if newdb == nil {
@ -264,11 +279,13 @@ func New(config *Config) (*Ethereum, error) {
netVersionId: config.NetworkId, netVersionId: config.NetworkId,
NatSpec: config.NatSpec, NatSpec: config.NatSpec,
MinerThreads: config.MinerThreads, MinerThreads: config.MinerThreads,
SolcPath: config.SolcPath,
AutoDAG: config.AutoDAG,
} }
eth.chainManager = core.NewChainManager(blockDb, stateDb, eth.EventMux())
eth.downloader = downloader.New(eth.EventMux(), eth.chainManager.HasBlock, eth.chainManager.GetBlock)
eth.pow = ethash.New() eth.pow = ethash.New()
eth.chainManager = core.NewChainManager(blockDb, stateDb, eth.pow, eth.EventMux())
eth.downloader = downloader.New(eth.EventMux(), eth.chainManager.HasBlock, eth.chainManager.GetBlock)
eth.txPool = core.NewTxPool(eth.EventMux(), eth.chainManager.State, eth.chainManager.GasLimit) eth.txPool = core.NewTxPool(eth.EventMux(), eth.chainManager.State, eth.chainManager.GasLimit)
eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.txPool, eth.chainManager, eth.EventMux()) eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.txPool, eth.chainManager, eth.EventMux())
eth.chainManager.SetProcessor(eth.blockProcessor) eth.chainManager.SetProcessor(eth.blockProcessor)
@ -443,6 +460,10 @@ func (s *Ethereum) Start() error {
// periodically flush databases // periodically flush databases
go s.syncDatabases() go s.syncDatabases()
if s.AutoDAG {
s.StartAutoDAG()
}
// Start services // Start services
go s.txPool.Start() go s.txPool.Start()
s.protocolManager.Start() s.protocolManager.Start()
@ -521,6 +542,7 @@ func (s *Ethereum) Stop() {
if s.whisper != nil { if s.whisper != nil {
s.whisper.Stop() s.whisper.Stop()
} }
s.StopAutoDAG()
glog.V(logger.Info).Infoln("Server stopped") glog.V(logger.Info).Infoln("Server stopped")
close(s.shutdownChan) close(s.shutdownChan)
@ -554,6 +576,77 @@ func (self *Ethereum) syncAccounts(tx *types.Transaction) {
} }
} }
// StartAutoDAG() spawns a go routine that checks the DAG every autoDAGcheckInterval
// by default that is 10 times per epoch
// in epoch n, if we past autoDAGepochHeight within-epoch blocks,
// it calls ethash.MakeDAG to pregenerate the DAG for the next epoch n+1
// if it does not exist yet as well as remove the DAG for epoch n-1
// the loop quits if autodagquit channel is closed, it can safely restart and
// stop any number of times.
// For any more sophisticated pattern of DAG generation, use CLI subcommand
// makedag
func (self *Ethereum) StartAutoDAG() {
if self.autodagquit != nil {
return // already started
}
go func() {
glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG ON (ethash dir: %s)", ethash.DefaultDir)
var nextEpoch uint64
timer := time.After(0)
self.autodagquit = make(chan bool)
for {
select {
case <-timer:
glog.V(logger.Info).Infof("checking DAG (ethash dir: %s)", ethash.DefaultDir)
currentBlock := self.ChainManager().CurrentBlock().NumberU64()
thisEpoch := currentBlock / epochLength
if nextEpoch <= thisEpoch {
if currentBlock%epochLength > autoDAGepochHeight {
if thisEpoch > 0 {
previousDag, previousDagFull := dagFiles(thisEpoch - 1)
os.Remove(filepath.Join(ethash.DefaultDir, previousDag))
os.Remove(filepath.Join(ethash.DefaultDir, previousDagFull))
glog.V(logger.Info).Infof("removed DAG for epoch %d (%s)", thisEpoch-1, previousDag)
}
nextEpoch = thisEpoch + 1
dag, _ := dagFiles(nextEpoch)
if _, err := os.Stat(dag); os.IsNotExist(err) {
glog.V(logger.Info).Infof("Pregenerating DAG for epoch %d (%s)", nextEpoch, dag)
err := ethash.MakeDAG(nextEpoch*epochLength, "") // "" -> ethash.DefaultDir
if err != nil {
glog.V(logger.Error).Infof("Error generating DAG for epoch %d (%s)", nextEpoch, dag)
return
}
} else {
glog.V(logger.Error).Infof("DAG for epoch %d (%s)", nextEpoch, dag)
}
}
}
timer = time.After(autoDAGcheckInterval)
case <-self.autodagquit:
return
}
}
}()
}
// dagFiles(epoch) returns the two alternative DAG filenames (not a path)
// 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])>
func dagFiles(epoch uint64) (string, string) {
seedHash, _ := ethash.GetSeedHash(epoch * epochLength)
dag := fmt.Sprintf("full-R%d-%x", ethashRevision, seedHash[:8])
return dag, "full-R" + dag
}
// stopAutoDAG stops automatic DAG pregeneration by quitting the loop
func (self *Ethereum) StopAutoDAG() {
if self.autodagquit != nil {
close(self.autodagquit)
self.autodagquit = nil
}
glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir)
}
func saveProtocolVersion(db common.Database, protov int) { func saveProtocolVersion(db common.Database, protov int) {
d, _ := db.Get([]byte("ProtocolVersion")) d, _ := db.Get([]byte("ProtocolVersion"))
protocolVersion := common.NewValue(d).Uint() protocolVersion := common.NewValue(d).Uint()
@ -571,3 +664,18 @@ func saveBlockchainVersion(db common.Database, bcVersion int) {
db.Put([]byte("BlockchainVersion"), common.NewValue(bcVersion).Bytes()) db.Put([]byte("BlockchainVersion"), common.NewValue(bcVersion).Bytes())
} }
} }
func (self *Ethereum) Solc() (*compiler.Solidity, error) {
var err error
if self.solc == nil {
self.solc, err = compiler.New(self.SolcPath)
}
return self.solc, err
}
// set in js console via admin interface or wrapper from cli flags
func (self *Ethereum) SetSolc(solcPath string) (*compiler.Solidity, error) {
self.SolcPath = solcPath
self.solc = nil
return self.Solc()
}

@ -15,8 +15,10 @@ import (
) )
const ( const (
maxHashFetch = 512 // Amount of hashes to be fetched per chunk MinHashFetch = 512 // Minimum amount of hashes to not consider a peer stalling
maxBlockFetch = 128 // Amount of blocks to be fetched per chunk MaxHashFetch = 2048 // Amount of hashes to be fetched per retrieval request
MaxBlockFetch = 128 // Amount of blocks to be fetched per retrieval request
peerCountTimeout = 12 * time.Second // Amount of time it takes for the peer handler to ignore minDesiredPeerCount peerCountTimeout = 12 * time.Second // Amount of time it takes for the peer handler to ignore minDesiredPeerCount
hashTTL = 5 * time.Second // Time it takes for a hash request to time out hashTTL = 5 * time.Second // Time it takes for a hash request to time out
) )
@ -28,10 +30,11 @@ var (
) )
var ( var (
errLowTd = errors.New("peer's TD is too low") errLowTd = errors.New("peers TD is too low")
ErrBusy = errors.New("busy") ErrBusy = errors.New("busy")
errUnknownPeer = errors.New("peer's unknown or unhealthy") errUnknownPeer = errors.New("peer is unknown or unhealthy")
ErrBadPeer = errors.New("action from bad peer ignored") ErrBadPeer = errors.New("action from bad peer ignored")
ErrStallingPeer = errors.New("peer is stalling")
errNoPeers = errors.New("no peers to keep download active") errNoPeers = errors.New("no peers to keep download active")
ErrPendingQueue = errors.New("pending items in queue") ErrPendingQueue = errors.New("pending items in queue")
ErrTimeout = errors.New("timeout") ErrTimeout = errors.New("timeout")
@ -60,13 +63,18 @@ type hashPack struct {
hashes []common.Hash hashes []common.Hash
} }
type crossCheck struct {
expire time.Time
parent common.Hash
}
type Downloader struct { type Downloader struct {
mux *event.TypeMux mux *event.TypeMux
mu sync.RWMutex mu sync.RWMutex
queue *queue // Scheduler for selecting the hashes to download queue *queue // Scheduler for selecting the hashes to download
peers *peerSet // Set of active peers from which download can proceed peers *peerSet // Set of active peers from which download can proceed
checks map[common.Hash]time.Time // Pending cross checks to verify a hash chain checks map[common.Hash]*crossCheck // Pending cross checks to verify a hash chain
// Callbacks // Callbacks
hasBlock hashCheckFn hasBlock hashCheckFn
@ -157,7 +165,7 @@ func (d *Downloader) Synchronise(id string, hash common.Hash) error {
// Reset the queue and peer set to clean any internal leftover state // Reset the queue and peer set to clean any internal leftover state
d.queue.Reset() d.queue.Reset()
d.peers.Reset() d.peers.Reset()
d.checks = make(map[common.Hash]time.Time) d.checks = make(map[common.Hash]*crossCheck)
// Retrieve the origin peer and initiate the downloading process // Retrieve the origin peer and initiate the downloading process
p := d.peers.Peer(id) p := d.peers.Peer(id)
@ -283,15 +291,22 @@ func (d *Downloader) fetchHashes(p *peer, h common.Hash) error {
return ErrBadPeer return ErrBadPeer
} }
if !done { if !done {
// Check that the peer is not stalling the sync
if len(inserts) < MinHashFetch {
return ErrStallingPeer
}
// Try and fetch a random block to verify the hash batch // Try and fetch a random block to verify the hash batch
// Skip the last hash as the cross check races with the next hash fetch // Skip the last hash as the cross check races with the next hash fetch
if len(inserts) > 1 { cross := rand.Intn(len(inserts) - 1)
cross := inserts[rand.Intn(len(inserts)-1)] origin, parent := inserts[cross], inserts[cross+1]
glog.V(logger.Detail).Infof("Cross checking (%s) with %x", active.id, cross) glog.V(logger.Detail).Infof("Cross checking (%s) with %x/%x", active.id, origin, parent)
d.checks[cross] = time.Now().Add(blockTTL) d.checks[origin] = &crossCheck{
active.getBlocks([]common.Hash{cross}) expire: time.Now().Add(blockTTL),
parent: parent,
} }
active.getBlocks([]common.Hash{origin})
// Also fetch a fresh // Also fetch a fresh
active.getHashes(head) active.getHashes(head)
continue continue
@ -310,8 +325,8 @@ func (d *Downloader) fetchHashes(p *peer, h common.Hash) error {
continue continue
} }
block := blockPack.blocks[0] block := blockPack.blocks[0]
if _, ok := d.checks[block.Hash()]; ok { if check, ok := d.checks[block.Hash()]; ok {
if !d.queue.Has(block.ParentHash()) { if block.ParentHash() != check.parent {
return ErrCrossCheckFailed return ErrCrossCheckFailed
} }
delete(d.checks, block.Hash()) delete(d.checks, block.Hash())
@ -319,8 +334,8 @@ func (d *Downloader) fetchHashes(p *peer, h common.Hash) error {
case <-crossTicker.C: case <-crossTicker.C:
// Iterate over all the cross checks and fail the hash chain if they're not verified // Iterate over all the cross checks and fail the hash chain if they're not verified
for hash, deadline := range d.checks { for hash, check := range d.checks {
if time.Now().After(deadline) { if time.Now().After(check.expire) {
glog.V(logger.Debug).Infof("Cross check timeout for %x", hash) glog.V(logger.Debug).Infof("Cross check timeout for %x", hash)
return ErrCrossCheckFailed return ErrCrossCheckFailed
} }
@ -438,7 +453,7 @@ out:
} }
// Get a possible chunk. If nil is returned no chunk // Get a possible chunk. If nil is returned no chunk
// could be returned due to no hashes available. // could be returned due to no hashes available.
request := d.queue.Reserve(peer, maxBlockFetch) request := d.queue.Reserve(peer, MaxBlockFetch)
if request == nil { if request == nil {
continue continue
} }

@ -53,6 +53,8 @@ type downloadTester struct {
blocks map[common.Hash]*types.Block // Blocks associated with the hashes blocks map[common.Hash]*types.Block // Blocks associated with the hashes
chain []common.Hash // Block-chain being constructed chain []common.Hash // Block-chain being constructed
maxHashFetch int // Overrides the maximum number of retrieved hashes
t *testing.T t *testing.T
pcount int pcount int
done chan bool done chan bool
@ -133,8 +135,12 @@ func (dl *downloadTester) getBlock(hash common.Hash) *types.Block {
// getHashes retrieves a batch of hashes for reconstructing the chain. // getHashes retrieves a batch of hashes for reconstructing the chain.
func (dl *downloadTester) getHashes(head common.Hash) error { func (dl *downloadTester) getHashes(head common.Hash) error {
limit := MaxHashFetch
if dl.maxHashFetch > 0 {
limit = dl.maxHashFetch
}
// Gather the next batch of hashes // Gather the next batch of hashes
hashes := make([]common.Hash, 0, maxHashFetch) hashes := make([]common.Hash, 0, limit)
for i, hash := range dl.hashes { for i, hash := range dl.hashes {
if hash == head { if hash == head {
i++ i++
@ -382,7 +388,7 @@ func TestRepeatingHashAttack(t *testing.T) {
// Make sure that syncing returns and does so with a failure // Make sure that syncing returns and does so with a failure
select { select {
case <-time.After(100 * time.Millisecond): case <-time.After(time.Second):
t.Fatalf("synchronisation blocked") t.Fatalf("synchronisation blocked")
case err := <-errc: case err := <-errc:
if err == nil { if err == nil {
@ -469,6 +475,23 @@ func TestMadeupHashChainAttack(t *testing.T) {
} }
} }
// Tests that if a malicious peer makes up a random hash chain, and tries to push
// indefinitely, one hash at a time, it actually gets caught with it. The reason
// this is separate from the classical made up chain attack is that sending hashes
// one by one prevents reliable block/parent verification.
func TestMadeupHashChainDrippingAttack(t *testing.T) {
// Create a random chain of hashes to drip
hashes := createHashes(0, 16*blockCacheLimit)
tester := newTester(t, hashes, nil)
// Try and sync with the attacker, one hash at a time
tester.maxHashFetch = 1
tester.newPeer("attack", big.NewInt(10000), hashes[0])
if _, err := tester.syncTake("attack", hashes[0]); err != ErrStallingPeer {
t.Fatalf("synchronisation error mismatch: have %v, want %v", err, ErrStallingPeer)
}
}
// Tests that if a malicious peer makes up a random block chain, and tried to // Tests that if a malicious peer makes up a random block chain, and tried to
// push indefinitely, it actually gets caught with it. // push indefinitely, it actually gets caught with it.
func TestMadeupBlockChainAttack(t *testing.T) { func TestMadeupBlockChainAttack(t *testing.T) {
@ -479,7 +502,7 @@ func TestMadeupBlockChainAttack(t *testing.T) {
crossCheckCycle = 25 * time.Millisecond crossCheckCycle = 25 * time.Millisecond
// Create a long chain of blocks and simulate an invalid chain by dropping every second // Create a long chain of blocks and simulate an invalid chain by dropping every second
hashes := createHashes(0, 32*blockCacheLimit) hashes := createHashes(0, 16*blockCacheLimit)
blocks := createBlocksFromHashes(hashes) blocks := createBlocksFromHashes(hashes)
gapped := make([]common.Hash, len(hashes)/2) gapped := make([]common.Hash, len(hashes)/2)
@ -502,3 +525,37 @@ func TestMadeupBlockChainAttack(t *testing.T) {
t.Fatalf("failed to synchronise blocks: %v", err) t.Fatalf("failed to synchronise blocks: %v", err)
} }
} }
// Advanced form of the above forged blockchain attack, where not only does the
// attacker make up a valid hashes for random blocks, but also forges the block
// parents to point to existing hashes.
func TestMadeupParentBlockChainAttack(t *testing.T) {
defaultBlockTTL := blockTTL
defaultCrossCheckCycle := crossCheckCycle
blockTTL = 100 * time.Millisecond
crossCheckCycle = 25 * time.Millisecond
// Create a long chain of blocks and simulate an invalid chain by dropping every second
hashes := createHashes(0, 16*blockCacheLimit)
blocks := createBlocksFromHashes(hashes)
forges := createBlocksFromHashes(hashes)
for hash, block := range forges {
block.ParentHeaderHash = hash // Simulate pointing to already known hash
}
// Try and sync with the malicious node and check that it fails
tester := newTester(t, hashes, forges)
tester.newPeer("attack", big.NewInt(10000), hashes[0])
if _, err := tester.syncTake("attack", hashes[0]); err != ErrCrossCheckFailed {
t.Fatalf("synchronisation error mismatch: have %v, want %v", err, ErrCrossCheckFailed)
}
// Ensure that a valid chain can still pass sync
blockTTL = defaultBlockTTL
crossCheckCycle = defaultCrossCheckCycle
tester.blocks = blocks
tester.newPeer("valid", big.NewInt(20000), hashes[0])
if _, err := tester.syncTake("valid", hashes[0]); err != nil {
t.Fatalf("failed to synchronise blocks: %v", err)
}
}

@ -17,7 +17,7 @@ import (
) )
const ( const (
blockCacheLimit = 1024 // Maximum number of blocks to cache before throttling the download blockCacheLimit = 8 * MaxBlockFetch // Maximum number of blocks to cache before throttling the download
) )
// fetchRequest is a currently running block retrieval operation. // fetchRequest is a currently running block retrieval operation.

@ -47,9 +47,7 @@ type ProtocolManager struct {
txpool txPool txpool txPool
chainman *core.ChainManager chainman *core.ChainManager
downloader *downloader.Downloader downloader *downloader.Downloader
peers *peerSet
pmu sync.Mutex
peers map[string]*peer
SubProtocol p2p.Protocol SubProtocol p2p.Protocol
@ -73,7 +71,7 @@ func NewProtocolManager(protocolVersion, networkId int, mux *event.TypeMux, txpo
txpool: txpool, txpool: txpool,
chainman: chainman, chainman: chainman,
downloader: downloader, downloader: downloader,
peers: make(map[string]*peer), peers: newPeerSet(),
newPeerCh: make(chan *peer, 1), newPeerCh: make(chan *peer, 1),
quitSync: make(chan struct{}), quitSync: make(chan struct{}),
} }
@ -95,10 +93,14 @@ func NewProtocolManager(protocolVersion, networkId int, mux *event.TypeMux, txpo
} }
func (pm *ProtocolManager) removePeer(peer *peer) { func (pm *ProtocolManager) removePeer(peer *peer) {
pm.pmu.Lock() // Unregister the peer from the downloader
defer pm.pmu.Unlock()
pm.downloader.UnregisterPeer(peer.id) pm.downloader.UnregisterPeer(peer.id)
delete(pm.peers, peer.id)
// Remove the peer from the Ethereum peer set too
glog.V(logger.Detail).Infoln("Removing peer", peer.id)
if err := pm.peers.Unregister(peer.id); err != nil {
glog.V(logger.Error).Infoln("Removal failed:", err)
}
} }
func (pm *ProtocolManager) Start() { func (pm *ProtocolManager) Start() {
@ -136,31 +138,32 @@ func (pm *ProtocolManager) newPeer(pv, nv int, p *p2p.Peer, rw p2p.MsgReadWriter
} }
func (pm *ProtocolManager) handle(p *peer) error { func (pm *ProtocolManager) handle(p *peer) error {
// Execute the Ethereum handshake, short circuit if fails
if err := p.handleStatus(); err != nil { if err := p.handleStatus(); err != nil {
return err return err
} }
pm.pmu.Lock() // Register the peer locally and in the downloader too
pm.peers[p.id] = p glog.V(logger.Detail).Infoln("Adding peer", p.id)
pm.pmu.Unlock() if err := pm.peers.Register(p); err != nil {
glog.V(logger.Error).Infoln("Addition failed:", err)
pm.downloader.RegisterPeer(p.id, p.recentHash, p.requestHashes, p.requestBlocks) return err
defer func() { }
pm.removePeer(p) defer pm.removePeer(p)
}()
if err := pm.downloader.RegisterPeer(p.id, p.recentHash, p.requestHashes, p.requestBlocks); err != nil {
return err
}
// propagate existing transactions. new transactions appearing // propagate existing transactions. new transactions appearing
// after this will be sent via broadcasts. // after this will be sent via broadcasts.
if err := p.sendTransactions(pm.txpool.GetTransactions()); err != nil { if err := p.sendTransactions(pm.txpool.GetTransactions()); err != nil {
return err return err
} }
// main loop. handle incoming messages. // main loop. handle incoming messages.
for { for {
if err := pm.handleMsg(p); err != nil { if err := pm.handleMsg(p); err != nil {
return err return err
} }
} }
return nil return nil
} }
@ -203,8 +206,8 @@ func (self *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "->msg %v: %v", msg, err) return errResp(ErrDecode, "->msg %v: %v", msg, err)
} }
if request.Amount > maxHashes { if request.Amount > downloader.MaxHashFetch {
request.Amount = maxHashes request.Amount = downloader.MaxHashFetch
} }
hashes := self.chainman.GetBlockHashesFromHash(request.Hash, request.Amount) hashes := self.chainman.GetBlockHashesFromHash(request.Hash, request.Amount)
@ -251,7 +254,7 @@ func (self *ProtocolManager) handleMsg(p *peer) error {
if block != nil { if block != nil {
blocks = append(blocks, block) blocks = append(blocks, block)
} }
if i == maxBlocks { if i == downloader.MaxBlockFetch {
break break
} }
} }
@ -346,18 +349,8 @@ func (pm *ProtocolManager) verifyTd(peer *peer, request newBlockMsgData) error {
// out which peers do not contain the block in their block set and will do a // out which peers do not contain the block in their block set and will do a
// sqrt(peers) to determine the amount of peers we broadcast to. // sqrt(peers) to determine the amount of peers we broadcast to.
func (pm *ProtocolManager) BroadcastBlock(hash common.Hash, block *types.Block) { func (pm *ProtocolManager) BroadcastBlock(hash common.Hash, block *types.Block) {
pm.pmu.Lock() // Broadcast block to a batch of peers not knowing about it
defer pm.pmu.Unlock() peers := pm.peers.PeersWithoutBlock(hash)
// Find peers who don't know anything about the given hash. Peers that
// don't know about the hash will be a candidate for the broadcast loop
var peers []*peer
for _, peer := range pm.peers {
if !peer.blockHashes.Has(hash) {
peers = append(peers, peer)
}
}
// Broadcast block to peer set
peers = peers[:int(math.Sqrt(float64(len(peers))))] peers = peers[:int(math.Sqrt(float64(len(peers))))]
for _, peer := range peers { for _, peer := range peers {
peer.sendNewBlock(block) peer.sendNewBlock(block)
@ -369,18 +362,8 @@ func (pm *ProtocolManager) BroadcastBlock(hash common.Hash, block *types.Block)
// out which peers do not contain the block in their block set and will do a // out which peers do not contain the block in their block set and will do a
// sqrt(peers) to determine the amount of peers we broadcast to. // sqrt(peers) to determine the amount of peers we broadcast to.
func (pm *ProtocolManager) BroadcastTx(hash common.Hash, tx *types.Transaction) { func (pm *ProtocolManager) BroadcastTx(hash common.Hash, tx *types.Transaction) {
pm.pmu.Lock() // Broadcast transaction to a batch of peers not knowing about it
defer pm.pmu.Unlock() peers := pm.peers.PeersWithoutTx(hash)
// Find peers who don't know anything about the given hash. Peers that
// don't know about the hash will be a candidate for the broadcast loop
var peers []*peer
for _, peer := range pm.peers {
if !peer.txHashes.Has(hash) {
peers = append(peers, peer)
}
}
// Broadcast block to peer set
//FIXME include this again: peers = peers[:int(math.Sqrt(float64(len(peers))))] //FIXME include this again: peers = peers[:int(math.Sqrt(float64(len(peers))))]
for _, peer := range peers { for _, peer := range peers {
peer.sendTransaction(tx) peer.sendTransaction(tx)

@ -1,17 +1,25 @@
package eth package eth
import ( import (
"errors"
"fmt" "fmt"
"math/big" "math/big"
"sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"gopkg.in/fatih/set.v0" "gopkg.in/fatih/set.v0"
) )
var (
errAlreadyRegistered = errors.New("peer is already registered")
errNotRegistered = errors.New("peer is not registered")
)
type statusMsgData struct { type statusMsgData struct {
ProtocolVersion uint32 ProtocolVersion uint32
NetworkId uint32 NetworkId uint32
@ -25,16 +33,6 @@ type getBlockHashesMsgData struct {
Amount uint64 Amount uint64
} }
func getBestPeer(peers map[string]*peer) *peer {
var peer *peer
for _, cp := range peers {
if peer == nil || cp.td.Cmp(peer.td) > 0 {
peer = cp
}
}
return peer
}
type peer struct { type peer struct {
*p2p.Peer *p2p.Peer
@ -103,8 +101,8 @@ func (p *peer) sendTransaction(tx *types.Transaction) error {
} }
func (p *peer) requestHashes(from common.Hash) error { func (p *peer) requestHashes(from common.Hash) error {
glog.V(logger.Debug).Infof("[%s] fetching hashes (%d) %x...\n", p.id, maxHashes, from[:4]) glog.V(logger.Debug).Infof("[%s] fetching hashes (%d) %x...\n", p.id, downloader.MaxHashFetch, from[:4])
return p2p.Send(p.rw, GetBlockHashesMsg, getBlockHashesMsgData{from, maxHashes}) return p2p.Send(p.rw, GetBlockHashesMsg, getBlockHashesMsgData{from, downloader.MaxHashFetch})
} }
func (p *peer) requestBlocks(hashes []common.Hash) error { func (p *peer) requestBlocks(hashes []common.Hash) error {
@ -159,3 +157,103 @@ func (p *peer) handleStatus() error {
return <-errc return <-errc
} }
// peerSet represents the collection of active peers currently participating in
// the Ethereum sub-protocol.
type peerSet struct {
peers map[string]*peer
lock sync.RWMutex
}
// newPeerSet creates a new peer set to track the active participants.
func newPeerSet() *peerSet {
return &peerSet{
peers: make(map[string]*peer),
}
}
// Register injects a new peer into the working set, or returns an error if the
// peer is already known.
func (ps *peerSet) Register(p *peer) error {
ps.lock.Lock()
defer ps.lock.Unlock()
if _, ok := ps.peers[p.id]; ok {
return errAlreadyRegistered
}
ps.peers[p.id] = p
return nil
}
// Unregister removes a remote peer from the active set, disabling any further
// actions to/from that particular entity.
func (ps *peerSet) Unregister(id string) error {
ps.lock.Lock()
defer ps.lock.Unlock()
if _, ok := ps.peers[id]; !ok {
return errNotRegistered
}
delete(ps.peers, id)
return nil
}
// Peer retrieves the registered peer with the given id.
func (ps *peerSet) Peer(id string) *peer {
ps.lock.RLock()
defer ps.lock.RUnlock()
return ps.peers[id]
}
// Len returns if the current number of peers in the set.
func (ps *peerSet) Len() int {
ps.lock.RLock()
defer ps.lock.RUnlock()
return len(ps.peers)
}
// PeersWithoutBlock retrieves a list of peers that do not have a given block in
// their set of known hashes.
func (ps *peerSet) PeersWithoutBlock(hash common.Hash) []*peer {
ps.lock.RLock()
defer ps.lock.RUnlock()
list := make([]*peer, 0, len(ps.peers))
for _, p := range ps.peers {
if !p.blockHashes.Has(hash) {
list = append(list, p)
}
}
return list
}
// PeersWithoutTx retrieves a list of peers that do not have a given transaction
// in their set of known hashes.
func (ps *peerSet) PeersWithoutTx(hash common.Hash) []*peer {
ps.lock.RLock()
defer ps.lock.RUnlock()
list := make([]*peer, 0, len(ps.peers))
for _, p := range ps.peers {
if !p.txHashes.Has(hash) {
list = append(list, p)
}
}
return list
}
// BestPeer retrieves the known peer with the currently highest total difficulty.
func (ps *peerSet) BestPeer() *peer {
ps.lock.RLock()
defer ps.lock.RUnlock()
var best *peer
for _, p := range ps.peers {
if best == nil || p.td.Cmp(best.td) > 0 {
best = p
}
}
return best
}

@ -12,8 +12,6 @@ const (
NetworkId = 0 NetworkId = 0
ProtocolLength = uint64(8) ProtocolLength = uint64(8)
ProtocolMaxMsgSize = 10 * 1024 * 1024 ProtocolMaxMsgSize = 10 * 1024 * 1024
maxHashes = 512
maxBlocks = 128
) )
// eth protocol message codes // eth protocol message codes

@ -10,8 +10,8 @@ import (
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
) )
// Sync contains all synchronisation code for the eth protocol // update periodically tries to synchronise with the network, both downloading
// hashes and blocks as well as retrieving cached ones.
func (pm *ProtocolManager) update() { func (pm *ProtocolManager) update() {
forceSync := time.Tick(forceSyncCycle) forceSync := time.Tick(forceSyncCycle)
blockProc := time.Tick(blockProcCycle) blockProc := time.Tick(blockProcCycle)
@ -20,22 +20,16 @@ func (pm *ProtocolManager) update() {
for { for {
select { select {
case <-pm.newPeerCh: case <-pm.newPeerCh:
// Meet the `minDesiredPeerCount` before we select our best peer // Make sure we have peers to select from, then sync
if len(pm.peers) < minDesiredPeerCount { if pm.peers.Len() < minDesiredPeerCount {
break break
} }
// Find the best peer and synchronise with it go pm.synchronise(pm.peers.BestPeer())
peer := getBestPeer(pm.peers)
if peer == nil {
glog.V(logger.Debug).Infoln("Sync attempt canceled. No peers available")
}
go pm.synchronise(peer)
case <-forceSync: case <-forceSync:
// Force a sync even if not enough peers are present // Force a sync even if not enough peers are present
if peer := getBestPeer(pm.peers); peer != nil { go pm.synchronise(pm.peers.BestPeer())
go pm.synchronise(peer)
}
case <-blockProc: case <-blockProc:
// Try to pull some blocks from the downloaded // Try to pull some blocks from the downloaded
if atomic.CompareAndSwapInt32(&blockProcPend, 0, 1) { if atomic.CompareAndSwapInt32(&blockProcPend, 0, 1) {
@ -51,10 +45,9 @@ func (pm *ProtocolManager) update() {
} }
} }
// processBlocks will attempt to reconstruct a chain by checking the first item and check if it's // processBlocks retrieves downloaded blocks from the download cache and tries
// a known parent. The first block in the chain may be unknown during downloading. When the // to construct the local block chain with it. Note, since the block retrieval
// downloader isn't downloading blocks will be dropped with an unknown parent until either it // order matters, access to this function *must* be synchronized/serialized.
// has depleted the list or found a known parent.
func (pm *ProtocolManager) processBlocks() error { func (pm *ProtocolManager) processBlocks() error {
pm.wg.Add(1) pm.wg.Add(1)
defer pm.wg.Done() defer pm.wg.Done()
@ -79,15 +72,24 @@ func (pm *ProtocolManager) processBlocks() error {
return nil return nil
} }
// synchronise tries to sync up our local block chain with a remote peer, both
// adding various sanity checks as well as wrapping it with various log entries.
func (pm *ProtocolManager) synchronise(peer *peer) { func (pm *ProtocolManager) synchronise(peer *peer) {
// Short circuit if no peers are available
if peer == nil {
glog.V(logger.Debug).Infoln("Synchronisation canceled: no peers available")
return
}
// Make sure the peer's TD is higher than our own. If not drop. // Make sure the peer's TD is higher than our own. If not drop.
if peer.td.Cmp(pm.chainman.Td()) <= 0 { if peer.td.Cmp(pm.chainman.Td()) <= 0 {
glog.V(logger.Debug).Infoln("Synchronisation canceled: peer TD too small")
return return
} }
// FIXME if we have the hash in our chain and the TD of the peer is // FIXME if we have the hash in our chain and the TD of the peer is
// much higher than ours, something is wrong with us or the peer. // much higher than ours, something is wrong with us or the peer.
// Check if the hash is on our own chain // Check if the hash is on our own chain
if pm.chainman.HasBlock(peer.recentHash) { if pm.chainman.HasBlock(peer.recentHash) {
glog.V(logger.Debug).Infoln("Synchronisation canceled: head already known")
return return
} }
// Get the hashes from the peer (synchronously) // Get the hashes from the peer (synchronously)

@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/opt" "github.com/syndtr/goleveldb/leveldb/opt"
) )
@ -24,9 +25,17 @@ type LDBDatabase struct {
quit chan struct{} quit chan struct{}
} }
// NewLDBDatabase returns a LevelDB wrapped object. LDBDatabase does not persist data by
// it self but requires a background poller which syncs every X. `Flush` should be called
// when data needs to be stored and written to disk.
func NewLDBDatabase(file string) (*LDBDatabase, error) { func NewLDBDatabase(file string) (*LDBDatabase, error) {
// Open the db // Open the db
db, err := leveldb.OpenFile(file, &opt.Options{OpenFilesCacheCapacity: OpenFileLimit}) db, err := leveldb.OpenFile(file, &opt.Options{OpenFilesCacheCapacity: OpenFileLimit})
// check for curruption and attempt to recover
if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted {
db, err = leveldb.RecoverFile(file, nil)
}
// (re) check for errors and abort if opening of the db failed
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -44,21 +53,15 @@ func (self *LDBDatabase) makeQueue() {
self.queue = make(map[string][]byte) self.queue = make(map[string][]byte)
} }
// Put puts the given key / value to the queue
func (self *LDBDatabase) Put(key []byte, value []byte) { func (self *LDBDatabase) Put(key []byte, value []byte) {
self.mu.Lock() self.mu.Lock()
defer self.mu.Unlock() defer self.mu.Unlock()
self.queue[string(key)] = value self.queue[string(key)] = value
/*
value = rle.Compress(value)
err := self.db.Put(key, value, nil)
if err != nil {
fmt.Println("Error put", err)
}
*/
} }
// Get returns the given key if it's present.
func (self *LDBDatabase) Get(key []byte) ([]byte, error) { func (self *LDBDatabase) Get(key []byte) ([]byte, error) {
self.mu.Lock() self.mu.Lock()
defer self.mu.Unlock() defer self.mu.Unlock()
@ -76,6 +79,7 @@ func (self *LDBDatabase) Get(key []byte) ([]byte, error) {
return rle.Decompress(dat) return rle.Decompress(dat)
} }
// Delete deletes the key from the queue and database
func (self *LDBDatabase) Delete(key []byte) error { func (self *LDBDatabase) Delete(key []byte) error {
self.mu.Lock() self.mu.Lock()
defer self.mu.Unlock() defer self.mu.Unlock()
@ -100,6 +104,7 @@ func (self *LDBDatabase) NewIterator() iterator.Iterator {
return self.db.NewIterator(nil, nil) return self.db.NewIterator(nil, nil)
} }
// Flush flushes out the queue to leveldb
func (self *LDBDatabase) Flush() error { func (self *LDBDatabase) Flush() error {
self.mu.Lock() self.mu.Lock()
defer self.mu.Unlock() defer self.mu.Unlock()

File diff suppressed because it is too large Load Diff

@ -40,7 +40,6 @@ func (self *CpuAgent) Stop() {
defer self.mu.Unlock() defer self.mu.Unlock()
close(self.quit) close(self.quit)
close(self.quitCurrentOp)
} }
func (self *CpuAgent) Start() { func (self *CpuAgent) Start() {
@ -50,7 +49,6 @@ func (self *CpuAgent) Start() {
self.quit = make(chan struct{}) self.quit = make(chan struct{})
// creating current op ch makes sure we're not closing a nil ch // creating current op ch makes sure we're not closing a nil ch
// later on // later on
self.quitCurrentOp = make(chan struct{})
self.workCh = make(chan *types.Block, 1) self.workCh = make(chan *types.Block, 1)
go self.update() go self.update()
@ -62,11 +60,19 @@ out:
select { select {
case block := <-self.workCh: case block := <-self.workCh:
self.mu.Lock() self.mu.Lock()
close(self.quitCurrentOp) if self.quitCurrentOp != nil {
close(self.quitCurrentOp)
}
self.quitCurrentOp = make(chan struct{})
go self.mine(block, self.quitCurrentOp)
self.mu.Unlock() self.mu.Unlock()
go self.mine(block)
case <-self.quit: case <-self.quit:
self.mu.Lock()
if self.quitCurrentOp != nil {
close(self.quitCurrentOp)
self.quitCurrentOp = nil
}
self.mu.Unlock()
break out break out
} }
} }
@ -84,16 +90,11 @@ done:
} }
} }
func (self *CpuAgent) mine(block *types.Block) { func (self *CpuAgent) mine(block *types.Block, stop <- chan struct{}) {
glog.V(logger.Debug).Infof("(re)started agent[%d]. mining...\n", self.index) glog.V(logger.Debug).Infof("(re)started agent[%d]. mining...\n", self.index)
// Reset the channel
self.mu.Lock()
self.quitCurrentOp = make(chan struct{})
self.mu.Unlock()
// Mine // Mine
nonce, mixDigest := self.pow.Search(block, self.quitCurrentOp) nonce, mixDigest := self.pow.Search(block, stop)
if nonce != 0 { if nonce != 0 {
block.SetNonce(nonce) block.SetNonce(nonce)
block.Header().MixDigest = common.BytesToHash(mixDigest) block.Header().MixDigest = common.BytesToHash(mixDigest)

@ -39,6 +39,10 @@ func New(eth core.Backend, mux *event.TypeMux, pow pow.PoW) *Miner {
return miner return miner
} }
// update keeps track of the downloader events. Please be aware that this is a one shot type of update loop.
// It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and
// the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks
// and halt your mining operation for as long as the DOS continues.
func (self *Miner) update() { func (self *Miner) update() {
events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
for ev := range events.Chan() { for ev := range events.Chan() {
@ -59,6 +63,10 @@ func (self *Miner) update() {
self.Start(self.coinbase, self.threads) self.Start(self.coinbase, self.threads)
} }
} }
// unsubscribe. we're only interested in this event once
events.Unsubscribe()
// stop immediately and ignore all further pending events
break
} }
} }

@ -224,7 +224,13 @@ func (self *worker) wait() {
} }
self.mux.Post(core.NewMinedBlockEvent{block}) self.mux.Post(core.NewMinedBlockEvent{block})
glog.V(logger.Info).Infof("🔨 Mined block #%v", block.Number()) var stale string
canonBlock := self.chain.GetBlockByNumber(block.NumberU64())
if canonBlock != nil && canonBlock.Hash() != block.Hash() {
stale = "stale-"
}
glog.V(logger.Info).Infof("🔨 Mined %sblock #%v (%x)", stale, block.Number(), block.Hash().Bytes()[:4])
jsonlogger.LogJson(&logger.EthMinerNewBlock{ jsonlogger.LogJson(&logger.EthMinerNewBlock{
BlockHash: block.Hash().Hex(), BlockHash: block.Hash().Hex(),
@ -264,6 +270,7 @@ func (self *worker) makeCurrent() {
} }
block.Header().Extra = self.extra block.Header().Extra = self.extra
// when 08 is processed ancestors contain 07 (quick block)
current := env(block, self.eth) current := env(block, self.eth)
for _, ancestor := range self.chain.GetAncestors(block, 7) { for _, ancestor := range self.chain.GetAncestors(block, 7) {
for _, uncle := range ancestor.Uncles() { for _, uncle := range ancestor.Uncles() {

@ -1,9 +1,9 @@
package rpc package rpc
import ( import (
"bytes"
"encoding/json" "encoding/json"
"math/big" "math/big"
// "sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -158,16 +158,16 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
v := api.xethAtStateNum(args.BlockNumber).CodeAtBytes(args.Address) v := api.xethAtStateNum(args.BlockNumber).CodeAtBytes(args.Address)
*reply = newHexData(v) *reply = newHexData(v)
case "eth_sign": // case "eth_sign":
args := new(NewSigArgs) // args := new(NewSigArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { // if err := json.Unmarshal(req.Params, &args); err != nil {
return err // return err
} // }
v, err := api.xeth().Sign(args.From, args.Data, false) // v, err := api.xeth().Sign(args.From, args.Data, false)
if err != nil { // if err != nil {
return err // return err
} // }
*reply = v // *reply = v
case "eth_sendTransaction", "eth_transact": case "eth_sendTransaction", "eth_transact":
args := new(NewTxArgs) args := new(NewTxArgs)
@ -230,7 +230,14 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
block := api.xeth().EthBlockByNumber(args.BlockNumber) block := api.xeth().EthBlockByNumber(args.BlockNumber)
br := NewBlockRes(block, args.IncludeTxs) br := NewBlockRes(block, args.IncludeTxs)
// If request was for "pending", nil nonsensical fields
if args.BlockNumber == -2 {
br.BlockHash = nil
br.BlockNumber = nil
br.Miner = nil
br.Nonce = nil
br.LogsBloom = nil
}
*reply = br *reply = br
case "eth_getTransactionByHash": case "eth_getTransactionByHash":
args := new(HashArgs) args := new(HashArgs)
@ -240,9 +247,12 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
tx, bhash, bnum, txi := api.xeth().EthTransactionByHash(args.Hash) tx, bhash, bnum, txi := api.xeth().EthTransactionByHash(args.Hash)
if tx != nil { if tx != nil {
v := NewTransactionRes(tx) v := NewTransactionRes(tx)
v.BlockHash = newHexData(bhash) // if the blockhash is 0, assume this is a pending transaction
v.BlockNumber = newHexNum(bnum) if bytes.Compare(bhash.Bytes(), bytes.Repeat([]byte{0}, 32)) != 0 {
v.TxIndex = newHexNum(txi) v.BlockHash = newHexData(bhash)
v.BlockNumber = newHexNum(bnum)
v.TxIndex = newHexNum(txi)
}
*reply = v *reply = v
} }
case "eth_getTransactionByBlockHashAndIndex": case "eth_getTransactionByBlockHashAndIndex":
@ -337,7 +347,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
solc, _ := api.xeth().Solc() solc, _ := api.xeth().Solc()
if solc == nil { if solc == nil {
return NewNotImplementedError(req.Method) return NewNotAvailableError(req.Method, "solc (solidity compiler) not found")
} }
args := new(SourceArgs) args := new(SourceArgs)
@ -345,12 +355,11 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err return err
} }
contract, err := solc.Compile(args.Source) contracts, err := solc.Compile(args.Source)
if err != nil { if err != nil {
return err return err
} }
contract.Code = newHexData(contract.Code).String() *reply = contracts
*reply = contract
case "eth_newFilter": case "eth_newFilter":
args := new(BlockFilterArgs) args := new(BlockFilterArgs)
@ -577,7 +586,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return NewNotImplementedError(req.Method) return NewNotImplementedError(req.Method)
} }
glog.V(logger.Detail).Infof("Reply: %T %s\n", reply, reply) // glog.V(logger.Detail).Infof("Reply: %v\n", reply)
return nil return nil
} }

@ -2,14 +2,11 @@ package rpc
import ( import (
"encoding/json" "encoding/json"
// "sync"
"testing"
// "time"
// "fmt"
"io/ioutil"
"strconv" "strconv"
"testing"
"github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
) )
@ -30,12 +27,15 @@ func TestWeb3Sha3(t *testing.T) {
} }
} }
const solcVersion = "0.9.23"
func TestCompileSolidity(t *testing.T) { func TestCompileSolidity(t *testing.T) {
t.Skip()
solc, err := compiler.New("") solc, err := compiler.New("")
if solc == nil { if solc == nil {
t.Skip("no solidity compiler") t.Skip("no solc found: skip")
} else if solc.Version() != solcVersion {
t.Logf("WARNING: solc different version found (%v, test written for %v, may need to update)", solc.Version(), solcVersion)
} }
source := `contract test {\n` + source := `contract test {\n` +
" /// @notice Will multiply `a` by 7." + `\n` + " /// @notice Will multiply `a` by 7." + `\n` +
@ -46,16 +46,16 @@ func TestCompileSolidity(t *testing.T) {
jsonstr := `{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["` + source + `"],"id":64}` jsonstr := `{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["` + source + `"],"id":64}`
//expCode := "605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056" expCode := "0x605880600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b603d6004803590602001506047565b8060005260206000f35b60006007820290506053565b91905056"
expAbiDefinition := `[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]` expAbiDefinition := `[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]`
expUserDoc := `{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}}` expUserDoc := `{"methods":{"multiply(uint256)":{"notice":"Will multiply ` + "`a`" + ` by 7."}}}`
expDeveloperDoc := `{"methods":{}}` expDeveloperDoc := `{"methods":{}}`
expCompilerVersion := `0.9.13` expCompilerVersion := solc.Version()
expLanguage := "Solidity" expLanguage := "Solidity"
expLanguageVersion := "0" expLanguageVersion := "0"
expSource := source expSource := source
api := NewEthereumApi(&xeth.XEth{}) api := NewEthereumApi(xeth.NewTest(&eth.Ethereum{}, nil))
var req RpcRequest var req RpcRequest
json.Unmarshal([]byte(jsonstr), &req) json.Unmarshal([]byte(jsonstr), &req)
@ -70,26 +70,34 @@ func TestCompileSolidity(t *testing.T) {
t.Errorf("expected no error, got %v", err) t.Errorf("expected no error, got %v", err)
} }
var contract = compiler.Contract{} var contracts = make(map[string]*compiler.Contract)
err = json.Unmarshal(respjson, &contract) err = json.Unmarshal(respjson, &contracts)
if err != nil { if err != nil {
t.Errorf("expected no error, got %v", err) t.Errorf("expected no error, got %v", err)
} }
/* if len(contracts) != 1 {
if contract.Code != expCode { t.Errorf("expected one contract, got %v", len(contracts))
t.Errorf("Expected %s got %s", expCode, contract.Code) }
}
*/ contract := contracts["test"]
if contract.Code != expCode {
t.Errorf("Expected \n%s got \n%s", expCode, contract.Code)
}
if strconv.Quote(contract.Info.Source) != `"`+expSource+`"` { if strconv.Quote(contract.Info.Source) != `"`+expSource+`"` {
t.Errorf("Expected \n'%s' got \n'%s'", expSource, strconv.Quote(contract.Info.Source)) t.Errorf("Expected \n'%s' got \n'%s'", expSource, strconv.Quote(contract.Info.Source))
} }
if contract.Info.Language != expLanguage { if contract.Info.Language != expLanguage {
t.Errorf("Expected %s got %s", expLanguage, contract.Info.Language) t.Errorf("Expected %s got %s", expLanguage, contract.Info.Language)
} }
if contract.Info.LanguageVersion != expLanguageVersion { if contract.Info.LanguageVersion != expLanguageVersion {
t.Errorf("Expected %s got %s", expLanguageVersion, contract.Info.LanguageVersion) t.Errorf("Expected %s got %s", expLanguageVersion, contract.Info.LanguageVersion)
} }
if contract.Info.CompilerVersion != expCompilerVersion { if contract.Info.CompilerVersion != expCompilerVersion {
t.Errorf("Expected %s got %s", expCompilerVersion, contract.Info.CompilerVersion) t.Errorf("Expected %s got %s", expCompilerVersion, contract.Info.CompilerVersion)
} }
@ -112,8 +120,6 @@ func TestCompileSolidity(t *testing.T) {
if string(abidef) != expAbiDefinition { if string(abidef) != expAbiDefinition {
t.Errorf("Expected \n'%s' got \n'%s'", expAbiDefinition, string(abidef)) t.Errorf("Expected \n'%s' got \n'%s'", expAbiDefinition, string(abidef))
} }
ioutil.WriteFile("/tmp/abidef", []byte(string(abidef)), 0700)
ioutil.WriteFile("/tmp/expabidef", []byte(expAbiDefinition), 0700)
if string(userdoc) != expUserDoc { if string(userdoc) != expUserDoc {
t.Errorf("Expected \n'%s' got \n'%s'", expUserDoc, string(userdoc)) t.Errorf("Expected \n'%s' got \n'%s'", expUserDoc, string(userdoc))

@ -166,45 +166,45 @@ type NewTxArgs struct {
BlockNumber int64 BlockNumber int64
} }
type NewSigArgs struct { // type NewSigArgs struct {
From string // From string
Data string // Data string
} // }
func (args *NewSigArgs) UnmarshalJSON(b []byte) (err error) { // func (args *NewSigArgs) UnmarshalJSON(b []byte) (err error) {
var obj []json.RawMessage // var obj []json.RawMessage
var ext struct { // var ext struct {
From string // From string
Data string // Data string
} // }
// Decode byte slice to array of RawMessages // // Decode byte slice to array of RawMessages
if err := json.Unmarshal(b, &obj); err != nil { // if err := json.Unmarshal(b, &obj); err != nil {
return NewDecodeParamError(err.Error()) // return NewDecodeParamError(err.Error())
} // }
// Check for sufficient params // // Check for sufficient params
if len(obj) < 1 { // if len(obj) < 1 {
return NewInsufficientParamsError(len(obj), 1) // return NewInsufficientParamsError(len(obj), 1)
} // }
// Decode 0th RawMessage to temporary struct // // Decode 0th RawMessage to temporary struct
if err := json.Unmarshal(obj[0], &ext); err != nil { // if err := json.Unmarshal(obj[0], &ext); err != nil {
return NewDecodeParamError(err.Error()) // return NewDecodeParamError(err.Error())
} // }
if len(ext.From) == 0 { // if len(ext.From) == 0 {
return NewValidationError("from", "is required") // return NewValidationError("from", "is required")
} // }
if len(ext.Data) == 0 { // if len(ext.Data) == 0 {
return NewValidationError("data", "is required") // return NewValidationError("data", "is required")
} // }
args.From = ext.From // args.From = ext.From
args.Data = ext.Data // args.Data = ext.Data
return nil // return nil
} // }
func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
var obj []json.RawMessage var obj []json.RawMessage

@ -6,6 +6,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
@ -39,7 +40,7 @@ func Start(pipe *xeth.XEth, config RpcConfig) error {
if len(config.CorsDomain) > 0 { if len(config.CorsDomain) > 0 {
var opts cors.Options var opts cors.Options
opts.AllowedMethods = []string{"POST"} opts.AllowedMethods = []string{"POST"}
opts.AllowedOrigins = []string{config.CorsDomain} opts.AllowedOrigins = strings.Split(config.CorsDomain, " ")
c := cors.New(opts) c := cors.New(opts)
handler = newStoppableHandler(c.Handler(JSONRPC(pipe)), l.stop) handler = newStoppableHandler(c.Handler(JSONRPC(pipe)), l.stop)

@ -48,6 +48,10 @@ func TestBcTotalDifficulty(t *testing.T) {
runBlockTestsInFile("files/BlockTests/bcTotalDifficultyTest.json", []string{}, t) runBlockTestsInFile("files/BlockTests/bcTotalDifficultyTest.json", []string{}, t)
} }
func TestBcWallet(t *testing.T) {
runBlockTestsInFile("files/BlockTests/bcWalletTest.json", []string{}, t)
}
func runBlockTestsInFile(filepath string, snafus []string, t *testing.T) { func runBlockTestsInFile(filepath string, snafus []string, t *testing.T) {
bt, err := LoadBlockTests(filepath) bt, err := LoadBlockTests(filepath)
if err != nil { if err != nil {

@ -924,6 +924,440 @@
"value" : "0x0186a0" "value" : "0x0186a0"
} }
}, },
"createFailBalanceTooLow" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x05f5e100",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"0000000000000000000000000000000000000000" : {
"balance" : "0x0de0b6b3a7640017",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0x715d",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a7638e8c",
"code" : "0x",
"nonce" : "0x01",
"storage" : {
}
}
},
"postStateRoot" : "5833e19631ddedaf4e3c9a766f696c2e59e7524e388eb871be19dc8e0ce37b6e",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x605a60005360016000670de0b6b3a7640018f0ff",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "0xcf1d",
"gasPrice" : "0x01",
"nonce" : "0x00",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0x17"
}
},
"createInitFailBadJumpDestination" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x05f5e100",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"0000000000000000000000000000000000000000" : {
"balance" : "0x0de0b6b3a76586a0",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0x05f58340",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a16cf620",
"code" : "0x",
"nonce" : "0x01",
"storage" : {
}
}
},
"postStateRoot" : "ccf7765eff3effe22a5f853099f7da88291b8346b689ffbf54b729ba04170e59",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x6056600053600160006001f0ff",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "0x05f5e100",
"gasPrice" : "0x01",
"nonce" : "0x00",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0x0186a0"
}
},
"createInitFailStackSizeLargerThan1024" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x05f5e100",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"0000000000000000000000000000000000000000" : {
"balance" : "0x0de0b6b3a76586a0",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0x05f58340",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a16cf620",
"code" : "0x",
"nonce" : "0x01",
"storage" : {
}
}
},
"postStateRoot" : "ccf7765eff3effe22a5f853099f7da88291b8346b689ffbf54b729ba04170e59",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x7f6103ff6000525b7f0102030405060708090a0102030405060708090a010203046000527f05060708090a0102600160005103600052600051600657000000000000000000602052604060006001f0ff",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "0x05f5e100",
"gasPrice" : "0x01",
"nonce" : "0x00",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0x0186a0"
}
},
"createInitFailStackUnderflow" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x05f5e100",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"0000000000000000000000000000000000000000" : {
"balance" : "0x0de0b6b3a76586a0",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0x05f58340",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a16cf620",
"code" : "0x",
"nonce" : "0x01",
"storage" : {
}
}
},
"postStateRoot" : "ccf7765eff3effe22a5f853099f7da88291b8346b689ffbf54b729ba04170e59",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x6001600053600160006001f0ff",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "0x05f5e100",
"gasPrice" : "0x01",
"nonce" : "0x00",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0x0186a0"
}
},
"createInitFailUndefinedInstruction" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x05f5e100",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"0000000000000000000000000000000000000000" : {
"balance" : "0x0de0b6b3a76586a0",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0x05f58340",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a16cf620",
"code" : "0x",
"nonce" : "0x01",
"storage" : {
}
}
},
"postStateRoot" : "ccf7765eff3effe22a5f853099f7da88291b8346b689ffbf54b729ba04170e59",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x60f4600053600160006001f0ff",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "0x05f5e100",
"gasPrice" : "0x01",
"nonce" : "0x00",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0x0186a0"
}
},
"createInitFail_OOGduringInit" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x05f5e100",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"0000000000000000000000000000000000000000" : {
"balance" : "0x0de0b6b3a76586a0",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0x715d",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a7620803",
"code" : "0x",
"nonce" : "0x01",
"storage" : {
}
}
},
"postStateRoot" : "297303455494578a5176177ff1b9db0b0a516255a3d062fb960bbc99e60d8eb5",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x605a600053600160006001f0ff",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "0xcf1d",
"gasPrice" : "0x01",
"nonce" : "0x00",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0x0186a0"
}
},
"createInitOOGforCREATE" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x05f5e100",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x605a600053600160006001f0ff",
"nonce" : "0x00",
"storage" : {
}
},
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0xcf1c",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a76330e4",
"code" : "0x",
"nonce" : "0x01",
"storage" : {
}
}
},
"postStateRoot" : "b61e4a95fae40806b0ddef0883479c3db70e79e019ab4260535560827525c00c",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x605a600053600160006001f0ff",
"nonce" : "0x00",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : "",
"gasLimit" : "0xcf1c",
"gasPrice" : "0x01",
"nonce" : "0x00",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0x0186a0"
}
},
"createJS_ExampleContract" : { "createJS_ExampleContract" : {
"env" : { "env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",

@ -266,35 +266,35 @@
}, },
"logs" : [ "logs" : [
], ],
"out" : "0x00", "out" : "#4294967295",
"post" : { "post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000", "balance" : "0x0de0b6b3a7640000",
"code" : "0x6401000000016000f3", "code" : "0x63ffffffff6000f3",
"nonce" : "0x00", "nonce" : "0x00",
"storage" : { "storage" : {
} }
}, },
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : { "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0x200018085211", "balance" : "0x20001800520e",
"code" : "0x", "code" : "0x",
"nonce" : "0x00", "nonce" : "0x00",
"storage" : { "storage" : {
} }
}, },
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x1869ec3b06114f6f", "balance" : "0x1869ec3b06194f72",
"code" : "0x", "code" : "0x",
"nonce" : "0x01", "nonce" : "0x01",
"storage" : { "storage" : {
} }
} }
}, },
"postStateRoot" : "8745f6bdec4290420747b8c024382c6ed14e09f4a11718bdc1f0f99e4d04607b", "postStateRoot" : "1716bcf6c106040a46ab1d37c569b7e0841f921b4d252dc5202fb05388308a39",
"pre" : { "pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000", "balance" : "0x0de0b6b3a7640000",
"code" : "0x6401000000016000f3", "code" : "0x63ffffffff6000f3",
"nonce" : "0x00", "nonce" : "0x00",
"storage" : { "storage" : {
} }
@ -328,11 +328,11 @@
}, },
"logs" : [ "logs" : [
], ],
"out" : "0x", "out" : "#4294967295",
"post" : { "post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000", "balance" : "0x0de0b6b3a7640000",
"code" : "0x60016000526401000000006000f3", "code" : "0x600160005263ffffffff6000f3",
"nonce" : "0x00", "nonce" : "0x00",
"storage" : { "storage" : {
} }
@ -352,11 +352,11 @@
} }
} }
}, },
"postStateRoot" : "e6c6c5b997cf7ecbc653c920a5b42d1ddd9f9ca2df2c68fd47059df2a3309b14", "postStateRoot" : "b95676d3103fc4a6bdcfb1c201a6ed49e0fa7a239520a4b8ac034ce3b6c697eb",
"pre" : { "pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000", "balance" : "0x0de0b6b3a7640000",
"code" : "0x60016000526401000000006000f3", "code" : "0x600160005263ffffffff6000f3",
"nonce" : "0x00", "nonce" : "0x00",
"storage" : { "storage" : {
} }

File diff suppressed because it is too large Load Diff

@ -268,42 +268,50 @@
}, },
"logs" : [ "logs" : [
], ],
"out" : "0x0000000000000000000000000000000000000000000000000000000000000000", "out" : "0x0000000000000000000000000000000000000000000000000000000000000001",
"post" : { "post" : {
"0000000000000000000000000000000000000000" : {
"balance" : "0x00",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0x0186a0", "balance" : "0x0186a0",
"code" : "0x6000357c0100000000000000000000000000000000000000000000000000000000900480633e0bca3b1461003a578063c04062261461004c57005b610042610099565b8060005260206000f35b61005461005e565b8060005260206000f35b6000610068610099565b600060006101000a81548160ff02191690830217905550600060009054906101000a900460ff169050610096565b90565b60006000600060019250825060018273ffffffffffffffffffffffffffffffffffffffff166381bda09b60206000827c010000000000000000000000000000000000000000000000000000000002600052600460006000866161da5a03f16100fd57005b505060005163ffffffff1614156101135761011c565b60009250610194565b60028173ffffffffffffffffffffffffffffffffffffffff166381bda09b60206000827c010000000000000000000000000000000000000000000000000000000002600052600460006000866161da5a03f161017457005b505060005163ffffffff16141561018a57610193565b60009250610194565b5b50509056", "code" : "0x7c010000000000000000000000000000000000000000000000000000000060003504633e0bca3b8114610039578063c0406226146100a857005b6100b55b600160008060456101ec8339604560006000f091508173ffffffffffffffffffffffffffffffffffffffff166381bda09b60206000827c010000000000000000000000000000000000000000000000000000000002600052600460006000866161da5a03f161011957005b6100bf60006100c961003d565b8060005260206000f35b8060005260206000f35b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016919091179081905560ff16919050565b505060005163ffffffff166002141561019d575b5b505090565b505060005163ffffffff1660011415610194575b60456101a7600039604560006000f090508073ffffffffffffffffffffffffffffffffffffffff166381bda09b60206000827c010000000000000000000000000000000000000000000000000000000002600052600460006000866161da5a03f16100ff57005b60009250610114565b600092506101145600603980600c6000396000f3007c0100000000000000000000000000000000000000000000000000000000600035046381bda09b8114602d57005b60026000818152602090f3603980600c6000396000f3007c0100000000000000000000000000000000000000000000000000000000600035046381bda09b8114602d57005b60016000818152602090f3",
"nonce" : "0x00", "nonce" : "0x02",
"storage" : { "storage" : {
"0x00" : "0x01"
} }
}, },
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : { "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "0xca69", "balance" : "0x01f758",
"code" : "0x", "code" : "0x",
"nonce" : "0x00", "nonce" : "0x00",
"storage" : { "storage" : {
} }
}, },
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x02fa2617", "balance" : "0x02f8f928",
"code" : "0x", "code" : "0x",
"nonce" : "0x01", "nonce" : "0x01",
"storage" : { "storage" : {
} }
},
"b88de88b35ecbf3c141e3caae2baf35834d18f63" : {
"balance" : "0x00",
"code" : "0x7c0100000000000000000000000000000000000000000000000000000000600035046381bda09b8114602d57005b60026000818152602090f3",
"nonce" : "0x00",
"storage" : {
}
},
"d2571607e241ecf590ed94b12d87c94babe36db6" : {
"balance" : "0x00",
"code" : "0x7c0100000000000000000000000000000000000000000000000000000000600035046381bda09b8114602d57005b60016000818152602090f3",
"nonce" : "0x00",
"storage" : {
}
} }
}, },
"postStateRoot" : "ed06b00015d227623175bd12a5d960281781493ea1d9fed79d6c40a20e2c6ef7", "postStateRoot" : "e7f84d674881d1cfd115be59e3e390271435c0b3474a482f7add54c3fe429d85",
"pre" : { "pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0x0186a0", "balance" : "0x0186a0",
"code" : "0x6000357c0100000000000000000000000000000000000000000000000000000000900480633e0bca3b1461003a578063c04062261461004c57005b610042610099565b8060005260206000f35b61005461005e565b8060005260206000f35b6000610068610099565b600060006101000a81548160ff02191690830217905550600060009054906101000a900460ff169050610096565b90565b60006000600060019250825060018273ffffffffffffffffffffffffffffffffffffffff166381bda09b60206000827c010000000000000000000000000000000000000000000000000000000002600052600460006000866161da5a03f16100fd57005b505060005163ffffffff1614156101135761011c565b60009250610194565b60028173ffffffffffffffffffffffffffffffffffffffff166381bda09b60206000827c010000000000000000000000000000000000000000000000000000000002600052600460006000866161da5a03f161017457005b505060005163ffffffff16141561018a57610193565b60009250610194565b5b50509056", "code" : "0x7c010000000000000000000000000000000000000000000000000000000060003504633e0bca3b8114610039578063c0406226146100a857005b6100b55b600160008060456101ec8339604560006000f091508173ffffffffffffffffffffffffffffffffffffffff166381bda09b60206000827c010000000000000000000000000000000000000000000000000000000002600052600460006000866161da5a03f161011957005b6100bf60006100c961003d565b8060005260206000f35b8060005260206000f35b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016919091179081905560ff16919050565b505060005163ffffffff166002141561019d575b5b505090565b505060005163ffffffff1660011415610194575b60456101a7600039604560006000f090508073ffffffffffffffffffffffffffffffffffffffff166381bda09b60206000827c010000000000000000000000000000000000000000000000000000000002600052600460006000866161da5a03f16100ff57005b60009250610114565b600092506101145600603980600c6000396000f3007c0100000000000000000000000000000000000000000000000000000000600035046381bda09b8114602d57005b60026000818152602090f3603980600c6000396000f3007c0100000000000000000000000000000000000000000000000000000000600035046381bda09b8114602d57005b60016000818152602090f3",
"nonce" : "0x00", "nonce" : "0x00",
"storage" : { "storage" : {
} }

@ -356,6 +356,8 @@
} }
}, },
"addmod1_overflow3" : { "addmod1_overflow3" : {
"callcreates" : [
],
"env" : { "env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100", "currentDifficulty" : "0x0100",
@ -369,11 +371,25 @@
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x60056001600160000308600055", "code" : "0x60056001600160000308600055",
"data" : "0x", "data" : "0x",
"gas" : "0x2710", "gas" : "0x0f4240",
"gasPrice" : "0x5af3107a4000", "gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000" "value" : "0x0de0b6b3a7640000"
}, },
"gas" : "0x0ef406",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x60056001600160000308600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0x01"
}
}
},
"pre" : { "pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000", "balance" : "0x0de0b6b3a7640000",
@ -385,6 +401,8 @@
} }
}, },
"addmod1_overflow4" : { "addmod1_overflow4" : {
"callcreates" : [
],
"env" : { "env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100", "currentDifficulty" : "0x0100",
@ -398,11 +416,25 @@
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x60056002600160000308600055", "code" : "0x60056002600160000308600055",
"data" : "0x", "data" : "0x",
"gas" : "0x2710", "gas" : "0x0f4240",
"gasPrice" : "0x5af3107a4000", "gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000" "value" : "0x0de0b6b3a7640000"
}, },
"gas" : "0x0ef406",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x60056002600160000308600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0x02"
}
}
},
"pre" : { "pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000", "balance" : "0x0de0b6b3a7640000",
@ -414,6 +446,8 @@
} }
}, },
"addmod1_overflowDiff" : { "addmod1_overflowDiff" : {
"callcreates" : [
],
"env" : { "env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100", "currentDifficulty" : "0x0100",
@ -427,11 +461,25 @@
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x60056002600003600160000308600055", "code" : "0x60056002600003600160000308600055",
"data" : "0x", "data" : "0x",
"gas" : "0x2710", "gas" : "0x0f4240",
"gasPrice" : "0x5af3107a4000", "gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000" "value" : "0x0de0b6b3a7640000"
}, },
"gas" : "0x0ef400",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x60056002600003600160000308600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0x04"
}
}
},
"pre" : { "pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000", "balance" : "0x0de0b6b3a7640000",
@ -842,6 +890,51 @@
} }
} }
}, },
"addmodDivByZero3" : {
"callcreates" : [
],
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x60016000600060000803600055",
"data" : "0x",
"gas" : "0x0186a0",
"gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000"
},
"gas" : "0x013866",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x60016000600060000803600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
}
}
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x60016000600060000803600055",
"nonce" : "0x00",
"storage" : {
}
}
}
},
"arith1" : { "arith1" : {
"callcreates" : [ "callcreates" : [
], ],
@ -1152,6 +1245,51 @@
} }
} }
}, },
"divByZero_2" : {
"callcreates" : [
],
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x60076000600d0401600055",
"data" : "0x",
"gas" : "0x0186a0",
"gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000"
},
"gas" : "0x01386c",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x60076000600d0401600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0x07"
}
}
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x60076000600d0401600055",
"nonce" : "0x00",
"storage" : {
}
}
}
},
"exp0" : { "exp0" : {
"callcreates" : [ "callcreates" : [
], ],
@ -5498,6 +5636,51 @@
} }
} }
}, },
"modByZero" : {
"callcreates" : [
],
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x6001600060030603600055",
"data" : "0x",
"gas" : "0x0186a0",
"gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000"
},
"gas" : "0x01386c",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x6001600060030603600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
}
}
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x6001600060030603600055",
"nonce" : "0x00",
"storage" : {
}
}
}
},
"mul0" : { "mul0" : {
"callcreates" : [ "callcreates" : [
], ],
@ -6017,6 +6200,8 @@
} }
}, },
"mulmod1_overflow2" : { "mulmod1_overflow2" : {
"callcreates" : [
],
"env" : { "env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100", "currentDifficulty" : "0x0100",
@ -6030,11 +6215,25 @@
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x600560027f800000000000000000000000000000000000000000000000000000000000000009600055", "code" : "0x600560027f800000000000000000000000000000000000000000000000000000000000000009600055",
"data" : "0x", "data" : "0x",
"gas" : "0x2710", "gas" : "0x0f4240",
"gasPrice" : "0x5af3107a4000", "gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000" "value" : "0x0de0b6b3a7640000"
}, },
"gas" : "0x0ef40c",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x600560027f800000000000000000000000000000000000000000000000000000000000000009600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0x01"
}
}
},
"pre" : { "pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000", "balance" : "0x0de0b6b3a7640000",
@ -6046,6 +6245,8 @@
} }
}, },
"mulmod1_overflow3" : { "mulmod1_overflow3" : {
"callcreates" : [
],
"env" : { "env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100", "currentDifficulty" : "0x0100",
@ -6059,11 +6260,25 @@
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x600560027f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff09600055", "code" : "0x600560027f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff09600055",
"data" : "0x", "data" : "0x",
"gas" : "0x2710", "gas" : "0x0f4240",
"gasPrice" : "0x5af3107a4000", "gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000" "value" : "0x0de0b6b3a7640000"
}, },
"gas" : "0x0ef40c",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x600560027f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff09600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0x04"
}
}
},
"pre" : { "pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000", "balance" : "0x0de0b6b3a7640000",
@ -6075,6 +6290,8 @@
} }
}, },
"mulmod1_overflow4" : { "mulmod1_overflow4" : {
"callcreates" : [
],
"env" : { "env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100", "currentDifficulty" : "0x0100",
@ -6088,11 +6305,25 @@
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x600560027f800000000000000000000000000000000000000000000000000000000000000109600055", "code" : "0x600560027f800000000000000000000000000000000000000000000000000000000000000109600055",
"data" : "0x", "data" : "0x",
"gas" : "0x2710", "gas" : "0x0f4240",
"gasPrice" : "0x5af3107a4000", "gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000" "value" : "0x0de0b6b3a7640000"
}, },
"gas" : "0x0ef40c",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x600560027f800000000000000000000000000000000000000000000000000000000000000109600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0x03"
}
}
},
"pre" : { "pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000", "balance" : "0x0de0b6b3a7640000",
@ -6502,6 +6733,51 @@
} }
} }
}, },
"mulmoddivByZero3" : {
"callcreates" : [
],
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x60006000600009600103600055",
"data" : "0x",
"gas" : "0x0186a0",
"gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000"
},
"gas" : "0x013866",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x60006000600009600103600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0x01"
}
}
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x60006000600009600103600055",
"nonce" : "0x00",
"storage" : {
}
}
}
},
"not1" : { "not1" : {
"callcreates" : [ "callcreates" : [
], ],
@ -7081,6 +7357,51 @@
} }
} }
}, },
"sdivByZero2" : {
"callcreates" : [
],
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x600160007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffcf923bdff6000030501600055",
"data" : "0x",
"gas" : "0x0186a0",
"gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000"
},
"gas" : "0x013866",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x600160007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffcf923bdff6000030501600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0x01"
}
}
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x600160007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffcf923bdff6000030501600055",
"nonce" : "0x00",
"storage" : {
}
}
}
},
"sdiv_dejavu" : { "sdiv_dejavu" : {
"callcreates" : [ "callcreates" : [
], ],
@ -8109,6 +8430,8 @@
} }
}, },
"smod6" : { "smod6" : {
"callcreates" : [
],
"env" : { "env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100", "currentDifficulty" : "0x0100",
@ -8122,11 +8445,25 @@
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60000307600055", "code" : "0x7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60000307600055",
"data" : "0x", "data" : "0x",
"gas" : "0x2710", "gas" : "0x0186a0",
"gasPrice" : "0x5af3107a4000", "gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000" "value" : "0x0de0b6b3a7640000"
}, },
"gas" : "0x01386c",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60000307600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0x01"
}
}
},
"pre" : { "pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000", "balance" : "0x0de0b6b3a7640000",
@ -8181,6 +8518,140 @@
} }
} }
}, },
"smod8_byZero" : {
"callcreates" : [
],
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x600d600060c86000030703600055",
"data" : "0x",
"gas" : "0x0186a0",
"gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000"
},
"gas" : "0x013866",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x600d600060c86000030703600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3"
}
}
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x600d600060c86000030703600055",
"nonce" : "0x00",
"storage" : {
}
}
}
},
"smod_i256min1" : {
"callcreates" : [
],
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x60016000037f800000000000000000000000000000000000000000000000000000000000000060000307600055",
"data" : "0x",
"gas" : "0x0186a0",
"gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000"
},
"gas" : "0x0172fe",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x60016000037f800000000000000000000000000000000000000000000000000000000000000060000307600055",
"nonce" : "0x00",
"storage" : {
}
}
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x60016000037f800000000000000000000000000000000000000000000000000000000000000060000307600055",
"nonce" : "0x00",
"storage" : {
}
}
}
},
"smod_i256min2" : {
"callcreates" : [
],
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x0100",
"currentGasLimit" : "0x0f4240",
"currentNumber" : "0x00",
"currentTimestamp" : "0x01",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"code" : "0x600160016000037f80000000000000000000000000000000000000000000000000000000000000006000030703600055",
"data" : "0x",
"gas" : "0x0186a0",
"gasPrice" : "0x5af3107a4000",
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
"value" : "0x0de0b6b3a7640000"
},
"gas" : "0x013860",
"logs" : [
],
"out" : "0x",
"post" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x600160016000037f80000000000000000000000000000000000000000000000000000000000000006000030703600055",
"nonce" : "0x00",
"storage" : {
"0x00" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
}
}
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "0x0de0b6b3a7640000",
"code" : "0x600160016000037f80000000000000000000000000000000000000000000000000000000000000006000030703600055",
"nonce" : "0x00",
"storage" : {
}
}
}
},
"stop" : { "stop" : {
"callcreates" : [ "callcreates" : [
], ],

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"testing" "testing"
@ -87,7 +88,7 @@ func RunVmTest(p string, t *testing.T) {
vm.Debug = true vm.Debug = true
glog.SetV(4) glog.SetV(4)
glog.SetToStderr(true) glog.SetToStderr(true)
if name != "stackLimitPush32_1024" { if name != "Call50000_sha256" {
continue continue
} }
*/ */
@ -128,9 +129,15 @@ func RunVmTest(p string, t *testing.T) {
ret, logs, gas, err = helper.RunState(statedb, env, test.Transaction) ret, logs, gas, err = helper.RunState(statedb, env, test.Transaction)
} }
rexp := helper.FromHex(test.Out) switch name {
if bytes.Compare(rexp, ret) != 0 { // the memory required for these tests (4294967297 bytes) would take too much time.
t.Errorf("%s's return failed. Expected %x, got %x\n", name, rexp, ret) // on 19 May 2015 decided to skip these tests their output.
case "mload32bitBound_return", "mload32bitBound_return2":
default:
rexp := helper.FromHex(test.Out)
if bytes.Compare(rexp, ret) != 0 {
t.Errorf("%s's return failed. Expected %x, got %x\n", name, rexp, ret)
}
} }
if isVmTest { if isVmTest {
@ -246,8 +253,7 @@ func TestLogTest(t *testing.T) {
} }
func TestPerformance(t *testing.T) { func TestPerformance(t *testing.T) {
t.Skip() const fn = "../files/VMTests/vmPerformanceTest.json"
const fn = "../files/VMTests/vmPerformance.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }
@ -281,13 +287,13 @@ func TestInputLimitsLight(t *testing.T) {
RunVmTest(fn, t) RunVmTest(fn, t)
} }
func TestStateExample(t *testing.T) { func TestStateSystemOperations(t *testing.T) {
const fn = "../files/StateTests/stExample.json" const fn = "../files/StateTests/stSystemOperationsTest.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }
func TestStateSystemOperations(t *testing.T) { func TestStateExample(t *testing.T) {
const fn = "../files/StateTests/stSystemOperationsTest.json" const fn = "../files/StateTests/stExample.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }
@ -342,13 +348,17 @@ func TestMemory(t *testing.T) {
} }
func TestMemoryStress(t *testing.T) { func TestMemoryStress(t *testing.T) {
t.Skip("Skipped due to...consuming too much memory :D") if os.Getenv("TEST_VM_COMPLEX") == "" {
t.Skip()
}
const fn = "../files/StateTests/stMemoryStressTest.json" const fn = "../files/StateTests/stMemoryStressTest.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }
func TestQuadraticComplexity(t *testing.T) { func TestQuadraticComplexity(t *testing.T) {
t.Skip() // takes too long if os.Getenv("TEST_VM_COMPLEX") == "" {
t.Skip()
}
const fn = "../files/StateTests/stQuadraticComplexityTest.json" const fn = "../files/StateTests/stQuadraticComplexityTest.json"
RunVmTest(fn, t) RunVmTest(fn, t)
} }

@ -28,6 +28,7 @@ var (
filterTickerTime = 5 * time.Minute filterTickerTime = 5 * time.Minute
defaultGasPrice = big.NewInt(10000000000000) //150000000000 defaultGasPrice = big.NewInt(10000000000000) //150000000000
defaultGas = big.NewInt(90000) //500000 defaultGas = big.NewInt(90000) //500000
dappStorePre = []byte("dapp-")
) )
// byte will be inferred // byte will be inferred
@ -66,12 +67,16 @@ type XEth struct {
// regmut sync.Mutex // regmut sync.Mutex
// register map[string][]*interface{} // TODO improve return type // register map[string][]*interface{} // TODO improve return type
solcPath string
solc *compiler.Solidity
agent *miner.RemoteAgent agent *miner.RemoteAgent
} }
func NewTest(eth *eth.Ethereum, frontend Frontend) *XEth {
return &XEth{
backend: eth,
frontend: frontend,
}
}
// New creates an XEth that uses the given frontend. // New creates an XEth that uses the given frontend.
// If a nil Frontend is provided, a default frontend which // If a nil Frontend is provided, a default frontend which
// confirms all transactions will be used. // confirms all transactions will be used.
@ -304,6 +309,8 @@ func (self *XEth) EthBlockByHash(strHash string) *types.Block {
} }
func (self *XEth) EthTransactionByHash(hash string) (tx *types.Transaction, blhash common.Hash, blnum *big.Int, txi uint64) { func (self *XEth) EthTransactionByHash(hash string) (tx *types.Transaction, blhash common.Hash, blnum *big.Int, txi uint64) {
// Due to increasing return params and need to determine if this is from transaction pool or
// some chain, this probably needs to be refactored for more expressiveness
data, _ := self.backend.ExtraDb().Get(common.FromHex(hash)) data, _ := self.backend.ExtraDb().Get(common.FromHex(hash))
if len(data) != 0 { if len(data) != 0 {
tx = types.NewTransactionFromBytes(data) tx = types.NewTransactionFromBytes(data)
@ -348,6 +355,24 @@ func (self *XEth) CurrentBlock() *types.Block {
return self.backend.ChainManager().CurrentBlock() return self.backend.ChainManager().CurrentBlock()
} }
func (self *XEth) GetBlockReceipts(bhash common.Hash) (receipts types.Receipts, err error) {
return self.backend.BlockProcessor().GetBlockReceipts(bhash)
}
func (self *XEth) GetTxReceipt(txhash common.Hash) (receipt *types.Receipt, err error) {
_, bhash, _, txi := self.EthTransactionByHash(common.ToHex(txhash[:]))
var receipts types.Receipts
receipts, err = self.backend.BlockProcessor().GetBlockReceipts(bhash)
if err == nil {
if txi < uint64(len(receipts)) {
receipt = receipts[txi]
} else {
err = fmt.Errorf("Invalid tx index")
}
}
return
}
func (self *XEth) GasLimit() *big.Int { func (self *XEth) GasLimit() *big.Int {
return self.backend.ChainManager().GasLimit() return self.backend.ChainManager().GasLimit()
} }
@ -357,7 +382,7 @@ func (self *XEth) Block(v interface{}) *Block {
return self.BlockByNumber(int64(n)) return self.BlockByNumber(int64(n))
} else if str, ok := v.(string); ok { } else if str, ok := v.(string); ok {
return self.BlockByHash(str) return self.BlockByHash(str)
} else if f, ok := v.(float64); ok { // Don't ask ... } else if f, ok := v.(float64); ok { // JSON numbers are represented as float64
return self.BlockByNumber(int64(f)) return self.BlockByNumber(int64(f))
} }
@ -377,27 +402,24 @@ func (self *XEth) Accounts() []string {
// accessor for solidity compiler. // accessor for solidity compiler.
// memoized if available, retried on-demand if not // memoized if available, retried on-demand if not
func (self *XEth) Solc() (*compiler.Solidity, error) { func (self *XEth) Solc() (*compiler.Solidity, error) {
var err error return self.backend.Solc()
if self.solc == nil {
self.solc, err = compiler.New(self.solcPath)
}
return self.solc, err
} }
// set in js console via admin interface or wrapper from cli flags // set in js console via admin interface or wrapper from cli flags
func (self *XEth) SetSolc(solcPath string) (*compiler.Solidity, error) { func (self *XEth) SetSolc(solcPath string) (*compiler.Solidity, error) {
self.solcPath = solcPath self.backend.SetSolc(solcPath)
self.solc = nil
return self.Solc() return self.Solc()
} }
// store DApp value in extra database
func (self *XEth) DbPut(key, val []byte) bool { func (self *XEth) DbPut(key, val []byte) bool {
self.backend.ExtraDb().Put(key, val) self.backend.ExtraDb().Put(append(dappStorePre, key...), val)
return true return true
} }
// retrieve DApp value from extra database
func (self *XEth) DbGet(key []byte) ([]byte, error) { func (self *XEth) DbGet(key []byte) ([]byte, error) {
val, err := self.backend.ExtraDb().Get(key) val, err := self.backend.ExtraDb().Get(append(dappStorePre, key...))
return val, err return val, err
} }
@ -778,7 +800,7 @@ func (self *XEth) PushTx(encodedTx string) (string, error) {
} }
func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) { func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) {
statedb := self.State().State().Copy() //self.eth.ChainManager().TransState() statedb := self.State().State().Copy()
var from *state.StateObject var from *state.StateObject
if len(fromStr) == 0 { if len(fromStr) == 0 {
accounts, err := self.backend.AccountManager().Accounts() accounts, err := self.backend.AccountManager().Accounts()
@ -862,13 +884,14 @@ func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceS
var ( var (
from = common.HexToAddress(fromStr) from = common.HexToAddress(fromStr)
to = common.HexToAddress(toStr) to = common.HexToAddress(toStr)
value = common.NewValue(valueStr) value = common.Big(valueStr)
gas = common.Big(gasStr) gas = common.Big(gasStr)
price = common.Big(gasPriceStr) price = common.Big(gasPriceStr)
data []byte data []byte
contractCreation bool contractCreation bool
) )
// 2015-05-18 Is this still needed?
// TODO if no_private_key then // TODO if no_private_key then
//if _, exists := p.register[args.From]; exists { //if _, exists := p.register[args.From]; exists {
// p.register[args.From] = append(p.register[args.From], args) // p.register[args.From] = append(p.register[args.From], args)
@ -908,9 +931,9 @@ func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceS
var tx *types.Transaction var tx *types.Transaction
if contractCreation { if contractCreation {
tx = types.NewContractCreationTx(value.BigInt(), gas, price, data) tx = types.NewContractCreationTx(value, gas, price, data)
} else { } else {
tx = types.NewTransactionMessage(to, value.BigInt(), gas, price, data) tx = types.NewTransactionMessage(to, value, gas, price, data)
} }
state := self.backend.ChainManager().TxState() state := self.backend.ChainManager().TxState()
@ -924,9 +947,11 @@ func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceS
tx.SetNonce(nonce) tx.SetNonce(nonce)
if err := self.sign(tx, from, false); err != nil { if err := self.sign(tx, from, false); err != nil {
state.RemoveNonce(from, tx.Nonce())
return "", err return "", err
} }
if err := self.backend.TxPool().Add(tx); err != nil { if err := self.backend.TxPool().Add(tx); err != nil {
state.RemoveNonce(from, tx.Nonce())
return "", err return "", err
} }