Compare commits

...

54 Commits

Author SHA1 Message Date
obscuren
0e703d92ac Merge branch 'release/0.9.26' 2015-05-28 18:21:14 +02:00
obscuren
12b90600eb core: moved guards 2015-05-28 18:18:23 +02:00
obscuren
2587b0ea62 Merge branch 'release/0.9.26' into develop 2015-05-28 18:01:54 +02:00
obscuren
f082c1b895 Merge branch 'release/0.9.26' 2015-05-28 18:01:40 +02:00
obscuren
d51d74eb55 cmd/geth: bump version v0.9.26 2015-05-28 17:43:05 +02:00
obscuren
35806ccc1c build server fix 2015-05-28 17:18:13 +02:00
Jeffrey Wilcke
b25e8b7079 Merge pull request #1142 from obscuren/100-pct-block-prop
eth: 100% block propogation
2015-05-28 08:07:11 -07:00
obscuren
e5d7627427 eth: 100% block propogation 2015-05-28 17:01:44 +02:00
Jeffrey Wilcke
a225ef9c13 Merge pull request #1140 from Gustav-Simonsson/fix_account_unlock_logging
Validate account length and avoid slicing in logging
2015-05-28 07:39:13 -07:00
Jeffrey Wilcke
b6e137b2b4 Merge pull request #1141 from obscuren/parallelisation-issue
Parallelisation issue
2015-05-28 07:37:37 -07:00
Jeffrey Wilcke
03178a77b6 Merge pull request #1132 from obscuren/log_optimisations
core: log optimisations
2015-05-28 07:35:07 -07:00
obscuren
16038b4e67 core: added bad block 2015-05-28 16:26:19 +02:00
obscuren
109f995684 core: log block hash during nonce error 2015-05-28 15:46:36 +02:00
obscuren
75f5ae80fd core: partially removed nonce parallelisation and added merge error chk
Invalid forks are now detected

Current setup of parellelisation actually inserts bad blocks. This fix
is tmp until a better one is found
2015-05-28 15:35:50 +02:00
Gustav Simonsson
9138955ba5 Validate account length and avoid slicing in logging 2015-05-28 15:20:05 +02:00
Jeffrey Wilcke
4baa5ca963 Merge pull request #1137 from obscuren/web3_update
cmd/geth: updated web3
2015-05-28 04:40:14 -07:00
obscuren
598e454d46 cmd/geth: updated web3 2015-05-28 13:24:09 +02:00
Jeffrey Wilcke
9f467c387a Merge pull request #1123 from fjl/lean-blockchain-commands
cmd/geth: leaner blockchain commands
2015-05-28 04:17:47 -07:00
Jeffrey Wilcke
8add3bb009 Merge pull request #1135 from karalabe/tempban-invalid-hashes
core, eth/downloader: expose the bad hashes, check in downloader
2015-05-28 04:13:08 -07:00
Péter Szilágyi
29b0480cfb core, eth/downloader: expose the bad hashes, check in downloader 2015-05-28 14:03:10 +03:00
Felix Lange
e84bbcce3c cmd/geth: don't flush databases after import 2015-05-28 01:20:58 +02:00
Felix Lange
e1fe75e3b6 cmd/utils: use constant for import batch size 2015-05-28 01:20:58 +02:00
Felix Lange
a8bc2181c9 cmd/utils: skip batches with known blocks during import
This makes block importing restartable.
2015-05-28 01:20:58 +02:00
Felix Lange
67effb94b6 cmd/geth, cmd/utils: make chain importing interruptible
Interrupting import with Ctrl-C could cause database corruption
because the signal wasn't handled. utils.ImportChain now checks
for a queued interrupt on every batch.
2015-05-28 01:09:26 +02:00
Felix Lange
705beb4c25 cmd/utils: print errors only once if stdout and stderr are the same file 2015-05-28 01:09:26 +02:00
Felix Lange
74706a0f02 cmd/geth, cmd/utils: rename utils.Get* -> utils.Make*
The renaming should make it clearer that these functions create a new
instance for every call. @obscuren suggested this renaming a while ago.
2015-05-28 01:09:26 +02:00
Felix Lange
8e4512a5e7 p2p/nat: bump timeout in TestAutoDiscRace 2015-05-28 01:09:26 +02:00
Felix Lange
651030c98d cmd/geth: move blockchain commands to chaincmd.go 2015-05-28 01:09:26 +02:00
Felix Lange
62671c93c4 cmd/mist: use utils.SetupLogger 2015-05-28 01:09:26 +02:00
Felix Lange
3b9808f23c cmd/geth, cmd/utils: don't use Ethereum for import, export and upgradedb
The blockchain commands don't need the full stack. With this change,
p2p, miner, downloader, etc are no longer started for blockchain
operations.
2015-05-28 01:09:26 +02:00
obscuren
e3253b5d5e core: fixed an issue with storing receipts 2015-05-28 01:00:23 +02:00
Jeffrey Wilcke
27e0d2a973 Merge pull request #1128 from karalabe/hard-disconnect-trial
eth: hard disconnect if a peer is flaky
2015-05-27 15:28:39 -07:00
Jeffrey Wilcke
5479be9f64 Merge pull request #1129 from obscuren/database_cache_removal
ethdb, common: cache removal
2015-05-27 10:00:08 -07:00
Jeffrey Wilcke
903b95fffa Merge pull request #1124 from karalabe/detaied-download-progress
cmd/geth: expand admin.progress() to something meaningful
2015-05-27 09:05:46 -07:00
obscuren
020006a8ed common, ethdb: removed caching and LastTD 2015-05-27 18:03:16 +02:00
Péter Szilágyi
5235e01b8d eth: hard disconnect if a peer is flaky 2015-05-27 18:58:51 +03:00
obscuren
7595716816 core: adjust gas calculation 2015-05-27 17:01:28 +02:00
Péter Szilágyi
3f91ee4ff8 cmd/geth: expand admin.progress() to something meaningful 2015-05-27 16:46:46 +03:00
Jeffrey Wilcke
8951a03db3 Merge pull request #1121 from obscuren/miner_time_fix
Miner time fix
2015-05-27 04:51:42 -07:00
Jeffrey Wilcke
e13f413ef5 Merge pull request #1112 from fjl/fix-console-exit
cmd/geth: exit the console cleanly when interrupted
2015-05-27 04:40:29 -07:00
Jeffrey Wilcke
69f7a1da5a Merge pull request #1122 from Gustav-Simonsson/improve_validate_header_comments
Update ValidateHeader comments
2015-05-27 04:34:34 -07:00
obscuren
912ae80350 miner: Added 5 blocks wait in prep for #1067 2015-05-27 13:33:52 +02:00
obscuren
12650e16d3 core, miner: fixed miner time issue and removed future blocks
* Miner should no longer generate blocks with a time stamp less or equal
than it's parent.
* Future blocks are no longer processed and queued directly.
  Closes #1118
