aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--README.md1
-rw-r--r--accounts/abi/bind/template.go4
-rw-r--r--cmd/faucet/faucet.go5
-rw-r--r--cmd/geth/main.go2
-rw-r--r--cmd/geth/usage.go2
-rw-r--r--cmd/puppeth/module_dashboard.go5
-rw-r--r--cmd/puppeth/module_ethstats.go5
-rw-r--r--cmd/puppeth/module_faucet.go5
-rw-r--r--cmd/puppeth/module_nginx.go5
-rw-r--r--cmd/puppeth/module_node.go5
-rw-r--r--cmd/utils/flags.go16
-rw-r--r--consensus/ethash/consensus.go81
-rw-r--r--core/blockchain.go5
-rw-r--r--core/chain_makers.go1
-rw-r--r--core/gen_genesis.go10
-rw-r--r--core/genesis.go1
-rw-r--r--core/state_processor.go10
-rw-r--r--core/tx_journal.go150
-rw-r--r--core/tx_pool.go152
-rw-r--r--core/tx_pool_test.go172
-rw-r--r--core/types/gen_receipt_json.go9
-rw-r--r--core/types/receipt.go75
-rw-r--r--core/vm/contracts.go3
-rw-r--r--core/vm/interpreter.go11
-rw-r--r--core/vm/logger.go25
-rw-r--r--core/vm/runtime/env.go2
-rw-r--r--eth/backend.go6
-rw-r--r--ethclient/ethclient.go2
-rw-r--r--light/txpool.go2
-rw-r--r--mobile/accounts.go11
-rw-r--r--mobile/big.go4
-rw-r--r--mobile/bind.go2
-rw-r--r--mobile/common.go4
-rw-r--r--mobile/ethereum.go2
-rw-r--r--mobile/interface.go2
-rw-r--r--mobile/params.go9
-rw-r--r--mobile/types.go26
-rw-r--r--node/config.go4
-rw-r--r--node/node.go2
-rw-r--r--swarm/api/http/server.go6
-rw-r--r--swarm/api/http/server_test.go62
-rw-r--r--trie/sync.go2
43 files changed, 732 insertions, 178 deletions
diff --git a/Makefile b/Makefile
index b6e2ddd5e..19924f952 100644
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@
.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64
.PHONY: geth-windows geth-windows-386 geth-windows-amd64
-GOBIN = build/bin
+GOBIN = $(pwd)/build/bin
GO ?= latest
geth:
diff --git a/README.md b/README.md
index 0ac9fe8fb..ac9760738 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,6 @@ The go-ethereum project comes with several wrappers/executables found in the `cm
| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default) archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) for command line options. |
| `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) with expanded functionality if the contract bytecode is also available. However it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts) wiki page for details. |
| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. |
-| `disasm` | Bytecode disassembler to convert EVM (Ethereum Virtual Machine) bytecode into more user friendly assembly-like opcodes (e.g. `echo "6001" | disasm`). For details on the individual opcodes, please see pages 22-30 of the [Ethereum Yellow Paper](http://gavwood.com/paper.pdf). |
| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow insolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). |
| `gethrpctest` | Developer utility tool to support our [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) specs. Please see the [test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. |
| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ethereum/wiki/wiki/RLP)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). |
diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go
index 64dd598c0..d07610e7c 100644
--- a/accounts/abi/bind/template.go
+++ b/accounts/abi/bind/template.go
@@ -52,8 +52,8 @@ var tmplSource = map[Lang]string{
// tmplSourceGo is the Go source template use to generate the contract binding
// based on.
const tmplSourceGo = `
-// This file is an automatically generated Go binding. Do not modify as any
-// change will likely be lost upon the next re-generation!
+// Code generated - DO NOT EDIT.
+// This file is a generated binding and any manual changes will be lost.
package {{.Package}}
diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go
index c06c4365b..8cd62441e 100644
--- a/cmd/faucet/faucet.go
+++ b/cmd/faucet/faucet.go
@@ -413,8 +413,9 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
// Iterate over all the files and look for Ethereum addresses
var address common.Address
for _, file := range gist.Files {
- if len(file.Content) == 2+common.AddressLength*2 {
- address = common.HexToAddress(file.Content)
+ content := strings.TrimSpace(file.Content)
+ if len(content) == 2+common.AddressLength*2 {
+ address = common.HexToAddress(content)
}
}
if address == (common.Address{}) {
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index 607414bbb..e89f88ec9 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -67,6 +67,8 @@ var (
utils.EthashDatasetsInMemoryFlag,
utils.EthashDatasetsOnDiskFlag,
utils.TxPoolNoLocalsFlag,
+ utils.TxPoolJournalFlag,
+ utils.TxPoolRejournalFlag,
utils.TxPoolPriceLimitFlag,
utils.TxPoolPriceBumpFlag,
utils.TxPoolAccountSlotsFlag,
diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go
index 275aad674..80861d852 100644
--- a/cmd/geth/usage.go
+++ b/cmd/geth/usage.go
@@ -96,6 +96,8 @@ var AppHelpFlagGroups = []flagGroup{
Name: "TRANSACTION POOL",
Flags: []cli.Flag{
utils.TxPoolNoLocalsFlag,
+ utils.TxPoolJournalFlag,
+ utils.TxPoolRejournalFlag,
utils.TxPoolPriceLimitFlag,
utils.TxPoolPriceBumpFlag,
utils.TxPoolAccountSlotsFlag,
diff --git a/cmd/puppeth/module_dashboard.go b/cmd/puppeth/module_dashboard.go
index 17f119111..1cf6cab79 100644
--- a/cmd/puppeth/module_dashboard.go
+++ b/cmd/puppeth/module_dashboard.go
@@ -425,6 +425,11 @@ services:
- "{{.Port}}:80"{{else}}
environment:
- VIRTUAL_HOST={{.VHost}}{{end}}
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "1m"
+ max-file: "10"
restart: always
`
diff --git a/cmd/puppeth/module_ethstats.go b/cmd/puppeth/module_ethstats.go
index 571df1454..da34acb3b 100644
--- a/cmd/puppeth/module_ethstats.go
+++ b/cmd/puppeth/module_ethstats.go
@@ -60,6 +60,11 @@ services:
environment:
- WS_SECRET={{.Secret}}{{if .VHost}}
- VIRTUAL_HOST={{.VHost}}{{end}}
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "1m"
+ max-file: "10"
restart: always
`
diff --git a/cmd/puppeth/module_faucet.go b/cmd/puppeth/module_faucet.go
index 5a5dc6506..acf1e4324 100644
--- a/cmd/puppeth/module_faucet.go
+++ b/cmd/puppeth/module_faucet.go
@@ -82,6 +82,11 @@ services:
- CAPTCHA_SECRET={{.CaptchaSecret}}{{if .VHost}}
- VIRTUAL_HOST={{.VHost}}
- VIRTUAL_PORT=8080{{end}}
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "1m"
+ max-file: "10"
restart: always
`
diff --git a/cmd/puppeth/module_nginx.go b/cmd/puppeth/module_nginx.go
index 0eac5ace5..fd6d1d74e 100644
--- a/cmd/puppeth/module_nginx.go
+++ b/cmd/puppeth/module_nginx.go
@@ -43,6 +43,11 @@ services:
- "{{.Port}}:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "1m"
+ max-file: "10"
restart: always
`
diff --git a/cmd/puppeth/module_node.go b/cmd/puppeth/module_node.go
index ce1d34135..9fe97c892 100644
--- a/cmd/puppeth/module_node.go
+++ b/cmd/puppeth/module_node.go
@@ -68,6 +68,11 @@ services:
- MINER_NAME={{.Etherbase}}
- GAS_TARGET={{.GasTarget}}
- GAS_PRICE={{.GasPrice}}
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "1m"
+ max-file: "10"
restart: always
`
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 0159364af..9f7b76c12 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -213,6 +213,16 @@ var (
Name: "txpool.nolocals",
Usage: "Disables price exemptions for locally submitted transactions",
}
+ TxPoolJournalFlag = cli.StringFlag{
+ Name: "txpool.journal",
+ Usage: "Disk journal for local transaction to survive node restarts",
+ Value: core.DefaultTxPoolConfig.Journal,
+ }
+ TxPoolRejournalFlag = cli.DurationFlag{
+ Name: "txpool.rejournal",
+ Usage: "Time interval to regenerate the local transaction journal",
+ Value: core.DefaultTxPoolConfig.Rejournal,
+ }
TxPoolPriceLimitFlag = cli.Uint64Flag{
Name: "txpool.pricelimit",
Usage: "Minimum gas price limit to enforce for acceptance into the pool",
@@ -838,6 +848,12 @@ func setTxPool(ctx *cli.Context, cfg *core.TxPoolConfig) {
if ctx.GlobalIsSet(TxPoolNoLocalsFlag.Name) {
cfg.NoLocals = ctx.GlobalBool(TxPoolNoLocalsFlag.Name)
}
+ if ctx.GlobalIsSet(TxPoolJournalFlag.Name) {
+ cfg.Journal = ctx.GlobalString(TxPoolJournalFlag.Name)
+ }
+ if ctx.GlobalIsSet(TxPoolRejournalFlag.Name) {
+ cfg.Rejournal = ctx.GlobalDuration(TxPoolRejournalFlag.Name)
+ }
if ctx.GlobalIsSet(TxPoolPriceLimitFlag.Name) {
cfg.PriceLimit = ctx.GlobalUint64(TxPoolPriceLimitFlag.Name)
}
diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go
index dd9c81fd4..01d97a470 100644
--- a/consensus/ethash/consensus.go
+++ b/consensus/ethash/consensus.go
@@ -287,8 +287,10 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
// given the parent block's time and difficulty.
// TODO (karalabe): Move the chain maker into this package and make this private!
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
- next := new(big.Int).Add(parent.Number, common.Big1)
+ next := new(big.Int).Add(parent.Number, big1)
switch {
+ case config.IsMetropolis(next):
+ return calcDifficultyMetropolis(time, parent)
case config.IsHomestead(next):
return calcDifficultyHomestead(time, parent)
default:
@@ -299,10 +301,65 @@ func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Heade
// Some weird constants to avoid constant memory allocs for them.
var (
expDiffPeriod = big.NewInt(100000)
+ big1 = big.NewInt(1)
+ big2 = big.NewInt(2)
+ big9 = big.NewInt(9)
big10 = big.NewInt(10)
bigMinus99 = big.NewInt(-99)
)
+// calcDifficultyMetropolis is the difficulty adjustment algorithm. It returns
+// the difficulty that a new block should have when created at time given the
+// parent block's time and difficulty. The calculation uses the Metropolis rules.
+func calcDifficultyMetropolis(time uint64, parent *types.Header) *big.Int {
+ // https://github.com/ethereum/EIPs/issues/100.
+ // algorithm:
+ // diff = (parent_diff +
+ // (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99))
+ // ) + 2^(periodCount - 2)
+
+ bigTime := new(big.Int).SetUint64(time)
+ bigParentTime := new(big.Int).Set(parent.Time)
+
+ // holds intermediate values to make the algo easier to read & audit
+ x := new(big.Int)
+ y := new(big.Int)
+
+ // (2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) // 9
+ x.Sub(bigTime, bigParentTime)
+ x.Div(x, big9)
+ if parent.UncleHash == types.EmptyUncleHash {
+ x.Sub(big1, x)
+ } else {
+ x.Sub(big2, x)
+ }
+ // max((2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) // 9, -99)
+ if x.Cmp(bigMinus99) < 0 {
+ x.Set(bigMinus99)
+ }
+ // (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
+ y.Div(parent.Difficulty, params.DifficultyBoundDivisor)
+ x.Mul(y, x)
+ x.Add(parent.Difficulty, x)
+
+ // minimum difficulty can ever be (before exponential factor)
+ if x.Cmp(params.MinimumDifficulty) < 0 {
+ x.Set(params.MinimumDifficulty)
+ }
+ // for the exponential factor
+ periodCount := new(big.Int).Add(parent.Number, big1)
+ periodCount.Div(periodCount, expDiffPeriod)
+
+ // the exponential factor, commonly referred to as "the bomb"
+ // diff = diff + 2^(periodCount - 2)
+ if periodCount.Cmp(big1) > 0 {
+ y.Sub(periodCount, big2)
+ y.Exp(big2, y, nil)
+ x.Add(x, y)
+ }
+ return x
+}
+
// calcDifficultyHomestead is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time given the
// parent block's time and difficulty. The calculation uses the Homestead rules.
@@ -320,12 +377,12 @@ func calcDifficultyHomestead(time uint64, parent *types.Header) *big.Int {
x := new(big.Int)
y := new(big.Int)
- // 1 - (block_timestamp -parent_timestamp) // 10
+ // 1 - (block_timestamp - parent_timestamp) // 10
x.Sub(bigTime, bigParentTime)
x.Div(x, big10)
- x.Sub(common.Big1, x)
+ x.Sub(big1, x)
- // max(1 - (block_timestamp - parent_timestamp) // 10, -99)))
+ // max(1 - (block_timestamp - parent_timestamp) // 10, -99)
if x.Cmp(bigMinus99) < 0 {
x.Set(bigMinus99)
}
@@ -339,14 +396,14 @@ func calcDifficultyHomestead(time uint64, parent *types.Header) *big.Int {
x.Set(params.MinimumDifficulty)
}
// for the exponential factor
- periodCount := new(big.Int).Add(parent.Number, common.Big1)
+ periodCount := new(big.Int).Add(parent.Number, big1)
periodCount.Div(periodCount, expDiffPeriod)
// the exponential factor, commonly referred to as "the bomb"
// diff = diff + 2^(periodCount - 2)
- if periodCount.Cmp(common.Big1) > 0 {
- y.Sub(periodCount, common.Big2)
- y.Exp(common.Big2, y, nil)
+ if periodCount.Cmp(big1) > 0 {
+ y.Sub(periodCount, big2)
+ y.Exp(big2, y, nil)
x.Add(x, y)
}
return x
@@ -373,12 +430,12 @@ func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int {
diff.Set(params.MinimumDifficulty)
}
- periodCount := new(big.Int).Add(parent.Number, common.Big1)
+ periodCount := new(big.Int).Add(parent.Number, big1)
periodCount.Div(periodCount, expDiffPeriod)
- if periodCount.Cmp(common.Big1) > 0 {
+ if periodCount.Cmp(big1) > 0 {
// diff = diff + 2^(periodCount - 2)
- expDiff := periodCount.Sub(periodCount, common.Big2)
- expDiff.Exp(common.Big2, expDiff, nil)
+ expDiff := periodCount.Sub(periodCount, big2)
+ expDiff.Exp(big2, expDiff, nil)
diff.Add(diff, expDiff)
diff = math.BigMax(diff, params.MinimumDifficulty)
}
diff --git a/core/blockchain.go b/core/blockchain.go
index bb1c14f43..3fb8be15f 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -827,6 +827,11 @@ func (bc *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err er
bc.mu.Lock()
defer bc.mu.Unlock()
+ if bc.HasBlock(block.Hash()) {
+ log.Trace("Block existed", "hash", block.Hash())
+ return
+ }
+
localTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
externTd := new(big.Int).Add(block.Difficulty(), ptd)
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 38a69d42a..976a8114d 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -218,6 +218,7 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St
Number: parent.Number(),
Time: new(big.Int).Sub(time, big.NewInt(10)),
Difficulty: parent.Difficulty(),
+ UncleHash: parent.UncleHash(),
}),
GasLimit: CalcGasLimit(parent),
GasUsed: new(big.Int),
diff --git a/core/gen_genesis.go b/core/gen_genesis.go
index 1f3b4a8aa..4d75704a6 100644
--- a/core/gen_genesis.go
+++ b/core/gen_genesis.go
@@ -13,8 +13,6 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-var _ = (*genesisSpecMarshaling)(nil)
-
func (g Genesis) MarshalJSON() ([]byte, error) {
type Genesis struct {
Config *params.ChainConfig `json:"config"`
@@ -26,7 +24,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
Mixhash common.Hash `json:"mixHash"`
Coinbase common.Address `json:"coinbase"`
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"`
- Number uint64 `json:"number"`
+ Number math.HexOrDecimal64 `json:"number"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
ParentHash common.Hash `json:"parentHash"`
}
@@ -45,7 +43,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
enc.Alloc[common.UnprefixedAddress(k)] = v
}
}
- enc.Number = g.Number
+ enc.Number = math.HexOrDecimal64(g.Number)
enc.GasUsed = math.HexOrDecimal64(g.GasUsed)
enc.ParentHash = g.ParentHash
return json.Marshal(&enc)
@@ -62,7 +60,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
Mixhash *common.Hash `json:"mixHash"`
Coinbase *common.Address `json:"coinbase"`
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"`
- Number *uint64 `json:"number"`
+ Number *math.HexOrDecimal64 `json:"number"`
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
ParentHash *common.Hash `json:"parentHash"`
}
@@ -104,7 +102,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
g.Alloc[common.Address(k)] = v
}
if dec.Number != nil {
- g.Number = *dec.Number
+ g.Number = uint64(*dec.Number)
}
if dec.GasUsed != nil {
g.GasUsed = uint64(*dec.GasUsed)
diff --git a/core/genesis.go b/core/genesis.go
index a507d522b..fd6ed6115 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -92,6 +92,7 @@ type genesisSpecMarshaling struct {
ExtraData hexutil.Bytes
GasLimit math.HexOrDecimal64
GasUsed math.HexOrDecimal64
+ Number math.HexOrDecimal64
Difficulty *math.HexOrDecimal256
Alloc map[common.UnprefixedAddress]GenesisAccount
}
diff --git a/core/state_processor.go b/core/state_processor.go
index 90f5a4f60..4489cfce2 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -104,11 +104,17 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
}
// Update the state with pending changes
+ var root []byte
+ if config.IsMetropolis(header.Number) {
+ statedb.Finalise()
+ } else {
+ root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
+ }
usedGas.Add(usedGas, gas)
+
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
// based on the eip phase, we're passing wether the root touch-delete accounts.
- root := statedb.IntermediateRoot(config.IsEIP158(header.Number))
- receipt := types.NewReceipt(root.Bytes(), usedGas)
+ receipt := types.NewReceipt(root, usedGas)
receipt.TxHash = tx.Hash()
receipt.GasUsed = new(big.Int).Set(gas)
// if the transaction created a contract, store the creation address in the receipt.
diff --git a/core/tx_journal.go b/core/tx_journal.go
new file mode 100644
index 000000000..94a9ff9b8
--- /dev/null
+++ b/core/tx_journal.go
@@ -0,0 +1,150 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package core
+
+import (
+ "errors"
+ "io"
+ "os"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// errNoActiveJournal is returned if a transaction is attempted to be inserted
+// into the journal, but no such file is currently open.
+var errNoActiveJournal = errors.New("no active journal")
+
+// txJournal is a rotating log of transactions with the aim of storing locally
+// created transactions to allow non-executed ones to survive node restarts.
+type txJournal struct {
+ path string // Filesystem path to store the transactions at
+ writer io.WriteCloser // Output stream to write new transactions into
+}
+
+// newTxJournal creates a new transaction journal to
+func newTxJournal(path string) *txJournal {
+ return &txJournal{
+ path: path,
+ }
+}
+
+// load parses a transaction journal dump from disk, loading its contents into
+// the specified pool.
+func (journal *txJournal) load(add func(*types.Transaction) error) error {
+ // Skip the parsing if the journal file doens't exist at all
+ if _, err := os.Stat(journal.path); os.IsNotExist(err) {
+ return nil
+ }
+ // Open the journal for loading any past transactions
+ input, err := os.Open(journal.path)
+ if err != nil {
+ return err
+ }
+ defer input.Close()
+
+ // Inject all transactions from the journal into the pool
+ stream := rlp.NewStream(input, 0)
+ total, dropped := 0, 0
+
+ var failure error
+ for {
+ // Parse the next transaction and terminate on error
+ tx := new(types.Transaction)
+ if err = stream.Decode(tx); err != nil {
+ if err != io.EOF {
+ failure = err
+ }
+ break
+ }
+ // Import the transaction and bump the appropriate progress counters
+ total++
+ if err = add(tx); err != nil {
+ log.Debug("Failed to add journaled transaction", "err", err)
+ dropped++
+ continue
+ }
+ }
+ log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped)
+
+ return failure
+}
+
+// insert adds the specified transaction to the local disk journal.
+func (journal *txJournal) insert(tx *types.Transaction) error {
+ if journal.writer == nil {
+ return errNoActiveJournal
+ }
+ if err := rlp.Encode(journal.writer, tx); err != nil {
+ return err
+ }
+ return nil
+}
+
+// rotate regenerates the transaction journal based on the current contents of
+// the transaction pool.
+func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error {
+ // Close the current journal (if any is open)
+ if journal.writer != nil {
+ if err := journal.writer.Close(); err != nil {
+ return err
+ }
+ journal.writer = nil
+ }
+ // Generate a new journal with the contents of the current pool
+ replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
+ if err != nil {
+ return err
+ }
+ journaled := 0
+ for _, txs := range all {
+ for _, tx := range txs {
+ if err = rlp.Encode(replacement, tx); err != nil {
+ replacement.Close()
+ return err
+ }
+ }
+ journaled += len(txs)
+ }
+ replacement.Close()
+
+ // Replace the live journal with the newly generated one
+ if err = os.Rename(journal.path+".new", journal.path); err != nil {
+ return err
+ }
+ sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0755)
+ if err != nil {
+ return err
+ }
+ journal.writer = sink
+ log.Info("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all))
+
+ return nil
+}
+
+// close flushes the transaction journal contents to disk and closes the file.
+func (journal *txJournal) close() error {
+ var err error
+
+ if journal.writer != nil {
+ err = journal.writer.Close()
+ journal.writer = nil
+ }
+ return err
+}
diff --git a/core/tx_pool.go b/core/tx_pool.go
index 8e2d1b31d..16f774265 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -99,7 +99,9 @@ type stateFn func() (*state.StateDB, error)
// TxPoolConfig are the configuration parameters of the transaction pool.
type TxPoolConfig struct {
- NoLocals bool // Whether local transaction handling should be disabled
+ NoLocals bool // Whether local transaction handling should be disabled
+ Journal string // Journal of local transactions to survive node restarts
+ Rejournal time.Duration // Time interval to regenerate the local transaction journal
PriceLimit uint64 // Minimum gas price to enforce for acceptance into the pool
PriceBump uint64 // Minimum price bump percentage to replace an already existing transaction (nonce)
@@ -115,6 +117,9 @@ type TxPoolConfig struct {
// DefaultTxPoolConfig contains the default configurations for the transaction
// pool.
var DefaultTxPoolConfig = TxPoolConfig{
+ Journal: "transactions.rlp",
+ Rejournal: time.Hour,
+
PriceLimit: 1,
PriceBump: 10,
@@ -130,6 +135,10 @@ var DefaultTxPoolConfig = TxPoolConfig{
// unreasonable or unworkable.
func (config *TxPoolConfig) sanitize() TxPoolConfig {
conf := *config
+ if conf.Rejournal < time.Second {
+ log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second)
+ conf.Rejournal = time.Second
+ }
if conf.PriceLimit < 1 {
log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultTxPoolConfig.PriceLimit)
conf.PriceLimit = DefaultTxPoolConfig.PriceLimit
@@ -157,18 +166,19 @@ type TxPool struct {
gasPrice *big.Int
eventMux *event.TypeMux
events *event.TypeMuxSubscription
- locals *accountSet
signer types.Signer
mu sync.RWMutex
+ locals *accountSet // Set of local transaction to exepmt from evicion rules
+ journal *txJournal // Journal of local transaction to back up to disk
+
pending map[common.Address]*txList // All currently processable transactions
queue map[common.Address]*txList // Queued but non-processable transactions
beats map[common.Address]time.Time // Last heartbeat from each known account
all map[common.Hash]*types.Transaction // All transactions to allow lookups
priced *txPricedList // All transactions sorted by price
- wg sync.WaitGroup // for shutdown sync
- quit chan struct{}
+ wg sync.WaitGroup // for shutdown sync
homestead bool
}
@@ -194,32 +204,48 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, eventMux *e
gasPrice: new(big.Int).SetUint64(config.PriceLimit),
pendingState: nil,
events: eventMux.Subscribe(ChainHeadEvent{}, RemovedTransactionEvent{}),
- quit: make(chan struct{}),
}
pool.locals = newAccountSet(pool.signer)
pool.priced = newTxPricedList(&pool.all)
pool.resetState()
- // Start the various events loops and return
- pool.wg.Add(2)
- go pool.eventLoop()
- go pool.expirationLoop()
+ // If local transactions and journaling is enabled, load from disk
+ if !config.NoLocals && config.Journal != "" {
+ pool.journal = newTxJournal(config.Journal)
+
+ if err := pool.journal.load(pool.AddLocal); err != nil {
+ log.Warn("Failed to load transaction journal", "err", err)
+ }
+ if err := pool.journal.rotate(pool.local()); err != nil {
+ log.Warn("Failed to rotate transaction journal", "err", err)
+ }
+ }
+ // Start the event loop and return
+ pool.wg.Add(1)
+ go pool.loop()
return pool
}
-func (pool *TxPool) eventLoop() {
+// loop is the transaction pool's main event loop, waiting for and reacting to
+// outside blockchain events as well as for various reporting and transaction
+// eviction events.
+func (pool *TxPool) loop() {
defer pool.wg.Done()
- // Start a ticker and keep track of interesting pool stats to report
+ // Start the stats reporting and transaction eviction tickers
var prevPending, prevQueued, prevStales int
report := time.NewTicker(statsReportInterval)
defer report.Stop()
- // Track chain events. When a chain events occurs (new chain canon block)
- // we need to know the new state. The new state will help us determine
- // the nonces in the managed state
+ evict := time.NewTicker(evictionInterval)
+ defer evict.Stop()
+
+ journal := time.NewTicker(pool.config.Rejournal)
+ defer journal.Stop()
+
+ // Keep waiting for and reacting to the various events
for {
select {
// Handle any events fired by the system
@@ -253,6 +279,31 @@ func (pool *TxPool) eventLoop() {
log.Debug("Transaction pool status report", "executable", pending, "queued", queued, "stales", stales)
prevPending, prevQueued, prevStales = pending, queued, stales
}
+
+ // Handle inactive account transaction eviction
+ case <-evict.C:
+ pool.mu.Lock()
+ for addr := range pool.queue {
+ // Skip local transactions from the eviction mechanism
+ if pool.locals.contains(addr) {
+ continue
+ }
+ // Any non-locals old enough should be removed
+ if time.Since(pool.beats[addr]) > pool.config.Lifetime {
+ for _, tx := range pool.queue[addr].Flatten() {
+ pool.removeTx(tx.Hash())
+ }
+ }
+ }
+ pool.mu.Unlock()
+
+ // Handle local transaction journal rotation
+ case <-journal.C:
+ if pool.journal != nil {
+ if err := pool.journal.rotate(pool.local()); err != nil {
+ log.Warn("Failed to rotate local tx journal", "err", err)
+ }
+ }
}
}
}
@@ -284,9 +335,11 @@ func (pool *TxPool) resetState() {
// Stop terminates the transaction pool.
func (pool *TxPool) Stop() {
pool.events.Unsubscribe()
- close(pool.quit)
pool.wg.Wait()
+ if pool.journal != nil {
+ pool.journal.close()
+ }
log.Info("Transaction pool stopped")
}
@@ -373,6 +426,22 @@ func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) {
return pending, nil
}
+// local retrieves all currently known local transactions, groupped by origin
+// account and sorted by nonce. The returned transaction set is a copy and can be
+// freely modified by calling code.
+func (pool *TxPool) local() map[common.Address]types.Transactions {
+ txs := make(map[common.Address]types.Transactions)
+ for addr := range pool.locals.accounts {
+ if pending := pool.pending[addr]; pending != nil {
+ txs[addr] = append(txs[addr], pending.Flatten()...)
+ }
+ if queued := pool.queue[addr]; queued != nil {
+ txs[addr] = append(txs[addr], queued.Flatten()...)
+ }
+ }
+ return txs
+}
+
// validateTx checks whether a transaction is valid according to the consensus
// rules and adheres to some heuristic limits of the local node (price and size).
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
@@ -473,18 +542,22 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
}
pool.all[tx.Hash()] = tx
pool.priced.Put(tx)
+ pool.journalTx(from, tx)
log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To())
return old != nil, nil
}
- // New transaction isn't replacing a pending one, push into queue and potentially mark local
+ // New transaction isn't replacing a pending one, push into queue
replace, err := pool.enqueueTx(hash, tx)
if err != nil {
return false, err
}
+ // Mark local addresses and journal local transactions
if local {
pool.locals.add(from)
}
+ pool.journalTx(from, tx)
+
log.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To())
return replace, nil
}
@@ -515,6 +588,18 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, er
return old != nil, nil
}
+// journalTx adds the specified transaction to the local disk journal if it is
+// deemed to have been sent from a local account.
+func (pool *TxPool) journalTx(from common.Address, tx *types.Transaction) {
+ // Only journal if it's enabled and the transaction is local
+ if pool.journal == nil || !pool.locals.contains(from) {
+ return
+ }
+ if err := pool.journal.insert(tx); err != nil {
+ log.Warn("Failed to journal local transaction", "err", err)
+ }
+}
+
// promoteTx adds a transaction to the pending (processable) list of transactions.
//
// Note, this method assumes the pool lock is held!
@@ -910,39 +995,6 @@ func (pool *TxPool) demoteUnexecutables(state *state.StateDB) {
}
}
-// expirationLoop is a loop that periodically iterates over all accounts with
-// queued transactions and drop all that have been inactive for a prolonged amount
-// of time.
-func (pool *TxPool) expirationLoop() {
- defer pool.wg.Done()
-
- evict := time.NewTicker(evictionInterval)
- defer evict.Stop()
-
- for {
- select {
- case <-evict.C:
- pool.mu.Lock()
- for addr := range pool.queue {
- // Skip local transactions from the eviction mechanism
- if pool.locals.contains(addr) {
- continue
- }
- // Any non-locals old enough should be removed
- if time.Since(pool.beats[addr]) > pool.config.Lifetime {
- for _, tx := range pool.queue[addr].Flatten() {
- pool.removeTx(tx.Hash())
- }
- }
- }
- pool.mu.Unlock()
-
- case <-pool.quit:
- return
- }
- }
-}
-
// addressByHeartbeat is an account address tagged with its last activity timestamp.
type addressByHeartbeat struct {
address common.Address
@@ -955,7 +1007,7 @@ func (a addresssByHeartbeat) Len() int { return len(a) }
func (a addresssByHeartbeat) Less(i, j int) bool { return a[i].heartbeat.Before(a[j].heartbeat) }
func (a addresssByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-// accountSet is simply a set of addresses to check for existance, and a signer
+// accountSet is simply a set of addresses to check for existence, and a signer
// capable of deriving addresses from transactions.
type accountSet struct {
accounts map[common.Address]struct{}
diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go
index 03ece3886..80bc0b384 100644
--- a/core/tx_pool_test.go
+++ b/core/tx_pool_test.go
@@ -19,8 +19,10 @@ package core
import (
"crypto/ecdsa"
"fmt"
+ "io/ioutil"
"math/big"
"math/rand"
+ "os"
"testing"
"time"
@@ -33,6 +35,15 @@ import (
"github.com/ethereum/go-ethereum/params"
)
+// testTxPoolConfig is a transaction pool configuration without stateful disk
+// sideeffects used during testing.
+var testTxPoolConfig TxPoolConfig
+
+func init() {
+ testTxPoolConfig = DefaultTxPoolConfig
+ testTxPoolConfig.Journal = ""
+}
+
func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
return pricedTransaction(nonce, gaslimit, big.NewInt(1), key)
}
@@ -47,8 +58,7 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
key, _ := crypto.GenerateKey()
- pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
- pool.resetState()
+ pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
return pool, key
}
@@ -125,9 +135,8 @@ func TestStateChangeDuringPoolReset(t *testing.T) {
gasLimitFunc := func() *big.Int { return big.NewInt(1000000000) }
- pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, mux, stateFunc, gasLimitFunc)
+ pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, mux, stateFunc, gasLimitFunc)
defer pool.Stop()
- pool.resetState()
nonce := pool.State().GetNonce(address)
if nonce != 0 {
@@ -618,25 +627,25 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
pool.resetState()
// Keep queuing up transactions and make sure all above a limit are dropped
- for i := uint64(1); i <= DefaultTxPoolConfig.AccountQueue+5; i++ {
+ for i := uint64(1); i <= testTxPoolConfig.AccountQueue+5; i++ {
if err := pool.AddRemote(transaction(i, big.NewInt(100000), key)); err != nil {
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
}
if len(pool.pending) != 0 {
t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, len(pool.pending), 0)
}
- if i <= DefaultTxPoolConfig.AccountQueue {
+ if i <= testTxPoolConfig.AccountQueue {
if pool.queue[account].Len() != int(i) {
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), i)
}
} else {
- if pool.queue[account].Len() != int(DefaultTxPoolConfig.AccountQueue) {
- t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, pool.queue[account].Len(), DefaultTxPoolConfig.AccountQueue)
+ if pool.queue[account].Len() != int(testTxPoolConfig.AccountQueue) {
+ t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, pool.queue[account].Len(), testTxPoolConfig.AccountQueue)
}
}
}
- if len(pool.all) != int(DefaultTxPoolConfig.AccountQueue) {
- t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), DefaultTxPoolConfig.AccountQueue)
+ if len(pool.all) != int(testTxPoolConfig.AccountQueue) {
+ t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), testTxPoolConfig.AccountQueue)
}
}
@@ -657,13 +666,12 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- config := DefaultTxPoolConfig
+ config := testTxPoolConfig
config.NoLocals = nolocals
config.GlobalQueue = config.AccountQueue*3 - 1 // reduce the queue limits to shorten test time (-1 to make it non divisible)
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
defer pool.Stop()
- pool.resetState()
// Create a number of test accounts and fund them (last one will be the local)
state, _ := pool.currentState()
@@ -748,13 +756,12 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- config := DefaultTxPoolConfig
+ config := testTxPoolConfig
config.Lifetime = 250 * time.Millisecond
config.NoLocals = nolocals
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
defer pool.Stop()
- pool.resetState()
// Create two test accounts to ensure remotes expire but locals do not
local, _ := crypto.GenerateKey()
@@ -817,7 +824,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
pool.resetState()
// Keep queuing up transactions and make sure all above a limit are dropped
- for i := uint64(0); i < DefaultTxPoolConfig.AccountQueue+5; i++ {
+ for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
if err := pool.AddRemote(transaction(i, big.NewInt(100000), key)); err != nil {
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
}
@@ -828,8 +835,8 @@ func TestTransactionPendingLimiting(t *testing.T) {
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), 0)
}
}
- if len(pool.all) != int(DefaultTxPoolConfig.AccountQueue+5) {
- t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), DefaultTxPoolConfig.AccountQueue+5)
+ if len(pool.all) != int(testTxPoolConfig.AccountQueue+5) {
+ t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), testTxPoolConfig.AccountQueue+5)
}
}
@@ -845,7 +852,7 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
state1, _ := pool1.currentState()
state1.AddBalance(account1, big.NewInt(1000000))
- for i := uint64(0); i < DefaultTxPoolConfig.AccountQueue+5; i++ {
+ for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
if err := pool1.AddRemote(transaction(origin+i, big.NewInt(100000), key1)); err != nil {
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
}
@@ -857,7 +864,7 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
state2.AddBalance(account2, big.NewInt(1000000))
txns := []*types.Transaction{}
- for i := uint64(0); i < DefaultTxPoolConfig.AccountQueue+5; i++ {
+ for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
txns = append(txns, transaction(origin+i, big.NewInt(100000), key2))
}
pool2.AddRemotes(txns)
@@ -888,12 +895,11 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- config := DefaultTxPoolConfig
+ config := testTxPoolConfig
config.GlobalSlots = config.AccountSlots * 10
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
defer pool.Stop()
- pool.resetState()
// Create a number of test accounts and fund them
state, _ := pool.currentState()
@@ -935,14 +941,13 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- config := DefaultTxPoolConfig
+ config := testTxPoolConfig
config.AccountSlots = 2
config.AccountQueue = 2
config.GlobalSlots = 8
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
defer pool.Stop()
- pool.resetState()
// Create a number of test accounts and fund them
state, _ := pool.currentState()
@@ -970,12 +975,11 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- config := DefaultTxPoolConfig
+ config := testTxPoolConfig
config.GlobalSlots = 0
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
defer pool.Stop()
- pool.resetState()
// Create a number of test accounts and fund them
state, _ := pool.currentState()
@@ -1019,9 +1023,8 @@ func TestTransactionPoolRepricing(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
+ pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
defer pool.Stop()
- pool.resetState()
// Create a number of test accounts and fund them
state, _ := pool.currentState()
@@ -1104,13 +1107,12 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- config := DefaultTxPoolConfig
+ config := testTxPoolConfig
config.GlobalSlots = 2
config.GlobalQueue = 2
pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
defer pool.Stop()
- pool.resetState()
// Create a number of test accounts and fund them
state, _ := pool.currentState()
@@ -1192,9 +1194,8 @@ func TestTransactionReplacement(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
- pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
+ pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
defer pool.Stop()
- pool.resetState()
// Create a test account to add transactions with
key, _ := crypto.GenerateKey()
@@ -1204,7 +1205,7 @@ func TestTransactionReplacement(t *testing.T) {
// Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
price := int64(100)
- threshold := (price * (100 + int64(DefaultTxPoolConfig.PriceBump))) / 100
+ threshold := (price * (100 + int64(testTxPoolConfig.PriceBump))) / 100
if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), key)); err != nil {
t.Fatalf("failed to add original cheap pending transaction: %v", err)
@@ -1250,6 +1251,113 @@ func TestTransactionReplacement(t *testing.T) {
}
}
+// Tests that local transactions are journaled to disk, but remote transactions
+// get discarded between restarts.
+func TestTransactionJournaling(t *testing.T) { testTransactionJournaling(t, false) }
+func TestTransactionJournalingNoLocals(t *testing.T) { testTransactionJournaling(t, true) }
+
+func testTransactionJournaling(t *testing.T, nolocals bool) {
+ // Create a temporary file for the journal
+ file, err := ioutil.TempFile("", "")
+ if err != nil {
+ t.Fatalf("failed to create temporary journal: %v", err)
+ }
+ journal := file.Name()
+ defer os.Remove(journal)
+
+ // Clean up the temporary file, we only need the path for now
+ file.Close()
+ os.Remove(journal)
+
+ // Create the original pool to inject transaction into the journal
+ db, _ := ethdb.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
+
+ config := testTxPoolConfig
+ config.NoLocals = nolocals
+ config.Journal = journal
+ config.Rejournal = time.Second
+
+ pool := NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
+
+ // Create two test accounts to ensure remotes expire but locals do not
+ local, _ := crypto.GenerateKey()
+ remote, _ := crypto.GenerateKey()
+
+ statedb, _ = pool.currentState()
+ statedb.AddBalance(crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000))
+ statedb.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
+
+ // Add three local and a remote transactions and ensure they are queued up
+ if err := pool.AddLocal(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), local)); err != nil {
+ t.Fatalf("failed to add local transaction: %v", err)
+ }
+ if err := pool.AddLocal(pricedTransaction(1, big.NewInt(100000), big.NewInt(1), local)); err != nil {
+ t.Fatalf("failed to add local transaction: %v", err)
+ }
+ if err := pool.AddLocal(pricedTransaction(2, big.NewInt(100000), big.NewInt(1), local)); err != nil {
+ t.Fatalf("failed to add local transaction: %v", err)
+ }
+ if err := pool.AddRemote(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), remote)); err != nil {
+ t.Fatalf("failed to add remote transaction: %v", err)
+ }
+ pending, queued := pool.stats()
+ if pending != 4 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 4)
+ }
+ if queued != 0 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
+ }
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ // Terminate the old pool, bump the local nonce, create a new pool and ensure relevant transaction survive
+ pool.Stop()
+ statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
+ pool = NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
+
+ pending, queued = pool.stats()
+ if queued != 0 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
+ }
+ if nolocals {
+ if pending != 0 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
+ }
+ } else {
+ if pending != 2 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
+ }
+ }
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ // Bump the nonce temporarily and ensure the newly invalidated transaction is removed
+ statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 2)
+ pool.resetState()
+ time.Sleep(2 * config.Rejournal)
+ pool.Stop()
+ statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
+ pool = NewTxPool(config, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
+
+ pending, queued = pool.stats()
+ if pending != 0 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
+ }
+ if nolocals {
+ if queued != 0 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
+ }
+ } else {
+ if queued != 1 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
+ }
+ }
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+}
+
// Benchmarks the speed of validating the contents of the pending queue of the
// transaction pool.
func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }
diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go
index edbd64ba4..eb2e5d42b 100644
--- a/core/types/gen_receipt_json.go
+++ b/core/types/gen_receipt_json.go
@@ -13,7 +13,7 @@ import (
func (r Receipt) MarshalJSON() ([]byte, error) {
type Receipt struct {
- PostState hexutil.Bytes `json:"root" gencodec:"required"`
+ PostState hexutil.Bytes `json:"root"`
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed" gencodec:"required"`
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Logs []*Log `json:"logs" gencodec:"required"`
@@ -34,7 +34,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
func (r *Receipt) UnmarshalJSON(input []byte) error {
type Receipt struct {
- PostState hexutil.Bytes `json:"root" gencodec:"required"`
+ PostState hexutil.Bytes `json:"root"`
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed" gencodec:"required"`
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
Logs []*Log `json:"logs" gencodec:"required"`
@@ -46,10 +46,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
- if dec.PostState == nil {
- return errors.New("missing required field 'root' for Receipt")
+ if dec.PostState != nil {
+ r.PostState = dec.PostState
}
- r.PostState = dec.PostState
if dec.CumulativeGasUsed == nil {
return errors.New("missing required field 'cumulativeGasUsed' for Receipt")
}
diff --git a/core/types/receipt.go b/core/types/receipt.go
index ef6f6a2bb..c9906b015 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -31,7 +31,7 @@ import (
// Receipt represents the results of a transaction.
type Receipt struct {
// Consensus fields
- PostState []byte `json:"root" gencodec:"required"`
+ PostState []byte `json:"root"`
CumulativeGasUsed *big.Int `json:"cumulativeGasUsed" gencodec:"required"`
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Logs []*Log `json:"logs" gencodec:"required"`
@@ -48,35 +48,88 @@ type receiptMarshaling struct {
GasUsed *hexutil.Big
}
+// homesteadReceiptRLP contains the receipt's Homestead consensus fields, used
+// during RLP serialization.
+type homesteadReceiptRLP struct {
+ PostState []byte
+ CumulativeGasUsed *big.Int
+ Bloom Bloom
+ Logs []*Log
+}
+
+// metropolisReceiptRLP contains the receipt's Metropolis consensus fields, used
+// during RLP serialization.
+type metropolisReceiptRLP struct {
+ CumulativeGasUsed *big.Int
+ Bloom Bloom
+ Logs []*Log
+}
+
// NewReceipt creates a barebone transaction receipt, copying the init fields.
func NewReceipt(root []byte, cumulativeGasUsed *big.Int) *Receipt {
return &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumulativeGasUsed)}
}
// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
-// into an RLP stream.
+// into an RLP stream. If no post state is present, metropolis fork is assumed.
func (r *Receipt) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, []interface{}{r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs})
+ if r.PostState == nil {
+ return rlp.Encode(w, &metropolisReceiptRLP{r.CumulativeGasUsed, r.Bloom, r.Logs})
+ }
+ return rlp.Encode(w, &homesteadReceiptRLP{r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs})
}
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
// from an RLP stream.
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
- var receipt struct {
- PostState []byte
- CumulativeGasUsed *big.Int
- Bloom Bloom
- Logs []*Log
+ // Load the raw bytes since we have multiple possible formats
+ raw, err := s.Raw()
+ if err != nil {
+ return err
}
- if err := s.Decode(&receipt); err != nil {
+ list, _, err := rlp.SplitList(raw)
+ if err != nil {
return err
}
- r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs = receipt.PostState, receipt.CumulativeGasUsed, receipt.Bloom, receipt.Logs
- return nil
+ items, err := rlp.CountValues(list)
+ if err != nil {
+ return err
+ }
+ // Deserialize based on the number of content items
+ switch items {
+ case 3:
+ // Metropolis receipts have 3 components
+ var metro metropolisReceiptRLP
+ if err := rlp.DecodeBytes(raw, &metro); err != nil {
+ return err
+ }
+ r.CumulativeGasUsed = metro.CumulativeGasUsed
+ r.Bloom = metro.Bloom
+ r.Logs = metro.Logs
+ return nil
+
+ case 4:
+ // Homestead receipts have 4 components
+ var home homesteadReceiptRLP
+ if err := rlp.DecodeBytes(raw, &home); err != nil {
+ return err
+ }
+ r.PostState = home.PostState[:]
+ r.CumulativeGasUsed = home.CumulativeGasUsed
+ r.Bloom = home.Bloom
+ r.Logs = home.Logs
+ return nil
+
+ default:
+ return fmt.Errorf("invalid receipt components: %v", items)
+ }
}
// String implements the Stringer interface.
func (r *Receipt) String() string {
+ if r.PostState == nil {
+ return fmt.Sprintf("receipt{cgas=%v bloom=%x logs=%v}", r.CumulativeGasUsed, r.Bloom, r.Logs)
+ }
return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs)
}
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 90b2f913e..43b60ba77 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -23,7 +23,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"golang.org/x/crypto/ripemd160"
)
@@ -76,14 +75,12 @@ func (c *ecrecover) Run(in []byte) ([]byte, error) {
// tighter sig s values in homestead only apply to tx sigs
if !allZero(in[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) {
- log.Trace("ECRECOVER error: v, r or s value invalid")
return nil, nil
}
// v needs to be at the end for libsecp256k1
pubKey, err := crypto.Ecrecover(in[:32], append(in[64:128], v))
// make sure the public key is a valid one
if err != nil {
- log.Trace("ECRECOVER failed", "err", err)
return nil, nil
}
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 545f7d650..0a225bca4 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -19,12 +19,10 @@ package vm
import (
"fmt"
"sync/atomic"
- "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
@@ -122,19 +120,12 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
)
contract.Input = input
- // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
defer func() {
if err != nil && in.cfg.Debug {
- // XXX For debugging
- //fmt.Printf("%04d: %8v cost = %-8d stack = %-8d ERR = %v\n", pc, op, cost, stack.len(), err)
in.cfg.Tracer.CaptureState(in.evm, pc, op, contract.Gas, cost, mem, stack, contract, in.evm.depth, err)
}
}()
- log.Debug("interpreter running contract", "hash", codehash[:])
- tstart := time.Now()
- defer log.Debug("interpreter finished running contract", "hash", codehash[:], "elapsed", time.Since(tstart))
-
// The Interpreter main run loop (contextual). This loop runs until either an
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
// the execution of one of the operations or until the done flag is set by the
@@ -190,8 +181,6 @@ func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret
if in.cfg.Debug {
in.cfg.Tracer.CaptureState(in.evm, pc, op, contract.Gas, cost, mem, stack, contract, in.evm.depth, err)
}
- // XXX For debugging
- //fmt.Printf("%04d: %8v cost = %-8d stack = %-8d\n", pc, op, cost, stack.len())
// execute the operation
res, err := operation.execute(&pc, in.evm, contract, mem, stack)
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 17a9c9ec3..b73b13bd9 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -196,20 +196,27 @@ func (l *StructLogger) StructLogs() []StructLog {
// WriteTrace writes a formatted trace to the given writer
func WriteTrace(writer io.Writer, logs []StructLog) {
for _, log := range logs {
- fmt.Fprintf(writer, "%-10spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost)
+ fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost)
if log.Err != nil {
fmt.Fprintf(writer, " ERROR: %v", log.Err)
}
- fmt.Fprintf(writer, "\n")
+ fmt.Fprintln(writer)
- for i := len(log.Stack) - 1; i >= 0; i-- {
- fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
+ if len(log.Stack) > 0 {
+ fmt.Fprintln(writer, "Stack:")
+ for i := len(log.Stack) - 1; i >= 0; i-- {
+ fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
+ }
}
-
- fmt.Fprint(writer, hex.Dump(log.Memory))
-
- for h, item := range log.Storage {
- fmt.Fprintf(writer, "%x: %x\n", h, item)
+ if len(log.Memory) > 0 {
+ fmt.Fprintln(writer, "Memory:")
+ fmt.Fprint(writer, hex.Dump(log.Memory))
+ }
+ if len(log.Storage) > 0 {
+ fmt.Fprintln(writer, "Storage:")
+ for h, item := range log.Storage {
+ fmt.Fprintf(writer, "%x: %x\n", h, item)
+ }
}
fmt.Fprintln(writer)
}
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index 9aa88e669..7b41fe85a 100644
--- a/core/vm/runtime/env.go
+++ b/core/vm/runtime/env.go
@@ -37,7 +37,7 @@ func NewEnv(cfg *Config, state *state.StateDB) *vm.EVM {
Time: cfg.Time,
Difficulty: cfg.Difficulty,
GasLimit: new(big.Int).SetUint64(cfg.GasLimit),
- GasPrice: new(big.Int),
+ GasPrice: cfg.GasPrice,
}
return vm.NewEVM(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
diff --git a/eth/backend.go b/eth/backend.go
index c7df517c0..8a837f7b8 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -148,8 +148,10 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
core.WriteChainConfig(chainDb, genesisHash, chainConfig)
}
- newPool := core.NewTxPool(config.TxPool, eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
- eth.txPool = newPool
+ if config.TxPool.Journal != "" {
+ config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)
+ }
+ eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
maxPeers := config.MaxPeers
if config.LightServ > 0 {
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 45bb87322..02df03fff 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -203,8 +203,6 @@ func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*
if err == nil {
if r == nil {
return nil, ethereum.NotFound
- } else if len(r.PostState) == 0 {
- return nil, fmt.Errorf("server returned receipt without post state")
}
}
return r, err
diff --git a/light/txpool.go b/light/txpool.go
index 416148b7e..1d52aa622 100644
--- a/light/txpool.go
+++ b/light/txpool.go
@@ -81,7 +81,7 @@ type TxRelayBackend interface {
func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, chain *LightChain, relay TxRelayBackend) *TxPool {
pool := &TxPool{
config: config,
- signer: types.HomesteadSigner{},
+ signer: types.NewEIP155Signer(config.ChainId),
nonce: make(map[common.Address]uint64),
pending: make(map[common.Hash]*types.Transaction),
mined: make(map[common.Hash][]*types.Transaction),
diff --git a/mobile/accounts.go b/mobile/accounts.go
index 977999c3a..4d979bfff 100644
--- a/mobile/accounts.go
+++ b/mobile/accounts.go
@@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
@@ -111,7 +112,7 @@ func (ks *KeyStore) DeleteAccount(account *Account, passphrase string) error {
// SignHash calculates a ECDSA signature for the given hash. The produced signature
// is in the [R || S || V] format where V is 0 or 1.
func (ks *KeyStore) SignHash(address *Address, hash []byte) (signature []byte, _ error) {
- return ks.keystore.SignHash(accounts.Account{Address: address.address}, hash)
+ return ks.keystore.SignHash(accounts.Account{Address: address.address}, common.CopyBytes(hash))
}
// SignTx signs the given transaction with the requested account.
@@ -130,7 +131,7 @@ func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (
// be decrypted with the given passphrase. The produced signature is in the
// [R || S || V] format where V is 0 or 1.
func (ks *KeyStore) SignHashPassphrase(account *Account, passphrase string, hash []byte) (signature []byte, _ error) {
- return ks.keystore.SignHashWithPassphrase(account.account, passphrase, hash)
+ return ks.keystore.SignHashWithPassphrase(account.account, passphrase, common.CopyBytes(hash))
}
// SignTxPassphrase signs the transaction if the private key matching the
@@ -189,7 +190,7 @@ func (ks *KeyStore) ExportKey(account *Account, passphrase, newPassphrase string
// ImportKey stores the given encrypted JSON key into the key directory.
func (ks *KeyStore) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (account *Account, _ error) {
- acc, err := ks.keystore.Import(keyJSON, passphrase, newPassphrase)
+ acc, err := ks.keystore.Import(common.CopyBytes(keyJSON), passphrase, newPassphrase)
if err != nil {
return nil, err
}
@@ -198,7 +199,7 @@ func (ks *KeyStore) ImportKey(keyJSON []byte, passphrase, newPassphrase string)
// ImportECDSAKey stores the given encrypted JSON key into the key directory.
func (ks *KeyStore) ImportECDSAKey(key []byte, passphrase string) (account *Account, _ error) {
- privkey, err := crypto.ToECDSA(key)
+ privkey, err := crypto.ToECDSA(common.CopyBytes(key))
if err != nil {
return nil, err
}
@@ -212,7 +213,7 @@ func (ks *KeyStore) ImportECDSAKey(key []byte, passphrase string) (account *Acco
// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
// a key file in the key directory. The key file is encrypted with the same passphrase.
func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (ccount *Account, _ error) {
- account, err := ks.keystore.ImportPreSaleKey(keyJSON, passphrase)
+ account, err := ks.keystore.ImportPreSaleKey(common.CopyBytes(keyJSON), passphrase)
if err != nil {
return nil, err
}
diff --git a/mobile/big.go b/mobile/big.go
index 525717caa..564fbad47 100644
--- a/mobile/big.go
+++ b/mobile/big.go
@@ -21,6 +21,8 @@ package geth
import (
"errors"
"math/big"
+
+ "github.com/ethereum/go-ethereum/common"
)
// A BigInt represents a signed multi-precision integer.
@@ -52,7 +54,7 @@ func (bi *BigInt) GetInt64() int64 {
// SetBytes interprets buf as the bytes of a big-endian unsigned integer and sets
// the big int to that value.
func (bi *BigInt) SetBytes(buf []byte) {
- bi.bigint.SetBytes(buf)
+ bi.bigint.SetBytes(common.CopyBytes(buf))
}
// SetInt64 sets the big int to x.
diff --git a/mobile/bind.go b/mobile/bind.go
index bc4eb25ba..084d6ef4c 100644
--- a/mobile/bind.go
+++ b/mobile/bind.go
@@ -119,7 +119,7 @@ func DeployContract(opts *TransactOpts, abiJSON string, bytecode []byte, client
if err != nil {
return nil, err
}
- addr, tx, bound, err := bind.DeployContract(&opts.opts, parsed, bytecode, client.client, args.objects...)
+ addr, tx, bound, err := bind.DeployContract(&opts.opts, parsed, common.CopyBytes(bytecode), client.client, args.objects...)
if err != nil {
return nil, err
}
diff --git a/mobile/common.go b/mobile/common.go
index 3090014c5..047d8e1f6 100644
--- a/mobile/common.go
+++ b/mobile/common.go
@@ -35,7 +35,7 @@ type Hash struct {
// NewHashFromBytes converts a slice of bytes to a hash value.
func NewHashFromBytes(binary []byte) (hash *Hash, _ error) {
h := new(Hash)
- if err := h.SetBytes(binary); err != nil {
+ if err := h.SetBytes(common.CopyBytes(binary)); err != nil {
return nil, err
}
return h, nil
@@ -136,7 +136,7 @@ type Address struct {
// NewAddressFromBytes converts a slice of bytes to a hash value.
func NewAddressFromBytes(binary []byte) (address *Address, _ error) {
a := new(Address)
- if err := a.SetBytes(binary); err != nil {
+ if err := a.SetBytes(common.CopyBytes(binary)); err != nil {
return nil, err
}
return a, nil
diff --git a/mobile/ethereum.go b/mobile/ethereum.go
index 30a94dc89..c9bb3013c 100644
--- a/mobile/ethereum.go
+++ b/mobile/ethereum.go
@@ -64,7 +64,7 @@ func (msg *CallMsg) SetFrom(address *Address) { msg.msg.From = address.address
func (msg *CallMsg) SetGas(gas int64) { msg.msg.Gas = big.NewInt(gas) }
func (msg *CallMsg) SetGasPrice(price *BigInt) { msg.msg.GasPrice = price.bigint }
func (msg *CallMsg) SetValue(value *BigInt) { msg.msg.Value = value.bigint }
-func (msg *CallMsg) SetData(data []byte) { msg.msg.Data = data }
+func (msg *CallMsg) SetData(data []byte) { msg.msg.Data = common.CopyBytes(data) }
func (msg *CallMsg) SetTo(address *Address) {
if address == nil {
msg.msg.To = nil
diff --git a/mobile/interface.go b/mobile/interface.go
index 10eac5f72..72958e66a 100644
--- a/mobile/interface.go
+++ b/mobile/interface.go
@@ -46,7 +46,7 @@ func (i *Interface) SetBool(b bool) { i.object = &b }
func (i *Interface) SetBools(bs []bool) { i.object = &bs }
func (i *Interface) SetString(str string) { i.object = &str }
func (i *Interface) SetStrings(strs *Strings) { i.object = &strs.strs }
-func (i *Interface) SetBinary(binary []byte) { i.object = &binary }
+func (i *Interface) SetBinary(binary []byte) { b := common.CopyBytes(binary); i.object = &b }
func (i *Interface) SetBinaries(binaries [][]byte) { i.object = &binaries }
func (i *Interface) SetAddress(address *Address) { i.object = &address.address }
func (i *Interface) SetAddresses(addrs *Addresses) { i.object = &addrs.addresses }
diff --git a/mobile/params.go b/mobile/params.go
index 9c58a90ab..45fe870ee 100644
--- a/mobile/params.go
+++ b/mobile/params.go
@@ -41,6 +41,15 @@ func TestnetGenesis() string {
return string(enc)
}
+// RinkebyGenesis returns the JSON spec to use for the Rinkeby test network
+func RinkebyGenesis() string {
+ enc, err := json.Marshal(core.DefaultRinkebyGenesisBlock())
+ if err != nil {
+ panic(err)
+ }
+ return string(enc)
+}
+
// FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated
// by the foundation running the V5 discovery protocol.
func FoundationBootnodes() *Enodes {
diff --git a/mobile/types.go b/mobile/types.go
index 02282f7a3..088c7c6b3 100644
--- a/mobile/types.go
+++ b/mobile/types.go
@@ -23,6 +23,7 @@ import (
"errors"
"fmt"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -68,7 +69,7 @@ func NewHeaderFromRLP(data []byte) (*Header, error) {
h := &Header{
header: new(types.Header),
}
- if err := rlp.DecodeBytes(data, h.header); err != nil {
+ if err := rlp.DecodeBytes(common.CopyBytes(data), h.header); err != nil {
return nil, err
}
return h, nil
@@ -145,7 +146,7 @@ func NewBlockFromRLP(data []byte) (*Block, error) {
b := &Block{
block: new(types.Block),
}
- if err := rlp.DecodeBytes(data, b.block); err != nil {
+ if err := rlp.DecodeBytes(common.CopyBytes(data), b.block); err != nil {
return nil, err
}
return b, nil
@@ -212,7 +213,7 @@ type Transaction struct {
// NewTransaction creates a new transaction with the given properties.
func NewTransaction(nonce int64, to *Address, amount, gasLimit, gasPrice *BigInt, data []byte) *Transaction {
- return &Transaction{types.NewTransaction(uint64(nonce), to.address, amount.bigint, gasLimit.bigint, gasPrice.bigint, data)}
+ return &Transaction{types.NewTransaction(uint64(nonce), to.address, amount.bigint, gasLimit.bigint, gasPrice.bigint, common.CopyBytes(data))}
}
// NewTransactionFromRLP parses a transaction from an RLP data dump.
@@ -220,7 +221,7 @@ func NewTransactionFromRLP(data []byte) (*Transaction, error) {
tx := &Transaction{
tx: new(types.Transaction),
}
- if err := rlp.DecodeBytes(data, tx.tx); err != nil {
+ if err := rlp.DecodeBytes(common.CopyBytes(data), tx.tx); err != nil {
return nil, err
}
return tx, nil
@@ -265,10 +266,11 @@ func (tx *Transaction) GetSigHash() *Hash { return &Hash{tx.tx.SigHash(types.Hom
func (tx *Transaction) GetCost() *BigInt { return &BigInt{tx.tx.Cost()} }
func (tx *Transaction) GetFrom(chainID *BigInt) (address *Address, _ error) {
- if chainID == nil { // Null passed from mobile app
- chainID = new(BigInt)
+ var signer types.Signer = types.HomesteadSigner{}
+ if chainID != nil {
+ signer = types.NewEIP155Signer(chainID.bigint)
}
- from, err := types.Sender(types.NewEIP155Signer(chainID.bigint), tx.tx)
+ from, err := types.Sender(signer, tx.tx)
return &Address{from}, err
}
@@ -279,8 +281,12 @@ func (tx *Transaction) GetTo() *Address {
return nil
}
-func (tx *Transaction) WithSignature(sig []byte) (signedTx *Transaction, _ error) {
- rawTx, err := tx.tx.WithSignature(types.HomesteadSigner{}, sig)
+func (tx *Transaction) WithSignature(sig []byte, chainID *BigInt) (signedTx *Transaction, _ error) {
+ var signer types.Signer = types.HomesteadSigner{}
+ if chainID != nil {
+ signer = types.NewEIP155Signer(chainID.bigint)
+ }
+ rawTx, err := tx.tx.WithSignature(signer, common.CopyBytes(sig))
return &Transaction{rawTx}, err
}
@@ -310,7 +316,7 @@ func NewReceiptFromRLP(data []byte) (*Receipt, error) {
r := &Receipt{
receipt: new(types.Receipt),
}
- if err := rlp.DecodeBytes(data, r.receipt); err != nil {
+ if err := rlp.DecodeBytes(common.CopyBytes(data), r.receipt); err != nil {
return nil, err
}
return r, nil
diff --git a/node/config.go b/node/config.go
index 61e0008ef..abbe64f43 100644
--- a/node/config.go
+++ b/node/config.go
@@ -316,8 +316,8 @@ func (c *Config) StaticNodes() []*discover.Node {
return c.parsePersistentNodes(c.resolvePath(datadirStaticNodes))
}
-// TrusterNodes returns a list of node enode URLs configured as trusted nodes.
-func (c *Config) TrusterNodes() []*discover.Node {
+// TrustedNodes returns a list of node enode URLs configured as trusted nodes.
+func (c *Config) TrustedNodes() []*discover.Node {
return c.parsePersistentNodes(c.resolvePath(datadirTrustedNodes))
}
diff --git a/node/node.go b/node/node.go
index a372b1c25..e3f0232b4 100644
--- a/node/node.go
+++ b/node/node.go
@@ -160,7 +160,7 @@ func (n *Node) Start() error {
n.serverConfig.StaticNodes = n.config.StaticNodes()
}
if n.serverConfig.TrustedNodes == nil {
- n.serverConfig.TrustedNodes = n.config.TrusterNodes()
+ n.serverConfig.TrustedNodes = n.config.TrustedNodes()
}
if n.serverConfig.NodeDatabase == "" {
n.serverConfig.NodeDatabase = n.config.NodeDB()
diff --git a/swarm/api/http/server.go b/swarm/api/http/server.go
index 5f64f971b..b4032839a 100644
--- a/swarm/api/http/server.go
+++ b/swarm/api/http/server.go
@@ -522,6 +522,12 @@ func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) {
// HandleGetFile handles a GET request to bzz://<manifest>/<path> and responds
// with the content of the file at <path> from the given <manifest>
func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
+ // ensure the root path has a trailing slash so that relative URLs work
+ if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
+ http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently)
+ return
+ }
+
key, err := s.api.Resolve(r.uri)
if err != nil {
s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
diff --git a/swarm/api/http/server_test.go b/swarm/api/http/server_test.go
index 0b124a19a..14abd1df4 100644
--- a/swarm/api/http/server_test.go
+++ b/swarm/api/http/server_test.go
@@ -18,12 +18,16 @@ package http_test
import (
"bytes"
+ "errors"
+ "fmt"
"io/ioutil"
"net/http"
"sync"
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/swarm/api"
+ swarm "github.com/ethereum/go-ethereum/swarm/api/client"
"github.com/ethereum/go-ethereum/swarm/storage"
"github.com/ethereum/go-ethereum/swarm/testutil"
)
@@ -128,3 +132,61 @@ func TestBzzrGetPath(t *testing.T) {
}
}
+
+// TestBzzRootRedirect tests that getting the root path of a manifest without
+// a trailing slash gets redirected to include the trailing slash so that
+// relative URLs work as expected.
+func TestBzzRootRedirect(t *testing.T) {
+ srv := testutil.NewTestSwarmServer(t)
+ defer srv.Close()
+
+ // create a manifest with some data at the root path
+ client := swarm.NewClient(srv.URL)
+ data := []byte("data")
+ file := &swarm.File{
+ ReadCloser: ioutil.NopCloser(bytes.NewReader(data)),
+ ManifestEntry: api.ManifestEntry{
+ Path: "",
+ ContentType: "text/plain",
+ Size: int64(len(data)),
+ },
+ }
+ hash, err := client.Upload(file, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // define a CheckRedirect hook which ensures there is only a single
+ // redirect to the correct URL
+ redirected := false
+ httpClient := http.Client{
+ CheckRedirect: func(req *http.Request, via []*http.Request) error {
+ if redirected {
+ return errors.New("too many redirects")
+ }
+ redirected = true
+ expectedPath := "/bzz:/" + hash + "/"
+ if req.URL.Path != expectedPath {
+ return fmt.Errorf("expected redirect to %q, got %q", expectedPath, req.URL.Path)
+ }
+ return nil
+ },
+ }
+
+ // perform the GET request and assert the response
+ res, err := httpClient.Get(srv.URL + "/bzz:/" + hash)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+ if !redirected {
+ t.Fatal("expected GET /bzz:/<hash> to redirect to /bzz:/<hash>/ but it didn't")
+ }
+ gotData, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(gotData, data) {
+ t.Fatalf("expected response to equal %q, got %q", data, gotData)
+ }
+}
diff --git a/trie/sync.go b/trie/sync.go
index 9e8449431..1e4f8d87c 100644
--- a/trie/sync.go
+++ b/trie/sync.go
@@ -55,7 +55,7 @@ type SyncResult struct {
// syncMemBatch is an in-memory buffer of successfully downloaded but not yet
// persisted data items.
type syncMemBatch struct {
- batch map[common.Hash][]byte // In-memory membatch of recently ocmpleted items
+ batch map[common.Hash][]byte // In-memory membatch of recently completed items
order []common.Hash // Order of completion to prevent out-of-order data loss
}