Merge commit '66432f3821badf24d526f2d9205f36c0543219de'

This commit is contained in:
Wei Tang 2018-05-12 18:39:23 +08:00
commit 64a4d7f1ad
44 changed files with 471 additions and 232 deletions

View file

@ -34,7 +34,6 @@ matrix:
script: script:
- unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703 - unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703
- brew update - brew update
- brew install caskroom/cask/brew-cask
- brew cask install osxfuse - brew cask install osxfuse
- go run build/ci.go install - go run build/ci.go install
- travis_wait 60 go run build/ci.go test - travis_wait 60 go run build/ci.go test

View file

@ -33,15 +33,15 @@ type Event struct {
Inputs Arguments Inputs Arguments
} }
func (event Event) String() string { func (e Event) String() string {
inputs := make([]string, len(event.Inputs)) inputs := make([]string, len(e.Inputs))
for i, input := range event.Inputs { for i, input := range e.Inputs {
inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type) inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type)
if input.Indexed { if input.Indexed {
inputs[i] = fmt.Sprintf("%v indexed %v", input.Name, input.Type) inputs[i] = fmt.Sprintf("%v indexed %v", input.Name, input.Type)
} }
} }
return fmt.Sprintf("event %v(%v)", event.Name, strings.Join(inputs, ", ")) return fmt.Sprintf("e %v(%v)", e.Name, strings.Join(inputs, ", "))
} }
// Id returns the canonical representation of the event's signature used by the // Id returns the canonical representation of the event's signature used by the

View file

@ -731,7 +731,7 @@ func doAndroidArchive(cmdline []string) {
// Build the Android archive and Maven resources // Build the Android archive and Maven resources
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind")) build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind"))
build.MustRun(gomobileTool("init", "--ndk", os.Getenv("ANDROID_NDK"))) build.MustRun(gomobileTool("init", "--ndk", os.Getenv("ANDROID_NDK")))
build.MustRun(gomobileTool("bind", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile")) build.MustRun(gomobileTool("bind", "-ldflags", "-s -w", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
if *local { if *local {
// If we're building locally, copy bundle to build dir and skip Maven // If we're building locally, copy bundle to build dir and skip Maven
@ -852,7 +852,7 @@ func doXCodeFramework(cmdline []string) {
// Build the iOS XCode framework // Build the iOS XCode framework
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind")) build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind"))
build.MustRun(gomobileTool("init")) build.MustRun(gomobileTool("init"))
bind := gomobileTool("bind", "--target", "ios", "--tags", "ios", "-v", "github.com/ethereum/go-ethereum/mobile") bind := gomobileTool("bind", "-ldflags", "-s -w", "--target", "ios", "--tags", "ios", "-v", "github.com/ethereum/go-ethereum/mobile")
if *local { if *local {
// If we're building locally, use the build folder and stop afterwards // If we're building locally, use the build folder and stop afterwards

View file

@ -12,6 +12,11 @@ synchronised with the chain or a particular Ethereum node that has no built-in (
Clef can run as a daemon on the same machine, or off a usb-stick like [usb armory](https://inversepath.com/usbarmory), Clef can run as a daemon on the same machine, or off a usb-stick like [usb armory](https://inversepath.com/usbarmory),
or a separate VM in a [QubesOS](https://www.qubes-os.org/) type os setup. or a separate VM in a [QubesOS](https://www.qubes-os.org/) type os setup.
Check out
* the [tutorial](tutorial.md) for some concrete examples on how the signer works.
* the [setup docs](docs/setup.md) for some information on how to configure it to work on QubesOS or USBArmory.
## Command line flags ## Command line flags
Clef accepts the following command line options: Clef accepts the following command line options:
@ -49,7 +54,6 @@ Example:
signer -keystore /my/keystore -chainid 4 signer -keystore /my/keystore -chainid 4
``` ```
Check out the [tutorial](tutorial.md) for some concrete examples on how the signer works.
## Security model ## Security model
@ -862,3 +866,12 @@ A UI should conform to the following rules.
along with the UI. along with the UI.
### UI Implementations
There are a couple of implementation for a UI. We'll try to keep this list up to date.
| Name | Repo | UI type| No external resources| Blocky support| Verifies permissions | Hash information | No secondary storage | Statically linked| Can modify parameters|
| ---- | ---- | -------| ---- | ---- | ---- |---- | ---- | ---- | ---- |
| QtSigner| https://github.com/holiman/qtsigner/| Python3/QT-based| :+1:| :+1:| :+1:| :+1:| :+1:| :x: | :+1: (partially)|
| GtkSigner| https://github.com/holiman/gtksigner| Python3/GTK-based| :+1:| :x:| :x:| :+1:| :+1:| :x: | :x: |
| Frame | https://github.com/floating/frame/commits/go-signer| Electron-based| :x:| :x:| :x:| :x:| ?| :x: | :x: |

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,23 @@
"""
This implements a dispatcher which listens to localhost:8550, and proxies
requests via qrexec to the service qubes.EthSign on a target domain
"""
import http.server
import socketserver,subprocess
PORT=8550
TARGET_DOMAIN= 'debian-work'
class Dispatcher(http.server.BaseHTTPRequestHandler):
def do_POST(self):
post_data = self.rfile.read(int(self.headers['Content-Length']))
p = subprocess.Popen(['/usr/bin/qrexec-client-vm',TARGET_DOMAIN,'qubes.Clefsign'],stdin=subprocess.PIPE, stdout=subprocess.PIPE)
output = p.communicate(post_data)[0]
self.wfile.write(output)
with socketserver.TCPServer(("",PORT), Dispatcher) as httpd:
print("Serving at port", PORT)
httpd.serve_forever()

View file

@ -0,0 +1,16 @@
#!/bin/bash
SIGNER_BIN="/home/user/tools/clef/clef"
SIGNER_CMD="/home/user/tools/gtksigner/gtkui.py -s $SIGNER_BIN"
# Start clef if not already started
if [ ! -S /home/user/.clef/clef.ipc ]; then
$SIGNER_CMD &
sleep 1
fi
# Should be started by now
if [ -S /home/user/.clef/clef.ipc ]; then
# Post incoming request to HTTP channel
curl -H "Content-Type: application/json" -X POST -d @- http://localhost:8550 2>/dev/null
fi

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

198
cmd/clef/docs/setup.md Normal file
View file

@ -0,0 +1,198 @@
# Setting up Clef
This document describes how Clef can be used in a more secure manner than executing it from your everyday laptop,
in order to ensure that the keys remain safe in the event that your computer should get compromised.
## Qubes OS
### Background
The Qubes operating system is based around virtual machines (qubes), where a set of virtual machines are configured, typically for
different purposes such as:
- personal
- Your personal email, browsing etc
- work
- Work email etc
- vault
- a VM without network access, where gpg-keys and/or keepass credentials are stored.
A couple of dedicated virtual machines handle externalities:
- sys-net provides networking to all other (network-enabled) machines
- sys-firewall handles firewall rules
- sys-usb handles USB devices, and can map usb-devices to certain qubes.
The goal of this document is to describe how we can set up clef to provide secure transaction
signing from a `vault` vm, to another networked qube which runs Dapps.
### Setup
There are two ways that this can be achieved: integrated via Qubes or integrated via networking.
#### 1. Qubes Integrated
Qubes provdes a facility for inter-qubes communication via `qrexec`. A qube can request to make a cross-qube RPC request
to another qube. The OS then asks the user if the call is permitted.
![Example](qubes/qrexec-example.png)
A policy-file can be created to allow such interaction. On the `target` domain, a service is invoked which can read the
`stdin` from the `client` qube.
This is how [Split GPG](https://www.qubes-os.org/doc/split-gpg/) is implemented. We can set up Clef the same way:
##### Server
![Clef via qrexec](qubes/clef_qubes_qrexec.png)
On the `target` qubes, we need to define the rpc service.
[qubes.Clefsign](qubes/qubes.Clefsign):
```bash
#!/bin/bash
SIGNER_BIN="/home/user/tools/clef/clef"
SIGNER_CMD="/home/user/tools/gtksigner/gtkui.py -s $SIGNER_BIN"
# Start clef if not already started
if [ ! -S /home/user/.clef/clef.ipc ]; then
$SIGNER_CMD &
sleep 1
fi
# Should be started by now
if [ -S /home/user/.clef/clef.ipc ]; then
# Post incoming request to HTTP channel
curl -H "Content-Type: application/json" -X POST -d @- http://localhost:8550 2>/dev/null
fi
```
This RPC service is not complete (see notes about HTTP headers below), but works as a proof-of-concept.
It will forward the data received on `stdin` (forwarded by the OS) to Clef's HTTP channel.
It would have been possible to send data directly to the `/home/user/.clef/.clef.ipc`
socket via e.g `nc -U /home/user/.clef/clef.ipc`, but the reason for sending the request
data over `HTTP` instead of `IPC` is that we want the ability to forward `HTTP` headers.
To enable the service:
``` bash
sudo cp qubes.Clefsign /etc/qubes-rpc/
sudo chmod +x /etc/qubes-rpc/ qubes.Clefsign
```
This setup uses [gtksigner](https://github.com/holiman/gtksigner), which is a very minimal GTK-based UI that works well
with minimal requirements.
##### Client
On the `client` qube, we need to create a listener which will receive the request from the Dapp, and proxy it.
[qubes-client.py](qubes/client/qubes-client.py):
```python
"""
This implements a dispatcher which listens to localhost:8550, and proxies
requests via qrexec to the service qubes.EthSign on a target domain
"""
import http.server
import socketserver,subprocess
PORT=8550
TARGET_DOMAIN= 'debian-work'
class Dispatcher(http.server.BaseHTTPRequestHandler):
def do_POST(self):
post_data = self.rfile.read(int(self.headers['Content-Length']))
p = subprocess.Popen(['/usr/bin/qrexec-client-vm',TARGET_DOMAIN,'qubes.Clefsign'],stdin=subprocess.PIPE, stdout=subprocess.PIPE)
output = p.communicate(post_data)[0]
self.wfile.write(output)
with socketserver.TCPServer(("",PORT), Dispatcher) as httpd:
print("Serving at port", PORT)
httpd.serve_forever()
```
#### Testing
To test the flow, if we have set up `debian-work` as the `target`, we can do
```bash
$ cat newaccnt.json
{ "id": 0, "jsonrpc": "2.0","method": "account_new","params": []}
$ cat newaccnt.json| qrexec-client-vm debian-work qubes.Clefsign
```
This should pop up first a dialog to allow the IPC call:
![one](qubes/qubes_newaccount-1.png)
Followed by a GTK-dialog to approve the operation
![two](qubes/qubes_newaccount-2.png)
To test the full flow, we use the client wrapper. Start it on the `client` qube:
```
[user@work qubes]$ python3 qubes-client.py
```
Make the request over http (`client` qube):
```
[user@work clef]$ cat newaccnt.json | curl -X POST -d @- http://localhost:8550
```
And it should show the same popups again.
##### Pros and cons
The benefits of this setup are:
- This is the qubes-os intended model for inter-qube communication,
- and thus benefits from qubes-os dialogs and policies for user approval
However, it comes with a couple of drawbacks:
- The `qubes-gpg-client` must forward the http request via RPC to the `target` qube. When doing so, the proxy
will either drop important headers, or replace them.
- The `Host` header is most likely `localhost`
- The `Origin` header must be forwarded
- Information about the remote ip must be added as a `X-Forwarded-For`. However, Clef cannot always trust an `XFF` header,
since malicious clients may lie about `XFF` in order to fool the http server into believing it comes from another address.
- Even with a policy in place to allow rpc-calls between `caller` and `target`, there will be several popups:
- One qubes-specific where the user specifies the `target` vm
- One clef-specific to approve the transaction
#### 2. Network integrated
The second way to set up Clef on a qubes system is to allow networking, and have Clef listen to a port which is accessible
form other qubes.
![Clef via http](qubes/clef_qubes_http.png)
## USBArmory
The [USB armory](https://inversepath.com/usbarmory) is an open source hardware design with an 800 Mhz ARM processor. It is a pocket-size
computer. When inserted into a laptop, it identifies itself as a USB network interface, basically adding another network
to your computer. Over this new network interface, you can SSH into the device.
Running Clef off a USB armory means that you can use the armory as a very versatile offline computer, which only
ever connects to a local network between your computer and the device itself.
Needless to say, the while this model should be fairly secure against remote attacks, an attacker with physical access
to the USB Armory would trivially be able to extract the contents of the device filesystem.

View file

@ -21,12 +21,12 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big"
"os" "os"
goruntime "runtime"
"runtime/pprof" "runtime/pprof"
"time" "time"
goruntime "runtime"
"github.com/ethereum/go-ethereum/cmd/evm/internal/compiler" "github.com/ethereum/go-ethereum/cmd/evm/internal/compiler"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -86,6 +86,7 @@ func runCmd(ctx *cli.Context) error {
chainConfig *params.ChainConfig chainConfig *params.ChainConfig
sender = common.BytesToAddress([]byte("sender")) sender = common.BytesToAddress([]byte("sender"))
receiver = common.BytesToAddress([]byte("receiver")) receiver = common.BytesToAddress([]byte("receiver"))
blockNumber uint64
) )
if ctx.GlobalBool(MachineFlag.Name) { if ctx.GlobalBool(MachineFlag.Name) {
tracer = NewJSONLogger(logconfig, os.Stdout) tracer = NewJSONLogger(logconfig, os.Stdout)
@ -101,6 +102,7 @@ func runCmd(ctx *cli.Context) error {
genesis := gen.ToBlock(db) genesis := gen.ToBlock(db)
statedb, _ = state.New(genesis.Root(), state.NewDatabase(db)) statedb, _ = state.New(genesis.Root(), state.NewDatabase(db))
chainConfig = gen.Config chainConfig = gen.Config
blockNumber = gen.Number
} else { } else {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ = state.New(common.Hash{}, state.NewDatabase(db)) statedb, _ = state.New(common.Hash{}, state.NewDatabase(db))
@ -156,11 +158,12 @@ func runCmd(ctx *cli.Context) error {
initialGas := ctx.GlobalUint64(GasFlag.Name) initialGas := ctx.GlobalUint64(GasFlag.Name)
runtimeConfig := runtime.Config{ runtimeConfig := runtime.Config{
Origin: sender, Origin: sender,
State: statedb, State: statedb,
GasLimit: initialGas, GasLimit: initialGas,
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name), GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
Value: utils.GlobalBig(ctx, ValueFlag.Name), Value: utils.GlobalBig(ctx, ValueFlag.Name),
BlockNumber: new(big.Int).SetUint64(blockNumber),
EVMConfig: vm.Config{ EVMConfig: vm.Config{
Tracer: tracer, Tracer: tracer,
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name), Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),

View file

@ -170,11 +170,11 @@ var (
} }
FastSyncFlag = cli.BoolFlag{ FastSyncFlag = cli.BoolFlag{
Name: "fast", Name: "fast",
Usage: "Enable fast syncing through state downloads", Usage: "Enable fast syncing through state downloads (replaced by --syncmode)",
} }
LightModeFlag = cli.BoolFlag{ LightModeFlag = cli.BoolFlag{
Name: "light", Name: "light",
Usage: "Enable light client mode", Usage: "Enable light client mode (replaced by --syncmode)",
} }
defaultSyncMode = eth.DefaultConfig.SyncMode defaultSyncMode = eth.DefaultConfig.SyncMode
SyncModeFlag = TextMarshalerFlag{ SyncModeFlag = TextMarshalerFlag{

View file

@ -178,9 +178,7 @@ func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
} }
value.SetBytes(content) value.SetBytes(content)
} }
if (value != common.Hash{}) { self.cachedStorage[key] = value
self.cachedStorage[key] = value
}
return value return value
} }
@ -197,7 +195,6 @@ func (self *stateObject) SetState(db Database, key, value common.Hash) {
func (self *stateObject) setState(key, value common.Hash) { func (self *stateObject) setState(key, value common.Hash) {
self.cachedStorage[key] = value self.cachedStorage[key] = value
self.dirtyStorage[key] = value self.dirtyStorage[key] = value
} }
// updateTrie writes cached storage modifications into the object's storage trie. // updateTrie writes cached storage modifications into the object's storage trie.

View file

@ -572,27 +572,6 @@ func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {
self.txIndex = ti self.txIndex = ti
} }
// DeleteSuicides flags the suicided objects for deletion so that it
// won't be referenced again when called / queried up on.
//
// DeleteSuicides should not be used for consensus related updates
// under any circumstances.
func (s *StateDB) DeleteSuicides() {
// Reset refund so that any used-gas calculations can use this method.
s.clearJournalAndRefund()
for addr := range s.stateObjectsDirty {
stateObject := s.stateObjects[addr]
// If the object has been removed by a suicide
// flag the object as deleted.
if stateObject.suicided {
stateObject.deleted = true
}
delete(s.stateObjectsDirty, addr)
}
}
func (s *StateDB) clearJournalAndRefund() { func (s *StateDB) clearJournalAndRefund() {
s.journal = newJournal() s.journal = newJournal()
s.validRevisions = s.validRevisions[:0] s.validRevisions = s.validRevisions[:0]

View file

@ -618,7 +618,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
// If the transaction pool is full, discard underpriced transactions // If the transaction pool is full, discard underpriced transactions
if uint64(len(pool.all)) >= pool.config.GlobalSlots+pool.config.GlobalQueue { if uint64(len(pool.all)) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
// If the new transaction is underpriced, don't accept it // If the new transaction is underpriced, don't accept it
if pool.priced.Underpriced(tx, pool.locals) { if !local && pool.priced.Underpriced(tx, pool.locals) {
log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice()) log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
underpricedTxCounter.Inc(1) underpricedTxCounter.Inc(1)
return false, ErrUnderpriced return false, ErrUnderpriced

View file

@ -1346,7 +1346,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
defer sub.Unsubscribe() defer sub.Unsubscribe()
// Create a number of test accounts and fund them // Create a number of test accounts and fund them
keys := make([]*ecdsa.PrivateKey, 3) keys := make([]*ecdsa.PrivateKey, 4)
for i := 0; i < len(keys); i++ { for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey() keys[i], _ = crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
@ -1406,18 +1406,22 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err) t.Fatalf("pool internal state corrupted: %v", err)
} }
// Ensure that adding local transactions can push out even higher priced ones // Ensure that adding local transactions can push out even higher priced ones
tx := pricedTransaction(1, 100000, big.NewInt(0), keys[2]) ltx = pricedTransaction(1, 100000, big.NewInt(0), keys[2])
if err := pool.AddLocal(tx); err != nil { if err := pool.AddLocal(ltx); err != nil {
t.Fatalf("failed to add underpriced local transaction: %v", err) t.Fatalf("failed to append underpriced local transaction: %v", err)
}
ltx = pricedTransaction(0, 100000, big.NewInt(0), keys[3])
if err := pool.AddLocal(ltx); err != nil {
t.Fatalf("failed to add new underpriced local transaction: %v", err)
} }
pending, queued = pool.Stats() pending, queued = pool.Stats()
if pending != 2 { if pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
} }
if queued != 2 { if queued != 1 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2) t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
} }
if err := validateEvents(events, 1); err != nil { if err := validateEvents(events, 2); err != nil {
t.Fatalf("local event firing failed: %v", err) t.Fatalf("local event firing failed: %v", err)
} }
if err := validateTxPoolInternals(pool); err != nil { if err := validateTxPoolInternals(pool); err != nil {

View file

@ -339,11 +339,14 @@ type TransactionsByPriceAndNonce struct {
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce { func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce {
// Initialize a price based heap with the head transactions // Initialize a price based heap with the head transactions
heads := make(TxByPrice, 0, len(txs)) heads := make(TxByPrice, 0, len(txs))
for _, accTxs := range txs { for from, accTxs := range txs {
heads = append(heads, accTxs[0]) heads = append(heads, accTxs[0])
// Ensure the sender address is from the signer // Ensure the sender address is from the signer
acc, _ := Sender(signer, accTxs[0]) acc, _ := Sender(signer, accTxs[0])
txs[acc] = accTxs[1:] txs[acc] = accTxs[1:]
if from != acc {
delete(txs, from)
}
} }
heap.Init(&heads) heap.Init(&heads)

View file

@ -139,15 +139,15 @@ func (c *Contract) Value() *big.Int {
} }
// SetCode sets the code to the contract // SetCode sets the code to the contract
func (self *Contract) SetCode(hash common.Hash, code []byte) { func (c *Contract) SetCode(hash common.Hash, code []byte) {
self.Code = code c.Code = code
self.CodeHash = hash c.CodeHash = hash
} }
// SetCallCode sets the code of the contract and address of the backing data // SetCallCode sets the code of the contract and address of the backing data
// object // object
func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) { func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
self.Code = code c.Code = code
self.CodeHash = hash c.CodeHash = hash
self.CodeAddr = addr c.CodeAddr = addr
} }

View file

@ -160,6 +160,11 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
precompiles = PrecompiledContractsByzantium precompiles = PrecompiledContractsByzantium
} }
if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 { if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
// Calling a non existing account, don't do antything, but ping the tracer
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
}
return nil, gas, nil return nil, gas, nil
} }
evm.StateDB.CreateAccount(addr) evm.StateDB.CreateAccount(addr)

View file

@ -31,9 +31,9 @@ import (
type Storage map[common.Hash]common.Hash type Storage map[common.Hash]common.Hash
func (self Storage) Copy() Storage { func (s Storage) Copy() Storage {
cpy := make(Storage) cpy := make(Storage)
for key, value := range self { for key, value := range s {
cpy[key] = value cpy[key] = value
} }

View file

@ -51,14 +51,14 @@ func (m *Memory) Resize(size uint64) {
} }
// Get returns offset + size as a new slice // Get returns offset + size as a new slice
func (self *Memory) Get(offset, size int64) (cpy []byte) { func (m *Memory) Get(offset, size int64) (cpy []byte) {
if size == 0 { if size == 0 {
return nil return nil
} }
if len(self.store) > int(offset) { if len(m.store) > int(offset) {
cpy = make([]byte, size) cpy = make([]byte, size)
copy(cpy, self.store[offset:offset+size]) copy(cpy, m.store[offset:offset+size])
return return
} }
@ -67,13 +67,13 @@ func (self *Memory) Get(offset, size int64) (cpy []byte) {
} }
// GetPtr returns the offset + size // GetPtr returns the offset + size
func (self *Memory) GetPtr(offset, size int64) []byte { func (m *Memory) GetPtr(offset, size int64) []byte {
if size == 0 { if size == 0 {
return nil return nil
} }
if len(self.store) > int(offset) { if len(m.store) > int(offset) {
return self.store[offset : offset+size] return m.store[offset : offset+size]
} }
return nil return nil

View file

@ -375,10 +375,10 @@ var opCodeToString = map[OpCode]string{
SWAP: "SWAP", SWAP: "SWAP",
} }
func (o OpCode) String() string { func (op OpCode) String() string {
str := opCodeToString[o] str := opCodeToString[op]
if len(str) == 0 { if len(str) == 0 {
return fmt.Sprintf("Missing opcode 0x%x", int(o)) return fmt.Sprintf("Missing opcode 0x%x", int(op))
} }
return str return str

View file

@ -201,7 +201,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
break break
} }
task.statedb.DeleteSuicides() task.statedb.Finalise(true)
task.results[i] = &txTraceResult{Result: res} task.results[i] = &txTraceResult{Result: res}
} }
// Stream the result back to the user or abort on teardown // Stream the result back to the user or abort on teardown
@ -640,7 +640,8 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
} }
statedb.DeleteSuicides() // Ensure any modifications are committed to the state
statedb.Finalise(true)
} }
return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash) return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash)
} }

View file

@ -102,8 +102,8 @@ func randomSource() *rand.Rand {
// call the functions of the otto vm directly to circumvent the queue. These // call the functions of the otto vm directly to circumvent the queue. These
// functions should be used if and only if running a routine that was already // functions should be used if and only if running a routine that was already
// called from JS through an RPC call. // called from JS through an RPC call.
func (self *JSRE) runEventLoop() { func (re *JSRE) runEventLoop() {
defer close(self.closed) defer close(re.closed)
vm := otto.New() vm := otto.New()
r := randomSource() r := randomSource()
@ -202,14 +202,14 @@ loop:
break loop break loop
} }
} }
case req := <-self.evalQueue: case req := <-re.evalQueue:
// run the code, send the result back // run the code, send the result back
req.fn(vm) req.fn(vm)
close(req.done) close(req.done)
if waitForCallbacks && (len(registry) == 0) { if waitForCallbacks && (len(registry) == 0) {
break loop break loop
} }
case waitForCallbacks = <-self.stopEventLoop: case waitForCallbacks = <-re.stopEventLoop:
if !waitForCallbacks || (len(registry) == 0) { if !waitForCallbacks || (len(registry) == 0) {
break loop break loop
} }
@ -223,31 +223,31 @@ loop:
} }
// Do executes the given function on the JS event loop. // Do executes the given function on the JS event loop.
func (self *JSRE) Do(fn func(*otto.Otto)) { func (re *JSRE) Do(fn func(*otto.Otto)) {
done := make(chan bool) done := make(chan bool)
req := &evalReq{fn, done} req := &evalReq{fn, done}
self.evalQueue <- req re.evalQueue <- req
<-done <-done
} }
// stops the event loop before exit, optionally waits for all timers to expire // stops the event loop before exit, optionally waits for all timers to expire
func (self *JSRE) Stop(waitForCallbacks bool) { func (re *JSRE) Stop(waitForCallbacks bool) {
select { select {
case <-self.closed: case <-re.closed:
case self.stopEventLoop <- waitForCallbacks: case re.stopEventLoop <- waitForCallbacks:
<-self.closed <-re.closed
} }
} }
// Exec(file) loads and runs the contents of a file // Exec(file) loads and runs the contents of a file
// if a relative path is given, the jsre's assetPath is used // if a relative path is given, the jsre's assetPath is used
func (self *JSRE) Exec(file string) error { func (re *JSRE) Exec(file string) error {
code, err := ioutil.ReadFile(common.AbsolutePath(self.assetPath, file)) code, err := ioutil.ReadFile(common.AbsolutePath(re.assetPath, file))
if err != nil { if err != nil {
return err return err
} }
var script *otto.Script var script *otto.Script
self.Do(func(vm *otto.Otto) { re.Do(func(vm *otto.Otto) {
script, err = vm.Compile(file, code) script, err = vm.Compile(file, code)
if err != nil { if err != nil {
return return
@ -259,36 +259,36 @@ func (self *JSRE) Exec(file string) error {
// Bind assigns value v to a variable in the JS environment // Bind assigns value v to a variable in the JS environment
// This method is deprecated, use Set. // This method is deprecated, use Set.
func (self *JSRE) Bind(name string, v interface{}) error { func (re *JSRE) Bind(name string, v interface{}) error {
return self.Set(name, v) return re.Set(name, v)
} }
// Run runs a piece of JS code. // Run runs a piece of JS code.
func (self *JSRE) Run(code string) (v otto.Value, err error) { func (re *JSRE) Run(code string) (v otto.Value, err error) {
self.Do(func(vm *otto.Otto) { v, err = vm.Run(code) }) re.Do(func(vm *otto.Otto) { v, err = vm.Run(code) })
return v, err return v, err
} }
// Get returns the value of a variable in the JS environment. // Get returns the value of a variable in the JS environment.
func (self *JSRE) Get(ns string) (v otto.Value, err error) { func (re *JSRE) Get(ns string) (v otto.Value, err error) {
self.Do(func(vm *otto.Otto) { v, err = vm.Get(ns) }) re.Do(func(vm *otto.Otto) { v, err = vm.Get(ns) })
return v, err return v, err
} }
// Set assigns value v to a variable in the JS environment. // Set assigns value v to a variable in the JS environment.
func (self *JSRE) Set(ns string, v interface{}) (err error) { func (re *JSRE) Set(ns string, v interface{}) (err error) {
self.Do(func(vm *otto.Otto) { err = vm.Set(ns, v) }) re.Do(func(vm *otto.Otto) { err = vm.Set(ns, v) })
return err return err
} }
// loadScript executes a JS script from inside the currently executing JS code. // loadScript executes a JS script from inside the currently executing JS code.
func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value { func (re *JSRE) loadScript(call otto.FunctionCall) otto.Value {
file, err := call.Argument(0).ToString() file, err := call.Argument(0).ToString()
if err != nil { if err != nil {
// TODO: throw exception // TODO: throw exception
return otto.FalseValue() return otto.FalseValue()
} }
file = common.AbsolutePath(self.assetPath, file) file = common.AbsolutePath(re.assetPath, file)
source, err := ioutil.ReadFile(file) source, err := ioutil.ReadFile(file)
if err != nil { if err != nil {
// TODO: throw exception // TODO: throw exception
@ -305,10 +305,10 @@ func (self *JSRE) loadScript(call otto.FunctionCall) otto.Value {
// Evaluate executes code and pretty prints the result to the specified output // Evaluate executes code and pretty prints the result to the specified output
// stream. // stream.
func (self *JSRE) Evaluate(code string, w io.Writer) error { func (re *JSRE) Evaluate(code string, w io.Writer) error {
var fail error var fail error
self.Do(func(vm *otto.Otto) { re.Do(func(vm *otto.Otto) {
val, err := vm.Run(code) val, err := vm.Run(code)
if err != nil { if err != nil {
prettyError(vm, err, w) prettyError(vm, err, w)
@ -321,8 +321,8 @@ func (self *JSRE) Evaluate(code string, w io.Writer) error {
} }
// Compile compiles and then runs a piece of JS code. // Compile compiles and then runs a piece of JS code.
func (self *JSRE) Compile(filename string, src interface{}) (err error) { func (re *JSRE) Compile(filename string, src interface{}) (err error) {
self.Do(func(vm *otto.Otto) { _, err = compileAndRun(vm, filename, src) }) re.Do(func(vm *otto.Otto) { _, err = compileAndRun(vm, filename, src) })
return err return err
} }

View file

@ -21,10 +21,10 @@ import (
) )
const ( const (
VersionMajor = 1 // Major version component of the current release VersionMajor = 1 // Major version component of the current release
VersionMinor = 8 // Minor version component of the current release VersionMinor = 8 // Minor version component of the current release
VersionPatch = 7 // Patch version component of the current release VersionPatch = 7 // Patch version component of the current release
VersionMeta = "unstable" // Version metadata to append to the version string VersionMeta = "stable" // Version metadata to append to the version string
) )
// Version holds the textual version string. // Version holds the textual version string.

View file

@ -104,7 +104,7 @@ func (t *BlockTest) Run() error {
return err return err
} }
if gblock.Hash() != t.json.Genesis.Hash { if gblock.Hash() != t.json.Genesis.Hash {
return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x\n", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6]) return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6])
} }
if gblock.Root() != t.json.Genesis.StateRoot { if gblock.Root() != t.json.Genesis.StateRoot {
return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6]) return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6])

View file

@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
// This table defines supported forks and their chain config. // Forks table defines supported forks and their chain config.
var Forks = map[string]*params.ChainConfig{ var Forks = map[string]*params.ChainConfig{
"Frontier": { "Frontier": {
ChainId: big.NewInt(1), ChainId: big.NewInt(1),

View file

@ -42,7 +42,7 @@ var (
difficultyTestDir = filepath.Join(baseDir, "BasicTests") difficultyTestDir = filepath.Join(baseDir, "BasicTests")
) )
func readJson(reader io.Reader, value interface{}) error { func readJSON(reader io.Reader, value interface{}) error {
data, err := ioutil.ReadAll(reader) data, err := ioutil.ReadAll(reader)
if err != nil { if err != nil {
return fmt.Errorf("error reading JSON file: %v", err) return fmt.Errorf("error reading JSON file: %v", err)
@ -57,14 +57,14 @@ func readJson(reader io.Reader, value interface{}) error {
return nil return nil
} }
func readJsonFile(fn string, value interface{}) error { func readJSONFile(fn string, value interface{}) error {
file, err := os.Open(fn) file, err := os.Open(fn)
if err != nil { if err != nil {
return err return err
} }
defer file.Close() defer file.Close()
err = readJson(file, value) err = readJSON(file, value)
if err != nil { if err != nil {
return fmt.Errorf("%s in file %s", err.Error(), fn) return fmt.Errorf("%s in file %s", err.Error(), fn)
} }
@ -169,9 +169,8 @@ func (tm *testMatcher) checkFailure(t *testing.T, name string, err error) error
if err != nil { if err != nil {
t.Logf("error: %v", err) t.Logf("error: %v", err)
return nil return nil
} else {
return fmt.Errorf("test succeeded unexpectedly")
} }
return fmt.Errorf("test succeeded unexpectedly")
} }
return err return err
} }
@ -213,7 +212,7 @@ func (tm *testMatcher) runTestFile(t *testing.T, path, name string, runTest inte
// Load the file as map[string]<testType>. // Load the file as map[string]<testType>.
m := makeMapFromTestFunc(runTest) m := makeMapFromTestFunc(runTest)
if err := readJsonFile(path, m.Addr().Interface()); err != nil { if err := readJSONFile(path, m.Addr().Interface()); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -72,9 +72,8 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error {
if err := rlp.DecodeBytes(tt.json.RLP, tx); err != nil { if err := rlp.DecodeBytes(tt.json.RLP, tx); err != nil {
if tt.json.Transaction == nil { if tt.json.Transaction == nil {
return nil return nil
} else {
return fmt.Errorf("RLP decoding failed: %v", err)
} }
return fmt.Errorf("RLP decoding failed: %v", err)
} }
// Check sender derivation. // Check sender derivation.
signer := types.MakeSigner(config, new(big.Int).SetUint64(uint64(tt.json.BlockNumber))) signer := types.MakeSigner(config, new(big.Int).SetUint64(uint64(tt.json.BlockNumber)))

View file

@ -303,7 +303,7 @@ func (it *nodeIterator) push(state *nodeIteratorState, parentIndex *int, path []
it.path = path it.path = path
it.stack = append(it.stack, state) it.stack = append(it.stack, state)
if parentIndex != nil { if parentIndex != nil {
*parentIndex += 1 *parentIndex++
} }
} }
@ -380,7 +380,7 @@ func (it *differenceIterator) Next(bool) bool {
if !it.b.Next(true) { if !it.b.Next(true) {
return false return false
} }
it.count += 1 it.count++
if it.eof { if it.eof {
// a has reached eof, so we just return all elements from b // a has reached eof, so we just return all elements from b
@ -395,7 +395,7 @@ func (it *differenceIterator) Next(bool) bool {
it.eof = true it.eof = true
return true return true
} }
it.count += 1 it.count++
case 1: case 1:
// b is before a // b is before a
return true return true
@ -405,12 +405,12 @@ func (it *differenceIterator) Next(bool) bool {
if !it.b.Next(hasHash) { if !it.b.Next(hasHash) {
return false return false
} }
it.count += 1 it.count++
if !it.a.Next(hasHash) { if !it.a.Next(hasHash) {
it.eof = true it.eof = true
return true return true
} }
it.count += 1 it.count++
} }
} }
} }
@ -504,14 +504,14 @@ func (it *unionIterator) Next(descend bool) bool {
skipped := heap.Pop(it.items).(NodeIterator) skipped := heap.Pop(it.items).(NodeIterator)
// Skip the whole subtree if the nodes have hashes; otherwise just skip this node // Skip the whole subtree if the nodes have hashes; otherwise just skip this node
if skipped.Next(skipped.Hash() == common.Hash{}) { if skipped.Next(skipped.Hash() == common.Hash{}) {
it.count += 1 it.count++
// If there are more elements, push the iterator back on the heap // If there are more elements, push the iterator back on the heap
heap.Push(it.items, skipped) heap.Push(it.items, skipped)
} }
} }
if least.Next(descend) { if least.Next(descend) {
it.count += 1 it.count++
heap.Push(it.items, least) heap.Push(it.items, least)
} }

View file

@ -123,17 +123,17 @@ func decodeNode(hash, buf []byte, cachegen uint16) (node, error) {
} }
switch c, _ := rlp.CountValues(elems); c { switch c, _ := rlp.CountValues(elems); c {
case 2: case 2:
n, err := decodeShort(hash, buf, elems, cachegen) n, err := decodeShort(hash, elems, cachegen)
return n, wrapError(err, "short") return n, wrapError(err, "short")
case 17: case 17:
n, err := decodeFull(hash, buf, elems, cachegen) n, err := decodeFull(hash, elems, cachegen)
return n, wrapError(err, "full") return n, wrapError(err, "full")
default: default:
return nil, fmt.Errorf("invalid number of list elements: %v", c) return nil, fmt.Errorf("invalid number of list elements: %v", c)
} }
} }
func decodeShort(hash, buf, elems []byte, cachegen uint16) (node, error) { func decodeShort(hash, elems []byte, cachegen uint16) (node, error) {
kbuf, rest, err := rlp.SplitString(elems) kbuf, rest, err := rlp.SplitString(elems)
if err != nil { if err != nil {
return nil, err return nil, err
@ -155,7 +155,7 @@ func decodeShort(hash, buf, elems []byte, cachegen uint16) (node, error) {
return &shortNode{key, r, flag}, nil return &shortNode{key, r, flag}, nil
} }
func decodeFull(hash, buf, elems []byte, cachegen uint16) (*fullNode, error) { func decodeFull(hash, elems []byte, cachegen uint16) (*fullNode, error) {
n := &fullNode{flags: nodeFlag{hash: hash, gen: cachegen}} n := &fullNode{flags: nodeFlag{hash: hash, gen: cachegen}}
for i := 0; i < 16; i++ { for i := 0; i < 16; i++ {
cld, rest, err := decodeRef(elems, cachegen) cld, rest, err := decodeRef(elems, cachegen)

View file

@ -12,7 +12,11 @@ import (
"sync" "sync"
) )
const typeShift = 3 const typeShift = 4
// Verify at compile-time that typeShift is large enough to cover all FileType
// values by confirming that 0 == 0.
var _ [0]struct{} = [TypeAll >> typeShift]struct{}{}
type memStorageLock struct { type memStorageLock struct {
ms *memStorage ms *memStorage
@ -143,7 +147,7 @@ func (ms *memStorage) Remove(fd FileDesc) error {
} }
func (ms *memStorage) Rename(oldfd, newfd FileDesc) error { func (ms *memStorage) Rename(oldfd, newfd FileDesc) error {
if FileDescOk(oldfd) || FileDescOk(newfd) { if !FileDescOk(oldfd) || !FileDescOk(newfd) {
return ErrInvalidFile return ErrInvalidFile
} }
if oldfd == newfd { if oldfd == newfd {

View file

@ -20,7 +20,7 @@ func shorten(str string) string {
return str[:3] + ".." + str[len(str)-3:] return str[:3] + ".." + str[len(str)-3:]
} }
var bunits = [...]string{"", "Ki", "Mi", "Gi"} var bunits = [...]string{"", "Ki", "Mi", "Gi", "Ti"}
func shortenb(bytes int) string { func shortenb(bytes int) string {
i := 0 i := 0

52
vendor/vendor.json vendored
View file

@ -418,76 +418,76 @@
"revisionTime": "2017-07-05T02:17:15Z" "revisionTime": "2017-07-05T02:17:15Z"
}, },
{ {
"checksumSHA1": "3QsnhPTXGytTbW3uDvQLgSo9s9M=", "checksumSHA1": "k13cCuMJO7+KhR8ZXx5oUqDKGQA=",
"path": "github.com/syndtr/goleveldb/leveldb", "path": "github.com/syndtr/goleveldb/leveldb",
"revision": "169b1b37be738edb2813dab48c97a549bcf99bb5", "revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
"revisionTime": "2018-03-07T11:33:52Z" "revisionTime": "2018-05-02T07:23:49Z"
}, },
{ {
"checksumSHA1": "EKIow7XkgNdWvR/982ffIZxKG8Y=", "checksumSHA1": "EKIow7XkgNdWvR/982ffIZxKG8Y=",
"path": "github.com/syndtr/goleveldb/leveldb/cache", "path": "github.com/syndtr/goleveldb/leveldb/cache",
"revision": "169b1b37be738edb2813dab48c97a549bcf99bb5", "revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
"revisionTime": "2018-03-07T11:33:52Z" "revisionTime": "2018-05-02T07:23:49Z"
}, },
{ {
"checksumSHA1": "5KPgnvCPlR0ysDAqo6jApzRQ3tw=", "checksumSHA1": "5KPgnvCPlR0ysDAqo6jApzRQ3tw=",
"path": "github.com/syndtr/goleveldb/leveldb/comparer", "path": "github.com/syndtr/goleveldb/leveldb/comparer",
"revision": "169b1b37be738edb2813dab48c97a549bcf99bb5", "revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
"revisionTime": "2018-03-07T11:33:52Z" "revisionTime": "2018-05-02T07:23:49Z"
}, },
{ {
"checksumSHA1": "1DRAxdlWzS4U0xKN/yQ/fdNN7f0=", "checksumSHA1": "1DRAxdlWzS4U0xKN/yQ/fdNN7f0=",
"path": "github.com/syndtr/goleveldb/leveldb/errors", "path": "github.com/syndtr/goleveldb/leveldb/errors",
"revision": "169b1b37be738edb2813dab48c97a549bcf99bb5", "revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
"revisionTime": "2018-03-07T11:33:52Z" "revisionTime": "2018-05-02T07:23:49Z"
}, },
{ {
"checksumSHA1": "eqKeD6DS7eNCtxVYZEHHRKkyZrw=", "checksumSHA1": "eqKeD6DS7eNCtxVYZEHHRKkyZrw=",
"path": "github.com/syndtr/goleveldb/leveldb/filter", "path": "github.com/syndtr/goleveldb/leveldb/filter",
"revision": "169b1b37be738edb2813dab48c97a549bcf99bb5", "revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
"revisionTime": "2018-03-07T11:33:52Z" "revisionTime": "2018-05-02T07:23:49Z"
}, },
{ {
"checksumSHA1": "weSsccMav4BCerDpSLzh3mMxAYo=", "checksumSHA1": "weSsccMav4BCerDpSLzh3mMxAYo=",
"path": "github.com/syndtr/goleveldb/leveldb/iterator", "path": "github.com/syndtr/goleveldb/leveldb/iterator",
"revision": "169b1b37be738edb2813dab48c97a549bcf99bb5", "revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
"revisionTime": "2018-03-07T11:33:52Z" "revisionTime": "2018-05-02T07:23:49Z"
}, },
{ {
"checksumSHA1": "gJY7bRpELtO0PJpZXgPQ2BYFJ88=", "checksumSHA1": "gJY7bRpELtO0PJpZXgPQ2BYFJ88=",
"path": "github.com/syndtr/goleveldb/leveldb/journal", "path": "github.com/syndtr/goleveldb/leveldb/journal",
"revision": "169b1b37be738edb2813dab48c97a549bcf99bb5", "revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
"revisionTime": "2018-03-07T11:33:52Z" "revisionTime": "2018-05-02T07:23:49Z"
}, },
{ {
"checksumSHA1": "MtYY1b2234y/MlS+djL8tXVAcQs=", "checksumSHA1": "MtYY1b2234y/MlS+djL8tXVAcQs=",
"path": "github.com/syndtr/goleveldb/leveldb/memdb", "path": "github.com/syndtr/goleveldb/leveldb/memdb",
"revision": "169b1b37be738edb2813dab48c97a549bcf99bb5", "revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
"revisionTime": "2018-03-07T11:33:52Z" "revisionTime": "2018-05-02T07:23:49Z"
}, },
{ {
"checksumSHA1": "UmQeotV+m8/FduKEfLOhjdp18rs=", "checksumSHA1": "UmQeotV+m8/FduKEfLOhjdp18rs=",
"path": "github.com/syndtr/goleveldb/leveldb/opt", "path": "github.com/syndtr/goleveldb/leveldb/opt",
"revision": "169b1b37be738edb2813dab48c97a549bcf99bb5", "revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
"revisionTime": "2018-03-07T11:33:52Z" "revisionTime": "2018-05-02T07:23:49Z"
}, },
{ {
"checksumSHA1": "QCSae2ub87f8awH+PKMpd8ZYOtg=", "checksumSHA1": "7H3fa12T7WoMAeXq1+qG5O7LD0w=",
"path": "github.com/syndtr/goleveldb/leveldb/storage", "path": "github.com/syndtr/goleveldb/leveldb/storage",
"revision": "169b1b37be738edb2813dab48c97a549bcf99bb5", "revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
"revisionTime": "2018-03-07T11:33:52Z" "revisionTime": "2018-05-02T07:23:49Z"
}, },
{ {
"checksumSHA1": "gWFPMz8OQeul0t54RM66yMTX49g=", "checksumSHA1": "gWFPMz8OQeul0t54RM66yMTX49g=",
"path": "github.com/syndtr/goleveldb/leveldb/table", "path": "github.com/syndtr/goleveldb/leveldb/table",
"revision": "169b1b37be738edb2813dab48c97a549bcf99bb5", "revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
"revisionTime": "2018-03-07T11:33:52Z" "revisionTime": "2018-05-02T07:23:49Z"
}, },
{ {
"checksumSHA1": "V/Dh7NV0/fy/5jX1KaAjmGcNbzI=", "checksumSHA1": "V/Dh7NV0/fy/5jX1KaAjmGcNbzI=",
"path": "github.com/syndtr/goleveldb/leveldb/util", "path": "github.com/syndtr/goleveldb/leveldb/util",
"revision": "169b1b37be738edb2813dab48c97a549bcf99bb5", "revision": "ae970a0732be3a1f5311da86118d37b9f4bd2a5a",
"revisionTime": "2018-03-07T11:33:52Z" "revisionTime": "2018-05-02T07:23:49Z"
}, },
{ {
"checksumSHA1": "TT1rac6kpQp2vz24m5yDGUNQ/QQ=", "checksumSHA1": "TT1rac6kpQp2vz24m5yDGUNQ/QQ=",

View file

@ -67,7 +67,6 @@ func (sc *Client) SetMaxMessageSize(ctx context.Context, size uint32) error {
} }
// SetMinimumPoW (experimental) sets the minimal PoW required by this node. // SetMinimumPoW (experimental) sets the minimal PoW required by this node.
// This experimental function was introduced for the future dynamic adjustment of // This experimental function was introduced for the future dynamic adjustment of
// PoW requirement. If the node is overwhelmed with messages, it should raise the // PoW requirement. If the node is overwhelmed with messages, it should raise the
// PoW requirement and notify the peers. The new value should be set relative to // PoW requirement and notify the peers. The new value should be set relative to
@ -77,7 +76,7 @@ func (sc *Client) SetMinimumPoW(ctx context.Context, pow float64) error {
return sc.c.CallContext(ctx, &ignored, "shh_setMinPoW", pow) return sc.c.CallContext(ctx, &ignored, "shh_setMinPoW", pow)
} }
// Marks specific peer trusted, which will allow it to send historic (expired) messages. // MarkTrustedPeer marks specific peer trusted, which will allow it to send historic (expired) messages.
// Note This function is not adding new nodes, the node needs to exists as a peer. // Note This function is not adding new nodes, the node needs to exists as a peer.
func (sc *Client) MarkTrustedPeer(ctx context.Context, enode string) error { func (sc *Client) MarkTrustedPeer(ctx context.Context, enode string) error {
var ignored bool var ignored bool

View file

@ -89,7 +89,7 @@ func (api *PublicWhisperAPI) SetMaxMessageSize(ctx context.Context, size uint32)
return true, api.w.SetMaxMessageSize(size) return true, api.w.SetMaxMessageSize(size)
} }
// SetMinPow sets the minimum PoW for a message before it is accepted. // SetMinPoW sets the minimum PoW for a message before it is accepted.
func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) { func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) {
return true, api.w.SetMinimumPoW(pow) return true, api.w.SetMinimumPoW(pow)
} }
@ -142,7 +142,7 @@ func (api *PublicWhisperAPI) GetPublicKey(ctx context.Context, id string) (hexut
return crypto.FromECDSAPub(&key.PublicKey), nil return crypto.FromECDSAPub(&key.PublicKey), nil
} }
// GetPublicKey returns the private key associated with the given key. The key is the hex // GetPrivateKey returns the private key associated with the given key. The key is the hex
// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62. // encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62.
func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) { func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) {
key, err := api.w.GetPrivateKey(id) key, err := api.w.GetPrivateKey(id)

View file

@ -15,7 +15,7 @@
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
/* /*
Package whisper implements the Whisper protocol (version 5). Package whisperv5 implements the Whisper protocol (version 5).
Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP). Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP).
As such it may be likened and compared to both, not dissimilar to the As such it may be likened and compared to both, not dissimilar to the

View file

@ -33,7 +33,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
// Options specifies the exact way a message should be wrapped into an Envelope. // MessageParams specifies the exact way a message should be wrapped into an Envelope.
type MessageParams struct { type MessageParams struct {
TTL uint32 TTL uint32
Src *ecdsa.PrivateKey Src *ecdsa.PrivateKey
@ -86,7 +86,7 @@ func (msg *ReceivedMessage) isAsymmetricEncryption() bool {
return msg.Dst != nil return msg.Dst != nil
} }
// NewMessage creates and initializes a non-signed, non-encrypted Whisper message. // NewSentMessage creates and initializes a non-signed, non-encrypted Whisper message.
func NewSentMessage(params *MessageParams) (*sentMessage, error) { func NewSentMessage(params *MessageParams) (*sentMessage, error) {
msg := sentMessage{} msg := sentMessage{}
msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit) msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit)
@ -330,7 +330,7 @@ func (msg *ReceivedMessage) extractPadding(end int) (int, bool) {
return paddingSize, true return paddingSize, true
} }
// Recover retrieves the public key of the message signer. // SigToPubKey retrieves the public key of the message signer.
func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey { func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey {
defer func() { recover() }() // in case of invalid signature defer func() { recover() }() // in case of invalid signature

View file

@ -27,7 +27,7 @@ import (
set "gopkg.in/fatih/set.v0" set "gopkg.in/fatih/set.v0"
) )
// peer represents a whisper protocol peer connection. // Peer represents a whisper protocol peer connection.
type Peer struct { type Peer struct {
host *Whisper host *Whisper
peer *p2p.Peer peer *p2p.Peer
@ -53,51 +53,51 @@ func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer {
// start initiates the peer updater, periodically broadcasting the whisper packets // start initiates the peer updater, periodically broadcasting the whisper packets
// into the network. // into the network.
func (p *Peer) start() { func (peer *Peer) start() {
go p.update() go peer.update()
log.Trace("start", "peer", p.ID()) log.Trace("start", "peer", peer.ID())
} }
// stop terminates the peer updater, stopping message forwarding to it. // stop terminates the peer updater, stopping message forwarding to it.
func (p *Peer) stop() { func (peer *Peer) stop() {
close(p.quit) close(peer.quit)
log.Trace("stop", "peer", p.ID()) log.Trace("stop", "peer", peer.ID())
} }
// handshake sends the protocol initiation status message to the remote peer and // handshake sends the protocol initiation status message to the remote peer and
// verifies the remote status too. // verifies the remote status too.
func (p *Peer) handshake() error { func (peer *Peer) handshake() error {
// Send the handshake status message asynchronously // Send the handshake status message asynchronously
errc := make(chan error, 1) errc := make(chan error, 1)
go func() { go func() {
errc <- p2p.Send(p.ws, statusCode, ProtocolVersion) errc <- p2p.Send(peer.ws, statusCode, ProtocolVersion)
}() }()
// Fetch the remote status packet and verify protocol match // Fetch the remote status packet and verify protocol match
packet, err := p.ws.ReadMsg() packet, err := peer.ws.ReadMsg()
if err != nil { if err != nil {
return err return err
} }
if packet.Code != statusCode { if packet.Code != statusCode {
return fmt.Errorf("peer [%x] sent packet %x before status packet", p.ID(), packet.Code) return fmt.Errorf("peer [%x] sent packet %x before status packet", peer.ID(), packet.Code)
} }
s := rlp.NewStream(packet.Payload, uint64(packet.Size)) s := rlp.NewStream(packet.Payload, uint64(packet.Size))
peerVersion, err := s.Uint() peerVersion, err := s.Uint()
if err != nil { if err != nil {
return fmt.Errorf("peer [%x] sent bad status message: %v", p.ID(), err) return fmt.Errorf("peer [%x] sent bad status message: %v", peer.ID(), err)
} }
if peerVersion != ProtocolVersion { if peerVersion != ProtocolVersion {
return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", p.ID(), peerVersion, ProtocolVersion) return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", peer.ID(), peerVersion, ProtocolVersion)
} }
// Wait until out own status is consumed too // Wait until out own status is consumed too
if err := <-errc; err != nil { if err := <-errc; err != nil {
return fmt.Errorf("peer [%x] failed to send status packet: %v", p.ID(), err) return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err)
} }
return nil return nil
} }
// update executes periodic operations on the peer, including message transmission // update executes periodic operations on the peer, including message transmission
// and expiration. // and expiration.
func (p *Peer) update() { func (peer *Peer) update() {
// Start the tickers for the updates // Start the tickers for the updates
expire := time.NewTicker(expirationCycle) expire := time.NewTicker(expirationCycle)
transmit := time.NewTicker(transmissionCycle) transmit := time.NewTicker(transmissionCycle)
@ -106,15 +106,15 @@ func (p *Peer) update() {
for { for {
select { select {
case <-expire.C: case <-expire.C:
p.expire() peer.expire()
case <-transmit.C: case <-transmit.C:
if err := p.broadcast(); err != nil { if err := peer.broadcast(); err != nil {
log.Trace("broadcast failed", "reason", err, "peer", p.ID()) log.Trace("broadcast failed", "reason", err, "peer", peer.ID())
return return
} }
case <-p.quit: case <-peer.quit:
return return
} }
} }
@ -148,16 +148,16 @@ func (peer *Peer) expire() {
// broadcast iterates over the collection of envelopes and transmits yet unknown // broadcast iterates over the collection of envelopes and transmits yet unknown
// ones over the network. // ones over the network.
func (p *Peer) broadcast() error { func (peer *Peer) broadcast() error {
var cnt int var cnt int
envelopes := p.host.Envelopes() envelopes := peer.host.Envelopes()
for _, envelope := range envelopes { for _, envelope := range envelopes {
if !p.marked(envelope) { if !peer.marked(envelope) {
err := p2p.Send(p.ws, messagesCode, envelope) err := p2p.Send(peer.ws, messagesCode, envelope)
if err != nil { if err != nil {
return err return err
} else { } else {
p.mark(envelope) peer.mark(envelope)
cnt++ cnt++
} }
} }
@ -168,7 +168,7 @@ func (p *Peer) broadcast() error {
return nil return nil
} }
func (p *Peer) ID() []byte { func (peer *Peer) ID() []byte {
id := p.peer.ID() id := peer.peer.ID()
return id[:] return id[:]
} }

View file

@ -32,7 +32,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/nat"
) )
var keys []string = []string{ var keys = []string{
"d49dcf37238dc8a7aac57dc61b9fee68f0a97f062968978b9fafa7d1033d03a9", "d49dcf37238dc8a7aac57dc61b9fee68f0a97f062968978b9fafa7d1033d03a9",
"73fd6143c48e80ed3c56ea159fe7494a0b6b393a392227b422f4c3e8f1b54f98", "73fd6143c48e80ed3c56ea159fe7494a0b6b393a392227b422f4c3e8f1b54f98",
"119dd32adb1daa7a4c7bf77f847fb28730785aa92947edf42fdd997b54de40dc", "119dd32adb1daa7a4c7bf77f847fb28730785aa92947edf42fdd997b54de40dc",
@ -84,9 +84,9 @@ type TestNode struct {
var result TestData var result TestData
var nodes [NumNodes]*TestNode var nodes [NumNodes]*TestNode
var sharedKey []byte = []byte("some arbitrary data here") var sharedKey = []byte("some arbitrary data here")
var sharedTopic TopicType = TopicType{0xF, 0x1, 0x2, 0} var sharedTopic TopicType = TopicType{0xF, 0x1, 0x2, 0}
var expectedMessage []byte = []byte("per rectum ad astra") var expectedMessage = []byte("per rectum ad astra")
// This test does the following: // This test does the following:
// 1. creates a chain of whisper nodes, // 1. creates a chain of whisper nodes,

View file

@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
) )
// Topic represents a cryptographically secure, probabilistic partial // TopicType represents a cryptographically secure, probabilistic partial
// classifications of a message, determined as the first (left) 4 bytes of the // classifications of a message, determined as the first (left) 4 bytes of the
// SHA3 hash of some arbitrary data given by the original author of the message. // SHA3 hash of some arbitrary data given by the original author of the message.
type TopicType [TopicLength]byte type TopicType [TopicLength]byte

View file

@ -469,18 +469,18 @@ func (w *Whisper) Stop() error {
// HandlePeer is called by the underlying P2P layer when the whisper sub-protocol // HandlePeer is called by the underlying P2P layer when the whisper sub-protocol
// connection is negotiated. // connection is negotiated.
func (wh *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error { func (w *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
// Create the new peer and start tracking it // Create the new peer and start tracking it
whisperPeer := newPeer(wh, peer, rw) whisperPeer := newPeer(w, peer, rw)
wh.peerMu.Lock() w.peerMu.Lock()
wh.peers[whisperPeer] = struct{}{} w.peers[whisperPeer] = struct{}{}
wh.peerMu.Unlock() w.peerMu.Unlock()
defer func() { defer func() {
wh.peerMu.Lock() w.peerMu.Lock()
delete(wh.peers, whisperPeer) delete(w.peers, whisperPeer)
wh.peerMu.Unlock() w.peerMu.Unlock()
}() }()
// Run the peer handshake and state updates // Run the peer handshake and state updates
@ -490,11 +490,11 @@ func (wh *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
whisperPeer.start() whisperPeer.start()
defer whisperPeer.stop() defer whisperPeer.stop()
return wh.runMessageLoop(whisperPeer, rw) return w.runMessageLoop(whisperPeer, rw)
} }
// runMessageLoop reads and processes inbound messages directly to merge into client-global state. // runMessageLoop reads and processes inbound messages directly to merge into client-global state.
func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { func (w *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
for { for {
// fetch the next packet // fetch the next packet
packet, err := rw.ReadMsg() packet, err := rw.ReadMsg()
@ -502,7 +502,7 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
log.Warn("message loop", "peer", p.peer.ID(), "err", err) log.Warn("message loop", "peer", p.peer.ID(), "err", err)
return err return err
} }
if packet.Size > wh.MaxMessageSize() { if packet.Size > w.MaxMessageSize() {
log.Warn("oversized message received", "peer", p.peer.ID()) log.Warn("oversized message received", "peer", p.peer.ID())
return errors.New("oversized message received") return errors.New("oversized message received")
} }
@ -518,7 +518,7 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
log.Warn("failed to decode envelope, peer will be disconnected", "peer", p.peer.ID(), "err", err) log.Warn("failed to decode envelope, peer will be disconnected", "peer", p.peer.ID(), "err", err)
return errors.New("invalid envelope") return errors.New("invalid envelope")
} }
cached, err := wh.add(&envelope) cached, err := w.add(&envelope)
if err != nil { if err != nil {
log.Warn("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err) log.Warn("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)
return errors.New("invalid envelope") return errors.New("invalid envelope")
@ -537,17 +537,17 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
log.Warn("failed to decode direct message, peer will be disconnected", "peer", p.peer.ID(), "err", err) log.Warn("failed to decode direct message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
return errors.New("invalid direct message") return errors.New("invalid direct message")
} }
wh.postEvent(&envelope, true) w.postEvent(&envelope, true)
} }
case p2pRequestCode: case p2pRequestCode:
// Must be processed if mail server is implemented. Otherwise ignore. // Must be processed if mail server is implemented. Otherwise ignore.
if wh.mailServer != nil { if w.mailServer != nil {
var request Envelope var request Envelope
if err := packet.Decode(&request); err != nil { if err := packet.Decode(&request); err != nil {
log.Warn("failed to decode p2p request message, peer will be disconnected", "peer", p.peer.ID(), "err", err) log.Warn("failed to decode p2p request message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
return errors.New("invalid p2p request") return errors.New("invalid p2p request")
} }
wh.mailServer.DeliverMail(p, &request) w.mailServer.DeliverMail(p, &request)
} }
default: default:
// New message types might be implemented in the future versions of Whisper. // New message types might be implemented in the future versions of Whisper.
@ -561,29 +561,27 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
// add inserts a new envelope into the message pool to be distributed within the // add inserts a new envelope into the message pool to be distributed within the
// whisper network. It also inserts the envelope into the expiration pool at the // whisper network. It also inserts the envelope into the expiration pool at the
// appropriate time-stamp. In case of error, connection should be dropped. // appropriate time-stamp. In case of error, connection should be dropped.
func (wh *Whisper) add(envelope *Envelope) (bool, error) { func (w *Whisper) add(envelope *Envelope) (bool, error) {
now := uint32(time.Now().Unix()) now := uint32(time.Now().Unix())
sent := envelope.Expiry - envelope.TTL sent := envelope.Expiry - envelope.TTL
if sent > now { if sent > now {
if sent-SynchAllowance > now { if sent-SynchAllowance > now {
return false, fmt.Errorf("envelope created in the future [%x]", envelope.Hash()) return false, fmt.Errorf("envelope created in the future [%x]", envelope.Hash())
} else {
// recalculate PoW, adjusted for the time difference, plus one second for latency
envelope.calculatePoW(sent - now + 1)
} }
// recalculate PoW, adjusted for the time difference, plus one second for latency
envelope.calculatePoW(sent - now + 1)
} }
if envelope.Expiry < now { if envelope.Expiry < now {
if envelope.Expiry+SynchAllowance*2 < now { if envelope.Expiry+SynchAllowance*2 < now {
return false, fmt.Errorf("very old message") return false, fmt.Errorf("very old message")
} else {
log.Debug("expired envelope dropped", "hash", envelope.Hash().Hex())
return false, nil // drop envelope without error
} }
log.Debug("expired envelope dropped", "hash", envelope.Hash().Hex())
return false, nil // drop envelope without error
} }
if uint32(envelope.size()) > wh.MaxMessageSize() { if uint32(envelope.size()) > w.MaxMessageSize() {
return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash()) return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash())
} }
@ -598,36 +596,36 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
return false, fmt.Errorf("wrong size of AESNonce: %d bytes [env: %x]", aesNonceSize, envelope.Hash()) return false, fmt.Errorf("wrong size of AESNonce: %d bytes [env: %x]", aesNonceSize, envelope.Hash())
} }
if envelope.PoW() < wh.MinPow() { if envelope.PoW() < w.MinPow() {
log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex()) log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex())
return false, nil // drop envelope without error return false, nil // drop envelope without error
} }
hash := envelope.Hash() hash := envelope.Hash()
wh.poolMu.Lock() w.poolMu.Lock()
_, alreadyCached := wh.envelopes[hash] _, alreadyCached := w.envelopes[hash]
if !alreadyCached { if !alreadyCached {
wh.envelopes[hash] = envelope w.envelopes[hash] = envelope
if wh.expirations[envelope.Expiry] == nil { if w.expirations[envelope.Expiry] == nil {
wh.expirations[envelope.Expiry] = set.NewNonTS() w.expirations[envelope.Expiry] = set.NewNonTS()
} }
if !wh.expirations[envelope.Expiry].Has(hash) { if !w.expirations[envelope.Expiry].Has(hash) {
wh.expirations[envelope.Expiry].Add(hash) w.expirations[envelope.Expiry].Add(hash)
} }
} }
wh.poolMu.Unlock() w.poolMu.Unlock()
if alreadyCached { if alreadyCached {
log.Trace("whisper envelope already cached", "hash", envelope.Hash().Hex()) log.Trace("whisper envelope already cached", "hash", envelope.Hash().Hex())
} else { } else {
log.Trace("cached whisper envelope", "hash", envelope.Hash().Hex()) log.Trace("cached whisper envelope", "hash", envelope.Hash().Hex())
wh.statsMu.Lock() w.statsMu.Lock()
wh.stats.memoryUsed += envelope.size() w.stats.memoryUsed += envelope.size()
wh.statsMu.Unlock() w.statsMu.Unlock()
wh.postEvent(envelope, false) // notify the local node about the new message w.postEvent(envelope, false) // notify the local node about the new message
if wh.mailServer != nil { if w.mailServer != nil {
wh.mailServer.Archive(envelope) w.mailServer.Archive(envelope)
} }
} }
return true, nil return true, nil
@ -838,9 +836,8 @@ func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error
// because it's a once in a session experience // because it's a once in a session experience
derivedKey := pbkdf2.Key(key, nil, 65356, aesKeyLength, sha256.New) derivedKey := pbkdf2.Key(key, nil, 65356, aesKeyLength, sha256.New)
return derivedKey, nil return derivedKey, nil
} else {
return nil, unknownVersionError(version)
} }
return nil, unknownVersionError(version)
} }
// GenerateRandomID generates a random string, which is then returned to be used as a key id // GenerateRandomID generates a random string, which is then returned to be used as a key id