2015-05-27 13:30:52 +02:00
Jeffrey Wilcke
34729c365b Merge pull request #1067 from carver/deep-mining-log
miner: log locally mined blocks after they are 5-deep in the chain
2015-05-27 04:30:31 -07:00
Gustav Simonsson
bf5f0b1d0c Update ValidateHeader comments 2015-05-27 13:30:24 +02:00
Jeffrey Wilcke
4b29e5ba85 Merge pull request #1120 from Gustav-Simonsson/revert_gas_limit_change
Revert "core: block.gasLimit - parent.gasLimit <= parent.gasLimit / G…
2015-05-27 04:23:55 -07:00
Gustav Simonsson
14955bd454 Revert "core: block.gasLimit - parent.gasLimit <= parent.gasLimit / GasLimitBoundDivisor"
This reverts commit be2b0501b5.
2015-05-27 13:01:06 +02:00
Jason Carver
de12183d38 deep-mining-log: need ring buffer to be one bigger for all-blocks-mined case 2015-05-26 18:55:52 -07:00
Jason Carver
6019f1bb0a deep-mining-log: only track non-stale blocks
if you track stale blocks, then you quickly overflow your ring buffer in the local network case where you're mining every block and generating a lot of stales.
2015-05-26 18:54:56 -07:00
obscuren
f5ce848cfe Merge branch 'release/0.9.25' into develop 2015-05-27 02:07:13 +02:00
Felix Lange
9253fc337e cmd/geth: exit the console cleanly when interrupted
This fix applies mostly to unsupported terminals that do not trigger the
special interrupt handling in liner. Supported terminals were covered
because liner.Prompt returns an error if Ctrl-C is pressed.
2015-05-27 00:54:48 +02:00
Jason Carver
f1ce5877ba do not export ring buffer struct 2015-05-23 12:09:59 -07:00
Jason Carver
8a7fb5fd34 do not export constant for when to log a deep block you mined 2015-05-23 12:09:52 -07:00
Jason Carver
ba295ec6fe Log locally mined blocks, after they are 5-deep in the chain
This helps determine which blocks are unlikely to end up as uncles

 * Store the 5 most recent locally mined block numbers
 * On every imported block, check if the 5-deep block num is in that store
 * Also confirm that the block is signed with miner's coinbase

Why not just check the coinbase? This log is useful if you're running
multiple miners and want to know if *this* miner is performing well.
2015-05-22 12:53:32 -07:00
24 changed files with 782 additions and 1990 deletions

View File

@@ -262,8 +262,8 @@ func (js *jsre) setHead(call otto.FunctionCall) otto.Value {
}
func (js *jsre) downloadProgress(call otto.FunctionCall) otto.Value {
current, max := js.ethereum.Downloader().Stats()
v, _ := call.Otto.ToValue(fmt.Sprintf("%d/%d", current, max))
pending, cached := js.ethereum.Downloader().Stats()
v, _ := call.Otto.ToValue(map[string]interface{}{"pending": pending, "cached": cached})
return v
}

View File

@@ -12,7 +12,7 @@ import (
"github.com/ethereum/go-ethereum/tests"
)
var blocktestCmd = cli.Command{
var blocktestCommand = cli.Command{
Action: runBlockTest,
Name: "blocktest",
Usage: `loads a block test file`,

159
cmd/geth/chaincmd.go Normal file
View File

@@ -0,0 +1,159 @@
package main
import (
"fmt"
"os"
"path/filepath"
"strconv"
"time"
"github.com/codegangsta/cli"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/logger/glog"
)
var (
importCommand = cli.Command{
Action: importChain,
Name: "import",
Usage: `import a blockchain file`,
}
exportCommand = cli.Command{
Action: exportChain,
Name: "export",
Usage: `export blockchain into file`,
}
upgradedbCommand = cli.Command{
Action: upgradeDB,
Name: "upgradedb",
Usage: "upgrade chainblock database",
}
removedbCommand = cli.Command{
Action: removeDB,
Name: "removedb",
Usage: "Remove blockchain and state databases",
}
dumpCommand = cli.Command{
Action: dump,
Name: "dump",
Usage: `dump a specific block from storage`,
Description: `
The arguments are interpreted as block numbers or hashes.
Use "ethereum dump 0" to dump the genesis block.
`,
}
)
func importChain(ctx *cli.Context) {
if len(ctx.Args()) != 1 {
utils.Fatalf("This command requires an argument.")
}
chain, blockDB, stateDB, extraDB := utils.MakeChain(ctx)
start := time.Now()
err := utils.ImportChain(chain, ctx.Args().First())
closeAll(blockDB, stateDB, extraDB)
if err != nil {
utils.Fatalf("Import error: %v", err)
}
fmt.Printf("Import done in %v", time.Since(start))
}
func exportChain(ctx *cli.Context) {
if len(ctx.Args()) != 1 {
utils.Fatalf("This command requires an argument.")
}
chain, _, _, _ := utils.MakeChain(ctx)
start := time.Now()
if err := utils.ExportChain(chain, ctx.Args().First()); err != nil {
utils.Fatalf("Export error: %v\n", err)
}
fmt.Printf("Export done in %v", time.Since(start))
}
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) {
glog.Infoln("Upgrading blockchain database")
chain, blockDB, stateDB, extraDB := utils.MakeChain(ctx)
v, _ := blockDB.Get([]byte("BlockchainVersion"))
bcVersion := int(common.NewValue(v).Uint())
if bcVersion == 0 {
bcVersion = core.BlockChainVersion
}
// Export the current chain.
filename := fmt.Sprintf("blockchain_%d_%s.chain", bcVersion, time.Now().Format("20060102_150405"))
exportFile := filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), filename)
if err := utils.ExportChain(chain, exportFile); err != nil {
utils.Fatalf("Unable to export chain for reimport %s", err)
}
closeAll(blockDB, stateDB, extraDB)
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "blockchain"))
os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "state"))
// Import the chain file.
chain, blockDB, stateDB, extraDB = utils.MakeChain(ctx)
blockDB.Put([]byte("BlockchainVersion"), common.NewValue(core.BlockChainVersion).Bytes())
err := utils.ImportChain(chain, exportFile)
closeAll(blockDB, stateDB, extraDB)
if err != nil {
utils.Fatalf("Import error %v (a backup is made in %s, use the import command to import it)", err, exportFile)
} else {
os.Remove(exportFile)
glog.Infoln("Import finished")
}
}
func dump(ctx *cli.Context) {
chain, _, stateDB, _ := utils.MakeChain(ctx)
for _, arg := range ctx.Args() {
var block *types.Block
if hashish(arg) {
block = chain.GetBlock(common.HexToHash(arg))
} else {
num, _ := strconv.Atoi(arg)
block = chain.GetBlockByNumber(uint64(num))
}
if block == nil {
fmt.Println("{}")
utils.Fatalf("block not found")
} else {
state := state.New(block.Root(), stateDB)
fmt.Printf("%s\n", state.Dump())
}
}
}
// hashish returns true for strings that look like hashes.
func hashish(x string) bool {
_, err := strconv.Atoi(x)
return err != nil
}
func closeAll(dbs ...common.Database) {
for _, db := range dbs {
db.Close()
}
}

View File

