Compare commits

...

241 Commits

Author SHA1 Message Date
Péter Szilágyi
2423ae01e0 params: release Geth v1.8.4 2018-04-17 11:20:53 +03:00
Felföldi Zsolt
92c6d13083 light: new CHTs (#16515) 2018-04-17 09:33:31 +03:00
Martin Holst Swende
ec3db0f56c cmd/clef, signer: initial poc of the standalone signer (#16154)
* signer: introduce external signer command

* cmd/signer, rpc: Implement new signer. Add info about remote user to Context

* signer: refactored request/response, made use of urfave.cli

* cmd/signer: Use common flags

* cmd/signer: methods to validate calldata against abi

* cmd/signer: work on abi parser

* signer: add mutex around UI

* cmd/signer: add json 4byte directory, remove passwords from api

* cmd/signer: minor changes

* cmd/signer: Use ErrRequestDenied, enable lightkdf

* cmd/signer: implement tests

* cmd/signer: made possible for UI to modify tx parameters

* cmd/signer: refactors, removed channels in ui comms, added UI-api via stdin/out

* cmd/signer: Made lowercase json-definitions, added UI-signer test functionality

* cmd/signer: update documentation

* cmd/signer: fix bugs, improve abi detection, abi argument display

* cmd/signer: minor change in json format

* cmd/signer: rework json communication

* cmd/signer: implement mixcase addresses in API, fix json id bug

* cmd/signer: rename fromaccount, update pythonpoc with new json encoding format

* cmd/signer: make use of new abi interface

* signer: documentation

* signer/main: remove redundant  option

* signer: implement audit logging

* signer: create package 'signer', minor changes

* common: add 0x-prefix to mixcaseaddress in json marshalling + validation

* signer, rules, storage: implement rules + ephemeral storage for signer rules

* signer: implement OnApprovedTx, change signing response (API BREAKAGE)

* signer: refactoring + documentation

* signer/rules: implement dispatching to next handler

* signer: docs

* signer/rules: hide json-conversion from users, ensure context is cleaned

* signer: docs

* signer: implement validation rules, change signature of call_info

* signer: fix log flaw with string pointer

* signer: implement custom 4byte databsae that saves submitted signatures

* signer/storage: implement aes-gcm-backed credential storage

* accounts: implement json unmarshalling of url

* signer: fix listresponse, fix gas->uint64

* node: make http/ipc start methods public

* signer: add ipc capability+review concerns

* accounts: correct docstring

* signer: address review concerns

* rpc: go fmt -s

* signer: review concerns+ baptize Clef

* signer,node: move Start-functions to separate file

* signer: formatting
2018-04-16 15:04:32 +03:00
gary rong
de2a7bb764 eth/downloader: wait for all fetcher goroutines to exit before terminating (#16509) 2018-04-16 11:37:48 +03:00
gary rong
6b2b328cdb ethdb: add leveldb write delay statistic (#16499) 2018-04-16 11:18:24 +03:00
Ryan Schneider
2a1fc3d155 miner: remove contention on currentMu for pending data retrievals (#16497) 2018-04-16 10:56:20 +03:00
Péter Szilágyi
60516c83b0 Merge pull request #16494 from karalabe/txpool-stable-pricedelete
core: txpool stable underprice drop order, perf fixes
2018-04-12 13:19:06 +03:00
Péter Szilágyi
db48d312e4 core: txpool stable underprice drop order, perf fixes 2018-04-12 12:54:22 +03:00
Péter Szilágyi
7e911b8e47 Merge pull request #16491 from holiman/fix_copy_again
core/state: fix ripemd-cornercase in Copy
2018-04-12 10:54:29 +03:00
Martin Holst Swende
7205366c9f core/state: fix ripemd-cornercase in Copy 2018-04-11 15:03:49 +02:00
Péter Szilágyi
5a79aca8b9 Merge pull request #16485 from holiman/fixcopycopy
core/state: fix bug in copy of copy State
2018-04-11 12:00:17 +03:00
Martin Holst Swende
0c7b99b8cc core/state: fix bug in copy of copy State 2018-04-11 10:23:01 +02:00
cpusoft
e7cc5b4160 les: add ps.lock.Unlock() before return (#16360) 2018-04-11 11:02:33 +03:00
Péter Szilágyi
34ecb495b6 Merge pull request #16481 from karalabe/go1.10.1
travis, appveyor: bump to Go 1.10.1
2018-04-11 10:56:32 +03:00
Elad_
2e247705cd travis.yml: add TEST_PACKAGES to speed up swarm testing (#16456)
This commit is meant to allow ecosystem projects such as ethersphere
to minimize CI build times by specifying an environment variable with
the packages to run tests on.

If the environment variable isn't defined the build script will test
all packages so this shouldn't affect the main go-ethereum repository.
2018-04-10 16:35:26 +02:00
Péter Szilágyi
95d5c22086 travis, appveyor: bump to Go 1.10.1 2018-04-10 17:07:58 +03:00
Felix Lange
3caf16b15f core: remove stray account creations in state transition (#16470)
The 'from' and 'to' methods on StateTransitions are reader methods and
shouldn't have inadvertent side effects on state.

It is safe to remove the check in 'from' because account existence is
implicitly checked by the nonce and balance checks. If the account has
non-zero balance or nonce, it must exist. Even if the sender account has
nonce zero at the start of the state transition or no balance, the nonce
is incremented before execution and the account will be created at that
time.

It is safe to remove the check in 'to' because the EVM creates the
account if necessary.

Fixes #15119
2018-04-10 15:33:25 +02:00
ligi
30deb6067f build: add -e and -X flags to get more information on #16433 (#16443) 2018-04-10 14:32:48 +03:00
Péter Szilágyi
989ab26028 Merge pull request #16478 from karalabe/fix-alltools-dockerfile
Dockerfile.alltools: fix invalid command
2018-04-10 14:13:52 +03:00
Felix Lange
c7ab3e5544 common: delete StringToAddress, StringToHash (#16436)
* common: delete StringToAddress, StringToHash

These functions are confusing because they don't parse hex, but use the
bytes of the string. This change removes them, replacing all uses of
StringToAddress(s) by BytesToAddress([]byte(s)).

* eth/filters: remove incorrect use of common.BytesToAddress
2018-04-10 14:12:07 +03:00
Péter Szilágyi
29213b1f8f Dockerfile.alltools: fix invalid command 2018-04-10 14:02:56 +03:00
Péter Szilágyi
149f706fde Merge pull request #16477 from karalabe/puppeth-dockerfile-permission-fix
cmd/puppeth: fix node deploys for updated dockerfile user
2018-04-10 13:40:52 +03:00
Péter Szilágyi
7c1e9a5004 cmd/puppeth: fix node deploys for updated dockerfile user 2018-04-10 13:13:05 +03:00
Péter Szilágyi
39f4c80155 Merge pull request #15225 from holiman/test_removefrom_dirtyset
Change handling of dirty objects in state
2018-04-10 12:28:30 +03:00
Martin Holst Swende
8c31d2897b core: add blockchain benchmarks 2018-04-10 11:20:06 +02:00
Martin Holst Swende
14c9215dd3 state: handle nil in journal dirties 2018-04-10 11:20:02 +02:00
gary rong
1100e8ba63 eth/downloader: flush state sync data before exit (#16280) 2018-04-09 14:46:27 +02:00
Felix Lange
0fac705ed0 compression/rle: delete RLE compression (#16468) 2018-04-09 13:47:06 +02:00
Ivo Georgiev
315b9b18df ethclient: remove empty object in newHeads subscription call (#16454) 2018-04-09 12:40:58 +02:00
DoubleWoodH
8de655ef3a bmt: fix comment typos (#16461) 2018-04-09 12:38:01 +02:00
dm4
3ebcf92b42 cmd/evm: print vm output when debug flag is on (#16326) 2018-04-06 12:43:36 +02:00
Zhenguo Niu
c43792a42c cmd/geth: update template for 'geth bug' command (#16350) 2018-04-06 12:42:11 +02:00
Federico Gimenez
50dbe8e244 Dockerfile: use non-privileged user account (#16052) 2018-04-05 14:14:32 +02:00
Steven Roose
ec8ee611ca core/types: remove String methods from struct types (#16205)
Most of these methods did not contain all the relevant information
inside the object and were not using a similar formatting type.
Moreover, the existence of a suboptimal String method breaks usage
with more advanced data dumping tools like go-spew.
2018-04-05 14:13:02 +02:00
Giovanni HoSang
1e248f3a6e README: change 'built in' to 'built-in' 2018-04-04 15:25:34 +02:00
Ricardo Domingos
6ab9f0a19f accounts/abi: improve test coverage (#16044) 2018-04-04 13:42:36 +02:00
Yusup
7aad81f881 eth: fix typos (#16414) 2018-04-04 12:25:02 +02:00
Nguyen Sy Thanh Son
2a4bd55b43 cmd/geth: remove relOracle variable (#16434) 2018-04-04 12:22:26 +02:00
Jia Chenhui
5909482fb5 core/state: avoid redundant addition to code size cache (#16427) 2018-04-03 17:13:19 +02:00
David Huie
d1af4e1a9e crypto/secp256k1: catch curve parameter parse errors (#16392) 2018-04-03 17:12:00 +02:00
Li Xuanji
6cdfb9a3eb .gitattributes: enable solidity highlighting on github (#16425) 2018-04-03 15:21:24 +02:00
Felix Lange
a095b84ec5 travis.yml: remove sudo requirement for PPA and Azure purge builders (#16404)
This is supposed to fix the FTP upload issue according to
travis-ci/travis-ci#9391.
2018-03-28 15:35:41 +03:00
Péter Szilágyi
d985b9052a core/state: avoid linear overhead on journal dirty listing 2018-03-28 09:32:02 +03:00
Martin Holst Swende
958ed4f3d9 core/state: rework dirty handling to avoid quadratic overhead 2018-03-28 09:29:28 +03:00
Jia Chenhui
1a8894b3d5 core/state: uniform parameter style (#16398)
- Uniform code style.
2018-03-28 09:26:37 +03:00
Guillaume Ballet
80449719bd whisper: fix issue in topic list copy (#16381)
- Fixes #16271. What was appeneded was a pointer to
an object that changes during the iteration.
- The topic is allocated as a 4-byte array, fill partial topics
with 0s. Partial topics are currently disabled, but would
crash as they rely on the presence of byte number 3.
2018-03-27 17:26:08 +02:00
Felföldi Zsolt
45bd4fedde light: new CHT for ropsten (#16393) 2018-03-27 10:09:15 +03:00
Péter Szilágyi
d763e20d55 Merge pull request #16394 from hydai/fix_typo
core/vm: Fixed typos in core/vm/interpreter.go
2018-03-27 10:08:58 +03:00
hydai
6134990709 core/vm: Fixed typos in core/vm/interpreter.go 2018-03-27 12:29:04 +08:00
Felix Lange
85ea9159d0 params, VERSION: v1.8.4 unstable 2018-03-26 18:09:56 +02:00
Felix Lange
329ac18ef6 params: v1.8.3 stable 2018-03-26 18:09:06 +02:00
Felix Lange
89cc604a50 light: new mainnet CHT (#16390) 2018-03-26 18:02:12 +03:00
Guillaume Ballet
cf799e5eaa whisper: switch all remaining components from v5 to v6 2018-03-26 16:36:14 +02:00
Péter Szilágyi
c053f1146d Merge pull request #16388 from hydai/fix_comments
core/vm: Fixed typos
2018-03-26 17:05:35 +03:00
hydai
c3dc814fea core/vm: Fixed typo in core/vm/evm.go 2018-03-26 21:40:00 +08:00
Zhenguo Niu
db9b2f5405 cmd/puppeth: add constraints to network name (#16336)
* cmd/puppeth: add constraints to network name

* cmd/puppeth: update usage of network arg

* cmd/puppeth: avoid package dependency on utils
2018-03-26 15:21:20 +03:00
Felix Lange
e9b5e22ad1 rpc: limit chunked requests (#16343) 2018-03-26 14:46:37 +03:00
Jia Chenhui
e506d384e9 core/state: fix typo (#16370) 2018-03-26 14:45:34 +03:00
Péter Szilágyi
dd708c1636 Merge pull request #16319 from rjl493456442/dump_preimages
cmd: implement preimage dump and import cmds
2018-03-26 14:45:01 +03:00
Péter Szilágyi
495bdb0c71 cmd: export preimages in RLP, support GZIP, uniform with block export 2018-03-26 14:08:01 +03:00
hydai
7c131f4d6d core/asm: fixed typo (posititon -> position) (#16366) 2018-03-26 13:48:39 +03:00
hydai
84c5db5409 core/vm: remove JIT VM codes (#16362) 2018-03-26 13:48:04 +03:00
David Huie
23ac783332 ecies: drop randomness parameter from PrivateKey.Decrypt (#16374)
The parameter `rand` is unused in `PrivateKey.Decrypt`. Decryption in
the ECIES encryption scheme is deterministic, so randomness isn't
needed.
2018-03-26 13:46:18 +03:00
Péter Szilágyi
e9a1d8de34 Merge pull request #16387 from karalabe/evm-polsihes
core: minor evm polishes and optimizations
2018-03-26 13:44:36 +03:00
rjl493456442
b6b6f52ec8 cmd: implement preimage dump and import cmds 2018-03-26 12:51:46 +03:00
Péter Szilágyi
1fae50a199 core: minor evm polishes and optimizations 2018-03-26 12:28:46 +03:00
Guillaume Ballet
3d013c1939 whisper: some components are still using v5, switch to v6 2018-03-22 15:48:52 +01:00
Martin Holst Swende
933972d139 Merge pull request #16256 from epiclabs-io/unpack_one_arg_event
Fix issue unmarshaling single parameter events from abigen generated go code #16208
2018-03-21 10:11:37 +01:00
Felix Lange
b1917ac9a3 build: add GOBIN to PATH for gomobile (#16344)
* build: add GOBIN to PATH for gomobile

* build: install gobind alongside gomobile
2018-03-20 02:00:45 +09:00
Péter Szilágyi
1203c6a237 crypto/bn256: full switchover to cloudflare's code (#16301)
* crypto/bn256: full switchover to cloudflare's code

* crypto/bn256: only use cloudflare for optimized architectures

* crypto/bn256: upstream fallback for non-optimized code

* .travis, build: drop support for Go 1.8 (need type aliases)

* crypto/bn256/cloudflare: enable curve mul lattice optimization
2018-03-20 01:13:54 +09:00
Guillaume Ballet
0965761a45 whisper: Notify Vlad and Guillaume of whisper PRs (#16340) 2018-03-19 22:27:46 +09:00
Martin Holst Swende
faed47b3c5 Merge pull request #15990 from markya0616/sim_backend_block_hash
accounts/abi, core: add AddTxWithChain in BlockGen for simulation
2018-03-19 08:29:54 +01:00
stompesi
fe6cf00f48 miner: remove duplicated code (#15968) 2018-03-16 14:13:52 +02:00
Péter Szilágyi
322006d0f2 Merge pull request #16315 from karalabe/drop-vagrant
containers: drop vagrant support, noone's maintaining it
2018-03-15 18:59:50 +02:00
Péter Szilágyi
56e2376e69 containers: drop vagrant support, noone's maintaining it 2018-03-14 13:23:40 +02:00
hydai
a063876749 core/asm: fixed typo (labal -> label) (#16313) 2018-03-14 11:59:06 +02:00
Péter Szilágyi
62bc179bb9 Merge pull request #16310 from karalabe/websocket-request-limits
rpc: enforce the 128KB request limits on websockets too
2018-03-13 14:35:18 +02:00
Péter Szilágyi
555f42cfd8 rpc: enforce the 128KB request limits on websockets too 2018-03-13 13:55:26 +02:00
Anton Evangelatov
6a2d2869f6 github: more information bot configuration (#16298) 2018-03-12 10:56:59 +02:00
Felföldi Zsolt
1488fdaf19 cmd/utils: fix maxpeers vs lightpeers logic (#16125) 2018-03-09 11:55:03 +02:00
gary rong
77da203547 eth: update higest block we know during the sync if a higher was found (#16283)
* eth: update higest block we know during the sync if a higher was found

* eth: avoid useless sync in fast sync
2018-03-09 11:51:30 +02:00
Péter Szilágyi
307846d046 Merge pull request #16287 from razum2um/master
Allow any vhost for wallet deployed by puppeth
2018-03-09 11:06:18 +02:00
Péter Szilágyi
38e2071df8 Merge pull request #16289 from jeffwalsh/remove-add-std-arg
common/compiler: remove "--add-std" arg, deprecated in solidity 0.4.21
2018-03-09 11:05:09 +02:00
Jeffery Robert Walsh
a25561dfb4 common/compiler: remove "--add-std" arg, deprecated in solidity 0.4.21 2018-03-08 18:05:56 -08:00
Vlad Bokov
52697fb1b2 cmd/puppeth: allow any vhost in wallet 2018-03-09 00:02:31 +07:00
Péter Szilágyi
b2f53f9621 Merge pull request #16128 from karalabe/go1.10
travis, Dockerfile, appveyor, build: bump to Go 1.10
2018-03-08 17:26:14 +02:00
Péter Szilágyi
669aba8e2c travis, Dockerfile, appveyor, build: bump to Go 1.10 2018-03-08 16:34:26 +02:00
Kurkó Mihály
39c16c8a1e cmd, ethdb, vendor: integrate leveldb iostats (#16277)
* cmd, dashboard, ethdb, vendor: send iostats to dashboard

* ethdb: change names

* ethdb: handle parsing errors

* ethdb: handle iostats syntax error

* ethdb: r -> w
2018-03-08 14:59:00 +02:00
Martin Holst Swende
4871e25f5f core/vm: optimize eq, slt, sgt and iszero + tests (#16047)
* vm: optimize eq, slt, sgt and iszero + tests

* core/vm: fix error in slt/sgt, found by vmtests. Added testcase

* core/vm: make slt/sgt cleaner
2018-03-08 14:48:19 +02:00
Péter Szilágyi
85d5f2c661 Merge pull request #16285 from karalabe/fix-resend-optional-param
internal/ethapi: make resent gas params optional
2018-03-08 12:52:38 +02:00
Péter Szilágyi
28ef23f446 internal/ethapi: make resent gas params optional 2018-03-08 12:29:42 +02:00
Kurkó Mihály
704840a8ad cmd, dashboard: use webpack dev server, remove custom assets (#16263)
* cmd, dashboard: remove custom assets, webpack dev server

* dashboard: yarn commands, small fixes
2018-03-08 10:22:21 +02:00
Péter Szilágyi
3ec1b9a92d Merge pull request #16275 from karalabe/bump-duktape
vendor: bump duktape to get rid of build warning
2018-03-07 16:29:31 +02:00
Péter Szilágyi
fc1f3f2618 vendor: bump duktape to get rid of build warning 2018-03-07 14:47:09 +02:00
Mark
cddb529d70 accounts/abi: normalize method name to a camel-case string (#15976) 2018-03-07 14:34:53 +02:00
Kyuntae Ethan Kim
63687f04e4 core: check transaction/receipt count match when reconstructing blocks (#16272) 2018-03-07 12:05:14 +02:00
Péter Szilágyi
d43ffdbf6a Merge pull request #16240 from cuiweixie/txpool
core: should enqueue the invalids tx anyway
2018-03-07 11:45:28 +02:00
Kyuntae Ethan Kim
f6bef558aa eth: fixed typo (#16274) 2018-03-07 11:15:54 +02:00
Péter Szilágyi
2b5d1a4a4c core: update txpool tests for the removal fix 2018-03-07 10:58:11 +02:00
cui
f8601430fd core: should enqueue the invalids tx anyway
even the pending is empty we shoud enqueue the invalid txs
2018-03-07 10:07:50 +02:00
gluk256
f1d440a437 whisper: final refactoring (#16259)
whisper: final refactoring
2018-03-06 23:37:43 +01:00
Anton Evangelatov
746392cfd2 swarm/storage: disable treechunker test while it is flaky (#16254) 2018-03-05 17:23:33 +01:00
Javier Peletier
60a999f238 accounts/abi: Modified unpackAtomic to accept struct lvalues 2018-03-05 16:01:40 +01:00
Javier Peletier
13b566e06e accounts/abi: Add one-parameter event test case from enriquefynn/unpack_one_arg_event 2018-03-05 16:00:03 +01:00
Péter Szilágyi
1548518644 VERSION, params: begin 1.8.3 release cycle 2018-03-05 15:52:21 +02:00
Péter Szilágyi
b8b9f7f447 params: release Geth 1.8.2 stable 2018-03-05 15:48:48 +02:00
Anton Evangelatov
c636ac4045 github: config for probot-stale bot (#16235)
* github: config for probot-stale bot

* github: use stale label, instead of wontfix
2018-03-05 15:23:26 +02:00
Péter Szilágyi
bd6879ac51 core/vm, crypto/bn256: switch over to cloudflare library (#16203)
* core/vm, crypto/bn256: switch over to cloudflare library

* crypto/bn256: unmarshal constraint + start pure go impl

* crypto/bn256: combo cloudflare and google lib

* travis: drop 386 test job
2018-03-05 14:33:45 +02:00
Péter Szilágyi
223fe3f26e Merge pull request #16229 from karalabe/evm-call-fix
cmd/evm, core/vm, internal/ethapi: don't disable call gas metering
2018-03-05 14:23:48 +02:00
Péter Szilágyi
b7e57ca1d0 cmd/evm, core/vm, internal/ethapi: don't disable call gas metering 2018-03-05 14:01:13 +02:00
Martin Holst Swende
478143d69a utils: fix #16138 by checking if vhosts flag is set (#16141)
* utils: fix #16138 by checking if vhosts flag is set

* utils,node: fix defaults for rpcvhosts

* node,utils: address review concerns
2018-03-05 13:02:32 +02:00
Guillaume Ballet
abed63c38f Merge pull request #16250 from gluk256/317-fatalf
whisper: refactoring go-routines workflow

Move the call mailServer.Init() down (to the bottom of the function) because if the function initialize() completes successfully, then it will be followed by mailServer.Close() in shutdown(). The workflow of the corresponding goroutines is clearer now.
2018-03-05 10:49:25 +01:00
Kyuntae Ethan Kim
d429a92f09 consensus/ethash: fixed typo (#16253) 2018-03-05 11:32:56 +02:00
Vlad
61a061c9b4 whisper: refactoring go-routines 2018-03-04 23:35:05 +01:00
protolambda
0b814d32f8 accounts/abi: Abi binding support for nested arrays, fixes #15648, including nested array unpack fix (#15676)
* accounts/abi/bind: support for multi-dim arrays

Also:
- reduce usage of regexes a bit.
- fix minor Java syntax problems

Fixes #15648

* accounts/abi/bind: Add some more documentation

* accounts/abi/bind: Improve code readability

* accounts/abi: bugfix for unpacking nested arrays

The code previously assumed the arrays/slices were always 1 level
deep. While the packing supports nested arrays (!!!).

The current code for unpacking doesn't return the "consumed" length, so
this fix had to work around that by calculating it (i.e. packing and
 getting resulting length) after the unpacking of the array element.
It's far from ideal, but unpacking behaviour is fixed now.

* accounts/abi: Fix unpacking of nested arrays

Removed the temporary workaround of packing to calculate size, which was
incorrect for slice-like types anyway.
Full size of nested arrays is used now.

* accounts/abi: deeply nested array unpack test

Test unpacking of an array nested more than one level.

* accounts/abi: Add deeply nested array pack test

Same as the deep nested array unpack test, but the other way around.

* accounts/abi/bind: deeply nested arrays bind test

Test the usage of bindings that were generated
for methods with multi-dimensional (and not
just a single extra dimension, like foo[2][3])
array arguments and returns.

edit: trigger rebuild, CI failed to fetch linter module.

* accounts/abi/bind: improve array binding

wrapArray uses a regex now, and arrayBindingJava is improved.

* accounts/abi: Improve naming of element size func

The full step size for unpacking an array
 is now retrieved with "getFullElemSize".

* accounts/abi: support nested nested array args

Previously, the code only considered the outer-size of the array,
ignoring the size of the contents. This was fine for most types,
but nested arrays are packed directly into it, and count towards
the total size. This resulted in arguments following a nested
array to replicate some of the binary contents of the array.

The fix: for arrays, calculate their complete contents size:
 count the arg.Type.Elem.Size when Elem is an Array, and
 repeat when their child is an array too, etc.
The count is the number of 32 byte elements, similar to how it
 previously counted, but nested.

* accounts/abi: Test deep nested arr multi-arguments

Arguments with a deeply nested array should not cause the next arguments
to be read from the wrong position.
2018-03-04 23:24:17 +01:00
Guillaume Ballet
7b1d637098 Merge pull request #16245 from gluk256/311-close-channel
whisper: close the `done` channel in one location
2018-03-04 23:22:26 +01:00
Vlad
95cca85d6d whisper: minor refactoring 2018-03-03 21:37:16 +01:00
gluk256
66cd41af1e Merge pull request #16231 from gluk256/303-reader
whisper: filereader mode introduced to wnode
2018-03-03 09:40:01 +01:00
gluk256
fa375955ad whisper/whisperv6: delete unused function (#16234) 2018-03-03 00:54:15 +01:00
Felföldi Zsolt
5ad7b9123c light: new CHTs (#16233) 2018-03-03 00:52:54 +01:00
Péter Szilágyi
ca64a122d3 eth/downloader: save and load trie sync progress (#16224) 2018-03-03 00:52:39 +01:00
Felix Lange
12f4d28411 internal/debug: add support for mutex profiles (#16230) 2018-03-03 00:52:21 +01:00
Vlad
6219a33822 whisper: filereader mode introduced to wnode 2018-03-02 14:54:54 +01:00
Péter Szilágyi
49bcb5fbd5 Merge pull request #16228 from karalabe/faucet-background-skip
cmd/faucet: update state in background, skip when busy
2018-03-02 12:13:19 +02:00
Péter Szilágyi
6f13e515f4 cmd/faucet: update state in background, skip when busy 2018-03-02 12:02:23 +02:00
Zhenguo Niu
d520bf4503 cmd/swarm: fix some typos in manifest cmd (#16227)
Replace "atleast" with "at least" in the manifest error message.
2018-03-02 10:59:26 +01:00
Guillaume Ballet
a76e46e3d7 Merge pull request #16223 from gluk256/300-msg-serialiation
whisper: topics replaced by bloom filters in mailserver communication
2018-03-01 20:27:20 +01:00
Anton Evangelatov
3ca3fffdf0 metrics: fix flaky Example metrics test (#16222)
* metrics: add sleep to test in order to get predictable output

* metrics: relax constraints on timer test
2018-03-01 19:55:31 +02:00
Vlad
ee75a90ab4 whisper: topics replaced by bloom filters 2018-03-01 16:04:09 +01:00
gluk256
5a150e1b77 whisper: serious security issue fixed (#16219)
The diagnostic tool was saving the unencrypted version of the messages, which is an obvious
security flaw. As of this commit:
  * encrypted messages saved instead of plain text.
  * all messages are stored, even that created by the user of wnode.
2018-03-01 09:34:46 +01:00
Guillaume Ballet
9b4e182ce5 Merge pull request #16210 from gluk256/288-filter-optimization
whisper: message filtering optimization

Only run the message through filters who registered their interest.
2018-02-28 17:28:09 +01:00
Vlad
d24d10a764 whisper: style fixes 2018-02-28 15:05:35 +01:00
Guillaume Ballet
52bb0a1ec7 Merge pull request #16214 from b00ris/whisperv6_datarace
whisper: fixed dataraces in peer unit tests
2018-02-28 14:31:19 +01:00
Guillaume Ballet
1843615456 Merge pull request #16206 from gluk256/277-mailserver
whisper: mailserver no longer supports the signature validation

Mailserver is provided as an example, but client validation belongs to the upper layer protocol and
needs not be covered in this example. The check that was previously available hinders the switch
to libp2p so we agreed not to include that check in that example code anymore.
2018-02-28 14:10:08 +01:00
Péter Szilágyi
7843192c8e Merge pull request #16217 from karalabe/rpc-receipt-fetch-fix
internal/ethapi: fix getTransactionReceipt
2018-02-28 13:40:17 +02:00
b00ris
62c239f608 whisper: fix typo 2018-02-28 14:38:42 +03:00
Péter Szilágyi
8f43c97433 Merge pull request #16207 from karalabe/drop-go1.7
travis, build, consensus: drop support for Go 1.7
2018-02-28 13:29:58 +02:00
Péter Szilágyi
ba7b384019 internal/ethapi: fix getTransactionReceipt 2018-02-28 12:40:15 +02:00
Mark Rushakoff
98ec5e5011 core/asm: rename isAlphaNumeric to isLetter (#16212)
The function would return false for numbers, so isLetter is a more
accurate description of the behavior.
2018-02-28 12:20:07 +02:00
b00ris
cf52d5c91f whisper: fixed datarace 2018-02-28 09:50:36 +03:00
Vlad
a69cb3b4ff whisper: comment updated 2018-02-28 00:39:38 +01:00
Vlad
c733792be4 whsiper: refactoring 2018-02-27 23:38:20 +01:00
Vlad
014d8d9837 whisper: message filtering optimized 2018-02-27 21:16:15 +01:00
Péter Szilágyi
17b0e226d3 travis, build, consensus: drop support for Go 1.7 2018-02-27 18:25:56 +02:00
Vlad
5e30a5f66e whisper: test fixed 2018-02-27 15:52:10 +01:00
Vlad
dadf4d53ab whisper: mailserver no longer supports the signature vaidation 2018-02-27 15:45:00 +01:00
Elad Nachmias
b574b57766 swarm: give correct error on 0x hash prefix (#16195)
- added a case error struct that contains information about certain error cases
in which we would like to output more information to the client
- added a validation method that iterates and adds the information that is
stored in the error cases
2018-02-27 15:32:38 +02:00
Anton Evangelatov
18bb3da55e node: fill StandardCounters as part of debugapi/metrics (#16054) 2018-02-27 15:30:07 +02:00
Elad Nachmias
dd389e595f eth: added travis build badge (#16117)
* eth: added travis build status for master branch

* README: fix travis badge order, link to CI
2018-02-27 13:04:47 +02:00
Saulius Grigaitis
c41f1a3e23 puppeth: fix Parity Chain Spec parameter GasLimitBoundDivision (#16188) 2018-02-27 12:56:51 +02:00
Andrey Petrov
2e9c8fd4fb eth, les: allow exceeding maxPeers for trusted peers (#16189)
Fixes #3326, #14472
2018-02-27 12:52:59 +02:00
Guillaume Ballet
4c845bdc27 Merge pull request #16198 from gluk256/266-wnode
whisper: refactor wnode to systematically store messages if a directory is provided
2018-02-26 21:23:51 +01:00
Vlad
f4e676cccd whipser: comments updated 2018-02-26 19:26:36 +01:00
JU HYEONG PARK
61c9730b2d p2p: fix doEncHandshake documentation (#16184) 2018-02-26 17:22:46 +01:00
Vlad
6e0667fa06 whisper: wnode updated - all messages are saved if savedir param is given 2018-02-26 13:58:04 +01:00
Martin Holst Swende
f83237573f core: make current*Block atomic, and accessor functions mutex-free (#16171)
* core: make current*Block atomic, and accessor functions mutex-free

* core: fix review concerns

* core: fix error in atomic assignment

* core/light: implement atomic getter/setter for headerchain
2018-02-26 11:53:10 +02:00
Domino Valdano
d398d04e27 cmd/geth: fix broken links to JavaScript-Console wiki in cmd line help (#16183)
* Fixed broken link to JavaScript-Console wiki in cmd line help

* cmd/geth: Added missing r in 'JavaScript'
2018-02-26 11:38:17 +02:00
Anton Evangelatov
764878d988 contracts/chequebook: increase interval between auto deposits (#16178) 2018-02-26 11:36:26 +02:00
cooganb
22fc6928d7 swarm: creates Swarm landing page for browser 'localhost:xxxx/' GET request when running Swarm (#15926)
* swarm: began work on GetHandleFile method re: issue #155

* swarm: now able to serve landing page template

* swarm: added landing page template

* swarm: landing page has working input

* swarm: fixed CSS issue in template

* swarm: deleted extra lines

* swarm: deleted time header and made redirect a relative path

* swarm: removed code mistakenly left
2018-02-26 09:56:40 +01:00
Guillaume Ballet
423c8bb1d8 Merge pull request #16176 from gluk256/255-refactoring
whisper: filters no longer get removed after a while
2018-02-23 18:02:51 +01:00
Anton Evangelatov
114738982e swarm/metrics: introduce metrics export flag (#16177) 2018-02-23 16:22:16 +01:00
Vlad
6919c36432 whisper: refactoring 2018-02-23 14:52:25 +01:00
Anton Evangelatov
dcca613a0b swarm: initial instrumentation (#15969)
* swarm: initial instrumentation with go-metrics

* swarm: initialise metrics collection and add ResettingTimer to HTTP requests

* swarm: update metrics flags names. remove redundant Timer.

* swarm: rename method for periodically updating gauges

* swarm: finalise metrics after feedback

* swarm/network: always init kad metrics containers

* swarm/network: off-by-one index in metrics containers

* swarm, metrics: resolved conflicts
2018-02-23 14:19:59 +01:00
Lewis Marshall
b677a07d36 swarm/api/http: Fix using deprecated bzzr scheme (#16152)
Without this, deprecated bzzr requests just return an empty response.

Signed-off-by: Lewis Marshall <lewis@lmars.net>
2018-02-23 14:09:01 +01:00
gluk256
4702ace5f7 Merge pull request #16172 from gluk256/244-light-client
whisper: light client mode introduced
2018-02-23 14:07:29 +01:00
Péter Szilágyi
89f914c030 core: flush out trie cache more meaningfully on stop (#16143)
* core: flush out trie cache more meaningfully on stop

* core: upgrade legacy tests to chain maker
2018-02-23 14:02:33 +02:00
Guillaume Ballet
fb5d085234 Merge pull request #16146 from status-im/pombeirp/whisperv6-peer-race-cond-fix
Fix race condition in whisperv6/peer.go
2018-02-23 11:49:47 +01:00
Martin Holst Swende
44d40ffce1 core, vm, common: define constantinople fork + shift (#16045)
* core, vm, common: define constantinople fork, start implementation of shift instructions

* vm: more testcases

* vm: add tests for intpool erroneous intpool handling

* core, vm, common: fix constantinople review concerns

* vm: add string<->op definitions for new opcodes
2018-02-23 12:32:57 +02:00
Vlad
d7b4b40cb6 whisper: light client mode introduced 2018-02-23 11:10:28 +01:00
Anton Evangelatov
ae9f97221a metrics: pull library and introduce ResettingTimer and InfluxDB reporter (#15910)
* go-metrics: fork library and introduce ResettingTimer and InfluxDB reporter.

* vendor: change nonsense/go-metrics to ethersphere/go-metrics

* go-metrics: add tests. move ResettingTimer logic from reporter to type.

* all, metrics: pull in metrics package in go-ethereum

* metrics/test: make sure metrics are enabled for tests

* metrics: apply gosimple rules

* metrics/exp, internal/debug: init expvar endpoint when starting pprof server

* internal/debug: tiny comment formatting fix
2018-02-23 11:56:08 +02:00
Péter Szilágyi
7f74bdf8dd Merge pull request #16164 from karalabe/les-receipt-fix-optimal
eth, les, light: filter on logs only, derive receipts on demand
2018-02-23 11:50:16 +02:00
Balint Gabor
a1984ce727 Merge pull request #15748 from janos/multiple-ens-endpoints
swarm: repeated --ens-api CLI flag (#14386)
2018-02-22 23:15:13 +01:00
Janos Guljas
6a9730edaa swarm, cmd/swarm: Merge branch 'master' into multiple-ens-endpoints 2018-02-22 18:51:34 +01:00
Ivan Daniluk
8522b31221 p2p: remove unused code (#16158)
* p2p: remove unused code

* p2p: remove unused imports
2018-02-22 19:20:28 +02:00
Péter Szilágyi
5cf1d35470 eth, les, light: filter on logs only, derive receipts on demand 2018-02-22 19:12:43 +02:00
Janoš Guljaš
4535247793 rpc: set rpcRequest.service as methodNotFoundError.service value (#16163)
RPC Server readRequest method sets the serverRequest error service
value as the rpcRequest.method and this change sets it to the right
service value.
2018-02-22 18:39:28 +02:00
Péter Szilágyi
44c393607e Merge pull request #16166 from karalabe/drop-dead
core: yeah, funny file, drop it
2018-02-22 16:31:28 +02:00
Balint Gabor
221486a291 Merge pull request #15919 from ethersphere/p2p-protocols-pr
p2p/protocols, p2p/testing: protocol abstraction and testing
2018-02-22 15:02:51 +01:00
Péter Szilágyi
0b3e23f636 core: yeah, funny file, drop it 2018-02-22 15:41:48 +02:00
Janos Guljas
a3a07350dc swarm, cmd/swarm: Merge branch 'master' into multiple-ens-endpoints 2018-02-22 14:23:17 +01:00
Péter Szilágyi
5be1085b6b Merge pull request #16165 from karalabe/faucet-twitter-api
cmd/faucet: resolve twitter user from final redirect
2018-02-22 13:30:40 +02:00
Péter Szilágyi
72c4c50777 cmd/faucet: resolve twitter user from final redirect 2018-02-22 13:20:36 +02:00
Anton Evangelatov
1e457b6599 p2p: don't send DiscReason when using net.Pipe (#16004) 2018-02-22 11:41:06 +01:00
Felix Lange
28b20cff4b p2p/protocols: gofmt -w -s 2018-02-22 11:37:57 +01:00
Guillaume Ballet
bb5349b154 whisper: Support for v2 has long been discontinued, remove it. (#16153) 2018-02-22 12:25:07 +02:00
Péter Szilágyi
724a915470 Merge pull request #16157 from nileshtrivedi/master
cmd/puppeth: Don't allow hyphen in network name. Fixes #16155
2018-02-22 09:56:59 +02:00
Nilesh Trivedi
085d3fbf72 cmd/puppeth: Don't allow hyphen in network name. Fixes #16155 2018-02-22 00:23:50 +05:30
Martin Holst Swende
45ce4dce3f Merge pull request #15776 from ProChain/master
accounts/abi: Fix the bug of mobile framework crashing
2018-02-21 19:21:41 +01:00
Martin Holst Swende
f54506ccf8 Merge pull request #15770 from holiman/abi_nostruct
accounts/abi: add another unpack interface
2018-02-21 15:14:07 +01:00
Martin Holst Swende
b585f76128 ethapi: prevent creating contract if no data is provided (#16108)
* ethapi: prevent creating contract if no data is provided

* internal/ethapi: downcase error for no data on contract creation
2018-02-21 16:10:18 +02:00
Dmitry Shulyak
14c76371ba p2p: when peer is removed remove it also from dial history (#16060)
This change removes a peer information from dialing history
when peer is removed from static list. It allows to force a
server to re-dial concrete peer if it is needed.

In our case we are running geth node on mobile devices, and
it is common for a network connection to flap on mobile.
Almost every time it flaps or network connection is changed
from cellular to wifi peers are disconnected with read
timeout. And usually it takes 30 seconds (default expiration
timeout) to recover connection with static peers after
connectivity is restored.

This change allows us to reconnect with peers almost
immediately and it seems harmless enough.
2018-02-21 15:03:26 +01:00
Péter Szilágyi
7d57824663 Merge pull request #16142 from karalabe/graceful-sigterm
cmd, console: support all termination signals
2018-02-21 15:50:34 +02:00
Péter Szilágyi
01507d9b9d cmd, console: support all termination signals 2018-02-21 15:23:10 +02:00
Pedro Pombeiro
34d94e22d9 whisper: Fix race condition in whisperv6/peer.go 2018-02-21 13:23:53 +01:00
Martin Holst Swende
61f2279bde abi: fix missing method on go 1.7/1.8 2018-02-21 12:27:50 +01:00
Martin Holst Swende
bd6ed23899 accounts/abi: harden unpacking against malicious input 2018-02-21 12:27:50 +01:00
Martin Holst Swende
08c5d4dd27 accounts/abi: address review concerns 2018-02-21 12:27:50 +01:00
Martin Holst Swende
f0f594d045 accounts/abi: Deduplicate code in unpacker 2018-02-21 12:27:50 +01:00
Martin Holst Swende
1ede68355d accounts/abi: add another unpack interface 2018-02-21 12:27:50 +01:00
steve greensill
5603715c06 README: add goreportcard.com badge to Readme (#16133)
* README: add goreportcard.com badge to Readme

* README: fix double github.com
2018-02-20 11:38:27 +02:00
Péter Szilágyi
46a5532ac5 VERSION, params: begin v1.8.2 release cycle 2018-02-19 11:38:36 +02:00
Péter Szilágyi
1e67410e88 params: release Geth v1.8.1 2018-02-19 11:35:31 +02:00
Felföldi Zsolt
1bdde620da les: fix light fetcher database race (#16103)
* les: fix light fetcher database race

* les: lightFetcher comments
2018-02-19 10:41:30 +02:00
Péter Szilágyi
06c5cae315 Merge pull request #16115 from nonsense/update_rjeczalik_notify
vendor: update rjeczalik/notify so that it compiles on go1.10
2018-02-19 10:39:28 +02:00
Janos Guljas
e07603bbc4 p2p/testing: check for all expectations in TestExchanges
Handle all expectations in ProtocolSession.TestExchanges in any
order that are received.
2018-02-17 23:42:28 +01:00
Péter Szilágyi
9fd76e33af Merge pull request #16109 from karalabe/p2p-bond-check
p2p/discover: validate bond against lastpong, not db presence
2018-02-17 18:47:44 +02:00
Anton Evangelatov
0a7cbd915a vendor: update rjeczalik/notify so that it compiles on go1.10 2018-02-17 14:35:59 +01:00
Felix Lange
aeedec4078 p2p/discover: s/lastPong/bondTime/, update TestUDP_findnode
I forgot to change the check in udp.go when I changed Table.bond to be
based on lastPong instead of node presence in db. Rename lastPong to
bondTime and add hasBond so it's clearer what this DB key is used for
now.
2018-02-16 21:29:20 +01:00
Péter Szilágyi
32301a4d6b p2p/discover: validate bond against lastpong, not db presence 2018-02-16 17:05:08 +02:00
Fynn
1e72271f57 accounts/abi: use unpackTuple to unpack event arguments
Events with just 1 argument fail before this change
2018-02-16 11:46:25 +01:00
cooganb
4e61ed02e2 swarm: add favicon for Swarm templates served by browser (#15958)
* swarm: added script to HTML header to create favicon addresses #153

* swarm: moved data blob direclty into link tag, removed script

* swarm: added favicon info to other html templates

* swarm: fixing test errors

* swarm: fixing favicon test

* swarm: fixing travis tests

* swarm: added script to HTML header to create favicon addresses #153

* swarm: moved data blob direclty into link tag, removed script

* swarm: added favicon info to other html templates

* swarm: fixing test errors

* swarm: fixing favicon test

* swarm: fixing travis tests
2018-02-15 16:24:20 +02:00
Guillaume Ballet
5f9b01a283 whisper: only use the node id as a p2p id, not for sending messages (#16102)
This is in preparation for the switch to libp2p: the ID generated
will be from a private key created with the help of libp2p's crypto
library, while Whisper will still use Go's default crypto libraries
for encrypting its messages. This change removes a conflict.

It shouldn't have any impact as the person receiving emails is
the user, not the node.
2018-02-15 14:43:48 +02:00
gluk256
fac6d9ce77 whisper: test timeout extended (#16088)
* whisper: timeout extended

* whisper: test updated

* whisper: test updated
2018-02-15 14:42:44 +02:00
Péter Szilágyi
2003b79779 Merge pull request #16095 from karalabe/les-lock
les: add missing lock around peer access
2018-02-15 13:02:36 +02:00
GuiltyMorishita
e2f2bb3e2e node: fix typo hvosts -> vhosts (#16096) 2018-02-15 12:38:39 +02:00
Péter Szilágyi
b92276c700 Merge pull request #16098 from holiman/fix_import
main: add gc flags to import-command
2018-02-15 12:34:33 +02:00
Martin Holst Swende
de93a9d437 main: add gc flags to import-command 2018-02-15 09:16:59 +01:00
ferhat elmas
dc7ca52b3b core: handle ignored error (#16065)
- according to implementation of `IntrinsicGas`
we can continue execution since problem will be detected
later. However, early return is future-proof for changes.
2018-02-14 22:02:51 +02:00
Péter Szilágyi
dfc5842a89 les: add missing lock around peer access 2018-02-14 21:09:20 +02:00
ferhat elmas
ff225db813 core/vm: remove unused hashing (#16075) 2018-02-14 15:41:05 +02:00
Felix Lange
752761cb57 params, VERSION: v1.8.1 unstable 2018-02-14 13:55:21 +01:00
mark.lin
c1d70ea970 accounts/abi, core: add AddTxWithChain in BlockGen for simulation 2018-01-29 18:47:08 +08:00
croath
a6787a6308 accounts/abi: fix the output at the beginning instead of making a workaround 2018-01-23 19:10:23 +08:00
zelig
407339085f p2p/protocols, p2p/testing: protocol abstraction and testing 2018-01-18 10:53:47 +01:00
croath
88e67c552e accounts/abi: add a test case for unpacking mobile interfaces 2017-12-31 19:12:55 +08:00
croath
e7cd627d93 accounts/abi: fix for one output interface crashing 2017-12-29 19:56:23 +08:00
Janos Guljas
820cf09c98 cmd/swarm: return error early in buildConfig function 2017-12-19 23:51:09 +01:00
Janos Guljas
dd5ae4fd8e cmd/swarm: add validation for EnsAPIs configuration parameter 2017-12-19 11:47:26 +01:00
Janos Guljas
0d6a735a72 swarm/api: implement NoResolverError with information about TLD
MultiResolver needs to provide information about TLD that has
no resolver configured for.
2017-12-18 23:07:48 +01:00
Janos Guljas
c0a4d9e1e6 cmd/swarm, swarm: disable ENS API by default
Specifying ENS API CLI flag, env variable or configuration
field is required for ENS resolving. Backward compatibility is
preserved with --ens-api="" CLI flag value.
2017-12-18 16:22:39 +01:00
Janos Guljas
47a8014559 cmd/swarm: Merge branch 'master' into multiple-ens-endpoints
Fix a conflict in cmd/swarm envVarsOverride function.
2017-12-14 10:36:12 +01:00
Janos Guljas
19982f9467 swarm, cmd/swarm: Merge branch 'master' into multiple-ens-endpoints
Merge with changes that implement config file PR #15548.

Field *EnsApi string* in swarm/api.Config is replaced with
*EnsAPIs []string*.

A new field *EnsDisabled bool* is added to swarm/api.Config for
easy way to disable ENS resolving with config file.

Signature of function swarm.NewSwarm is changed and simplified.
2017-12-13 10:40:39 +01:00
Janos Guljas
1dc19de5da swarm/api: use path.Ext instead strings.LastIndex in MultiResolver.Resolve 2017-12-04 22:56:52 +01:00
Janos Guljas
e451b65fae swarm: deprecate --ens-addr CLI flag with a warning message 2017-12-04 22:41:21 +01:00
Janos Guljas
3732c15faa swarm/api: remove unneeded blank assignement 2017-12-04 22:32:33 +01:00
Janos Guljas
a758b5cf7a swarm/api: initialize map with make to comply with the convention 2017-12-04 22:31:00 +01:00
Janos Guljas
34edbc8868 swarm/api: remove unneeded assignment in MultiResolverOptionWithResolver 2017-12-04 22:29:37 +01:00
Janos Guljas
15ad6f27da swarm: check if "--ens-api ''" is specified in order to disable ENS 2017-12-04 22:28:11 +01:00
Janos Guljas
b33a051a48 swarm: add comment for parseFlagEnsAPI and fix a mistake in comment in code 2017-12-04 22:20:29 +01:00
Janos Guljas
7898e0d585 swarm: multiple --ens-api flags
Allow multiple --ens-api flags to be specified with value format
[tld:][contract-addr@]url.

Backward compatibility with only one --ens-api flag and --ens-addr
flag is preserved and conflict cases are handled:

 - multiple --ens-api with --ens-addr returns an error

 - single --ens-api with contract address and --ens-addr with
   different contract address returns an error

Previously implemented --ens-endpoint is removed. Its functionality
is replaced with multiple --ens-api flags.
2017-12-04 12:44:24 +01:00
Janos Guljas
057af8c5c8 swarm: add CLI --ens-endpoint flag (#14386)
Implement a CLI flag that can be repeated to allow multiple ENS
resolvers for different TLDs.
2017-12-01 11:25:50 +01:00
401 changed files with 32653 additions and 16169 deletions

1
.gitattributes vendored
View File

@@ -1,2 +1,3 @@
# Auto detect text files and perform LF normalization
* text=auto
*.sol linguist-language=Solidity

3
.github/CODEOWNERS vendored
View File

@@ -5,5 +5,8 @@ accounts/usbwallet @karalabe
consensus @karalabe
core/ @karalabe @holiman
eth/ @karalabe
les/ @zsfelfoldi
light/ @zsfelfoldi
mobile/ @karalabe
p2p/ @fjl @zsfelfoldi
whisper/ @gballet @gluk256

11
.github/no-response.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
# Number of days of inactivity before an Issue is closed for lack of response
daysUntilClose: 30
# Label requiring a response
responseRequiredLabel: more-information-needed
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
closeComment: >
This issue has been automatically closed because there has been no response
to our request for more information from the original author. With only the
information that is currently in the issue, we don't have enough information
to take action. Please reach out if you have or find the answers we need so
that we can investigate further.

17
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 366
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 42
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

6
.gitignore vendored
View File

@@ -34,8 +34,14 @@ profile.cov
# IdeaIDE
.idea
# VS Code
.vscode
# dashboard
/dashboard/assets/flow-typed
/dashboard/assets/node_modules
/dashboard/assets/stats.json
/dashboard/assets/bundle.js
/dashboard/assets/package-lock.json
**/yarn-error.log

View File

@@ -6,51 +6,40 @@ matrix:
- os: linux
dist: trusty
sudo: required
go: 1.7.x
go: 1.9.x
script:
- sudo modprobe fuse
- sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf
- go run build/ci.go install
- go run build/ci.go test -coverage
- os: linux
dist: trusty
sudo: required
go: 1.8.x
script:
- sudo modprobe fuse
- sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf
- go run build/ci.go install
- go run build/ci.go test -coverage
- go run build/ci.go test -coverage $TEST_PACKAGES
# These are the latest Go versions.
- os: linux
dist: trusty
sudo: required
go: 1.9.x
go: 1.10.x
script:
- sudo modprobe fuse
- sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf
- go run build/ci.go install
- go run build/ci.go test -coverage
- go run build/ci.go test -coverage $TEST_PACKAGES
- os: osx
go: 1.9.x
go: 1.10.x
script:
- unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703
- brew update
- brew install caskroom/cask/brew-cask
- brew cask install osxfuse
- go run build/ci.go install
- go run build/ci.go test -coverage
- go run build/ci.go test -coverage $TEST_PACKAGES
# This builder only tests code linters on latest version of Go
- os: linux
dist: trusty
go: 1.9.x
go: 1.10.x
env:
- lint
git:
@@ -58,14 +47,12 @@ matrix:
script:
- go run build/ci.go lint
# This builder does the Ubuntu PPA and Linux Azure uploads
# This builder does the Ubuntu PPA upload
- os: linux
dist: trusty
sudo: required
go: 1.9.x
go: 1.10.x
env:
- ubuntu-ppa
- azure-linux
git:
submodules: false # avoid cloning ethereum/tests
addons:
@@ -74,11 +61,25 @@ matrix:
- devscripts
- debhelper
- dput
- gcc-multilib
- fakeroot
script:
# Build for the primary platforms that Trusty can manage
- go run build/ci.go debsrc -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>" -upload ppa:ethereum/ethereum
# This builder does the Linux Azure uploads
- os: linux
dist: trusty
sudo: required
go: 1.10.x
env:
- azure-linux
git:
submodules: false # avoid cloning ethereum/tests
addons:
apt:
packages:
- gcc-multilib
script:
# Build for the primary platforms that Trusty can manage
- go run build/ci.go install
- go run build/ci.go archive -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go install -arch 386
@@ -102,7 +103,7 @@ matrix:
dist: trusty
services:
- docker
go: 1.9.x
go: 1.10.x
env:
- azure-linux-mips
git:
@@ -146,7 +147,7 @@ matrix:
git:
submodules: false # avoid cloning ethereum/tests
before_install:
- curl https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz | tar -xz
- curl https://storage.googleapis.com/golang/go1.10.1.linux-amd64.tar.gz | tar -xz
- export PATH=`pwd`/go/bin:$PATH
- export GOROOT=`pwd`/go
- export GOPATH=$HOME/go
@@ -163,7 +164,7 @@ matrix:
# This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads
- os: osx
go: 1.9.x
go: 1.10.x
env:
- azure-osx
- azure-ios
@@ -192,8 +193,7 @@ matrix:
# This builder does the Azure archive purges to avoid accumulating junk
- os: linux
dist: trusty
sudo: required
go: 1.9.x
go: 1.10.x
env:
- azure-purge
git:

View File

@@ -1,5 +1,5 @@
# Build Geth in a stock Go builder container
FROM golang:1.9-alpine as builder
FROM golang:1.10-alpine as builder
RUN apk add --no-cache make gcc musl-dev linux-headers
@@ -12,5 +12,11 @@ FROM alpine:latest
RUN apk add --no-cache ca-certificates
COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/
RUN addgroup -g 1000 geth && \
adduser -h /root -D -u 1000 -G geth geth && \
chown geth:geth /root
USER geth
EXPOSE 8545 8546 30303 30303/udp 30304/udp
ENTRYPOINT ["geth"]

View File

@@ -1,5 +1,5 @@
# Build Geth in a stock Go builder container
FROM golang:1.9-alpine as builder
FROM golang:1.10-alpine as builder
RUN apk add --no-cache make gcc musl-dev linux-headers
@@ -12,4 +12,10 @@ FROM alpine:latest
RUN apk add --no-cache ca-certificates
COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
RUN addgroup -g 1000 geth && \
adduser -h /root -D -u 1000 -G geth geth && \
chown geth:geth /root
USER geth
EXPOSE 8545 8546 30303 30303/udp 30304/udp

View File

@@ -5,6 +5,8 @@ Official golang implementation of the Ethereum protocol.
[![API Reference](
https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667
)](https://godoc.org/github.com/ethereum/go-ethereum)
[![Go Report Card](https://goreportcard.com/badge/github.com/ethereum/go-ethereum)](https://goreportcard.com/report/github.com/ethereum/go-ethereum)
[![Travis](https://travis-ci.org/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/go-ethereum?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Automated builds are available for stable releases and the unstable master branch.
@@ -140,7 +142,7 @@ Do not forget `--rpcaddr 0.0.0.0`, if you want to access RPC from other containe
### Programatically interfacing Geth nodes
As a developer, sooner rather than later you'll want to start interacting with Geth and the Ethereum
network via your own programs and not manually through the console. To aid this, Geth has built in
network via your own programs and not manually through the console. To aid this, Geth has built-in
support for a JSON-RPC based APIs ([standard APIs](https://github.com/ethereum/wiki/wiki/JSON-RPC) and
[Geth specific APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs)). These can be
exposed via HTTP, WebSockets and IPC (unix sockets on unix based platforms, and named pipes on Windows).

View File

@@ -1 +1 @@
1.8.0
1.8.4

View File

@@ -136,11 +136,11 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
// MethodById looks up a method by the 4-byte id
// returns nil if none found
func (abi *ABI) MethodById(sigdata []byte) *Method {
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
for _, method := range abi.Methods {
if bytes.Equal(method.Id(), sigdata[:4]) {
return &method
return &method, nil
}
}
return nil
return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
}

View File

@@ -621,14 +621,16 @@ func TestBareEvents(t *testing.T) {
// TestUnpackEvent is based on this contract:
// contract T {
// event received(address sender, uint amount, bytes memo);
// event receivedAddr(address sender);
// function receive(bytes memo) external payable {
// received(msg.sender, msg.value, memo);
// receivedAddr(msg.sender);
// }
// }
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
func TestUnpackEvent(t *testing.T) {
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
abi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
@@ -656,6 +658,17 @@ func TestUnpackEvent(t *testing.T) {
} else {
t.Logf("len(data): %d; received event: %+v", len(data), ev)
}
type ReceivedAddrEvent struct {
Address common.Address
}
var receivedAddrEv ReceivedAddrEvent
err = abi.Unpack(&receivedAddrEv, "receivedAddr", data)
if err != nil {
t.Error(err)
} else {
t.Logf("len(data): %d; received event: %+v", len(data), receivedAddrEv)
}
}
func TestABI_MethodById(t *testing.T) {
@@ -689,7 +702,11 @@ func TestABI_MethodById(t *testing.T) {
}
for name, m := range abi.Methods {
a := fmt.Sprintf("%v", m)
b := fmt.Sprintf("%v", abi.MethodById(m.Id()))
m2, err := abi.MethodById(m.Id())
if err != nil {
t.Fatalf("Failed to look up ABI method: %v", err)
}
b := fmt.Sprintf("%v", m2)
if a != b {
t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, common.ToHex(m.Id()))
}

View File

@@ -67,6 +67,17 @@ func (arguments Arguments) LengthNonIndexed() int {
return out
}
// NonIndexed returns the arguments with indexed arguments filtered out
func (arguments Arguments) NonIndexed() Arguments {
var ret []Argument
for _, arg := range arguments {
if !arg.Indexed {
ret = append(ret, arg)
}
}
return ret
}
// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[]
func (arguments Arguments) isTuple() bool {
return len(arguments) > 1
@@ -74,21 +85,25 @@ func (arguments Arguments) isTuple() bool {
// Unpack performs the operation hexdata -> Go format
func (arguments Arguments) Unpack(v interface{}, data []byte) error {
if arguments.isTuple() {
return arguments.unpackTuple(v, data)
}
return arguments.unpackAtomic(v, data)
}
func (arguments Arguments) unpackTuple(v interface{}, output []byte) error {
// make sure the passed value is arguments pointer
valueOf := reflect.ValueOf(v)
if reflect.Ptr != valueOf.Kind() {
if reflect.Ptr != reflect.ValueOf(v).Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
}
marshalledValues, err := arguments.UnpackValues(data)
if err != nil {
return err
}
if arguments.isTuple() {
return arguments.unpackTuple(v, marshalledValues)
}
return arguments.unpackAtomic(v, marshalledValues)
}
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
var (
value = valueOf.Elem()
value = reflect.ValueOf(v).Elem()
typ = value.Type()
kind = value.Kind()
)
@@ -98,53 +113,19 @@ func (arguments Arguments) unpackTuple(v interface{}, output []byte) error {
}
// If the output interface is a struct, make sure names don't collide
if kind == reflect.Struct {
exists := make(map[string]bool)
for _, arg := range arguments {
field := capitalise(arg.Name)
if field == "" {
return fmt.Errorf("abi: purely underscored output cannot unpack to struct")
}
if exists[field] {
return fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", field)
}
exists[field] = true
}
}
// `i` counts the nonindexed arguments.
// `j` counts the number of complex types.
// both `i` and `j` are used to to correctly compute `data` offset.
i, j := -1, 0
for _, arg := range arguments {
if arg.Indexed {
// can't read, continue
continue
}
i++
marshalledValue, err := toGoType((i+j)*32, arg.Type, output)
if err != nil {
if err := requireUniqueStructFieldNames(arguments); err != nil {
return err
}
}
for i, arg := range arguments.NonIndexed() {
if arg.Type.T == ArrayTy {
// combined index ('i' + 'j') need to be adjusted only by size of array, thus
// we need to decrement 'j' because 'i' was incremented
j += arg.Type.Size - 1
}
reflectValue := reflect.ValueOf(marshalledValue)
reflectValue := reflect.ValueOf(marshalledValues[i])
switch kind {
case reflect.Struct:
name := capitalise(arg.Name)
for j := 0; j < typ.NumField(); j++ {
// TODO read tags: `abi:"fieldName"`
if typ.Field(j).Name == name {
if err := set(value.Field(j), reflectValue, arg); err != nil {
return err
}
}
err := unpackStruct(value, reflectValue, arg)
if err != nil {
return err
}
case reflect.Slice, reflect.Array:
if value.Len() < i {
@@ -166,34 +147,84 @@ func (arguments Arguments) unpackTuple(v interface{}, output []byte) error {
}
// unpackAtomic unpacks ( hexdata -> go ) a single value
func (arguments Arguments) unpackAtomic(v interface{}, output []byte) error {
// make sure the passed value is arguments pointer
valueOf := reflect.ValueOf(v)
if reflect.Ptr != valueOf.Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interface{}) error {
if len(marshalledValues) != 1 {
return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues))
}
arg := arguments[0]
if arg.Indexed {
return fmt.Errorf("abi: attempting to unpack indexed variable into element.")
elem := reflect.ValueOf(v).Elem()
kind := elem.Kind()
reflectValue := reflect.ValueOf(marshalledValues[0])
if kind == reflect.Struct {
//make sure names don't collide
if err := requireUniqueStructFieldNames(arguments); err != nil {
return err
}
return unpackStruct(elem, reflectValue, arguments[0])
}
value := valueOf.Elem()
return set(elem, reflectValue, arguments.NonIndexed()[0])
marshalledValue, err := toGoType(0, arg.Type, output)
if err != nil {
return err
}
return set(value, reflect.ValueOf(marshalledValue), arg)
}
// Unpack performs the operation Go format -> Hexdata
// Computes the full size of an array;
// i.e. counting nested arrays, which count towards size for unpacking.
func getArraySize(arr *Type) int {
size := arr.Size
// Arrays can be nested, with each element being the same size
arr = arr.Elem
for arr.T == ArrayTy {
// Keep multiplying by elem.Size while the elem is an array.
size *= arr.Size
arr = arr.Elem
}
// Now we have the full array size, including its children.
return size
}
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
// without supplying a struct to unpack into. Instead, this method returns a list containing the
// values. An atomic argument will be a list with one element.
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
retval := make([]interface{}, 0, arguments.LengthNonIndexed())
virtualArgs := 0
for index, arg := range arguments.NonIndexed() {
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
if arg.Type.T == ArrayTy {
// If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256.
// This means that we need to add two 'virtual' arguments when
// we count the index from now on.
//
// Array values nested multiple levels deep are also encoded inline:
// [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
//
// Calculate the full array size to get the correct offset for the next argument.
// Decrement it by 1, as the normal index increment is still applied.
virtualArgs += getArraySize(&arg.Type) - 1
}
if err != nil {
return nil, err
}
retval = append(retval, marshalledValue)
}
return retval, nil
}
// PackValues performs the operation Go format -> Hexdata
// It is the semantic opposite of UnpackValues
func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) {
return arguments.Pack(args...)
}
// Pack performs the operation Go format -> Hexdata
func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
// Make sure arguments match up and pack them
abiArgs := arguments
if len(args) != len(abiArgs) {
return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(abiArgs))
}
// variable input is the output appended at the end of packed
// output. This is used for strings and bytes types input.
var variableInput []byte
@@ -207,7 +238,6 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
inputOffset += 32
}
}
var ret []byte
for i, a := range args {
input := abiArgs[i]
@@ -216,7 +246,6 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
if err != nil {
return nil, err
}
// check for a slice type (string, bytes, slice)
if input.Type.requiresLengthPrefix() {
// calculate the offset
@@ -248,3 +277,18 @@ func capitalise(input string) string {
}
return strings.ToUpper(input[:1]) + input[1:]
}
//unpackStruct extracts each argument into its corresponding struct field
func unpackStruct(value, reflectValue reflect.Value, arg Argument) error {
name := capitalise(arg.Name)
typ := value.Type()
for j := 0; j < typ.NumField(); j++ {
// TODO read tags: `abi:"fieldName"`
if typ.Field(j).Name == name {
if err := set(value.Field(j), reflectValue, arg); err != nil {
return err
}
}
}
return nil
}

View File

@@ -307,9 +307,9 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
for _, tx := range b.pendingBlock.Transactions() {
block.AddTx(tx)
block.AddTxWithChain(b.blockchain, tx)
}
block.AddTx(tx)
block.AddTxWithChain(b.blockchain, tx)
})
statedb, _ := b.blockchain.State()
@@ -428,10 +428,23 @@ func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumb
}
return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
}
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil
}
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
receipts := core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash))
if receipts == nil {
return nil, nil
}
logs := make([][]*types.Log, len(receipts))
for i, receipt := range receipts {
logs[i] = receipt.Logs
}
return logs, nil
}
func (fb *filterBackend) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
return event.NewSubscription(func(quit <-chan struct{}) error {
<-quit

View File

@@ -164,118 +164,147 @@ var bindType = map[Lang]func(kind abi.Type) string{
LangJava: bindTypeJava,
}
// Helper function for the binding generators.
// It reads the unmatched characters after the inner type-match,
// (since the inner type is a prefix of the total type declaration),
// looks for valid arrays (possibly a dynamic one) wrapping the inner type,
// and returns the sizes of these arrays.
//
// Returned array sizes are in the same order as solidity signatures; inner array size first.
// Array sizes may also be "", indicating a dynamic array.
func wrapArray(stringKind string, innerLen int, innerMapping string) (string, []string) {
remainder := stringKind[innerLen:]
//find all the sizes
matches := regexp.MustCompile(`\[(\d*)\]`).FindAllStringSubmatch(remainder, -1)
parts := make([]string, 0, len(matches))
for _, match := range matches {
//get group 1 from the regex match
parts = append(parts, match[1])
}
return innerMapping, parts
}
// Translates the array sizes to a Go-lang declaration of a (nested) array of the inner type.
// Simply returns the inner type if arraySizes is empty.
func arrayBindingGo(inner string, arraySizes []string) string {
out := ""
//prepend all array sizes, from outer (end arraySizes) to inner (start arraySizes)
for i := len(arraySizes) - 1; i >= 0; i-- {
out += "[" + arraySizes[i] + "]"
}
out += inner
return out
}
// bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping
// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
// mapped will use an upscaled type (e.g. *big.Int).
func bindTypeGo(kind abi.Type) string {
stringKind := kind.String()
innerLen, innerMapping := bindUnnestedTypeGo(stringKind)
return arrayBindingGo(wrapArray(stringKind, innerLen, innerMapping))
}
// The inner function of bindTypeGo, this finds the inner type of stringKind.
// (Or just the type itself if it is not an array or slice)
// The length of the matched part is returned, with the the translated type.
func bindUnnestedTypeGo(stringKind string) (int, string) {
switch {
case strings.HasPrefix(stringKind, "address"):
parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 2 {
return stringKind
}
return fmt.Sprintf("%scommon.Address", parts[1])
return len("address"), "common.Address"
case strings.HasPrefix(stringKind, "bytes"):
parts := regexp.MustCompile(`bytes([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 3 {
return stringKind
}
return fmt.Sprintf("%s[%s]byte", parts[2], parts[1])
parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind)
return len(parts[0]), fmt.Sprintf("[%s]byte", parts[1])
case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 4 {
return stringKind
}
parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind)
switch parts[2] {
case "8", "16", "32", "64":
return fmt.Sprintf("%s%sint%s", parts[3], parts[1], parts[2])
return len(parts[0]), fmt.Sprintf("%sint%s", parts[1], parts[2])
}
return fmt.Sprintf("%s*big.Int", parts[3])
return len(parts[0]), "*big.Int"
case strings.HasPrefix(stringKind, "bool") || strings.HasPrefix(stringKind, "string"):
parts := regexp.MustCompile(`([a-z]+)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 3 {
return stringKind
}
return fmt.Sprintf("%s%s", parts[2], parts[1])
case strings.HasPrefix(stringKind, "bool"):
return len("bool"), "bool"
case strings.HasPrefix(stringKind, "string"):
return len("string"), "string"
default:
return stringKind
return len(stringKind), stringKind
}
}
// Translates the array sizes to a Java declaration of a (nested) array of the inner type.
// Simply returns the inner type if arraySizes is empty.
func arrayBindingJava(inner string, arraySizes []string) string {
// Java array type declarations do not include the length.
return inner + strings.Repeat("[]", len(arraySizes))
}
// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
// mapped will use an upscaled type (e.g. BigDecimal).
func bindTypeJava(kind abi.Type) string {
stringKind := kind.String()
innerLen, innerMapping := bindUnnestedTypeJava(stringKind)
return arrayBindingJava(wrapArray(stringKind, innerLen, innerMapping))
}
// The inner function of bindTypeJava, this finds the inner type of stringKind.
// (Or just the type itself if it is not an array or slice)
// The length of the matched part is returned, with the the translated type.
func bindUnnestedTypeJava(stringKind string) (int, string) {
switch {
case strings.HasPrefix(stringKind, "address"):
parts := regexp.MustCompile(`address(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 2 {
return stringKind
return len(stringKind), stringKind
}
if parts[1] == "" {
return fmt.Sprintf("Address")
return len("address"), "Address"
}
return fmt.Sprintf("Addresses")
return len(parts[0]), "Addresses"
case strings.HasPrefix(stringKind, "bytes"):
parts := regexp.MustCompile(`bytes([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 3 {
return stringKind
parts := regexp.MustCompile(`bytes([0-9]*)`).FindStringSubmatch(stringKind)
if len(parts) != 2 {
return len(stringKind), stringKind
}
if parts[2] != "" {
return "byte[][]"
}
return "byte[]"
return len(parts[0]), "byte[]"
case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 4 {
return stringKind
//Note that uint and int (without digits) are also matched,
// these are size 256, and will translate to BigInt (the default).
parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(stringKind)
if len(parts) != 3 {
return len(stringKind), stringKind
}
switch parts[2] {
case "8", "16", "32", "64":
if parts[1] == "" {
if parts[3] == "" {
return fmt.Sprintf("int%s", parts[2])
}
return fmt.Sprintf("int%s[]", parts[2])
}
namedSize := map[string]string{
"8": "byte",
"16": "short",
"32": "int",
"64": "long",
}[parts[2]]
//default to BigInt
if namedSize == "" {
namedSize = "BigInt"
}
if parts[3] == "" {
return fmt.Sprintf("BigInt")
}
return fmt.Sprintf("BigInts")
return len(parts[0]), namedSize
case strings.HasPrefix(stringKind, "bool"):
parts := regexp.MustCompile(`bool(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 2 {
return stringKind
}
if parts[1] == "" {
return fmt.Sprintf("bool")
}
return fmt.Sprintf("bool[]")
return len("bool"), "boolean"
case strings.HasPrefix(stringKind, "string"):
parts := regexp.MustCompile(`string(\[[0-9]*\])?`).FindStringSubmatch(stringKind)
if len(parts) != 2 {
return stringKind
}
if parts[1] == "" {
return fmt.Sprintf("String")
}
return fmt.Sprintf("String[]")
return len("string"), "String"
default:
return stringKind
return len(stringKind), stringKind
}
}
@@ -325,11 +354,13 @@ func namedTypeJava(javaKind string, solKind abi.Type) string {
return "String"
case "string[]":
return "Strings"
case "bool":
case "boolean":
return "Bool"
case "bool[]":
case "boolean[]":
return "Bools"
case "BigInt":
case "BigInt[]":
return "BigInts"
default:
parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
if len(parts) != 4 {
return javaKind
@@ -344,8 +375,6 @@ func namedTypeJava(javaKind string, solKind abi.Type) string {
default:
return javaKind
}
default:
return javaKind
}
}
@@ -356,8 +385,7 @@ var methodNormalizer = map[Lang]func(string) string{
LangJava: decapitalise,
}
// capitalise makes the first character of a string upper case, also removing any
// prefixing underscores from the variable names.
// capitalise makes a camel-case string which starts with an upper case character.
func capitalise(input string) string {
for len(input) > 0 && input[0] == '_' {
input = input[1:]
@@ -365,12 +393,42 @@ func capitalise(input string) string {
if len(input) == 0 {
return ""
}
return strings.ToUpper(input[:1]) + input[1:]
return toCamelCase(strings.ToUpper(input[:1]) + input[1:])
}
// decapitalise makes the first character of a string lower case.
// decapitalise makes a camel-case string which starts with a lower case character.
func decapitalise(input string) string {
return strings.ToLower(input[:1]) + input[1:]
for len(input) > 0 && input[0] == '_' {
input = input[1:]
}
if len(input) == 0 {
return ""
}
return toCamelCase(strings.ToLower(input[:1]) + input[1:])
}
// toCamelCase converts an under-score string to a camel-case string
func toCamelCase(input string) string {
toupper := false
result := ""
for k, v := range input {
switch {
case k == 0:
result = strings.ToUpper(string(input[0]))
case toupper:
result += strings.ToUpper(string(v))
toupper = false
case v == '_':
toupper = true
default:
result += string(v)
}
}
return result
}
// structured checks whether a list of ABI data types has enough information to

View File

@@ -425,7 +425,7 @@ var bindTests = []struct {
}
`,
},
// Tests that gas estimation works for contracts with weird gas mechanics too.
// Tests that gas estimation works for contracts with weird gas mechanics too.
{
`FunkyGasPattern`,
`
@@ -506,6 +506,7 @@ var bindTests = []struct {
}
`,
},
// Tests that methods and returns with underscores inside work correctly.
{
`Underscorer`,
`
@@ -518,7 +519,7 @@ var bindTests = []struct {
}
function LowerUpperCollision() constant returns (int _res, int Res) {
return (1, 2);
}
}
function UpperLowerCollision() constant returns (int _Res, int res) {
return (1, 2);
}
@@ -531,9 +532,12 @@ var bindTests = []struct {
function AllPurelyUnderscoredOutput() constant returns (int _, int __) {
return (1, 2);
}
function _under_scored_func() constant returns (int _int) {
return 0;
}
}
`, `6060604052341561000f57600080fd5b6103498061001e6000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806303a592131461008857806367e6633d146100b85780639df484851461014d578063af7486ab1461017d578063b564b34d146101ad578063e02ab24d146101dd578063e409ca451461020d575b600080fd5b341561009357600080fd5b61009b61023d565b604051808381526020018281526020019250505060405180910390f35b34156100c357600080fd5b6100cb610252565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156101115780820151818401526020810190506100f6565b50505050905090810190601f16801561013e5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561015857600080fd5b6101606102a0565b604051808381526020018281526020019250505060405180910390f35b341561018857600080fd5b6101906102b5565b604051808381526020018281526020019250505060405180910390f35b34156101b857600080fd5b6101c06102ca565b604051808381526020018281526020019250505060405180910390f35b34156101e857600080fd5b6101f06102df565b604051808381526020018281526020019250505060405180910390f35b341561021857600080fd5b6102206102f4565b604051808381526020018281526020019250505060405180910390f35b60008060016002819150809050915091509091565b600061025c610309565b61013a8090506040805190810160405280600281526020017f7069000000000000000000000000000000000000000000000000000000000000815250915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b6020604051908101604052806000815250905600a165627a7a72305820c11dcfa136fc7d182ee4d34f0b12d988496228f7e2d02d2b5376d996ca1743d00029`,
`[{"constant":true,"inputs":[],"name":"LowerUpperCollision","outputs":[{"name":"_res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UnderscoredOutput","outputs":[{"name":"_int","type":"int256"},{"name":"_string","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperLowerCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"AllPurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"__","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperUpperCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"LowerLowerCollision","outputs":[{"name":"_res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"}]`,
`, `6060604052341561000f57600080fd5b6103858061001e6000396000f30060606040526004361061008e576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806303a592131461009357806346546dbe146100c357806367e6633d146100ec5780639df4848514610181578063af7486ab146101b1578063b564b34d146101e1578063e02ab24d14610211578063e409ca4514610241575b600080fd5b341561009e57600080fd5b6100a6610271565b604051808381526020018281526020019250505060405180910390f35b34156100ce57600080fd5b6100d6610286565b6040518082815260200191505060405180910390f35b34156100f757600080fd5b6100ff61028e565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561014557808201518184015260208101905061012a565b50505050905090810190601f1680156101725780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561018c57600080fd5b6101946102dc565b604051808381526020018281526020019250505060405180910390f35b34156101bc57600080fd5b6101c46102f1565b604051808381526020018281526020019250505060405180910390f35b34156101ec57600080fd5b6101f4610306565b604051808381526020018281526020019250505060405180910390f35b341561021c57600080fd5b61022461031b565b604051808381526020018281526020019250505060405180910390f35b341561024c57600080fd5b610254610330565b604051808381526020018281526020019250505060405180910390f35b60008060016002819150809050915091509091565b600080905090565b6000610298610345565b61013a8090506040805190810160405280600281526020017f7069000000000000000000000000000000000000000000000000000000000000815250915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b6020604051908101604052806000815250905600a165627a7a72305820d1a53d9de9d1e3d55cb3dc591900b63c4f1ded79114f7b79b332684840e186a40029`,
`[{"constant":true,"inputs":[],"name":"LowerUpperCollision","outputs":[{"name":"_res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_under_scored_func","outputs":[{"name":"_int","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UnderscoredOutput","outputs":[{"name":"_int","type":"int256"},{"name":"_string","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperLowerCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"AllPurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"__","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperUpperCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"LowerLowerCollision","outputs":[{"name":"_res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"}]`,
`
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
@@ -562,6 +566,7 @@ var bindTests = []struct {
a, b, _ = underscorer.UpperUpperCollision(nil)
a, b, _ = underscorer.PurelyUnderscoredOutput(nil)
a, b, _ = underscorer.AllPurelyUnderscoredOutput(nil)
a, _ = underscorer.UnderScoredFunc(nil)
fmt.Println(a, b, err)
`,
@@ -737,6 +742,73 @@ var bindTests = []struct {
}
`,
},
{
`DeeplyNestedArray`,
`
contract DeeplyNestedArray {
uint64[3][4][5] public deepUint64Array;
function storeDeepUintArray(uint64[3][4][5] arr) public {
deepUint64Array = arr;
}
function retrieveDeepArray() public view returns (uint64[3][4][5]) {
return deepUint64Array;
}
}
`,
`6060604052341561000f57600080fd5b6106438061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063344248551461005c5780638ed4573a1461011457806398ed1856146101ab575b600080fd5b341561006757600080fd5b610112600480806107800190600580602002604051908101604052809291906000905b828210156101055783826101800201600480602002604051908101604052809291906000905b828210156100f25783826060020160038060200260405190810160405280929190826003602002808284378201915050505050815260200190600101906100b0565b505050508152602001906001019061008a565b5050505091905050610208565b005b341561011f57600080fd5b61012761021d565b604051808260056000925b8184101561019b578284602002015160046000925b8184101561018d5782846020020151600360200280838360005b8381101561017c578082015181840152602081019050610161565b505050509050019260010192610147565b925050509260010192610132565b9250505091505060405180910390f35b34156101b657600080fd5b6101de6004808035906020019091908035906020019091908035906020019091905050610309565b604051808267ffffffffffffffff1667ffffffffffffffff16815260200191505060405180910390f35b80600090600561021992919061035f565b5050565b6102256103b0565b6000600580602002604051908101604052809291906000905b8282101561030057838260040201600480602002604051908101604052809291906000905b828210156102ed578382016003806020026040519081016040528092919082600380156102d9576020028201916000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff16815260200190600801906020826007010492830192600103820291508084116102945790505b505050505081526020019060010190610263565b505050508152602001906001019061023e565b50505050905090565b60008360058110151561031857fe5b600402018260048110151561032957fe5b018160038110151561033757fe5b6004918282040191900660080292509250509054906101000a900467ffffffffffffffff1681565b826005600402810192821561039f579160200282015b8281111561039e5782518290600461038e9291906103df565b5091602001919060040190610375565b5b5090506103ac919061042d565b5090565b610780604051908101604052806005905b6103c9610459565b8152602001906001900390816103c15790505090565b826004810192821561041c579160200282015b8281111561041b5782518290600361040b929190610488565b50916020019190600101906103f2565b5b5090506104299190610536565b5090565b61045691905b8082111561045257600081816104499190610562565b50600401610433565b5090565b90565b610180604051908101604052806004905b6104726105a7565b81526020019060019003908161046a5790505090565b82600380016004900481019282156105255791602002820160005b838211156104ef57835183826101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555092602001926008016020816007010492830192600103026104a3565b80156105235782816101000a81549067ffffffffffffffff02191690556008016020816007010492830192600103026104ef565b505b50905061053291906105d9565b5090565b61055f91905b8082111561055b57600081816105529190610610565b5060010161053c565b5090565b90565b50600081816105719190610610565b50600101600081816105839190610610565b50600101600081816105959190610610565b5060010160006105a59190610610565b565b6060604051908101604052806003905b600067ffffffffffffffff168152602001906001900390816105b75790505090565b61060d91905b8082111561060957600081816101000a81549067ffffffffffffffff0219169055506001016105df565b5090565b90565b50600090555600a165627a7a7230582087e5a43f6965ab6ef7a4ff056ab80ed78fd8c15cff57715a1bf34ec76a93661c0029`,
`[{"constant":false,"inputs":[{"name":"arr","type":"uint64[3][4][5]"}],"name":"storeDeepUintArray","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"retrieveDeepArray","outputs":[{"name":"","type":"uint64[3][4][5]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"name":"deepUint64Array","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"}]`,
`
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth := bind.NewKeyedTransactor(key)
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
//deploy the test contract
_, _, testContract, err := DeployDeeplyNestedArray(auth, sim)
if err != nil {
t.Fatalf("Failed to deploy test contract: %v", err)
}
// Finish deploy.
sim.Commit()
//Create coordinate-filled array, for testing purposes.
testArr := [5][4][3]uint64{}
for i := 0; i < 5; i++ {
testArr[i] = [4][3]uint64{}
for j := 0; j < 4; j++ {
testArr[i][j] = [3]uint64{}
for k := 0; k < 3; k++ {
//pack the coordinates, each array value will be unique, and can be validated easily.
testArr[i][j][k] = uint64(i) << 16 | uint64(j) << 8 | uint64(k)
}
}
}
if _, err := testContract.StoreDeepUintArray(&bind.TransactOpts{
From: auth.From,
Signer: auth.Signer,
}, testArr); err != nil {
t.Fatalf("Failed to store nested array in test contract: %v", err)
}
sim.Commit()
retrievedArr, err := testContract.RetrieveDeepArray(&bind.CallOpts{
From: auth.From,
Pending: false,
})
if err != nil {
t.Fatalf("Failed to retrieve nested array from test contract: %v", err)
}
//quick check to see if contents were copied
// (See accounts/abi/unpack_test.go for more extensive testing)
if retrievedArr[4][3][2] != testArr[4][3][2] {
t.Fatalf("Retrieved value does not match expected value! got: %d, expected: %d. %v", retrievedArr[4][3][2], testArr[4][3][2], err)
}
`,
},
}
// Tests that packages generated by the binder can be successfully compiled and

View File

@@ -25,23 +25,23 @@ import (
)
var (
big_t = reflect.TypeOf(&big.Int{})
derefbig_t = reflect.TypeOf(big.Int{})
uint8_t = reflect.TypeOf(uint8(0))
uint16_t = reflect.TypeOf(uint16(0))
uint32_t = reflect.TypeOf(uint32(0))
uint64_t = reflect.TypeOf(uint64(0))
int_t = reflect.TypeOf(int(0))
int8_t = reflect.TypeOf(int8(0))
int16_t = reflect.TypeOf(int16(0))
int32_t = reflect.TypeOf(int32(0))
int64_t = reflect.TypeOf(int64(0))
address_t = reflect.TypeOf(common.Address{})
int_ts = reflect.TypeOf([]int(nil))
int8_ts = reflect.TypeOf([]int8(nil))
int16_ts = reflect.TypeOf([]int16(nil))
int32_ts = reflect.TypeOf([]int32(nil))
int64_ts = reflect.TypeOf([]int64(nil))
bigT = reflect.TypeOf(&big.Int{})
derefbigT = reflect.TypeOf(big.Int{})
uint8T = reflect.TypeOf(uint8(0))
uint16T = reflect.TypeOf(uint16(0))
uint32T = reflect.TypeOf(uint32(0))
uint64T = reflect.TypeOf(uint64(0))
intT = reflect.TypeOf(int(0))
int8T = reflect.TypeOf(int8(0))
int16T = reflect.TypeOf(int16(0))
int32T = reflect.TypeOf(int32(0))
int64T = reflect.TypeOf(int64(0))
addressT = reflect.TypeOf(common.Address{})
intTS = reflect.TypeOf([]int(nil))
int8TS = reflect.TypeOf([]int8(nil))
int16TS = reflect.TypeOf([]int16(nil))
int32TS = reflect.TypeOf([]int32(nil))
int64TS = reflect.TypeOf([]int64(nil))
)
// U256 converts a big Int into a 256bit EVM number.
@@ -52,7 +52,7 @@ func U256(n *big.Int) []byte {
// checks whether the given reflect value is signed. This also works for slices with a number type
func isSigned(v reflect.Value) bool {
switch v.Type() {
case int_ts, int8_ts, int16_ts, int32_ts, int64_ts, int_t, int8_t, int16_t, int32_t, int64_t:
case intTS, int8TS, int16TS, int32TS, int64TS, intT, int8T, int16T, int32T, int64T:
return true
}
return false

View File

@@ -299,6 +299,11 @@ func TestPack(t *testing.T) {
[32]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"uint32[2][3][4]",
[4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018"),
},
{
"address[]",
[]common.Address{{1}, {2}},

View File

@@ -24,7 +24,7 @@ import (
// indirect recursively dereferences the value until it either gets the value
// or finds a big.Int
func indirect(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Ptr && v.Elem().Type() != derefbig_t {
if v.Kind() == reflect.Ptr && v.Elem().Type() != derefbigT {
return indirect(v.Elem())
}
return v
@@ -36,26 +36,26 @@ func reflectIntKindAndType(unsigned bool, size int) (reflect.Kind, reflect.Type)
switch size {
case 8:
if unsigned {
return reflect.Uint8, uint8_t
return reflect.Uint8, uint8T
}
return reflect.Int8, int8_t
return reflect.Int8, int8T
case 16:
if unsigned {
return reflect.Uint16, uint16_t
return reflect.Uint16, uint16T
}
return reflect.Int16, int16_t
return reflect.Int16, int16T
case 32:
if unsigned {
return reflect.Uint32, uint32_t
return reflect.Uint32, uint32T
}
return reflect.Int32, int32_t
return reflect.Int32, int32T
case 64:
if unsigned {
return reflect.Uint64, uint64_t
return reflect.Uint64, uint64T
}
return reflect.Int64, int64_t
return reflect.Int64, int64T
}
return reflect.Ptr, big_t
return reflect.Ptr, bigT
}
// mustArrayToBytesSlice creates a new byte slice with the exact same size as value
@@ -110,3 +110,19 @@ func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
}
return nil
}
// requireUniqueStructFieldNames makes sure field names don't collide
func requireUniqueStructFieldNames(args Arguments) error {
exists := make(map[string]bool)
for _, arg := range args {
field := capitalise(arg.Name)
if field == "" {
return fmt.Errorf("abi: purely underscored output cannot unpack to struct")
}
if exists[field] {
return fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", field)
}
exists[field] = true
}
return nil
}

View File

@@ -135,7 +135,7 @@ func NewType(t string) (typ Type, err error) {
typ.Type = reflect.TypeOf(bool(false))
case "address":
typ.Kind = reflect.Array
typ.Type = address_t
typ.Type = addressT
typ.Size = 20
typ.T = AddressTy
case "string":

View File

@@ -46,36 +46,36 @@ func TestTypeRegexp(t *testing.T) {
{"bool[2][2][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}},
{"bool[][][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}},
{"bool[][2][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][2][]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}},
{"int8", Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}},
{"int16", Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}},
{"int32", Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}},
{"int64", Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}},
{"int256", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}},
{"int8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
{"int8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
{"int16[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
{"int16[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
{"int32[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
{"int32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
{"int64[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
{"int64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
{"int256[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
{"int256[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
{"uint8", Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}},
{"uint16", Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}},
{"uint32", Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}},
{"uint64", Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}},
{"uint256", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}},
{"uint8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
{"uint8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
{"uint16[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
{"uint16[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
{"uint32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
{"uint32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
{"uint64[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
{"uint64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
{"uint256[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
{"uint256[2]", Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
{"int8", Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}},
{"int16", Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}},
{"int32", Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}},
{"int64", Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}},
{"int256", Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}},
{"int8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
{"int8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
{"int16[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
{"int16[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
{"int32[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
{"int32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
{"int64[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
{"int64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
{"int256[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
{"int256[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
{"uint8", Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}},
{"uint16", Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}},
{"uint32", Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}},
{"uint64", Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}},
{"uint256", Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}},
{"uint8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
{"uint8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
{"uint16[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
{"uint16[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
{"uint32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
{"uint32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
{"uint64[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
{"uint64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
{"uint256[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
{"uint256[2]", Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
{"bytes32", Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}},
{"bytes[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]byte{}), Elem: &Type{Kind: reflect.Slice, Type: reflect.TypeOf([]byte{}), T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
{"bytes[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]byte{}), Elem: &Type{T: BytesTy, Type: reflect.TypeOf([]byte{}), Kind: reflect.Slice, stringKind: "bytes"}, stringKind: "bytes[2]"}},
@@ -84,9 +84,9 @@ func TestTypeRegexp(t *testing.T) {
{"string", Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}},
{"string[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]string{}), Elem: &Type{Kind: reflect.String, Type: reflect.TypeOf(""), T: StringTy, stringKind: "string"}, stringKind: "string[]"}},
{"string[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]string{}), Elem: &Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}, stringKind: "string[2]"}},
{"address", Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}},
{"address[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
{"address[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
{"address", Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}},
{"address[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
{"address[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
// TODO when fixed types are implemented properly
// {"fixed", Type{}},
// {"fixed128x128", Type{}},
@@ -252,6 +252,9 @@ func TestTypeCheck(t *testing.T) {
{"bytes20", common.Address{}, ""},
{"address", [20]byte{}, ""},
{"address", common.Address{}, ""},
{"bytes32[]]", "", "invalid arg type in abi"},
{"invalidType", "", "unsupported arg type: invalidType"},
{"invalidSlice[]", "", "unsupported arg type: invalidSlice"},
} {
typ, err := NewType(test.typ)
if err != nil && len(test.err) == 0 {

View File

@@ -93,15 +93,28 @@ func readFixedBytes(t Type, word []byte) (interface{}, error) {
}
func getFullElemSize(elem *Type) int {
//all other should be counted as 32 (slices have pointers to respective elements)
size := 32
//arrays wrap it, each element being the same size
for elem.T == ArrayTy {
size *= elem.Size
elem = elem.Elem
}
return size
}
// iteratively unpack elements
func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
if size < 0 {
return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
}
if start+32*size > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
}
// this value will become our slice or our array, depending on the type
var refSlice reflect.Value
slice := output[start : start+size*32]
if t.T == SliceTy {
// declare our slice
@@ -113,15 +126,20 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
}
for i, j := start, 0; j*32 < len(slice); i, j = i+32, j+1 {
// this corrects the arrangement so that we get all the underlying array values
if t.Elem.T == ArrayTy && j != 0 {
i = start + t.Elem.Size*32*j
}
// Arrays have packed elements, resulting in longer unpack steps.
// Slices have just 32 bytes per element (pointing to the contents).
elemSize := 32
if t.T == ArrayTy {
elemSize = getFullElemSize(t.Elem)
}
for i, j := start, 0; j < size; i, j = i+elemSize, j+1 {
inter, err := toGoType(i, *t.Elem, output)
if err != nil {
return nil, err
}
// append the item to our reflect slice
refSlice.Index(j).Set(reflect.ValueOf(inter))
}
@@ -181,16 +199,32 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
// interprets a 32 byte slice as an offset and then determines which indice to look to decode the type.
func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
offset := int(binary.BigEndian.Uint64(output[index+24 : index+32]))
if offset+32 > len(output) {
return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
}
length = int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
if offset+32+length > len(output) {
return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+length)
}
start = offset + 32
bigOffsetEnd := big.NewInt(0).SetBytes(output[index : index+32])
bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
outputLength := big.NewInt(int64(len(output)))
//fmt.Printf("LENGTH PREFIX INFO: \nsize: %v\noffset: %v\nstart: %v\n", length, offset, start)
if bigOffsetEnd.Cmp(outputLength) > 0 {
return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", bigOffsetEnd, outputLength)
}
if bigOffsetEnd.BitLen() > 63 {
return 0, 0, fmt.Errorf("abi offset larger than int64: %v", bigOffsetEnd)
}
offsetEnd := int(bigOffsetEnd.Uint64())
lengthBig := big.NewInt(0).SetBytes(output[offsetEnd-32 : offsetEnd])
totalSize := big.NewInt(0)
totalSize.Add(totalSize, bigOffsetEnd)
totalSize.Add(totalSize, lengthBig)
if totalSize.BitLen() > 63 {
return 0, 0, fmt.Errorf("abi length larger than int64: %v", totalSize)
}
if totalSize.Cmp(outputLength) > 0 {
return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %v require %v", outputLength, totalSize)
}
start = int(bigOffsetEnd.Uint64())
length = int(lengthBig.Uint64())
return
}

View File

@@ -56,6 +56,23 @@ var unpackTests = []unpackTest{
enc: "0000000000000000000000000000000000000000000000000000000000000001",
want: true,
},
{
def: `[{ "type": "bool" }]`,
enc: "0000000000000000000000000000000000000000000000000000000000000000",
want: false,
},
{
def: `[{ "type": "bool" }]`,
enc: "0000000000000000000000000000000000000000000000000001000000000001",
want: false,
err: "abi: improperly encoded boolean value",
},
{
def: `[{ "type": "bool" }]`,
enc: "0000000000000000000000000000000000000000000000000000000000000003",
want: false,
err: "abi: improperly encoded boolean value",
},
{
def: `[{"type": "uint32"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000001",
@@ -130,7 +147,7 @@ var unpackTests = []unpackTest{
{
def: `[{"type": "bytes32"}]`,
enc: "0100000000000000000000000000000000000000000000000000000000000000",
want: common.HexToHash("0100000000000000000000000000000000000000000000000000000000000000"),
want: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
},
{
def: `[{"type": "function"}]`,
@@ -189,6 +206,11 @@ var unpackTests = []unpackTest{
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: [2]uint32{1, 2},
},
{
def: `[{"type": "uint32[2][3][4]"}]`,
enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018",
want: [4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
},
{
def: `[{"type": "uint64[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
@@ -435,6 +457,46 @@ func TestMultiReturnWithArray(t *testing.T) {
}
}
func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
// Similar to TestMultiReturnWithArray, but with a special case in mind:
// values of nested static arrays count towards the size as well, and any element following
// after such nested array argument should be read with the correct offset,
// so that it does not read content from the previous array argument.
const definition = `[{"name" : "multi", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
buff := new(bytes.Buffer)
// construct the test array, each 3 char element is joined with 61 '0' chars,
// to from the ((3 + 61) * 0.5) = 32 byte elements in the array.
buff.Write(common.Hex2Bytes(strings.Join([]string{
"", //empty, to apply the 61-char separator to the first element as well.
"111", "112", "113", "121", "122", "123",
"211", "212", "213", "221", "222", "223",
"311", "312", "313", "321", "322", "323",
"411", "412", "413", "421", "422", "423",
}, "0000000000000000000000000000000000000000000000000000000000000")))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000009876"))
ret1, ret1Exp := new([4][2][3]uint64), [4][2][3]uint64{
{{0x111, 0x112, 0x113}, {0x121, 0x122, 0x123}},
{{0x211, 0x212, 0x213}, {0x221, 0x222, 0x223}},
{{0x311, 0x312, 0x313}, {0x321, 0x322, 0x323}},
{{0x411, 0x412, 0x413}, {0x421, 0x422, 0x423}},
}
ret2, ret2Exp := new(uint64), uint64(0x9876)
if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*ret1, ret1Exp) {
t.Error("array result", *ret1, "!= Expected", ret1Exp)
}
if *ret2 != ret2Exp {
t.Error("int result", *ret2, "!= Expected", ret2Exp)
}
}
func TestUnmarshal(t *testing.T) {
const definition = `[
{ "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] },
@@ -683,3 +745,73 @@ func TestUnmarshal(t *testing.T) {
t.Fatal("expected error:", err)
}
}
func TestOOMMaliciousInput(t *testing.T) {
oomTests := []unpackTest{
{
def: `[{"type": "uint8[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
"0000000000000000000000000000000000000000000000000000000000000003" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
{ // Length larger than 64 bits
def: `[{"type": "uint8[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
"00ffffffffffffffffffffffffffffffffffffffffffffff0000000000000002" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
{ // Offset very large (over 64 bits)
def: `[{"type": "uint8[]"}]`,
enc: "00ffffffffffffffffffffffffffffffffffffffffffffff0000000000000020" + // offset
"0000000000000000000000000000000000000000000000000000000000000002" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
{ // Offset very large (below 64 bits)
def: `[{"type": "uint8[]"}]`,
enc: "0000000000000000000000000000000000000000000000007ffffffffff00020" + // offset
"0000000000000000000000000000000000000000000000000000000000000002" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
{ // Offset negative (as 64 bit)
def: `[{"type": "uint8[]"}]`,
enc: "000000000000000000000000000000000000000000000000f000000000000020" + // offset
"0000000000000000000000000000000000000000000000000000000000000002" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
{ // Negative length
def: `[{"type": "uint8[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
"000000000000000000000000000000000000000000000000f000000000000002" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
{ // Very large length
def: `[{"type": "uint8[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
"0000000000000000000000000000000000000000000000007fffffffff000002" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
}
for i, test := range oomTests {
def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
abi, err := JSON(strings.NewReader(def))
if err != nil {
t.Fatalf("invalid ABI definition %s: %v", def, err)
}
encb, err := hex.DecodeString(test.enc)
if err != nil {
t.Fatalf("invalid hex: %s" + test.enc)
}
_, err = abi.Methods["method"].Outputs.UnpackValues(encb)
if err == nil {
t.Fatalf("Expected error on malicious input, test %d", i)
}
}
}

View File

@@ -74,6 +74,22 @@ func (u URL) MarshalJSON() ([]byte, error) {
return json.Marshal(u.String())
}
// UnmarshalJSON parses url.
func (u *URL) UnmarshalJSON(input []byte) error {
var textUrl string
err := json.Unmarshal(input, &textUrl)
if err != nil {
return err
}
url, err := parseURL(textUrl)
if err != nil {
return err
}
u.Scheme = url.Scheme
u.Path = url.Path
return nil
}
// Cmp compares x and y and returns:
//
// -1 if x < y

View File

@@ -127,7 +127,7 @@ func (hub *Hub) refreshWallets() {
// breaking the Ledger protocol if that is waiting for user confirmation. This
// is a bug acknowledged at Ledger, but it won't be fixed on old devices so we
// need to prevent concurrent comms ourselves. The more elegant solution would
// be to ditch enumeration in favor of hutplug events, but that don't work yet
// be to ditch enumeration in favor of hotplug events, but that don't work yet
// on Windows so if we need to hack it anyway, this is more elegant for now.
hub.commsLock.Lock()
if hub.commsPend > 0 { // A confirmation is pending, don't refresh

View File

@@ -99,7 +99,7 @@ type wallet struct {
//
// As such, a hardware wallet needs two locks to function correctly. A state
// lock can be used to protect the wallet's software-side internal state, which
// must not be held exlusively during hardware communication. A communication
// must not be held exclusively during hardware communication. A communication
// lock can be used to achieve exclusive access to the device itself, this one
// however should allow "skipping" waiting for operations that might want to
// use the device, but can live without too (e.g. account self-derivation).

View File

@@ -23,8 +23,8 @@ environment:
install:
- git submodule update --init
- rmdir C:\go /s /q
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.9.2.windows-%GETH_ARCH%.zip
- 7z x go1.9.2.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.10.1.windows-%GETH_ARCH%.zip
- 7z x go1.10.1.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
- go version
- gcc --version

View File

@@ -75,7 +75,7 @@ type Hasher struct {
blocksize int // segment size (size of hash) also for hash.Hash
count int // segment count
size int // for hash.Hash same as hashsize
cur int // cursor position for righmost currently open chunk
cur int // cursor position for rightmost currently open chunk
segment []byte // the rightmost open segment (not complete)
depth int // index of last level
result chan []byte // result channel
@@ -149,7 +149,7 @@ func NewTreePool(hasher BaseHasher, segmentCount, capacity int) *TreePool {
}
}
// Drain drains the pool uptil it has no more than n resources
// Drain drains the pool until it has no more than n resources
func (self *TreePool) Drain(n int) {
self.lock.Lock()
defer self.lock.Unlock()
@@ -412,11 +412,10 @@ func (self *Hasher) Reset() {
// ResetWithLength needs to be called before writing to the hasher
// the argument is supposed to be the byte slice binary representation of
// the legth of the data subsumed under the hash
// the length of the data subsumed under the hash
func (self *Hasher) ResetWithLength(l []byte) {
self.Reset()
self.blockLength = l
}
// Release gives back the Tree to the pool whereby it unlocks
@@ -531,7 +530,7 @@ func (self *Hasher) finalise(n *Node, i int) (d int) {
for {
// when the final segment's path is going via left segments
// the incoming data is pushed to the parent upon pulling the left
// we do not need toogle the state since this condition is
// we do not need toggle the state since this condition is
// detectable
n.unbalanced = isLeft
n.right = nil

View File

@@ -2,12 +2,7 @@
Tagged releases and develop branch commits are available as installable Debian packages
for Ubuntu. Packages are built for the all Ubuntu versions which are supported by
Canonical:
- Trusty Tahr (14.04 LTS)
- Xenial Xerus (16.04 LTS)
- Yakkety Yak (16.10)
- Zesty Zapus (17.04)
Canonical.
Packages of develop branch commits have suffix -unstable and cannot be installed alongside
the stable version. Switching between release streams requires user intervention.
@@ -21,18 +16,18 @@ variable which Travis CI makes available to certain builds.
We want to build go-ethereum with the most recent version of Go, irrespective of the Go
version that is available in the main Ubuntu repository. In order to make this possible,
our PPA depends on the ~gophers/ubuntu/archive PPA. Our source package build-depends on
golang-1.9, which is co-installable alongside the regular golang package. PPA dependencies
golang-1.10, which is co-installable alongside the regular golang package. PPA dependencies
can be edited at https://launchpad.net/%7Eethereum/+archive/ubuntu/ethereum/+edit-dependencies
## Building Packages Locally (for testing)
You need to run Ubuntu to do test packaging.
Add the gophers PPA and install Go 1.9 and Debian packaging tools:
Add the gophers PPA and install Go 1.10 and Debian packaging tools:
$ sudo apt-add-repository ppa:gophers/ubuntu/archive
$ sudo apt-get update
$ sudo apt-get install build-essential golang-1.9 devscripts debhelper
$ sudo apt-get install build-essential golang-1.10 devscripts debhelper
Create the source packages:

View File

@@ -182,13 +182,13 @@ func doInstall(cmdline []string) {
// Check Go version. People regularly open issues about compilation
// failure with outdated Go. This should save them the trouble.
if !strings.Contains(runtime.Version(), "devel") {
// Figure out the minor version number since we can't textually compare (1.10 < 1.7)
// Figure out the minor version number since we can't textually compare (1.10 < 1.9)
var minor int
fmt.Sscanf(strings.TrimPrefix(runtime.Version(), "go1."), "%d", &minor)
if minor < 7 {
if minor < 9 {
log.Println("You have Go version", runtime.Version())
log.Println("go-ethereum requires at least Go version 1.7 and cannot")
log.Println("go-ethereum requires at least Go version 1.9 and cannot")
log.Println("be compiled with an earlier version. Please upgrade your Go installation.")
os.Exit(1)
}
@@ -262,16 +262,6 @@ func goTool(subcmd string, args ...string) *exec.Cmd {
func goToolArch(arch string, cc string, subcmd string, args ...string) *exec.Cmd {
cmd := build.GoTool(subcmd, args...)
if subcmd == "build" || subcmd == "install" || subcmd == "test" {
// Go CGO has a Windows linker error prior to 1.8 (https://github.com/golang/go/issues/8756).
// Work around issue by allowing multiple definitions for <1.8 builds.
var minor int
fmt.Sscanf(strings.TrimPrefix(runtime.Version(), "go1."), "%d", &minor)
if runtime.GOOS == "windows" && minor < 8 {
cmd.Args = append(cmd.Args, []string{"-ldflags", "-extldflags -Wl,--allow-multiple-definition"}...)
}
}
cmd.Env = []string{"GOPATH=" + build.GOPATH()}
if arch == "" || arch == runtime.GOARCH {
cmd.Env = append(cmd.Env, "GOBIN="+GOBIN)
@@ -736,7 +726,7 @@ func doAndroidArchive(cmdline []string) {
log.Fatal("Please ensure ANDROID_NDK points to your Android NDK")
}
// Build the Android archive and Maven resources
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile"))
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("bind", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
@@ -776,7 +766,7 @@ func doAndroidArchive(cmdline []string) {
if meta.Develop {
repo = *deploy + "/content/repositories/snapshots"
}
build.MustRunCommand("mvn", "gpg:sign-and-deploy-file",
build.MustRunCommand("mvn", "gpg:sign-and-deploy-file", "-e", "-X",
"-settings=build/mvn.settings", "-Durl="+repo, "-DrepositoryId=ossrh",
"-DpomFile="+meta.Package+".pom", "-Dfile="+meta.Package+".aar")
}
@@ -787,9 +777,10 @@ func gomobileTool(subcmd string, args ...string) *exec.Cmd {
cmd.Args = append(cmd.Args, args...)
cmd.Env = []string{
"GOPATH=" + build.GOPATH(),
"PATH=" + GOBIN + string(os.PathListSeparator) + os.Getenv("PATH"),
}
for _, e := range os.Environ() {
if strings.HasPrefix(e, "GOPATH=") {
if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "PATH=") {
continue
}
cmd.Env = append(cmd.Env, e)
@@ -856,7 +847,7 @@ func doXCodeFramework(cmdline []string) {
env := build.Env()
// Build the iOS XCode framework
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile"))
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind"))
build.MustRun(gomobileTool("init"))
bind := gomobileTool("bind", "--target", "ios", "--tags", "ios", "-v", "github.com/ethereum/go-ethereum/mobile")

View File

@@ -2,7 +2,7 @@ Source: {{.Name}}
Section: science
Priority: extra
Maintainer: {{.Author}}
Build-Depends: debhelper (>= 8.0.0), golang-1.9
Build-Depends: debhelper (>= 8.0.0), golang-1.10
Standards-Version: 3.9.5
Homepage: https://ethereum.org
Vcs-Git: git://github.com/ethereum/go-ethereum.git

View File

@@ -5,7 +5,7 @@
#export DH_VERBOSE=1
override_dh_auto_build:
build/env.sh /usr/lib/go-1.9/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
build/env.sh /usr/lib/go-1.10/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
override_dh_auto_test:

1
cmd/clef/4byte.json Normal file

File diff suppressed because one or more lines are too long

864
cmd/clef/README.md Normal file
View File

@@ -0,0 +1,864 @@
Clef
----
Clef can be used to sign transactions and data and is meant as a replacement for geth's account management.
This allows DApps not to depend on geth's account management. When a DApp wants to sign data it can send the data to
the signer, the signer will then provide the user with context and asks the user for permission to sign the data. If
the users grants the signing request the signer will send the signature back to the DApp.
This setup allows a DApp to connect to a remote Ethereum node and send transactions that are locally signed. This can
help in situations when a DApp is connected to a remote node because a local Ethereum node is not available, not
synchronised with the chain or a particular Ethereum node that has no built-in (or limited) account management.
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.
## Command line flags
Clef accepts the following command line options:
```
COMMANDS:
init Initialize the signer, generate secret storage
attest Attest that a js-file is to be used
addpw Store a credential for a keystore file
help Shows a list of commands or help for one command
GLOBAL OPTIONS:
--loglevel value log level to emit to the screen (default: 4)
--keystore value Directory for the keystore (default: "$HOME/.ethereum/keystore")
--configdir value Directory for clef configuration (default: "$HOME/.clef")
--networkid value Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby) (default: 1)
--lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength
--nousb Disables monitoring for and managing USB hardware wallets
--rpcaddr value HTTP-RPC server listening interface (default: "localhost")
--rpcport value HTTP-RPC server listening port (default: 8550)
--signersecret value A file containing the password used to encrypt signer credentials, e.g. keystore credentials and ruleset hash
--4bytedb value File containing 4byte-identifiers (default: "./4byte.json")
--4bytedb-custom value File used for writing new 4byte-identifiers submitted via API (default: "./4byte-custom.json")
--auditlog value File used to emit audit logs. Set to "" to disable (default: "audit.log")
--rules value Enable rule-engine (default: "rules.json")
--stdio-ui Use STDIN/STDOUT as a channel for an external UI. This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user interface, and can be used when the signer is started by an external process.
--stdio-ui-test Mechanism to test interface between signer and UI. Requires 'stdio-ui'.
--help, -h show help
--version, -v print the version
```
Example:
```
signer -keystore /my/keystore -chainid 4
```
Check out the [tutorial](tutorial.md) for some concrete examples on how the signer works.
## Security model
The security model of the signer is as follows:
* One critical component (the signer binary / daemon) is responsible for handling cryptographic operations: signing, private keys, encryption/decryption of keystore files.
* The signer binary has a well-defined 'external' API.
* The 'external' API is considered UNTRUSTED.
* The signer binary also communicates with whatever process that invoked the binary, via stdin/stdout.
* This channel is considered 'trusted'. Over this channel, approvals and passwords are communicated.
The general flow for signing a transaction using e.g. geth is as follows:
![image](sign_flow.png)
In this case, `geth` would be started with `--externalsigner=http://localhost:8550` and would relay requests to `eth.sendTransaction`.
## TODOs
Some snags and todos
* [ ] The signer should take a startup param "--no-change", for UIs that do not contain the capability
to perform changes to things, only approve/deny. Such a UI should be able to start the signer in
a more secure mode by telling it that it only wants approve/deny capabilities.
* [x] It would be nice if the signer could collect new 4byte-id:s/method selectors, and have a
secondary database for those (`4byte_custom.json`). Users could then (optionally) submit their collections for
inclusion upstream.
* It should be possible to configure the signer to check if an account is indeed known to it, before
passing on to the UI. The reason it currently does not, is that it would make it possible to enumerate
accounts if it immediately returned "unknown account".
* [x] It should be possible to configure the signer to auto-allow listing (certain) accounts, instead of asking every time.
* [x] Done Upon startup, the signer should spit out some info to the caller (particularly important when executed in `stdio-ui`-mode),
invoking methods with the following info:
* [x] Version info about the signer
* [x] Address of API (http/ipc)
* [ ] List of known accounts
* [ ] Have a default timeout on signing operations, so that if the user has not answered withing e.g. 60 seconds, the request is rejected.
* [ ] `account_signRawTransaction`
* [ ] `account_bulkSignTransactions([] transactions)` should
* only exist if enabled via config/flag
* only allow non-data-sending transactions
* all txs must use the same `from`-account
* let the user confirm, showing
* the total amount
* the number of unique recipients
* Geth todos
- The signer should pass the `Origin` header as call-info to the UI. As of right now, the way that info about the request is
put together is a bit of a hack into the http server. This could probably be greatly improved
- Relay: Geth should be started in `geth --external_signer localhost:8550`.
- Currently, the Geth APIs use `common.Address` in the arguments to transaction submission (e.g `to` field). This
type is 20 `bytes`, and is incapable of carrying checksum information. The signer uses `common.MixedcaseAddress`, which
retains the original input.
- The Geth api should switch to use the same type, and relay `to`-account verbatim to the external api.
* [x] Storage
* [x] An encrypted key-value storage should be implemented
* See [rules.md](rules.md) for more info about this.
* Another potential thing to introduce is pairing.
* To prevent spurious requests which users just accept, implement a way to "pair" the caller with the signer (external API).
* Thus geth/mist/cpp would cryptographically handshake and afterwards the caller would be allowed to make signing requests.
* This feature would make the addition of rules less dangerous.
* Wallets / accounts. Add API methods for wallets.
## Communication
### External API
The signer listens to HTTP requests on `rpcaddr`:`rpcport`, with the same JSONRPC standard as Geth. The messages are
expected to be JSON [jsonrpc 2.0 standard](http://www.jsonrpc.org/specification).
Some of these call can require user interaction. Clients must be aware that responses
may be delayed significanlty or may never be received if a users decides to ignore the confirmation request.
The External API is **untrusted** : it does not accept credentials over this api, nor does it expect
that requests have any authority.
### UI API
The signer has one native console-based UI, for operation without any standalone tools.
However, there is also an API to communicate with an external UI. To enable that UI,
the signer needs to be executed with the `--stdio-ui` option, which allocates the
`stdin`/`stdout` for the UI-api.
An example (insecure) proof-of-concept of has been implemented in `pythonsigner.py`.
The model is as follows:
* The user starts the UI app (`pythonsigner.py`).
* The UI app starts the `signer` with `--stdio-ui`, and listens to the
process output for confirmation-requests.
* The `signer` opens the external http api.
* When the `signer` receives requests, it sends a `jsonrpc` request via `stdout`.
* The UI app prompts the user accordingly, and responds to the `signer`
* The `signer` signs (or not), and responds to the original request.
## External API
See the [external api changelog](extapi_changelog.md) for information about changes to this API.
### Encoding
- number: positive integers that are hex encoded
- data: hex encoded data
- string: ASCII string
All hex encoded values must be prefixed with `0x`.
## Methods
### account_new
#### Create new password protected account
The signer will generate a new private key, encrypts it according to [web3 keystore spec](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) and stores it in the keystore directory.
The client is responsible for creating a backup of the keystore. If the keystore is lost there is no method of retrieving lost accounts.
#### Arguments
None
#### Result
- address [string]: account address that is derived from the generated key
- url [string]: location of the keyfile
#### Sample call
```json
{
"id": 0,
"jsonrpc": "2.0",
"method": "account_new",
"params": []
}
{
"id": 0,
"jsonrpc": "2.0",
"result": {
"address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133",
"url": "keystore:///my/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133"
}
}
```
### account_list
#### List available accounts
List all accounts that this signer currently manages
#### Arguments
None
#### Result
- array with account records:
- account.address [string]: account address that is derived from the generated key
- account.type [string]: type of the
- account.url [string]: location of the account
#### Sample call
```json
{
"id": 1,
"jsonrpc": "2.0",
"method": "account_list"
}
{
"id": 1,
"jsonrpc": "2.0",
"result": [
{
"address": "0xafb2f771f58513609765698f65d3f2f0224a956f",
"type": "account",
"url": "keystore:///tmp/keystore/UTC--2017-08-24T07-26-47.162109726Z--afb2f771f58513609765698f65d3f2f0224a956f"
},
{
"address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133",
"type": "account",
"url": "keystore:///tmp/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133"
}
]
}
```
### account_signTransaction
#### Sign transactions
Signs a transactions and responds with the signed transaction in RLP encoded form.
#### Arguments
2. transaction object:
- `from` [address]: account to send the transaction from
- `to` [address]: receiver account. If omitted or `0x`, will cause contract creation.
- `gas` [number]: maximum amount of gas to burn
- `gasPrice` [number]: gas price
- `value` [number:optional]: amount of Wei to send with the transaction
- `data` [data:optional]: input data
- `nonce` [number]: account nonce
3. method signature [string:optional]
- The method signature, if present, is to aid decoding the calldata. Should consist of `methodname(paramtype,...)`, e.g. `transfer(uint256,address)`. The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected.
#### Result
- signed transaction in RLP encoded form [data]
#### Sample call
```json
{
"id": 2,
"jsonrpc": "2.0",
"method": "account_signTransaction",
"params": [
{
"from": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
"gas": "0x55555",
"gasPrice": "0x1234",
"input": "0xabcd",
"nonce": "0x0",
"to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
"value": "0x1234"
}
]
}
```
Response
```json
{
"jsonrpc": "2.0",
"id": 67,
"error": {
"code": -32000,
"message": "Request denied"
}
}
```
#### Sample call with ABI-data
```json
{
"jsonrpc": "2.0",
"method": "account_signTransaction",
"params": [
{
"from": "0x694267f14675d7e1b9494fd8d72fefe1755710fa",
"gas": "0x333",
"gasPrice": "0x1",
"nonce": "0x0",
"to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
"value": "0x0",
"data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"
},
"safeSend(address)"
],
"id": 67
}
```
Response
```json
{
"jsonrpc": "2.0",
"id": 67,
"result": {
"raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
"tx": {
"nonce": "0x0",
"gasPrice": "0x1",
"gas": "0x333",
"to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
"value": "0x0",
"input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012",
"v": "0x26",
"r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",
"s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
"hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"
}
}
}
```
Bash example:
```bash
#curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
{"jsonrpc":"2.0","id":67,"result":{"raw":"0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","tx":{"nonce":"0x0","gasPrice":"0x1","gas":"0x333","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0","value":"0x0","input":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012","v":"0x26","r":"0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e","s":"0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","hash":"0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"}}}
```
### account_sign
#### Sign data
Signs a chunk of data and returns the calculated signature.
#### Arguments
- account [address]: account to sign with
- data [data]: data to sign
#### Result
- calculated signature [data]
#### Sample call
```json
{
"id": 3,
"jsonrpc": "2.0",
"method": "account_sign",
"params": [
"0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
"0xaabbccdd"
]
}
```
Response
```json
{
"id": 3,
"jsonrpc": "2.0",
"result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
}
```
### account_ecRecover
#### Recover address
Derive the address from the account that was used to sign data from the data and signature.
#### Arguments
- data [data]: data that was signed
- signature [data]: the signature to verify
#### Result
- derived account [address]
#### Sample call
```json
{
"id": 4,
"jsonrpc": "2.0",
"method": "account_ecRecover",
"params": [
"0xaabbccdd",
"0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
]
}
```
Response
```json
{
"id": 4,
"jsonrpc": "2.0",
"result": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db"
}
```
### account_import
#### Import account
Import a private key into the keystore. The imported key is expected to be encrypted according to the web3 keystore
format.
#### Arguments
- account [object]: key in [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) (retrieved with account_export)
#### Result
- imported key [object]:
- key.address [address]: address of the imported key
- key.type [string]: type of the account
- key.url [string]: key URL
#### Sample call
```json
{
"id": 6,
"jsonrpc": "2.0",
"method": "account_import",
"params": [
{
"address": "c7412fc59930fd90099c917a50e5f11d0934b2f5",
"crypto": {
"cipher": "aes-128-ctr",
"cipherparams": {
"iv": "401c39a7c7af0388491c3d3ecb39f532"
},
"ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204",
"kdf": "scrypt",
"kdfparams": {
"dklen": 32,
"n": 262144,
"p": 1,
"r": 8,
"salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a"
},
"mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806"
},
"id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9",
"version": 3
},
]
}
```
Response
```json
{
"id": 6,
"jsonrpc": "2.0",
"result": {
"address": "0xc7412fc59930fd90099c917a50e5f11d0934b2f5",
"type": "account",
"url": "keystore:///tmp/keystore/UTC--2017-08-24T11-00-42.032024108Z--c7412fc59930fd90099c917a50e5f11d0934b2f5"
}
}
```
### account_export
#### Export account from keystore
Export a private key from the keystore. The exported private key is encrypted with the original passphrase. When the
key is imported later this passphrase is required.
#### Arguments
- account [address]: export private key that is associated with this account
#### Result
- exported key, see [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) for
more information
#### Sample call
```json
{
"id": 5,
"jsonrpc": "2.0",
"method": "account_export",
"params": [
"0xc7412fc59930fd90099c917a50e5f11d0934b2f5"
]
}
```
Response
```json
{
"id": 5,
"jsonrpc": "2.0",
"result": {
"address": "c7412fc59930fd90099c917a50e5f11d0934b2f5",
"crypto": {
"cipher": "aes-128-ctr",
"cipherparams": {
"iv": "401c39a7c7af0388491c3d3ecb39f532"
},
"ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204",
"kdf": "scrypt",
"kdfparams": {
"dklen": 32,
"n": 262144,
"p": 1,
"r": 8,
"salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a"
},
"mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806"
},
"id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9",
"version": 3
}
}
```
## UI API
These methods needs to be implemented by a UI listener.
By starting the signer with the switch `--stdio-ui-test`, the signer will invoke all known methods, and expect the UI to respond with
denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented.
See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'.
All methods in this API uses object-based parameters, so that there can be no mixups of parameters: each piece of data is accessed by key.
See the [ui api changelog](intapi_changelog.md) for information about changes to this API.
OBS! A slight deviation from `json` standard is in place: every request and response should be confined to a single line.
Whereas the `json` specification allows for linebreaks, linebreaks __should not__ be used in this communication channel, to make
things simpler for both parties.
### ApproveTx
Invoked when there's a transaction for approval.
#### Sample call
Here's a method invocation:
```bash
curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
```
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "ApproveTx",
"params": [
{
"transaction": {
"from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa",
"to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
"gas": "0x333",
"gasPrice": "0x1",
"value": "0x0",
"nonce": "0x0",
"data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012",
"input": null
},
"call_info": [
{
"type": "WARNING",
"message": "Invalid checksum on to-address"
},
{
"type": "Info",
"message": "safeSend(address: 0x0000000000000000000000000000000000000012)"
}
],
"meta": {
"remote": "127.0.0.1:48486",
"local": "localhost:8550",
"scheme": "HTTP/1.1"
}
}
]
}
```
The same method invocation, but with invalid data:
```bash
curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000002000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
```
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "ApproveTx",
"params": [
{
"transaction": {
"from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa",
"to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
"gas": "0x333",
"gasPrice": "0x1",
"value": "0x0",
"nonce": "0x0",
"data": "0x4401a6e40000000000000002000000000000000000000000000000000000000000000012",
"input": null
},
"call_info": [
{
"type": "WARNING",
"message": "Invalid checksum on to-address"
},
{
"type": "WARNING",
"message": "Transaction data did not match ABI-interface: WARNING: Supplied data is stuffed with extra data. \nWant 0000000000000002000000000000000000000000000000000000000000000012\nHave 0000000000000000000000000000000000000000000000000000000000000012\nfor method safeSend(address)"
}
],
"meta": {
"remote": "127.0.0.1:48492",
"local": "localhost:8550",
"scheme": "HTTP/1.1"
}
}
]
}
```
One which has missing `to`, but with no `data`:
```json
{
"jsonrpc": "2.0",
"id": 3,
"method": "ApproveTx",
"params": [
{
"transaction": {
"from": "",
"to": null,
"gas": "0x0",
"gasPrice": "0x0",
"value": "0x0",
"nonce": "0x0",
"data": null,
"input": null
},
"call_info": [
{
"type": "CRITICAL",
"message": "Tx will create contract with empty code!"
}
],
"meta": {
"remote": "signer binary",
"local": "main",
"scheme": "in-proc"
}
}
]
}
```
### ApproveExport
Invoked when a request to export an account has been made.
#### Sample call
```json
{
"jsonrpc": "2.0",
"id": 7,
"method": "ApproveExport",
"params": [
{
"address": "0x0000000000000000000000000000000000000000",
"meta": {
"remote": "signer binary",
"local": "main",
"scheme": "in-proc"
}
}
]
}
```
### ApproveListing
Invoked when a request for account listing has been made.
#### Sample call
```json
{
"jsonrpc": "2.0",
"id": 5,
"method": "ApproveListing",
"params": [
{
"accounts": [
{
"type": "Account",
"url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-20T14-44-54.089682944Z--123409812340981234098123409812deadbeef42",
"address": "0x123409812340981234098123409812deadbeef42"
},
{
"type": "Account",
"url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-23T21-59-03.199240693Z--cafebabedeadbeef34098123409812deadbeef42",
"address": "0xcafebabedeadbeef34098123409812deadbeef42"
}
],
"meta": {
"remote": "signer binary",
"local": "main",
"scheme": "in-proc"
}
}
]
}
```
### ApproveSignData
#### Sample call
```json
{
"jsonrpc": "2.0",
"id": 4,
"method": "ApproveSignData",
"params": [
{
"address": "0x123409812340981234098123409812deadbeef42",
"raw_data": "0x01020304",
"message": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004",
"hash": "0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310",
"meta": {
"remote": "signer binary",
"local": "main",
"scheme": "in-proc"
}
}
]
}
```
### ShowInfo
The UI should show the info to the user. Does not expect response.
#### Sample call
```json
{
"jsonrpc": "2.0",
"id": 9,
"method": "ShowInfo",
"params": [
{
"text": "Tests completed"
}
]
}
```
### ShowError
The UI should show the info to the user. Does not expect response.
```json
{
"jsonrpc": "2.0",
"id": 2,
"method": "ShowError",
"params": [
{
"text": "Testing 'ShowError'"
}
]
}
```
### OnApproved
`OnApprovedTx` is called when a transaction has been approved and signed. The call contains the return value that will be sent to the external caller. The return value from this method is ignored - the reason for having this callback is to allow the ruleset to keep track of approved transactions.
When implementing rate-limited rules, this callback should be used.
TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`.
### OnSignerStartup
This method provide the UI with information about what API version the signer uses (both internal and external) aswell as build-info and external api,
in k/v-form.
Example call:
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "OnSignerStartup",
"params": [
{
"info": {
"extapi_http": "http://localhost:8550",
"extapi_ipc": null,
"extapi_version": "2.0.0",
"intapi_version": "1.2.0"
}
}
]
}
```
### Rules for UI apis
A UI should conform to the following rules.
* A UI MUST NOT load any external resources that were not embedded/part of the UI package.
* For example, not load icons, stylesheets from the internet
* Not load files from the filesystem, unless they reside in the same local directory (e.g. config files)
* A Graphical UI MUST show the blocky-identicon for ethereum addresses.
* A UI MUST warn display approproate warning if the destination-account is formatted with invalid checksum.
* A UI MUST NOT open any ports or services
* The signer opens the public port
* A UI SHOULD verify the permissions on the signer binary, and refuse to execute or warn if permissions allow non-user write.
* A UI SHOULD inform the user about the `SHA256` or `MD5` hash of the binary being executed
* A UI SHOULD NOT maintain a secondary storage of data, e.g. list of accounts
* The signer provides accounts
* A UI SHOULD, to the best extent possible, use static linking / bundling, so that requried libraries are bundled
along with the UI.

View File

@@ -0,0 +1,25 @@
### Changelog for external API
#### 2.0.0
* Commit `73abaf04b1372fa4c43201fb1b8019fe6b0a6f8d`, move `from` into `transaction` object in `signTransaction`. This
makes the `accounts_signTransaction` identical to the old `eth_signTransaction`.
#### 1.0.0
Initial release.
### Versioning
The API uses [semantic versioning](https://semver.org/).
TLDR; Given a version number MAJOR.MINOR.PATCH, increment the:
* MAJOR version when you make incompatible API changes,
* MINOR version when you add functionality in a backwards-compatible manner, and
* PATCH version when you make backwards-compatible bug fixes.
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

View File

@@ -0,0 +1,86 @@
### Changelog for internal API (ui-api)
### 2.0.0
* Modify how `call_info` on a transaction is conveyed. New format:
```
{
"jsonrpc": "2.0",
"id": 2,
"method": "ApproveTx",
"params": [
{
"transaction": {
"from": "0x82A2A876D39022B3019932D30Cd9c97ad5616813",
"to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
"gas": "0x333",
"gasPrice": "0x123",
"value": "0x10",
"nonce": "0x0",
"data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012",
"input": null
},
"call_info": [
{
"type": "WARNING",
"message": "Invalid checksum on to-address"
},
{
"type": "WARNING",
"message": "Tx contains data, but provided ABI signature could not be matched: Did not match: test (0 matches)"
}
],
"meta": {
"remote": "127.0.0.1:54286",
"local": "localhost:8550",
"scheme": "HTTP/1.1"
}
}
]
}
```
#### 1.2.0
* Add `OnStartup` method, to provide the UI with information about what API version
the signer uses (both internal and external) aswell as build-info and external api.
Example call:
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "OnSignerStartup",
"params": [
{
"info": {
"extapi_http": "http://localhost:8550",
"extapi_ipc": null,
"extapi_version": "2.0.0",
"intapi_version": "1.2.0"
}
}
]
}
```
#### 1.1.0
* Add `OnApproved` method
#### 1.0.0
Initial release.
### Versioning
The API uses [semantic versioning](https://semver.org/).
TLDR; Given a version number MAJOR.MINOR.PATCH, increment the:
* MAJOR version when you make incompatible API changes,
* MINOR version when you add functionality in a backwards-compatible manner, and
* PATCH version when you make backwards-compatible bug fixes.
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

640
cmd/clef/main.go Normal file
View File

@@ -0,0 +1,640 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// signer is a utility that can be used so sign transactions and
// arbitrary data.
package main
import (
"bufio"
"context"
"crypto/rand"
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"runtime"
"strings"
"encoding/hex"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/signer/core"
"github.com/ethereum/go-ethereum/signer/rules"
"github.com/ethereum/go-ethereum/signer/storage"
"gopkg.in/urfave/cli.v1"
"os/signal"
)
// ExternalApiVersion -- see extapi_changelog.md
const ExternalApiVersion = "2.0.0"
// InternalApiVersion -- see intapi_changelog.md
const InternalApiVersion = "2.0.0"
const legalWarning = `
WARNING!
Clef is alpha software, and not yet publically released. This software has _not_ been audited, and there
are no guarantees about the workings of this software. It may contain severe flaws. You should not use this software
unless you agree to take full responsibility for doing so, and know what you are doing.
TLDR; THIS IS NOT PRODUCTION-READY SOFTWARE!
`
var (
logLevelFlag = cli.IntFlag{
Name: "loglevel",
Value: 4,
Usage: "log level to emit to the screen",
}
keystoreFlag = cli.StringFlag{
Name: "keystore",
Value: filepath.Join(node.DefaultDataDir(), "keystore"),
Usage: "Directory for the keystore",
}
configdirFlag = cli.StringFlag{
Name: "configdir",
Value: DefaultConfigDir(),
Usage: "Directory for Clef configuration",
}
rpcPortFlag = cli.IntFlag{
Name: "rpcport",
Usage: "HTTP-RPC server listening port",
Value: node.DefaultHTTPPort + 5,
}
signerSecretFlag = cli.StringFlag{
Name: "signersecret",
Usage: "A file containing the password used to encrypt Clef credentials, e.g. keystore credentials and ruleset hash",
}
dBFlag = cli.StringFlag{
Name: "4bytedb",
Usage: "File containing 4byte-identifiers",
Value: "./4byte.json",
}
customDBFlag = cli.StringFlag{
Name: "4bytedb-custom",
Usage: "File used for writing new 4byte-identifiers submitted via API",
Value: "./4byte-custom.json",
}
auditLogFlag = cli.StringFlag{
Name: "auditlog",
Usage: "File used to emit audit logs. Set to \"\" to disable",
Value: "audit.log",
}
ruleFlag = cli.StringFlag{
Name: "rules",
Usage: "Enable rule-engine",
Value: "rules.json",
}
stdiouiFlag = cli.BoolFlag{
Name: "stdio-ui",
Usage: "Use STDIN/STDOUT as a channel for an external UI. " +
"This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user " +
"interface, and can be used when Clef is started by an external process.",
}
testFlag = cli.BoolFlag{
Name: "stdio-ui-test",
Usage: "Mechanism to test interface between Clef and UI. Requires 'stdio-ui'.",
}
app = cli.NewApp()
initCommand = cli.Command{
Action: utils.MigrateFlags(initializeSecrets),
Name: "init",
Usage: "Initialize the signer, generate secret storage",
ArgsUsage: "",
Flags: []cli.Flag{
logLevelFlag,
configdirFlag,
},
Description: `
The init command generates a master seed which Clef can use to store credentials and data needed for
the rule-engine to work.`,
}
attestCommand = cli.Command{
Action: utils.MigrateFlags(attestFile),
Name: "attest",
Usage: "Attest that a js-file is to be used",
ArgsUsage: "<sha256sum>",
Flags: []cli.Flag{
logLevelFlag,
configdirFlag,
signerSecretFlag,
},
Description: `
The attest command stores the sha256 of the rule.js-file that you want to use for automatic processing of
incoming requests.
Whenever you make an edit to the rule file, you need to use attestation to tell
Clef that the file is 'safe' to execute.`,
}
addCredentialCommand = cli.Command{
Action: utils.MigrateFlags(addCredential),
Name: "addpw",
Usage: "Store a credential for a keystore file",
ArgsUsage: "<address> <password>",
Flags: []cli.Flag{
logLevelFlag,
configdirFlag,
signerSecretFlag,
},
Description: `
The addpw command stores a password for a given address (keyfile). If you invoke it with only one parameter, it will
remove any stored credential for that address (keyfile)
`,
}
)
func init() {
app.Name = "Clef"
app.Usage = "Manage Ethereum account operations"
app.Flags = []cli.Flag{
logLevelFlag,
keystoreFlag,
configdirFlag,
utils.NetworkIdFlag,
utils.LightKDFFlag,
utils.NoUSBFlag,
utils.RPCListenAddrFlag,
utils.RPCVirtualHostsFlag,
utils.IPCDisabledFlag,
utils.IPCPathFlag,
utils.RPCEnabledFlag,
rpcPortFlag,
signerSecretFlag,
dBFlag,
customDBFlag,
auditLogFlag,
ruleFlag,
stdiouiFlag,
testFlag,
}
app.Action = signer
app.Commands = []cli.Command{initCommand, attestCommand, addCredentialCommand}
}
func main() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func initializeSecrets(c *cli.Context) error {
if err := initialize(c); err != nil {
return err
}
configDir := c.String(configdirFlag.Name)
masterSeed := make([]byte, 256)
n, err := io.ReadFull(rand.Reader, masterSeed)
if err != nil {
return err
}
if n != len(masterSeed) {
return fmt.Errorf("failed to read enough random")
}
err = os.Mkdir(configDir, 0700)
if err != nil && !os.IsExist(err) {
return err
}
location := filepath.Join(configDir, "secrets.dat")
if _, err := os.Stat(location); err == nil {
return fmt.Errorf("file %v already exists, will not overwrite", location)
}
err = ioutil.WriteFile(location, masterSeed, 0700)
if err != nil {
return err
}
fmt.Printf("A master seed has been generated into %s\n", location)
fmt.Printf(`
This is required to be able to store credentials, such as :
* Passwords for keystores (used by rule engine)
* Storage for javascript rules
* Hash of rule-file
You should treat that file with utmost secrecy, and make a backup of it.
NOTE: This file does not contain your accounts. Those need to be backed up separately!
`)
return nil
}
func attestFile(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires an argument.")
}
if err := initialize(ctx); err != nil {
return err
}
stretchedKey, err := readMasterKey(ctx)
if err != nil {
utils.Fatalf(err.Error())
}
configDir := ctx.String(configdirFlag.Name)
vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
confKey := crypto.Keccak256([]byte("config"), stretchedKey)
// Initialize the encrypted storages
configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confKey)
val := ctx.Args().First()
configStorage.Put("ruleset_sha256", val)
log.Info("Ruleset attestation updated", "sha256", val)
return nil
}
func addCredential(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires at leaste one argument.")
}
if err := initialize(ctx); err != nil {
return err
}
stretchedKey, err := readMasterKey(ctx)
if err != nil {
utils.Fatalf(err.Error())
}
configDir := ctx.String(configdirFlag.Name)
vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey)
// Initialize the encrypted storages
pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
key := ctx.Args().First()
value := ""
if len(ctx.Args()) > 1 {
value = ctx.Args().Get(1)
}
pwStorage.Put(key, value)
log.Info("Credential store updated", "key", key)
return nil
}
func initialize(c *cli.Context) error {
// Set up the logger to print everything
logOutput := os.Stdout
if c.Bool(stdiouiFlag.Name) {
logOutput = os.Stderr
// If using the stdioui, we can't do the 'confirm'-flow
fmt.Fprintf(logOutput, legalWarning)
} else {
if !confirm(legalWarning) {
return fmt.Errorf("aborted by user")
}
}
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(logOutput, log.TerminalFormat(true))))
return nil
}
func signer(c *cli.Context) error {
if err := initialize(c); err != nil {
return err
}
var (
ui core.SignerUI
)
if c.Bool(stdiouiFlag.Name) {
log.Info("Using stdin/stdout as UI-channel")
ui = core.NewStdIOUI()
} else {
log.Info("Using CLI as UI-channel")
ui = core.NewCommandlineUI()
}
db, err := core.NewAbiDBFromFiles(c.String(dBFlag.Name), c.String(customDBFlag.Name))
if err != nil {
utils.Fatalf(err.Error())
}
log.Info("Loaded 4byte db", "signatures", db.Size(), "file", c.String("4bytedb"))
var (
api core.ExternalAPI
)
configDir := c.String(configdirFlag.Name)
if stretchedKey, err := readMasterKey(c); err != nil {
log.Info("No master seed provided, rules disabled")
} else {
if err != nil {
utils.Fatalf(err.Error())
}
vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
// Generate domain specific keys
pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey)
jskey := crypto.Keccak256([]byte("jsstorage"), stretchedKey)
confkey := crypto.Keccak256([]byte("config"), stretchedKey)
// Initialize the encrypted storages
pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
jsStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "jsstorage.json"), jskey)
configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confkey)
//Do we have a rule-file?
ruleJS, err := ioutil.ReadFile(c.String(ruleFlag.Name))
if err != nil {
log.Info("Could not load rulefile, rules not enabled", "file", "rulefile")
} else {
hasher := sha256.New()
hasher.Write(ruleJS)
shasum := hasher.Sum(nil)
storedShasum := configStorage.Get("ruleset_sha256")
if storedShasum != hex.EncodeToString(shasum) {
log.Info("Could not validate ruleset hash, rules not enabled", "got", hex.EncodeToString(shasum), "expected", storedShasum)
} else {
// Initialize rules
ruleEngine, err := rules.NewRuleEvaluator(ui, jsStorage, pwStorage)
if err != nil {
utils.Fatalf(err.Error())
}
ruleEngine.Init(string(ruleJS))
ui = ruleEngine
log.Info("Rule engine configured", "file", c.String(ruleFlag.Name))
}
}
}
apiImpl := core.NewSignerAPI(
c.Int64(utils.NetworkIdFlag.Name),
c.String(keystoreFlag.Name),
c.Bool(utils.NoUSBFlag.Name),
ui, db,
c.Bool(utils.LightKDFFlag.Name))
api = apiImpl
// Audit logging
if logfile := c.String(auditLogFlag.Name); logfile != "" {
api, err = core.NewAuditLogger(logfile, api)
if err != nil {
utils.Fatalf(err.Error())
}
log.Info("Audit logs configured", "file", logfile)
}
// register signer API with server
var (
extapiUrl = "n/a"
ipcApiUrl = "n/a"
)
rpcApi := []rpc.API{
{
Namespace: "account",
Public: true,
Service: api,
Version: "1.0"},
}
if c.Bool(utils.RPCEnabledFlag.Name) {
vhosts := splitAndTrim(c.GlobalString(utils.RPCVirtualHostsFlag.Name))
cors := splitAndTrim(c.GlobalString(utils.RPCCORSDomainFlag.Name))
// start http server
httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcApi, []string{"account"}, cors, vhosts)
if err != nil {
utils.Fatalf("Could not start RPC api: %v", err)
}
extapiUrl = fmt.Sprintf("http://%s", httpEndpoint)
log.Info("HTTP endpoint opened", "url", extapiUrl)
defer func() {
listener.Close()
log.Info("HTTP endpoint closed", "url", httpEndpoint)
}()
}
if !c.Bool(utils.IPCDisabledFlag.Name) {
if c.IsSet(utils.IPCPathFlag.Name) {
ipcApiUrl = c.String(utils.IPCPathFlag.Name)
} else {
ipcApiUrl = filepath.Join(configDir, "clef.ipc")
}
listener, _, err := rpc.StartIPCEndpoint(func() bool { return true }, ipcApiUrl, rpcApi)
if err != nil {
utils.Fatalf("Could not start IPC api: %v", err)
}
log.Info("IPC endpoint opened", "url", ipcApiUrl)
defer func() {
listener.Close()
log.Info("IPC endpoint closed", "url", ipcApiUrl)
}()
}
if c.Bool(testFlag.Name) {
log.Info("Performing UI test")
go testExternalUI(apiImpl)
}
ui.OnSignerStartup(core.StartupInfo{
Info: map[string]interface{}{
"extapi_version": ExternalApiVersion,
"intapi_version": InternalApiVersion,
"extapi_http": extapiUrl,
"extapi_ipc": ipcApiUrl,
},
})
abortChan := make(chan os.Signal)
signal.Notify(abortChan, os.Interrupt)
sig := <-abortChan
log.Info("Exiting...", "signal", sig)
return nil
}
// splitAndTrim splits input separated by a comma
// and trims excessive white space from the substrings.
func splitAndTrim(input string) []string {
result := strings.Split(input, ",")
for i, r := range result {
result[i] = strings.TrimSpace(r)
}
return result
}
// DefaultConfigDir is the default config directory to use for the vaults and other
// persistence requirements.
func DefaultConfigDir() string {
// Try to place the data folder in the user's home dir
home := homeDir()
if home != "" {
if runtime.GOOS == "darwin" {
return filepath.Join(home, "Library", "Signer")
} else if runtime.GOOS == "windows" {
return filepath.Join(home, "AppData", "Roaming", "Signer")
} else {
return filepath.Join(home, ".clef")
}
}
// As we cannot guess a stable location, return empty and handle later
return ""
}
func homeDir() string {
if home := os.Getenv("HOME"); home != "" {
return home
}
if usr, err := user.Current(); err == nil {
return usr.HomeDir
}
return ""
}
func readMasterKey(ctx *cli.Context) ([]byte, error) {
var (
file string
configDir = ctx.String(configdirFlag.Name)
)
if ctx.IsSet(signerSecretFlag.Name) {
file = ctx.String(signerSecretFlag.Name)
} else {
file = filepath.Join(configDir, "secrets.dat")
}
if err := checkFile(file); err != nil {
return nil, err
}
masterKey, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
if len(masterKey) < 256 {
return nil, fmt.Errorf("master key of insufficient length, expected >255 bytes, got %d", len(masterKey))
}
// Create vault location
vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), masterKey)[:10]))
err = os.Mkdir(vaultLocation, 0700)
if err != nil && !os.IsExist(err) {
return nil, err
}
//!TODO, use KDF to stretch the master key
// stretched_key := stretch_key(master_key)
return masterKey, nil
}
// checkFile is a convenience function to check if a file
// * exists
// * is mode 0600
func checkFile(filename string) error {
info, err := os.Stat(filename)
if err != nil {
return fmt.Errorf("failed stat on %s: %v", filename, err)
}
// Check the unix permission bits
if info.Mode().Perm()&077 != 0 {
return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String())
}
return nil
}
// confirm displays a text and asks for user confirmation
func confirm(text string) bool {
fmt.Printf(text)
fmt.Printf("\nEnter 'ok' to proceed:\n>")
text, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
log.Crit("Failed to read user input", "err", err)
}
if text := strings.TrimSpace(text); text == "ok" {
return true
}
return false
}
func testExternalUI(api *core.SignerAPI) {
ctx := context.WithValue(context.Background(), "remote", "clef binary")
ctx = context.WithValue(ctx, "scheme", "in-proc")
ctx = context.WithValue(ctx, "local", "main")
errs := make([]string, 0)
api.UI.ShowInfo("Testing 'ShowInfo'")
api.UI.ShowError("Testing 'ShowError'")
checkErr := func(method string, err error) {
if err != nil && err != core.ErrRequestDenied {
errs = append(errs, fmt.Sprintf("%v: %v", method, err.Error()))
}
}
var err error
_, err = api.SignTransaction(ctx, core.SendTxArgs{From: common.MixedcaseAddress{}}, nil)
checkErr("SignTransaction", err)
_, err = api.Sign(ctx, common.MixedcaseAddress{}, common.Hex2Bytes("01020304"))
checkErr("Sign", err)
_, err = api.List(ctx)
checkErr("List", err)
_, err = api.New(ctx)
checkErr("New", err)
_, err = api.Export(ctx, common.Address{})
checkErr("Export", err)
_, err = api.Import(ctx, json.RawMessage{})
checkErr("Import", err)
api.UI.ShowInfo("Tests completed")
if len(errs) > 0 {
log.Error("Got errors")
for _, e := range errs {
log.Error(e)
}
} else {
log.Info("No errors")
}
}
/**
//Create Account
curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_new","params":["test"],"id":67}' localhost:8550
// List accounts
curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_list","params":[""],"id":67}' http://localhost:8550/
// Make Transaction
// safeSend(0x12)
// 4401a6e40000000000000000000000000000000000000000000000000000000000000012
// supplied abi
curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x82A2A876D39022B3019932D30Cd9c97ad5616813","gas":"0x333","gasPrice":"0x123","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x10", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"test"],"id":67}' http://localhost:8550/
// Not supplied
curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x82A2A876D39022B3019932D30Cd9c97ad5616813","gas":"0x333","gasPrice":"0x123","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x10", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"}],"id":67}' http://localhost:8550/
// Sign data
curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_sign","params":["0x694267f14675d7e1b9494fd8d72fefe1755710fa","bazonk gaz baz"],"id":67}' http://localhost:8550/
**/

179
cmd/clef/pythonsigner.py Normal file
View File

@@ -0,0 +1,179 @@
import os,sys, subprocess
from tinyrpc.transports import ServerTransport
from tinyrpc.protocols.jsonrpc import JSONRPCProtocol
from tinyrpc.dispatch import public,RPCDispatcher
from tinyrpc.server import RPCServer
""" This is a POC example of how to write a custom UI for Clef. The UI starts the
clef process with the '--stdio-ui' option, and communicates with clef using standard input / output.
The standard input/output is a relatively secure way to communicate, as it does not require opening any ports
or IPC files. Needless to say, it does not protect against memory inspection mechanisms where an attacker
can access process memory."""
try:
import urllib.parse as urlparse
except ImportError:
import urllib as urlparse
class StdIOTransport(ServerTransport):
""" Uses std input/output for RPC """
def receive_message(self):
return None, urlparse.unquote(sys.stdin.readline())
def send_reply(self, context, reply):
print(reply)
class PipeTransport(ServerTransport):
""" Uses std a pipe for RPC """
def __init__(self,input, output):
self.input = input
self.output = output
def receive_message(self):
data = self.input.readline()
print(">> {}".format( data))
return None, urlparse.unquote(data)
def send_reply(self, context, reply):
print("<< {}".format( reply))
self.output.write(reply)
self.output.write("\n")
class StdIOHandler():
def __init__(self):
pass
@public
def ApproveTx(self,req):
"""
Example request:
{
"jsonrpc": "2.0",
"method": "ApproveTx",
"params": [{
"transaction": {
"to": "0xae967917c465db8578ca9024c205720b1a3651A9",
"gas": "0x333",
"gasPrice": "0x123",
"value": "0x10",
"data": "0xd7a5865800000000000000000000000000000000000000000000000000000000000000ff",
"nonce": "0x0"
},
"from": "0xAe967917c465db8578ca9024c205720b1a3651A9",
"call_info": "Warning! Could not validate ABI-data against calldata\nSupplied ABI spec does not contain method signature in data: 0xd7a58658",
"meta": {
"remote": "127.0.0.1:34572",
"local": "localhost:8550",
"scheme": "HTTP/1.1"
}
}],
"id": 1
}
:param transaction: transaction info
:param call_info: info abou the call, e.g. if ABI info could not be
:param meta: metadata about the request, e.g. where the call comes from
:return:
"""
transaction = req.get('transaction')
_from = req.get('from')
call_info = req.get('call_info')
meta = req.get('meta')
return {
"approved" : False,
#"transaction" : transaction,
# "from" : _from,
# "password" : None,
}
@public
def ApproveSignData(self, req):
""" Example request
"""
return {"approved": False, "password" : None}
@public
def ApproveExport(self, req):
""" Example request
"""
return {"approved" : False}
@public
def ApproveImport(self, req):
""" Example request
"""
return { "approved" : False, "old_password": "", "new_password": ""}
@public
def ApproveListing(self, req):
""" Example request
"""
return {'accounts': []}
@public
def ApproveNewAccount(self, req):
"""
Example request
:return:
"""
return {"approved": False,
#"password": ""
}
@public
def ShowError(self,message = {}):
"""
Example request:
{"jsonrpc":"2.0","method":"ShowInfo","params":{"message":"Testing 'ShowError'"},"id":1}
:param message: to show
:return: nothing
"""
if 'text' in message.keys():
sys.stderr.write("Error: {}\n".format( message['text']))
return
@public
def ShowInfo(self,message = {}):
"""
Example request
{"jsonrpc":"2.0","method":"ShowInfo","params":{"message":"Testing 'ShowInfo'"},"id":0}
:param message: to display
:return:nothing
"""
if 'text' in message.keys():
sys.stdout.write("Error: {}\n".format( message['text']))
return
def main(args):
cmd = ["./clef", "--stdio-ui"]
if len(args) > 0 and args[0] == "test":
cmd.extend(["--stdio-ui-test"])
print("cmd: {}".format(" ".join(cmd)))
dispatcher = RPCDispatcher()
dispatcher.register_instance(StdIOHandler(), '')
# line buffered
p = subprocess.Popen(cmd, bufsize=1, universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
rpc_server = RPCServer(
PipeTransport(p.stdout, p.stdin),
JSONRPCProtocol(),
dispatcher
)
rpc_server.serve_forever()
if __name__ == '__main__':
main(sys.argv[1:])

236
cmd/clef/rules.md Normal file
View File

@@ -0,0 +1,236 @@
# Rules
The `signer` binary contains a ruleset engine, implemented with [OttoVM](https://github.com/robertkrimen/otto)
It enables usecases like the following:
* I want to auto-approve transactions with contract `CasinoDapp`, with up to `0.05 ether` in value to maximum `1 ether` per 24h period
* I want to auto-approve transaction to contract `EthAlarmClock` with `data`=`0xdeadbeef`, if `value=0`, `gas < 44k` and `gasPrice < 40Gwei`
The two main features that are required for this to work well are;
1. Rule Implementation: how to create, manage and interpret rules in a flexible but secure manner
2. Credential managements and credentials; how to provide auto-unlock without exposing keys unnecessarily.
The section below deals with both of them
## Rule Implementation
A ruleset file is implemented as a `js` file. Under the hood, the ruleset-engine is a `SignerUI`, implementing the same methods as the `json-rpc` methods
defined in the UI protocol. Example:
```javascript
function asBig(str){
if(str.slice(0,2) == "0x"){ return new BigNumber(str.slice(2),16)}
return new BigNumber(str)
}
// Approve transactions to a certain contract if value is below a certain limit
function ApproveTx(req){
var limit = big.Newint("0xb1a2bc2ec50000")
var value = asBig(req.transaction.value);
if(req.transaction.to.toLowerCase()=="0xae967917c465db8578ca9024c205720b1a3651a9")
&& value.lt(limit) ){
return "Approve"
}
// If we return "Reject", it will be rejected.
// By not returning anything, it will be passed to the next UI, for manual processing
}
//Approve listings if request made from IPC
function ApproveListing(req){
if (req.metadata.scheme == "ipc"){ return "Approve"}
}
```
Whenever the external API is called (and the ruleset is enabled), the `signer` calls the UI, which is an instance of a ruleset-engine. The ruleset-engine
invokes the corresponding method. In doing so, there are three possible outcomes:
1. JS returns "Approve"
* Auto-approve request
2. JS returns "Reject"
* Auto-reject request
3. Error occurs, or something else is returned
* Pass on to `next` ui: the regular UI channel.
A more advanced example can be found below, "Example 1: ruleset for a rate-limited window", using `storage` to `Put` and `Get` `string`s by key.
* At the time of writing, storage only exists as an ephemeral unencrypted implementation, to be used during testing.
### Things to note
The Otto vm has a few [caveats](https://github.com/robertkrimen/otto):
* "use strict" will parse, but does nothing.
* The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification.
* Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported.
Additionally, a few more have been added
* The rule execution cannot load external javascript files.
* The only preloaded libary is [`bignumber.js`](https://github.com/MikeMcl/bignumber.js) version `2.0.3`. This one is fairly old, and is not aligned with the documentation at the github repository.
* Each invocation is made in a fresh virtual machine. This means that you cannot store data in global variables between invocations. This is a deliberate choice -- if you want to store data, use the disk-backed `storage`, since rules should not rely on ephemeral data.
* Javascript API parameters are _always_ an object. This is also a design choice, to ensure that parameters are accessed by _key_ and not by order. This is to prevent mistakes due to missing parameters or parameter changes.
* The JS engine has access to `storage` and `console`.
#### Security considerations
##### Security of ruleset
Some security precautions can be made, such as:
* Never load `ruleset.js` unless the file is `readonly` (`r-??-??-?`). If the user wishes to modify the ruleset, he must make it writeable and then set back to readonly.
* This is to prevent attacks where files are dropped on the users disk.
* Since we're going to have to have some form of secure storage (not defined in this section), we could also store the `sha3` of the `ruleset.js` file in there.
* If the user wishes to modify the ruleset, he'd then have to perform e.g. `signer --attest /path/to/ruleset --credential <creds>`
##### Security of implementation
The drawbacks of this very flexible solution is that the `signer` needs to contain a javascript engine. This is pretty simple to implement, since it's already
implemented for `geth`. There are no known security vulnerabilities in, nor have we had any security-problems with it so far.
The javascript engine would be an added attack surface; but if the validation of `rulesets` is made good (with hash-based attestation), the actual javascript cannot be considered
an attack surface -- if an attacker can control the ruleset, a much simpler attack would be to implement an "always-approve" rule instead of exploiting the js vm. The only benefit
to be gained from attacking the actual `signer` process from the `js` side would be if it could somehow extract cryptographic keys from memory.
##### Security in usability
Javascript is flexible, but also easy to get wrong, especially when users assume that `js` can handle large integers natively. Typical errors
include trying to multiply `gasCost` with `gas` without using `bigint`:s.
It's unclear whether any other DSL could be more secure; since there's always the possibility of erroneously implementing a rule.
## Credential management
The ability to auto-approve transaction means that the signer needs to have necessary credentials to decrypt keyfiles. These passwords are hereafter called `ksp` (keystore pass).
### Example implementation
Upon startup of the signer, the signer is given a switch: `--seed <path/to/masterseed>`
The `seed` contains a blob of bytes, which is the master seed for the `signer`.
The `signer` uses the `seed` to:
* Generate the `path` where the settings are stored.
* `./settings/1df094eb-c2b1-4689-90dd-790046d38025/vault.dat`
* `./settings/1df094eb-c2b1-4689-90dd-790046d38025/rules.js`
* Generate the encryption password for `vault.dat`.
The `vault.dat` would be an encrypted container storing the following information:
* `ksp` entries
* `sha256` hash of `rules.js`
* Information about pair:ed callers (not yet specified)
### Security considerations
This would leave it up to the user to ensure that the `path/to/masterseed` is handled in a secure way. It's difficult to get around this, although one could
imagine leveraging OS-level keychains where supported. The setup is however in general similar to how ssh-keys are stored in `.ssh/`.
# Implementation status
This is now implemented (with ephemeral non-encrypted storage for now, so not yet enabled).
## Example 1: ruleset for a rate-limited window
```javascript
function big(str){
if(str.slice(0,2) == "0x"){ return new BigNumber(str.slice(2),16)}
return new BigNumber(str)
}
// Time window: 1 week
var window = 1000* 3600*24*7;
// Limit : 1 ether
var limit = new BigNumber("1e18");
function isLimitOk(transaction){
var value = big(transaction.value)
// Start of our window function
var windowstart = new Date().getTime() - window;
var txs = [];
var stored = storage.Get('txs');
if(stored != ""){
txs = JSON.parse(stored)
}
// First, remove all that have passed out of the time-window
var newtxs = txs.filter(function(tx){return tx.tstamp > windowstart});
console.log(txs, newtxs.length);
// Secondly, aggregate the current sum
sum = new BigNumber(0)
sum = newtxs.reduce(function(agg, tx){ return big(tx.value).plus(agg)}, sum);
console.log("ApproveTx > Sum so far", sum);
console.log("ApproveTx > Requested", value.toNumber());
// Would we exceed weekly limit ?
return sum.plus(value).lt(limit)
}
function ApproveTx(r){
if (isLimitOk(r.transaction)){
return "Approve"
}
return "Nope"
}
/**
* OnApprovedTx(str) is called when a transaction has been approved and signed. The parameter
* 'response_str' contains the return value that will be sent to the external caller.
* The return value from this method is ignore - the reason for having this callback is to allow the
* ruleset to keep track of approved transactions.
*
* When implementing rate-limited rules, this callback should be used.
* If a rule responds with neither 'Approve' nor 'Reject' - the tx goes to manual processing. If the user
* then accepts the transaction, this method will be called.
*
* TLDR; Use this method to keep track of signed transactions, instead of using the data in ApproveTx.
*/
function OnApprovedTx(resp){
var value = big(resp.tx.value)
var txs = []
// Load stored transactions
var stored = storage.Get('txs');
if(stored != ""){
txs = JSON.parse(stored)
}
// Add this to the storage
txs.push({tstamp: new Date().getTime(), value: value});
storage.Put("txs", JSON.stringify(txs));
}
```
## Example 2: allow destination
```javascript
function ApproveTx(r){
if(r.transaction.from.toLowerCase()=="0x0000000000000000000000000000000000001337"){ return "Approve"}
if(r.transaction.from.toLowerCase()=="0x000000000000000000000000000000000000dead"){ return "Reject"}
// Otherwise goes to manual processing
}
```
## Example 3: Allow listing
```javascript
function ApproveListing(){
return "Approve"
}
```

BIN
cmd/clef/sign_flow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

198
cmd/clef/tutorial.md Normal file
View File

@@ -0,0 +1,198 @@
## Initializing the signer
First, initialize the master seed.
```text
#./signer init
WARNING!
The signer is alpha software, and not yet publically released. This software has _not_ been audited, and there
are no guarantees about the workings of this software. It may contain severe flaws. You should not use this software
unless you agree to take full responsibility for doing so, and know what you are doing.
TLDR; THIS IS NOT PRODUCTION-READY SOFTWARE!
Enter 'ok' to proceed:
>ok
A master seed has been generated into /home/martin/.signer/secrets.dat
This is required to be able to store credentials, such as :
* Passwords for keystores (used by rule engine)
* Storage for javascript rules
* Hash of rule-file
You should treat that file with utmost secrecy, and make a backup of it.
NOTE: This file does not contain your accounts. Those need to be backed up separately!
```
(for readability purposes, we'll remove the WARNING printout in the rest of this document)
## Creating rules
Now, you can create a rule-file.
```javascript
function ApproveListing(){
return "Approve"
}
```
Get the `sha256` hash....
```text
#sha256sum rules.js
6c21d1737429d6d4f2e55146da0797782f3c0a0355227f19d702df377c165d72 rules.js
```
...And then `attest` the file:
```text
#./signer attest 6c21d1737429d6d4f2e55146da0797782f3c0a0355227f19d702df377c165d72
INFO [02-21|12:14:38] Ruleset attestation updated sha256=6c21d1737429d6d4f2e55146da0797782f3c0a0355227f19d702df377c165d72
```
At this point, we then start the signer with the rule-file:
```text
#./signer --rules rules.json
INFO [02-21|12:15:18] Using CLI as UI-channel
INFO [02-21|12:15:18] Loaded 4byte db signatures=5509 file=./4byte.json
INFO [02-21|12:15:18] Could not load rulefile, rules not enabled file=rulefile
DEBUG[02-21|12:15:18] FS scan times list=35.335µs set=5.536µs diff=5.073µs
DEBUG[02-21|12:15:18] Ledger support enabled
DEBUG[02-21|12:15:18] Trezor support enabled
INFO [02-21|12:15:18] Audit logs configured file=audit.log
INFO [02-21|12:15:18] HTTP endpoint opened url=http://localhost:8550
------- Signer info -------
* extapi_http : http://localhost:8550
* extapi_ipc : <nil>
* extapi_version : 2.0.0
* intapi_version : 1.2.0
```
Any list-requests will now be auto-approved by our rule-file.
## Under the hood
While doing the operations above, these files have been created:
```text
#ls -laR ~/.signer/
/home/martin/.signer/:
total 16
drwx------ 3 martin martin 4096 feb 21 12:14 .
drwxr-xr-x 71 martin martin 4096 feb 21 12:12 ..
drwx------ 2 martin martin 4096 feb 21 12:14 43f73718397aa54d1b22
-rwx------ 1 martin martin 256 feb 21 12:12 secrets.dat
/home/martin/.signer/43f73718397aa54d1b22:
total 12
drwx------ 2 martin martin 4096 feb 21 12:14 .
drwx------ 3 martin martin 4096 feb 21 12:14 ..
-rw------- 1 martin martin 159 feb 21 12:14 config.json
#cat /home/martin/.signer/43f73718397aa54d1b22/config.json
{"ruleset_sha256":{"iv":"6v4W4tfJxj3zZFbl","c":"6dt5RTDiTq93yh1qDEjpsat/tsKG7cb+vr3sza26IPL2fvsQ6ZoqFx++CPUa8yy6fD9Bbq41L01ehkKHTG3pOAeqTW6zc/+t0wv3AB6xPmU="}}
```
In `~/.signer`, the `secrets.dat` file was created, containing the `master_seed`.
The `master_seed` was then used to derive a few other things:
- `vault_location` : in this case `43f73718397aa54d1b22` .
- Thus, if you use a different `master_seed`, another `vault_location` will be used that does not conflict with each other.
- Example: `signer --signersecret /path/to/afile ...`
- `config.json` which is the encrypted key/value storage for configuration data, containing the key `ruleset_sha256`.
## Adding credentials
In order to make more useful rules; sign transactions, the signer needs access to the passwords needed to unlock keystores.
```text
#./signer addpw 0x694267f14675d7e1b9494fd8d72fefe1755710fa test
INFO [02-21|13:43:21] Credential store updated key=0x694267f14675d7e1b9494fd8d72fefe1755710fa
```
## More advanced rules
Now let's update the rules to make use of credentials
```javascript
function ApproveListing(){
return "Approve"
}
function ApproveSignData(r){
if( r.address.toLowerCase() == "0x694267f14675d7e1b9494fd8d72fefe1755710fa")
{
if(r.message.indexOf("bazonk") >= 0){
return "Approve"
}
return "Reject"
}
// Otherwise goes to manual processing
}
```
In this example,
* any requests to sign data with the account `0x694...` will be
* auto-approved if the message contains with `bazonk`,
* and auto-rejected if it does not.
* Any other signing-requests will be passed along for manual approve/reject.
..attest the new file
```text
#sha256sum rules.js
2a0cb661dacfc804b6e95d935d813fd17c0997a7170e4092ffbc34ca976acd9f rules.js
#./signer attest 2a0cb661dacfc804b6e95d935d813fd17c0997a7170e4092ffbc34ca976acd9f
INFO [02-21|14:36:30] Ruleset attestation updated sha256=2a0cb661dacfc804b6e95d935d813fd17c0997a7170e4092ffbc34ca976acd9f
```
And start the signer:
```
#./signer --rules rules.js
INFO [02-21|14:41:56] Using CLI as UI-channel
INFO [02-21|14:41:56] Loaded 4byte db signatures=5509 file=./4byte.json
INFO [02-21|14:41:56] Rule engine configured file=rules.js
DEBUG[02-21|14:41:56] FS scan times list=34.607µs set=4.509µs diff=4.87µs
DEBUG[02-21|14:41:56] Ledger support enabled
DEBUG[02-21|14:41:56] Trezor support enabled
INFO [02-21|14:41:56] Audit logs configured file=audit.log
INFO [02-21|14:41:56] HTTP endpoint opened url=http://localhost:8550
------- Signer info -------
* extapi_version : 2.0.0
* intapi_version : 1.2.0
* extapi_http : http://localhost:8550
* extapi_ipc : <nil>
INFO [02-21|14:41:56] error occurred during execution error="ReferenceError: 'OnSignerStartup' is not defined"
```
And then test signing, once with `bazonk` and once without:
```
#curl -H "Content-Type: application/json" -X POST --data "{\"jsonrpc\":\"2.0\",\"method\":\"account_sign\",\"params\":[\"0x694267f14675d7e1b9494fd8d72fefe1755710fa\",\"0x$(xxd -pu <<< ' bazonk baz gaz')\"],\"id\":67}" http://localhost:8550/
{"jsonrpc":"2.0","id":67,"result":"0x93e6161840c3ae1efc26dc68dedab6e8fc233bb3fefa1b4645dbf6609b93dace160572ea4ab33240256bb6d3dadb60dcd9c515d6374d3cf614ee897408d41d541c"}
#curl -H "Content-Type: application/json" -X POST --data "{\"jsonrpc\":\"2.0\",\"method\":\"account_sign\",\"params\":[\"0x694267f14675d7e1b9494fd8d72fefe1755710fa\",\"0x$(xxd -pu <<< ' bonk baz gaz')\"],\"id\":67}" http://localhost:8550/
{"jsonrpc":"2.0","id":67,"error":{"code":-32000,"message":"Request denied"}}
```
Meanwhile, in the signer output:
```text
INFO [02-21|14:42:41] Op approved
INFO [02-21|14:42:56] Op rejected
```
The signer also stores all traffic over the external API in a log file. The last 4 lines shows the two requests and their responses:
```text
#tail audit.log -n 4
t=2018-02-21T14:42:41+0100 lvl=info msg=Sign api=signer type=request metadata="{\"remote\":\"127.0.0.1:49706\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\"}" addr="0x694267f14675d7e1b9494fd8d72fefe1755710fa [chksum INVALID]" data=202062617a6f6e6b2062617a2067617a0a
t=2018-02-21T14:42:42+0100 lvl=info msg=Sign api=signer type=response data=93e6161840c3ae1efc26dc68dedab6e8fc233bb3fefa1b4645dbf6609b93dace160572ea4ab33240256bb6d3dadb60dcd9c515d6374d3cf614ee897408d41d541c error=nil
t=2018-02-21T14:42:56+0100 lvl=info msg=Sign api=signer type=request metadata="{\"remote\":\"127.0.0.1:49708\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\"}" addr="0x694267f14675d7e1b9494fd8d72fefe1755710fa [chksum INVALID]" data=2020626f6e6b2062617a2067617a0a
t=2018-02-21T14:42:56+0100 lvl=info msg=Sign api=signer type=response data= error="Request denied"
```

View File

@@ -86,10 +86,6 @@ var (
Name: "create",
Usage: "indicates the action should be create rather than call",
}
DisableGasMeteringFlag = cli.BoolFlag{
Name: "nogasmetering",
Usage: "disable gas metering",
}
GenesisFlag = cli.StringFlag{
Name: "prestate",
Usage: "JSON file with prestate (genesis) config",
@@ -128,7 +124,6 @@ func init() {
ValueFlag,
DumpFlag,
InputFlag,
DisableGasMeteringFlag,
MemProfileFlag,
CPUProfileFlag,
StatDumpFlag,

View File

@@ -76,6 +76,7 @@ func runCmd(ctx *cli.Context) error {
logconfig := &vm.LogConfig{
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
Debug: ctx.GlobalBool(DebugFlag.Name),
}
var (
@@ -83,8 +84,8 @@ func runCmd(ctx *cli.Context) error {
debugLogger *vm.StructLogger
statedb *state.StateDB
chainConfig *params.ChainConfig
sender = common.StringToAddress("sender")
receiver = common.StringToAddress("receiver")
sender = common.BytesToAddress([]byte("sender"))
receiver = common.BytesToAddress([]byte("receiver"))
)
if ctx.GlobalBool(MachineFlag.Name) {
tracer = NewJSONLogger(logconfig, os.Stdout)
@@ -161,9 +162,8 @@ func runCmd(ctx *cli.Context) error {
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
Value: utils.GlobalBig(ctx, ValueFlag.Name),
EVMConfig: vm.Config{
Tracer: tracer,
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
Tracer: tracer,
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
},
}
@@ -235,9 +235,7 @@ Gas used: %d
`, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas)
}
if tracer != nil {
tracer.CaptureEnd(ret, initialGas-leftOverGas, execTime, err)
} else {
if tracer == nil {
fmt.Printf("0x%x\n", ret)
if err != nil {
fmt.Printf(" error: %v\n", err)

View File

@@ -533,9 +533,11 @@ func (f *faucet) loop() {
}
defer sub.Unsubscribe()
for {
select {
case head := <-heads:
// Start a goroutine to update the state from head notifications in the background
update := make(chan *types.Header)
go func() {
for head := range update {
// New chain head arrived, query the current stats and stream to clients
var (
balance *big.Int
@@ -588,6 +590,17 @@ func (f *faucet) loop() {
}
}
f.lock.RUnlock()
}
}()
// Wait for various events and assing to the appropriate background threads
for {
select {
case head := <-heads:
// New head arrived, send if for state update if there's none running
select {
case update <- head:
default:
}
case <-f.update:
// Pending requests updated, stream to clients
@@ -686,8 +699,6 @@ func authTwitter(url string) (string, string, common.Address, error) {
if len(parts) < 4 || parts[len(parts)-2] != "status" {
return "", "", common.Address{}, errors.New("Invalid Twitter status URL")
}
username := parts[len(parts)-3]
// Twitter's API isn't really friendly with direct links. Still, we don't
// want to do ask read permissions from users, so just load the public posts and
// scrape it for the Ethereum address and profile URL.
@@ -697,6 +708,13 @@ func authTwitter(url string) (string, string, common.Address, error) {
}
defer res.Body.Close()
// Resolve the username from the final redirect, no intermediate junk
parts = strings.Split(res.Request.URL.String(), "/")
if len(parts) < 4 || parts[len(parts)-2] != "status" {
return "", "", common.Address{}, errors.New("Invalid Twitter status URL")
}
username := parts[len(parts)-3]
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", "", common.Address{}, err

View File

@@ -49,15 +49,17 @@ func reportBug(ctx *cli.Context) error {
// execute template and write contents to buff
var buff bytes.Buffer
fmt.Fprintln(&buff, header)
fmt.Fprintln(&buff, "#### System information")
fmt.Fprintln(&buff)
fmt.Fprintln(&buff, "Version:", params.Version)
fmt.Fprintln(&buff, "Go Version:", runtime.Version())
fmt.Fprintln(&buff, "OS:", runtime.GOOS)
printOSDetails(&buff)
fmt.Fprintln(&buff, header)
// open a new GH issue
if !browser.Open(issueUrl + "?body=" + url.QueryEscape(buff.String())) {
fmt.Printf("Please file a new issue at %s using this template:\n%s", issueUrl, buff.String())
fmt.Printf("Please file a new issue at %s using this template:\n\n%s", issueUrl, buff.String())
}
return nil
}
@@ -97,13 +99,15 @@ func printCmdOut(w io.Writer, prefix, path string, args ...string) {
fmt.Fprintf(w, "%s%s\n", prefix, bytes.TrimSpace(out))
}
const header = `Please answer these questions before submitting your issue. Thanks!
const header = `
#### Expected behaviour
#### What did you do?
#### What did you expect to see?
#### What did you see instead?
#### System details
#### Actual behaviour
#### Steps to reproduce the behaviour
#### Backtrace
`

View File

@@ -67,6 +67,9 @@ It expects the genesis file as argument.`,
utils.DataDirFlag,
utils.CacheFlag,
utils.LightModeFlag,
utils.GCModeFlag,
utils.CacheDatabaseFlag,
utils.CacheGCFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Description: `
@@ -92,6 +95,34 @@ Requires a first argument of the file to write to.
Optional second and third arguments control the first and
last block to write. In this mode, the file will be appended
if already existing.`,
}
importPreimagesCommand = cli.Command{
Action: utils.MigrateFlags(importPreimages),
Name: "import-preimages",
Usage: "Import the preimage database from an RLP stream",
ArgsUsage: "<datafile>",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.CacheFlag,
utils.LightModeFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Description: `
The import-preimages command imports hash preimages from an RLP encoded stream.`,
}
exportPreimagesCommand = cli.Command{
Action: utils.MigrateFlags(exportPreimages),
Name: "export-preimages",
Usage: "Export the preimage database into an RLP stream",
ArgsUsage: "<dumpfile>",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.CacheFlag,
utils.LightModeFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Description: `
The export-preimages command export hash preimages to an RLP encoded stream`,
}
copydbCommand = cli.Command{
Action: utils.MigrateFlags(copyDb),
@@ -222,6 +253,13 @@ func importChain(ctx *cli.Context) error {
utils.Fatalf("Failed to read database stats: %v", err)
}
fmt.Println(stats)
ioStats, err := db.LDB().GetProperty("leveldb.iostats")
if err != nil {
utils.Fatalf("Failed to read database iostats: %v", err)
}
fmt.Println(ioStats)
fmt.Printf("Trie cache misses: %d\n", trie.CacheMisses())
fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads())
@@ -252,6 +290,12 @@ func importChain(ctx *cli.Context) error {
}
fmt.Println(stats)
ioStats, err = db.LDB().GetProperty("leveldb.iostats")
if err != nil {
utils.Fatalf("Failed to read database iostats: %v", err)
}
fmt.Println(ioStats)
return nil
}
@@ -283,7 +327,39 @@ func exportChain(ctx *cli.Context) error {
if err != nil {
utils.Fatalf("Export error: %v\n", err)
}
fmt.Printf("Export done in %v", time.Since(start))
fmt.Printf("Export done in %v\n", time.Since(start))
return nil
}
// importPreimages imports preimage data from the specified file.
func importPreimages(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires an argument.")
}
stack := makeFullNode(ctx)
diskdb := utils.MakeChainDatabase(ctx, stack).(*ethdb.LDBDatabase)
start := time.Now()
if err := utils.ImportPreimages(diskdb, ctx.Args().First()); err != nil {
utils.Fatalf("Export error: %v\n", err)
}
fmt.Printf("Export done in %v\n", time.Since(start))
return nil
}
// exportPreimages dumps the preimage data to specified json file in streaming way.
func exportPreimages(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires an argument.")
}
stack := makeFullNode(ctx)
diskdb := utils.MakeChainDatabase(ctx, stack).(*ethdb.LDBDatabase)
start := time.Now()
if err := utils.ExportPreimages(diskdb, ctx.Args().First()); err != nil {
utils.Fatalf("Export error: %v\n", err)
}
fmt.Printf("Export done in %v\n", time.Since(start))
return nil
}

View File

@@ -32,7 +32,7 @@ import (
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
"github.com/naoina/toml"
)

View File

@@ -22,6 +22,7 @@ import (
"os/signal"
"path/filepath"
"strings"
"syscall"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/console"
@@ -42,7 +43,7 @@ var (
Description: `
The Geth console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console.`,
See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console.`,
}
attachCommand = cli.Command{
@@ -55,7 +56,7 @@ See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console.`,
Description: `
The Geth console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console.
See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console.
This command allows to open a console on a running geth node.`,
}
@@ -68,7 +69,7 @@ This command allows to open a console on a running geth node.`,
Category: "CONSOLE COMMANDS",
Description: `
The JavaScript VM exposes a node admin interface as well as the Ðapp
JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console`,
JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console`,
}
)
@@ -207,7 +208,7 @@ func ephemeralConsole(ctx *cli.Context) error {
}
// Wait for pending callbacks, but stop for Ctrl-C.
abort := make(chan os.Signal, 1)
signal.Notify(abort, os.Interrupt)
signal.Notify(abort, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-abort

View File

@@ -28,7 +28,6 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethclient"
@@ -46,8 +45,6 @@ const (
var (
// Git SHA1 commit hash of the release (set via linker flags)
gitCommit = ""
// Ethereum address of the Geth release oracle.
relOracle = common.HexToAddress("0xfa7b9770ca4cb04296cac84f37736d4041251cdf")
// The app that holds all commands and flags.
app = utils.NewApp(gitCommit, "the go-ethereum command line interface")
// flags that configure the node
@@ -65,7 +62,6 @@ var (
utils.DashboardAddrFlag,
utils.DashboardPortFlag,
utils.DashboardRefreshFlag,
utils.DashboardAssetsFlag,
utils.EthashCacheDirFlag,
utils.EthashCachesInMemoryFlag,
utils.EthashCachesOnDiskFlag,
@@ -156,6 +152,8 @@ func init() {
initCommand,
importCommand,
exportCommand,
importPreimagesCommand,
exportPreimagesCommand,
copydbCommand,
removedbCommand,
dumpCommand,

View File

@@ -168,19 +168,18 @@ type parityChainSpec struct {
Engine struct {
Ethash struct {
Params struct {
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"`
DurationLimit *hexutil.Big `json:"durationLimit"`
BlockReward *hexutil.Big `json:"blockReward"`
HomesteadTransition uint64 `json:"homesteadTransition"`
EIP150Transition uint64 `json:"eip150Transition"`
EIP160Transition uint64 `json:"eip160Transition"`
EIP161abcTransition uint64 `json:"eip161abcTransition"`
EIP161dTransition uint64 `json:"eip161dTransition"`
EIP649Reward *hexutil.Big `json:"eip649Reward"`
EIP100bTransition uint64 `json:"eip100bTransition"`
EIP649Transition uint64 `json:"eip649Transition"`
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
DurationLimit *hexutil.Big `json:"durationLimit"`
BlockReward *hexutil.Big `json:"blockReward"`
HomesteadTransition uint64 `json:"homesteadTransition"`
EIP150Transition uint64 `json:"eip150Transition"`
EIP160Transition uint64 `json:"eip160Transition"`
EIP161abcTransition uint64 `json:"eip161abcTransition"`
EIP161dTransition uint64 `json:"eip161dTransition"`
EIP649Reward *hexutil.Big `json:"eip649Reward"`
EIP100bTransition uint64 `json:"eip100bTransition"`
EIP649Transition uint64 `json:"eip649Transition"`
} `json:"params"`
} `json:"Ethash"`
} `json:"engine"`
@@ -188,6 +187,7 @@ type parityChainSpec struct {
Params struct {
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"`
NetworkID hexutil.Uint64 `json:"networkID"`
MaxCodeSize uint64 `json:"maxCodeSize"`
EIP155Transition uint64 `json:"eip155Transition"`
@@ -270,7 +270,6 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
}
spec.Engine.Ethash.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
spec.Engine.Ethash.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor)
spec.Engine.Ethash.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor)
spec.Engine.Ethash.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit)
spec.Engine.Ethash.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
spec.Engine.Ethash.Params.HomesteadTransition = genesis.Config.HomesteadBlock.Uint64()
@@ -284,6 +283,7 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
spec.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor)
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainId.Uint64())
spec.Params.MaxCodeSize = params.MaxCodeSize
spec.Params.EIP155Transition = genesis.Config.EIP155Block.Uint64()

View File

@@ -631,6 +631,7 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
"Tangerine": conf.Genesis.Config.EIP150Block,
"Spurious": conf.Genesis.Config.EIP155Block,
"Byzantium": conf.Genesis.Config.ByzantiumBlock,
"Constantinople": conf.Genesis.Config.ConstantinopleBlock,
})
files[filepath.Join(workdir, "index.html")] = indexfile.Bytes()

View File

@@ -40,11 +40,11 @@ ADD genesis.json /genesis.json
ADD signer.pass /signer.pass
{{end}}
RUN \
echo 'geth --cache 512 init /genesis.json' > geth.sh && \{{if .Unlock}}
echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> geth.sh && \{{end}}
echo $'geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine --minerthreads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> geth.sh
echo 'geth --cache 512 init /genesis.json' > /root/geth.sh && \{{if .Unlock}}
echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> /root/geth.sh && \{{end}}
echo $'geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine --minerthreads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> /root/geth.sh
ENTRYPOINT ["/bin/sh", "geth.sh"]
ENTRYPOINT ["/bin/sh", "/root/geth.sh"]
`
// nodeComposefile is the docker-compose.yml file required to deploy and maintain

View File

@@ -37,7 +37,7 @@ ADD genesis.json /genesis.json
RUN \
echo 'node server.js &' > wallet.sh && \
echo 'geth --cache 512 init /genesis.json' >> wallet.sh && \
echo $'geth --networkid {{.NetworkID}} --port {{.NodePort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --rpc --rpcaddr=0.0.0.0 --rpccorsdomain "*"' >> wallet.sh
echo $'geth --networkid {{.NetworkID}} --port {{.NodePort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --rpc --rpcaddr=0.0.0.0 --rpccorsdomain "*" --rpcvhosts "*"' >> wallet.sh
RUN \
sed -i 's/PuppethNetworkID/{{.NetworkID}}/g' dist/js/etherwallet-master.js && \

View File

@@ -20,6 +20,7 @@ package main
import (
"math/rand"
"os"
"strings"
"time"
"github.com/ethereum/go-ethereum/log"
@@ -34,7 +35,7 @@ func main() {
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "network",
Usage: "name of the network to administer",
Usage: "name of the network to administer (no spaces or hyphens, please)",
},
cli.IntFlag{
Name: "loglevel",
@@ -47,6 +48,10 @@ func main() {
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
rand.Seed(time.Now().UnixNano())
network := c.String("network")
if strings.Contains(network, " ") || strings.Contains(network, "-") {
log.Crit("No spaces or hyphens allowed in network name")
}
// Start the wizard and relinquish control
makeWizard(c.String("network")).run()
return nil

View File

@@ -59,15 +59,16 @@ func (w *wizard) run() {
fmt.Println()
// Make sure we have a good network name to work with fmt.Println()
// Docker accepts hyphens in image names, but doesn't like it for container names
if w.network == "" {
fmt.Println("Please specify a network name to administer (no spaces, please)")
fmt.Println("Please specify a network name to administer (no spaces or hyphens, please)")
for {
w.network = w.readString()
if !strings.Contains(w.network, " ") {
if !strings.Contains(w.network, " ") && !strings.Contains(w.network, "-") {
fmt.Printf("\nSweet, you can set this via --network=%s next time!\n\n", w.network)
break
}
log.Error("I also like to live dangerously, still no spaces")
log.Error("I also like to live dangerously, still no spaces or hyphens")
}
}
log.Info("Administering Ethereum network", "name", w.network)

View File

@@ -23,6 +23,7 @@ import (
"os"
"reflect"
"strconv"
"strings"
"unicode"
cli "gopkg.in/urfave/cli.v1"
@@ -97,10 +98,15 @@ func buildConfig(ctx *cli.Context) (config *bzzapi.Config, err error) {
config = bzzapi.NewDefaultConfig()
//first load settings from config file (if provided)
config, err = configFileOverride(config, ctx)
if err != nil {
return nil, err
}
//override settings provided by environment variables
config = envVarsOverride(config)
//override settings provided by command line
config = cmdLineOverride(config, ctx)
//validate configuration parameters
err = validateConfig(config)
return
}
@@ -194,12 +200,16 @@ func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Con
utils.Fatalf(SWARM_ERR_SWAP_SET_NO_API)
}
//EnsApi can be set to "", so can't check for empty string, as it is allowed!
if ctx.GlobalIsSet(EnsAPIFlag.Name) {
currentConfig.EnsApi = ctx.GlobalString(EnsAPIFlag.Name)
ensAPIs := ctx.GlobalStringSlice(EnsAPIFlag.Name)
// preserve backward compatibility to disable ENS with --ens-api=""
if len(ensAPIs) == 1 && ensAPIs[0] == "" {
ensAPIs = nil
}
currentConfig.EnsAPIs = ensAPIs
}
if ensaddr := ctx.GlobalString(EnsAddrFlag.Name); ensaddr != "" {
if ensaddr := ctx.GlobalString(DeprecatedEnsAddrFlag.Name); ensaddr != "" {
currentConfig.EnsRoot = common.HexToAddress(ensaddr)
}
@@ -266,9 +276,8 @@ func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) {
utils.Fatalf(SWARM_ERR_SWAP_SET_NO_API)
}
//EnsApi can be set to "", so can't check for empty string, as it is allowed
if ensapi, exists := os.LookupEnv(SWARM_ENV_ENS_API); exists {
currentConfig.EnsApi = ensapi
if ensapi := os.Getenv(SWARM_ENV_ENS_API); ensapi != "" {
currentConfig.EnsAPIs = strings.Split(ensapi, ",")
}
if ensaddr := os.Getenv(SWARM_ENV_ENS_ADDR); ensaddr != "" {
@@ -309,6 +318,43 @@ func checkDeprecated(ctx *cli.Context) {
if ctx.GlobalString(DeprecatedEthAPIFlag.Name) != "" {
utils.Fatalf("--ethapi is no longer a valid command line flag, please use --ens-api and/or --swap-api.")
}
// warn if --ens-api flag is set
if ctx.GlobalString(DeprecatedEnsAddrFlag.Name) != "" {
log.Warn("--ens-addr is no longer a valid command line flag, please use --ens-api to specify contract address.")
}
}
//validate configuration parameters
func validateConfig(cfg *bzzapi.Config) (err error) {
for _, ensAPI := range cfg.EnsAPIs {
if ensAPI != "" {
if err := validateEnsAPIs(ensAPI); err != nil {
return fmt.Errorf("invalid format [tld:][contract-addr@]url for ENS API endpoint configuration %q: %v", ensAPI, err)
}
}
}
return nil
}
//validate EnsAPIs configuration parameter
func validateEnsAPIs(s string) (err error) {
// missing contract address
if strings.HasPrefix(s, "@") {
return errors.New("missing contract address")
}
// missing url
if strings.HasSuffix(s, "@") {
return errors.New("missing url")
}
// missing tld
if strings.HasPrefix(s, ":") {
return errors.New("missing tld")
}
// missing url
if strings.HasSuffix(s, ":") {
return errors.New("missing url")
}
return nil
}
//print a Config as string

View File

@@ -457,3 +457,98 @@ func TestCmdLineOverridesFile(t *testing.T) {
node.Shutdown()
}
func TestValidateConfig(t *testing.T) {
for _, c := range []struct {
cfg *api.Config
err string
}{
{
cfg: &api.Config{EnsAPIs: []string{
"/data/testnet/geth.ipc",
}},
},
{
cfg: &api.Config{EnsAPIs: []string{
"http://127.0.0.1:1234",
}},
},
{
cfg: &api.Config{EnsAPIs: []string{
"ws://127.0.0.1:1234",
}},
},
{
cfg: &api.Config{EnsAPIs: []string{
"test:/data/testnet/geth.ipc",
}},
},
{
cfg: &api.Config{EnsAPIs: []string{
"test:ws://127.0.0.1:1234",
}},
},
{
cfg: &api.Config{EnsAPIs: []string{
"314159265dD8dbb310642f98f50C066173C1259b@/data/testnet/geth.ipc",
}},
},
{
cfg: &api.Config{EnsAPIs: []string{
"314159265dD8dbb310642f98f50C066173C1259b@http://127.0.0.1:1234",
}},
},
{
cfg: &api.Config{EnsAPIs: []string{
"314159265dD8dbb310642f98f50C066173C1259b@ws://127.0.0.1:1234",
}},
},
{
cfg: &api.Config{EnsAPIs: []string{
"test:314159265dD8dbb310642f98f50C066173C1259b@/data/testnet/geth.ipc",
}},
},
{
cfg: &api.Config{EnsAPIs: []string{
"eth:314159265dD8dbb310642f98f50C066173C1259b@http://127.0.0.1:1234",
}},
},
{
cfg: &api.Config{EnsAPIs: []string{
"eth:314159265dD8dbb310642f98f50C066173C1259b@ws://127.0.0.1:12344",
}},
},
{
cfg: &api.Config{EnsAPIs: []string{
"eth:",
}},
err: "invalid format [tld:][contract-addr@]url for ENS API endpoint configuration \"eth:\": missing url",
},
{
cfg: &api.Config{EnsAPIs: []string{
"314159265dD8dbb310642f98f50C066173C1259b@",
}},
err: "invalid format [tld:][contract-addr@]url for ENS API endpoint configuration \"314159265dD8dbb310642f98f50C066173C1259b@\": missing url",
},
{
cfg: &api.Config{EnsAPIs: []string{
":314159265dD8dbb310642f98f50C066173C1259",
}},
err: "invalid format [tld:][contract-addr@]url for ENS API endpoint configuration \":314159265dD8dbb310642f98f50C066173C1259\": missing tld",
},
{
cfg: &api.Config{EnsAPIs: []string{
"@/data/testnet/geth.ipc",
}},
err: "invalid format [tld:][contract-addr@]url for ENS API endpoint configuration \"@/data/testnet/geth.ipc\": missing contract address",
},
} {
err := validateConfig(c.cfg)
if c.err != "" && err.Error() != c.err {
t.Errorf("expected error %q, got %q", c.err, err)
}
if c.err == "" && err != nil {
t.Errorf("unexpected error %q", err)
}
}
}

View File

@@ -17,11 +17,9 @@
package main
import (
"context"
"crypto/ecdsa"
"fmt"
"io/ioutil"
"math/big"
"os"
"os/signal"
"runtime"
@@ -29,14 +27,12 @@ import (
"strconv"
"strings"
"syscall"
"time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/contracts/ens"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/debug"
@@ -45,9 +41,9 @@ import (
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm"
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics"
"gopkg.in/urfave/cli.v1"
)
@@ -110,16 +106,11 @@ var (
Usage: "Swarm Syncing enabled (default true)",
EnvVar: SWARM_ENV_SYNC_ENABLE,
}
EnsAPIFlag = cli.StringFlag{
EnsAPIFlag = cli.StringSliceFlag{
Name: "ens-api",
Usage: "URL of the Ethereum API provider to use for ENS record lookups",
Usage: "ENS API endpoint for a TLD and with contract address, can be repeated, format [tld:][contract-addr@]url",
EnvVar: SWARM_ENV_ENS_API,
}
EnsAddrFlag = cli.StringFlag{
Name: "ens-addr",
Usage: "ENS contract address (default is detected as testnet or mainnet using --ens-api)",
EnvVar: SWARM_ENV_ENS_ADDR,
}
SwarmApiFlag = cli.StringFlag{
Name: "bzzapi",
Usage: "Swarm HTTP endpoint",
@@ -156,6 +147,10 @@ var (
Name: "ethapi",
Usage: "DEPRECATED: please use --ens-api and --swap-api",
}
DeprecatedEnsAddrFlag = cli.StringFlag{
Name: "ens-addr",
Usage: "DEPRECATED: ENS contract address, please use --ens-api with contract address according to its format",
}
)
//declare a few constant error messages, useful for later error check comparisons in test
@@ -343,7 +338,6 @@ DEPRECATED: use 'swarm db clean'.
// bzzd-specific flags
CorsStringFlag,
EnsAPIFlag,
EnsAddrFlag,
SwarmTomlConfigPathFlag,
SwarmConfigPathFlag,
SwarmSwapEnabledFlag,
@@ -363,11 +357,17 @@ DEPRECATED: use 'swarm db clean'.
SwarmUploadMimeType,
//deprecated flags
DeprecatedEthAPIFlag,
DeprecatedEnsAddrFlag,
}
app.Flags = append(app.Flags, debug.Flags...)
app.Flags = append(app.Flags, swarmmetrics.Flags...)
app.Before = func(ctx *cli.Context) error {
runtime.GOMAXPROCS(runtime.NumCPU())
return debug.Setup(ctx)
if err := debug.Setup(ctx); err != nil {
return err
}
swarmmetrics.Setup(ctx)
return nil
}
app.After = func(ctx *cli.Context) error {
debug.Exit()
@@ -448,38 +448,6 @@ func bzzd(ctx *cli.Context) error {
return nil
}
// detectEnsAddr determines the ENS contract address by getting both the
// version and genesis hash using the client and matching them to either
// mainnet or testnet addresses
func detectEnsAddr(client *rpc.Client) (common.Address, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var version string
if err := client.CallContext(ctx, &version, "net_version"); err != nil {
return common.Address{}, err
}
block, err := ethclient.NewClient(client).BlockByNumber(ctx, big.NewInt(0))
if err != nil {
return common.Address{}, err
}
switch {
case version == "1" && block.Hash() == params.MainnetGenesisHash:
log.Info("using Mainnet ENS contract address", "addr", ens.MainNetAddress)
return ens.MainNetAddress, nil
case version == "3" && block.Hash() == params.TestnetGenesisHash:
log.Info("using Testnet ENS contract address", "addr", ens.TestNetAddress)
return ens.TestNetAddress, nil
default:
return common.Address{}, fmt.Errorf("unknown version and genesis hash: %s %s", version, block.Hash())
}
}
func registerBzzService(bzzconfig *bzzapi.Config, ctx *cli.Context, stack *node.Node) {
//define the swarm service boot function
@@ -494,27 +462,7 @@ func registerBzzService(bzzconfig *bzzapi.Config, ctx *cli.Context, stack *node.
}
}
var ensClient *ethclient.Client
if bzzconfig.EnsApi != "" {
log.Info("connecting to ENS API", "url", bzzconfig.EnsApi)
client, err := rpc.Dial(bzzconfig.EnsApi)
if err != nil {
return nil, fmt.Errorf("error connecting to ENS API %s: %s", bzzconfig.EnsApi, err)
}
ensClient = ethclient.NewClient(client)
//no ENS root address set yet
if bzzconfig.EnsRoot == (common.Address{}) {
ensAddr, err := detectEnsAddr(client)
if err == nil {
bzzconfig.EnsRoot = ensAddr
} else {
log.Warn(fmt.Sprintf("could not determine ENS contract address, using default %s", bzzconfig.EnsRoot), "err", err)
}
}
}
return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, bzzconfig.SwapEnabled, bzzconfig.SyncEnabled, bzzconfig.Cors)
return swarm.NewSwarm(ctx, swapClient, bzzconfig)
}
//register within the ethereum node
if err := stack.Register(boot); err != nil {

View File

@@ -35,7 +35,7 @@ const bzzManifestJSON = "application/bzz-manifest+json"
func add(ctx *cli.Context) {
args := ctx.Args()
if len(args) < 3 {
utils.Fatalf("Need atleast three arguments <MHASH> <path> <HASH> [<content-type>]")
utils.Fatalf("Need at least three arguments <MHASH> <path> <HASH> [<content-type>]")
}
var (
@@ -69,7 +69,7 @@ func update(ctx *cli.Context) {
args := ctx.Args()
if len(args) < 3 {
utils.Fatalf("Need atleast three arguments <MHASH> <path> <HASH>")
utils.Fatalf("Need at least three arguments <MHASH> <path> <HASH>")
}
var (
@@ -101,7 +101,7 @@ func update(ctx *cli.Context) {
func remove(ctx *cli.Context) {
args := ctx.Args()
if len(args) < 2 {
utils.Fatalf("Need atleast two arguments <MHASH> <path>")
utils.Fatalf("Need at least two arguments <MHASH> <path>")
}
var (

View File

@@ -25,9 +25,13 @@ import (
"os/signal"
"runtime"
"strings"
"syscall"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
@@ -64,7 +68,7 @@ func StartNode(stack *node.Node) {
}
go func() {
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, os.Interrupt)
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(sigc)
<-sigc
log.Info("Got interrupt, shutting down...")
@@ -85,7 +89,7 @@ func ImportChain(chain *core.BlockChain, fn string) error {
// If a signal is received, the import will stop at the next batch.
interrupt := make(chan os.Signal, 1)
stop := make(chan struct{})
signal.Notify(interrupt, os.Interrupt)
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(interrupt)
defer close(interrupt)
go func() {
@@ -104,6 +108,8 @@ func ImportChain(chain *core.BlockChain, fn string) error {
}
log.Info("Importing blockchain", "file", fn)
// Open the file handle and potentially unwrap the gzip stream
fh, err := os.Open(fn)
if err != nil {
return err
@@ -179,8 +185,12 @@ func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block
return nil
}
// ExportChain exports a blockchain into the specified file, truncating any data
// already present in the file.
func ExportChain(blockchain *core.BlockChain, fn string) error {
log.Info("Exporting blockchain", "file", fn)
// Open the file handle and potentially wrap with a gzip stream
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
if err != nil {
return err
@@ -192,7 +202,7 @@ func ExportChain(blockchain *core.BlockChain, fn string) error {
writer = gzip.NewWriter(writer)
defer writer.(*gzip.Writer).Close()
}
// Iterate over the blocks and export them
if err := blockchain.Export(writer); err != nil {
return err
}
@@ -201,9 +211,12 @@ func ExportChain(blockchain *core.BlockChain, fn string) error {
return nil
}
// ExportAppendChain exports a blockchain into the specified file, appending to
// the file if data already exists in it.
func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error {
log.Info("Exporting blockchain", "file", fn)
// TODO verify mode perms
// Open the file handle and potentially wrap with a gzip stream
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
if err != nil {
return err
@@ -215,10 +228,86 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
writer = gzip.NewWriter(writer)
defer writer.(*gzip.Writer).Close()
}
// Iterate over the blocks and export them
if err := blockchain.ExportN(writer, first, last); err != nil {
return err
}
log.Info("Exported blockchain to", "file", fn)
return nil
}
// ImportPreimages imports a batch of exported hash preimages into the database.
func ImportPreimages(db *ethdb.LDBDatabase, fn string) error {
log.Info("Importing preimages", "file", fn)
// Open the file handle and potentially unwrap the gzip stream
fh, err := os.Open(fn)
if err != nil {
return err
}
defer fh.Close()
var reader io.Reader = fh
if strings.HasSuffix(fn, ".gz") {
if reader, err = gzip.NewReader(reader); err != nil {
return err
}
}
stream := rlp.NewStream(reader, 0)
// Import the preimages in batches to prevent disk trashing
preimages := make(map[common.Hash][]byte)
for {
// Read the next entry and ensure it's not junk
var blob []byte
if err := stream.Decode(&blob); err != nil {
if err == io.EOF {
break
}
return err
}
// Accumulate the preimages and flush when enough ws gathered
preimages[crypto.Keccak256Hash(blob)] = common.CopyBytes(blob)
if len(preimages) > 1024 {
if err := core.WritePreimages(db, 0, preimages); err != nil {
return err
}
preimages = make(map[common.Hash][]byte)
}
}
// Flush the last batch preimage data
if len(preimages) > 0 {
return core.WritePreimages(db, 0, preimages)
}
return nil
}
// ExportPreimages exports all known hash preimages into the specified file,
// truncating any data already present in the file.
func ExportPreimages(db *ethdb.LDBDatabase, fn string) error {
log.Info("Exporting preimages", "file", fn)
// Open the file handle and potentially wrap with a gzip stream
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
if err != nil {
return err
}
defer fh.Close()
var writer io.Writer = fh
if strings.HasSuffix(fn, ".gz") {
writer = gzip.NewWriter(writer)
defer writer.(*gzip.Writer).Close()
}
// Iterate over the preimages and export them
it := db.NewIteratorWithPrefix([]byte("secure-key-"))
for it.Next() {
if err := rlp.Encode(writer, it.Value()); err != nil {
return err
}
}
log.Info("Exported preimages", "file", fn)
return nil
}

View File

@@ -55,7 +55,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil"
"github.com/ethereum/go-ethereum/params"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
"gopkg.in/urfave/cli.v1"
)
@@ -209,11 +209,6 @@ var (
Usage: "Dashboard metrics collection refresh rate",
Value: dashboard.DefaultConfig.Refresh,
}
DashboardAssetsFlag = cli.StringFlag{
Name: "dashboard.assets",
Usage: "Developer flag to serve the dashboard from the local file system",
Value: dashboard.DefaultConfig.Assets,
}
// Ethash settings
EthashCacheDirFlag = DirectoryFlag{
Name: "ethash.cachedir",
@@ -400,7 +395,7 @@ var (
RPCVirtualHostsFlag = cli.StringFlag{
Name: "rpcvhosts",
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
Value: "localhost",
Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","),
}
RPCApiFlag = cli.StringFlag{
Name: "rpcapi",
@@ -695,8 +690,9 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
if ctx.GlobalIsSet(RPCApiFlag.Name) {
cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name))
}
cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(RPCVirtualHostsFlag.Name))
if ctx.GlobalIsSet(RPCVirtualHostsFlag.Name) {
cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(RPCVirtualHostsFlag.Name))
}
}
// setWS creates the WebSocket RPC listener interface string from the set
@@ -818,6 +814,9 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
if ctx.GlobalIsSet(MaxPeersFlag.Name) {
cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name)
if lightServer && !ctx.GlobalIsSet(LightPeersFlag.Name) {
cfg.MaxPeers += lightPeers
}
} else {
if lightServer {
cfg.MaxPeers += lightPeers
@@ -1119,7 +1118,6 @@ func SetDashboardConfig(ctx *cli.Context, cfg *dashboard.Config) {
cfg.Host = ctx.GlobalString(DashboardAddrFlag.Name)
cfg.Port = ctx.GlobalInt(DashboardPortFlag.Name)
cfg.Refresh = ctx.GlobalDuration(DashboardRefreshFlag.Name)
cfg.Assets = ctx.GlobalString(DashboardAssetsFlag.Name)
}
// RegisterEthService adds an Ethereum client to the stack.

View File

@@ -22,6 +22,7 @@ package main
import (
"bufio"
"crypto/ecdsa"
crand "crypto/rand"
"crypto/sha512"
"encoding/binary"
"encoding/hex"
@@ -48,6 +49,7 @@ import (
)
const quitCommand = "~Q"
const entropySize = 32
// singletons
var (
@@ -55,6 +57,7 @@ var (
shh *whisper.Whisper
done chan struct{}
mailServer mailserver.WMailServer
entropy [entropySize]byte
input = bufio.NewReader(os.Stdin)
)
@@ -76,14 +79,15 @@ var (
// cmd arguments
var (
bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't actively connect to peers, wait for incoming connections")
forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither send nor decrypt messages")
bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't initiate connection to peers, just wait for incoming connections")
forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither encrypt nor decrypt messages")
mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand")
requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server")
asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption")
generateKey = flag.Bool("generatekey", false, "generate and show the private key")
fileExMode = flag.Bool("fileexchange", false, "file exchange mode")
testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics")
fileReader = flag.Bool("filereader", false, "load and decrypt messages saved as files, display as plain text")
testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics (password, etc.)")
echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics")
argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level")
@@ -99,13 +103,14 @@ var (
argIDFile = flag.String("idfile", "", "file name with node id (private key)")
argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)")
argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)")
argSaveDir = flag.String("savedir", "", "directory where incoming messages will be saved as files")
argSaveDir = flag.String("savedir", "", "directory where all incoming messages will be saved as files")
)
func main() {
processArgs()
initialize()
run()
shutdown()
}
func processArgs() {
@@ -192,6 +197,8 @@ func initialize() {
if len(*argIP) == 0 {
argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ")
}
} else if *fileReader {
*bootstrapMode = true
} else {
if len(*argEnode) == 0 {
argEnode = scanLineA("Please enter the peer's enode: ")
@@ -200,11 +207,6 @@ func initialize() {
peers = append(peers, peer)
}
cfg := &whisper.Config{
MaxMessageSize: uint32(*argMaxSize),
MinimumAcceptedPOW: *argPoW,
}
if *mailServerMode {
if len(msPassword) == 0 {
msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
@@ -212,14 +214,15 @@ func initialize() {
utils.Fatalf("Failed to read Mail Server password: %s", err)
}
}
shh = whisper.New(cfg)
shh.RegisterServer(&mailServer)
mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
} else {
shh = whisper.New(cfg)
}
cfg := &whisper.Config{
MaxMessageSize: uint32(*argMaxSize),
MinimumAcceptedPOW: *argPoW,
}
shh = whisper.New(cfg)
if *argPoW != whisper.DefaultMinimumPoW {
err := shh.SetMinimumPoW(*argPoW)
if err != nil {
@@ -261,11 +264,21 @@ func initialize() {
maxPeers = 800
}
_, err = crand.Read(entropy[:])
if err != nil {
utils.Fatalf("crypto/rand failed: %s", err)
}
if *mailServerMode {
shh.RegisterServer(&mailServer)
mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
}
server = &p2p.Server{
Config: p2p.Config{
PrivateKey: nodeid,
MaxPeers: maxPeers,
Name: common.MakeName("wnode", "5.0"),
Name: common.MakeName("wnode", "6.0"),
Protocols: shh.Protocols(),
ListenAddr: *argIP,
NAT: nat.Any(),
@@ -276,10 +289,11 @@ func initialize() {
}
}
func startServer() {
func startServer() error {
err := server.Start()
if err != nil {
utils.Fatalf("Failed to start Whisper peer: %s.", err)
fmt.Printf("Failed to start Whisper peer: %s.", err)
return err
}
fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey)))
@@ -295,9 +309,14 @@ func startServer() {
configureNode()
}
if !*forwarderMode {
if *fileExMode {
fmt.Printf("Please type the file name to be send. To quit type: '%s'\n", quitCommand)
} else if *fileReader {
fmt.Printf("Please type the file name to be decrypted. To quit type: '%s'\n", quitCommand)
} else if !*forwarderMode {
fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand)
}
return nil
}
func isKeyValid(k *ecdsa.PublicKey) bool {
@@ -411,8 +430,10 @@ func waitForConnection(timeout bool) {
}
func run() {
defer mailServer.Close()
startServer()
err := startServer()
if err != nil {
return
}
defer server.Stop()
shh.Start(nil)
defer shh.Stop()
@@ -425,21 +446,26 @@ func run() {
requestExpiredMessagesLoop()
} else if *fileExMode {
sendFilesLoop()
} else if *fileReader {
fileReaderLoop()
} else {
sendLoop()
}
}
func shutdown() {
close(done)
mailServer.Close()
}
func sendLoop() {
for {
s := scanLine("")
if s == quitCommand {
fmt.Println("Quit command received")
close(done)
break
return
}
sendMsg([]byte(s))
if *asymmetricMode {
// print your own message for convenience,
// because in asymmetric mode it is impossible to decrypt it
@@ -455,13 +481,11 @@ func sendFilesLoop() {
s := scanLine("")
if s == quitCommand {
fmt.Println("Quit command received")
close(done)
break
return
}
b, err := ioutil.ReadFile(s)
if err != nil {
fmt.Printf(">>> Error: %s \n", err)
continue
} else {
h := sendMsg(b)
if (h == common.Hash{}) {
@@ -475,6 +499,38 @@ func sendFilesLoop() {
}
}
func fileReaderLoop() {
watcher1 := shh.GetFilter(symFilterID)
watcher2 := shh.GetFilter(asymFilterID)
if watcher1 == nil && watcher2 == nil {
fmt.Println("Error: neither symmetric nor asymmetric filter is installed")
return
}
for {
s := scanLine("")
if s == quitCommand {
fmt.Println("Quit command received")
return
}
raw, err := ioutil.ReadFile(s)
if err != nil {
fmt.Printf(">>> Error: %s \n", err)
} else {
env := whisper.Envelope{Data: raw} // the topic is zero
msg := env.Open(watcher1) // force-open envelope regardless of the topic
if msg == nil {
msg = env.Open(watcher2)
}
if msg == nil {
fmt.Printf(">>> Error: failed to decrypt the message \n")
} else {
printMessageInfo(msg)
}
}
}
}
func scanLine(prompt string) string {
if len(prompt) > 0 {
fmt.Print(prompt)
@@ -517,6 +573,7 @@ func sendMsg(payload []byte) common.Hash {
if err != nil {
utils.Fatalf("failed to create new message: %s", err)
}
envelope, err := msg.Wrap(&params)
if err != nil {
fmt.Printf("failed to seal message: %v \n", err)
@@ -548,21 +605,21 @@ func messageLoop() {
for {
select {
case <-ticker.C:
messages := sf.Retrieve()
m1 := sf.Retrieve()
m2 := af.Retrieve()
messages := append(m1, m2...)
for _, msg := range messages {
if *fileExMode || len(msg.Payload) > 2048 {
writeMessageToFile(*argSaveDir, msg)
} else {
reportedOnce := false
if !*fileExMode && len(msg.Payload) <= 2048 {
printMessageInfo(msg)
reportedOnce = true
}
}
messages = af.Retrieve()
for _, msg := range messages {
if *fileExMode || len(msg.Payload) > 2048 {
writeMessageToFile(*argSaveDir, msg)
} else {
printMessageInfo(msg)
// All messages are saved upon specifying argSaveDir.
// fileExMode only specifies how messages are displayed on the console after they are saved.
// if fileExMode == true, only the hashes are displayed, since messages might be too big.
if len(*argSaveDir) > 0 {
writeMessageToFile(*argSaveDir, msg, !reportedOnce)
}
}
case <-done:
@@ -587,7 +644,11 @@ func printMessageInfo(msg *whisper.ReceivedMessage) {
}
}
func writeMessageToFile(dir string, msg *whisper.ReceivedMessage) {
func writeMessageToFile(dir string, msg *whisper.ReceivedMessage, show bool) {
if len(dir) == 0 {
return
}
timestamp := fmt.Sprintf("%d", msg.Sent)
name := fmt.Sprintf("%x", msg.EnvelopeHash)
@@ -596,27 +657,32 @@ func writeMessageToFile(dir string, msg *whisper.ReceivedMessage) {
address = crypto.PubkeyToAddress(*msg.Src)
}
if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
// message from myself: don't save, only report
fmt.Printf("\n%s <%x>: message received: '%s'\n", timestamp, address, name)
} else if len(dir) > 0 {
fullpath := filepath.Join(dir, name)
err := ioutil.WriteFile(fullpath, msg.Payload, 0644)
if err != nil {
fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err)
} else {
fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(msg.Payload))
}
} else {
fmt.Printf("\n%s {%x}: big message received (%d bytes), but not saved: %s\n", timestamp, address, len(msg.Payload), name)
env := shh.GetEnvelope(msg.EnvelopeHash)
if env == nil {
fmt.Printf("\nUnexpected error: envelope not found: %x\n", msg.EnvelopeHash)
return
}
// this is a sample code; uncomment if you don't want to save your own messages.
//if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
// fmt.Printf("\n%s <%x>: message from myself received, not saved: '%s'\n", timestamp, address, name)
// return
//}
fullpath := filepath.Join(dir, name)
err := ioutil.WriteFile(fullpath, env.Data, 0644)
if err != nil {
fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err)
} else if show {
fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(env.Data))
}
}
func requestExpiredMessagesLoop() {
var key, peerID []byte
var key, peerID, bloom []byte
var timeLow, timeUpp uint32
var t string
var xt, empty whisper.TopicType
var xt whisper.TopicType
keyID, err := shh.AddSymKeyFromPassword(msPassword)
if err != nil {
@@ -632,31 +698,37 @@ func requestExpiredMessagesLoop() {
for {
timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ")
timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ")
t = scanLine("Please enter the topic (hexadecimal): ")
if len(t) >= whisper.TopicLength*2 {
t = scanLine("Enter the topic (hex). Press enter to request all messages, regardless of the topic: ")
if len(t) == whisper.TopicLength*2 {
x, err := hex.DecodeString(t)
if err != nil {
utils.Fatalf("Failed to parse the topic: %s", err)
fmt.Printf("Failed to parse the topic: %s \n", err)
continue
}
xt = whisper.BytesToTopic(x)
bloom = whisper.TopicToBloom(xt)
obfuscateBloom(bloom)
} else if len(t) == 0 {
bloom = whisper.MakeFullNodeBloom()
} else {
fmt.Println("Error: topic is invalid, request aborted")
continue
}
if timeUpp == 0 {
timeUpp = 0xFFFFFFFF
}
data := make([]byte, 8+whisper.TopicLength)
data := make([]byte, 8, 8+whisper.BloomFilterSize)
binary.BigEndian.PutUint32(data, timeLow)
binary.BigEndian.PutUint32(data[4:], timeUpp)
copy(data[8:], xt[:])
if xt == empty {
data = data[:8]
}
data = append(data, bloom...)
var params whisper.MessageParams
params.PoW = *argServerPoW
params.Payload = data
params.KeySym = key
params.Src = nodeid
params.Src = asymKey
params.WorkTime = 5
msg, err := whisper.NewSentMessage(&params)
@@ -684,3 +756,20 @@ func extractIDFromEnode(s string) []byte {
}
return n.ID[:]
}
// obfuscateBloom adds 16 random bits to the the bloom
// filter, in order to obfuscate the containing topics.
// it does so deterministically within every session.
// despite additional bits, it will match on average
// 32000 times less messages than full node's bloom filter.
func obfuscateBloom(bloom []byte) {
const half = entropySize / 2
for i := 0; i < half; i++ {
x := int(entropy[i])
if entropy[half+i] < 128 {
x += 256
}
bloom[x/8] = 1 << uint(x%8) // set the bit number X
}
}

View File

@@ -25,6 +25,6 @@ var (
Big3 = big.NewInt(3)
Big0 = big.NewInt(0)
Big32 = big.NewInt(32)
Big256 = big.NewInt(0xff)
Big256 = big.NewInt(256)
Big257 = big.NewInt(257)
)

View File

@@ -65,7 +65,6 @@ type solcOutput struct {
func (s *Solidity) makeArgs() []string {
p := []string{
"--combined-json", "bin,abi,userdoc,devdoc",
"--add-std", // include standard lib contracts
"--optimize", // code optimizer switched on
}
if s.Major > 0 || s.Minor > 4 || s.Patch > 6 {

View File

@@ -23,8 +23,10 @@ import (
"math/rand"
"reflect"
"encoding/json"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto/sha3"
"strings"
)
const (
@@ -45,9 +47,8 @@ func BytesToHash(b []byte) Hash {
h.SetBytes(b)
return h
}
func StringToHash(s string) Hash { return BytesToHash([]byte(s)) }
func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
// Get the string representation of the underlying hash
func (h Hash) Str() string { return string(h[:]) }
@@ -143,9 +144,8 @@ func BytesToAddress(b []byte) Address {
a.SetBytes(b)
return a
}
func StringToAddress(s string) Address { return BytesToAddress([]byte(s)) }
func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) }
func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) }
func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) }
func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) }
// IsHexAddress verifies whether a string can represent a valid hex-encoded
// Ethereum address or not.
@@ -240,3 +240,63 @@ func (a *UnprefixedAddress) UnmarshalText(input []byte) error {
func (a UnprefixedAddress) MarshalText() ([]byte, error) {
return []byte(hex.EncodeToString(a[:])), nil
}
// MixedcaseAddress retains the original string, which may or may not be
// correctly checksummed
type MixedcaseAddress struct {
addr Address
original string
}
// NewMixedcaseAddress constructor (mainly for testing)
func NewMixedcaseAddress(addr Address) MixedcaseAddress {
return MixedcaseAddress{addr: addr, original: addr.Hex()}
}
// NewMixedcaseAddressFromString is mainly meant for unit-testing
func NewMixedcaseAddressFromString(hexaddr string) (*MixedcaseAddress, error) {
if !IsHexAddress(hexaddr) {
return nil, fmt.Errorf("Invalid address")
}
a := FromHex(hexaddr)
return &MixedcaseAddress{addr: BytesToAddress(a), original: hexaddr}, nil
}
// UnmarshalJSON parses MixedcaseAddress
func (ma *MixedcaseAddress) UnmarshalJSON(input []byte) error {
if err := hexutil.UnmarshalFixedJSON(addressT, input, ma.addr[:]); err != nil {
return err
}
return json.Unmarshal(input, &ma.original)
}
// MarshalJSON marshals the original value
func (ma *MixedcaseAddress) MarshalJSON() ([]byte, error) {
if strings.HasPrefix(ma.original, "0x") || strings.HasPrefix(ma.original, "0X") {
return json.Marshal(fmt.Sprintf("0x%s", ma.original[2:]))
}
return json.Marshal(fmt.Sprintf("0x%s", ma.original))
}
// Address returns the address
func (ma *MixedcaseAddress) Address() Address {
return ma.addr
}
// String implements fmt.Stringer
func (ma *MixedcaseAddress) String() string {
if ma.ValidChecksum() {
return fmt.Sprintf("%s [chksum ok]", ma.original)
}
return fmt.Sprintf("%s [chksum INVALID]", ma.original)
}
// ValidChecksum returns true if the address has valid checksum
func (ma *MixedcaseAddress) ValidChecksum() bool {
return ma.original == ma.addr.Hex()
}
// Original returns the mixed-case input string
func (ma *MixedcaseAddress) Original() string {
return ma.original
}

View File

@@ -18,6 +18,7 @@ package common
import (
"encoding/json"
"math/big"
"strings"
"testing"
@@ -149,3 +150,46 @@ func BenchmarkAddressHex(b *testing.B) {
testAddr.Hex()
}
}
func TestMixedcaseAccount_Address(t *testing.T) {
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
// Note: 0X{checksum_addr} is not valid according to spec above
var res []struct {
A MixedcaseAddress
Valid bool
}
if err := json.Unmarshal([]byte(`[
{"A" : "0xae967917c465db8578ca9024c205720b1a3651A9", "Valid": false},
{"A" : "0xAe967917c465db8578ca9024c205720b1a3651A9", "Valid": true},
{"A" : "0XAe967917c465db8578ca9024c205720b1a3651A9", "Valid": false},
{"A" : "0x1111111111111111111112222222222223333323", "Valid": true}
]`), &res); err != nil {
t.Fatal(err)
}
for _, r := range res {
if got := r.A.ValidChecksum(); got != r.Valid {
t.Errorf("Expected checksum %v, got checksum %v, input %v", r.Valid, got, r.A.String())
}
}
//These should throw exceptions:
var r2 []MixedcaseAddress
for _, r := range []string{
`["0x11111111111111111111122222222222233333"]`, // Too short
`["0x111111111111111111111222222222222333332"]`, // Too short
`["0x11111111111111111111122222222222233333234"]`, // Too long
`["0x111111111111111111111222222222222333332344"]`, // Too long
`["1111111111111111111112222222222223333323"]`, // Missing 0x
`["x1111111111111111111112222222222223333323"]`, // Missing 0
`["0xG111111111111111111112222222222223333323"]`, //Non-hex
} {
if err := json.Unmarshal([]byte(r), &r2); err == nil {
t.Errorf("Expected failure, input %v", r)
}
}
}

View File

@@ -1,101 +0,0 @@
// Copyright 2014 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 rle implements the run-length encoding used for Ethereum data.
package rle
import (
"bytes"
"errors"
"github.com/ethereum/go-ethereum/crypto"
)
const (
token byte = 0xfe
emptyShaToken = 0xfd
emptyListShaToken = 0xfe
tokenToken = 0xff
)
var empty = crypto.Keccak256([]byte(""))
var emptyList = crypto.Keccak256([]byte{0x80})
func Decompress(dat []byte) ([]byte, error) {
buf := new(bytes.Buffer)
for i := 0; i < len(dat); i++ {
if dat[i] == token {
if i+1 < len(dat) {
switch dat[i+1] {
case emptyShaToken:
buf.Write(empty)
case emptyListShaToken:
buf.Write(emptyList)
case tokenToken:
buf.WriteByte(token)
default:
buf.Write(make([]byte, int(dat[i+1]-2)))
}
i++
} else {
return nil, errors.New("error reading bytes. token encountered without proceeding bytes")
}
} else {
buf.WriteByte(dat[i])
}
}
return buf.Bytes(), nil
}
func compressChunk(dat []byte) (ret []byte, n int) {
switch {
case dat[0] == token:
return []byte{token, tokenToken}, 1
case len(dat) > 1 && dat[0] == 0x0 && dat[1] == 0x0:
j := 0
for j <= 254 && j < len(dat) {
if dat[j] != 0 {
break
}
j++
}
return []byte{token, byte(j + 2)}, j
case len(dat) >= 32:
if dat[0] == empty[0] && bytes.Equal(dat[:32], empty) {
return []byte{token, emptyShaToken}, 32
} else if dat[0] == emptyList[0] && bytes.Equal(dat[:32], emptyList) {
return []byte{token, emptyListShaToken}, 32
}
fallthrough
default:
return dat[:1], 1
}
}
func Compress(dat []byte) []byte {
buf := new(bytes.Buffer)
i := 0
for i < len(dat) {
b, n := compressChunk(dat[i:])
buf.Write(b)
i += n
}
return buf.Bytes()
}

View File

@@ -1,50 +0,0 @@
// Copyright 2014 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 rle
import (
"testing"
checker "gopkg.in/check.v1"
)
func Test(t *testing.T) { checker.TestingT(t) }
type CompressionRleSuite struct{}
var _ = checker.Suite(&CompressionRleSuite{})
func (s *CompressionRleSuite) TestDecompressSimple(c *checker.C) {
exp := []byte{0xc5, 0xd2, 0x46, 0x1, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x3, 0xc0, 0xe5, 0x0, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x4, 0x5d, 0x85, 0xa4, 0x70}
res, err := Decompress([]byte{token, 0xfd})
c.Assert(err, checker.IsNil)
c.Assert(res, checker.DeepEquals, exp)
exp = []byte{0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x1, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21}
res, err = Decompress([]byte{token, 0xfe})
c.Assert(err, checker.IsNil)
c.Assert(res, checker.DeepEquals, exp)
res, err = Decompress([]byte{token, 0xff})
c.Assert(err, checker.IsNil)
c.Assert(res, checker.DeepEquals, []byte{token})
res, err = Decompress([]byte{token, 12})
c.Assert(err, checker.IsNil)
c.Assert(res, checker.DeepEquals, make([]byte, 10))
}

View File

@@ -19,6 +19,7 @@ package ethash
import (
"encoding/binary"
"hash"
"math/big"
"reflect"
"runtime"
"sync"
@@ -47,6 +48,48 @@ const (
loopAccesses = 64 // Number of accesses in hashimoto loop
)
// cacheSize returns the size of the ethash verification cache that belongs to a certain
// block number.
func cacheSize(block uint64) uint64 {
epoch := int(block / epochLength)
if epoch < maxEpoch {
return cacheSizes[epoch]
}
return calcCacheSize(epoch)
}
// calcCacheSize calculates the cache size for epoch. The cache size grows linearly,
// however, we always take the highest prime below the linearly growing threshold in order
// to reduce the risk of accidental regularities leading to cyclic behavior.
func calcCacheSize(epoch int) uint64 {
size := cacheInitBytes + cacheGrowthBytes*uint64(epoch) - hashBytes
for !new(big.Int).SetUint64(size / hashBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * hashBytes
}
return size
}
// datasetSize returns the size of the ethash mining dataset that belongs to a certain
// block number.
func datasetSize(block uint64) uint64 {
epoch := int(block / epochLength)
if epoch < maxEpoch {
return datasetSizes[epoch]
}
return calcDatasetSize(epoch)
}
// calcDatasetSize calculates the dataset size for epoch. The dataset size grows linearly,
// however, we always take the highest prime below the linearly growing threshold in order
// to reduce the risk of accidental regularities leading to cyclic behavior.
func calcDatasetSize(epoch int) uint64 {
size := datasetInitBytes + datasetGrowthBytes*uint64(epoch) - mixBytes
for !new(big.Int).SetUint64(size / mixBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * mixBytes
}
return size
}
// hasher is a repetitive hasher allowing the same hash data structures to be
// reused between hash runs instead of requiring new ones to be created.
type hasher func(dest []byte, data []byte)

View File

@@ -1,47 +0,0 @@
// 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/>.
// +build !go1.8
package ethash
// cacheSize calculates and returns the size of the ethash verification cache that
// belongs to a certain block number. The cache size grows linearly, however, we
// always take the highest prime below the linearly growing threshold in order to
// reduce the risk of accidental regularities leading to cyclic behavior.
func cacheSize(block uint64) uint64 {
// If we have a pre-generated value, use that
epoch := int(block / epochLength)
if epoch < maxEpoch {
return cacheSizes[epoch]
}
// We don't have a way to verify primes fast before Go 1.8
panic("fast prime testing unsupported in Go < 1.8")
}
// datasetSize calculates and returns the size of the ethash mining dataset that
// belongs to a certain block number. The dataset size grows linearly, however, we
// always take the highest prime below the linearly growing threshold in order to
// reduce the risk of accidental regularities leading to cyclic behavior.
func datasetSize(block uint64) uint64 {
// If we have a pre-generated value, use that
epoch := int(block / epochLength)
if epoch < maxEpoch {
return datasetSizes[epoch]
}
// We don't have a way to verify primes fast before Go 1.8
panic("fast prime testing unsupported in Go < 1.8")
}

View File

@@ -1,63 +0,0 @@
// 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/>.
// +build go1.8
package ethash
import "math/big"
// cacheSize returns the size of the ethash verification cache that belongs to a certain
// block number.
func cacheSize(block uint64) uint64 {
epoch := int(block / epochLength)
if epoch < maxEpoch {
return cacheSizes[epoch]
}
return calcCacheSize(epoch)
}
// calcCacheSize calculates the cache size for epoch. The cache size grows linearly,
// however, we always take the highest prime below the linearly growing threshold in order
// to reduce the risk of accidental regularities leading to cyclic behavior.
func calcCacheSize(epoch int) uint64 {
size := cacheInitBytes + cacheGrowthBytes*uint64(epoch) - hashBytes
for !new(big.Int).SetUint64(size / hashBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * hashBytes
}
return size
}
// datasetSize returns the size of the ethash mining dataset that belongs to a certain
// block number.
func datasetSize(block uint64) uint64 {
epoch := int(block / epochLength)
if epoch < maxEpoch {
return datasetSizes[epoch]
}
return calcDatasetSize(epoch)
}
// calcDatasetSize calculates the dataset size for epoch. The dataset size grows linearly,
// however, we always take the highest prime below the linearly growing threshold in order
// to reduce the risk of accidental regularities leading to cyclic behavior.
func calcDatasetSize(epoch int) uint64 {
size := datasetInitBytes + datasetGrowthBytes*uint64(epoch) - mixBytes
for !new(big.Int).SetUint64(size / mixBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * mixBytes
}
return size
}

View File

@@ -30,6 +30,22 @@ import (
"github.com/ethereum/go-ethereum/core/types"
)
// Tests whether the dataset size calculator works correctly by cross checking the
// hard coded lookup table with the value generated by it.
func TestSizeCalculations(t *testing.T) {
// Verify all the cache and dataset sizes from the lookup table.
for epoch, want := range cacheSizes {
if size := calcCacheSize(epoch); size != want {
t.Errorf("cache %d: cache size mismatch: have %d, want %d", epoch, size, want)
}
}
for epoch, want := range datasetSizes {
if size := calcDatasetSize(epoch); size != want {
t.Errorf("dataset %d: dataset size mismatch: have %d, want %d", epoch, size, want)
}
}
}
// Tests that verification caches can be correctly generated.
func TestCacheGeneration(t *testing.T) {
tests := []struct {

View File

@@ -53,7 +53,6 @@ var (
errDuplicateUncle = errors.New("duplicate uncle")
errUncleIsAncestor = errors.New("uncle is ancestor")
errDanglingUncle = errors.New("uncle's parent is not ancestor")
errNonceOutOfRange = errors.New("nonce out of range")
errInvalidDifficulty = errors.New("non-positive difficulty")
errInvalidMixDigest = errors.New("invalid mix digest")
errInvalidPoW = errors.New("invalid proof-of-work")
@@ -356,7 +355,7 @@ func calcDifficultyByzantium(time uint64, parent *types.Header) *big.Int {
if x.Cmp(params.MinimumDifficulty) < 0 {
x.Set(params.MinimumDifficulty)
}
// calculate a fake block numer for the ice-age delay:
// calculate a fake block number for the ice-age delay:
// https://github.com/ethereum/EIPs/pull/669
// fake_block_number = min(0, block.number - 3_000_000
fakeBlockNumber := new(big.Int)
@@ -474,18 +473,13 @@ func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Head
if ethash.shared != nil {
return ethash.shared.VerifySeal(chain, header)
}
// Sanity check that the block number is below the lookup table size (60M blocks)
number := header.Number.Uint64()
if number/epochLength >= maxEpoch {
// Go < 1.7 cannot calculate new cache/dataset sizes (no fast prime check)
return errNonceOutOfRange
}
// Ensure that we have a valid difficulty for the block
if header.Difficulty.Sign() <= 0 {
return errInvalidDifficulty
}
// Recompute the digest and PoW value and verify against the header
number := header.Number.Uint64()
cache := ethash.cache(number)
size := datasetSize(number)
if ethash.config.PowMode == ModeTest {

View File

@@ -35,9 +35,9 @@ import (
mmap "github.com/edsrzf/mmap-go"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/rpc"
"github.com/hashicorp/golang-lru/simplelru"
metrics "github.com/rcrowley/go-metrics"
)
var ErrInvalidDumpMagic = errors.New("invalid dump magic")

View File

@@ -26,6 +26,7 @@ import (
"regexp"
"sort"
"strings"
"syscall"
"github.com/ethereum/go-ethereum/internal/jsre"
"github.com/ethereum/go-ethereum/internal/web3ext"
@@ -332,7 +333,7 @@ func (c *Console) Interactive() {
}()
// Monitor Ctrl-C too in case the input is empty and we need to bail
abort := make(chan os.Signal, 1)
signal.Notify(abort, os.Interrupt)
signal.Notify(abort, syscall.SIGINT, syscall.SIGTERM)
// Start sending prompts to the user and reading back inputs
for {

View File

@@ -1 +0,0 @@
.vagrant

View File

@@ -1,38 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
require 'yaml'
VAGRANTFILE_API_VERSION = 2
VM_RAM = 2048
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.define "ubuntu", :primary => true do |ubuntu|
ubuntu.vm.box = "ubuntu/trusty64"
ubuntu.vm.provision "shell", :path => "provisioners/shell/ubuntu.sh"
end
config.vm.define "debian", :primary => true do |debian|
debian.vm.box = "debian/jessie64"
debian.vm.provision "shell", :path => "provisioners/shell/debian.sh"
end
config.vm.define "centos", :autostart => false do |centos|
centos.vm.box = "centos/7"
centos.vm.provision "shell", :path => "provisioners/shell/centos.sh"
end
config.vm.provider "virtualbox" do |vb|
vb.memory = VM_RAM
end
config.vm.provider "libvirt" do |lv|
lv.memory = VM_RAM
config.vm.synced_folder ".", "/home/vagrant/sync", :disabled => true
end
config.vm.synced_folder ".", "/vagrant", :disabled => true
config.vm.synced_folder "../../", "/home/vagrant/go/src/github.com/ethereum/go-ethereum"
end

View File

@@ -1,11 +0,0 @@
#!/bin/bash
sudo yum install -y git wget
sudo yum update -y
wget --continue https://storage.googleapis.com/golang/go1.8.1.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.8.1.linux-amd64.tar.gz
GETH_PATH="~vagrant/go/src/github.com/ethereum/go-ethereum/build/bin/"
echo "export PATH=$PATH:/usr/local/go/bin:$GETH_PATH" >> ~vagrant/.bashrc

View File

@@ -1,11 +0,0 @@
#!/bin/bash
sudo apt-get install -y build-essential git-all wget
sudo apt-get update
wget --continue https://storage.googleapis.com/golang/go1.8.1.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.8.1.linux-amd64.tar.gz
GETH_PATH="~vagrant/go/src/github.com/ethereum/go-ethereum/build/bin/"
echo "export PATH=$PATH:/usr/local/go/bin:$GETH_PATH" >> ~vagrant/.bashrc

View File

@@ -1,11 +0,0 @@
#!/bin/bash
sudo apt-get install -y build-essential git-all wget
sudo apt-get update
wget --continue https://storage.googleapis.com/golang/go1.8.1.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.8.1.linux-amd64.tar.gz
GETH_PATH="~vagrant/go/src/github.com/ethereum/go-ethereum/build/bin/"
echo "export PATH=$PATH:/usr/local/go/bin:$GETH_PATH" >> ~vagrant/.bashrc

View File

@@ -281,8 +281,8 @@ func TestDeposit(t *testing.T) {
t.Fatalf("expected balance %v, got %v", exp, chbook.Balance())
}
// autodeposit every 30ms if new cheque issued
interval := 30 * time.Millisecond
// autodeposit every 200ms if new cheque issued
interval := 200 * time.Millisecond
chbook.AutoDeposit(interval, common.Big1, balance)
_, err = chbook.Issue(addr1, amount)
if err != nil {

View File

@@ -114,7 +114,7 @@ func (c *Compiler) Compile() (string, []error) {
}
// next returns the next token and increments the
// posititon.
// position.
func (c *Compiler) next() token {
token := c.tokens[c.pos]
c.pos++
@@ -122,7 +122,7 @@ func (c *Compiler) next() token {
}
// compile line compiles a single line instruction e.g.
// "push 1", "jump @labal".
// "push 1", "jump @label".
func (c *Compiler) compileLine() error {
n := c.next()
if n.typ != lineStart {

View File

@@ -48,7 +48,7 @@ const (
lineEnd // emitted when a line ends
invalidStatement // any invalid statement
element // any element during element parsing
label // label is emitted when a labal is found
label // label is emitted when a label is found
labelDef // label definition is emitted when a new label is found
number // number is emitted when a number is found
stringValue // stringValue is emitted when a string has been found
@@ -206,7 +206,7 @@ func lexLine(l *lexer) stateFn {
return lexComment
case isSpace(r):
l.ignore()
case isAlphaNumeric(r) || r == '_':
case isLetter(r) || r == '_':
return lexElement
case isNumber(r):
return lexNumber
@@ -278,7 +278,7 @@ func lexElement(l *lexer) stateFn {
return lexLine
}
func isAlphaNumeric(t rune) bool {
func isLetter(t rune) bool {
return unicode.IsLetter(t)
}

View File

@@ -46,7 +46,7 @@ import (
)
var (
blockInsertTimer = metrics.NewTimer("chain/inserts")
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
ErrNoGenesis = errors.New("Genesis not found in chain")
)
@@ -107,8 +107,8 @@ type BlockChain struct {
procmu sync.RWMutex // block processor lock
checkpoint int // checkpoint counts towards the new checkpoint
currentBlock *types.Block // Current head of the block chain
currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!)
currentBlock atomic.Value // Current head of the block chain
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
stateCache state.Database // State database to reuse between imports (contains state cache)
bodyCache *lru.Cache // Cache for the most recent block bodies
@@ -224,10 +224,10 @@ func (bc *BlockChain) loadLastState() error {
}
}
// Everything seems to be fine, set as the head block
bc.currentBlock = currentBlock
bc.currentBlock.Store(currentBlock)
// Restore the last known head header
currentHeader := bc.currentBlock.Header()
currentHeader := currentBlock.Header()
if head := GetHeadHeaderHash(bc.db); head != (common.Hash{}) {
if header := bc.GetHeaderByHash(head); header != nil {
currentHeader = header
@@ -236,21 +236,23 @@ func (bc *BlockChain) loadLastState() error {
bc.hc.SetCurrentHeader(currentHeader)
// Restore the last known head fast block
bc.currentFastBlock = bc.currentBlock
bc.currentFastBlock.Store(currentBlock)
if head := GetHeadFastBlockHash(bc.db); head != (common.Hash{}) {
if block := bc.GetBlockByHash(head); block != nil {
bc.currentFastBlock = block
bc.currentFastBlock.Store(block)
}
}
// Issue a status log for the user
currentFastBlock := bc.CurrentFastBlock()
headerTd := bc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
blockTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
fastTd := bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64())
blockTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
fastTd := bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64())
log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd)
log.Info("Loaded most recent local full block", "number", bc.currentBlock.Number(), "hash", bc.currentBlock.Hash(), "td", blockTd)
log.Info("Loaded most recent local fast block", "number", bc.currentFastBlock.Number(), "hash", bc.currentFastBlock.Hash(), "td", fastTd)
log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd)
log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd)
return nil
}
@@ -279,30 +281,32 @@ func (bc *BlockChain) SetHead(head uint64) error {
bc.futureBlocks.Purge()
// Rewind the block chain, ensuring we don't end up with a stateless head block
if bc.currentBlock != nil && currentHeader.Number.Uint64() < bc.currentBlock.NumberU64() {
bc.currentBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
if currentBlock := bc.CurrentBlock(); currentBlock != nil && currentHeader.Number.Uint64() < currentBlock.NumberU64() {
bc.currentBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
}
if bc.currentBlock != nil {
if _, err := state.New(bc.currentBlock.Root(), bc.stateCache); err != nil {
if currentBlock := bc.CurrentBlock(); currentBlock != nil {
if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
// Rewound state missing, rolled back to before pivot, reset to genesis
bc.currentBlock = nil
bc.currentBlock.Store(bc.genesisBlock)
}
}
// Rewind the fast block in a simpleton way to the target head
if bc.currentFastBlock != nil && currentHeader.Number.Uint64() < bc.currentFastBlock.NumberU64() {
bc.currentFastBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && currentHeader.Number.Uint64() < currentFastBlock.NumberU64() {
bc.currentFastBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
}
// If either blocks reached nil, reset to the genesis state
if bc.currentBlock == nil {
bc.currentBlock = bc.genesisBlock
if currentBlock := bc.CurrentBlock(); currentBlock == nil {
bc.currentBlock.Store(bc.genesisBlock)
}
if bc.currentFastBlock == nil {
bc.currentFastBlock = bc.genesisBlock
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock == nil {
bc.currentFastBlock.Store(bc.genesisBlock)
}
if err := WriteHeadBlockHash(bc.db, bc.currentBlock.Hash()); err != nil {
currentBlock := bc.CurrentBlock()
currentFastBlock := bc.CurrentFastBlock()
if err := WriteHeadBlockHash(bc.db, currentBlock.Hash()); err != nil {
log.Crit("Failed to reset head full block", "err", err)
}
if err := WriteHeadFastBlockHash(bc.db, bc.currentFastBlock.Hash()); err != nil {
if err := WriteHeadFastBlockHash(bc.db, currentFastBlock.Hash()); err != nil {
log.Crit("Failed to reset head fast block", "err", err)
}
return bc.loadLastState()
@@ -321,7 +325,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
}
// If all checks out, manually set the head block
bc.mu.Lock()
bc.currentBlock = block
bc.currentBlock.Store(block)
bc.mu.Unlock()
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
@@ -330,28 +334,19 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
// GasLimit returns the gas limit of the current HEAD block.
func (bc *BlockChain) GasLimit() uint64 {
bc.mu.RLock()
defer bc.mu.RUnlock()
return bc.currentBlock.GasLimit()
return bc.CurrentBlock().GasLimit()
}
// CurrentBlock retrieves the current head block of the canonical chain. The
// block is retrieved from the blockchain's internal cache.
func (bc *BlockChain) CurrentBlock() *types.Block {
bc.mu.RLock()
defer bc.mu.RUnlock()
return bc.currentBlock
return bc.currentBlock.Load().(*types.Block)
}
// CurrentFastBlock retrieves the current fast-sync head block of the canonical
// chain. The block is retrieved from the blockchain's internal cache.
func (bc *BlockChain) CurrentFastBlock() *types.Block {
bc.mu.RLock()
defer bc.mu.RUnlock()
return bc.currentFastBlock
return bc.currentFastBlock.Load().(*types.Block)
}
// SetProcessor sets the processor required for making state modifications.
@@ -416,10 +411,10 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
}
bc.genesisBlock = genesis
bc.insert(bc.genesisBlock)
bc.currentBlock = bc.genesisBlock
bc.currentBlock.Store(bc.genesisBlock)
bc.hc.SetGenesis(bc.genesisBlock.Header())
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
bc.currentFastBlock = bc.genesisBlock
bc.currentFastBlock.Store(bc.genesisBlock)
return nil
}
@@ -444,7 +439,7 @@ func (bc *BlockChain) repair(head **types.Block) error {
// Export writes the active chain to the given writer.
func (bc *BlockChain) Export(w io.Writer) error {
return bc.ExportN(w, uint64(0), bc.currentBlock.NumberU64())
return bc.ExportN(w, uint64(0), bc.CurrentBlock().NumberU64())
}
// ExportN writes a subset of the active chain to the given writer.
@@ -488,7 +483,7 @@ func (bc *BlockChain) insert(block *types.Block) {
if err := WriteHeadBlockHash(bc.db, block.Hash()); err != nil {
log.Crit("Failed to insert head block hash", "err", err)
}
bc.currentBlock = block
bc.currentBlock.Store(block)
// If the block is better than our head or is on a different chain, force update heads
if updateHeads {
@@ -497,7 +492,7 @@ func (bc *BlockChain) insert(block *types.Block) {
if err := WriteHeadFastBlockHash(bc.db, block.Hash()); err != nil {
log.Crit("Failed to insert head fast block hash", "err", err)
}
bc.currentFastBlock = block
bc.currentFastBlock.Store(block)
}
}
@@ -648,22 +643,21 @@ func (bc *BlockChain) Stop() {
bc.wg.Wait()
// Ensure the state of a recent block is also stored to disk before exiting.
// It is fine if this state does not exist (fast start/stop cycle), but it is
// advisable to leave an N block gap from the head so 1) a restart loads up
// the last N blocks as sync assistance to remote nodes; 2) a restart during
// a (small) reorg doesn't require deep reprocesses; 3) chain "repair" from
// missing states are constantly tested.
//
// This may be tuned a bit on mainnet if its too annoying to reprocess the last
// N blocks.
// We're writing three different states to catch different restart scenarios:
// - HEAD: So we don't need to reprocess any blocks in the general case
// - HEAD-1: So we don't do large reorgs if our HEAD becomes an uncle
// - HEAD-127: So we have a hard limit on the number of blocks reexecuted
if !bc.cacheConfig.Disabled {
triedb := bc.stateCache.TrieDB()
if number := bc.CurrentBlock().NumberU64(); number >= triesInMemory {
recent := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - triesInMemory + 1)
log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
if err := triedb.Commit(recent.Root(), true); err != nil {
log.Error("Failed to commit recent state trie", "err", err)
for _, offset := range []uint64{0, 1, triesInMemory - 1} {
if number := bc.CurrentBlock().NumberU64(); number > offset {
recent := bc.GetBlockByNumber(number - offset)
log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
if err := triedb.Commit(recent.Root(), true); err != nil {
log.Error("Failed to commit recent state trie", "err", err)
}
}
}
for !bc.triegc.Empty() {
@@ -715,22 +709,27 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
if currentHeader.Hash() == hash {
bc.hc.SetCurrentHeader(bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1))
}
if bc.currentFastBlock.Hash() == hash {
bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash(), bc.currentFastBlock.NumberU64()-1)
WriteHeadFastBlockHash(bc.db, bc.currentFastBlock.Hash())
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock.Hash() == hash {
newFastBlock := bc.GetBlock(currentFastBlock.ParentHash(), currentFastBlock.NumberU64()-1)
bc.currentFastBlock.Store(newFastBlock)
WriteHeadFastBlockHash(bc.db, newFastBlock.Hash())
}
if bc.currentBlock.Hash() == hash {
bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash(), bc.currentBlock.NumberU64()-1)
WriteHeadBlockHash(bc.db, bc.currentBlock.Hash())
if currentBlock := bc.CurrentBlock(); currentBlock.Hash() == hash {
newBlock := bc.GetBlock(currentBlock.ParentHash(), currentBlock.NumberU64()-1)
bc.currentBlock.Store(newBlock)
WriteHeadBlockHash(bc.db, newBlock.Hash())
}
}
}
// SetReceiptsData computes all the non-consensus fields of the receipts
func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) {
func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) error {
signer := types.MakeSigner(config, block.Number())
transactions, logIndex := block.Transactions(), uint(0)
if len(transactions) != len(receipts) {
return errors.New("transaction and receipt count mismatch")
}
for j := 0; j < len(receipts); j++ {
// The transaction hash can be retrieved from the transaction itself
@@ -758,6 +757,7 @@ func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts ty
logIndex++
}
}
return nil
}
// InsertReceiptChain attempts to complete an already existing header chain with
@@ -798,7 +798,9 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
continue
}
// Compute all the non-consensus fields of the receipts
SetReceiptsData(bc.chainConfig, block, receipts)
if err := SetReceiptsData(bc.chainConfig, block, receipts); err != nil {
return i, fmt.Errorf("failed to set receipts data: %v", err)
}
// Write all the data out into the database
if err := WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()); err != nil {
return i, fmt.Errorf("failed to write block body: %v", err)
@@ -830,11 +832,12 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
bc.mu.Lock()
head := blockChain[len(blockChain)-1]
if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
if bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64()).Cmp(td) < 0 {
currentFastBlock := bc.CurrentFastBlock()
if bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64()).Cmp(td) < 0 {
if err := WriteHeadFastBlockHash(bc.db, head.Hash()); err != nil {
log.Crit("Failed to update head fast block hash", "err", err)
}
bc.currentFastBlock = head
bc.currentFastBlock.Store(head)
}
}
bc.mu.Unlock()
@@ -881,7 +884,8 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
bc.mu.Lock()
defer bc.mu.Unlock()
localTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
currentBlock := bc.CurrentBlock()
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
externTd := new(big.Int).Add(block.Difficulty(), ptd)
// Irrelevant of the canonical status, write the block itself to the database
@@ -956,14 +960,15 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
// Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
reorg := externTd.Cmp(localTd) > 0
currentBlock = bc.CurrentBlock()
if !reorg && externTd.Cmp(localTd) == 0 {
// Split same-difficulty blocks by number, then at random
reorg = block.NumberU64() < bc.currentBlock.NumberU64() || (block.NumberU64() == bc.currentBlock.NumberU64() && mrand.Float64() < 0.5)
reorg = block.NumberU64() < currentBlock.NumberU64() || (block.NumberU64() == currentBlock.NumberU64() && mrand.Float64() < 0.5)
}
if reorg {
// Reorganise the chain if the parent is not the head block
if block.ParentHash() != bc.currentBlock.Hash() {
if err := bc.reorg(bc.currentBlock, block); err != nil {
if block.ParentHash() != currentBlock.Hash() {
if err := bc.reorg(currentBlock, block); err != nil {
return NonStatTy, err
}
}
@@ -1092,7 +1097,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
case err == consensus.ErrPrunedAncestor:
// Block competing with the canonical chain, store in the db, but don't process
// until the competitor TD goes above the canonical TD
localTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
currentBlock := bc.CurrentBlock()
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty())
if localTd.Cmp(externTd) > 0 {
if err = bc.WriteBlockWithoutState(block, externTd); err != nil {
@@ -1481,9 +1487,6 @@ func (bc *BlockChain) writeHeader(header *types.Header) error {
// CurrentHeader retrieves the current head header of the canonical chain. The
// header is retrieved from the HeaderChain's internal cache.
func (bc *BlockChain) CurrentHeader() *types.Header {
bc.mu.RLock()
defer bc.mu.RUnlock()
return bc.hc.CurrentHeader()
}

View File

@@ -34,26 +34,6 @@ import (
"github.com/ethereum/go-ethereum/params"
)
// newTestBlockChain creates a blockchain without validation.
func newTestBlockChain(fake bool) *BlockChain {
db, _ := ethdb.NewMemDatabase()
gspec := &Genesis{
Config: params.TestChainConfig,
Difficulty: big.NewInt(1),
}
gspec.MustCommit(db)
engine := ethash.NewFullFaker()
if !fake {
engine = ethash.NewTester()
}
blockchain, err := NewBlockChain(db, nil, gspec.Config, engine, vm.Config{})
if err != nil {
panic(err)
}
blockchain.SetValidator(bproc{})
return blockchain
}
// Test fork of length N starting from block i
func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, comparator func(td1, td2 *big.Int)) {
// Copy old chain up to #i into a new db
@@ -183,13 +163,18 @@ func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t *
}
func TestLastBlock(t *testing.T) {
bchain := newTestBlockChain(false)
defer bchain.Stop()
_, blockchain, err := newCanonical(ethash.NewFaker(), 0, true)
if err != nil {
t.Fatalf("failed to create pristine chain: %v", err)
}
defer blockchain.Stop()
block := makeBlockChain(bchain.CurrentBlock(), 1, ethash.NewFaker(), bchain.db, 0)[0]
bchain.insert(block)
if block.Hash() != GetHeadBlockHash(bchain.db) {
t.Errorf("Write/Get HeadBlockHash failed")
blocks := makeBlockChain(blockchain.CurrentBlock(), 1, ethash.NewFullFaker(), blockchain.db, 0)
if _, err := blockchain.InsertChain(blocks); err != nil {
t.Fatalf("Failed to insert block: %v", err)
}
if blocks[len(blocks)-1].Hash() != GetHeadBlockHash(blockchain.db) {
t.Fatalf("Write/Get HeadBlockHash failed")
}
}
@@ -337,55 +322,13 @@ func testBrokenChain(t *testing.T, full bool) {
}
}
type bproc struct{}
func (bproc) ValidateBody(*types.Block) error { return nil }
func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error {
return nil
}
func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
return nil, nil, 0, nil
}
func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header {
blocks := makeBlockChainWithDiff(genesis, d, seed)
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
headers[i] = block.Header()
}
return headers
}
func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block {
var chain []*types.Block
for i, difficulty := range d {
header := &types.Header{
Coinbase: common.Address{seed},
Number: big.NewInt(int64(i + 1)),
Difficulty: big.NewInt(int64(difficulty)),
UncleHash: types.EmptyUncleHash,
TxHash: types.EmptyRootHash,
ReceiptHash: types.EmptyRootHash,
Time: big.NewInt(int64(i) + 1),
}
if i == 0 {
header.ParentHash = genesis.Hash()
} else {
header.ParentHash = chain[i-1].Hash()
}
block := types.NewBlockWithHeader(header)
chain = append(chain, block)
}
return chain
}
// Tests that reorganising a long difficult chain after a short easy one
// overwrites the canonical numbers and links in the database.
func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false) }
func TestReorgLongBlocks(t *testing.T) { testReorgLong(t, true) }
func testReorgLong(t *testing.T, full bool) {
testReorg(t, []int{1, 2, 4}, []int{1, 2, 3, 4}, 10, full)
testReorg(t, []int64{0, 0, -9}, []int64{0, 0, 0, -9}, 393280, full)
}
// Tests that reorganising a short difficult chain after a long easy one
@@ -394,45 +337,82 @@ func TestReorgShortHeaders(t *testing.T) { testReorgShort(t, false) }
func TestReorgShortBlocks(t *testing.T) { testReorgShort(t, true) }
func testReorgShort(t *testing.T, full bool) {
testReorg(t, []int{1, 2, 3, 4}, []int{1, 10}, 11, full)
// Create a long easy chain vs. a short heavy one. Due to difficulty adjustment
// we need a fairly long chain of blocks with different difficulties for a short
// one to become heavyer than a long one. The 96 is an empirical value.
easy := make([]int64, 96)
for i := 0; i < len(easy); i++ {
easy[i] = 60
}
diff := make([]int64, len(easy)-1)
for i := 0; i < len(diff); i++ {
diff[i] = -9
}
testReorg(t, easy, diff, 12615120, full)
}
func testReorg(t *testing.T, first, second []int, td int64, full bool) {
bc := newTestBlockChain(true)
defer bc.Stop()
func testReorg(t *testing.T, first, second []int64, td int64, full bool) {
// Create a pristine chain and database
db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full)
if err != nil {
t.Fatalf("failed to create pristine chain: %v", err)
}
defer blockchain.Stop()
// Insert an easy and a difficult chain afterwards
easyBlocks, _ := GenerateChain(params.TestChainConfig, blockchain.CurrentBlock(), ethash.NewFaker(), db, len(first), func(i int, b *BlockGen) {
b.OffsetTime(first[i])
})
diffBlocks, _ := GenerateChain(params.TestChainConfig, blockchain.CurrentBlock(), ethash.NewFaker(), db, len(second), func(i int, b *BlockGen) {
b.OffsetTime(second[i])
})
if full {
bc.InsertChain(makeBlockChainWithDiff(bc.genesisBlock, first, 11))
bc.InsertChain(makeBlockChainWithDiff(bc.genesisBlock, second, 22))
if _, err := blockchain.InsertChain(easyBlocks); err != nil {
t.Fatalf("failed to insert easy chain: %v", err)
}
if _, err := blockchain.InsertChain(diffBlocks); err != nil {
t.Fatalf("failed to insert difficult chain: %v", err)
}
} else {
bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1)
bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1)
easyHeaders := make([]*types.Header, len(easyBlocks))
for i, block := range easyBlocks {
easyHeaders[i] = block.Header()
}
diffHeaders := make([]*types.Header, len(diffBlocks))
for i, block := range diffBlocks {
diffHeaders[i] = block.Header()
}
if _, err := blockchain.InsertHeaderChain(easyHeaders, 1); err != nil {
t.Fatalf("failed to insert easy chain: %v", err)
}
if _, err := blockchain.InsertHeaderChain(diffHeaders, 1); err != nil {
t.Fatalf("failed to insert difficult chain: %v", err)
}
}
// Check that the chain is valid number and link wise
if full {
prev := bc.CurrentBlock()
for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) {
prev := blockchain.CurrentBlock()
for block := blockchain.GetBlockByNumber(blockchain.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, blockchain.GetBlockByNumber(block.NumberU64()-1) {
if prev.ParentHash() != block.Hash() {
t.Errorf("parent block hash mismatch: have %x, want %x", prev.ParentHash(), block.Hash())
}
}
} else {
prev := bc.CurrentHeader()
for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) {
prev := blockchain.CurrentHeader()
for header := blockchain.GetHeaderByNumber(blockchain.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, blockchain.GetHeaderByNumber(header.Number.Uint64()-1) {
if prev.ParentHash != header.Hash() {
t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash())
}
}
}
// Make sure the chain total difficulty is the correct one
want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td))
want := new(big.Int).Add(blockchain.genesisBlock.Difficulty(), big.NewInt(td))
if full {
if have := bc.GetTdByHash(bc.CurrentBlock().Hash()); have.Cmp(want) != 0 {
if have := blockchain.GetTdByHash(blockchain.CurrentBlock().Hash()); have.Cmp(want) != 0 {
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
}
} else {
if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 {
if have := blockchain.GetTdByHash(blockchain.CurrentHeader().Hash()); have.Cmp(want) != 0 {
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
}
}
@@ -443,19 +423,28 @@ func TestBadHeaderHashes(t *testing.T) { testBadHashes(t, false) }
func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) }
func testBadHashes(t *testing.T, full bool) {
bc := newTestBlockChain(true)
defer bc.Stop()
// Create a pristine chain and database
db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full)
if err != nil {
t.Fatalf("failed to create pristine chain: %v", err)
}
defer blockchain.Stop()
// Create a chain, ban a hash and try to import
var err error
if full {
blocks := makeBlockChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10)
blocks := makeBlockChain(blockchain.CurrentBlock(), 3, ethash.NewFaker(), db, 10)
BadHashes[blocks[2].Header().Hash()] = true
_, err = bc.InsertChain(blocks)
defer func() { delete(BadHashes, blocks[2].Header().Hash()) }()
_, err = blockchain.InsertChain(blocks)
} else {
headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10)
headers := makeHeaderChain(blockchain.CurrentHeader(), 3, ethash.NewFaker(), db, 10)
BadHashes[headers[2].Hash()] = true
_, err = bc.InsertHeaderChain(headers, 1)
defer func() { delete(BadHashes, headers[2].Hash()) }()
_, err = blockchain.InsertHeaderChain(headers, 1)
}
if err != ErrBlacklistedHash {
t.Errorf("error mismatch: have: %v, want: %v", err, ErrBlacklistedHash)
@@ -468,40 +457,41 @@ func TestReorgBadHeaderHashes(t *testing.T) { testReorgBadHashes(t, false) }
func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) }
func testReorgBadHashes(t *testing.T, full bool) {
bc := newTestBlockChain(true)
defer bc.Stop()
// Create a pristine chain and database
db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full)
if err != nil {
t.Fatalf("failed to create pristine chain: %v", err)
}
// Create a chain, import and ban afterwards
headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)
blocks := makeBlockChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)
headers := makeHeaderChain(blockchain.CurrentHeader(), 4, ethash.NewFaker(), db, 10)
blocks := makeBlockChain(blockchain.CurrentBlock(), 4, ethash.NewFaker(), db, 10)
if full {
if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import blocks: %v", err)
if _, err = blockchain.InsertChain(blocks); err != nil {
t.Errorf("failed to import blocks: %v", err)
}
if bc.CurrentBlock().Hash() != blocks[3].Hash() {
t.Errorf("last block hash mismatch: have: %x, want %x", bc.CurrentBlock().Hash(), blocks[3].Header().Hash())
if blockchain.CurrentBlock().Hash() != blocks[3].Hash() {
t.Errorf("last block hash mismatch: have: %x, want %x", blockchain.CurrentBlock().Hash(), blocks[3].Header().Hash())
}
BadHashes[blocks[3].Header().Hash()] = true
defer func() { delete(BadHashes, blocks[3].Header().Hash()) }()
} else {
if _, err := bc.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to import headers: %v", err)
if _, err = blockchain.InsertHeaderChain(headers, 1); err != nil {
t.Errorf("failed to import headers: %v", err)
}
if bc.CurrentHeader().Hash() != headers[3].Hash() {
t.Errorf("last header hash mismatch: have: %x, want %x", bc.CurrentHeader().Hash(), headers[3].Hash())
if blockchain.CurrentHeader().Hash() != headers[3].Hash() {
t.Errorf("last header hash mismatch: have: %x, want %x", blockchain.CurrentHeader().Hash(), headers[3].Hash())
}
BadHashes[headers[3].Hash()] = true
defer func() { delete(BadHashes, headers[3].Hash()) }()
}
blockchain.Stop()
// Create a new BlockChain and check that it rolled back the state.
ncm, err := NewBlockChain(bc.db, nil, bc.chainConfig, ethash.NewFaker(), vm.Config{})
ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{})
if err != nil {
t.Fatalf("failed to create new chain manager: %v", err)
}
defer ncm.Stop()
if full {
if ncm.CurrentBlock().Hash() != blocks[2].Header().Hash() {
t.Errorf("last block hash mismatch: have: %x, want %x", ncm.CurrentBlock().Hash(), blocks[2].Header().Hash())
@@ -514,6 +504,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), headers[2].Hash())
}
}
ncm.Stop()
}
// Tests chain insertions in the face of one entity containing an invalid nonce.
@@ -989,10 +980,13 @@ done:
// Tests if the canonical block can be fetched from the database during chain insertion.
func TestCanonicalBlockRetrieval(t *testing.T) {
bc := newTestBlockChain(true)
defer bc.Stop()
_, blockchain, err := newCanonical(ethash.NewFaker(), 0, true)
if err != nil {
t.Fatalf("failed to create pristine chain: %v", err)
}
defer blockchain.Stop()
chain, _ := GenerateChain(bc.chainConfig, bc.genesisBlock, ethash.NewFaker(), bc.db, 10, func(i int, gen *BlockGen) {})
chain, _ := GenerateChain(blockchain.chainConfig, blockchain.genesisBlock, ethash.NewFaker(), blockchain.db, 10, func(i int, gen *BlockGen) {})
var pend sync.WaitGroup
pend.Add(len(chain))
@@ -1003,14 +997,14 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
for {
ch := GetCanonicalHash(bc.db, block.NumberU64())
ch := GetCanonicalHash(blockchain.db, block.NumberU64())
if ch == (common.Hash{}) {
continue // busy wait for canonical hash to be written
}
if ch != block.Hash() {
t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
}
fb := GetBlock(bc.db, ch, block.NumberU64())
fb := GetBlock(blockchain.db, ch, block.NumberU64())
if fb == nil {
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
}
@@ -1021,7 +1015,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
}
}(chain[i])
if _, err := bc.InsertChain(types.Blocks{chain[i]}); err != nil {
if _, err := blockchain.InsertChain(types.Blocks{chain[i]}); err != nil {
t.Fatalf("failed to insert block %d: %v", i, err)
}
}
@@ -1344,3 +1338,114 @@ func TestLargeReorgTrieGC(t *testing.T) {
}
}
}
// Benchmarks large blocks with value transfers to non-existing accounts
func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks int, recipientFn func(uint64) common.Address, dataFn func(uint64) []byte) {
var (
signer = types.HomesteadSigner{}
testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
bankFunds = big.NewInt(100000000000000000)
gspec = Genesis{
Config: params.TestChainConfig,
Alloc: GenesisAlloc{
testBankAddress: {Balance: bankFunds},
common.HexToAddress("0xc0de"): {
Code: []byte{0x60, 0x01, 0x50},
Balance: big.NewInt(0),
}, // push 1, pop
},
GasLimit: 100e6, // 100 M
}
)
// Generate the original common chain segment and the two competing forks
engine := ethash.NewFaker()
db, _ := ethdb.NewMemDatabase()
genesis := gspec.MustCommit(db)
blockGenerator := func(i int, block *BlockGen) {
block.SetCoinbase(common.Address{1})
for txi := 0; txi < numTxs; txi++ {
uniq := uint64(i*numTxs + txi)
recipient := recipientFn(uniq)
//recipient := common.BigToAddress(big.NewInt(0).SetUint64(1337 + uniq))
tx, err := types.SignTx(types.NewTransaction(uniq, recipient, big.NewInt(1), params.TxGas, big.NewInt(1), nil), signer, testBankKey)
if err != nil {
b.Error(err)
}
block.AddTx(tx)
}
}
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, numBlocks, blockGenerator)
b.StopTimer()
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Import the shared chain and the original canonical one
diskdb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
if err != nil {
b.Fatalf("failed to create tester chain: %v", err)
}
b.StartTimer()
if _, err := chain.InsertChain(shared); err != nil {
b.Fatalf("failed to insert shared chain: %v", err)
}
b.StopTimer()
if got := chain.CurrentBlock().Transactions().Len(); got != numTxs*numBlocks {
b.Fatalf("Transactions were not included, expected %d, got %d", (numTxs * numBlocks), got)
}
}
}
func BenchmarkBlockChain_1x1000ValueTransferToNonexisting(b *testing.B) {
var (
numTxs = 1000
numBlocks = 1
)
recipientFn := func(nonce uint64) common.Address {
return common.BigToAddress(big.NewInt(0).SetUint64(1337 + nonce))
}
dataFn := func(nonce uint64) []byte {
return nil
}
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn)
}
func BenchmarkBlockChain_1x1000ValueTransferToExisting(b *testing.B) {
var (
numTxs = 1000
numBlocks = 1
)
b.StopTimer()
b.ResetTimer()
recipientFn := func(nonce uint64) common.Address {
return common.BigToAddress(big.NewInt(0).SetUint64(1337))
}
dataFn := func(nonce uint64) []byte {
return nil
}
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn)
}
func BenchmarkBlockChain_1x1000Executions(b *testing.B) {
var (
numTxs = 1000
numBlocks = 1
)
b.StopTimer()
b.ResetTimer()
recipientFn := func(nonce uint64) common.Address {
return common.BigToAddress(big.NewInt(0).SetUint64(0xc0de))
}
dataFn := func(nonce uint64) []byte {
return nil
}
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn)
}

View File

@@ -82,11 +82,23 @@ func (b *BlockGen) SetExtra(data []byte) {
// added. Notably, contract code relying on the BLOCKHASH instruction
// will panic during execution.
func (b *BlockGen) AddTx(tx *types.Transaction) {
b.AddTxWithChain(nil, tx)
}
// AddTxWithChain adds a transaction to the generated block. If no coinbase has
// been set, the block's coinbase is set to the zero address.
//
// AddTxWithChain panics if the transaction cannot be executed. In addition to
// the protocol-imposed limitations (gas limit, etc.), there are some
// further limitations on the content of transactions that can be
// added. If contract code relies on the BLOCKHASH instruction,
// the block in chain will be returned.
func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
if b.gasPool == nil {
b.SetCoinbase(common.Address{})
}
b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs))
receipt, _, err := ApplyTransaction(b.config, nil, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
receipt, _, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
if err != nil {
panic(err)
}

View File

@@ -47,6 +47,7 @@ var (
headHeaderKey = []byte("LastHeader")
headBlockKey = []byte("LastBlock")
headFastKey = []byte("LastFast")
trieSyncKey = []byte("TrieSync")
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`).
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
@@ -70,8 +71,8 @@ var (
ErrChainConfigNotFound = errors.New("ChainConfig not found") // general config not found error
preimageCounter = metrics.NewCounter("db/preimage/total")
preimageHitCounter = metrics.NewCounter("db/preimage/hits")
preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
)
// TxLookupEntry is a positional metadata to help looking up the data content of
@@ -146,6 +147,16 @@ func GetHeadFastBlockHash(db DatabaseReader) common.Hash {
return common.BytesToHash(data)
}
// GetTrieSyncProgress retrieves the number of tries nodes fast synced to allow
// reportinc correct numbers across restarts.
func GetTrieSyncProgress(db DatabaseReader) uint64 {
data, _ := db.Get(trieSyncKey)
if len(data) == 0 {
return 0
}
return new(big.Int).SetBytes(data).Uint64()
}
// GetHeaderRLP retrieves a block header in its raw RLP database encoding, or nil
// if the header's not found.
func GetHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
@@ -374,6 +385,15 @@ func WriteHeadFastBlockHash(db ethdb.Putter, hash common.Hash) error {
return nil
}
// WriteTrieSyncProgress stores the fast sync trie process counter to support
// retrieving it across restarts.
func WriteTrieSyncProgress(db ethdb.Putter, count uint64) error {
if err := db.Put(trieSyncKey, new(big.Int).SetUint64(count).Bytes()); err != nil {
log.Crit("Failed to store fast sync trie progress", "err", err)
}
return nil
}
// WriteHeader serializes a block header into the database.
func WriteHeader(db ethdb.Putter, header *types.Header) error {
data, err := rlp.EncodeToBytes(header)

View File

@@ -317,7 +317,7 @@ func TestLookupStorage(t *testing.T) {
if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) {
t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i)
}
if tx.String() != txn.String() {
if tx.Hash() != txn.Hash() {
t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx)
}
}

View File

@@ -60,13 +60,26 @@ func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author
// GetHashFn returns a GetHashFunc which retrieves header hashes by number
func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash {
var cache map[uint64]common.Hash
return func(n uint64) common.Hash {
for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) {
if header.Number.Uint64() == n {
return header.Hash()
// If there's no hash cache yet, make one
if cache == nil {
cache = map[uint64]common.Hash{
ref.Number.Uint64() - 1: ref.ParentHash,
}
}
// Try to fulfill the request from the cache
if hash, ok := cache[n]; ok {
return hash
}
// Not cached, iterate the blocks and cache the hashes
for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) {
cache[header.Number.Uint64()-1] = header.ParentHash
if n == header.Number.Uint64()-1 {
return header.ParentHash
}
}
return common.Hash{}
}
}

View File

@@ -118,10 +118,12 @@ func TestSetupGenesis(t *testing.T) {
// Commit the 'old' genesis block with Homestead transition at #2.
// Advance to block #4, past the homestead transition block of customg.
genesis := oldcustomg.MustCommit(db)
bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{})
defer bc.Stop()
bc.SetValidator(bproc{})
bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0))
blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil)
bc.InsertChain(blocks)
bc.CurrentBlock()
// This should return a compatibility error.
return SetupGenesisBlock(db, &customg)

View File

@@ -23,6 +23,7 @@ import (
"math"
"math/big"
mrand "math/rand"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -51,8 +52,8 @@ type HeaderChain struct {
chainDb ethdb.Database
genesisHeader *types.Header
currentHeader *types.Header // Current head of the header chain (may be above the block chain!)
currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time)
currentHeader atomic.Value // Current head of the header chain (may be above the block chain!)
currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time)
headerCache *lru.Cache // Cache for the most recent block headers
tdCache *lru.Cache // Cache for the most recent block total difficulties
@@ -95,13 +96,13 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
return nil, ErrNoGenesis
}
hc.currentHeader = hc.genesisHeader
hc.currentHeader.Store(hc.genesisHeader)
if head := GetHeadBlockHash(chainDb); head != (common.Hash{}) {
if chead := hc.GetHeaderByHash(head); chead != nil {
hc.currentHeader = chead
hc.currentHeader.Store(chead)
}
}
hc.currentHeaderHash = hc.currentHeader.Hash()
hc.currentHeaderHash = hc.CurrentHeader().Hash()
return hc, nil
}
@@ -139,7 +140,7 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
if ptd == nil {
return NonStatTy, consensus.ErrUnknownAncestor
}
localTd := hc.GetTd(hc.currentHeaderHash, hc.currentHeader.Number.Uint64())
localTd := hc.GetTd(hc.currentHeaderHash, hc.CurrentHeader().Number.Uint64())
externTd := new(big.Int).Add(header.Difficulty, ptd)
// Irrelevant of the canonical status, write the td and header to the database
@@ -181,7 +182,8 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil {
log.Crit("Failed to insert head header hash", "err", err)
}
hc.currentHeaderHash, hc.currentHeader = hash, types.CopyHeader(header)
hc.currentHeaderHash = hash
hc.currentHeader.Store(types.CopyHeader(header))
status = CanonStatTy
} else {
@@ -383,7 +385,7 @@ func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header {
// CurrentHeader retrieves the current head header of the canonical chain. The
// header is retrieved from the HeaderChain's internal cache.
func (hc *HeaderChain) CurrentHeader() *types.Header {
return hc.currentHeader
return hc.currentHeader.Load().(*types.Header)
}
// SetCurrentHeader sets the current head header of the canonical chain.
@@ -391,7 +393,7 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil {
log.Crit("Failed to insert head header hash", "err", err)
}
hc.currentHeader = head
hc.currentHeader.Store(head)
hc.currentHeaderHash = head.Hash()
}
@@ -403,19 +405,20 @@ type DeleteCallback func(common.Hash, uint64)
// will be deleted and the new one set.
func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
height := uint64(0)
if hc.currentHeader != nil {
height = hc.currentHeader.Number.Uint64()
if hdr := hc.CurrentHeader(); hdr != nil {
height = hdr.Number.Uint64()
}
for hc.currentHeader != nil && hc.currentHeader.Number.Uint64() > head {
hash := hc.currentHeader.Hash()
num := hc.currentHeader.Number.Uint64()
for hdr := hc.CurrentHeader(); hdr != nil && hdr.Number.Uint64() > head; hdr = hc.CurrentHeader() {
hash := hdr.Hash()
num := hdr.Number.Uint64()
if delFn != nil {
delFn(hash, num)
}
DeleteHeader(hc.chainDb, hash, num)
DeleteTd(hc.chainDb, hash, num)
hc.currentHeader = hc.GetHeader(hc.currentHeader.ParentHash, hc.currentHeader.Number.Uint64()-1)
hc.currentHeader.Store(hc.GetHeader(hdr.ParentHash, hdr.Number.Uint64()-1))
}
// Roll back the canonical chain numbering
for i := height; i > head; i-- {
@@ -426,10 +429,10 @@ func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
hc.tdCache.Purge()
hc.numberCache.Purge()
if hc.currentHeader == nil {
hc.currentHeader = hc.genesisHeader
if hc.CurrentHeader() == nil {
hc.currentHeader.Store(hc.genesisHeader)
}
hc.currentHeaderHash = hc.currentHeader.Hash()
hc.currentHeaderHash = hc.CurrentHeader().Hash()
if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil {
log.Crit("Failed to reset head header hash", "err", err)

View File

@@ -26,7 +26,7 @@ import (
lru "github.com/hashicorp/golang-lru"
)
// Trie cache generation limit after which to evic trie nodes from memory.
// Trie cache generation limit after which to evict trie nodes from memory.
var MaxTrieCacheGen = uint16(120)
const (
@@ -151,9 +151,6 @@ func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, erro
return cached.(int), nil
}
code, err := db.ContractCode(addrHash, codeHash)
if err == nil {
db.codeSizeCache.Add(codeHash, len(code))
}
return len(code), err
}

View File

@@ -53,7 +53,7 @@ func (self *StateDB) RawDump() Dump {
panic(err)
}
obj := newObject(nil, common.BytesToAddress(addr), data, nil)
obj := newObject(nil, common.BytesToAddress(addr), data)
account := DumpAccount{
Balance: data.Balance.String(),
Nonce: data.Nonce,

View File

@@ -22,11 +22,67 @@ import (
"github.com/ethereum/go-ethereum/common"
)
// journalEntry is a modification entry in the state change journal that can be
// reverted on demand.
type journalEntry interface {
undo(*StateDB)
// revert undoes the changes introduced by this journal entry.
revert(*StateDB)
// dirtied returns the Ethereum address modified by this journal entry.
dirtied() *common.Address
}
type journal []journalEntry
// journal contains the list of state modifications applied since the last state
// commit. These are tracked to be able to be reverted in case of an execution
// exception or revertal request.
type journal struct {
entries []journalEntry // Current changes tracked by the journal
dirties map[common.Address]int // Dirty accounts and the number of changes
}
// newJournal create a new initialized journal.
func newJournal() *journal {
return &journal{
dirties: make(map[common.Address]int),
}
}
// append inserts a new modification entry to the end of the change journal.
func (j *journal) append(entry journalEntry) {
j.entries = append(j.entries, entry)
if addr := entry.dirtied(); addr != nil {
j.dirties[*addr]++
}
}
// revert undoes a batch of journalled modifications along with any reverted
// dirty handling too.
func (j *journal) revert(statedb *StateDB, snapshot int) {
for i := len(j.entries) - 1; i >= snapshot; i-- {
// Undo the changes made by the operation
j.entries[i].revert(statedb)
// Drop any dirty tracking induced by the change
if addr := j.entries[i].dirtied(); addr != nil {
if j.dirties[*addr]--; j.dirties[*addr] == 0 {
delete(j.dirties, *addr)
}
}
}
j.entries = j.entries[:snapshot]
}
// dirty explicitly sets an address to dirty, even if the change entries would
// otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD
// precompile consensus exception.
func (j *journal) dirty(addr common.Address) {
j.dirties[addr]++
}
// length returns the current number of entries in the journal.
func (j *journal) length() int {
return len(j.entries)
}
type (
// Changes to the account trie.
@@ -77,16 +133,24 @@ type (
}
)
func (ch createObjectChange) undo(s *StateDB) {
func (ch createObjectChange) revert(s *StateDB) {
delete(s.stateObjects, *ch.account)
delete(s.stateObjectsDirty, *ch.account)
}
func (ch resetObjectChange) undo(s *StateDB) {
func (ch createObjectChange) dirtied() *common.Address {
return ch.account
}
func (ch resetObjectChange) revert(s *StateDB) {
s.setStateObject(ch.prev)
}
func (ch suicideChange) undo(s *StateDB) {
func (ch resetObjectChange) dirtied() *common.Address {
return nil
}
func (ch suicideChange) revert(s *StateDB) {
obj := s.getStateObject(*ch.account)
if obj != nil {
obj.suicided = ch.prev
@@ -94,38 +158,60 @@ func (ch suicideChange) undo(s *StateDB) {
}
}
var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")
func (ch touchChange) undo(s *StateDB) {
if !ch.prev && *ch.account != ripemd {
s.getStateObject(*ch.account).touched = ch.prev
if !ch.prevDirty {
delete(s.stateObjectsDirty, *ch.account)
}
}
func (ch suicideChange) dirtied() *common.Address {
return ch.account
}
func (ch balanceChange) undo(s *StateDB) {
var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")
func (ch touchChange) revert(s *StateDB) {
}
func (ch touchChange) dirtied() *common.Address {
return ch.account
}
func (ch balanceChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setBalance(ch.prev)
}
func (ch nonceChange) undo(s *StateDB) {
func (ch balanceChange) dirtied() *common.Address {
return ch.account
}
func (ch nonceChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setNonce(ch.prev)
}
func (ch codeChange) undo(s *StateDB) {
func (ch nonceChange) dirtied() *common.Address {
return ch.account
}
func (ch codeChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
}
func (ch storageChange) undo(s *StateDB) {
func (ch codeChange) dirtied() *common.Address {
return ch.account
}
func (ch storageChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
}
func (ch refundChange) undo(s *StateDB) {
func (ch storageChange) dirtied() *common.Address {
return ch.account
}
func (ch refundChange) revert(s *StateDB) {
s.refund = ch.prev
}
func (ch addLogChange) undo(s *StateDB) {
func (ch refundChange) dirtied() *common.Address {
return nil
}
func (ch addLogChange) revert(s *StateDB) {
logs := s.logs[ch.txhash]
if len(logs) == 1 {
delete(s.logs, ch.txhash)
@@ -135,6 +221,14 @@ func (ch addLogChange) undo(s *StateDB) {
s.logSize--
}
func (ch addPreimageChange) undo(s *StateDB) {
func (ch addLogChange) dirtied() *common.Address {
return nil
}
func (ch addPreimageChange) revert(s *StateDB) {
delete(s.preimages, ch.hash)
}
func (ch addPreimageChange) dirtied() *common.Address {
return nil
}

View File

@@ -85,9 +85,7 @@ type stateObject struct {
// during the "update" phase of the state transition.
dirtyCode bool // true if the code was updated
suicided bool
touched bool
deleted bool
onDirty func(addr common.Address) // Callback method to mark a state object newly dirty
}
// empty returns whether the account is considered empty.
@@ -105,7 +103,7 @@ type Account struct {
}
// newObject creates a state object.
func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *stateObject {
func newObject(db *StateDB, address common.Address, data Account) *stateObject {
if data.Balance == nil {
data.Balance = new(big.Int)
}
@@ -119,7 +117,6 @@ func newObject(db *StateDB, address common.Address, data Account, onDirty func(a
data: data,
cachedStorage: make(Storage),
dirtyStorage: make(Storage),
onDirty: onDirty,
}
}
@@ -137,23 +134,17 @@ func (self *stateObject) setError(err error) {
func (self *stateObject) markSuicided() {
self.suicided = true
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}
func (c *stateObject) touch() {
c.db.journal = append(c.db.journal, touchChange{
account: &c.address,
prev: c.touched,
prevDirty: c.onDirty == nil,
c.db.journal.append(touchChange{
account: &c.address,
})
if c.onDirty != nil {
c.onDirty(c.Address())
c.onDirty = nil
if c.address == ripemd {
// Explicitly put it in the dirty-cache, which is otherwise generated from
// flattened journals.
c.db.journal.dirty(c.address)
}
c.touched = true
}
func (c *stateObject) getTrie(db Database) Trie {
@@ -195,7 +186,7 @@ func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
// SetState updates a value in account storage.
func (self *stateObject) SetState(db Database, key, value common.Hash) {
self.db.journal = append(self.db.journal, storageChange{
self.db.journal.append(storageChange{
account: &self.address,
key: key,
prevalue: self.GetState(db, key),
@@ -207,10 +198,6 @@ func (self *stateObject) setState(key, value common.Hash) {
self.cachedStorage[key] = value
self.dirtyStorage[key] = value
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}
// updateTrie writes cached storage modifications into the object's storage trie.
@@ -274,7 +261,7 @@ func (c *stateObject) SubBalance(amount *big.Int) {
}
func (self *stateObject) SetBalance(amount *big.Int) {
self.db.journal = append(self.db.journal, balanceChange{
self.db.journal.append(balanceChange{
account: &self.address,
prev: new(big.Int).Set(self.data.Balance),
})
@@ -283,17 +270,13 @@ func (self *stateObject) SetBalance(amount *big.Int) {
func (self *stateObject) setBalance(amount *big.Int) {
self.data.Balance = amount
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}
// Return the gas back to the origin. Used by the Virtual machine or Closures
func (c *stateObject) ReturnGas(gas *big.Int) {}
func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject {
stateObject := newObject(db, self.address, self.data, onDirty)
func (self *stateObject) deepCopy(db *StateDB) *stateObject {
stateObject := newObject(db, self.address, self.data)
if self.trie != nil {
stateObject.trie = db.db.CopyTrie(self.trie)
}
@@ -333,7 +316,7 @@ func (self *stateObject) Code(db Database) []byte {
func (self *stateObject) SetCode(codeHash common.Hash, code []byte) {
prevcode := self.Code(self.db.db)
self.db.journal = append(self.db.journal, codeChange{
self.db.journal.append(codeChange{
account: &self.address,
prevhash: self.CodeHash(),
prevcode: prevcode,
@@ -345,14 +328,10 @@ func (self *stateObject) setCode(codeHash common.Hash, code []byte) {
self.code = code
self.data.CodeHash = codeHash[:]
self.dirtyCode = true
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}
func (self *stateObject) SetNonce(nonce uint64) {
self.db.journal = append(self.db.journal, nonceChange{
self.db.journal.append(nonceChange{
account: &self.address,
prev: self.data.Nonce,
})
@@ -361,10 +340,6 @@ func (self *stateObject) SetNonce(nonce uint64) {
func (self *stateObject) setNonce(nonce uint64) {
self.data.Nonce = nonce
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}
func (self *stateObject) CodeHash() []byte {

View File

@@ -76,14 +76,14 @@ type StateDB struct {
// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
journal journal
journal *journal
validRevisions []revision
nextRevisionId int
lock sync.Mutex
}
// Create a new state from a given trie
// Create a new state from a given trie.
func New(root common.Hash, db Database) (*StateDB, error) {
tr, err := db.OpenTrie(root)
if err != nil {
@@ -96,6 +96,7 @@ func New(root common.Hash, db Database) (*StateDB, error) {
stateObjectsDirty: make(map[common.Address]struct{}),
logs: make(map[common.Hash][]*types.Log),
preimages: make(map[common.Hash][]byte),
journal: newJournal(),
}, nil
}
@@ -110,7 +111,7 @@ func (self *StateDB) Error() error {
return self.dbErr
}
// Reset clears out all emphemeral state objects from the state db, but keeps
// Reset clears out all ephemeral state objects from the state db, but keeps
// the underlying state trie to avoid reloading data for the next operations.
func (self *StateDB) Reset(root common.Hash) error {
tr, err := self.db.OpenTrie(root)
@@ -131,7 +132,7 @@ func (self *StateDB) Reset(root common.Hash) error {
}
func (self *StateDB) AddLog(log *types.Log) {
self.journal = append(self.journal, addLogChange{txhash: self.thash})
self.journal.append(addLogChange{txhash: self.thash})
log.TxHash = self.thash
log.BlockHash = self.bhash
@@ -156,7 +157,7 @@ func (self *StateDB) Logs() []*types.Log {
// AddPreimage records a SHA3 preimage seen by the VM.
func (self *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
if _, ok := self.preimages[hash]; !ok {
self.journal = append(self.journal, addPreimageChange{hash: hash})
self.journal.append(addPreimageChange{hash: hash})
pi := make([]byte, len(preimage))
copy(pi, preimage)
self.preimages[hash] = pi
@@ -169,7 +170,7 @@ func (self *StateDB) Preimages() map[common.Hash][]byte {
}
func (self *StateDB) AddRefund(gas uint64) {
self.journal = append(self.journal, refundChange{prev: self.refund})
self.journal.append(refundChange{prev: self.refund})
self.refund += gas
}
@@ -235,10 +236,10 @@ func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
return common.BytesToHash(stateObject.CodeHash())
}
func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
stateObject := self.getStateObject(a)
func (self *StateDB) GetState(addr common.Address, bhash common.Hash) common.Hash {
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.GetState(self.db, b)
return stateObject.GetState(self.db, bhash)
}
return common.Hash{}
}
@@ -250,12 +251,12 @@ func (self *StateDB) Database() Database {
// StorageTrie returns the storage trie of an account.
// The return value is a copy and is nil for non-existent accounts.
func (self *StateDB) StorageTrie(a common.Address) Trie {
stateObject := self.getStateObject(a)
func (self *StateDB) StorageTrie(addr common.Address) Trie {
stateObject := self.getStateObject(addr)
if stateObject == nil {
return nil
}
cpy := stateObject.deepCopy(self, nil)
cpy := stateObject.deepCopy(self)
return cpy.updateTrie(self.db)
}
@@ -271,7 +272,7 @@ func (self *StateDB) HasSuicided(addr common.Address) bool {
* SETTERS
*/
// AddBalance adds amount to the account associated with addr
// AddBalance adds amount to the account associated with addr.
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
@@ -279,7 +280,7 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
}
}
// SubBalance subtracts amount from the account associated with addr
// SubBalance subtracts amount from the account associated with addr.
func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
@@ -308,7 +309,7 @@ func (self *StateDB) SetCode(addr common.Address, code []byte) {
}
}
func (self *StateDB) SetState(addr common.Address, key common.Hash, value common.Hash) {
func (self *StateDB) SetState(addr common.Address, key, value common.Hash) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetState(self.db, key, value)
@@ -325,7 +326,7 @@ func (self *StateDB) Suicide(addr common.Address) bool {
if stateObject == nil {
return false
}
self.journal = append(self.journal, suicideChange{
self.journal.append(suicideChange{
account: &addr,
prev: stateObject.suicided,
prevbalance: new(big.Int).Set(stateObject.Balance()),
@@ -337,7 +338,7 @@ func (self *StateDB) Suicide(addr common.Address) bool {
}
//
// Setting, updating & deleting state object methods
// Setting, updating & deleting state object methods.
//
// updateStateObject writes the given object to the trie.
@@ -379,7 +380,7 @@ func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObje
return nil
}
// Insert into the live set.
obj := newObject(self, addr, data, self.MarkStateObjectDirty)
obj := newObject(self, addr, data)
self.setStateObject(obj)
return obj
}
@@ -388,7 +389,7 @@ func (self *StateDB) setStateObject(object *stateObject) {
self.stateObjects[object.Address()] = object
}
// Retrieve a state object or create a new state object if nil
// Retrieve a state object or create a new state object if nil.
func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
stateObject := self.getStateObject(addr)
if stateObject == nil || stateObject.deleted {
@@ -397,22 +398,16 @@ func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
return stateObject
}
// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
func (self *StateDB) MarkStateObjectDirty(addr common.Address) {
self.stateObjectsDirty[addr] = struct{}{}
}
// createObject creates a new state object. If there is an existing account with
// the given address, it is overwritten and returned as the second return value.
func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
prev = self.getStateObject(addr)
newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty)
newobj = newObject(self, addr, Account{})
newobj.setNonce(0) // sets the object to dirty
if prev == nil {
self.journal = append(self.journal, createObjectChange{account: &addr})
self.journal.append(createObjectChange{account: &addr})
} else {
self.journal = append(self.journal, resetObjectChange{prev: prev})
self.journal.append(resetObjectChange{prev: prev})
}
self.setStateObject(newobj)
return newobj, prev
@@ -466,18 +461,35 @@ func (self *StateDB) Copy() *StateDB {
state := &StateDB{
db: self.db,
trie: self.db.CopyTrie(self.trie),
stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)),
stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)),
stateObjects: make(map[common.Address]*stateObject, len(self.journal.dirties)),
stateObjectsDirty: make(map[common.Address]struct{}, len(self.journal.dirties)),
refund: self.refund,
logs: make(map[common.Hash][]*types.Log, len(self.logs)),
logSize: self.logSize,
preimages: make(map[common.Hash][]byte),
journal: newJournal(),
}
// Copy the dirty states, logs, and preimages
for addr := range self.stateObjectsDirty {
state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty)
state.stateObjectsDirty[addr] = struct{}{}
for addr := range self.journal.dirties {
// As documented [here](https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527),
// and in the Finalise-method, there is a case where an object is in the journal but not
// in the stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we need to check for
// nil
if object, exist := self.stateObjects[addr]; exist {
state.stateObjects[addr] = object.deepCopy(state)
state.stateObjectsDirty[addr] = struct{}{}
}
}
// Above, we don't copy the actual journal. This means that if the copy is copied, the
// loop above will be a no-op, since the copy's journal is empty.
// Thus, here we iterate over stateObjects, to enable copies of copies
for addr := range self.stateObjectsDirty {
if _, exist := state.stateObjects[addr]; !exist {
state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state)
state.stateObjectsDirty[addr] = struct{}{}
}
}
for hash, logs := range self.logs {
state.logs[hash] = make([]*types.Log, len(logs))
copy(state.logs[hash], logs)
@@ -492,7 +504,7 @@ func (self *StateDB) Copy() *StateDB {
func (self *StateDB) Snapshot() int {
id := self.nextRevisionId
self.nextRevisionId++
self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)})
self.validRevisions = append(self.validRevisions, revision{id, self.journal.length()})
return id
}
@@ -507,13 +519,8 @@ func (self *StateDB) RevertToSnapshot(revid int) {
}
snapshot := self.validRevisions[idx].journalIndex
// Replay the journal to undo changes.
for i := len(self.journal) - 1; i >= snapshot; i-- {
self.journal[i].undo(self)
}
self.journal = self.journal[:snapshot]
// Remove invalidated snapshots from the stack.
// Replay the journal to undo changes and remove invalidated snapshots
self.journal.revert(self, snapshot)
self.validRevisions = self.validRevisions[:idx]
}
@@ -525,14 +532,25 @@ func (self *StateDB) GetRefund() uint64 {
// Finalise finalises the state by removing the self destructed objects
// and clears the journal as well as the refunds.
func (s *StateDB) Finalise(deleteEmptyObjects bool) {
for addr := range s.stateObjectsDirty {
stateObject := s.stateObjects[addr]
for addr := range s.journal.dirties {
stateObject, exist := s.stateObjects[addr]
if !exist {
// ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2
// That tx goes out of gas, and although the notion of 'touched' does not exist there, the
// touch-event will still be recorded in the journal. Since ripeMD is a special snowflake,
// it will persist in the journal even though the journal is reverted. In this special circumstance,
// it may exist in `s.journal.dirties` but not in `s.stateObjects`.
// Thus, we can safely ignore it here
continue
}
if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) {
s.deleteStateObject(stateObject)
} else {
stateObject.updateRoot(s.db)
s.updateStateObject(stateObject)
}
s.stateObjectsDirty[addr] = struct{}{}
}
// Invalidate journal because reverting across transactions is not allowed.
s.clearJournalAndRefund()
@@ -576,7 +594,7 @@ func (s *StateDB) DeleteSuicides() {
}
func (s *StateDB) clearJournalAndRefund() {
s.journal = nil
s.journal = newJournal()
s.validRevisions = s.validRevisions[:0]
s.refund = 0
}
@@ -585,6 +603,9 @@ func (s *StateDB) clearJournalAndRefund() {
func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
defer s.clearJournalAndRefund()
for addr := range s.journal.dirties {
s.stateObjectsDirty[addr] = struct{}{}
}
// Commit objects to the trie.
for addr, stateObject := range s.stateObjects {
_, isDirty := s.stateObjectsDirty[addr]

View File

@@ -413,11 +413,28 @@ func (s *StateSuite) TestTouchDelete(c *check.C) {
snapshot := s.state.Snapshot()
s.state.AddBalance(common.Address{}, new(big.Int))
if len(s.state.stateObjectsDirty) != 1 {
if len(s.state.journal.dirties) != 1 {
c.Fatal("expected one dirty state object")
}
s.state.RevertToSnapshot(snapshot)
if len(s.state.stateObjectsDirty) != 0 {
if len(s.state.journal.dirties) != 0 {
c.Fatal("expected no dirty state object")
}
}
// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy.
// See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512
func TestCopyOfCopy(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
sdb, _ := New(common.Hash{}, NewDatabase(db))
addr := common.HexToAddress("aaaa")
sdb.SetBalance(addr, big.NewInt(42))
if got := sdb.Copy().GetBalance(addr).Uint64(); got != 42 {
t.Fatalf("1st copy fail, expected 42, got %v", got)
}
if got := sdb.Copy().Copy().GetBalance(addr).Uint64(); got != 42 {
t.Fatalf("2nd copy fail, expected 42, got %v", got)
}
}

View File

@@ -132,28 +132,12 @@ func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool,
return NewStateTransition(evm, msg, gp).TransitionDb()
}
func (st *StateTransition) from() vm.AccountRef {
f := st.msg.From()
if !st.state.Exist(f) {
st.state.CreateAccount(f)
// to returns the recipient of the message.
func (st *StateTransition) to() common.Address {
if st.msg == nil || st.msg.To() == nil /* contract creation */ {
return common.Address{}
}
return vm.AccountRef(f)
}
func (st *StateTransition) to() vm.AccountRef {
if st.msg == nil {
return vm.AccountRef{}
}
to := st.msg.To()
if to == nil {
return vm.AccountRef{} // contract creation
}
reference := vm.AccountRef(*to)
if !st.state.Exist(*to) {
st.state.CreateAccount(*to)
}
return reference
return *st.msg.To()
}
func (st *StateTransition) useGas(amount uint64) error {
@@ -166,12 +150,8 @@ func (st *StateTransition) useGas(amount uint64) error {
}
func (st *StateTransition) buyGas() error {
var (
state = st.state
sender = st.from()
)
mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
if state.GetBalance(sender.Address()).Cmp(mgval) < 0 {
if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 {
return errInsufficientBalanceForGas
}
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
@@ -180,20 +160,17 @@ func (st *StateTransition) buyGas() error {
st.gas += st.msg.Gas()
st.initialGas = st.msg.Gas()
state.SubBalance(sender.Address(), mgval)
st.state.SubBalance(st.msg.From(), mgval)
return nil
}
func (st *StateTransition) preCheck() error {
msg := st.msg
sender := st.from()
// Make sure this transaction's nonce is correct
if msg.CheckNonce() {
nonce := st.state.GetNonce(sender.Address())
if nonce < msg.Nonce() {
// Make sure this transaction's nonce is correct.
if st.msg.CheckNonce() {
nonce := st.state.GetNonce(st.msg.From())
if nonce < st.msg.Nonce() {
return ErrNonceTooHigh
} else if nonce > msg.Nonce() {
} else if nonce > st.msg.Nonce() {
return ErrNonceTooLow
}
}
@@ -208,13 +185,15 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
return
}
msg := st.msg
sender := st.from() // err checked in preCheck
sender := vm.AccountRef(msg.From())
homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber)
contractCreation := msg.To() == nil
// Pay intrinsic gas
gas, err := IntrinsicGas(st.data, contractCreation, homestead)
if err != nil {
return nil, 0, false, err
}
if err = st.useGas(gas); err != nil {
return nil, 0, false, err
}
@@ -230,8 +209,8 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
} else {
// Increment the nonce for the next transaction
st.state.SetNonce(sender.Address(), st.state.GetNonce(sender.Address())+1)
ret, st.gas, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value)
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value)
}
if vmerr != nil {
log.Debug("VM returned with error", "err", vmerr)
@@ -257,10 +236,8 @@ func (st *StateTransition) refundGas() {
st.gas += refund
// Return ETH for remaining gas, exchanged at the original rate.
sender := st.from()
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
st.state.AddBalance(sender.Address(), remaining)
st.state.AddBalance(st.msg.From(), remaining)
// Also return remaining gas to the block gas counter so it is
// available for the next transaction.

View File

@@ -367,9 +367,20 @@ func (l *txList) Flatten() types.Transactions {
// price-sorted transactions to discard when the pool fills up.
type priceHeap []*types.Transaction
func (h priceHeap) Len() int { return len(h) }
func (h priceHeap) Less(i, j int) bool { return h[i].GasPrice().Cmp(h[j].GasPrice()) < 0 }
func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h priceHeap) Len() int { return len(h) }
func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h priceHeap) Less(i, j int) bool {
// Sort primarily by price, returning the cheaper one
switch h[i].GasPrice().Cmp(h[j].GasPrice()) {
case -1:
return true
case 1:
return false
}
// If the prices match, stabilize via nonces (high nonce is worse)
return h[i].Nonce() > h[j].Nonce()
}
func (h *priceHeap) Push(x interface{}) {
*h = append(*h, x.(*types.Transaction))

View File

@@ -87,20 +87,20 @@ var (
var (
// Metrics for the pending pool
pendingDiscardCounter = metrics.NewCounter("txpool/pending/discard")
pendingReplaceCounter = metrics.NewCounter("txpool/pending/replace")
pendingRateLimitCounter = metrics.NewCounter("txpool/pending/ratelimit") // Dropped due to rate limiting
pendingNofundsCounter = metrics.NewCounter("txpool/pending/nofunds") // Dropped due to out-of-funds
pendingDiscardCounter = metrics.NewRegisteredCounter("txpool/pending/discard", nil)
pendingReplaceCounter = metrics.NewRegisteredCounter("txpool/pending/replace", nil)
pendingRateLimitCounter = metrics.NewRegisteredCounter("txpool/pending/ratelimit", nil) // Dropped due to rate limiting
pendingNofundsCounter = metrics.NewRegisteredCounter("txpool/pending/nofunds", nil) // Dropped due to out-of-funds
// Metrics for the queued pool
queuedDiscardCounter = metrics.NewCounter("txpool/queued/discard")
queuedReplaceCounter = metrics.NewCounter("txpool/queued/replace")
queuedRateLimitCounter = metrics.NewCounter("txpool/queued/ratelimit") // Dropped due to rate limiting
queuedNofundsCounter = metrics.NewCounter("txpool/queued/nofunds") // Dropped due to out-of-funds
queuedDiscardCounter = metrics.NewRegisteredCounter("txpool/queued/discard", nil)
queuedReplaceCounter = metrics.NewRegisteredCounter("txpool/queued/replace", nil)
queuedRateLimitCounter = metrics.NewRegisteredCounter("txpool/queued/ratelimit", nil) // Dropped due to rate limiting
queuedNofundsCounter = metrics.NewRegisteredCounter("txpool/queued/nofunds", nil) // Dropped due to out-of-funds
// General tx metrics
invalidTxCounter = metrics.NewCounter("txpool/invalid")
underpricedTxCounter = metrics.NewCounter("txpool/underpriced")
invalidTxCounter = metrics.NewRegisteredCounter("txpool/invalid", nil)
underpricedTxCounter = metrics.NewRegisteredCounter("txpool/underpriced", nil)
)
// TxStatus is the current status of a transaction as seen by the pool.
@@ -320,7 +320,7 @@ func (pool *TxPool) loop() {
// 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.removeTx(tx.Hash(), true)
}
}
}
@@ -468,7 +468,7 @@ func (pool *TxPool) SetGasPrice(price *big.Int) {
pool.gasPrice = price
for _, tx := range pool.priced.Cap(price, pool.locals) {
pool.removeTx(tx.Hash())
pool.removeTx(tx.Hash(), false)
}
log.Info("Transaction pool price threshold updated", "price", price)
}
@@ -630,7 +630,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
for _, tx := range drop {
log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
underpricedTxCounter.Inc(1)
pool.removeTx(tx.Hash())
pool.removeTx(tx.Hash(), false)
}
}
// If the transaction is replacing an already pending one, do directly
@@ -695,8 +695,10 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, er
pool.priced.Removed()
queuedReplaceCounter.Inc(1)
}
pool.all[hash] = tx
pool.priced.Put(tx)
if pool.all[hash] == nil {
pool.all[hash] = tx
pool.priced.Put(tx)
}
return old != nil, nil
}
@@ -862,7 +864,7 @@ func (pool *TxPool) Get(hash common.Hash) *types.Transaction {
// removeTx removes a single transaction from the queue, moving all subsequent
// transactions back to the future queue.
func (pool *TxPool) removeTx(hash common.Hash) {
func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
// Fetch the transaction we wish to delete
tx, ok := pool.all[hash]
if !ok {
@@ -872,20 +874,20 @@ func (pool *TxPool) removeTx(hash common.Hash) {
// Remove it from the list of known transactions
delete(pool.all, hash)
pool.priced.Removed()
if outofbound {
pool.priced.Removed()
}
// Remove the transaction from the pending lists and reset the account nonce
if pending := pool.pending[addr]; pending != nil {
if removed, invalids := pending.Remove(tx); removed {
// If no more transactions are left, remove the list
// If no more pending transactions are left, remove the list
if pending.Empty() {
delete(pool.pending, addr)
delete(pool.beats, addr)
} else {
// Otherwise postpone any invalidated transactions
for _, tx := range invalids {
pool.enqueueTx(tx.Hash(), tx)
}
}
// Postpone any invalidated transactions
for _, tx := range invalids {
pool.enqueueTx(tx.Hash(), tx)
}
// Update the account nonce if needed
if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
@@ -1053,7 +1055,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
// Drop all transactions if they are less than the overflow
if size := uint64(list.Len()); size <= drop {
for _, tx := range list.Flatten() {
pool.removeTx(tx.Hash())
pool.removeTx(tx.Hash(), true)
}
drop -= size
queuedRateLimitCounter.Inc(int64(size))
@@ -1062,7 +1064,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
// Otherwise drop only last few transactions
txs := list.Flatten()
for i := len(txs) - 1; i >= 0 && drop > 0; i-- {
pool.removeTx(txs[i].Hash())
pool.removeTx(txs[i].Hash(), true)
drop--
queuedRateLimitCounter.Inc(1)
}

Some files were not shown because too many files have changed in this diff Show More