@@ -22,6 +22,7 @@ import (
"fmt"
"math/big"
"os"
"os/signal"
"path/filepath"
"strings"
@@ -47,7 +48,8 @@ type dumbterm struct{ r *bufio.Reader }
func (r dumbterm) Prompt(p string) (string, error) {
fmt.Print(p)
return r.r.ReadString('\n')
line, err := r.r.ReadString('\n')
return strings.TrimSuffix(line, "\n"), err
}
func (r dumbterm) PasswordPrompt(p string) (string, error) {
@@ -182,30 +184,52 @@ func (self *jsre) exec(filename string) error {
}
func (self *jsre) interactive() {
for {
input, err := self.Prompt(self.ps1)
if err != nil {
break
}
if input == "" {
continue
}
str += input + "\n"
self.setIndent()
if indentCount <= 0 {
if input == "exit" {
break
// Read input lines.
prompt := make(chan string)
inputln := make(chan string)
go func() {
defer close(inputln)
for {
line, err := self.Prompt(<-prompt)
if err != nil {
return
}
inputln <- line
}
}()
// Wait for Ctrl-C, too.
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt)
defer func() {
if self.atexit != nil {
self.atexit()
}
self.re.Stop(false)
}()
for {
prompt <- self.ps1
select {
case <-sig:
fmt.Println("caught interrupt, exiting")
return
case input, ok := <-inputln:
if !ok || indentCount <= 0 && input == "exit" {
return
}
if input == "" {
continue
}
str += input + "\n"
self.setIndent()
if indentCount <= 0 {
hist := str[:len(str)-1]
self.AppendHistory(hist)
self.parseInput(str)
str = ""
}
hist := str[:len(str)-1]
self.AppendHistory(hist)
self.parseInput(str)
str = ""
}
}
if self.atexit != nil {
self.atexit()
}
self.re.Stop(false)
}
func (self *jsre) withHistory(op func(*os.File)) {

View File

@@ -24,31 +24,27 @@ import (
"fmt"
"io"
"io/ioutil"
_ "net/http/pprof"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/codegangsta/cli"
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/logger"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
)
import _ "net/http/pprof"
const (
ClientIdentifier = "Geth"
Version = "0.9.25"
Version = "0.9.26"
)
var (
@@ -68,7 +64,12 @@ func init() {
app.Action = run
app.HideVersion = true // we have a command to print the version
app.Commands = []cli.Command{
blocktestCmd,
blocktestCommand,
importCommand,
exportCommand,
upgradedbCommand,
removedbCommand,
dumpCommand,
{
Action: makedag,
Name: "makedag",
@@ -193,15 +194,6 @@ nodes.
},
},
},
{
Action: dump,
Name: "dump",
Usage: `dump a specific block from storage`,
Description: `
The arguments are interpreted as block numbers or hashes.
Use "ethereum dump 0" to dump the genesis block.
`,
},
{
Action: console,
Name: "console",
@@ -221,26 +213,6 @@ The JavaScript VM exposes a node admin interface as well as the Ðapp
JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
`,
},
{
Action: importchain,
Name: "import",
Usage: `import a blockchain file`,
},
{
Action: exportchain,
Name: "export",
Usage: `export blockchain into file`,
},
{
Action: upgradeDb,
Name: "upgradedb",
Usage: "upgrade chainblock database",
},
{
Action: removeDb,
Name: "removedb",
Usage: "Remove blockchain and state databases",
},
}
app.Flags = []cli.Flag{
utils.IdentityFlag,
@@ -282,17 +254,12 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.SolcPathFlag,
}
app.Before = func(ctx *cli.Context) error {
utils.SetupLogger(ctx)
if ctx.GlobalBool(utils.PProfEanbledFlag.Name) {
utils.StartPProf(ctx)
}
return nil
}
// missing:
// flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
// flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0")
// flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false")
}
func main() {
@@ -373,13 +340,13 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (pass
var err error
// Load startup keys. XXX we are going to need a different format
if len(account) == 0 {
if !((len(account) == 40) || (len(account) == 42)) { // with or without 0x
utils.Fatalf("Invalid account address '%s'", account)
}
// 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)
msg := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", account, tries+1, attempts)
passphrase = getPassPhrase(ctx, msg, false)
err = am.Unlock(common.HexToAddress(account), passphrase)
if err == nil {
@@ -427,7 +394,7 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) {
}
func accountList(ctx *cli.Context) {
am := utils.GetAccountManager(ctx)
am := utils.MakeAccountManager(ctx)
accts, err := am.Accounts()
if err != nil {
utils.Fatalf("Could not list accounts: %v", err)
@@ -469,7 +436,7 @@ func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase
}
func accountCreate(ctx *cli.Context) {
am := utils.GetAccountManager(ctx)
am := utils.MakeAccountManager(ctx)
passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true)
acct, err := am.NewAccount(passphrase)
if err != nil {
@@ -488,7 +455,7 @@ func importWallet(ctx *cli.Context) {
utils.Fatalf("Could not read wallet file: %v", err)
}
am := utils.GetAccountManager(ctx)
am := utils.MakeAccountManager(ctx)
passphrase := getPassPhrase(ctx, "", false)
acct, err := am.ImportPreSaleKey(keyJson, passphrase)
@@ -503,7 +470,7 @@ func accountImport(ctx *cli.Context) {
if len(keyfile) == 0 {
utils.Fatalf("keyfile must be given as argument")
}
am := utils.GetAccountManager(ctx)
am := utils.MakeAccountManager(ctx)
passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true)
acct, err := am.Import(keyfile, passphrase)
if err != nil {
@@ -512,153 +479,6 @@ func accountImport(ctx *cli.Context) {
fmt.Printf("Address: %x\n", acct)
}
func importchain(ctx *cli.Context) {
if len(ctx.Args()) != 1 {
utils.Fatalf("This command requires an argument.")
}
cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
cfg.SkipBcVersionCheck = true
ethereum, err := eth.New(cfg)
if err != nil {
utils.Fatalf("%v\n", err)
}
chainmgr := ethereum.ChainManager()
start := time.Now()
err = utils.ImportChain(chainmgr, ctx.Args().First())
if err != nil {
utils.Fatalf("Import error: %v\n", err)
}
// force database flush
ethereum.BlockDb().Flush()
ethereum.StateDb().Flush()
ethereum.ExtraDb().Flush()
fmt.Printf("Import done in %v", time.Since(start))
return
}
func exportchain(ctx *cli.Context) {
if len(ctx.Args()) != 1 {
utils.Fatalf("This command requires an argument.")
}
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
cfg.SkipBcVersionCheck = true
ethereum, err := eth.New(cfg)
if err != nil {
utils.Fatalf("%v\n", err)
}
chainmgr := ethereum.ChainManager()
start := time.Now()
err = utils.ExportChain(chainmgr, ctx.Args().First())
if err != nil {
utils.Fatalf("Export error: %v\n", err)
}
fmt.Printf("Export done in %v", time.Since(start))
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) {
fmt.Println("Upgrade blockchain DB")
cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
cfg.SkipBcVersionCheck = true
ethereum, err := eth.New(cfg)
if err != nil {
utils.Fatalf("%v\n", err)
}
v, _ := ethereum.BlockDb().Get([]byte("BlockchainVersion"))
bcVersion := int(common.NewValue(v).Uint())
if bcVersion == 0 {
bcVersion = core.BlockChainVersion
}
filename := fmt.Sprintf("blockchain_%d_%s.chain", bcVersion, time.Now().Format("20060102_150405"))
exportFile := filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), filename)
err = utils.ExportChain(ethereum.ChainManager(), exportFile)
if err != nil {
utils.Fatalf("Unable to export chain for reimport %s\n", err)
}
ethereum.BlockDb().Close()
ethereum.StateDb().Close()
ethereum.ExtraDb().Close()
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)
if err != nil {
utils.Fatalf("%v\n", err)
}
ethereum.BlockDb().Put([]byte("BlockchainVersion"), common.NewValue(core.BlockChainVersion).Bytes())
err = utils.ImportChain(ethereum.ChainManager(), exportFile)
if err != nil {
utils.Fatalf("Import error %v (a backup is made in %s, use the import command to import it)\n", err, exportFile)
}
// force database flush
ethereum.BlockDb().Flush()
ethereum.StateDb().Flush()
ethereum.ExtraDb().Flush()
os.Remove(exportFile)
fmt.Println("Import finished")
}
func dump(ctx *cli.Context) {
chainmgr, _, stateDb := utils.GetChain(ctx)
for _, arg := range ctx.Args() {
var block *types.Block
if hashish(arg) {
block = chainmgr.GetBlock(common.HexToHash(arg))
} else {
num, _ := strconv.Atoi(arg)
block = chainmgr.GetBlockByNumber(uint64(num))
}
if block == nil {
fmt.Println("{}")
utils.Fatalf("block not found")
} else {
statedb := state.New(block.Root(), stateDb)
fmt.Printf("%s\n", statedb.Dump())
}
}
}
func makedag(ctx *cli.Context) {
args := ctx.Args()
wrongArgs := func() {
@@ -701,9 +521,3 @@ func version(c *cli.Context) {
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
}
// hashish returns true for strings that look like hashes.
func hashish(x string) bool {
_, err := strconv.Atoi(x)
return err != nil
}

View File

@@ -102,7 +102,7 @@ window.filter = filter;
var amount = parseInt( value.value );
console.log("transact: ", to.value, " => ", amount)
contract.sendTransaction({from: eth.accounts[0]}).send( to.value, amount );
contract.send.sendTransaction(to.value, amount ,{from: eth.accounts[0]});
to.value = "";
value.value = "";

View File

@@ -86,6 +86,10 @@ func init() {
utils.BlockchainVersionFlag,
utils.NetworkIdFlag,
}
app.Before = func(ctx *cli.Context) error {
utils.SetupLogger(ctx)
return nil
}
}
func main() {

View File

@@ -40,6 +40,10 @@ import (
"github.com/peterh/liner"
)
const (
importBatchSize = 2500
)
var interruptCallbacks = []func(os.Signal){}
// Register interrupt handlers callbacks
@@ -125,10 +129,17 @@ func initDataDir(Datadir string) {
}
}
// Fatalf formats a message to standard output and exits the program.
// Fatalf formats a message to standard error and exits the program.
// The message is also printed to standard output if standard error
// is redirected to a different file.
func Fatalf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, "Fatal: "+format+"\n", args...)
fmt.Fprintf(os.Stdout, "Fatal: "+format+"\n", args...)
w := io.MultiWriter(os.Stdout, os.Stderr)
outf, _ := os.Stdout.Stat()
errf, _ := os.Stderr.Stat()
if outf != nil && errf != nil && os.SameFile(outf, errf) {
w = os.Stderr
}
fmt.Fprintf(w, "Fatal: "+format+"\n", args...)
logger.Flush()
os.Exit(1)
}
@@ -166,53 +177,86 @@ func FormatTransactionData(data string) []byte {
return d
}
func ImportChain(chainmgr *core.ChainManager, fn string) error {
fmt.Printf("importing blockchain '%s'\n", fn)
fh, err := os.OpenFile(fn, os.O_RDONLY, os.ModePerm)
func ImportChain(chain *core.ChainManager, fn string) error {
// Watch for Ctrl-C while the import is running.
// If a signal is received, the import will stop at the next batch.
interrupt := make(chan os.Signal, 1)
stop := make(chan struct{})
signal.Notify(interrupt, os.Interrupt)
defer signal.Stop(interrupt)
defer close(interrupt)
go func() {
if _, ok := <-interrupt; ok {
glog.Info("caught interrupt during import, will stop at next batch")
}
close(stop)
}()
checkInterrupt := func() bool {
select {
case <-stop:
return true
default:
return false
}
}
glog.Infoln("Importing blockchain", fn)
fh, err := os.Open(fn)
if err != nil {
return err
}
defer fh.Close()
chainmgr.Reset()
stream := rlp.NewStream(fh, 0)
var i, n int
batchSize := 2500
blocks := make(types.Blocks, batchSize)
for ; ; i++ {
var b types.Block
if err := stream.Decode(&b); err == io.EOF {
break
} else if err != nil {
return fmt.Errorf("at block %d: %v", i, err)
// Run actual the import.
blocks := make(types.Blocks, importBatchSize)
n := 0
for batch := 0; ; batch++ {
// Load a batch of RLP blocks.
if checkInterrupt() {
return fmt.Errorf("interrupted")
}
blocks[n] = &b
n++
if n == batchSize {
if _, err := chainmgr.InsertChain(blocks); err != nil {
return fmt.Errorf("invalid block %v", err)
i := 0
for ; i < importBatchSize; i++ {
var b types.Block
if err := stream.Decode(&b); err == io.EOF {
break
} else if err != nil {
return fmt.Errorf("at block %d: %v", n, err)
}
n = 0
blocks = make(types.Blocks, batchSize)
blocks[i] = &b
n++
}
if i == 0 {
break
}
// Import the batch.
if checkInterrupt() {
return fmt.Errorf("interrupted")
}
if hasAllBlocks(chain, blocks[:i]) {
glog.Infof("skipping batch %d, all blocks present [%x / %x]",
batch, blocks[0].Hash().Bytes()[:4], blocks[i-1].Hash().Bytes()[:4])
continue
}
if _, err := chain.InsertChain(blocks[:i]); err != nil {
return fmt.Errorf("invalid block %d: %v", n, err)
}
}
if n > 0 {
if _, err := chainmgr.InsertChain(blocks[:n]); err != nil {
return fmt.Errorf("invalid block %v", err)
}
}
fmt.Printf("imported %d blocks\n", i)
return nil
}
func hasAllBlocks(chain *core.ChainManager, bs []*types.Block) bool {
for _, b := range bs {
if !chain.HasBlock(b.Hash()) {
return false
}
}
return true
}
func ExportChain(chainmgr *core.ChainManager, fn string) error {
fmt.Printf("exporting blockchain '%s'\n", fn)
glog.Infoln("Exporting blockchain to", fn)
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
if err != nil {
return err
@@ -221,6 +265,6 @@ func ExportChain(chainmgr *core.ChainManager, fn string) error {
if err := chainmgr.Export(fh); err != nil {
return err
}
fmt.Printf("exported blockchain\n")
glog.Infoln("Exported blockchain to", fn)
return nil
}

View File

@@ -256,7 +256,8 @@ var (
}
)
func GetNAT(ctx *cli.Context) nat.Interface {
// MakeNAT creates a port mapper from set command line flags.
func MakeNAT(ctx *cli.Context) nat.Interface {
natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name))
if err != nil {
Fatalf("Option %s: %v", NATFlag.Name, err)
@@ -264,7 +265,8 @@ func GetNAT(ctx *cli.Context) nat.Interface {
return natif
}
func GetNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) {
// MakeNodeKey creates a node key from set command line flags.
func MakeNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) {
hex, file := ctx.GlobalString(NodeKeyHexFlag.Name), ctx.GlobalString(NodeKeyFileFlag.Name)
var err error
switch {
@@ -282,21 +284,12 @@ func GetNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) {
return key
}
// MakeEthConfig creates ethereum options from set command line flags.
func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
// Set verbosity on glog
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
glog.CopyStandardLogTo("INFO")
// Set the log type
//glog.SetToStderr(ctx.GlobalBool(LogToStdErrFlag.Name))
glog.SetToStderr(true)
// Set the log dir
glog.SetLogDir(ctx.GlobalString(LogFileFlag.Name))
customName := ctx.GlobalString(IdentityFlag.Name)
if len(customName) > 0 {
clientID += "/" + customName
}
return &eth.Config{
Name: common.MakeName(clientID, version),
DataDir: ctx.GlobalString(DataDirFlag.Name),
@@ -309,15 +302,15 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
LogJSON: ctx.GlobalString(LogJSONFlag.Name),
Etherbase: ctx.GlobalString(EtherbaseFlag.Name),
MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
AccountManager: GetAccountManager(ctx),
AccountManager: MakeAccountManager(ctx),
VmDebug: ctx.GlobalBool(VMDebugFlag.Name),
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name),
Port: ctx.GlobalString(ListenPortFlag.Name),
NAT: GetNAT(ctx),
NAT: MakeNAT(ctx),
NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name),
Discovery: !ctx.GlobalBool(NoDiscoverFlag.Name),
NodeKey: GetNodeKey(ctx),
NodeKey: MakeNodeKey(ctx),
Shh: ctx.GlobalBool(WhisperEnabledFlag.Name),
Dial: true,
BootNodes: ctx.GlobalString(BootnodesFlag.Name),
@@ -327,35 +320,39 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
}
}
func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Database) {
dataDir := ctx.GlobalString(DataDirFlag.Name)
// SetupLogger configures glog from the logging-related command line flags.
func SetupLogger(ctx *cli.Context) {
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
glog.CopyStandardLogTo("INFO")
glog.SetToStderr(true)
glog.SetLogDir(ctx.GlobalString(LogFileFlag.Name))
}
blockDb, err := ethdb.NewLDBDatabase(filepath.Join(dataDir, "blockchain"))
if err != nil {
// MakeChain creates a chain manager from set command line flags.
func MakeChain(ctx *cli.Context) (chain *core.ChainManager, blockDB, stateDB, extraDB common.Database) {
dd := ctx.GlobalString(DataDirFlag.Name)
var err error
if blockDB, err = ethdb.NewLDBDatabase(filepath.Join(dd, "blockchain")); err != nil {
Fatalf("Could not open database: %v", err)
}
stateDb, err := ethdb.NewLDBDatabase(filepath.Join(dataDir, "state"))
if err != nil {
if stateDB, err = ethdb.NewLDBDatabase(filepath.Join(dd, "state")); err != nil {
Fatalf("Could not open database: %v", err)
}
extraDb, err := ethdb.NewLDBDatabase(filepath.Join(dataDir, "extra"))
if err != nil {
if extraDB, err = ethdb.NewLDBDatabase(filepath.Join(dd, "extra")); err != nil {
Fatalf("Could not open database: %v", err)
}
eventMux := new(event.TypeMux)
pow := ethash.New()
chainManager := core.NewChainManager(blockDb, stateDb, pow, eventMux)
txPool := core.NewTxPool(eventMux, chainManager.State, chainManager.GasLimit)
blockProcessor := core.NewBlockProcessor(stateDb, extraDb, pow, txPool, chainManager, eventMux)
chainManager.SetProcessor(blockProcessor)
return chainManager, blockDb, stateDb
chain = core.NewChainManager(blockDB, stateDB, pow, eventMux)
txpool := core.NewTxPool(eventMux, chain.State, chain.GasLimit)
proc := core.NewBlockProcessor(stateDB, extraDB, pow, txpool, chain, eventMux)
chain.SetProcessor(proc)
return chain, blockDB, stateDB, extraDB
}
func GetAccountManager(ctx *cli.Context) *accounts.Manager {
// MakeChain creates an account manager from set command line flags.
func MakeAccountManager(ctx *cli.Context) *accounts.Manager {
dataDir := ctx.GlobalString(DataDirFlag.Name)
ks := crypto.NewKeyStorePassphrase(filepath.Join(dataDir, "keystore"))
return accounts.NewManager(ks)

View File

@@ -5,7 +5,6 @@ type Database interface {
Put(key []byte, value []byte)
Get(key []byte) ([]byte, error)
Delete(key []byte) error
LastKnownTD() []byte
Close()
Flush() error
}

View File

@@ -40,11 +40,6 @@ type BlockProcessor struct {
txpool *TxPool
// The last attempted block is mainly used for debugging purposes
// This does not have to be a valid block and will be set during
// 'Process' & canonical validation.
lastAttemptedBlock *types.Block
events event.Subscription
eventMux *event.TypeMux
@@ -188,8 +183,6 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, err erro
}
func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs state.Logs, err error) {
sm.lastAttemptedBlock = block
// Create a new state based on the parent's root (e.g., create copy)
state := state.New(parent.Root(), sm.db)
@@ -255,6 +248,12 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
return
}
// store the receipts
err = putReceipts(sm.extraDb, block.Hash(), receipts)
if err != nil {
return nil, err
}
// Calculate the td for this block
//td = CalculateTD(block, parent)
// Sync the current block's state to the database
@@ -268,26 +267,11 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
putTx(sm.extraDb, tx, block, uint64(i))
}
receiptsRlp := block.Receipts().RlpEncode()
sm.extraDb.Put(append(receiptsPre, block.Hash().Bytes()...), receiptsRlp)
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,
// an uncle or anything that isn't on the current block chain.
// Validation validates easy over difficult (dagger takes longer time = difficult)
// See YP section 4.3.4. "Block Header Validity"
// Validates a block. Returns an error if the block is invalid.
func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header, checkPow bool) error {
if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
return fmt.Errorf("Block extra data too long (%d)", len(block.Extra))
@@ -298,16 +282,14 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header, checkPow b
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
}
// block.gasLimit - parent.gasLimit <= parent.gasLimit / GasLimitBoundDivisor
a := new(big.Int).Sub(block.GasLimit, parent.GasLimit)
a.Abs(a)
b := new(big.Int).Div(parent.GasLimit, params.GasLimitBoundDivisor)
if !(a.Cmp(b) <= 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) {
if !(a.Cmp(b) < 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) {
return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b)
}
// Allow future blocks up to 10 seconds
if int64(block.Time) > time.Now().Unix()+4 {
if int64(block.Time) > time.Now().Unix() {
return BlockFutureErr
}
@@ -394,13 +376,25 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
return nil
}
// GetBlockReceipts returns the receipts beloniging to the block hash
func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) (receipts types.Receipts, err error) {
return getBlockReceipts(sm.extraDb, bhash)
}
// GetLogs returns the logs of the given block. This method is using a two step approach
// where it tries to get it from the (updated) method which gets them from the receipts or
// the depricated way by re-processing the block.
func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err error) {
if !sm.bc.HasBlock(block.Header().ParentHash) {
return nil, ParentError(block.Header().ParentHash)
receipts, err := sm.GetBlockReceipts(block.Hash())
if err == nil && len(receipts) > 0 {
// coalesce logs
for _, receipt := range receipts {
logs = append(logs, receipt.Logs()...)
}
return
}
sm.lastAttemptedBlock = block
// TODO: remove backward compatibility
var (
parent = sm.bc.GetBlock(block.Header().ParentHash)
state = state.New(parent.Root(), sm.db)
@@ -411,6 +405,16 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro
return state.Logs(), nil
}
func getBlockReceipts(db common.Database, bhash common.Hash) (receipts types.Receipts, err error) {
var rdata []byte
rdata, err = db.Get(append(receiptsPre, bhash[:]...))
if err == nil {
err = rlp.DecodeBytes(rdata, &receipts)
}
return
}
func putTx(db common.Database, tx *types.Transaction, block *types.Block, i uint64) {
rlpEnc, err := rlp.EncodeToBytes(tx)
if err != nil {
@@ -434,3 +438,19 @@ func putTx(db common.Database, tx *types.Transaction, block *types.Block, i uint
}
db.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta)
}
func putReceipts(db common.Database, hash common.Hash, receipts types.Receipts) error {
storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
for i, receipt := range receipts {
storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
}
bytes, err := rlp.EncodeToBytes(storageReceipts)
if err != nil {
return err
}
db.Put(append(receiptsPre, hash[:]...), bytes)
return nil
}

View File

@@ -5,6 +5,8 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/pow/ezp"
@@ -35,3 +37,33 @@ func TestNumber(t *testing.T) {
t.Errorf("didn't expect block number error")
}
}
func TestPutReceipt(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
var addr common.Address
addr[0] = 1
var hash common.Hash
hash[0] = 2
receipt := new(types.Receipt)
receipt.SetLogs(state.Logs{&state.Log{
Address: addr,
Topics: []common.Hash{hash},
Data: []byte("hi"),
Number: 42,
TxHash: hash,
TxIndex: 0,
BlockHash: hash,
Index: 0,
}})
putReceipts(db, hash, types.Receipts{receipt})
receipts, err := getBlockReceipts(db, hash)
if err != nil {
t.Error("got err:", err)
}
if len(receipts) != 1 {
t.Error("expected to get 1 receipt, got", len(receipts))
}
}

View File

@@ -2,7 +2,9 @@ package core
import "github.com/ethereum/go-ethereum/common"
var badHashes = []common.Hash{
common.HexToHash("f269c503aed286caaa0d114d6a5320e70abbc2febe37953207e76a2873f2ba79"),
common.HexToHash("38f5bbbffd74804820ffa4bab0cd540e9de229725afb98c1a7e57936f4a714bc"),
// Set of manually tracked bad hashes (usually hard forks)
var BadHashes = map[common.Hash]bool{
common.HexToHash("f269c503aed286caaa0d114d6a5320e70abbc2febe37953207e76a2873f2ba79"): true,
common.HexToHash("38f5bbbffd74804820ffa4bab0cd540e9de229725afb98c1a7e57936f4a714bc"): true,
common.HexToHash("7064455b364775a16afbdecd75370e912c6e2879f202eda85b9beae547fff3ac"): true,
}

View File

@@ -69,6 +69,7 @@ func CalcGasLimit(parent *types.Block) *big.Int {
gl := new(big.Int).Sub(parent.GasLimit(), decay)
gl = gl.Add(gl, contrib)
gl = gl.Add(gl, big.NewInt(1))
gl = common.BigMax(gl, params.MinGasLimit)
if gl.Cmp(params.GenesisGasLimit) < 0 {
@@ -120,7 +121,7 @@ func NewChainManager(blockDb, stateDb common.Database, pow pow.PoW, mux *event.T
bc.setLastState()
// Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain
for _, hash := range badHashes {
for hash, _ := range BadHashes {
if block := bc.GetBlock(hash); block != nil {
glog.V(logger.Error).Infof("Found bad hash. Reorganising chain to state %x\n", block.ParentHash().Bytes()[:4])
block = bc.GetBlock(block.ParentHash())
@@ -547,18 +548,21 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
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 {
if block == nil {
continue
}
if BadHashes[block.Hash()] {
err := fmt.Errorf("Found known bad hash in chain %x", block.Hash())
blockErr(block, err)
return i, err
}
// create a nonce channel for parallisation of the nonce check
nonceErrCh := make(chan error)
go verifyBlockNonce(self.pow, block, nonceErrCh)
// Setting block.Td regardless of error (known for example) prevents errors down the line
// in the protocol handler
block.Td = new(big.Int).Set(CalcTD(block, self.GetBlock(block.ParentHash())))
@@ -567,13 +571,14 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
// all others will fail too (unless a known block is returned).
logs, err := self.processor.Process(block)
if err != nil {
// empty the nonce channel
<-nonceErrCh
if IsKnownBlockErr(err) {
stats.ignored++
continue
}
// Do not penelise on future block. We'll need a block queue eventually that will queue
// future block for future use
if err == BlockFutureErr {
block.SetQueued(true)
self.futureBlocks.Push(block)
@@ -592,18 +597,23 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
return i, err
}
// Wait and check nonce channel and make sure it checks out fine
// otherwise return the error
if err := <-nonceErrCh; err != nil {
return i, err
}
cblock := self.currentBlock
// Write block to database. Eventually we'll have to improve on this and throw away blocks that are
// not in the canonical chain.
self.write(block)
// Compare the TD of the last known block in the canonical chain to make sure it's greater.
// At this point it's possible that a different chain (fork) becomes the new canonical chain.
if block.Td.Cmp(self.td) > 0 {
// chain fork
if block.ParentHash() != cblock.Hash() {
// during split we merge two different chains and create the new canonical chain
self.merge(cblock, block)
err := self.merge(cblock, block)
if err != nil {
return i, err
}
queue[i] = ChainSplitEvent{block, logs}
queueEvent.splitCount++
@@ -636,19 +646,16 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
queue[i] = ChainSideEvent{block, logs}
queueEvent.sideCount++
}
// Write block to database. Eventually we'll have to improve on this and throw away blocks that are
// not in the canonical chain.
self.write(block)
// Delete from future blocks
self.futureBlocks.Delete(block.Hash())
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)) {
tend := time.Since(tstart)
start, end := chain[0], chain[len(chain)-1]
@@ -662,7 +669,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
// diff takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
// to be part of the new canonical chain.
func (self *ChainManager) diff(oldBlock, newBlock *types.Block) types.Blocks {
func (self *ChainManager) diff(oldBlock, newBlock *types.Block) (types.Blocks, error) {
var (
newChain types.Blocks
commonBlock *types.Block
@@ -673,14 +680,20 @@ func (self *ChainManager) diff(oldBlock, newBlock *types.Block) types.Blocks {
// first reduce whoever is higher bound
if oldBlock.NumberU64() > newBlock.NumberU64() {
// reduce old chain
for oldBlock = oldBlock; oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = self.GetBlock(oldBlock.ParentHash()) {
for oldBlock = oldBlock; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = self.GetBlock(oldBlock.ParentHash()) {
}
} else {
// reduce new chain and append new chain blocks for inserting later on
for newBlock = newBlock; newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = self.GetBlock(newBlock.ParentHash()) {
for newBlock = newBlock; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = self.GetBlock(newBlock.ParentHash()) {
newChain = append(newChain, newBlock)
}
}
if oldBlock == nil {
return nil, fmt.Errorf("Invalid old chain")
}
if newBlock == nil {
return nil, fmt.Errorf("Invalid new chain")
}
numSplit := newBlock.Number()
for {
@@ -691,6 +704,12 @@ func (self *ChainManager) diff(oldBlock, newBlock *types.Block) types.Blocks {
newChain = append(newChain, newBlock)
oldBlock, newBlock = self.GetBlock(oldBlock.ParentHash()), self.GetBlock(newBlock.ParentHash())
if oldBlock == nil {
return nil, fmt.Errorf("Invalid old chain")
}
if newBlock == nil {
return nil, fmt.Errorf("Invalid new chain")
}
}
if glog.V(logger.Info) {
@@ -698,17 +717,22 @@ func (self *ChainManager) diff(oldBlock, newBlock *types.Block) types.Blocks {
glog.Infof("Fork detected @ %x. Reorganising chain from #%v %x to %x", commonHash[:4], numSplit, oldStart.Hash().Bytes()[:4], newStart.Hash().Bytes()[:4])
}
return newChain
return newChain, nil
}
// merge merges two different chain to the new canonical chain
func (self *ChainManager) merge(oldBlock, newBlock *types.Block) {
newChain := self.diff(oldBlock, newBlock)
func (self *ChainManager) merge(oldBlock, newBlock *types.Block) error {
newChain, err := self.diff(oldBlock, newBlock)
if err != nil {
return fmt.Errorf("chain reorg failed: %v", err)
}
// insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly
for _, block := range newChain {
self.insert(block)
}
return nil
}
func (self *ChainManager) update() {
@@ -801,9 +825,17 @@ func verifyNonces(pow pow.PoW, blocks []*types.Block) error {
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)
done <- ValidationError("Block (#%v / %x) nonce is invalid (= %x)", block.Number(), block.Hash(), block.Nonce)
} else {
done <- nil
}
}
}
func verifyBlockNonce(pow pow.PoW, block *types.Block, done chan<- error) {
if !pow.Verify(block) {
done <- ValidationError("Block (#%v / %x) nonce is invalid (= %x)", block.Number(), block.Hash(), block.Nonce)
} else {
done <- nil
}
}

View File

@@ -3,9 +3,7 @@ package core
import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p"
)
// TODO move this to types?
@@ -14,11 +12,7 @@ type Backend interface {
BlockProcessor() *BlockProcessor
ChainManager() *ChainManager
TxPool() *TxPool
PeerCount() int
IsListening() bool
Peers() []*p2p.Peer
BlockDb() common.Database
StateDb() common.Database
EventMux() *event.TypeMux
Downloader() *downloader.Downloader
}

View File

@@ -29,15 +29,22 @@ func (self *Log) EncodeRLP(w io.Writer) error {
}
func (self *Log) String() string {
return fmt.Sprintf(`log: %x %x %x`, self.Address, self.Topics, self.Data)
return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, self.Address, self.Topics, self.Data, self.TxHash, self.TxIndex, self.BlockHash, self.Index)
}
type Logs []*Log
func (self Logs) String() (ret string) {
for _, log := range self {
ret += fmt.Sprintf("%v", log)
}
type LogForStorage Log
return "[" + ret + "]"
func (self *LogForStorage) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, []interface{}{
self.Address,
self.Topics,
self.Data,
self.Number,
self.TxHash,
self.TxIndex,
self.BlockHash,
self.Index,
})
}

View File

@@ -26,10 +26,39 @@ func (self *Receipt) SetLogs(logs state.Logs) {
self.logs = logs
}
func (self *Receipt) Logs() state.Logs {
return self.logs
}
func (self *Receipt) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs})
}
func (self *Receipt) DecodeRLP(s *rlp.Stream) error {
var r struct {
PostState []byte
CumulativeGasUsed *big.Int
Bloom Bloom
Logs state.Logs
}
if err := s.Decode(&r); err != nil {
return err
}
self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs = r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs
return nil
}
type ReceiptForStorage Receipt
func (self *ReceiptForStorage) EncodeRLP(w io.Writer) error {
storageLogs := make([]*state.LogForStorage, len(self.logs))
for i, log := range self.logs {
storageLogs[i] = (*state.LogForStorage)(log)
}
return rlp.Encode(w, []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, storageLogs})
}
func (self *Receipt) RlpEncode() []byte {
bytes, err := rlp.EncodeToBytes(self)
if err != nil {

View File

@@ -7,7 +7,10 @@ import (
"sync/atomic"
"time"
"gopkg.in/fatih/set.v0"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
@@ -75,6 +78,7 @@ type Downloader struct {
queue *queue // Scheduler for selecting the hashes to download
peers *peerSet // Set of active peers from which download can proceed
checks map[common.Hash]*crossCheck // Pending cross checks to verify a hash chain
banned *set.SetNonTS // Set of hashes we've received and banned
// Callbacks
hasBlock hashCheckFn
@@ -100,6 +104,7 @@ type Block struct {
}
func New(mux *event.TypeMux, hasBlock hashCheckFn, getBlock getBlockFn) *Downloader {
// Create the base downloader
downloader := &Downloader{
mux: mux,
queue: newQueue(),
@@ -110,6 +115,11 @@ func New(mux *event.TypeMux, hasBlock hashCheckFn, getBlock getBlockFn) *Downloa
hashCh: make(chan hashPack, 1),
blockCh: make(chan blockPack, 1),
}
// Inject all the known bad hashes
downloader.banned = set.NewNonTS()
for hash, _ := range core.BadHashes {
downloader.banned.Add(hash)
}
return downloader
}
@@ -280,6 +290,12 @@ func (d *Downloader) fetchHashes(p *peer, h common.Hash) error {
glog.V(logger.Debug).Infof("Peer (%s) responded with empty hash set\n", active.id)
return errEmptyHashSet
}
for _, hash := range hashPack.hashes {
if d.banned.Has(hash) {
glog.V(logger.Debug).Infof("Peer (%s) sent a known invalid chain\n", active.id)
return ErrInvalidChain
}
}
// Determine if we're done fetching hashes (queue up all pending), and continue if not done
done, index := false, 0
for index, head = range hashPack.hashes {

View File

@@ -2,7 +2,6 @@ package eth
import (
"fmt"
"math"
"math/big"
"sync"
"time"
@@ -93,14 +92,22 @@ func NewProtocolManager(protocolVersion, networkId int, mux *event.TypeMux, txpo
}
func (pm *ProtocolManager) removePeer(id string) {
// Unregister the peer from the downloader
pm.downloader.UnregisterPeer(id)
// Short circuit if the peer was already removed
peer := pm.peers.Peer(id)
if peer == nil {
return
}
glog.V(logger.Debug).Infoln("Removing peer", id)
// Remove the peer from the Ethereum peer set too
glog.V(logger.Detail).Infoln("Removing peer", id)
// Unregister the peer from the downloader and Ethereum peer set
pm.downloader.UnregisterPeer(id)
if err := pm.peers.Unregister(id); err != nil {
glog.V(logger.Error).Infoln("Removal failed:", err)
}
// Hard disconnect at the networking layer
if peer != nil {
peer.Peer.Disconnect(p2p.DiscUselessPeer)
}
}
func (pm *ProtocolManager) Start() {
@@ -351,7 +358,7 @@ func (pm *ProtocolManager) verifyTd(peer *peer, request newBlockMsgData) error {
func (pm *ProtocolManager) BroadcastBlock(hash common.Hash, block *types.Block) {
// Broadcast block to a batch of peers not knowing about it
peers := pm.peers.PeersWithoutBlock(hash)
peers = peers[:int(math.Sqrt(float64(len(peers))))]
//peers = peers[:int(math.Sqrt(float64(len(peers))))]
for _, peer := range peers {
peer.sendNewBlock(block)
}

View File

@@ -70,6 +70,7 @@ func (pm *ProtocolManager) processBlocks() error {
// Try to inset the blocks, drop the originating peer if there's an error
index, err := pm.chainman.InsertChain(raw)
if err != nil {
glog.V(logger.Debug).Infoln("Downloaded block import failed:", err)
pm.removePeer(blocks[index].OriginPeer)
pm.downloader.Cancel()
return err
@@ -84,12 +85,10 @@ func (pm *ProtocolManager) processBlocks() error {
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.
if peer.td.Cmp(pm.chainman.Td()) <= 0 {
glog.V(logger.Debug).Infoln("Synchronisation canceled: peer's total difficulty is too small")
return
}
// FIXME if we have the hash in our chain and the TD of the peer is

View File

@@ -1,8 +1,6 @@
package ethdb
import (
"sync"
"github.com/ethereum/go-ethereum/compression/rle"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -15,14 +13,10 @@ import (
var OpenFileLimit = 64
type LDBDatabase struct {
// filename for reporting
fn string
mu sync.Mutex
// LevelDB instance
db *leveldb.DB
queue map[string][]byte
quit chan struct{}
}
// NewLDBDatabase returns a LevelDB wrapped object. LDBDatabase does not persist data by
@@ -40,85 +34,39 @@ func NewLDBDatabase(file string) (*LDBDatabase, error) {
return nil, err
}
database := &LDBDatabase{
fn: file,
db: db,
quit: make(chan struct{}),
fn: file,
db: db,
}
database.makeQueue()
return database, nil
}
func (self *LDBDatabase) makeQueue() {
self.queue = make(map[string][]byte)
}
// Put puts the given key / value to the queue
func (self *LDBDatabase) Put(key []byte, value []byte) {
self.mu.Lock()
defer self.mu.Unlock()
self.queue[string(key)] = value
self.db.Put(key, rle.Compress(value), nil)
}
// Get returns the given key if it's present.
func (self *LDBDatabase) Get(key []byte) ([]byte, error) {
self.mu.Lock()
defer self.mu.Unlock()
// Check queue first
if dat, ok := self.queue[string(key)]; ok {
return dat, nil
}
dat, err := self.db.Get(key, nil)
if err != nil {
return nil, err
}
return rle.Decompress(dat)
}
// Delete deletes the key from the queue and database
func (self *LDBDatabase) Delete(key []byte) error {
self.mu.Lock()
defer self.mu.Unlock()
// make sure it's not in the queue
delete(self.queue, string(key))
return self.db.Delete(key, nil)
}
func (self *LDBDatabase) LastKnownTD() []byte {
data, _ := self.Get([]byte("LTD"))
if len(data) == 0 {
data = []byte{0x0}
}
return data
}
func (self *LDBDatabase) NewIterator() iterator.Iterator {
return self.db.NewIterator(nil, nil)
}
// Flush flushes out the queue to leveldb
func (self *LDBDatabase) Flush() error {
self.mu.Lock()
defer self.mu.Unlock()
batch := new(leveldb.Batch)
for key, value := range self.queue {
batch.Put([]byte(key), rle.Compress(value))
}
self.makeQueue() // reset the queue
glog.V(logger.Detail).Infoln("Flush database: ", self.fn)
return self.db.Write(batch, nil)
return nil
}
func (self *LDBDatabase) Close() {

File diff suppressed because it is too large Load Diff

View File

@@ -38,6 +38,13 @@ type Agent interface {
GetHashRate() int64
}
const miningLogAtDepth = 5
type uint64RingBuffer struct {
ints []uint64 //array of all integers in buffer
next int //where is the next insertion? assert 0 <= next < len(ints)
}
// environment is the workers current environment and holds
// all of the current state information
type environment struct {
@@ -54,6 +61,7 @@ type environment struct {
lowGasTransactors *set.Set
ownedAccounts *set.Set
lowGasTxs types.Transactions
localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion)
}
// env returns a new environment for the current cycle
@@ -209,6 +217,18 @@ out:
events.Unsubscribe()
}
func newLocalMinedBlock(blockNumber uint64, prevMinedBlocks *uint64RingBuffer) (minedBlocks *uint64RingBuffer) {
if prevMinedBlocks == nil {
minedBlocks = &uint64RingBuffer{next: 0, ints: make([]uint64, miningLogAtDepth+1)}
} else {
minedBlocks = prevMinedBlocks
}
minedBlocks.ints[minedBlocks.next] = blockNumber
minedBlocks.next = (minedBlocks.next + 1) % len(minedBlocks.ints)
return minedBlocks
}
func (self *worker) wait() {
for {
for block := range self.recv {
@@ -224,13 +244,16 @@ func (self *worker) wait() {
}
self.mux.Post(core.NewMinedBlockEvent{block})
var stale string
var stale, confirm string
canonBlock := self.chain.GetBlockByNumber(block.NumberU64())
if canonBlock != nil && canonBlock.Hash() != block.Hash() {
stale = "stale-"
stale = "stale "
} else {
confirm = "Wait 5 blocks for confirmation"
self.current.localMinedBlocks = newLocalMinedBlock(block.Number().Uint64(), self.current.localMinedBlocks)
}
glog.V(logger.Info).Infof("🔨 Mined %sblock #%v (%x)", stale, block.Number(), block.Hash().Bytes()[:4])
glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm)
jsonlogger.LogJson(&logger.EthMinerNewBlock{
BlockHash: block.Hash().Hex(),
@@ -265,8 +288,14 @@ func (self *worker) push() {
func (self *worker) makeCurrent() {
block := self.chain.NewBlock(self.coinbase)
if block.Time() == self.chain.CurrentBlock().Time() {
block.Header().Time++
parent := self.chain.GetBlock(block.ParentHash())
// TMP fix for build server ...
if parent == nil {
return
}
if block.Time() <= parent.Time() {
block.Header().Time = parent.Header().Time + 1
}
block.Header().Extra = self.extra
@@ -286,8 +315,10 @@ func (self *worker) makeCurrent() {
current.ignoredTransactors = set.New()
current.lowGasTransactors = set.New()
current.ownedAccounts = accountAddressesSet(accounts)
if self.current != nil {
current.localMinedBlocks = self.current.localMinedBlocks
}
parent := self.chain.GetBlock(current.block.ParentHash())
current.coinbase.SetGasPool(core.CalcGasLimit(parent))
self.current = current
@@ -304,6 +335,38 @@ func (w *worker) setGasPrice(p *big.Int) {
w.mux.Post(core.GasPriceChanged{w.gasPrice})
}
func (self *worker) isBlockLocallyMined(deepBlockNum uint64) bool {
//Did this instance mine a block at {deepBlockNum} ?
var isLocal = false
for idx, blockNum := range self.current.localMinedBlocks.ints {
if deepBlockNum == blockNum {
isLocal = true
self.current.localMinedBlocks.ints[idx] = 0 //prevent showing duplicate logs
break
}
}
//Short-circuit on false, because the previous and following tests must both be true
if !isLocal {
return false
}
//Does the block at {deepBlockNum} send earnings to my coinbase?
var block = self.chain.GetBlockByNumber(deepBlockNum)
return block.Header().Coinbase == self.coinbase
}
func (self *worker) logLocalMinedBlocks(previous *environment) {
if previous != nil && self.current.localMinedBlocks != nil {
nextBlockNum := self.current.block.Number().Uint64()
for checkBlockNum := previous.block.Number().Uint64(); checkBlockNum < nextBlockNum; checkBlockNum++ {
inspectBlockNum := checkBlockNum - miningLogAtDepth
if self.isBlockLocallyMined(inspectBlockNum) {
glog.V(logger.Info).Infof("🔨 🔗 Mined %d blocks back: block #%v", miningLogAtDepth, inspectBlockNum)
}
}
}
}
func (self *worker) commitNewWork() {
self.mu.Lock()
defer self.mu.Unlock()
@@ -312,6 +375,7 @@ func (self *worker) commitNewWork() {
self.currentMu.Lock()
defer self.currentMu.Unlock()
previous := self.current
self.makeCurrent()
current := self.current
@@ -347,6 +411,7 @@ func (self *worker) commitNewWork() {
// We only care about logging if we're actually mining
if atomic.LoadInt32(&self.mining) == 1 {
glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles\n", current.block.Number(), current.tcount, len(uncles))
self.logLocalMinedBlocks(previous)
}
for _, hash := range badUncles {

View File

@@ -30,7 +30,7 @@ func TestAutoDiscRace(t *testing.T) {
}
// Check that they all return the correct result within the deadline.
deadline := time.After(550 * time.Millisecond)
deadline := time.After(2 * time.Second)
for i := 0; i < cap(results); i++ {
select {
case <-deadline: