Compare commits

...

272 Commits

Author SHA1 Message Date
ab5646c532 params: v1.6.7 stable 2017-07-11 16:32:36 +02:00
3b25012481 Merge pull request #14792 from karalabe/slim-docker-image
dockerignore: ignore all git metadata and all tests
2017-07-11 17:07:01 +03:00
bd381be9c8 dockerignore: ignore all git metadata and all tests 2017-07-11 17:02:22 +03:00
225de7ca0a tests: update tests and implement general state tests (#14734)
Tests are now included as a submodule. This should make updating easier
and removes ~60MB of JSON data from the working copy.

State tests are replaced by General State Tests, which run the same test
with multiple fork configurations.

With the new test runner, consensus tests are run as subtests by walking
json files. Many hex issues have been fixed upstream since the last
update and most custom parsing code is replaced by existing JSON hex
types. Tests can now be marked as 'expected failures', ensuring that
fixes for those tests will trigger an update to test configuration. The
new test runner also supports parallel execution and the -short flag.
2017-07-11 13:49:14 +02:00
bd01cd7183 vendor: update go-stack to fix a sigpanic panic (#14790) 2017-07-11 13:43:33 +02:00
0958770625 internal/web3ext: fix debug.traceBlockFromFile wrapper (#14774)
As stated in the documentation, this method should be called traceBlockFromFile
and not traceBlockByFile. Previously this would result in a 'The method ... does
not exist/is not available' error.
2017-07-10 12:47:50 +02:00
4f7a38001f Merge pull request #14737 from holiman/txpool_localaccounts
Txpool localaccounts
2017-07-10 12:43:23 +03:00
65f0e905dd README: add missing full stop (#14766) 2017-07-07 16:41:26 +02:00
4b8860a7b4 Merge pull request #14723 from Arachnid/downloadrefactor
Refactor downloader to use interfaces instead of callbacks
2017-07-06 12:40:22 +01:00
34ec9913f6 core: test locals support in txpool queue limits, fix
The commit reworks the transaction pool queue limitation tests
to cater for testing local accounts, also testing the nolocal flag.

In addition, it also fixes a panic if local transactions exceeded
the global queue allowance (no accounts left to drop from) and also
fixes queue eviction to operate on all accounts, not just the one
being updated.
2017-07-06 11:51:59 +03:00
f25486c3fb core: fix typo in error message (#14763) 2017-07-06 00:19:38 +02:00
88b4fe7d21 core: handle nolocals during add, exepmt locals from expiration 2017-07-05 17:16:42 +03:00
5e38f7a664 cmd, core: add --txpool.nolocals to disable local price exemptions 2017-07-05 17:06:05 +03:00
4c1d0b164b eth: drop leftover from previous nonce protection scheme 2017-07-05 16:53:40 +03:00
48ee7f9de7 core, eth, les: polish txpool API around local/remote txs 2017-07-05 16:51:55 +03:00
fe13949d9d eth/downloader: Doc fixes 2017-07-05 11:13:16 +01:00
138f26c93a Merge pull request #14749 from karalabe/disable-metro-allprotocols
params: remove redundant consts, disable metro on AllProtocolChanges
2017-07-04 14:11:04 +03:00
8f12d76a47 params: remove redundant consts, disable metro on AllProtocolChanges 2017-07-04 12:28:58 +03:00
be8f8409bc eth/downloader, les, light: Changes in response to review 2017-07-03 15:17:12 +01:00
a633a2d7ea core: Prevent local tx:s from being discarded 2017-06-30 22:55:10 +02:00
67aff49822 core: Change local-handling to use sender-account instead of tx hashes 2017-06-30 22:43:26 +02:00
a0aa071ca6 Merge pull request #14732 from ethersphere/swarm-remove-ethapi
cmd/swarm: Exit if --ethapi is set
2017-06-30 13:06:52 +03:00
c7041fe145 cmd/swarm: Exit if --ethapi is set
The previous attempt to use --ethapi as a fallback if --ens-api is not
set does not work because --ens-api has a default value, and also
setting --ens-api to "" is the suggested way to disable ENS lookups.

Signed-off-by: Lewis Marshall <lewis@lmars.net>
2017-06-30 10:16:03 +01:00
41318f3776 Merge pull request #14646 from ethersphere/swarm-ens-api
cmd/swarm: Support using Mainnet for resolving ENS names
2017-06-29 19:09:01 +03:00
65b96dc4b9 Merge pull request #14727 from holiman/count_txs_right
Fix error when reporting numer of txs in imported blocks
2017-06-29 16:49:43 +03:00
8bbd598ef4 core: fix an off-by-one when the block import counts blocks 2017-06-29 14:19:10 +02:00
ae11545bc5 eth, les: Refactor downloader peer to use structs 2017-06-29 12:49:18 +01:00
0550957989 eth, les, light: Refactor downloader to use blockchain interface 2017-06-28 15:58:41 +01:00
dfd076244d Merge pull request #14718 from holiman/gascalc_fix
core/vm: fix overflow in gas calculation formula
2017-06-28 13:12:13 +03:00
6dc32e897a core/vm: add benchmarks for some ops and precompiles (#14641) 2017-06-28 11:45:45 +02:00
e4301564c2 core/vm : fix testcase for gas calculation 2017-06-28 10:47:07 +02:00
bae7565231 core/vm: fix overflow in gas calculation formula 2017-06-28 09:51:31 +02:00
9e5f03b6c4 core/state: access trie through Database interface, track errors (#14589)
With this commit, core/state's access to the underlying key/value database is
mediated through an interface. Database errors are tracked in StateDB and
returned by CommitTo or the new Error method.

Motivation for this change: We can remove the light client's duplicated copy of
core/state. The light client now supports node iteration, so tracing and storage
enumeration can work with the light client (not implemented in this commit).
2017-06-27 15:57:06 +02:00
bb366271fe Merge pull request #14686 from fjl/hexutil-json-type-error
common/hexutil: wrap errors in json.UnmarshalTypeError
2017-06-27 15:02:43 +03:00
4a741df757 common/hexutil: wrap errors in json.UnmarshalTypeError
This adds type and struct field context to error messages.
Instead of "hex string of odd length" users will now see "json: cannot
unmarshal hex string of odd length into Go struct field SendTxArgs.from
of type common.Address".
2017-06-27 13:15:12 +02:00
5421a08d2f accounts/abi: reorganizing package with small fixes (#14610)
* accounts/abi: reorganizing package and some notes and a quick correction of name.

Signed-off-by: RJ Catalano <rj@monax.io>

get rid of some imports

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: move file names

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: fix boolean decode function

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: fix for the array set and for creating a bool

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: be very very very correct

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: fix up error message and variable names

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: take out unnecessary argument in pack method

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: add bool unpack test and add a panic to readBool function

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: fix panic message

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: change from panic to basic error

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: fix nil to false

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: fill out type regex tests and fill with the correct type for integers

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: move packNumbers into pack.go.

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: separation of the testing suite into appropriately named files.

Signed-off-by: RJ Catalano <rj@monax.io>

* account/abi: change to hex string tests.

Signed-off-by: RJ Catalano <rj@monax.io>

* account/abi: fix up rest of tests to hex

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: declare bool at the package level

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: use errors package in the error file.

Signed-off-by: RJ Catalano <rj@monax.io>

* accounts/abi: fix ugly hack and fix error type declaration.

Signed-off-by: RJ Catalano <rj@monax.io>
2017-06-27 11:05:33 +03:00
cf611c50b9 build: fix devel golang detection on debian/ubuntu (#14711) 2017-06-27 09:11:41 +02:00
c008176f9f Merge pull request #14690 from karalabe/faucet-key-reuse
cmd/puppeth: fix key reuse during faucet deploys
2017-06-26 14:03:46 +03:00
f3359d5e58 cmd/swarm: Support using Mainnet for resolving ENS names
Signed-off-by: Lewis Marshall <lewis@lmars.net>
2017-06-26 12:51:33 +02:00
feb2932706 Merge pull request #14540 from bas-vk/whisper-api
whisperv5: integrate whisper and implement API
2017-06-26 13:44:35 +03:00
ea1d1825a8 cmd/geth: fix whisper flag group capitalization 2017-06-26 13:40:43 +03:00
f321ed23fb Merge pull request #14687 from markya0616/unused_events
core: remove unused events
2017-06-26 13:27:39 +03:00
413dc1d265 rpc: fix closure problem in batch processing (#14688)
Demo of the issue: https://play.golang.org/p/EeTLFfppqC
2017-06-26 12:26:22 +03:00
fdf2184b1e Merge pull request #14697 from homotopycolimit/master
swarm/storage: remove panic on invalid chunk
2017-06-26 12:21:11 +03:00
3c7338d6c8 Makefile: add make swarm command (#14698)
* Makefile: add make swarm command

* Makefile: minor code formatting polishes
2017-06-26 12:19:53 +03:00
ef8d4711d5 Update README.md (#14701)
README: change heading to "Go Ethereum"
2017-06-26 12:17:06 +03:00
caa00b7e6d swarm/storage: remove panic on invalid chunk 2017-06-25 16:10:54 +02:00
5603eb9116 cmd/puppeth: fix key reuse during faucet deploys 2017-06-23 13:34:21 +03:00
78c04c920d params, VERSION: 1.6.7 unstable 2017-06-23 11:28:43 +02:00
10a45cb59b params: 1.6.6 stable 2017-06-23 11:26:54 +02:00
cd88f69715 Merge pull request #14596 from markya0616/valid_clique_vote
consensus/clique: choose valid votes
2017-06-23 11:21:38 +03:00
46d0d04f97 Merge pull request #14685 from karalabe/ethdb-metrics-fail-fix
eth: gracefully error if database cannot be opened
2017-06-23 11:10:29 +03:00
514659a023 consensus/clique: minor cleanups 2017-06-23 11:06:38 +03:00
01c9cf1cb5 node: don't return non-nil database on error 2017-06-23 09:56:30 +02:00
b751cf3901 Merge pull request #14681 from fjl/build-fixup
travis.yml, cmd/swarm: fix Travis CI build
2017-06-23 10:30:27 +03:00
b6e99deee9 whisper: renamed Info#Message to Info#Messages 2017-06-23 09:18:02 +02:00
d40179f882 eth: gracefully error if database cannot be opened 2017-06-23 10:12:41 +03:00
beb708e6d7 core: remove unused events 2017-06-23 10:39:38 +08:00
f2c5b2cc1c travis.yml: add fakeroot to launchpad builder 2017-06-22 22:21:59 +02:00
a4277450b2 cmd/swarm: disable TestCLISwarmUp because it's flaky 2017-06-22 22:17:53 +02:00
b664bedcf2 Merge pull request #14673 from holiman/txfix
core: add testcase for txpool
2017-06-22 23:01:43 +03:00
eebde1a2e2 core: ensure transactions correctly drop on pool limiting 2017-06-22 21:03:54 +03:00
b0b3cf2eeb core: add testcase for txpool 2017-06-22 20:36:07 +03:00
c98d9b49bf Merge pull request #14677 from karalabe/miner-cli-gasprice
cmd/geth: corrently init gas price for CLI CPU mining
2017-06-22 15:54:24 +03:00
0042f13d47 eth/downloader: separate state sync from queue (#14460)
* eth/downloader: separate state sync from queue

Scheduling of state node downloads hogged the downloader queue lock when
new requests were scheduled. This caused timeouts for other requests.
With this change, state sync is fully independent of all other downloads
and doesn't involve the queue at all.

State sync is started and checked on in processContent. This is slightly
awkward because processContent doesn't have a select loop. Instead, the
queue is closed by an auxiliary goroutine when state sync fails. We
tried several alternatives to this but settled on the current approach
because it's the least amount of change overall.

Handling of the pivot block has changed slightly: the queue previously
prevented import of pivot block receipts before the state of the pivot
block was available. In this commit, the receipt will be imported before
the state. This causes an annoyance where the pivot block is committed
as fast block head even when state downloads fail. Stay tuned for more
updates in this area ;)

* eth/downloader: remove cancelTimeout channel

* eth/downloader: retry state requests on timeout

* eth/downloader: improve comment

* eth/downloader: mark peers idle when state sync is done

* eth/downloader: move pivot block splitting to processContent

This change also ensures that pivot block receipts aren't imported
before the pivot block itself.

* eth/downloader: limit state node retries

* eth/downloader: improve state node error handling and retry check

* eth/downloader: remove maxStateNodeRetries

It fails the sync too much.

* eth/downloader: remove last use of cancelCh in statesync.go

Fixes TestDeliverHeadersHang*Fast and (hopefully)
the weird cancellation behaviour at the end of fast sync.

* eth/downloader: fix leak in runStateSync

* eth/downloader: don't run processFullSyncContent in LightSync mode

* eth/downloader: improve comments

* eth/downloader: fix vet, megacheck

* eth/downloader: remove unrequested tasks anyway

* eth/downloader, trie: various polishes around duplicate items

This commit explicitly tracks duplicate and unexpected state
delieveries done against a trie Sync structure, also adding
there to import info logs.

The commit moves the db batch used to commit trie changes one
level deeper so its flushed after every node insertion. This
is needed to avoid a lot of duplicate retrievals caused by
inconsistencies between Sync internals and database. A better
approach is to track not-yet-written states in trie.Sync and
flush on commit, but I'm focuing on correctness first now.

The commit fixes a regression around pivot block fail count.
The counter previously was reset to 1 if and only if a sync
cycle progressed (inserted at least 1 entry to the database).
The current code reset it already if a node was delivered,
which is not stong enough, because unless it ends up written
to disk, an attacker can just loop and attack ad infinitum.

The commit also fixes a regression around state deliveries
and timeouts. The old downloader tracked if a delivery is
stale (none of the deliveries were requestedt), in which
case it didn't mark the node idle and did not send further
requests, since it signals a past timeout. The current code
did mark it idle even on stale deliveries, which eventually
caused two requests to be in flight at the same time, making
the deliveries always stale and mass duplicating retrievals
between multiple peers.

* eth/downloader: fix state request leak

This commit fixes the hang seen sometimes while doing the state
sync. The cause of the hang was a rare combination of events:
request state data from peer, peer drops and reconnects almost
immediately. This caused a new download task to be assigned to
the peer, overwriting the old one still waiting for a timeout,
which in turned leaked the requests out, never to be retried.
The fix is to ensure that a task assignment moves any pending
one back into the retry queue.

The commit also fixes a regression with peer dropping due to
stalls. The current code considered a peer stalling if they
timed out delivering 1 item. However, the downloader never
requests only one, the minimum is 2 (attempt to fine tune
estimated latency/bandwidth). The fix is simply to drop if
a timeout is detected at 2 items.

Apart from the above bugfixes, the commit contains some code
polishes I made while debugging the hang.

* core, eth, trie: support batched trie sync db writes

* trie: rename SyncMemCache to syncMemBatch
2017-06-22 15:26:03 +03:00
d432688886 cmd/geth: corrently init gas price for CLI CPU mining 2017-06-22 14:58:07 +03:00
58a1e13e6d Merge pull request #14657 from markya0616/refactor_clique
consensus/clique: fix typo and don't need to add snapshot into recents again
2017-06-21 17:58:15 +03:00
a1f3878ec5 swarm/test: add integration test for 'swarm up' (#14353) 2017-06-21 14:54:23 +02:00
a20a02ce0b README: document new config file option (#14348) 2017-06-21 14:53:08 +02:00
9a44e1035e cmd/evm, core/vm: add --nomemory, --nostack to evm (#14617) 2017-06-21 14:52:31 +02:00
c62d5422bb whisper: use hexutil.UnmarshalFixedText for topic parsing 2017-06-21 12:58:00 +02:00
9012863ad7 Merge pull request #14667 from fjl/swarm-fuse-cleanup
swarm/fuse: simplify externalUnmount, use subtests
2017-06-21 13:51:00 +03:00
a5d08c893d les: code refactoring (#14416)
This commit does various code refactorings:

- generalizes and moves the request retrieval/timeout/resend logic out of LesOdr
  (will be used by a subsequent PR)
- reworks the peer management logic so that all services can register with
  peerSet to get notified about added/dropped peers (also gets rid of the ugly
  getAllPeers callback in requestDistributor)
- moves peerSet, LesOdr, requestDistributor and retrieveManager initialization
  out of ProtocolManager because I believe they do not really belong there and the
  whole init process was ugly and ad-hoc
2017-06-21 12:27:38 +02:00
a4e4c76cb3 whisper: use syncmap for dynamic configuration options 2017-06-21 11:15:47 +02:00
7a11e86442 whisper: move flags from whisper package to utils 2017-06-21 10:49:14 +02:00
4a1d516d78 whisper: renamed errors 2017-06-21 10:32:36 +02:00
b6b0e00198 whisper: fallback to default config if none is specified 2017-06-21 10:24:34 +02:00
3d66ba56ef whisper: remove obsolete api tests 2017-06-21 10:23:45 +02:00
767dc6c73d whisper: remove gencodec override for config 2017-06-21 10:11:54 +02:00
36e7963467 whisper: move ShhClient to its own package 2017-06-21 10:11:46 +02:00
98e101ef8e swarm/fuse: use subtests 2017-06-21 09:56:58 +02:00
50c18e6eb8 swarm/fuse: simplify externalUnmount
The code looked for /usr/bin/diskutil on darwin, but it's actually
located in /usr/sbin. Fix that by not specifying the absolute path.
Also remove weird timeout construction and extra whitespace.
2017-06-21 09:56:58 +02:00
60e27b51bc ethclient: fix TransactionByHash pending return value. (#14663)
As per #14661 TransactionByHash always returns false for pending.
This uses blockNumber rather than blockHash to ensure that it returns
the correct value for pending and will not suffer side-effects if
eth_getTransactionByHash is fixed in future.
2017-06-21 09:53:50 +02:00
693d9ccbfb trie: more node iterator improvements (#14615)
* ethdb: remove Set

Set deadlocks immediately and isn't part of the Database interface.

* trie: add Err to Iterator

This is useful for testing because the underlying NodeIterator doesn't
need to be kept in a separate variable just to get the error.

* trie: add LeafKey to iterator, panic when not at leaf

LeafKey is useful for callers that can't interpret Path.

* trie: retry failed seek/peek in iterator Next

Instead of failing iteration irrecoverably, make it so Next retries the
pending seek or peek every time.

Smaller changes in this commit make this easier to test:

* The iterator previously returned from Next on encountering a hash
  node. This caused it to visit the same path twice.
* Path returned nibbles with terminator symbol for valueNode attached
  to fullNode, but removed it for valueNode attached to shortNode. Now
  the terminator is always present. This makes Path unique to each node
  and simplifies Leaf.

* trie: add Path to MissingNodeError

The light client trie iterator needs to know the path of the node that's
missing so it can retrieve a proof for it. NodeIterator.Path is not
sufficient because it is updated when the node is resolved and actually
visited by the iterator.

Also remove unused fields. They were added a long time ago before we
knew which fields would be needed for the light client.
2017-06-20 18:26:09 +02:00
5c53a5be66 consensus/clique: fix typo and don't add snapshot into recents again 2017-06-20 10:20:45 +08:00
431cf2a1e4 Merge pull request #14635 from necaremus/patch-1
cmd/geth: fixed a minor typo in the comments
2017-06-16 17:11:54 +03:00
4f77857f74 cmd/geth: fixed a minor typo in the comments 2017-06-16 15:03:19 +02:00
fade09a7ff eth: remove les server from protocol manager (#14625) 2017-06-15 15:28:57 +02:00
b58a501673 whisperv5: integrate whisper and add whisper RPC simulator 2017-06-15 11:53:15 +02:00
db6e695002 consensus/clique: choose valid votes 2017-06-14 16:49:33 +08:00
335abdceb1 Merge pull request #14581 from holiman/byte_opt
core/vm: improve opByte
2017-06-13 14:44:19 +03:00
732273094c Merge pull request #14604 from bas-vk/mobile-getfrom
mobile: use EIP155 signer for determining sender
2017-06-13 14:09:25 +03:00
b8793edd83 mobile: add a regression test for signer recovery 2017-06-13 13:39:39 +03:00
eb92522278 mobile: use EIP155 signer for determining sender 2017-06-13 09:13:59 +02:00
061889d4ea rlp, trie, contracts, compression, consensus: improve comments (#14580) 2017-06-12 14:45:17 +02:00
e3dfd55820 Merge pull request #14598 from konradkonrad/fix_makedag
consensus/ethash, cmd/geth: Fix `makedag` epoch
2017-06-12 13:34:26 +03:00
2fefe4baa0 consensus: Fix makedag epoch
`geth makedag <blocknumber> <path>` was creating DAGs for
`<blocknumber>/<epoch_length> + 1`, hence
it was impossible to create an epoch 0 DAG.

This fixes the calculations in `consensus/ethash/ethash.go` for
`MakeDataset` and `MakeCache`, and applies `gofmt`.
2017-06-12 11:15:16 +02:00
ac9865791a core/vm, common/math: Add doc about Byte, fix format 2017-06-08 23:16:05 +02:00
80f7c6c299 cmd/evm: add --prestate, --sender, --json flags for fuzzing (#14476) 2017-06-07 17:09:08 +02:00
bc24b7a912 core/types: use Header.Hash for block hashes (#14587)
Fixes #14586
2017-06-07 12:06:25 +02:00
1496b3aff6 common/math, core/vm: Un-expose bigEndianByteAt, use correct terms for endianness 2017-06-06 18:38:38 +02:00
1e9f86b49e cmd/swarm: fix error handling in 'swarm up' (#14557)
The error returned by client.Upload was previously being ignored due to becoming
out of scope outside the if statement. This has been fixed by instead defining a
function which returns the hash and error (rather than trying to set the hash in
each branch of the if statement).
2017-06-06 09:39:10 +02:00
65ea913e29 Merge pull request #14583 from ethersphere/core-log-fixes
core: Fix VM error logging
2017-06-06 10:31:27 +03:00
9a0e433b13 accounts: fix spelling error (#14567) 2017-06-06 09:28:47 +02:00
04d2de9119 core: Fix VM error logging
Signed-off-by: Lewis Marshall <lewis@lmars.net>
2017-06-05 23:51:32 +01:00
3285a0fda3 core/vm, common/math: Add fast getByte for bigints, improve opByte 2017-06-05 08:44:11 +02:00
6171d01b11 VERSION, params: begin Geth 1.6.6 release cycle 2017-06-01 22:08:19 +03:00
cf87713dd4 params: mark Geth v1.6.5 stable (Hat Trick) 2017-06-01 21:48:47 +03:00
ac92d7c411 Merge pull request #14570 from Arachnid/jumpdestanalysis
core/vm: Use a bitmap instead of a map for jumpdest analysis
2017-06-01 21:44:50 +03:00
d5a79934dc core/vm: Use a bitmap instead of a map for jumpdest analysis
t push --force
2017-06-01 19:14:05 +01:00
0424192e61 VERSION, params: begin geth 1.6.5 cycle 2017-06-01 17:37:44 +03:00
9c2882b2e5 params: Geth 1.6.4 stable (hotfix) 2017-06-01 17:33:17 +03:00
1a0eb903f1 internal/ethapi: initialize account mutex in lock properly 2017-06-01 17:16:12 +03:00
0036e2a747 swarm/dev: add development environment (#14332)
This PR adds a Swarm development environment which can be run in a
Docker container and provides scripts for building binaries and running
Swarm clusters.
2017-06-01 12:52:18 +02:00
727eadacca VERSION, params: begin Geth 1.6.4 release cycle 2017-06-01 11:43:57 +03:00
99cba96f26 params: release Geth 1.6.3 - Covfefe 2017-06-01 11:41:48 +03:00
f272879e5a Merge pull request #14565 from karalabe/relax-privkey-checks
accounts/keystore, crypto: don't enforce key checks on existing keyfiles
2017-06-01 11:14:11 +03:00
72dd51e25a accounts/keystore, crypto: don't enforce key checks on existing keyfiles 2017-06-01 11:11:06 +03:00
799a469000 Merge pull request #14561 from karalabe/txpool-perf-fix
core: reduce transaction reorganization overhead
2017-06-01 10:33:47 +03:00
f4d81178d8 Merge pull request #14563 from karalabe/ethstats-reduce-traffic-2
ethstats: reduce ethstats traffic by trottling reports
2017-06-01 10:33:21 +03:00
310d2e7ef4 Merge pull request #14564 from karalabe/fix-1.6-docker
cotnainers/docker: fix the legacy alpine image before dropping
2017-06-01 10:33:03 +03:00
3ecde4e2aa cotnainers/docker: fix the legacy alpine image before dropping 2017-06-01 00:21:47 +03:00
a355b401db ethstats: reduce ethstats traffic by trottling reports 2017-06-01 00:16:19 +03:00
cba33029a8 core: only reorg changed account, not all 2017-05-31 23:26:24 +03:00
9702badd83 core: don't uselessly recheck transactions on dump 2017-05-31 21:29:50 +03:00
067dc2cbf5 params, VERSION: 1.6.3 unstable 2017-05-31 05:47:35 +02:00
65979770e6 params: 1.6.2 stable 2017-05-31 05:45:13 +02:00
41bdf49eed Merge pull request #14516 from holiman/noncefixes
internal/ethapi: add mutex around signing + nonce assignment
2017-05-30 18:15:57 +03:00
ea11f7dd7a internal/ethapi: add mutex around signing + nonce assignment
This prevents concurrent assignment of identical nonces when automatic
assignment is used.
2017-05-30 16:43:38 +02:00
8df24760d7 Merge pull request #14553 from karalabe/puppeth-key-check
cmd/puppeth: fix improper key validation for remotes
2017-05-30 14:50:48 +03:00
71814bf6c4 Merge pull request #14547 from karalabe/txpool-gas-decrease
core: check for gas limit exceeding txs too on new block
2017-05-30 14:50:27 +03:00
ec1700600a cmd/puppeth: fix improper key validation for remotes 2017-05-30 14:24:01 +03:00
b0f30b0b37 Merge pull request #14545 from karalabe/clique-cache-signatures
consensus/clique: cache block signatures for fast checks
2017-05-30 12:56:57 +03:00
e96f2981e2 Merge pull request #14548 from karalabe/ethstats-no-txs
ethstats: don't report transaction content, only hash
2017-05-30 12:54:00 +03:00
09d59da3a1 ethstats: don't report transaction content, only hash 2017-05-30 02:15:40 +03:00
280609c99b core: check for gas limit exceeding txs too on new block 2017-05-30 00:31:37 +03:00
309da541de consensus/clique: cache block signatures for fast checks 2017-05-29 22:07:02 +03:00
dd06c85843 Merge pull request #14523 from karalabe/txpool-cli-flags
cmd, core, eth: configurable txpool parameters
2017-05-29 11:42:48 +03:00
ae40d51410 Merge pull request #14539 from karalabe/txpool-inspec-nonces
internal/ethapi: fix tx nonces in pool inspect/content
2017-05-29 11:37:34 +03:00
b865fad888 Merge pull request #14537 from karalabe/setgasprice-durning-nomine
eth: update default gas price when not mining too
2017-05-29 11:37:26 +03:00
afb17cf071 Merge pull request #14524 from karalabe/noimport-during-fastsync
eth: don't import propagated blocks during fastsync
2017-05-29 11:37:08 +03:00
08959bbc70 cmd, core, eth: configurable txpool parameters 2017-05-29 11:29:46 +03:00
673c92db6b internal/ethapi: fix tx nonces in pool inspect/content 2017-05-29 11:17:31 +03:00
c2a494c743 eth: update default gas price when not mining too 2017-05-29 10:21:34 +03:00
afdd23b5ca eth: don't import propagated blocks during fastsync 2017-05-26 16:04:12 +03:00
cb809c03da Merge pull request #14517 from Ali92hm/master
Improved Dockerfile?
2017-05-26 10:54:41 +01:00
45421d3130 dockerfile: expose 30303/udp 2017-05-25 12:34:29 -07:00
115e7d71cc dockerfile: cp geth to /usr/local/bin 2017-05-25 12:34:28 -07:00
dd5ed01f3b Merge pull request #14514 from karalabe/go1.8.3
travis, appveyor: bump to Go 1.8.3, Android NDK 14b
2017-05-25 17:42:40 +03:00
b7ff0d42e3 Merge pull request #14515 from karalabe/golint-tooooolong
core: fix various golint warnings and errors
2017-05-25 17:40:51 +03:00
c98bce709c core: fix minor accidental typos and comment errors 2017-05-25 17:22:45 +03:00
17f0b11942 core: typos and comments improve
1. fix typos
2. methods recevier of struct should be same
3. comments improve

(cherry picked from commit 1ba9795395)
2017-05-25 17:14:33 +03:00
6231edcbab travis, appveyor: bump to Go 1.8.3, Android NDK 14b 2017-05-25 17:05:33 +03:00
07aae19e5d Merge pull request #14446 from bas-vk/cli-help
Rewrite templates for (sub)commands help section
2017-05-25 13:58:55 +03:00
b596b4ba5b Merge pull request #14513 from obscuren/allocate-stack
core/vm: allocate stack to 1024
2017-05-25 13:22:56 +03:00
8b1e4c4c5e README: corrected attach example (#14512) 2017-05-25 13:22:26 +03:00
846d091bd2 core/vm: allocate stack to 1024
Pre allocate the stack to 1024 optimising stack pushing, reducing calls
to runtime.makeslice and runtime.mallocgc
2017-05-25 11:37:04 +02:00
a346aedb90 cmd/geth: reorganise help section for new cli flag handling 2017-05-25 09:15:51 +02:00
ef25b826e6 Merge pull request #14502 from karalabe/mobile-import-ecdsa
Enforce 256 bit keys on raw import, support raw mobile imports
2017-05-24 22:30:47 +02:00
261b3e2351 Merge pull request #14336 from obscuren/metropolis-preparation
consensus, core/*, params: metropolis preparation refactor
2017-05-24 22:28:22 +02:00
344f25fb3e Merge pull request #14507 from karalabe/faucet-misspell
cmd/faucet: fix a few typos
2017-05-24 17:14:21 +03:00
1afaea4bfe cmd/faucet: fix a few typos 2017-05-24 17:12:07 +03:00
11cf5b7ead consensus/ethash: fix TestCalcDifficulty 2017-05-24 15:40:54 +02:00
069cb661c3 crypto/bn256: fix go vet false positive
Also add the package to the license tool ignore list.
2017-05-24 15:40:26 +02:00
3b8915e387 Merge pull request #14504 from bas-vk/wallet-import
cmd/geth: reintroduce wallet import subcommand
2017-05-24 13:00:22 +03:00
437ceaa9be cmd/geth: reintroduce wallet import subcommand 2017-05-23 17:45:26 +02:00
136f78ff0a mobile: support importing flat ecdsa keyst too 2017-05-23 14:58:28 +03:00
aa73420207 accounts/keystore, crypto: enforce 256 bit keys on import 2017-05-23 14:58:03 +03:00
3556962053 Merge pull request #14501 from sqli-nantes/master
mobile: manage FilterQuery enabling contract events subscription
2017-05-23 13:47:22 +03:00
e1e87d8b1a common: fixed byte padding functions
Byte padding function should return the given slice if the length is
smaller or equal rather than *only* smaller than.

This fix improves almost all EVM push operations.
2017-05-23 11:24:07 +02:00
30cc1c3bf0 mobile: Add management methods to {Addresses,Topics,Hashes} structures 2017-05-23 11:16:25 +02:00
10582a97ca core/vm: expose intpool to stack dup method
Improve the duplication method of the stack to reuse big ints by passing
in an existing integer pool.
2017-05-23 10:52:11 +02:00
e16a7ef60f core/vm: capped int pool 2017-05-23 10:40:09 +02:00
a816e75662 core/vm: improved push instructions
Improved push instructions by removing unnecessary big int allocations
and by making it int instead of big.Int
2017-05-23 10:39:53 +02:00
3ee75bec9f cmd/evm: added mem/cpu profiling 2017-05-23 10:17:55 +02:00
04b668b232 core/vm: improve error message for invalid opcodes 2017-05-22 17:48:07 +02:00
da636c53d6 mobile: Allows mobile clients to create custom FilterQueries 2017-05-22 17:12:36 +02:00
2a41e76b39 swarm/api: Fix adding paths which exist as manifests (#14482)
Signed-off-by: Lewis Marshall <lewis@lmars.net>
2017-05-22 08:57:03 +02:00
4a2c17b1ab cmd/swarm: Add --httpaddr flag (#14475)
Fixes #14474.

Signed-off-by: Lewis Marshall <lewis@lmars.net>
2017-05-22 08:56:40 +02:00
bc75351edf README: fixing typo in documentation (#14493) 2017-05-22 08:47:27 +02:00
33b158e0ed discover: Changed Logging from Debug to Info (#14485) 2017-05-20 13:10:59 +02:00
83721a95ce internal/ethapi: lock when auto-filling transaction nonce (#14483)
More context in the bug This solves the problems of transactions being
submitted simultaneously, and getting the same nonce, due to the gap (due to
signing) between nonce-issuance and nonce-update. With this PR, a lock will
need to be acquired whenever a nonce is used, and released when the transaction
is submitted or errors out.
2017-05-19 15:03:56 +02:00
e7119ce12d core/state: fixed (self)destructed objects
Add the object to the list of destructed objects during a selfdestruct /
suicide operation and also remove it from the list once the journal
reverts.
2017-05-18 09:05:58 +02:00
a5f6a1cb7c consensus, core, core/vm, parems: review fixes 2017-05-18 09:05:58 +02:00
e6aff513db core/types: corrected abstract signing address 2017-05-18 09:05:58 +02:00
8a4c1fb799 consensus/ethash: set time to current instead of parent time 2017-05-18 09:05:58 +02:00
10a57fc3d4 consensus, core/*, params: metropolis preparation refactor
This commit is a preparation for the upcoming metropolis hardfork. It
prepares the state, core and vm packages such that integration with
metropolis becomes less of a hassle.

* Difficulty calculation requires header instead of individual
  parameters
* statedb.StartRecord renamed to statedb.Prepare and added Finalise
  method required by metropolis, which removes unwanted accounts from
  the state (i.e. selfdestruct)
* State keeps record of destructed objects (in addition to dirty
  objects)
* core/vm pre-compiles may now return errors
* core/vm pre-compiles gas check now take the full byte slice as argument
  instead of just the size
* core/vm now keeps several hard-fork instruction tables instead of a
  single instruction table and removes the need for hard-fork checks in
  the instructions
* core/vm contains a empty restruction function which is added in
  preparation of metropolis write-only mode operations
* Adds the bn256 curve
* Adds and sets the metropolis chain config block parameters (2^64-1)
2017-05-18 09:05:58 +02:00
a2f23ca9b1 cmd, core, eth, miner: remove txpool gas price limits (#14442) 2017-05-16 21:07:27 +02:00
e20158176d les: fix goroutine leak in execQueue (#14480)
execQueue used an atomic counter to track whether the queue had been
closed, but the checking the counter didn't happen because the queue was
blocked on its channel.

Fix it by using a condition variable instead of sync/atomic. I tried an
implementation based on channels first, but it was hard to make it
reliable.

quit now waits for the queue loop to exit.
2017-05-16 20:56:02 +02:00
ef7b9fb7d0 cmd/puppeth: v4/v5 boot separation, signer gas configs (#14453) 2017-05-13 02:03:56 +02:00
b0d0fafd68 swarm/api: fix error reporting in api.Resolve (#14464)
Previously, resolve errors were being swallowed and the returned error
was a generic "not a content hash" which isn't helpful.

This updates the Resolve function to fail fast rather than only
returning an error at the end, and also adds test coverage.
2017-05-13 02:02:25 +02:00
90c7155ef4 mobile: accept nil for chainid as homestead signing (#14463) 2017-05-13 02:00:39 +02:00
df4e7eccf5 containers/vagrant: add support for CentOS (#14380)
CentOS has been added as a multi-machine option to the Vagrant script.
Ubuntu is still the default option. For starting the CentOS machine, use:

   vagrant up centos
2017-05-13 01:59:03 +02:00
7c707d14d1 Merge pull request #14454 from karalabe/mobile-surface-txrlp
mobile: add toString & rlp/json encoding for protocol types
2017-05-11 17:53:15 +03:00
953a995116 mobile: add toString & rlp/json encoding for protocol types 2017-05-11 17:25:40 +03:00
c5840ce12f Merge pull request #14452 from karalabe/dual-bootnodes
cmd, node: support different bootnodes, fix default light port
2017-05-10 21:00:22 +03:00
3b3989de6a cmd, node: support different bootnodes, fix default light port 2017-05-10 17:51:52 +03:00
40976ea1a0 README: update attach instructions for testnet users (#14448) 2017-05-09 16:06:07 +02:00
d18b509e40 Merge pull request #14441 from karalabe/receipt-data-regression
core: fix processing regression during receipt import
2017-05-08 12:37:01 +03:00
2e4d23a793 Merge pull request #14427 from zsfelfoldi/compress
common/bitutil: added data compression algorithm
2017-05-08 12:30:35 +03:00
60293820b7 core: fix processing regression during receipt import 2017-05-08 12:09:35 +03:00
82defe5c56 common/compress: internalize encoders, add length wrappers 2017-05-08 11:38:25 +03:00
dd483d7d0d Merge pull request #14440 from karalabe/cocoapods-confirm-fix
travis: adapt build script to new travis VM settings
2017-05-08 11:26:35 +03:00
dddebe469b travis: adapt build script to new travis VM settings 2017-05-08 11:22:08 +03:00
cf19586cfb common/bitutil: fix decompression corner cases; fuzz, test & bench 2017-05-06 19:06:17 +03:00
fd5d51c9ae common/bitutil: added data compression algorithm 2017-05-05 20:24:48 +02:00
2ec5cf1673 Merge pull request #14423 from karalabe/bitutil
common/bitutil, consensus/ethash: reusable bitutil package
2017-05-05 18:23:08 +03:00
36a800a1d2 common/bitutil, consensus/ethash: reusable bitutil package 2017-05-05 16:00:11 +03:00
93832b633e VERSION, params: begin 1.6.2 release cycle 2017-05-04 14:34:59 +03:00
021c3c2816 params: release Geth 1.6.1, Deripors of Ohratuu 2017-05-04 14:11:45 +03:00
ff2c966e7f Merge pull request #14418 from karalabe/rinkeby-flag
cmd, core, params: add --rinkeby flag for fast connectivity
2017-05-04 13:54:02 +03:00
14cc40a31a Hive-test fixes (#14419)
* core: Fix for consensus test gasLimit > 2^63-1 https://github.com/ethereum/tests/blob/develop/BlockchainTests/bcInvalidHeaderTest.json#L238

* core: fix testcase for uncle gasUsage > gasLimit : https://github.com/ethereum/tests/blob/develop/BlockchainTests/EIP150/bcUncleHeaderValiditiy.json#L986

* math/big: rename TTM63m1 -> MaxBig63, + go fmt

* core: documentation
2017-05-04 13:53:42 +03:00
881df0e629 Merge pull request #14413 from bas-vk/cli-chain-mngt
Migrate remaining flags/command to new style
2017-05-04 13:31:09 +03:00
1c2f6f5597 Merge pull request #14402 from karalabe/tiered-faucet
cmd/faucet, cmd/puppeth: support multi-tiered faucet
2017-05-04 13:07:41 +03:00
d51a9fd6b7 cmd, core, params: add --rinkeby flag for fast connectivity 2017-05-04 12:36:20 +03:00
464f30d301 cmd/faucet: fix period to days conversion 2017-05-04 11:43:18 +03:00
8a28408616 cmd/faucet, cmd/puppeth: support multi-tiered faucet 2017-05-04 11:42:43 +03:00
e1dc7ece62 Merge pull request #14414 from gluk256/77_release
build: wnode added to the build configuration
2017-05-03 16:45:39 +03:00
99127ff2e7 build: wnode added to the build configuration 2017-05-03 15:36:02 +02:00
81d6ec908a cmd/geth: migrate dumpconfig command/flags 2017-05-03 14:39:07 +02:00
38b4fc8069 cmd/geth: migrate bug command/flags 2017-05-03 14:33:08 +02:00
11ab73f6d8 cmd/geth: migrate metric command/flags 2017-05-03 14:33:08 +02:00
18e9cb1187 cmd/geth: reorganise misc commands/flags 2017-05-03 14:33:07 +02:00
502a2bd69f cmd/geth: reorganise console/attach commands/flags 2017-05-03 14:33:07 +02:00
8b517d7f00 cmd/geth: reorganise chain commands/flags 2017-05-03 14:33:03 +02:00
cad071009d Merge pull request #14412 from karalabe/init-both-chains
cmd/geth, cmd/utils: init/removedb on light/full dbs simultaneously
2017-05-03 14:32:24 +03:00
181a3309df cmd/geth, cmd/utils: init/removedb on light/full dbs simultaneously 2017-05-03 13:35:47 +03:00
02fa3e3179 Merge pull request #14411 from karalabe/clique-double-sign
consensus/clique: fix overflow on recent signer check around genesis
2017-05-03 11:45:36 +03:00
a8eafcdc0e Merge pull request #13885 from bas-vk/rpc_generic_pubsub
rpc: support subscriptions under custom namespaces
2017-05-03 11:41:07 +03:00
bcf2465b0b consensus/clique: fix overflow on recent signer check around genesis 2017-05-03 11:01:06 +03:00
c3dc01caf1 README: add config to genesis.json (#14373)
README: add config to genesis.json
2017-05-03 09:10:36 +02:00
cf4faa491a cmd/puppeth, vendor: update ssh, manage server keys (#14398) 2017-05-03 09:09:34 +02:00
59966255ad Merge pull request #14407 from karalabe/ethash-generation-race
consensus/ethash: fix a timestamp update race
2017-05-03 09:17:01 +03:00
f8acc0af7e consensus/ethash: fix a timestamp update race 2017-05-02 16:48:36 +03:00
02a29060d2 Merge pull request #14406 from karalabe/downloader-sensitive-code
eth/downloader: fix a potential issue against future refactors
2017-05-02 16:31:31 +03:00
0255ed6335 Merge pull request #14403 from fjl/console-number
console: avoid float64 when remarshaling parameters
2017-05-02 16:24:34 +03:00
96c2ab22e0 eth/downloader: fix a potential issue against future refactors 2017-05-02 16:14:35 +03:00
5494e6e7ab Merge pull request #14399 from bas-vk/rpc-cors
rpc: disable CORS if user has not specified a custom config
2017-05-02 15:53:47 +03:00
32db571681 console: avoid float64 when remarshaling parameters
With Go 1.7, encoding/json marshals float64 using scientific
notation ("10e+6"), but Go's int and *big.Int decoders don't accept such
numbers. This change disables use of float64 to avoid the problem.
2017-05-02 13:42:55 +02:00
a6af56fa4d rpc: disable CORS if user has not specified custom config 2017-05-02 11:14:40 +02:00
5884606ec3 Merge pull request #14388 from bas-vk/cli-account-mngt
cmd/geth: reorganise account/wallet command/flags
2017-05-02 10:05:20 +03:00
f6c0f76cc5 cmd/geth: reorganise account/wallet command/flags 2017-04-28 14:01:54 +02:00
f9be9a2302 whisper: switching to v5 + minor refactoring (#14387) 2017-04-28 11:57:15 +02:00
95f0bd0acf whisper: message format refactoring (#14335)
* whisper: salt removed from AES encryption
* whisper: padding format updated
* whisper: padding test added
* whisper: padding refactored, tests fixed
* whisper: padding test updated
* whisper: wnode bugfix
* whisper: send/receive protocol updated
* whisper: minor update
* whisper: bugfix in test
* whisper: updated parameter names and comments
* whisper: functions renamed
* whisper: minor refactoring
2017-04-26 21:05:48 +02:00
8dce4c283d Merge pull request #14379 from farazdagi/fix/deadlock-in-node-wait
node: fixes deadlock on Wait()
2017-04-25 18:47:55 +03:00
fff16169c6 Merge pull request #14377 from karalabe/unify-network-ids
cmd, eth, les, mobile: make networkid uint64 everywhere
2017-04-25 18:30:56 +03:00
5f7eb78918 node: fixes deadlock on Wait() 2017-04-25 18:04:02 +03:00
e61035c5a3 cmd, eth, les, mobile: make networkid uint64 everywhere 2017-04-25 14:53:50 +03:00
37e3f561f1 rpc: support subscriptions under custom namespaces 2017-04-25 11:13:22 +02:00
ba3bcd16a6 Merge pull request #14350 from fjl/trie-iterator-skip-2
eth: add debug_storageRangeAt
2017-04-25 11:10:20 +03:00
207bd7d2cd eth: add debug_storageRangeAt 2017-04-25 02:14:32 +02:00
4047ccad2f trie: add start key to NodeIterator constructors
The 'step' method is split into two parts, 'peek' and 'push'. peek
returns the next state but doesn't make it current.

The end of iteration was previously tracked by setting 'trie' to nil.
End of iteration is now tracked using the 'iteratorEnd' error, which is
slightly cleaner and requires less code.
2017-04-25 02:14:31 +02:00
a13e920af0 trie: clean up iterator constructors
Make it so each iterator has exactly one public constructor:

- NodeIterators can be created through a method.
- Iterators can be created through NewIterator on any NodeIterator.
2017-04-25 02:14:31 +02:00
f958d7d482 trie: rework and document key encoding
'encode' and 'decode' are meaningless because the code deals with three
encodings. Document the encodings and give a name to each one.
2017-04-25 02:14:31 +02:00
7cc6abeef6 Merge pull request #14372 from bas-vk/bootnodegenkey
cmd/bootnode: stop after generating/writing nodekey
2017-04-24 19:31:23 +03:00
54253aae4c internal/ethapi: return empty arrays instead of null (#14374)
* internal/ethapi: return empty arrays instead of null

* internal/ethapi: minor comments to avoid future regressions
2017-04-24 15:00:30 +03:00
09aabaea9f Merge pull request #14364 from fjl/core-remove-split-stat-ty
core, light: delete SplitStatTy, ChainSplitEvent (unused)
2017-04-24 11:43:22 +03:00
ecec454e92 cmd/bootnode: stop after generating/writing nodekey 2017-04-24 10:40:20 +02:00
7b2fc0643f core, light: delete SplitStatTy, ChainSplitEvent (unused) 2017-04-21 18:56:00 +02:00
d2fda73ad7 Merge pull request #14339 from karalabe/faucet-block-banned-users
cmd/faucet: further user validations and bot protection
2017-04-20 17:42:36 +03:00
5aa21d8b32 Merge pull request #14357 from karalabe/nousb-flag
cmd, node: add --nousb and node.Config.NoUSB to disable hw wallets
2017-04-20 17:40:57 +03:00
9fc90b6747 Merge pull request #14358 from karalabe/wrong-genesis-description
core: make genesis incompatibility error more explicit
2017-04-20 15:17:59 +03:00
edef84da2b core: make genesis incompatibility error more explicit 2017-04-20 14:14:13 +03:00
6430e672c9 cmd, node: add --nosub and node.Config.NoUSB to disable hw wallets 2017-04-20 14:01:51 +03:00
a31d268b76 trie: remove Key in MissingNodeError
The key was constructed from nibbles, which isn't possible for all
nodes. Remove the only use of Key in LightTrie by always retrying with
the original key that was looked up.
2017-04-18 14:52:11 +02:00
e353f9c088 Merge pull request #13886 from bas-vk/rpc_blocknum_parse
rpc: improve BlockNumber unmarshal parsing
2017-04-18 14:55:53 +03:00
af48a331bf cmd: integrate invisible recaptcha into puppeth 2017-04-16 20:53:27 +03:00
80e74fc1e0 cmd/faucet: fix websocket double close/reopen 2017-04-16 20:39:42 +03:00
03dffe3efd cmd/faucet: add optional recaptcha validation support 2017-04-16 19:49:40 +03:00
cb3f5f8b93 cmd/faucet: double check user against the GH website 2017-04-16 18:49:06 +03:00
c7a4d9cf8a VERSION, params: begin 1.6.1 release cycle 2017-04-14 13:43:10 +03:00
7d0ac94809 rpc: improve BlockNumber unmarshal parsing 2017-04-13 13:20:19 +02:00
1242 changed files with 19973 additions and 2514512 deletions

View File

@ -1,3 +1,6 @@
.git
**/.git
**/*_test.go
build/_workspace
build/_bin
tests/testdata

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "tests"]
path = tests/testdata
url = https://github.com/ethereum/tests

View File

@ -6,7 +6,7 @@ matrix:
- os: linux
dist: trusty
sudo: required
go: 1.7.5
go: 1.7.6
script:
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse
- sudo modprobe fuse
@ -19,7 +19,7 @@ matrix:
- os: linux
dist: trusty
sudo: required
go: 1.8.1
go: 1.8.3
script:
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse
- sudo modprobe fuse
@ -29,7 +29,7 @@ matrix:
- go run build/ci.go test -coverage -misspell
- os: osx
go: 1.8.1
go: 1.8.3
sudo: required
script:
- brew update
@ -42,7 +42,7 @@ matrix:
- os: linux
dist: trusty
sudo: required
go: 1.8.1
go: 1.8.3
env:
- ubuntu-ppa
- azure-linux
@ -53,6 +53,7 @@ matrix:
- 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
@ -80,7 +81,7 @@ matrix:
sudo: required
services:
- docker
go: 1.8.1
go: 1.8.3
env:
- azure-linux-mips
script:
@ -120,16 +121,16 @@ matrix:
- azure-android
- maven-android
before_install:
- curl https://storage.googleapis.com/golang/go1.8.1.linux-amd64.tar.gz | tar -xz
- curl https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz | tar -xz
- export PATH=`pwd`/go/bin:$PATH
- export GOROOT=`pwd`/go
- export GOPATH=$HOME/go
script:
# Build the Android archive and upload it to Maven Central and Azure
- curl https://dl.google.com/android/repository/android-ndk-r13b-linux-x86_64.zip -o android-ndk-r13b.zip
- unzip -q android-ndk-r13b.zip && rm android-ndk-r13b.zip
- mv android-ndk-r13b $HOME
- export ANDROID_NDK=$HOME/android-ndk-r13b
- curl https://dl.google.com/android/repository/android-ndk-r14b-linux-x86_64.zip -o android-ndk-r14b.zip
- unzip -q android-ndk-r14b.zip && rm android-ndk-r14b.zip
- mv android-ndk-r14b $HOME
- export ANDROID_NDK=$HOME/android-ndk-r14b
- mkdir -p $GOPATH/src/github.com/ethereum
- ln -s `pwd` $GOPATH/src/github.com/ethereum
@ -137,7 +138,7 @@ matrix:
# This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads
- os: osx
go: 1.8.1
go: 1.8.3
env:
- azure-osx
- azure-ios
@ -147,7 +148,7 @@ matrix:
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -upload gethstore/builds
# Build the iOS framework and upload it to CocoaPods and Azure
- gem uninstall cocoapods -a
- gem uninstall cocoapods -a -x
- gem install cocoapods
- mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak
@ -163,7 +164,7 @@ matrix:
- os: linux
dist: trusty
sudo: required
go: 1.8.1
go: 1.8.3
env:
- azure-purge
script:

View File

@ -4,11 +4,12 @@ ADD . /go-ethereum
RUN \
apk add --update git go make gcc musl-dev linux-headers && \
(cd go-ethereum && make geth) && \
cp go-ethereum/build/bin/geth /geth && \
cp go-ethereum/build/bin/geth /usr/local/bin/ && \
apk del git go make gcc musl-dev linux-headers && \
rm -rf /go-ethereum && rm -rf /var/cache/apk/*
EXPOSE 8545
EXPOSE 30303
EXPOSE 30303/udp
ENTRYPOINT ["/geth"]
ENTRYPOINT ["geth"]

View File

@ -2,7 +2,7 @@
# with Go source code. If you know what GOPATH is then you probably
# don't need to bother with make.
.PHONY: geth android ios geth-cross evm all test clean
.PHONY: geth android ios geth-cross swarm evm all test clean
.PHONY: geth-linux geth-linux-386 geth-linux-amd64 geth-linux-mips64 geth-linux-mips64le
.PHONY: geth-linux-arm geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64
.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64
@ -16,6 +16,11 @@ geth:
@echo "Done building."
@echo "Run \"$(GOBIN)/geth\" to launch geth."
swarm:
build/env.sh go run build/ci.go install ./cmd/swarm
@echo "Done building."
@echo "Run \"$(GOBIN)/swarm\" to launch swarm."
evm:
build/env.sh go run build/ci.go install ./cmd/evm
@echo "Done building."

View File

@ -1,4 +1,4 @@
## Ethereum Go
## Go Ethereum
Official golang implementation of the Ethereum protocol.
@ -32,7 +32,7 @@ The go-ethereum project comes with several wrappers/executables found in the `cm
| Command | Description |
|:----------:|-------------|
| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default) archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) for command line options |
| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default) archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) for command line options. |
| `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) with expanded functionality if the contract bytecode is also available. However it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts) wiki page for details. |
| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. |
| `disasm` | Bytecode disassembler to convert EVM (Ethereum Virtual Machine) bytecode into more user friendly assembly-like opcodes (e.g. `echo "6001" | disasm`). For details on the individual opcodes, please see pages 22-30 of the [Ethereum Yellow Paper](http://gavwood.com/paper.pdf). |
@ -70,7 +70,7 @@ This command will:
(via the trailing `console` subcommand) through which you can invoke all official [`web3` methods](https://github.com/ethereum/wiki/wiki/JavaScript-API)
as well as Geth's own [management APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs).
This too is optional and if you leave it out you can always attach to an already running Geth instance
with `geth --attach`.
with `geth attach`.
### Full node on the Ethereum test network
@ -84,21 +84,39 @@ $ geth --testnet --fast --cache=512 console
```
The `--fast`, `--cache` flags and `console` subcommand have the exact same meaning as above and they
are equially useful on the testnet too. Please see above for their explanations if you've skipped to
are equally useful on the testnet too. Please see above for their explanations if you've skipped to
here.
Specifying the `--testnet` flag however will reconfigure your Geth instance a bit:
* Instead of using the default data directory (`~/.ethereum` on Linux for example), Geth will nest
itself one level deeper into a `testnet` subfolder (`~/.ethereum/testnet` on Linux).
itself one level deeper into a `testnet` subfolder (`~/.ethereum/testnet` on Linux). Note, on OSX
and Linux this also means that attaching to a running testnet node requires the use of a custom
endpoint since `geth attach` will try to attach to a production node endpoint by default. E.g.
`geth attach <datadir>/testnet/geth.ipc`. Windows users are not affected by this.
* Instead of connecting the main Ethereum network, the client will connect to the test network,
which uses different P2P bootnodes, different network IDs and genesis states.
*Note: Although there are some internal protective measures to prevent transactions from crossing
over between the main network and test network (different starting nonces), you should make sure to
always use separate accounts for play-money and real-money. Unless you manually move accounts, Geth
will by default correctly separate the two networks and will not make any accounts available between
them.*
over between the main network and test network, you should make sure to always use separate accounts
for play-money and real-money. Unless you manually move accounts, Geth will by default correctly
separate the two networks and will not make any accounts available between them.*
### Configuration
As an alternative to passing the numerous flags to the `geth` binary, you can also pass a configuration file via:
```
$ geth --config /path/to/your_config.toml
```
To get an idea how the file should look like you can use the `dumpconfig` subcommand to export your existing configuration:
```
$ geth --your-favourite-flags dumpconfig
```
*Note: This works only with geth v1.6.0 and above*
#### Docker quick start
@ -161,6 +179,12 @@ and agree upon. This consists of a small JSON file (e.g. call it `genesis.json`)
```json
{
"config": {
"chainId": 0,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"alloc" : {},
"coinbase" : "0x0000000000000000000000000000000000000000",
"difficulty" : "0x20000",

View File

@ -1 +1 @@
1.6.0
1.6.7

View File

@ -17,11 +17,9 @@
package abi
import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
"math/big"
"reflect"
"strings"
@ -67,7 +65,7 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
}
method = m
}
arguments, err := method.pack(method, args...)
arguments, err := method.pack(args...)
if err != nil {
return nil, err
}
@ -78,199 +76,6 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
return append(method.Id(), arguments...), nil
}
// toGoSliceType parses the input and casts it to the proper slice defined by the ABI
// argument in T.
func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
index := i * 32
// The slice must, at very least be large enough for the index+32 which is exactly the size required
// for the [offset in output, size of offset].
if index+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), index+32)
}
elem := t.Type.Elem
// first we need to create a slice of the type
var refSlice reflect.Value
switch elem.T {
case IntTy, UintTy, BoolTy:
// create a new reference slice matching the element type
switch t.Type.Kind {
case reflect.Bool:
refSlice = reflect.ValueOf([]bool(nil))
case reflect.Uint8:
refSlice = reflect.ValueOf([]uint8(nil))
case reflect.Uint16:
refSlice = reflect.ValueOf([]uint16(nil))
case reflect.Uint32:
refSlice = reflect.ValueOf([]uint32(nil))
case reflect.Uint64:
refSlice = reflect.ValueOf([]uint64(nil))
case reflect.Int8:
refSlice = reflect.ValueOf([]int8(nil))
case reflect.Int16:
refSlice = reflect.ValueOf([]int16(nil))
case reflect.Int32:
refSlice = reflect.ValueOf([]int32(nil))
case reflect.Int64:
refSlice = reflect.ValueOf([]int64(nil))
default:
refSlice = reflect.ValueOf([]*big.Int(nil))
}
case AddressTy: // address must be of slice Address
refSlice = reflect.ValueOf([]common.Address(nil))
case HashTy: // hash must be of slice hash
refSlice = reflect.ValueOf([]common.Hash(nil))
case FixedBytesTy:
refSlice = reflect.ValueOf([][]byte(nil))
default: // no other types are supported
return nil, fmt.Errorf("abi: unsupported slice type %v", elem.T)
}
var slice []byte
var size int
var offset int
if t.Type.IsSlice {
// get the offset which determines the start of this array ...
offset = int(binary.BigEndian.Uint64(output[index+24 : index+32]))
if offset+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
}
slice = output[offset:]
// ... starting with the size of the array in elements ...
size = int(binary.BigEndian.Uint64(slice[24:32]))
slice = slice[32:]
// ... and make sure that we've at the very least the amount of bytes
// available in the buffer.
if size*32 > len(slice) {
return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32)
}
// reslice to match the required size
slice = slice[:size*32]
} else if t.Type.IsArray {
//get the number of elements in the array
size = t.Type.SliceSize
//check to make sure array size matches up
if index+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), index+32*size)
}
//slice is there for a fixed amount of times
slice = output[index : index+size*32]
}
for i := 0; i < size; i++ {
var (
inter interface{} // interface type
returnOutput = slice[i*32 : i*32+32] // the return output
)
// set inter to the correct type (cast)
switch elem.T {
case IntTy, UintTy:
inter = readInteger(t.Type.Kind, returnOutput)
case BoolTy:
inter = !allZero(returnOutput)
case AddressTy:
inter = common.BytesToAddress(returnOutput)
case HashTy:
inter = common.BytesToHash(returnOutput)
case FixedBytesTy:
inter = returnOutput
}
// append the item to our reflect slice
refSlice = reflect.Append(refSlice, reflect.ValueOf(inter))
}
// return the interface
return refSlice.Interface(), nil
}
func readInteger(kind reflect.Kind, b []byte) interface{} {
switch kind {
case reflect.Uint8:
return uint8(b[len(b)-1])
case reflect.Uint16:
return binary.BigEndian.Uint16(b[len(b)-2:])
case reflect.Uint32:
return binary.BigEndian.Uint32(b[len(b)-4:])
case reflect.Uint64:
return binary.BigEndian.Uint64(b[len(b)-8:])
case reflect.Int8:
return int8(b[len(b)-1])
case reflect.Int16:
return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
case reflect.Int32:
return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
case reflect.Int64:
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
default:
return new(big.Int).SetBytes(b)
}
}
func allZero(b []byte) bool {
for _, byte := range b {
if byte != 0 {
return false
}
}
return true
}
// toGoType parses the input and casts it to the proper type defined by the ABI
// argument in T.
func toGoType(i int, t Argument, output []byte) (interface{}, error) {
// we need to treat slices differently
if (t.Type.IsSlice || t.Type.IsArray) && t.Type.T != BytesTy && t.Type.T != StringTy && t.Type.T != FixedBytesTy && t.Type.T != FunctionTy {
return toGoSlice(i, t, output)
}
index := i * 32
if index+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
}
// Parse the given index output and check whether we need to read
// a different offset and length based on the type (i.e. string, bytes)
var returnOutput []byte
switch t.Type.T {
case StringTy, BytesTy: // variable arrays are written at the end of the return bytes
// parse offset from which we should start reading
offset := int(binary.BigEndian.Uint64(output[index+24 : index+32]))
if offset+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32)
}
// parse the size up until we should be reading
size := int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
if offset+32+size > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+size)
}
// get the bytes for this return value
returnOutput = output[offset+32 : offset+32+size]
default:
returnOutput = output[index : index+32]
}
// convert the bytes to whatever is specified by the ABI.
switch t.Type.T {
case IntTy, UintTy:
return readInteger(t.Type.Kind, returnOutput), nil
case BoolTy:
return !allZero(returnOutput), nil
case AddressTy:
return common.BytesToAddress(returnOutput), nil
case HashTy:
return common.BytesToHash(returnOutput), nil
case BytesTy, FixedBytesTy, FunctionTy:
return returnOutput, nil
case StringTy:
return string(returnOutput), nil
}
return nil, fmt.Errorf("abi: unknown type %v", t.Type.T)
}
// these variable are used to determine certain types during type assertion for
// assignment.
var (

View File

@ -48,412 +48,6 @@ func pad(input []byte, size int, left bool) []byte {
return common.RightPadBytes(input, size)
}
func TestTypeCheck(t *testing.T) {
for i, test := range []struct {
typ string
input interface{}
err string
}{
{"uint", big.NewInt(1), ""},
{"int", big.NewInt(1), ""},
{"uint30", big.NewInt(1), ""},
{"uint30", uint8(1), "abi: cannot use uint8 as type ptr as argument"},
{"uint16", uint16(1), ""},
{"uint16", uint8(1), "abi: cannot use uint8 as type uint16 as argument"},
{"uint16[]", []uint16{1, 2, 3}, ""},
{"uint16[]", [3]uint16{1, 2, 3}, ""},
{"uint16[]", []uint32{1, 2, 3}, "abi: cannot use []uint32 as type []uint16 as argument"},
{"uint16[3]", [3]uint32{1, 2, 3}, "abi: cannot use [3]uint32 as type [3]uint16 as argument"},
{"uint16[3]", [4]uint16{1, 2, 3}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
{"uint16[3]", []uint16{1, 2, 3}, ""},
{"uint16[3]", []uint16{1, 2, 3, 4}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
{"address[]", []common.Address{{1}}, ""},
{"address[1]", []common.Address{{1}}, ""},
{"address[1]", [1]common.Address{{1}}, ""},
{"address[2]", [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"},
{"bytes32", [32]byte{}, ""},
{"bytes32", [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"},
{"bytes32", common.Hash{1}, ""},
{"bytes31", [31]byte{}, ""},
{"bytes31", [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"},
{"bytes", []byte{0, 1}, ""},
{"bytes", [2]byte{0, 1}, ""},
{"bytes", common.Hash{1}, ""},
{"string", "hello world", ""},
{"bytes32[]", [][32]byte{{}}, ""},
{"function", [24]byte{}, ""},
} {
typ, err := NewType(test.typ)
if err != nil {
t.Fatal("unexpected parse error:", err)
}
err = typeCheck(typ, reflect.ValueOf(test.input))
if err != nil && len(test.err) == 0 {
t.Errorf("%d failed. Expected no err but got: %v", i, err)
continue
}
if err == nil && len(test.err) != 0 {
t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
continue
}
if err != nil && len(test.err) != 0 && err.Error() != test.err {
t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
}
}
}
func TestSimpleMethodUnpack(t *testing.T) {
for i, test := range []struct {
def string // definition of the **output** ABI params
marshalledOutput []byte // evm return data
expectedOut interface{} // the expected output
outVar string // the output variable (e.g. uint32, *big.Int, etc)
err string // empty or error if expected
}{
{
`[ { "type": "uint32" } ]`,
pad([]byte{1}, 32, true),
uint32(1),
"uint32",
"",
},
{
`[ { "type": "uint32" } ]`,
pad([]byte{1}, 32, true),
nil,
"uint16",
"abi: cannot unmarshal uint32 in to uint16",
},
{
`[ { "type": "uint17" } ]`,
pad([]byte{1}, 32, true),
nil,
"uint16",
"abi: cannot unmarshal *big.Int in to uint16",
},
{
`[ { "type": "uint17" } ]`,
pad([]byte{1}, 32, true),
big.NewInt(1),
"*big.Int",
"",
},
{
`[ { "type": "int32" } ]`,
pad([]byte{1}, 32, true),
int32(1),
"int32",
"",
},
{
`[ { "type": "int32" } ]`,
pad([]byte{1}, 32, true),
nil,
"int16",
"abi: cannot unmarshal int32 in to int16",
},
{
`[ { "type": "int17" } ]`,
pad([]byte{1}, 32, true),
nil,
"int16",
"abi: cannot unmarshal *big.Int in to int16",
},
{
`[ { "type": "int17" } ]`,
pad([]byte{1}, 32, true),
big.NewInt(1),
"*big.Int",
"",
},
{
`[ { "type": "address" } ]`,
pad(pad([]byte{1}, 20, false), 32, true),
common.Address{1},
"address",
"",
},
{
`[ { "type": "bytes32" } ]`,
pad([]byte{1}, 32, false),
pad([]byte{1}, 32, false),
"bytes",
"",
},
{
`[ { "type": "bytes32" } ]`,
pad([]byte{1}, 32, false),
pad([]byte{1}, 32, false),
"hash",
"",
},
{
`[ { "type": "bytes32" } ]`,
pad([]byte{1}, 32, false),
pad([]byte{1}, 32, false),
"interface",
"",
},
{
`[ { "type": "function" } ]`,
pad([]byte{1}, 32, false),
[24]byte{1},
"function",
"",
},
} {
abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
abi, err := JSON(strings.NewReader(abiDefinition))
if err != nil {
t.Errorf("%d failed. %v", i, err)
continue
}
var outvar interface{}
switch test.outVar {
case "uint8":
var v uint8
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "uint16":
var v uint16
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "uint32":
var v uint32
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "uint64":
var v uint64
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "int8":
var v int8
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "int16":
var v int16
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "int32":
var v int32
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "int64":
var v int64
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "*big.Int":
var v *big.Int
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "address":
var v common.Address
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "bytes":
var v []byte
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "hash":
var v common.Hash
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "function":
var v [24]byte
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "interface":
err = abi.Unpack(&outvar, "method", test.marshalledOutput)
default:
t.Errorf("unsupported type '%v' please add it to the switch statement in this test", test.outVar)
continue
}
if err != nil && len(test.err) == 0 {
t.Errorf("%d failed. Expected no err but got: %v", i, err)
continue
}
if err == nil && len(test.err) != 0 {
t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
continue
}
if err != nil && len(test.err) != 0 && err.Error() != test.err {
t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
continue
}
if err == nil {
// bit of an ugly hack for hash type but I don't feel like finding a proper solution
if test.outVar == "hash" {
tmp := outvar.(common.Hash) // without assignment it's unaddressable
outvar = tmp[:]
}
if !reflect.DeepEqual(test.expectedOut, outvar) {
t.Errorf("%d failed. Output error: expected %v, got %v", i, test.expectedOut, outvar)
}
}
}
}
func TestUnpackSetInterfaceSlice(t *testing.T) {
var (
var1 = new(uint8)
var2 = new(uint8)
)
out := []interface{}{var1, var2}
abi, err := JSON(strings.NewReader(`[{"type":"function", "name":"ints", "outputs":[{"type":"uint8"}, {"type":"uint8"}]}]`))
if err != nil {
t.Fatal(err)
}
marshalledReturn := append(pad([]byte{1}, 32, true), pad([]byte{2}, 32, true)...)
err = abi.Unpack(&out, "ints", marshalledReturn)
if err != nil {
t.Fatal(err)
}
if *var1 != 1 {
t.Error("expected var1 to be 1, got", *var1)
}
if *var2 != 2 {
t.Error("expected var2 to be 2, got", *var2)
}
out = []interface{}{var1}
err = abi.Unpack(&out, "ints", marshalledReturn)
expErr := "abi: cannot marshal in to slices of unequal size (require: 2, got: 1)"
if err == nil || err.Error() != expErr {
t.Error("expected err:", expErr, "Got:", err)
}
}
func TestUnpackSetInterfaceArrayOutput(t *testing.T) {
var (
var1 = new([1]uint32)
var2 = new([1]uint32)
)
out := []interface{}{var1, var2}
abi, err := JSON(strings.NewReader(`[{"type":"function", "name":"ints", "outputs":[{"type":"uint32[1]"}, {"type":"uint32[1]"}]}]`))
if err != nil {
t.Fatal(err)
}
marshalledReturn := append(pad([]byte{1}, 32, true), pad([]byte{2}, 32, true)...)
err = abi.Unpack(&out, "ints", marshalledReturn)
if err != nil {
t.Fatal(err)
}
if *var1 != [1]uint32{1} {
t.Error("expected var1 to be [1], got", *var1)
}
if *var2 != [1]uint32{2} {
t.Error("expected var2 to be [2], got", *var2)
}
}
func TestPack(t *testing.T) {
for i, test := range []struct {
typ string
input interface{}
output []byte
}{
{"uint16", uint16(2), pad([]byte{2}, 32, true)},
{"uint16[]", []uint16{1, 2}, formatSliceOutput([]byte{1}, []byte{2})},
{"bytes20", [20]byte{1}, pad([]byte{1}, 32, false)},
{"uint256[]", []*big.Int{big.NewInt(1), big.NewInt(2)}, formatSliceOutput([]byte{1}, []byte{2})},
{"address[]", []common.Address{{1}, {2}}, formatSliceOutput(pad([]byte{1}, 20, false), pad([]byte{2}, 20, false))},
{"bytes32[]", []common.Hash{{1}, {2}}, formatSliceOutput(pad([]byte{1}, 32, false), pad([]byte{2}, 32, false))},
{"function", [24]byte{1}, pad([]byte{1}, 32, false)},
} {
typ, err := NewType(test.typ)
if err != nil {
t.Fatal("unexpected parse error:", err)
}
output, err := typ.pack(reflect.ValueOf(test.input))
if err != nil {
t.Fatal("unexpected pack error:", err)
}
if !bytes.Equal(output, test.output) {
t.Errorf("%d failed. Expected bytes: '%x' Got: '%x'", i, test.output, output)
}
}
}
func TestMethodPack(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Fatal(err)
}
sig := abi.Methods["slice"].Id()
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
packed, err := abi.Pack("slice", []uint32{1, 2})
if err != nil {
t.Error(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
var addrA, addrB = common.Address{1}, common.Address{2}
sig = abi.Methods["sliceAddress"].Id()
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrB[:], 32)...)
packed, err = abi.Pack("sliceAddress", []common.Address{addrA, addrB})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
var addrC, addrD = common.Address{3}, common.Address{4}
sig = abi.Methods["sliceMultiAddress"].Id()
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrB[:], 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrC[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrD[:], 32)...)
packed, err = abi.Pack("sliceMultiAddress", []common.Address{addrA, addrB}, []common.Address{addrC, addrD})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
sig = abi.Methods["slice256"].Id()
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
packed, err = abi.Pack("slice256", []*big.Int{big.NewInt(1), big.NewInt(2)})
if err != nil {
t.Error(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
}
const jsondata = `
[
{ "type" : "function", "name" : "balance", "constant" : true },
@ -843,399 +437,3 @@ func TestBareEvents(t *testing.T) {
}
}
}
func TestMultiReturnWithStruct(t *testing.T) {
const definition = `[
{ "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
// using buff to make the code readable
buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
stringOut := "hello"
buff.Write(common.RightPadBytes([]byte(stringOut), 32))
var inter struct {
Int *big.Int
String string
}
err = abi.Unpack(&inter, "multi", buff.Bytes())
if err != nil {
t.Error(err)
}
if inter.Int == nil || inter.Int.Cmp(big.NewInt(1)) != 0 {
t.Error("expected Int to be 1 got", inter.Int)
}
if inter.String != stringOut {
t.Error("expected String to be", stringOut, "got", inter.String)
}
var reversed struct {
String string
Int *big.Int
}
err = abi.Unpack(&reversed, "multi", buff.Bytes())
if err != nil {
t.Error(err)
}
if reversed.Int == nil || reversed.Int.Cmp(big.NewInt(1)) != 0 {
t.Error("expected Int to be 1 got", reversed.Int)
}
if reversed.String != stringOut {
t.Error("expected String to be", stringOut, "got", reversed.String)
}
}
func TestMultiReturnWithSlice(t *testing.T) {
const definition = `[
{ "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
// using buff to make the code readable
buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
stringOut := "hello"
buff.Write(common.RightPadBytes([]byte(stringOut), 32))
var inter []interface{}
err = abi.Unpack(&inter, "multi", buff.Bytes())
if err != nil {
t.Error(err)
}
if len(inter) != 2 {
t.Fatal("expected 2 results got", len(inter))
}
if num, ok := inter[0].(*big.Int); !ok || num.Cmp(big.NewInt(1)) != 0 {
t.Error("expected index 0 to be 1 got", num)
}
if str, ok := inter[1].(string); !ok || str != stringOut {
t.Error("expected index 1 to be", stringOut, "got", str)
}
}
func TestMarshalArrays(t *testing.T) {
const definition = `[
{ "name" : "bytes32", "constant" : false, "outputs": [ { "type": "bytes32" } ] },
{ "name" : "bytes10", "constant" : false, "outputs": [ { "type": "bytes10" } ] }
]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
output := common.LeftPadBytes([]byte{1}, 32)
var bytes10 [10]byte
err = abi.Unpack(&bytes10, "bytes32", output)
if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" {
t.Error("expected error or bytes32 not be assignable to bytes10:", err)
}
var bytes32 [32]byte
err = abi.Unpack(&bytes32, "bytes32", output)
if err != nil {
t.Error("didn't expect error:", err)
}
if !bytes.Equal(bytes32[:], output) {
t.Error("expected bytes32[31] to be 1 got", bytes32[31])
}
type (
B10 [10]byte
B32 [32]byte
)
var b10 B10
err = abi.Unpack(&b10, "bytes32", output)
if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" {
t.Error("expected error or bytes32 not be assignable to bytes10:", err)
}
var b32 B32
err = abi.Unpack(&b32, "bytes32", output)
if err != nil {
t.Error("didn't expect error:", err)
}
if !bytes.Equal(b32[:], output) {
t.Error("expected bytes32[31] to be 1 got", bytes32[31])
}
output[10] = 1
var shortAssignLong [32]byte
err = abi.Unpack(&shortAssignLong, "bytes10", output)
if err != nil {
t.Error("didn't expect error:", err)
}
if !bytes.Equal(output, shortAssignLong[:]) {
t.Errorf("expected %x to be %x", shortAssignLong, output)
}
}
func TestUnmarshal(t *testing.T) {
const definition = `[
{ "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] },
{ "name" : "bool", "constant" : false, "outputs": [ { "type": "bool" } ] },
{ "name" : "bytes", "constant" : false, "outputs": [ { "type": "bytes" } ] },
{ "name" : "fixed", "constant" : false, "outputs": [ { "type": "bytes32" } ] },
{ "name" : "multi", "constant" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
{ "name" : "intArraySingle", "constant" : false, "outputs": [ { "type": "uint256[3]" } ] },
{ "name" : "addressSliceSingle", "constant" : false, "outputs": [ { "type": "address[]" } ] },
{ "name" : "addressSliceDouble", "constant" : false, "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] },
{ "name" : "mixedBytes", "constant" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
buff := new(bytes.Buffer)
// marshal int
var Int *big.Int
err = abi.Unpack(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
if err != nil {
t.Error(err)
}
if Int == nil || Int.Cmp(big.NewInt(1)) != 0 {
t.Error("expected Int to be 1 got", Int)
}
// marshal bool
var Bool bool
err = abi.Unpack(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
if err != nil {
t.Error(err)
}
if !Bool {
t.Error("expected Bool to be true")
}
// marshal dynamic bytes max length 32
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
bytesOut := common.RightPadBytes([]byte("hello"), 32)
buff.Write(bytesOut)
var Bytes []byte
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
if err != nil {
t.Error(err)
}
if !bytes.Equal(Bytes, bytesOut) {
t.Errorf("expected %x got %x", bytesOut, Bytes)
}
// marshall dynamic bytes max length 64
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
bytesOut = common.RightPadBytes([]byte("hello"), 64)
buff.Write(bytesOut)
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
if err != nil {
t.Error(err)
}
if !bytes.Equal(Bytes, bytesOut) {
t.Errorf("expected %x got %x", bytesOut, Bytes)
}
// marshall dynamic bytes max length 63
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000003f"))
bytesOut = common.RightPadBytes([]byte("hello"), 63)
buff.Write(bytesOut)
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
if err != nil {
t.Error(err)
}
if !bytes.Equal(Bytes, bytesOut) {
t.Errorf("expected %x got %x", bytesOut, Bytes)
}
// marshal dynamic bytes output empty
err = abi.Unpack(&Bytes, "bytes", nil)
if err == nil {
t.Error("expected error")
}
// marshal dynamic bytes length 5
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
buff.Write(common.RightPadBytes([]byte("hello"), 32))
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
if err != nil {
t.Error(err)
}
if !bytes.Equal(Bytes, []byte("hello")) {
t.Errorf("expected %x got %x", bytesOut, Bytes)
}
// marshal dynamic bytes length 5
buff.Reset()
buff.Write(common.RightPadBytes([]byte("hello"), 32))
var hash common.Hash
err = abi.Unpack(&hash, "fixed", buff.Bytes())
if err != nil {
t.Error(err)
}
helloHash := common.BytesToHash(common.RightPadBytes([]byte("hello"), 32))
if hash != helloHash {
t.Errorf("Expected %x to equal %x", hash, helloHash)
}
// marshal error
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
if err == nil {
t.Error("expected error")
}
err = abi.Unpack(&Bytes, "multi", make([]byte, 64))
if err == nil {
t.Error("expected error")
}
// marshal mixed bytes
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
fixed := common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")
buff.Write(fixed)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
bytesOut = common.RightPadBytes([]byte("hello"), 32)
buff.Write(bytesOut)
var out []interface{}
err = abi.Unpack(&out, "mixedBytes", buff.Bytes())
if err != nil {
t.Fatal("didn't expect error:", err)
}
if !bytes.Equal(bytesOut, out[0].([]byte)) {
t.Errorf("expected %x, got %x", bytesOut, out[0])
}
if !bytes.Equal(fixed, out[1].([]byte)) {
t.Errorf("expected %x, got %x", fixed, out[1])
}
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
// marshal int array
var intArray [3]*big.Int
err = abi.Unpack(&intArray, "intArraySingle", buff.Bytes())
if err != nil {
t.Error(err)
}
var testAgainstIntArray [3]*big.Int
testAgainstIntArray[0] = big.NewInt(1)
testAgainstIntArray[1] = big.NewInt(2)
testAgainstIntArray[2] = big.NewInt(3)
for i, Int := range intArray {
if Int.Cmp(testAgainstIntArray[i]) != 0 {
t.Errorf("expected %v, got %v", testAgainstIntArray[i], Int)
}
}
// marshal address slice
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) // offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // size
buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
var outAddr []common.Address
err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes())
if err != nil {
t.Fatal("didn't expect error:", err)
}
if len(outAddr) != 1 {
t.Fatal("expected 1 item, got", len(outAddr))
}
if outAddr[0] != (common.Address{1}) {
t.Errorf("expected %x, got %x", common.Address{1}, outAddr[0])
}
// marshal multiple address slice
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // size
buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // size
buff.Write(common.Hex2Bytes("0000000000000000000000000200000000000000000000000000000000000000"))
buff.Write(common.Hex2Bytes("0000000000000000000000000300000000000000000000000000000000000000"))
var outAddrStruct struct {
A []common.Address
B []common.Address
}
err = abi.Unpack(&outAddrStruct, "addressSliceDouble", buff.Bytes())
if err != nil {
t.Fatal("didn't expect error:", err)
}
if len(outAddrStruct.A) != 1 {
t.Fatal("expected 1 item, got", len(outAddrStruct.A))
}
if outAddrStruct.A[0] != (common.Address{1}) {
t.Errorf("expected %x, got %x", common.Address{1}, outAddrStruct.A[0])
}
if len(outAddrStruct.B) != 2 {
t.Fatal("expected 1 item, got", len(outAddrStruct.B))
}
if outAddrStruct.B[0] != (common.Address{2}) {
t.Errorf("expected %x, got %x", common.Address{2}, outAddrStruct.B[0])
}
if outAddrStruct.B[1] != (common.Address{3}) {
t.Errorf("expected %x, got %x", common.Address{3}, outAddrStruct.B[1])
}
// marshal invalid address slice
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000100"))
err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes())
if err == nil {
t.Fatal("expected error:", err)
}
}

View File

@ -90,7 +90,7 @@ func (b *SimulatedBackend) Rollback() {
func (b *SimulatedBackend) rollback() {
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
b.pendingState, _ = state.New(b.pendingBlock.Root(), state.NewDatabase(b.database))
}
// CodeAt returns the code associated with a certain account in the blockchain.
@ -279,7 +279,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
block.AddTx(tx)
})
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
b.pendingState, _ = state.New(b.pendingBlock.Root(), state.NewDatabase(b.database))
return nil
}

View File

@ -17,10 +17,15 @@
package abi
import (
"errors"
"fmt"
"reflect"
)
var (
errBadBool = errors.New("abi: improperly encoded boolean value")
)
// formatSliceString formats the reflection kind with the given slice size
// and returns a formatted string representation.
func formatSliceString(kind reflect.Kind, sliceSize int) string {

View File

@ -39,7 +39,7 @@ type Method struct {
Outputs []Argument
}
func (m Method) pack(method Method, args ...interface{}) ([]byte, error) {
func (method Method) pack(args ...interface{}) ([]byte, error) {
// Make sure arguments match up and pack them
if len(args) != len(method.Inputs) {
return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Inputs))

View File

@ -62,19 +62,6 @@ func U256(n *big.Int) []byte {
return math.PaddedBigBytes(math.U256(n), 32)
}
// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
func packNum(value reflect.Value) []byte {
switch kind := value.Kind(); kind {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return U256(new(big.Int).SetUint64(value.Uint()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return U256(big.NewInt(value.Int()))
case reflect.Ptr:
return U256(value.Interface().(*big.Int))
}
return nil
}
// 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() {

View File

@ -18,7 +18,6 @@ package abi
import (
"bytes"
"math"
"math/big"
"reflect"
"testing"
@ -34,43 +33,6 @@ func TestNumberTypes(t *testing.T) {
}
}
func TestPackNumber(t *testing.T) {
tests := []struct {
value reflect.Value
packed []byte
}{
// Protocol limits
{reflect.ValueOf(0), []byte{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, 0}},
{reflect.ValueOf(1), []byte{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, 1}},
{reflect.ValueOf(-1), []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}},
// Type corner cases
{reflect.ValueOf(uint8(math.MaxUint8)), []byte{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, 255}},
{reflect.ValueOf(uint16(math.MaxUint16)), []byte{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, 255, 255}},
{reflect.ValueOf(uint32(math.MaxUint32)), []byte{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, 255, 255, 255, 255}},
{reflect.ValueOf(uint64(math.MaxUint64)), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255}},
{reflect.ValueOf(int8(math.MaxInt8)), []byte{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, 127}},
{reflect.ValueOf(int16(math.MaxInt16)), []byte{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, 127, 255}},
{reflect.ValueOf(int32(math.MaxInt32)), []byte{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, 127, 255, 255, 255}},
{reflect.ValueOf(int64(math.MaxInt64)), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 255, 255, 255}},
{reflect.ValueOf(int8(math.MinInt8)), []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128}},
{reflect.ValueOf(int16(math.MinInt16)), []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0}},
{reflect.ValueOf(int32(math.MinInt32)), []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0, 0, 0}},
{reflect.ValueOf(int64(math.MinInt64)), []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 0, 0, 0, 0, 0, 0, 0}},
}
for i, tt := range tests {
packed := packNum(tt.value)
if !bytes.Equal(packed, tt.packed) {
t.Errorf("test %d: pack mismatch: have %x, want %x", i, packed, tt.packed)
}
}
if packed := packNum(reflect.ValueOf("string")); packed != nil {
t.Errorf("expected 'string' to pack to nil. got %x instead", packed)
}
}
func TestSigned(t *testing.T) {
if isSigned(reflect.ValueOf(uint(10))) {
t.Error("signed")

View File

@ -17,6 +17,7 @@
package abi
import (
"math/big"
"reflect"
"github.com/ethereum/go-ethereum/common"
@ -59,8 +60,20 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
if reflectValue.Kind() == reflect.Array {
reflectValue = mustArrayToByteSlice(reflectValue)
}
return common.RightPadBytes(reflectValue.Bytes(), 32)
}
panic("abi: fatal error")
}
// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
func packNum(value reflect.Value) []byte {
switch kind := value.Kind(); kind {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return U256(new(big.Int).SetUint64(value.Uint()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return U256(big.NewInt(value.Int()))
case reflect.Ptr:
return U256(value.Interface().(*big.Int))
}
return nil
}

441
accounts/abi/pack_test.go Normal file
View File

@ -0,0 +1,441 @@
// Copyright 2015 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 abi
import (
"bytes"
"math"
"math/big"
"reflect"
"strings"
"testing"
"github.com/ethereum/go-ethereum/common"
)
func TestPack(t *testing.T) {
for i, test := range []struct {
typ string
input interface{}
output []byte
}{
{
"uint8",
uint8(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint8[]",
[]uint8{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint16",
uint16(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint16[]",
[]uint16{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint32",
uint32(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint32[]",
[]uint32{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint64",
uint64(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint64[]",
[]uint64{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint256",
big.NewInt(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"uint256[]",
[]*big.Int{big.NewInt(1), big.NewInt(2)},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int8",
int8(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int8[]",
[]int8{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int16",
int16(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int16[]",
[]int16{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int32",
int32(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int32[]",
[]int32{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int64",
int64(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int64[]",
[]int64{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int256",
big.NewInt(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
},
{
"int256[]",
[]*big.Int{big.NewInt(1), big.NewInt(2)},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
},
{
"bytes1",
[1]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes2",
[2]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes3",
[3]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes4",
[4]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes5",
[5]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes6",
[6]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes7",
[7]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes8",
[8]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes9",
[9]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes10",
[10]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes11",
[11]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes12",
[12]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes13",
[13]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes14",
[14]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes15",
[15]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes16",
[16]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes17",
[17]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes18",
[18]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes19",
[19]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes20",
[20]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes21",
[21]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes22",
[22]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes23",
[23]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes24",
[24]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes24",
[24]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes25",
[25]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes26",
[26]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes27",
[27]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes28",
[28]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes29",
[29]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes30",
[30]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes31",
[31]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes32",
[32]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"address[]",
[]common.Address{{1}, {2}},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000"),
},
{
"bytes32[]",
[]common.Hash{{1}, {2}},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000201000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000"),
},
{
"function",
[24]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"string",
"foobar",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000006666f6f6261720000000000000000000000000000000000000000000000000000"),
},
} {
typ, err := NewType(test.typ)
if err != nil {
t.Fatal("unexpected parse error:", err)
}
output, err := typ.pack(reflect.ValueOf(test.input))
if err != nil {
t.Fatal("unexpected pack error:", err)
}
if !bytes.Equal(output, test.output) {
t.Errorf("%d failed. Expected bytes: '%x' Got: '%x'", i, test.output, output)
}
}
}
func TestMethodPack(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Fatal(err)
}
sig := abi.Methods["slice"].Id()
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
packed, err := abi.Pack("slice", []uint32{1, 2})
if err != nil {
t.Error(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
var addrA, addrB = common.Address{1}, common.Address{2}
sig = abi.Methods["sliceAddress"].Id()
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrB[:], 32)...)
packed, err = abi.Pack("sliceAddress", []common.Address{addrA, addrB})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
var addrC, addrD = common.Address{3}, common.Address{4}
sig = abi.Methods["sliceMultiAddress"].Id()
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrB[:], 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrC[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrD[:], 32)...)
packed, err = abi.Pack("sliceMultiAddress", []common.Address{addrA, addrB}, []common.Address{addrC, addrD})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
sig = abi.Methods["slice256"].Id()
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
packed, err = abi.Pack("slice256", []*big.Int{big.NewInt(1), big.NewInt(2)})
if err != nil {
t.Error(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
}
func TestPackNumber(t *testing.T) {
tests := []struct {
value reflect.Value
packed []byte
}{
// Protocol limits
{reflect.ValueOf(0), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")},
{reflect.ValueOf(1), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")},
{reflect.ValueOf(-1), common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")},
// Type corner cases
{reflect.ValueOf(uint8(math.MaxUint8)), common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000ff")},
{reflect.ValueOf(uint16(math.MaxUint16)), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000ffff")},
{reflect.ValueOf(uint32(math.MaxUint32)), common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000ffffffff")},
{reflect.ValueOf(uint64(math.MaxUint64)), common.Hex2Bytes("000000000000000000000000000000000000000000000000ffffffffffffffff")},
{reflect.ValueOf(int8(math.MaxInt8)), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000007f")},
{reflect.ValueOf(int16(math.MaxInt16)), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000007fff")},
{reflect.ValueOf(int32(math.MaxInt32)), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000007fffffff")},
{reflect.ValueOf(int64(math.MaxInt64)), common.Hex2Bytes("0000000000000000000000000000000000000000000000007fffffffffffffff")},
{reflect.ValueOf(int8(math.MinInt8)), common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80")},
{reflect.ValueOf(int16(math.MinInt16)), common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000")},
{reflect.ValueOf(int32(math.MinInt32)), common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000")},
{reflect.ValueOf(int64(math.MinInt64)), common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffff8000000000000000")},
}
for i, tt := range tests {
packed := packNum(tt.value)
if !bytes.Equal(packed, tt.packed) {
t.Errorf("test %d: pack mismatch: have %x, want %x", i, packed, tt.packed)
}
}
if packed := packNum(reflect.ValueOf("string")); packed != nil {
t.Errorf("expected 'string' to pack to nil. got %x instead", packed)
}
}

View File

@ -32,30 +32,30 @@ func indirect(v reflect.Value) reflect.Value {
// reflectIntKind returns the reflect using the given size and
// unsignedness.
func reflectIntKind(unsigned bool, size int) reflect.Kind {
func reflectIntKindAndType(unsigned bool, size int) (reflect.Kind, reflect.Type) {
switch size {
case 8:
if unsigned {
return reflect.Uint8
return reflect.Uint8, uint8_t
}
return reflect.Int8
return reflect.Int8, int8_t
case 16:
if unsigned {
return reflect.Uint16
return reflect.Uint16, uint16_t
}
return reflect.Int16
return reflect.Int16, int16_t
case 32:
if unsigned {
return reflect.Uint32
return reflect.Uint32, uint32_t
}
return reflect.Int32
return reflect.Int32, int32_t
case 64:
if unsigned {
return reflect.Uint64
return reflect.Uint64, uint64_t
}
return reflect.Int64
return reflect.Int64, int64_t
}
return reflect.Ptr
return reflect.Ptr, big_t
}
// mustArrayToBytesSlice creates a new byte slice with the exact same size as value

View File

@ -33,7 +33,7 @@ const (
FixedBytesTy
BytesTy
HashTy
FixedpointTy
FixedPointTy
FunctionTy
)
@ -126,13 +126,11 @@ func NewType(t string) (typ Type, err error) {
switch varType {
case "int":
typ.Kind = reflectIntKind(false, varSize)
typ.Type = big_t
typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
typ.Size = varSize
typ.T = IntTy
case "uint":
typ.Kind = reflectIntKind(true, varSize)
typ.Type = ubig_t
typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
typ.Size = varSize
typ.T = UintTy
case "bool":

View File

@ -17,8 +17,11 @@
package abi
import (
"math/big"
"reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
)
// typeWithoutStringer is a alias for the Type type which simply doesn't implement
@ -31,26 +34,44 @@ func TestTypeRegexp(t *testing.T) {
blob string
kind Type
}{
{"int", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}},
{"int8", Type{Kind: reflect.Int8, Type: big_t, Size: 8, T: IntTy, stringKind: "int8"}},
{"bool", Type{Kind: reflect.Bool, T: BoolTy, stringKind: "bool"}},
{"bool[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Bool, T: BoolTy, Elem: &Type{Kind: reflect.Bool, T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}},
{"bool[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Bool, T: BoolTy, Elem: &Type{Kind: reflect.Bool, T: BoolTy, stringKind: "bool"}, 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"}},
{"int[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
{"int[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
{"int32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
{"int32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
{"uint", Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}},
{"uint8", Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}},
{"uint256", Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}},
{"uint[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
{"uint[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
{"uint32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint32, Type: ubig_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: big_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
{"uint32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint32, Type: ubig_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: big_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
{"bytes", Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}},
{"bytes32", Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}},
{"bytes[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
{"bytes[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[2]"}},
{"bytes32[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
{"bytes32[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
{"int8[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
{"int8[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
{"int16[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
{"int16[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
{"int32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
{"int32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
{"int64[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
{"int64[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
{"int256[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
{"int256[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, 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{IsSlice: true, SliceSize: -1, Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
{"uint8[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
{"uint16[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
{"uint16[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
{"uint32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
{"uint32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
{"uint64[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
{"uint64[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
{"uint256[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
{"uint256[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
{"bytes32", Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}},
{"bytes[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
{"bytes[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[2]"}},
{"bytes32[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
{"bytes32[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
{"string", Type{Kind: reflect.String, Size: -1, T: StringTy, stringKind: "string"}},
{"string[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[]"}},
{"string[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[2]"}},
@ -76,3 +97,59 @@ func TestTypeRegexp(t *testing.T) {
}
}
}
func TestTypeCheck(t *testing.T) {
for i, test := range []struct {
typ string
input interface{}
err string
}{
{"uint", big.NewInt(1), ""},
{"int", big.NewInt(1), ""},
{"uint30", big.NewInt(1), ""},
{"uint30", uint8(1), "abi: cannot use uint8 as type ptr as argument"},
{"uint16", uint16(1), ""},
{"uint16", uint8(1), "abi: cannot use uint8 as type uint16 as argument"},
{"uint16[]", []uint16{1, 2, 3}, ""},
{"uint16[]", [3]uint16{1, 2, 3}, ""},
{"uint16[]", []uint32{1, 2, 3}, "abi: cannot use []uint32 as type []uint16 as argument"},
{"uint16[3]", [3]uint32{1, 2, 3}, "abi: cannot use [3]uint32 as type [3]uint16 as argument"},
{"uint16[3]", [4]uint16{1, 2, 3}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
{"uint16[3]", []uint16{1, 2, 3}, ""},
{"uint16[3]", []uint16{1, 2, 3, 4}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
{"address[]", []common.Address{{1}}, ""},
{"address[1]", []common.Address{{1}}, ""},
{"address[1]", [1]common.Address{{1}}, ""},
{"address[2]", [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"},
{"bytes32", [32]byte{}, ""},
{"bytes32", [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"},
{"bytes32", common.Hash{1}, ""},
{"bytes31", [31]byte{}, ""},
{"bytes31", [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"},
{"bytes", []byte{0, 1}, ""},
{"bytes", [2]byte{0, 1}, ""},
{"bytes", common.Hash{1}, ""},
{"string", "hello world", ""},
{"bytes32[]", [][32]byte{{}}, ""},
{"function", [24]byte{}, ""},
} {
typ, err := NewType(test.typ)
if err != nil {
t.Fatal("unexpected parse error:", err)
}
err = typeCheck(typ, reflect.ValueOf(test.input))
if err != nil && len(test.err) == 0 {
t.Errorf("%d failed. Expected no err but got: %v", i, err)
continue
}
if err == nil && len(test.err) != 0 {
t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
continue
}
if err != nil && len(test.err) != 0 && err.Error() != test.err {
t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
}
}
}

235
accounts/abi/unpack.go Normal file
View File

@ -0,0 +1,235 @@
// Copyright 2015 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 abi
import (
"encoding/binary"
"fmt"
"math/big"
"reflect"
"github.com/ethereum/go-ethereum/common"
)
// toGoSliceType parses the input and casts it to the proper slice defined by the ABI
// argument in T.
func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
index := i * 32
// The slice must, at very least be large enough for the index+32 which is exactly the size required
// for the [offset in output, size of offset].
if index+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), index+32)
}
elem := t.Type.Elem
// first we need to create a slice of the type
var refSlice reflect.Value
switch elem.T {
case IntTy, UintTy, BoolTy:
// create a new reference slice matching the element type
switch t.Type.Kind {
case reflect.Bool:
refSlice = reflect.ValueOf([]bool(nil))
case reflect.Uint8:
refSlice = reflect.ValueOf([]uint8(nil))
case reflect.Uint16:
refSlice = reflect.ValueOf([]uint16(nil))
case reflect.Uint32:
refSlice = reflect.ValueOf([]uint32(nil))
case reflect.Uint64:
refSlice = reflect.ValueOf([]uint64(nil))
case reflect.Int8:
refSlice = reflect.ValueOf([]int8(nil))
case reflect.Int16:
refSlice = reflect.ValueOf([]int16(nil))
case reflect.Int32:
refSlice = reflect.ValueOf([]int32(nil))
case reflect.Int64:
refSlice = reflect.ValueOf([]int64(nil))
default:
refSlice = reflect.ValueOf([]*big.Int(nil))
}
case AddressTy: // address must be of slice Address
refSlice = reflect.ValueOf([]common.Address(nil))
case HashTy: // hash must be of slice hash
refSlice = reflect.ValueOf([]common.Hash(nil))
case FixedBytesTy:
refSlice = reflect.ValueOf([][]byte(nil))
default: // no other types are supported
return nil, fmt.Errorf("abi: unsupported slice type %v", elem.T)
}
var slice []byte
var size int
var offset int
if t.Type.IsSlice {
// get the offset which determines the start of this array ...
offset = int(binary.BigEndian.Uint64(output[index+24 : index+32]))
if offset+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
}
slice = output[offset:]
// ... starting with the size of the array in elements ...
size = int(binary.BigEndian.Uint64(slice[24:32]))
slice = slice[32:]
// ... and make sure that we've at the very least the amount of bytes
// available in the buffer.
if size*32 > len(slice) {
return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32)
}
// reslice to match the required size
slice = slice[:size*32]
} else if t.Type.IsArray {
//get the number of elements in the array
size = t.Type.SliceSize
//check to make sure array size matches up
if index+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), index+32*size)
}
//slice is there for a fixed amount of times
slice = output[index : index+size*32]
}
for i := 0; i < size; i++ {
var (
inter interface{} // interface type
returnOutput = slice[i*32 : i*32+32] // the return output
err error
)
// set inter to the correct type (cast)
switch elem.T {
case IntTy, UintTy:
inter = readInteger(t.Type.Kind, returnOutput)
case BoolTy:
inter, err = readBool(returnOutput)
if err != nil {
return nil, err
}
case AddressTy:
inter = common.BytesToAddress(returnOutput)
case HashTy:
inter = common.BytesToHash(returnOutput)
case FixedBytesTy:
inter = returnOutput
}
// append the item to our reflect slice
refSlice = reflect.Append(refSlice, reflect.ValueOf(inter))
}
// return the interface
return refSlice.Interface(), nil
}
func readInteger(kind reflect.Kind, b []byte) interface{} {
switch kind {
case reflect.Uint8:
return uint8(b[len(b)-1])
case reflect.Uint16:
return binary.BigEndian.Uint16(b[len(b)-2:])
case reflect.Uint32:
return binary.BigEndian.Uint32(b[len(b)-4:])
case reflect.Uint64:
return binary.BigEndian.Uint64(b[len(b)-8:])
case reflect.Int8:
return int8(b[len(b)-1])
case reflect.Int16:
return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
case reflect.Int32:
return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
case reflect.Int64:
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
default:
return new(big.Int).SetBytes(b)
}
}
func readBool(word []byte) (bool, error) {
if len(word) != 32 {
return false, fmt.Errorf("abi: fatal error: incorrect word length")
}
for i, b := range word {
if b != 0 && i != 31 {
return false, errBadBool
}
}
switch word[31] {
case 0:
return false, nil
case 1:
return true, nil
default:
return false, errBadBool
}
}
// toGoType parses the input and casts it to the proper type defined by the ABI
// argument in T.
func toGoType(i int, t Argument, output []byte) (interface{}, error) {
// we need to treat slices differently
if (t.Type.IsSlice || t.Type.IsArray) && t.Type.T != BytesTy && t.Type.T != StringTy && t.Type.T != FixedBytesTy && t.Type.T != FunctionTy {
return toGoSlice(i, t, output)
}
index := i * 32
if index+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
}
// Parse the given index output and check whether we need to read
// a different offset and length based on the type (i.e. string, bytes)
var returnOutput []byte
switch t.Type.T {
case StringTy, BytesTy: // variable arrays are written at the end of the return bytes
// parse offset from which we should start reading
offset := int(binary.BigEndian.Uint64(output[index+24 : index+32]))
if offset+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32)
}
// parse the size up until we should be reading
size := int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
if offset+32+size > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+size)
}
// get the bytes for this return value
returnOutput = output[offset+32 : offset+32+size]
default:
returnOutput = output[index : index+32]
}
// convert the bytes to whatever is specified by the ABI.
switch t.Type.T {
case IntTy, UintTy:
return readInteger(t.Type.Kind, returnOutput), nil
case BoolTy:
return readBool(returnOutput)
case AddressTy:
return common.BytesToAddress(returnOutput), nil
case HashTy:
return common.BytesToHash(returnOutput), nil
case BytesTy, FixedBytesTy, FunctionTy:
return returnOutput, nil
case StringTy:
return string(returnOutput), nil
}
return nil, fmt.Errorf("abi: unknown type %v", t.Type.T)
}

681
accounts/abi/unpack_test.go Normal file
View File

@ -0,0 +1,681 @@
// Copyright 2015 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 abi
import (
"bytes"
"fmt"
"math/big"
"reflect"
"strings"
"testing"
"github.com/ethereum/go-ethereum/common"
)
func TestSimpleMethodUnpack(t *testing.T) {
for i, test := range []struct {
def string // definition of the **output** ABI params
marshalledOutput []byte // evm return data
expectedOut interface{} // the expected output
outVar string // the output variable (e.g. uint32, *big.Int, etc)
err string // empty or error if expected
}{
{
`[ { "type": "bool" } ]`,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
bool(true),
"bool",
"",
},
{
`[ { "type": "uint32" } ]`,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
uint32(1),
"uint32",
"",
},
{
`[ { "type": "uint32" } ]`,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
nil,
"uint16",
"abi: cannot unmarshal uint32 in to uint16",
},
{
`[ { "type": "uint17" } ]`,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
nil,
"uint16",
"abi: cannot unmarshal *big.Int in to uint16",
},
{
`[ { "type": "uint17" } ]`,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
big.NewInt(1),
"*big.Int",
"",
},
{
`[ { "type": "int32" } ]`,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
int32(1),
"int32",
"",
},
{
`[ { "type": "int32" } ]`,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
nil,
"int16",
"abi: cannot unmarshal int32 in to int16",
},
{
`[ { "type": "int17" } ]`,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
nil,
"int16",
"abi: cannot unmarshal *big.Int in to int16",
},
{
`[ { "type": "int17" } ]`,
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
big.NewInt(1),
"*big.Int",
"",
},
{
`[ { "type": "address" } ]`,
common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"),
common.Address{1},
"address",
"",
},
{
`[ { "type": "bytes32" } ]`,
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
"bytes",
"",
},
{
`[ { "type": "bytes32" } ]`,
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
"hash",
"",
},
{
`[ { "type": "bytes32" } ]`,
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
"interface",
"",
},
{
`[ { "type": "function" } ]`,
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
[24]byte{1},
"function",
"",
},
} {
abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
abi, err := JSON(strings.NewReader(abiDefinition))
if err != nil {
t.Errorf("%d failed. %v", i, err)
continue
}
var outvar interface{}
switch test.outVar {
case "bool":
var v bool
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "uint8":
var v uint8
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "uint16":
var v uint16
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "uint32":
var v uint32
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "uint64":
var v uint64
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "int8":
var v int8
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "int16":
var v int16
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "int32":
var v int32
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "int64":
var v int64
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "*big.Int":
var v *big.Int
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "address":
var v common.Address
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "bytes":
var v []byte
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "hash":
var v common.Hash
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v.Bytes()[:]
case "function":
var v [24]byte
err = abi.Unpack(&v, "method", test.marshalledOutput)
outvar = v
case "interface":
err = abi.Unpack(&outvar, "method", test.marshalledOutput)
default:
t.Errorf("unsupported type '%v' please add it to the switch statement in this test", test.outVar)
continue
}
if err != nil && len(test.err) == 0 {
t.Errorf("%d failed. Expected no err but got: %v", i, err)
continue
}
if err == nil && len(test.err) != 0 {
t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
continue
}
if err != nil && len(test.err) != 0 && err.Error() != test.err {
t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
continue
}
if err == nil {
if !reflect.DeepEqual(test.expectedOut, outvar) {
t.Errorf("%d failed. Output error: expected %v, got %v", i, test.expectedOut, outvar)
}
}
}
}
func TestUnpackSetInterfaceSlice(t *testing.T) {
var (
var1 = new(uint8)
var2 = new(uint8)
)
out := []interface{}{var1, var2}
abi, err := JSON(strings.NewReader(`[{"type":"function", "name":"ints", "outputs":[{"type":"uint8"}, {"type":"uint8"}]}]`))
if err != nil {
t.Fatal(err)
}
marshalledReturn := append(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")...)
err = abi.Unpack(&out, "ints", marshalledReturn)
if err != nil {
t.Fatal(err)
}
if *var1 != 1 {
t.Error("expected var1 to be 1, got", *var1)
}
if *var2 != 2 {
t.Error("expected var2 to be 2, got", *var2)
}
out = []interface{}{var1}
err = abi.Unpack(&out, "ints", marshalledReturn)
expErr := "abi: cannot marshal in to slices of unequal size (require: 2, got: 1)"
if err == nil || err.Error() != expErr {
t.Error("expected err:", expErr, "Got:", err)
}
}
func TestUnpackSetInterfaceArrayOutput(t *testing.T) {
var (
var1 = new([1]uint32)
var2 = new([1]uint32)
)
out := []interface{}{var1, var2}
abi, err := JSON(strings.NewReader(`[{"type":"function", "name":"ints", "outputs":[{"type":"uint32[1]"}, {"type":"uint32[1]"}]}]`))
if err != nil {
t.Fatal(err)
}
marshalledReturn := append(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")...)
err = abi.Unpack(&out, "ints", marshalledReturn)
if err != nil {
t.Fatal(err)
}
if *var1 != [1]uint32{1} {
t.Error("expected var1 to be [1], got", *var1)
}
if *var2 != [1]uint32{2} {
t.Error("expected var2 to be [2], got", *var2)
}
}
func TestMultiReturnWithStruct(t *testing.T) {
const definition = `[
{ "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
// using buff to make the code readable
buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
stringOut := "hello"
buff.Write(common.RightPadBytes([]byte(stringOut), 32))
var inter struct {
Int *big.Int
String string
}
err = abi.Unpack(&inter, "multi", buff.Bytes())
if err != nil {
t.Error(err)
}
if inter.Int == nil || inter.Int.Cmp(big.NewInt(1)) != 0 {
t.Error("expected Int to be 1 got", inter.Int)
}
if inter.String != stringOut {
t.Error("expected String to be", stringOut, "got", inter.String)
}
var reversed struct {
String string
Int *big.Int
}
err = abi.Unpack(&reversed, "multi", buff.Bytes())
if err != nil {
t.Error(err)
}
if reversed.Int == nil || reversed.Int.Cmp(big.NewInt(1)) != 0 {
t.Error("expected Int to be 1 got", reversed.Int)
}
if reversed.String != stringOut {
t.Error("expected String to be", stringOut, "got", reversed.String)
}
}
func TestMultiReturnWithSlice(t *testing.T) {
const definition = `[
{ "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
// using buff to make the code readable
buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
stringOut := "hello"
buff.Write(common.RightPadBytes([]byte(stringOut), 32))
var inter []interface{}
err = abi.Unpack(&inter, "multi", buff.Bytes())
if err != nil {
t.Error(err)
}
if len(inter) != 2 {
t.Fatal("expected 2 results got", len(inter))
}
if num, ok := inter[0].(*big.Int); !ok || num.Cmp(big.NewInt(1)) != 0 {
t.Error("expected index 0 to be 1 got", num)
}
if str, ok := inter[1].(string); !ok || str != stringOut {
t.Error("expected index 1 to be", stringOut, "got", str)
}
}
func TestMarshalArrays(t *testing.T) {
const definition = `[
{ "name" : "bytes32", "constant" : false, "outputs": [ { "type": "bytes32" } ] },
{ "name" : "bytes10", "constant" : false, "outputs": [ { "type": "bytes10" } ] }
]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
output := common.LeftPadBytes([]byte{1}, 32)
var bytes10 [10]byte
err = abi.Unpack(&bytes10, "bytes32", output)
if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" {
t.Error("expected error or bytes32 not be assignable to bytes10:", err)
}
var bytes32 [32]byte
err = abi.Unpack(&bytes32, "bytes32", output)
if err != nil {
t.Error("didn't expect error:", err)
}
if !bytes.Equal(bytes32[:], output) {
t.Error("expected bytes32[31] to be 1 got", bytes32[31])
}
type (
B10 [10]byte
B32 [32]byte
)
var b10 B10
err = abi.Unpack(&b10, "bytes32", output)
if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" {
t.Error("expected error or bytes32 not be assignable to bytes10:", err)
}
var b32 B32
err = abi.Unpack(&b32, "bytes32", output)
if err != nil {
t.Error("didn't expect error:", err)
}
if !bytes.Equal(b32[:], output) {
t.Error("expected bytes32[31] to be 1 got", bytes32[31])
}
output[10] = 1
var shortAssignLong [32]byte
err = abi.Unpack(&shortAssignLong, "bytes10", output)
if err != nil {
t.Error("didn't expect error:", err)
}
if !bytes.Equal(output, shortAssignLong[:]) {
t.Errorf("expected %x to be %x", shortAssignLong, output)
}
}
func TestUnmarshal(t *testing.T) {
const definition = `[
{ "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] },
{ "name" : "bool", "constant" : false, "outputs": [ { "type": "bool" } ] },
{ "name" : "bytes", "constant" : false, "outputs": [ { "type": "bytes" } ] },
{ "name" : "fixed", "constant" : false, "outputs": [ { "type": "bytes32" } ] },
{ "name" : "multi", "constant" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
{ "name" : "intArraySingle", "constant" : false, "outputs": [ { "type": "uint256[3]" } ] },
{ "name" : "addressSliceSingle", "constant" : false, "outputs": [ { "type": "address[]" } ] },
{ "name" : "addressSliceDouble", "constant" : false, "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] },
{ "name" : "mixedBytes", "constant" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
buff := new(bytes.Buffer)
// marshal int
var Int *big.Int
err = abi.Unpack(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
if err != nil {
t.Error(err)
}
if Int == nil || Int.Cmp(big.NewInt(1)) != 0 {
t.Error("expected Int to be 1 got", Int)
}
// marshal bool
var Bool bool
err = abi.Unpack(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
if err != nil {
t.Error(err)
}
if !Bool {
t.Error("expected Bool to be true")
}
// marshal dynamic bytes max length 32
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
bytesOut := common.RightPadBytes([]byte("hello"), 32)
buff.Write(bytesOut)
var Bytes []byte
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
if err != nil {
t.Error(err)
}
if !bytes.Equal(Bytes, bytesOut) {
t.Errorf("expected %x got %x", bytesOut, Bytes)
}
// marshall dynamic bytes max length 64
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
bytesOut = common.RightPadBytes([]byte("hello"), 64)
buff.Write(bytesOut)
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
if err != nil {
t.Error(err)
}
if !bytes.Equal(Bytes, bytesOut) {
t.Errorf("expected %x got %x", bytesOut, Bytes)
}
// marshall dynamic bytes max length 63
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000003f"))
bytesOut = common.RightPadBytes([]byte("hello"), 63)
buff.Write(bytesOut)
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
if err != nil {
t.Error(err)
}
if !bytes.Equal(Bytes, bytesOut) {
t.Errorf("expected %x got %x", bytesOut, Bytes)
}
// marshal dynamic bytes output empty
err = abi.Unpack(&Bytes, "bytes", nil)
if err == nil {
t.Error("expected error")
}
// marshal dynamic bytes length 5
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
buff.Write(common.RightPadBytes([]byte("hello"), 32))
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
if err != nil {
t.Error(err)
}
if !bytes.Equal(Bytes, []byte("hello")) {
t.Errorf("expected %x got %x", bytesOut, Bytes)
}
// marshal dynamic bytes length 5
buff.Reset()
buff.Write(common.RightPadBytes([]byte("hello"), 32))
var hash common.Hash
err = abi.Unpack(&hash, "fixed", buff.Bytes())
if err != nil {
t.Error(err)
}
helloHash := common.BytesToHash(common.RightPadBytes([]byte("hello"), 32))
if hash != helloHash {
t.Errorf("Expected %x to equal %x", hash, helloHash)
}
// marshal error
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
if err == nil {
t.Error("expected error")
}
err = abi.Unpack(&Bytes, "multi", make([]byte, 64))
if err == nil {
t.Error("expected error")
}
// marshal mixed bytes
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
fixed := common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")
buff.Write(fixed)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
bytesOut = common.RightPadBytes([]byte("hello"), 32)
buff.Write(bytesOut)
var out []interface{}
err = abi.Unpack(&out, "mixedBytes", buff.Bytes())
if err != nil {
t.Fatal("didn't expect error:", err)
}
if !bytes.Equal(bytesOut, out[0].([]byte)) {
t.Errorf("expected %x, got %x", bytesOut, out[0])
}
if !bytes.Equal(fixed, out[1].([]byte)) {
t.Errorf("expected %x, got %x", fixed, out[1])
}
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
// marshal int array
var intArray [3]*big.Int
err = abi.Unpack(&intArray, "intArraySingle", buff.Bytes())
if err != nil {
t.Error(err)
}
var testAgainstIntArray [3]*big.Int
testAgainstIntArray[0] = big.NewInt(1)
testAgainstIntArray[1] = big.NewInt(2)
testAgainstIntArray[2] = big.NewInt(3)
for i, Int := range intArray {
if Int.Cmp(testAgainstIntArray[i]) != 0 {
t.Errorf("expected %v, got %v", testAgainstIntArray[i], Int)
}
}
// marshal address slice
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) // offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // size
buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
var outAddr []common.Address
err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes())
if err != nil {
t.Fatal("didn't expect error:", err)
}
if len(outAddr) != 1 {
t.Fatal("expected 1 item, got", len(outAddr))
}
if outAddr[0] != (common.Address{1}) {
t.Errorf("expected %x, got %x", common.Address{1}, outAddr[0])
}
// marshal multiple address slice
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // size
buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // size
buff.Write(common.Hex2Bytes("0000000000000000000000000200000000000000000000000000000000000000"))
buff.Write(common.Hex2Bytes("0000000000000000000000000300000000000000000000000000000000000000"))
var outAddrStruct struct {
A []common.Address
B []common.Address
}
err = abi.Unpack(&outAddrStruct, "addressSliceDouble", buff.Bytes())
if err != nil {
t.Fatal("didn't expect error:", err)
}
if len(outAddrStruct.A) != 1 {
t.Fatal("expected 1 item, got", len(outAddrStruct.A))
}
if outAddrStruct.A[0] != (common.Address{1}) {
t.Errorf("expected %x, got %x", common.Address{1}, outAddrStruct.A[0])
}
if len(outAddrStruct.B) != 2 {
t.Fatal("expected 1 item, got", len(outAddrStruct.B))
}
if outAddrStruct.B[0] != (common.Address{2}) {
t.Errorf("expected %x, got %x", common.Address{2}, outAddrStruct.B[0])
}
if outAddrStruct.B[1] != (common.Address{3}) {
t.Errorf("expected %x, got %x", common.Address{3}, outAddrStruct.B[1])
}
// marshal invalid address slice
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000100"))
err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes())
if err == nil {
t.Fatal("expected error:", err)
}
}

View File

@ -38,7 +38,7 @@ var ErrNotSupported = errors.New("not supported")
var ErrInvalidPassphrase = errors.New("invalid passphrase")
// ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the
// secodn time.
// second time.
var ErrWalletAlreadyOpen = errors.New("wallet already open")
// ErrWalletClosed is returned if a wallet is attempted to be opened the

View File

@ -124,14 +124,13 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
if err != nil {
return err
}
privkey, err := hex.DecodeString(keyJSON.PrivateKey)
privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey)
if err != nil {
return err
}
k.Address = common.BytesToAddress(addr)
k.PrivateKey = crypto.ToECDSA(privkey)
k.PrivateKey = privkey
return nil
}

View File

@ -450,7 +450,6 @@ func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (acco
if ks.cache.hasAddress(key.Address) {
return accounts.Account{}, fmt.Errorf("account already exists")
}
return ks.importKey(key, passphrase)
}

View File

@ -182,7 +182,8 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
if err != nil {
return nil, err
}
key := crypto.ToECDSA(keyBytes)
key := crypto.ToECDSAUnsafe(keyBytes)
return &Key{
Id: uuid.UUID(keyId),
Address: crypto.PubkeyToAddress(key.PublicKey),

View File

@ -46,7 +46,7 @@ func TestKeyEncryptDecrypt(t *testing.T) {
// Decrypt with the correct password
key, err := DecryptKey(keyjson, password)
if err != nil {
t.Errorf("test %d: json key failed to decrypt: %v", i, err)
t.Fatalf("test %d: json key failed to decrypt: %v", i, err)
}
if key.Address != address {
t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address)

View File

@ -22,6 +22,7 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
@ -140,21 +141,32 @@ func TestV3_PBKDF2_1(t *testing.T) {
testDecryptV3(tests["wikipage_test_vector_pbkdf2"], t)
}
var testsSubmodule = filepath.Join("..", "..", "tests", "testdata", "KeyStoreTests")
func skipIfSubmoduleMissing(t *testing.T) {
if !common.FileExist(testsSubmodule) {
t.Skipf("can't find JSON tests from submodule at %s", testsSubmodule)
}
}
func TestV3_PBKDF2_2(t *testing.T) {
skipIfSubmoduleMissing(t)
t.Parallel()
tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
tests := loadKeyStoreTestV3(filepath.Join(testsSubmodule, "basic_tests.json"), t)
testDecryptV3(tests["test1"], t)
}
func TestV3_PBKDF2_3(t *testing.T) {
skipIfSubmoduleMissing(t)
t.Parallel()
tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
tests := loadKeyStoreTestV3(filepath.Join(testsSubmodule, "basic_tests.json"), t)
testDecryptV3(tests["python_generated_test_with_odd_iv"], t)
}
func TestV3_PBKDF2_4(t *testing.T) {
skipIfSubmoduleMissing(t)
t.Parallel()
tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
tests := loadKeyStoreTestV3(filepath.Join(testsSubmodule, "basic_tests.json"), t)
testDecryptV3(tests["evilnonce"], t)
}
@ -165,8 +177,9 @@ func TestV3_Scrypt_1(t *testing.T) {
}
func TestV3_Scrypt_2(t *testing.T) {
skipIfSubmoduleMissing(t)
t.Parallel()
tests := loadKeyStoreTestV3("../../tests/files/KeyStoreTests/basic_tests.json", t)
tests := loadKeyStoreTestV3(filepath.Join(testsSubmodule, "basic_tests.json"), t)
testDecryptV3(tests["test2"], t)
}

View File

@ -74,7 +74,8 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
return nil, err
}
ethPriv := crypto.Keccak256(plainText)
ecKey := crypto.ToECDSA(ethPriv)
ecKey := crypto.ToECDSAUnsafe(ethPriv)
key = &Key{
Id: nil,
Address: crypto.PubkeyToAddress(ecKey.PublicKey),

View File

@ -21,9 +21,10 @@ environment:
PATH: C:\msys64\mingw32\bin\;C:\Program Files (x86)\NSIS\;%PATH%
install:
- git submodule update --init
- rmdir C:\go /s /q
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.8.1.windows-%GETH_ARCH%.zip
- 7z x go1.8.1.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.8.3.windows-%GETH_ARCH%.zip
- 7z x go1.8.3.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
- go version
- gcc --version

View File

@ -77,6 +77,7 @@ var (
executablePath("puppeth"),
executablePath("rlpdump"),
executablePath("swarm"),
executablePath("wnode"),
}
// A debian package is created for all executables listed here.
@ -109,6 +110,10 @@ var (
Name: "swarm",
Description: "Ethereum Swarm daemon and tools",
},
{
Name: "wnode",
Description: "Ethereum Whisper diagnostic tool",
},
}
// Distros for which packages are created.
@ -170,7 +175,7 @@ func doInstall(cmdline []string) {
// Check Go version. People regularly open issues about compilation
// failure with outdated Go. This should save them the trouble.
if runtime.Version() < "go1.7" && !strings.HasPrefix(runtime.Version(), "devel") {
if runtime.Version() < "go1.7" && !strings.Contains(runtime.Version(), "devel") {
log.Println("You have Go version", runtime.Version())
log.Println("go-ethereum requires at least Go version 1.7 and cannot")
log.Println("be compiled with an earlier version. Please upgrade your Go installation.")

View File

@ -45,13 +45,16 @@ var (
// paths with any of these prefixes will be skipped
skipPrefixes = []string{
// boring stuff
"vendor/", "tests/files/", "build/",
"vendor/", "tests/testdata/", "build/",
// don't relicense vendored sources
"crypto/sha3/", "crypto/ecies/", "log/",
"crypto/secp256k1/curve.go",
"consensus/ethash/xor.go",
"internal/jsre/deps",
"cmd/internal/browser",
"consensus/ethash/xor.go",
"crypto/bn256/",
"crypto/ecies/",
"crypto/secp256k1/curve.go",
"crypto/sha3/",
"internal/jsre/deps",
"log/",
// don't license generated files
"contracts/chequebook/contract/",
"contracts/ens/contract/",

View File

@ -68,6 +68,7 @@ func main() {
if err = crypto.SaveECDSA(*genKey, nodeKey); err != nil {
utils.Fatalf("%v", err)
}
return
case *nodeKeyFile == "" && *nodeKeyHex == "":
utils.Fatalf("Use -nodekey or -nodekeyhex to specify a private key")
case *nodeKeyFile != "" && *nodeKeyHex != "":

67
cmd/evm/json_logger.go Normal file
View File

@ -0,0 +1,67 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"encoding/json"
"io"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/vm"
)
type JSONLogger struct {
encoder *json.Encoder
cfg *vm.LogConfig
}
func NewJSONLogger(cfg *vm.LogConfig, writer io.Writer) *JSONLogger {
return &JSONLogger{json.NewEncoder(writer), cfg}
}
// CaptureState outputs state information on the logger.
func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
log := vm.StructLog{
Pc: pc,
Op: op,
Gas: gas + cost,
GasCost: cost,
MemorySize: memory.Len(),
Storage: nil,
Depth: depth,
Err: err,
}
if !l.cfg.DisableMemory {
log.Memory = memory.Data()
}
if !l.cfg.DisableStack {
log.Stack = stack.Data()
}
return l.encoder.Encode(log)
}
// CaptureEnd is triggered at end of execution.
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration) error {
type endLog struct {
Output string `json:"output"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
Time time.Duration `json:"time"`
}
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t})
}

View File

@ -35,6 +35,18 @@ var (
Name: "debug",
Usage: "output full trace logs",
}
MemProfileFlag = cli.StringFlag{
Name: "memprofile",
Usage: "creates a memory profile at the given path",
}
CPUProfileFlag = cli.StringFlag{
Name: "cpuprofile",
Usage: "creates a CPU profile at the given path",
}
StatDumpFlag = cli.BoolFlag{
Name: "statdump",
Usage: "displays stack and heap memory information",
}
CodeFlag = cli.StringFlag{
Name: "code",
Usage: "EVM code",
@ -78,6 +90,26 @@ var (
Name: "nogasmetering",
Usage: "disable gas metering",
}
GenesisFlag = cli.StringFlag{
Name: "prestate",
Usage: "JSON file with prestate (genesis) config",
}
MachineFlag = cli.BoolFlag{
Name: "json",
Usage: "output trace logs in machine readable format (json)",
}
SenderFlag = cli.StringFlag{
Name: "sender",
Usage: "The transaction origin",
}
DisableMemoryFlag = cli.BoolFlag{
Name: "nomemory",
Usage: "disable memory output",
}
DisableStackFlag = cli.BoolFlag{
Name: "nostack",
Usage: "disable stack output",
}
)
func init() {
@ -93,6 +125,14 @@ func init() {
DumpFlag,
InputFlag,
DisableGasMeteringFlag,
MemProfileFlag,
CPUProfileFlag,
StatDumpFlag,
GenesisFlag,
MachineFlag,
SenderFlag,
DisableMemoryFlag,
DisableStackFlag,
}
app.Commands = []cli.Command{
compileCommand,

View File

@ -18,9 +18,11 @@ package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"runtime/pprof"
"time"
goruntime "runtime"
@ -28,11 +30,13 @@ import (
"github.com/ethereum/go-ethereum/cmd/evm/internal/compiler"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/core/vm/runtime"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
cli "gopkg.in/urfave/cli.v1"
)
@ -44,17 +48,63 @@ var runCommand = cli.Command{
Description: `The run command runs arbitrary EVM code.`,
}
// readGenesis will read the given JSON format genesis file and return
// the initialized Genesis structure
func readGenesis(genesisPath string) *core.Genesis {
// Make sure we have a valid genesis JSON
//genesisPath := ctx.Args().First()
if len(genesisPath) == 0 {
utils.Fatalf("Must supply path to genesis JSON file")
}
file, err := os.Open(genesisPath)
if err != nil {
utils.Fatalf("Failed to read genesis file: %v", err)
}
defer file.Close()
genesis := new(core.Genesis)
if err := json.NewDecoder(file).Decode(genesis); err != nil {
utils.Fatalf("invalid genesis file: %v", err)
}
return genesis
}
func runCmd(ctx *cli.Context) error {
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
log.Root().SetHandler(glogger)
logconfig := &vm.LogConfig{
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
}
var (
db, _ = ethdb.NewMemDatabase()
statedb, _ = state.New(common.Hash{}, db)
sender = common.StringToAddress("sender")
logger = vm.NewStructLogger(nil)
tracer vm.Tracer
debugLogger *vm.StructLogger
statedb *state.StateDB
chainConfig *params.ChainConfig
sender = common.StringToAddress("sender")
)
if ctx.GlobalBool(MachineFlag.Name) {
tracer = NewJSONLogger(logconfig, os.Stdout)
} else if ctx.GlobalBool(DebugFlag.Name) {
debugLogger = vm.NewStructLogger(logconfig)
tracer = debugLogger
} else {
debugLogger = vm.NewStructLogger(logconfig)
}
if ctx.GlobalString(GenesisFlag.Name) != "" {
gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
_, statedb = gen.ToBlock()
chainConfig = gen.Config
} else {
db, _ := ethdb.NewMemDatabase()
statedb, _ = state.New(common.Hash{}, state.NewDatabase(db))
}
if ctx.GlobalString(SenderFlag.Name) != "" {
sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name))
}
statedb.CreateAccount(sender)
var (
@ -94,43 +144,77 @@ func runCmd(ctx *cli.Context) error {
}
code = common.Hex2Bytes(string(bytes.TrimRight(hexcode, "\n")))
}
initialGas := ctx.GlobalUint64(GasFlag.Name)
runtimeConfig := runtime.Config{
Origin: sender,
State: statedb,
GasLimit: ctx.GlobalUint64(GasFlag.Name),
GasLimit: initialGas,
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
Value: utils.GlobalBig(ctx, ValueFlag.Name),
EVMConfig: vm.Config{
Tracer: logger,
Debug: ctx.GlobalBool(DebugFlag.Name),
Tracer: tracer,
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
},
}
if cpuProfilePath := ctx.GlobalString(CPUProfileFlag.Name); cpuProfilePath != "" {
f, err := os.Create(cpuProfilePath)
if err != nil {
fmt.Println("could not create CPU profile: ", err)
os.Exit(1)
}
if err := pprof.StartCPUProfile(f); err != nil {
fmt.Println("could not start CPU profile: ", err)
os.Exit(1)
}
defer pprof.StopCPUProfile()
}
if chainConfig != nil {
runtimeConfig.ChainConfig = chainConfig
}
tstart := time.Now()
var leftOverGas uint64
if ctx.GlobalBool(CreateFlag.Name) {
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
ret, _, err = runtime.Create(input, &runtimeConfig)
ret, _, leftOverGas, err = runtime.Create(input, &runtimeConfig)
} else {
receiver := common.StringToAddress("receiver")
statedb.SetCode(receiver, code)
ret, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig)
ret, leftOverGas, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig)
}
execTime := time.Since(tstart)
if ctx.GlobalBool(DumpFlag.Name) {
statedb.Commit(true)
statedb.IntermediateRoot(true)
fmt.Println(string(statedb.Dump()))
}
if memProfilePath := ctx.GlobalString(MemProfileFlag.Name); memProfilePath != "" {
f, err := os.Create(memProfilePath)
if err != nil {
fmt.Println("could not create memory profile: ", err)
os.Exit(1)
}
if err := pprof.WriteHeapProfile(f); err != nil {
fmt.Println("could not write memory profile: ", err)
os.Exit(1)
}
f.Close()
}
if ctx.GlobalBool(DebugFlag.Name) {
fmt.Fprintln(os.Stderr, "#### TRACE ####")
vm.WriteTrace(os.Stderr, logger.StructLogs())
if debugLogger != nil {
fmt.Fprintln(os.Stderr, "#### TRACE ####")
vm.WriteTrace(os.Stderr, debugLogger.StructLogs())
}
fmt.Fprintln(os.Stderr, "#### LOGS ####")
vm.WriteLogs(os.Stderr, statedb.Logs())
}
if ctx.GlobalBool(StatDumpFlag.Name) {
var mem goruntime.MemStats
goruntime.ReadMemStats(&mem)
fmt.Fprintf(os.Stderr, `evm execution time: %v
@ -138,14 +222,18 @@ heap objects: %d
allocations: %d
total allocations: %d
GC calls: %d
Gas used: %d
`, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC)
`, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas)
}
if tracer != nil {
tracer.CaptureEnd(ret, initialGas-leftOverGas, execTime)
} else {
fmt.Printf("0x%x\n", ret)
}
fmt.Printf("0x%x", ret)
if err != nil {
fmt.Printf(" error: %v", err)
fmt.Printf(" error: %v\n", err)
}
fmt.Println()
return nil
}

View File

@ -27,10 +27,13 @@ import (
"fmt"
"html/template"
"io/ioutil"
"math"
"math/big"
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
@ -60,12 +63,13 @@ var (
apiPortFlag = flag.Int("apiport", 8080, "Listener port for the HTTP API connection")
ethPortFlag = flag.Int("ethport", 30303, "Listener port for the devp2p connection")
bootFlag = flag.String("bootnodes", "", "Comma separated bootnode enode URLs to seed with")
netFlag = flag.Int("network", 0, "Network ID to use for the Ethereum protocol")
netFlag = flag.Uint64("network", 0, "Network ID to use for the Ethereum protocol")
statsFlag = flag.String("ethstats", "", "Ethstats network monitoring auth string")
netnameFlag = flag.String("faucet.name", "", "Network name to assign to the faucet")
payoutFlag = flag.Int("faucet.amount", 1, "Number of Ethers to pay out per user request")
minutesFlag = flag.Int("faucet.minutes", 1440, "Number of minutes to wait between funding rounds")
tiersFlag = flag.Int("faucet.tiers", 3, "Number of funding tiers to enable (x3 time, x2.5 funds)")
accJSONFlag = flag.String("account.json", "", "Key json file to fund user requests with")
accPassFlag = flag.String("account.pass", "", "Decryption password to access faucet funds")
@ -73,6 +77,9 @@ var (
githubUser = flag.String("github.user", "", "GitHub user to authenticate with for Gist access")
githubToken = flag.String("github.token", "", "GitHub personal token to access Gists with")
captchaToken = flag.String("captcha.token", "", "Recaptcha site key to authenticate client side")
captchaSecret = flag.String("captcha.secret", "", "Recaptcha secret key to authenticate server side")
logFlag = flag.Int("loglevel", 3, "Log level to use for Ethereum and the faucet")
)
@ -85,21 +92,47 @@ func main() {
flag.Parse()
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*logFlag), log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
// Construct the payout tiers
amounts := make([]string, *tiersFlag)
periods := make([]string, *tiersFlag)
for i := 0; i < *tiersFlag; i++ {
// Calculate the amount for the next tier and format it
amount := float64(*payoutFlag) * math.Pow(2.5, float64(i))
amounts[i] = fmt.Sprintf("%s Ethers", strconv.FormatFloat(amount, 'f', -1, 64))
if amount == 1 {
amounts[i] = strings.TrimSuffix(amounts[i], "s")
}
// Calculate the period for the next tier and format it
period := *minutesFlag * int(math.Pow(3, float64(i)))
periods[i] = fmt.Sprintf("%d mins", period)
if period%60 == 0 {
period /= 60
periods[i] = fmt.Sprintf("%d hours", period)
if period%24 == 0 {
period /= 24
periods[i] = fmt.Sprintf("%d days", period)
}
}
if period == 1 {
periods[i] = strings.TrimSuffix(periods[i], "s")
}
}
// Load up and render the faucet website
tmpl, err := Asset("faucet.html")
if err != nil {
log.Crit("Failed to load the faucet template", "err", err)
}
period := fmt.Sprintf("%d minute(s)", *minutesFlag)
if *minutesFlag%60 == 0 {
period = fmt.Sprintf("%d hour(s)", *minutesFlag/60)
}
website := new(bytes.Buffer)
template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{
"Network": *netnameFlag,
"Amount": *payoutFlag,
"Period": period,
err = template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{
"Network": *netnameFlag,
"Amounts": amounts,
"Periods": periods,
"Recaptcha": *captchaToken,
})
if err != nil {
log.Crit("Failed to render the faucet template", "err", err)
}
// Load and parse the genesis block requested by the user
blob, err := ioutil.ReadFile(*genesisFlag)
if err != nil {
@ -166,15 +199,15 @@ type faucet struct {
nonce uint64 // Current pending nonce of the faucet
price *big.Int // Current gas price to issue funds with
conns []*websocket.Conn // Currently live websocket connections
history map[string]time.Time // History of users and their funding requests
reqs []*request // Currently pending funding requests
update chan struct{} // Channel to signal request updates
conns []*websocket.Conn // Currently live websocket connections
timeouts map[string]time.Time // History of users and their funding timeouts
reqs []*request // Currently pending funding requests
update chan struct{} // Channel to signal request updates
lock sync.RWMutex // Lock protecting the faucet's internals
}
func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network int, stats string, ks *keystore.KeyStore, index []byte) (*faucet, error) {
func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network uint64, stats string, ks *keystore.KeyStore, index []byte) (*faucet, error) {
// Assemble the raw devp2p protocol stack
stack, err := node.New(&node.Config{
Name: "geth",
@ -236,7 +269,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network i
index: index,
keystore: ks,
account: ks.Accounts()[0],
history: make(map[string]time.Time),
timeouts: make(map[string]time.Time),
update: make(chan struct{}, 1),
}, nil
}
@ -290,14 +323,23 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
"peers": f.stack.Server().PeerCount(),
"requests": f.reqs,
})
header, _ := f.client.HeaderByNumber(context.Background(), nil)
websocket.JSON.Send(conn, header)
// Send the initial block to the client
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
header, err := f.client.HeaderByNumber(ctx, nil)
cancel()
if err != nil {
log.Error("Failed to retrieve latest header", "err", err)
} else {
websocket.JSON.Send(conn, header)
}
// Keep reading requests from the websocket until the connection breaks
for {
// Fetch the next funding request and validate against github
var msg struct {
URL string `json:"url"`
URL string `json:"url"`
Tier uint `json:"tier"`
Captcha string `json:"captcha"`
}
if err := websocket.JSON.Receive(conn, &msg); err != nil {
return
@ -306,8 +348,39 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
websocket.JSON.Send(conn, map[string]string{"error": "URL doesn't link to GitHub Gists"})
continue
}
log.Info("Faucet funds requested", "gist", msg.URL)
if msg.Tier >= uint(*tiersFlag) {
websocket.JSON.Send(conn, map[string]string{"error": "Invalid funding tier requested"})
continue
}
log.Info("Faucet funds requested", "gist", msg.URL, "tier", msg.Tier)
// If captcha verifications are enabled, make sure we're not dealing with a robot
if *captchaToken != "" {
form := url.Values{}
form.Add("secret", *captchaSecret)
form.Add("response", msg.Captcha)
res, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", form)
if err != nil {
websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
continue
}
var result struct {
Success bool `json:"success"`
Errors json.RawMessage `json:"error-codes"`
}
err = json.NewDecoder(res.Body).Decode(&result)
res.Body.Close()
if err != nil {
websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
continue
}
if !result.Success {
log.Warn("Captcha verification failed", "err", string(result.Errors))
websocket.JSON.Send(conn, map[string]string{"error": "Beep-bop, you're a robot!"})
continue
}
}
// Retrieve the gist from the GitHub Gist APIs
parts := strings.Split(msg.URL, "/")
req, _ := http.NewRequest("GET", "https://api.github.com/gists/"+parts[len(parts)-1], nil)
@ -334,7 +407,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
continue
}
if gist.Owner.Login == "" {
websocket.JSON.Send(conn, map[string]string{"error": "Nice try ;)"})
websocket.JSON.Send(conn, map[string]string{"error": "Anonymous Gists not allowed"})
continue
}
// Iterate over all the files and look for Ethereum addresses
@ -348,15 +421,30 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
websocket.JSON.Send(conn, map[string]string{"error": "No Ethereum address found to fund"})
continue
}
// Validate the user's existence since the API is unhelpful here
if res, err = http.Head("https://github.com/" + gist.Owner.Login); err != nil {
websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
continue
}
res.Body.Close()
if res.StatusCode != 200 {
websocket.JSON.Send(conn, map[string]string{"error": "Invalid user... boom!"})
continue
}
// Ensure the user didn't request funds too recently
f.lock.Lock()
var (
fund bool
elapsed time.Duration
timeout time.Time
)
if elapsed = time.Since(f.history[gist.Owner.Login]); elapsed > time.Duration(*minutesFlag)*time.Minute {
if timeout = f.timeouts[gist.Owner.Login]; time.Now().After(timeout) {
// User wasn't funded recently, create the funding transaction
tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether), big.NewInt(21000), f.price, nil)
amount := new(big.Int).Mul(big.NewInt(int64(*payoutFlag)), ether)
amount = new(big.Int).Mul(amount, new(big.Int).Exp(big.NewInt(5), big.NewInt(int64(msg.Tier)), nil))
amount = new(big.Int).Div(amount, new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(msg.Tier)), nil))
tx := types.NewTransaction(f.nonce+uint64(len(f.reqs)), address, amount, big.NewInt(21000), f.price, nil)
signed, err := f.keystore.SignTx(f.account, tx, f.config.ChainId)
if err != nil {
websocket.JSON.Send(conn, map[string]string{"error": err.Error()})
@ -375,14 +463,14 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
Time: time.Now(),
Tx: signed,
})
f.history[gist.Owner.Login] = time.Now()
f.timeouts[gist.Owner.Login] = time.Now().Add(time.Duration(*minutesFlag*int(math.Pow(3, float64(msg.Tier)))) * time.Minute)
fund = true
}
f.lock.Unlock()
// Send an error if too frequent funding, othewise a success
if !fund {
websocket.JSON.Send(conn, map[string]string{"error": fmt.Sprintf("User already funded %s ago", common.PrettyDuration(elapsed))})
websocket.JSON.Send(conn, map[string]string{"error": fmt.Sprintf("%s left until next allowance", common.PrettyDuration(timeout.Sub(time.Now())))})
continue
}
websocket.JSON.Send(conn, map[string]string{"success": fmt.Sprintf("Funding request accepted for %s into %s", gist.Owner.Login, address.Hex())})

View File

@ -51,9 +51,13 @@
<div class="input-group">
<input id="gist" type="text" class="form-control" placeholder="GitHub Gist URL containing your Ethereum address...">
<span class="input-group-btn">
<button class="btn btn-default" type="button" onclick="submit()">Give me Ether!</button>
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Give me Ether <i class="fa fa-caret-down" aria-hidden="true"></i></button>
<ul class="dropdown-menu dropdown-menu-right">{{range $idx, $amount := .Amounts}}
<li><a style="text-align: center;" onclick="tier={{$idx}}; {{if $.Recaptcha}}grecaptcha.execute(){{else}}submit({{$idx}}){{end}}">{{$amount}} / {{index $.Periods $idx}}</a></li>{{end}}
</ul>
</span>
</div>
</div>{{if .Recaptcha}}
<div class="g-recaptcha" data-sitekey="{{.Recaptcha}}" data-callback="submit" data-size="invisible"></div>{{end}}
</div>
</div>
<div class="row" style="margin-top: 32px;">
@ -76,8 +80,9 @@
<div class="row" style="margin-top: 32px;">
<div class="col-lg-12">
<h3>How does this work?</h3>
<p>This Ether faucet is running on the {{.Network}} network. To prevent malicious actors from exhausting all available funds or accumulating enough Ether to mount long running spam attacks, requests are tied to GitHub accounts. Anyone having a GitHub account may request funds within the permitted limit of <strong>{{.Amount}} Ether(s) / {{.Period}}</strong>.</p>
<p>This Ether faucet is running on the {{.Network}} network. To prevent malicious actors from exhausting all available funds or accumulating enough Ether to mount long running spam attacks, requests are tied to GitHub accounts. Anyone having a GitHub account may request funds within the permitted limits.</p>
<p>To request funds, simply create a <a href="https://gist.github.com/" target="_about:blank">GitHub Gist</a> with your Ethereum address pasted into the contents (the file name doesn't matter), copy paste the gists URL into the above input box and fire away! You can track the current pending requests below the input field to see how much you have to wait until your turn comes.</p>
{{if .Recaptcha}}<em>The faucet is running invisible reCaptcha protection against bots.</em>{{end}}
</div>
</div>
</div>
@ -87,10 +92,12 @@
// Global variables to hold the current status of the faucet
var attempt = 0;
var server;
var tier = 0;
// Define the function that submits a gist url to the server
var submit = function() {
server.send(JSON.stringify({url: $("#gist")[0].value}));
var submit = function({{if .Recaptcha}}captcha{{end}}) {
server.send(JSON.stringify({url: $("#gist")[0].value, tier: tier{{if .Recaptcha}}, captcha: captcha{{end}}}));{{if .Recaptcha}}
grecaptcha.reset();{{end}}
};
// Define a method to reconnect upon server loss
var reconnect = function() {
@ -134,10 +141,10 @@
}
}
server.onclose = function() { setTimeout(reconnect, 3000); };
server.onerror = function() { setTimeout(reconnect, 3000); };
}
// Establish a websocket connection to the API server
reconnect();
</script>
</script>{{if .Recaptcha}}
<script src="https://www.google.com/recaptcha/api.js" async defer></script>{{end}}
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -40,32 +40,39 @@ var (
will prompt for your password and imports your ether presale account.
It can be used non-interactively with the --password option taking a
passwordfile as argument containing the wallet password in plaintext.
`,
passwordfile as argument containing the wallet password in plaintext.`,
Subcommands: []cli.Command{
{
Action: importWallet,
Name: "import",
Usage: "Import Ethereum presale wallet",
ArgsUsage: "<keyFile>",
Action: utils.MigrateFlags(importWallet),
Category: "ACCOUNT COMMANDS",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.KeyStoreDirFlag,
utils.PasswordFileFlag,
utils.LightKDFFlag,
},
Description: `
TODO: Please write this
`,
geth wallet [options] /path/to/my/presale.wallet
will prompt for your password and imports your ether presale account.
It can be used non-interactively with the --password option taking a
passwordfile as argument containing the wallet password in plaintext.`,
},
},
}
accountCommand = cli.Command{
Action: accountList,
Name: "account",
Usage: "Manage accounts",
ArgsUsage: "",
Category: "ACCOUNT COMMANDS",
Description: `
Manage accounts lets you create new accounts, list all existing accounts,
import a private key into a new account.
' help' shows a list of subcommands or help for one subcommand.
accountCommand = cli.Command{
Name: "account",
Usage: "Manage accounts",
Category: "ACCOUNT COMMANDS",
Description: `
Manage accounts, list all existing accounts, import a private key into a new
account, create a new account or update an existing account.
It supports interactive mode, when you are prompted for password as well as
non-interactive mode where passwords are supplied via a given password file.
@ -80,36 +87,34 @@ Note that exporting your key in unencrypted format is NOT supported.
Keys are stored under <DATADIR>/keystore.
It is safe to transfer the entire directory or the individual keys therein
between ethereum nodes by simply copying.
Make sure you backup your keys regularly.
In order to use your account to send transactions, you need to unlock them using
the '--unlock' option. The argument is a space separated list of addresses or
indexes. If used non-interactively with a passwordfile, the file should contain
the respective passwords one per line. If you unlock n accounts and the password
file contains less than n entries, then the last password is meant to apply to
all remaining accounts.
And finally. DO NOT FORGET YOUR PASSWORD.
`,
Make sure you backup your keys regularly.`,
Subcommands: []cli.Command{
{
Action: accountList,
Name: "list",
Usage: "Print account addresses",
ArgsUsage: " ",
Name: "list",
Usage: "Print summary of existing accounts",
Action: utils.MigrateFlags(accountList),
Flags: []cli.Flag{
utils.DataDirFlag,
utils.KeyStoreDirFlag,
},
Description: `
TODO: Please write this
`,
Print a short summary of all accounts`,
},
{
Action: accountCreate,
Name: "new",
Usage: "Create a new account",
ArgsUsage: " ",
Name: "new",
Usage: "Create a new account",
Action: utils.MigrateFlags(accountCreate),
Flags: []cli.Flag{
utils.DataDirFlag,
utils.KeyStoreDirFlag,
utils.PasswordFileFlag,
utils.LightKDFFlag,
},
Description: `
geth account new
Creates a new account. Prints the address.
Creates a new account and prints the address.
The account is saved in encrypted format, you are prompted for a passphrase.
@ -117,17 +122,20 @@ You must remember this passphrase to unlock your account in the future.
For non-interactive use the passphrase can be specified with the --password flag:
geth --password <passwordfile> account new
Note, this is meant to be used for testing only, it is a bad idea to save your
password to file or expose in any other way.
`,
},
{
Action: accountUpdate,
Name: "update",
Usage: "Update an existing account",
Action: utils.MigrateFlags(accountUpdate),
ArgsUsage: "<address>",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.KeyStoreDirFlag,
utils.LightKDFFlag,
},
Description: `
geth account update <address>
@ -141,16 +149,22 @@ format to the newest format or change the password for an account.
For non-interactive use the passphrase can be specified with the --password flag:
geth --password <passwordfile> account update <address>
geth account update [options] <address>
Since only one password can be given, only format update can be performed,
changing your password is only possible interactively.
`,
},
{
Action: accountImport,
Name: "import",
Usage: "Import a private key into a new account",
Name: "import",
Usage: "Import a private key into a new account",
Action: utils.MigrateFlags(accountImport),
Flags: []cli.Flag{
utils.DataDirFlag,
utils.KeyStoreDirFlag,
utils.PasswordFileFlag,
utils.LightKDFFlag,
},
ArgsUsage: "<keyFile>",
Description: `
geth account import <keyfile>
@ -166,7 +180,7 @@ You must remember this passphrase to unlock your account in the future.
For non-interactive use the passphrase can be specified with the -password flag:
geth --password <passwordfile> account import <keyfile>
geth account import [options] <keyfile>
Note:
As you can directly copy your encrypted accounts to another ethereum instance,
@ -219,7 +233,7 @@ func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i in
return accounts.Account{}, ""
}
// getPassPhrase retrieves the passwor associated with an account, either fetched
// getPassPhrase retrieves the password associated with an account, either fetched
// from a list of preloaded passphrases, or requested interactively from the user.
func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
// If a list of passwords was supplied, retrieve from them
@ -298,10 +312,12 @@ func accountUpdate(ctx *cli.Context) error {
stack, _ := makeConfigNode(ctx)
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
account, oldPassword := unlockAccount(ctx, ks, ctx.Args().First(), 0, nil)
newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
if err := ks.Update(account, oldPassword, newPassword); err != nil {
utils.Fatalf("Could not update the account: %v", err)
for _, addr := range ctx.Args() {
account, oldPassword := unlockAccount(ctx, ks, addr, 0, nil)
newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
if err := ks.Update(account, oldPassword, newPassword); err != nil {
utils.Fatalf("Could not update the account: %v", err)
}
}
return nil
}

View File

@ -43,22 +43,22 @@ func tmpDatadirWithKeystore(t *testing.T) string {
}
func TestAccountListEmpty(t *testing.T) {
geth := runGeth(t, "account")
geth.expectExit()
geth := runGeth(t, "account", "list")
geth.ExpectExit()
}
func TestAccountList(t *testing.T) {
datadir := tmpDatadirWithKeystore(t)
geth := runGeth(t, "--datadir", datadir, "account")
defer geth.expectExit()
geth := runGeth(t, "account", "list", "--datadir", datadir)
defer geth.ExpectExit()
if runtime.GOOS == "windows" {
geth.expect(`
geth.Expect(`
Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}\keystore\UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}\keystore\aaa
Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}\keystore\zzz
`)
} else {
geth.expect(`
geth.Expect(`
Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}/keystore/aaa
Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}/keystore/zzz
@ -67,21 +67,21 @@ Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}/k
}
func TestAccountNew(t *testing.T) {
geth := runGeth(t, "--lightkdf", "account", "new")
defer geth.expectExit()
geth.expect(`
geth := runGeth(t, "account", "new", "--lightkdf")
defer geth.ExpectExit()
geth.Expect(`
Your new account is locked with a password. Please give a password. Do not forget this password.
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}}
Repeat passphrase: {{.InputLine "foobar"}}
`)
geth.expectRegexp(`Address: \{[0-9a-f]{40}\}\n`)
geth.ExpectRegexp(`Address: \{[0-9a-f]{40}\}\n`)
}
func TestAccountNewBadRepeat(t *testing.T) {
geth := runGeth(t, "--lightkdf", "account", "new")
defer geth.expectExit()
geth.expect(`
geth := runGeth(t, "account", "new", "--lightkdf")
defer geth.ExpectExit()
geth.Expect(`
Your new account is locked with a password. Please give a password. Do not forget this password.
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "something"}}
@ -92,11 +92,11 @@ Fatal: Passphrases do not match
func TestAccountUpdate(t *testing.T) {
datadir := tmpDatadirWithKeystore(t)
geth := runGeth(t,
geth := runGeth(t, "account", "update",
"--datadir", datadir, "--lightkdf",
"account", "update", "f466859ead1932d743d622cb74fc058882e8648a")
defer geth.expectExit()
geth.expect(`
"f466859ead1932d743d622cb74fc058882e8648a")
defer geth.ExpectExit()
geth.Expect(`
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}}
@ -107,9 +107,9 @@ Repeat passphrase: {{.InputLine "foobar2"}}
}
func TestWalletImport(t *testing.T) {
geth := runGeth(t, "--lightkdf", "wallet", "import", "testdata/guswallet.json")
defer geth.expectExit()
geth.expect(`
geth := runGeth(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json")
defer geth.ExpectExit()
geth.Expect(`
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foo"}}
Address: {d4584b5f6229b7be90727b0fc8c6b91bb427821f}
@ -122,9 +122,9 @@ Address: {d4584b5f6229b7be90727b0fc8c6b91bb427821f}
}
func TestWalletImportBadPassword(t *testing.T) {
geth := runGeth(t, "--lightkdf", "wallet", "import", "testdata/guswallet.json")
defer geth.expectExit()
geth.expect(`
geth := runGeth(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json")
defer geth.ExpectExit()
geth.Expect(`
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "wrong"}}
Fatal: could not decrypt key with given passphrase
@ -137,19 +137,19 @@ func TestUnlockFlag(t *testing.T) {
"--datadir", datadir, "--nat", "none", "--nodiscover", "--dev",
"--unlock", "f466859ead1932d743d622cb74fc058882e8648a",
"js", "testdata/empty.js")
geth.expect(`
geth.Expect(`
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}}
`)
geth.expectExit()
geth.ExpectExit()
wantMessages := []string{
"Unlocked account",
"=0xf466859ead1932d743d622cb74fc058882e8648a",
}
for _, m := range wantMessages {
if !strings.Contains(geth.stderrText(), m) {
if !strings.Contains(geth.StderrText(), m) {
t.Errorf("stderr text does not contain %q", m)
}
}
@ -160,8 +160,8 @@ func TestUnlockFlagWrongPassword(t *testing.T) {
geth := runGeth(t,
"--datadir", datadir, "--nat", "none", "--nodiscover", "--dev",
"--unlock", "f466859ead1932d743d622cb74fc058882e8648a")
defer geth.expectExit()
geth.expect(`
defer geth.ExpectExit()
geth.Expect(`
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "wrong1"}}
@ -180,14 +180,14 @@ func TestUnlockFlagMultiIndex(t *testing.T) {
"--datadir", datadir, "--nat", "none", "--nodiscover", "--dev",
"--unlock", "0,2",
"js", "testdata/empty.js")
geth.expect(`
geth.Expect(`
Unlocking account 0 | Attempt 1/3
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}}
Unlocking account 2 | Attempt 1/3
Passphrase: {{.InputLine "foobar"}}
`)
geth.expectExit()
geth.ExpectExit()
wantMessages := []string{
"Unlocked account",
@ -195,7 +195,7 @@ Passphrase: {{.InputLine "foobar"}}
"=0x289d485d9771714cce91d3393d764e1311907acc",
}
for _, m := range wantMessages {
if !strings.Contains(geth.stderrText(), m) {
if !strings.Contains(geth.StderrText(), m) {
t.Errorf("stderr text does not contain %q", m)
}
}
@ -207,7 +207,7 @@ func TestUnlockFlagPasswordFile(t *testing.T) {
"--datadir", datadir, "--nat", "none", "--nodiscover", "--dev",
"--password", "testdata/passwords.txt", "--unlock", "0,2",
"js", "testdata/empty.js")
geth.expectExit()
geth.ExpectExit()
wantMessages := []string{
"Unlocked account",
@ -215,7 +215,7 @@ func TestUnlockFlagPasswordFile(t *testing.T) {
"=0x289d485d9771714cce91d3393d764e1311907acc",
}
for _, m := range wantMessages {
if !strings.Contains(geth.stderrText(), m) {
if !strings.Contains(geth.StderrText(), m) {
t.Errorf("stderr text does not contain %q", m)
}
}
@ -226,8 +226,8 @@ func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) {
geth := runGeth(t,
"--datadir", datadir, "--nat", "none", "--nodiscover", "--dev",
"--password", "testdata/wrong-passwords.txt", "--unlock", "0,2")
defer geth.expectExit()
geth.expect(`
defer geth.ExpectExit()
geth.Expect(`
Fatal: Failed to unlock account 0 (could not decrypt key with given passphrase)
`)
}
@ -238,14 +238,14 @@ func TestUnlockFlagAmbiguous(t *testing.T) {
"--keystore", store, "--nat", "none", "--nodiscover", "--dev",
"--unlock", "f466859ead1932d743d622cb74fc058882e8648a",
"js", "testdata/empty.js")
defer geth.expectExit()
defer geth.ExpectExit()
// Helper for the expect template, returns absolute keystore path.
geth.setTemplateFunc("keypath", func(file string) string {
geth.SetTemplateFunc("keypath", func(file string) string {
abs, _ := filepath.Abs(filepath.Join(store, file))
return abs
})
geth.expect(`
geth.Expect(`
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}}
@ -257,14 +257,14 @@ Your passphrase unlocked keystore://{{keypath "1"}}
In order to avoid this warning, you need to remove the following duplicate key files:
keystore://{{keypath "2"}}
`)
geth.expectExit()
geth.ExpectExit()
wantMessages := []string{
"Unlocked account",
"=0xf466859ead1932d743d622cb74fc058882e8648a",
}
for _, m := range wantMessages {
if !strings.Contains(geth.stderrText(), m) {
if !strings.Contains(geth.StderrText(), m) {
t.Errorf("stderr text does not contain %q", m)
}
}
@ -275,14 +275,14 @@ func TestUnlockFlagAmbiguousWrongPassword(t *testing.T) {
geth := runGeth(t,
"--keystore", store, "--nat", "none", "--nodiscover", "--dev",
"--unlock", "f466859ead1932d743d622cb74fc058882e8648a")
defer geth.expectExit()
defer geth.ExpectExit()
// Helper for the expect template, returns absolute keystore path.
geth.setTemplateFunc("keypath", func(file string) string {
geth.SetTemplateFunc("keypath", func(file string) string {
abs, _ := filepath.Abs(filepath.Join(store, file))
return abs
})
geth.expect(`
geth.Expect(`
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "wrong"}}
@ -292,5 +292,5 @@ Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
Testing your passphrase against all of them...
Fatal: None of the listed files could be unlocked.
`)
geth.expectExit()
geth.ExpectExit()
}

View File

@ -29,11 +29,12 @@ import (
"github.com/ethereum/go-ethereum/cmd/internal/browser"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/cmd/utils"
cli "gopkg.in/urfave/cli.v1"
)
var bugCommand = cli.Command{
Action: reportBug,
Action: utils.MigrateFlags(reportBug),
Name: "bug",
Usage: "opens a window to report a bug on the geth repo",
ArgsUsage: " ",

View File

@ -40,80 +40,98 @@ import (
var (
initCommand = cli.Command{
Action: initGenesis,
Action: utils.MigrateFlags(initGenesis),
Name: "init",
Usage: "Bootstrap and initialize a new genesis block",
ArgsUsage: "<genesisPath>",
Category: "BLOCKCHAIN COMMANDS",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.LightModeFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Description: `
The init command initializes a new genesis block and definition for the network.
This is a destructive action and changes the network in which you will be
participating.
`,
It expects the genesis file as argument.`,
}
importCommand = cli.Command{
Action: importChain,
Action: utils.MigrateFlags(importChain),
Name: "import",
Usage: "Import a blockchain file",
ArgsUsage: "<filename> (<filename 2> ... <filename N>) ",
Category: "BLOCKCHAIN COMMANDS",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.CacheFlag,
utils.LightModeFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Description: `
The import command imports blocks from an RLP-encoded form. The form can be one file
with several RLP-encoded blocks, or several files can be used.
The import command imports blocks from an RLP-encoded form. The form can be one file
with several RLP-encoded blocks, or several files can be used.
If only one file is used, import error will result in failure. If several files are used,
processing will proceed even if an individual RLP-file import failure occurs.
`,
processing will proceed even if an individual RLP-file import failure occurs.`,
}
exportCommand = cli.Command{
Action: exportChain,
Action: utils.MigrateFlags(exportChain),
Name: "export",
Usage: "Export blockchain into file",
ArgsUsage: "<filename> [<blockNumFirst> <blockNumLast>]",
Category: "BLOCKCHAIN COMMANDS",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.CacheFlag,
utils.LightModeFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Description: `
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.
`,
if already existing.`,
}
removedbCommand = cli.Command{
Action: removeDB,
Action: utils.MigrateFlags(removeDB),
Name: "removedb",
Usage: "Remove blockchain and state databases",
ArgsUsage: " ",
Category: "BLOCKCHAIN COMMANDS",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.LightModeFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Description: `
TODO: Please write this
`,
Remove blockchain and state databases`,
}
dumpCommand = cli.Command{
Action: dump,
Action: utils.MigrateFlags(dump),
Name: "dump",
Usage: "Dump a specific block from storage",
ArgsUsage: "[<blockHash> | <blockNum>]...",
Category: "BLOCKCHAIN COMMANDS",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.CacheFlag,
utils.LightModeFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Description: `
The arguments are interpreted as block numbers or hashes.
Use "ethereum dump 0" to dump the genesis block.
`,
Use "ethereum dump 0" to dump the genesis block.`,
}
)
// initGenesis will initialise the given JSON format genesis file and writes it as
// the zero'd block (i.e. genesis) or will fail hard if it can't succeed.
func initGenesis(ctx *cli.Context) error {
// Make sure we have a valid genesis JSON
genesisPath := ctx.Args().First()
if len(genesisPath) == 0 {
utils.Fatalf("must supply path to genesis JSON file")
utils.Fatalf("Must supply path to genesis JSON file")
}
stack := makeFullNode(ctx)
chaindb := utils.MakeChainDatabase(ctx, stack)
file, err := os.Open(genesisPath)
if err != nil {
utils.Fatalf("failed to read genesis file: %v", err)
utils.Fatalf("Failed to read genesis file: %v", err)
}
defer file.Close()
@ -121,12 +139,19 @@ func initGenesis(ctx *cli.Context) error {
if err := json.NewDecoder(file).Decode(genesis); err != nil {
utils.Fatalf("invalid genesis file: %v", err)
}
_, hash, err := core.SetupGenesisBlock(chaindb, genesis)
if err != nil {
utils.Fatalf("failed to write genesis block: %v", err)
// Open an initialise both full and light databases
stack := makeFullNode(ctx)
for _, name := range []string{"chaindata", "lightchaindata"} {
chaindb, err := stack.OpenDatabase(name, 0, 0)
if err != nil {
utils.Fatalf("Failed to open database: %v", err)
}
_, hash, err := core.SetupGenesisBlock(chaindb, genesis)
if err != nil {
utils.Fatalf("Failed to write genesis block: %v", err)
}
log.Info("Successfully wrote genesis state", "database", name, "hash", hash)
}
log.Info("Successfully wrote genesis state", "hash", hash)
return nil
}
@ -245,24 +270,29 @@ func exportChain(ctx *cli.Context) error {
func removeDB(ctx *cli.Context) error {
stack, _ := makeConfigNode(ctx)
dbdir := stack.ResolvePath(utils.ChainDbName(ctx))
if !common.FileExist(dbdir) {
fmt.Println(dbdir, "does not exist")
return nil
}
fmt.Println(dbdir)
confirm, err := console.Stdin.PromptConfirm("Remove this database?")
switch {
case err != nil:
utils.Fatalf("%v", err)
case !confirm:
fmt.Println("Operation aborted")
default:
fmt.Println("Removing...")
start := time.Now()
os.RemoveAll(dbdir)
fmt.Printf("Removed in %v\n", time.Since(start))
for _, name := range []string{"chaindata", "lightchaindata"} {
// Ensure the database exists in the first place
logger := log.New("database", name)
dbdir := stack.ResolvePath(name)
if !common.FileExist(dbdir) {
logger.Info("Database doesn't exist, skipping", "path", dbdir)
continue
}
// Confirm removal and execute
fmt.Println(dbdir)
confirm, err := console.Stdin.PromptConfirm("Remove this database?")
switch {
case err != nil:
utils.Fatalf("%v", err)
case !confirm:
logger.Warn("Database deletion aborted")
default:
start := time.Now()
os.RemoveAll(dbdir)
logger.Info("Database successfully deleted", "elapsed", common.PrettyDuration(time.Since(start)))
}
}
return nil
}
@ -282,7 +312,7 @@ func dump(ctx *cli.Context) error {
fmt.Println("{}")
utils.Fatalf("block not found")
} else {
state, err := state.New(block.Root(), chainDb)
state, err := state.New(block.Root(), state.NewDatabase(chainDb))
if err != nil {
utils.Fatalf("could not create new state: %v", err)
}

View File

@ -33,15 +33,17 @@ 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"
"github.com/naoina/toml"
)
var (
dumpConfigCommand = cli.Command{
Action: dumpConfig,
Action: utils.MigrateFlags(dumpConfig),
Name: "dumpconfig",
Usage: "Show configuration values",
ArgsUsage: "",
Flags: append(append(nodeFlags, rpcFlags...), whisperFlags...),
Category: "MISCELLANEOUS COMMANDS",
Description: `The dumpconfig command shows configuration values.`,
}
@ -75,6 +77,7 @@ type ethstatsConfig struct {
type gethConfig struct {
Eth eth.Config
Shh whisper.Config
Node node.Config
Ethstats ethstatsConfig
}
@ -98,8 +101,8 @@ func defaultNodeConfig() node.Config {
cfg := node.DefaultConfig
cfg.Name = clientIdentifier
cfg.Version = params.VersionWithCommit(gitCommit)
cfg.HTTPModules = append(cfg.HTTPModules, "eth")
cfg.WSModules = append(cfg.WSModules, "eth")
cfg.HTTPModules = append(cfg.HTTPModules, "eth", "shh")
cfg.WSModules = append(cfg.WSModules, "eth", "shh")
cfg.IPCPath = "geth.ipc"
return cfg
}
@ -108,6 +111,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
// Load defaults.
cfg := gethConfig{
Eth: eth.DefaultConfig,
Shh: whisper.DefaultConfig,
Node: defaultNodeConfig(),
}
@ -129,19 +133,37 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
}
utils.SetShhConfig(ctx, stack, &cfg.Shh)
return stack, cfg
}
// enableWhisper returns true in case one of the whisper flags is set.
func enableWhisper(ctx *cli.Context) bool {
for _, flag := range whisperFlags {
if ctx.GlobalIsSet(flag.GetName()) {
return true
}
}
return false
}
func makeFullNode(ctx *cli.Context) *node.Node {
stack, cfg := makeConfigNode(ctx)
utils.RegisterEthService(stack, &cfg.Eth)
// Whisper must be explicitly enabled, but is auto-enabled in --dev mode.
shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name)
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
shhEnabled := enableWhisper(ctx)
shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name)
if shhEnabled || shhAutoEnabled {
utils.RegisterShhService(stack)
if ctx.GlobalIsSet(utils.WhisperMaxMessageSizeFlag.Name) {
cfg.Shh.MaxMessageSize = uint32(ctx.Int(utils.WhisperMaxMessageSizeFlag.Name))
}
if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) {
cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name)
}
utils.RegisterShhService(stack, &cfg.Shh)
}
// Add the Ethereum Stats daemon if requested.

View File

@ -29,41 +29,44 @@ import (
)
var (
consoleFlags = []cli.Flag{utils.JSpathFlag, utils.ExecFlag, utils.PreloadJSFlag}
consoleCommand = cli.Command{
Action: localConsole,
Name: "console",
Usage: "Start an interactive JavaScript environment",
ArgsUsage: "", // TODO: Write this!
Category: "CONSOLE COMMANDS",
Action: utils.MigrateFlags(localConsole),
Name: "console",
Usage: "Start an interactive JavaScript environment",
Flags: append(append(append(nodeFlags, rpcFlags...), consoleFlags...), whisperFlags...),
Category: "CONSOLE COMMANDS",
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/Javascipt-Console.`,
}
attachCommand = cli.Command{
Action: remoteConsole,
Action: utils.MigrateFlags(remoteConsole),
Name: "attach",
Usage: "Start an interactive JavaScript environment (connect to node)",
ArgsUsage: "", // TODO: Write this!
ArgsUsage: "[endpoint]",
Flags: append(consoleFlags, utils.DataDirFlag),
Category: "CONSOLE COMMANDS",
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.
This command allows to open a console on a running geth node.
`,
This command allows to open a console on a running geth node.`,
}
javascriptCommand = cli.Command{
Action: ephemeralConsole,
Action: utils.MigrateFlags(ephemeralConsole),
Name: "js",
Usage: "Execute the specified JavaScript files",
ArgsUsage: "", // TODO: Write this!
ArgsUsage: "<jsfile> [jsfile...]",
Flags: append(nodeFlags, consoleFlags...),
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/Javascipt-Console`,
}
)
@ -81,11 +84,12 @@ func localConsole(ctx *cli.Context) error {
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
}
config := console.Config{
DataDir: node.DataDir(),
DataDir: utils.MakeDataDir(ctx),
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
Client: client,
Preload: utils.MakeConsolePreloads(ctx),
}
console, err := console.New(config)
if err != nil {
utils.Fatalf("Failed to start the JavaScript console: %v", err)
@ -118,17 +122,18 @@ func remoteConsole(ctx *cli.Context) error {
Client: client,
Preload: utils.MakeConsolePreloads(ctx),
}
console, err := console.New(config)
if err != nil {
utils.Fatalf("Failed to start the JavaScript console: %v", err)
}
defer console.Stop(false)
// If only a short execution was requested, evaluate and return
if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" {
console.Evaluate(script)
return nil
}
// Otherwise print the welcome screen and enter interactive mode
console.Welcome()
console.Interactive()
@ -151,7 +156,7 @@ func dialRPC(endpoint string) (*rpc.Client, error) {
}
// ephemeralConsole starts a new geth node, attaches an ephemeral JavaScript
// console to it, and each of the files specified as arguments and tears the
// console to it, executes each of the files specified as arguments and tears
// everything down.
func ephemeralConsole(ctx *cli.Context) error {
// Create and start the node based on the CLI flags
@ -165,11 +170,12 @@ func ephemeralConsole(ctx *cli.Context) error {
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
}
config := console.Config{
DataDir: node.DataDir(),
DataDir: utils.MakeDataDir(ctx),
DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
Client: client,
Preload: utils.MakeConsolePreloads(ctx),
}
console, err := console.New(config)
if err != nil {
utils.Fatalf("Failed to start the JavaScript console: %v", err)

View File

@ -47,15 +47,15 @@ func TestConsoleWelcome(t *testing.T) {
"console")
// Gather all the infos the welcome message needs to contain
geth.setTemplateFunc("goos", func() string { return runtime.GOOS })
geth.setTemplateFunc("goarch", func() string { return runtime.GOARCH })
geth.setTemplateFunc("gover", runtime.Version)
geth.setTemplateFunc("gethver", func() string { return params.Version })
geth.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
geth.setTemplateFunc("apis", func() string { return ipcAPIs })
geth.SetTemplateFunc("goos", func() string { return runtime.GOOS })
geth.SetTemplateFunc("goarch", func() string { return runtime.GOARCH })
geth.SetTemplateFunc("gover", runtime.Version)
geth.SetTemplateFunc("gethver", func() string { return params.Version })
geth.SetTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
geth.SetTemplateFunc("apis", func() string { return ipcAPIs })
// Verify the actual welcome message to the required template
geth.expect(`
geth.Expect(`
Welcome to the Geth JavaScript console!
instance: Geth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}}
@ -66,7 +66,7 @@ at block: 0 ({{niltime}})
> {{.InputLine "exit"}}
`)
geth.expectExit()
geth.ExpectExit()
}
// Tests that a console can be attached to a running node via various means.
@ -90,8 +90,8 @@ func TestIPCAttachWelcome(t *testing.T) {
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
geth.interrupt()
geth.expectExit()
geth.Interrupt()
geth.ExpectExit()
}
func TestHTTPAttachWelcome(t *testing.T) {
@ -104,8 +104,8 @@ func TestHTTPAttachWelcome(t *testing.T) {
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
testAttachWelcome(t, geth, "http://localhost:"+port, httpAPIs)
geth.interrupt()
geth.expectExit()
geth.Interrupt()
geth.ExpectExit()
}
func TestWSAttachWelcome(t *testing.T) {
@ -119,29 +119,29 @@ func TestWSAttachWelcome(t *testing.T) {
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
testAttachWelcome(t, geth, "ws://localhost:"+port, httpAPIs)
geth.interrupt()
geth.expectExit()
geth.Interrupt()
geth.ExpectExit()
}
func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
// Attach to a running geth note and terminate immediately
attach := runGeth(t, "attach", endpoint)
defer attach.expectExit()
attach.stdin.Close()
defer attach.ExpectExit()
attach.CloseStdin()
// Gather all the infos the welcome message needs to contain
attach.setTemplateFunc("goos", func() string { return runtime.GOOS })
attach.setTemplateFunc("goarch", func() string { return runtime.GOARCH })
attach.setTemplateFunc("gover", runtime.Version)
attach.setTemplateFunc("gethver", func() string { return params.Version })
attach.setTemplateFunc("etherbase", func() string { return geth.Etherbase })
attach.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
attach.setTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
attach.setTemplateFunc("datadir", func() string { return geth.Datadir })
attach.setTemplateFunc("apis", func() string { return apis })
attach.SetTemplateFunc("goos", func() string { return runtime.GOOS })
attach.SetTemplateFunc("goarch", func() string { return runtime.GOARCH })
attach.SetTemplateFunc("gover", runtime.Version)
attach.SetTemplateFunc("gethver", func() string { return params.Version })
attach.SetTemplateFunc("etherbase", func() string { return geth.Etherbase })
attach.SetTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
attach.SetTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
attach.SetTemplateFunc("datadir", func() string { return geth.Datadir })
attach.SetTemplateFunc("apis", func() string { return apis })
// Verify the actual welcome message to the required template
attach.expect(`
attach.Expect(`
Welcome to the Geth JavaScript console!
instance: Geth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}}
@ -152,7 +152,7 @@ at block: 0 ({{niltime}}){{if ipc}}
> {{.InputLine "exit" }}
`)
attach.expectExit()
attach.ExpectExit()
}
// trulyRandInt generates a crypto random integer used by the console tests to

View File

@ -89,7 +89,7 @@ func TestDAOForkBlockNewChain(t *testing.T) {
expectVote bool
}{
// Test DAO Default Mainnet
{"", params.MainNetDAOForkBlock, true},
{"", params.MainnetChainConfig.DAOForkBlock, true},
// test DAO Init Old Privnet
{daoOldGenesis, nil, false},
// test DAO Default No Fork Privnet
@ -112,12 +112,12 @@ func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBloc
if err := ioutil.WriteFile(json, []byte(genesis), 0600); err != nil {
t.Fatalf("test %d: failed to write genesis file: %v", test, err)
}
runGeth(t, "--datadir", datadir, "init", json).cmd.Wait()
runGeth(t, "--datadir", datadir, "init", json).WaitExit()
} else {
// Force chain initialization
args := []string{"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir}
geth := runGeth(t, append(args, []string{"--exec", "2+2", "console"}...)...)
geth.cmd.Wait()
geth.WaitExit()
}
// Retrieve the DAO config flag from the database
path := filepath.Join(datadir, "geth", "chaindata")

View File

@ -97,14 +97,14 @@ func TestCustomGenesis(t *testing.T) {
if err := ioutil.WriteFile(json, []byte(tt.genesis), 0600); err != nil {
t.Fatalf("test %d: failed to write genesis file: %v", i, err)
}
runGeth(t, "--datadir", datadir, "init", json).cmd.Wait()
runGeth(t, "--datadir", datadir, "init", json).WaitExit()
// Query the custom genesis block
geth := runGeth(t,
"--datadir", datadir, "--maxpeers", "0", "--port", "0",
"--nodiscover", "--nat", "none", "--ipcdisable",
"--exec", tt.query, "console")
geth.expectRegexp(tt.result)
geth.expectExit()
geth.ExpectRegexp(tt.result)
geth.ExpectExit()
}
}

View File

@ -49,6 +49,88 @@ var (
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
nodeFlags = []cli.Flag{
utils.IdentityFlag,
utils.UnlockedAccountFlag,
utils.PasswordFileFlag,
utils.BootnodesFlag,
utils.BootnodesV4Flag,
utils.BootnodesV5Flag,
utils.DataDirFlag,
utils.KeyStoreDirFlag,
utils.NoUSBFlag,
utils.EthashCacheDirFlag,
utils.EthashCachesInMemoryFlag,
utils.EthashCachesOnDiskFlag,
utils.EthashDatasetDirFlag,
utils.EthashDatasetsInMemoryFlag,
utils.EthashDatasetsOnDiskFlag,
utils.TxPoolNoLocalsFlag,
utils.TxPoolPriceLimitFlag,
utils.TxPoolPriceBumpFlag,
utils.TxPoolAccountSlotsFlag,
utils.TxPoolGlobalSlotsFlag,
utils.TxPoolAccountQueueFlag,
utils.TxPoolGlobalQueueFlag,
utils.TxPoolLifetimeFlag,
utils.FastSyncFlag,
utils.LightModeFlag,
utils.SyncModeFlag,
utils.LightServFlag,
utils.LightPeersFlag,
utils.LightKDFFlag,
utils.CacheFlag,
utils.TrieCacheGenFlag,
utils.ListenPortFlag,
utils.MaxPeersFlag,
utils.MaxPendingPeersFlag,
utils.EtherbaseFlag,
utils.GasPriceFlag,
utils.MinerThreadsFlag,
utils.MiningEnabledFlag,
utils.TargetGasLimitFlag,
utils.NATFlag,
utils.NoDiscoverFlag,
utils.DiscoveryV5Flag,
utils.NetrestrictFlag,
utils.NodeKeyFileFlag,
utils.NodeKeyHexFlag,
utils.DevModeFlag,
utils.TestnetFlag,
utils.RinkebyFlag,
utils.VMEnableDebugFlag,
utils.NetworkIdFlag,
utils.RPCCORSDomainFlag,
utils.EthStatsURLFlag,
utils.MetricsEnabledFlag,
utils.FakePoWFlag,
utils.NoCompactionFlag,
utils.GpoBlocksFlag,
utils.GpoPercentileFlag,
utils.ExtraDataFlag,
configFileFlag,
}
rpcFlags = []cli.Flag{
utils.RPCEnabledFlag,
utils.RPCListenAddrFlag,
utils.RPCPortFlag,
utils.RPCApiFlag,
utils.WSEnabledFlag,
utils.WSListenAddrFlag,
utils.WSPortFlag,
utils.WSApiFlag,
utils.WSAllowedOriginsFlag,
utils.IPCDisabledFlag,
utils.IPCPathFlag,
}
whisperFlags = []cli.Flag{
utils.WhisperEnabledFlag,
utils.WhisperMaxMessageSizeFlag,
utils.WhisperMinPOWFlag,
}
)
func init() {
@ -81,71 +163,11 @@ func init() {
dumpConfigCommand,
}
app.Flags = []cli.Flag{
utils.IdentityFlag,
utils.UnlockedAccountFlag,
utils.PasswordFileFlag,
utils.BootnodesFlag,
utils.DataDirFlag,
utils.KeyStoreDirFlag,
utils.EthashCacheDirFlag,
utils.EthashCachesInMemoryFlag,
utils.EthashCachesOnDiskFlag,
utils.EthashDatasetDirFlag,
utils.EthashDatasetsInMemoryFlag,
utils.EthashDatasetsOnDiskFlag,
utils.FastSyncFlag,
utils.LightModeFlag,
utils.SyncModeFlag,
utils.LightServFlag,
utils.LightPeersFlag,
utils.LightKDFFlag,
utils.CacheFlag,
utils.TrieCacheGenFlag,
utils.JSpathFlag,
utils.ListenPortFlag,
utils.MaxPeersFlag,
utils.MaxPendingPeersFlag,
utils.EtherbaseFlag,
utils.GasPriceFlag,
utils.MinerThreadsFlag,
utils.MiningEnabledFlag,
utils.TargetGasLimitFlag,
utils.NATFlag,
utils.NoDiscoverFlag,
utils.DiscoveryV5Flag,
utils.NetrestrictFlag,
utils.NodeKeyFileFlag,
utils.NodeKeyHexFlag,
utils.RPCEnabledFlag,
utils.RPCListenAddrFlag,
utils.RPCPortFlag,
utils.RPCApiFlag,
utils.WSEnabledFlag,
utils.WSListenAddrFlag,
utils.WSPortFlag,
utils.WSApiFlag,
utils.WSAllowedOriginsFlag,
utils.IPCDisabledFlag,
utils.IPCPathFlag,
utils.ExecFlag,
utils.PreloadJSFlag,
utils.WhisperEnabledFlag,
utils.DevModeFlag,
utils.TestNetFlag,
utils.VMEnableDebugFlag,
utils.NetworkIdFlag,
utils.RPCCORSDomainFlag,
utils.EthStatsURLFlag,
utils.MetricsEnabledFlag,
utils.FakePoWFlag,
utils.NoCompactionFlag,
utils.GpoBlocksFlag,
utils.GpoPercentileFlag,
utils.ExtraDataFlag,
configFileFlag,
}
app.Flags = append(app.Flags, nodeFlags...)
app.Flags = append(app.Flags, rpcFlags...)
app.Flags = append(app.Flags, consoleFlags...)
app.Flags = append(app.Flags, debug.Flags...)
app.Flags = append(app.Flags, whisperFlags...)
app.Before = func(ctx *cli.Context) error {
runtime.GOMAXPROCS(runtime.NumCPU())
@ -237,10 +259,12 @@ func startNode(ctx *cli.Context, stack *node.Node) {
}()
// Start auxiliary services if enabled
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
// Mining only makes sense if a full Ethereum node is running
var ethereum *eth.Ethereum
if err := stack.Service(&ethereum); err != nil {
utils.Fatalf("ethereum service not running: %v", err)
}
// Use a reduced number of threads if requested
if threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name); threads > 0 {
type threaded interface {
SetThreads(threads int)
@ -249,6 +273,8 @@ func startNode(ctx *cli.Context, stack *node.Node) {
th.SetThreads(threads)
}
}
// Set the gas price to the limits from the CLI and start mining
ethereum.TxPool().SetGasPrice(utils.GlobalBig(ctx, utils.GasPriceFlag.Name))
if err := ethereum.StartMining(true); err != nil {
utils.Fatalf("Failed to start mining: %v", err)
}

View File

@ -34,7 +34,7 @@ import (
var (
makedagCommand = cli.Command{
Action: makedag,
Action: utils.MigrateFlags(makedag),
Name: "makedag",
Usage: "Generate ethash DAG (for testing)",
ArgsUsage: "<blockNum> <outputDir>",
@ -47,7 +47,7 @@ Regular users do not need to execute it.
`,
}
versionCommand = cli.Command{
Action: version,
Action: utils.MigrateFlags(version),
Name: "version",
Usage: "Print version numbers",
ArgsUsage: " ",
@ -57,7 +57,7 @@ The output of this command is supposed to be machine-readable.
`,
}
licenseCommand = cli.Command{
Action: license,
Action: utils.MigrateFlags(license),
Name: "license",
Usage: "Display license information",
ArgsUsage: " ",
@ -103,7 +103,7 @@ func version(ctx *cli.Context) error {
}
fmt.Println("Architecture:", runtime.GOARCH)
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name))
fmt.Println("Network Id:", eth.DefaultConfig.NetworkId)
fmt.Println("Go Version:", runtime.Version())
fmt.Println("Operating System:", runtime.GOOS)
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))

View File

@ -49,7 +49,7 @@ var (
Usage: "Refresh interval in seconds",
}
monitorCommand = cli.Command{
Action: monitor,
Action: utils.MigrateFlags(monitor), // keep track of migration progress
Name: "monitor",
Usage: "Monitor and visualize node metrics",
ArgsUsage: " ",

View File

@ -17,18 +17,13 @@
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"regexp"
"sync"
"testing"
"text/template"
"time"
"github.com/docker/docker/pkg/reexec"
"github.com/ethereum/go-ethereum/internal/cmdtest"
)
func tmpdir(t *testing.T) string {
@ -40,36 +35,37 @@ func tmpdir(t *testing.T) string {
}
type testgeth struct {
// For total convenience, all testing methods are available.
*testing.T
// template variables for expect
Datadir string
Executable string
Etherbase string
Func template.FuncMap
*cmdtest.TestCmd
removeDatadir bool
cmd *exec.Cmd
stdout *bufio.Reader
stdin io.WriteCloser
stderr *testlogger
// template variables for expect
Datadir string
Etherbase string
}
func init() {
// Run the app if we're the child process for runGeth.
if os.Getenv("GETH_TEST_CHILD") != "" {
// Run the app if we've been exec'd as "geth-test" in runGeth.
reexec.Register("geth-test", func() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Exit(0)
})
}
func TestMain(m *testing.M) {
// check if we have been reexec'd
if reexec.Init() {
return
}
os.Exit(m.Run())
}
// spawns geth with the given command line args. If the args don't set --datadir, the
// child g gets a temporary data directory.
func runGeth(t *testing.T, args ...string) *testgeth {
tt := &testgeth{T: t, Executable: os.Args[0]}
tt := &testgeth{}
tt.TestCmd = cmdtest.NewTestCmd(t, tt)
for i, arg := range args {
switch {
case arg == "-datadir" || arg == "--datadir":
@ -84,215 +80,19 @@ func runGeth(t *testing.T, args ...string) *testgeth {
}
if tt.Datadir == "" {
tt.Datadir = tmpdir(t)
tt.removeDatadir = true
tt.Cleanup = func() { os.RemoveAll(tt.Datadir) }
args = append([]string{"-datadir", tt.Datadir}, args...)
// Remove the temporary datadir if something fails below.
defer func() {
if t.Failed() {
os.RemoveAll(tt.Datadir)
tt.Cleanup()
}
}()
}
// Boot "geth". This actually runs the test binary but the init function
// will prevent any tests from running.
tt.stderr = &testlogger{t: t}
tt.cmd = exec.Command(os.Args[0], args...)
tt.cmd.Env = append(os.Environ(), "GETH_TEST_CHILD=1")
tt.cmd.Stderr = tt.stderr
stdout, err := tt.cmd.StdoutPipe()
if err != nil {
t.Fatal(err)
}
tt.stdout = bufio.NewReader(stdout)
if tt.stdin, err = tt.cmd.StdinPipe(); err != nil {
t.Fatal(err)
}
if err := tt.cmd.Start(); err != nil {
t.Fatal(err)
}
// Boot "geth". This actually runs the test binary but the TestMain
// function will prevent any tests from running.
tt.Run("geth-test", args...)
return tt
}
// InputLine writes the given text to the childs stdin.
// This method can also be called from an expect template, e.g.:
//
// geth.expect(`Passphrase: {{.InputLine "password"}}`)
func (tt *testgeth) InputLine(s string) string {
io.WriteString(tt.stdin, s+"\n")
return ""
}
func (tt *testgeth) setTemplateFunc(name string, fn interface{}) {
if tt.Func == nil {
tt.Func = make(map[string]interface{})
}
tt.Func[name] = fn
}
// expect runs its argument as a template, then expects the
// child process to output the result of the template within 5s.
//
// If the template starts with a newline, the newline is removed
// before matching.
func (tt *testgeth) expect(tplsource string) {
// Generate the expected output by running the template.
tpl := template.Must(template.New("").Funcs(tt.Func).Parse(tplsource))
wantbuf := new(bytes.Buffer)
if err := tpl.Execute(wantbuf, tt); err != nil {
panic(err)
}
// Trim exactly one newline at the beginning. This makes tests look
// much nicer because all expect strings are at column 0.
want := bytes.TrimPrefix(wantbuf.Bytes(), []byte("\n"))
if err := tt.matchExactOutput(want); err != nil {
tt.Fatal(err)
}
tt.Logf("Matched stdout text:\n%s", want)
}
func (tt *testgeth) matchExactOutput(want []byte) error {
buf := make([]byte, len(want))
n := 0
tt.withKillTimeout(func() { n, _ = io.ReadFull(tt.stdout, buf) })
buf = buf[:n]
if n < len(want) || !bytes.Equal(buf, want) {
// Grab any additional buffered output in case of mismatch
// because it might help with debugging.
buf = append(buf, make([]byte, tt.stdout.Buffered())...)
tt.stdout.Read(buf[n:])
// Find the mismatch position.
for i := 0; i < n; i++ {
if want[i] != buf[i] {
return fmt.Errorf("Output mismatch at ◊:\n---------------- (stdout text)\n%s◊%s\n---------------- (expected text)\n%s",
buf[:i], buf[i:n], want)
}
}
if n < len(want) {
return fmt.Errorf("Not enough output, got until ◊:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%s◊%s",
buf, want[:n], want[n:])
}
}
return nil
}
// expectRegexp expects the child process to output text matching the
// given regular expression within 5s.
//
// Note that an arbitrary amount of output may be consumed by the
// regular expression. This usually means that expect cannot be used
// after expectRegexp.
func (tt *testgeth) expectRegexp(resource string) (*regexp.Regexp, []string) {
var (
re = regexp.MustCompile(resource)
rtee = &runeTee{in: tt.stdout}
matches []int
)
tt.withKillTimeout(func() { matches = re.FindReaderSubmatchIndex(rtee) })
output := rtee.buf.Bytes()
if matches == nil {
tt.Fatalf("Output did not match:\n---------------- (stdout text)\n%s\n---------------- (regular expression)\n%s",
output, resource)
return re, nil
}
tt.Logf("Matched stdout text:\n%s", output)
var submatch []string
for i := 0; i < len(matches); i += 2 {
submatch = append(submatch, string(output[i:i+1]))
}
return re, submatch
}
// expectExit expects the child process to exit within 5s without
// printing any additional text on stdout.
func (tt *testgeth) expectExit() {
var output []byte
tt.withKillTimeout(func() {
output, _ = ioutil.ReadAll(tt.stdout)
})
tt.cmd.Wait()
if tt.removeDatadir {
os.RemoveAll(tt.Datadir)
}
if len(output) > 0 {
tt.Errorf("Unmatched stdout text:\n%s", output)
}
}
func (tt *testgeth) interrupt() {
tt.cmd.Process.Signal(os.Interrupt)
}
// stderrText returns any stderr output written so far.
// The returned text holds all log lines after expectExit has
// returned.
func (tt *testgeth) stderrText() string {
tt.stderr.mu.Lock()
defer tt.stderr.mu.Unlock()
return tt.stderr.buf.String()
}
func (tt *testgeth) withKillTimeout(fn func()) {
timeout := time.AfterFunc(5*time.Second, func() {
tt.Log("killing the child process (timeout)")
tt.cmd.Process.Kill()
if tt.removeDatadir {
os.RemoveAll(tt.Datadir)
}
})
defer timeout.Stop()
fn()
}
// testlogger logs all written lines via t.Log and also
// collects them for later inspection.
type testlogger struct {
t *testing.T
mu sync.Mutex
buf bytes.Buffer
}
func (tl *testlogger) Write(b []byte) (n int, err error) {
lines := bytes.Split(b, []byte("\n"))
for _, line := range lines {
if len(line) > 0 {
tl.t.Logf("(stderr) %s", line)
}
}
tl.mu.Lock()
tl.buf.Write(b)
tl.mu.Unlock()
return len(b), err
}
// runeTee collects text read through it into buf.
type runeTee struct {
in interface {
io.Reader
io.ByteReader
io.RuneReader
}
buf bytes.Buffer
}
func (rtee *runeTee) Read(b []byte) (n int, err error) {
n, err = rtee.in.Read(b)
rtee.buf.Write(b[:n])
return n, err
}
func (rtee *runeTee) ReadRune() (r rune, size int, err error) {
r, size, err = rtee.in.ReadRune()
if err == nil {
rtee.buf.WriteRune(r)
}
return r, size, err
}
func (rtee *runeTee) ReadByte() (b byte, err error) {
b, err = rtee.in.ReadByte()
if err == nil {
rtee.buf.WriteByte(b)
}
return b, err
}

View File

@ -20,6 +20,7 @@ package main
import (
"io"
"sort"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/internal/debug"
@ -67,8 +68,10 @@ var AppHelpFlagGroups = []flagGroup{
configFileFlag,
utils.DataDirFlag,
utils.KeyStoreDirFlag,
utils.NoUSBFlag,
utils.NetworkIdFlag,
utils.TestNetFlag,
utils.TestnetFlag,
utils.RinkebyFlag,
utils.DevModeFlag,
utils.SyncModeFlag,
utils.EthStatsURLFlag,
@ -89,6 +92,19 @@ var AppHelpFlagGroups = []flagGroup{
utils.EthashDatasetsOnDiskFlag,
},
},
{
Name: "TRANSACTION POOL",
Flags: []cli.Flag{
utils.TxPoolNoLocalsFlag,
utils.TxPoolPriceLimitFlag,
utils.TxPoolPriceBumpFlag,
utils.TxPoolAccountSlotsFlag,
utils.TxPoolGlobalSlotsFlag,
utils.TxPoolAccountQueueFlag,
utils.TxPoolGlobalQueueFlag,
utils.TxPoolLifetimeFlag,
},
},
{
Name: "PERFORMANCE TUNING",
Flags: []cli.Flag{
@ -127,6 +143,8 @@ var AppHelpFlagGroups = []flagGroup{
Name: "NETWORKING",
Flags: []cli.Flag{
utils.BootnodesFlag,
utils.BootnodesV4Flag,
utils.BootnodesV5Flag,
utils.ListenPortFlag,
utils.MaxPeersFlag,
utils.MaxPendingPeersFlag,
@ -170,6 +188,10 @@ var AppHelpFlagGroups = []flagGroup{
utils.NoCompactionFlag,
}, debug.Flags...),
},
{
Name: "WHISPER (EXPERIMENTAL)",
Flags: whisperFlags,
},
{
Name: "DEPRECATED",
Flags: []cli.Flag{
@ -178,13 +200,43 @@ var AppHelpFlagGroups = []flagGroup{
},
},
{
Name: "EXPERIMENTAL",
Flags: []cli.Flag{
utils.WhisperEnabledFlag,
},
Name: "MISC",
},
}
// byCategory sorts an array of flagGroup by Name in the order
// defined in AppHelpFlagGroups.
type byCategory []flagGroup
func (a byCategory) Len() int { return len(a) }
func (a byCategory) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byCategory) Less(i, j int) bool {
iCat, jCat := a[i].Name, a[j].Name
iIdx, jIdx := len(AppHelpFlagGroups), len(AppHelpFlagGroups) // ensure non categorized flags come last
for i, group := range AppHelpFlagGroups {
if iCat == group.Name {
iIdx = i
}
if jCat == group.Name {
jIdx = i
}
}
return iIdx < jIdx
}
func flagCategory(flag cli.Flag) string {
for _, category := range AppHelpFlagGroups {
for _, flg := range category.Flags {
if flg.GetName() == flag.GetName() {
return category.Name
}
}
}
return "MISC"
}
func init() {
// Override the default app help template
cli.AppHelpTemplate = AppHelpTemplate
@ -194,6 +246,7 @@ func init() {
App interface{}
FlagGroups []flagGroup
}
// Override the default app help printer, but only for the global app help
originalHelpPrinter := cli.HelpPrinter
cli.HelpPrinter = func(w io.Writer, tmpl string, data interface{}) {
@ -223,6 +276,27 @@ func init() {
}
// Render out custom usage screen
originalHelpPrinter(w, tmpl, helpData{data, AppHelpFlagGroups})
} else if tmpl == utils.CommandHelpTemplate {
// Iterate over all command specific flags and categorize them
categorized := make(map[string][]cli.Flag)
for _, flag := range data.(cli.Command).Flags {
if _, ok := categorized[flag.String()]; !ok {
categorized[flagCategory(flag)] = append(categorized[flagCategory(flag)], flag)
}
}
// sort to get a stable ordering
sorted := make([]flagGroup, 0, len(categorized))
for cat, flgs := range categorized {
sorted = append(sorted, flagGroup{cat, flgs})
}
sort.Sort(byCategory(sorted))
// add sorted array to data and render with default printer
originalHelpPrinter(w, tmpl, map[string]interface{}{
"cmd": data,
"categorizedFlags": sorted,
})
} else {
originalHelpPrinter(w, tmpl, data)
}

View File

@ -51,9 +51,10 @@ ADD account.pass /account.pass
EXPOSE 8080
CMD [ \
"/faucet", "--genesis", "/genesis.json", "--network", "{{.NetworkID}}", "--bootnodes", "{{.Bootnodes}}", "--ethstats", "{{.Ethstats}}", \
"--ethport", "{{.EthPort}}", "--faucet.name", "{{.FaucetName}}", "--faucet.amount", "{{.FaucetAmount}}", "--faucet.minutes", "{{.FaucetMinutes}}", \
"--github.user", "{{.GitHubUser}}", "--github.token", "{{.GitHubToken}}", "--account.json", "/account.json", "--account.pass", "/account.pass" \
"/faucet", "--genesis", "/genesis.json", "--network", "{{.NetworkID}}", "--bootnodes", "{{.Bootnodes}}", "--ethstats", "{{.Ethstats}}", "--ethport", "{{.EthPort}}", \
"--faucet.name", "{{.FaucetName}}", "--faucet.amount", "{{.FaucetAmount}}", "--faucet.minutes", "{{.FaucetMinutes}}", "--faucet.tiers", "{{.FaucetTiers}}", \
"--github.user", "{{.GitHubUser}}", "--github.token", "{{.GitHubToken}}", "--account.json", "/account.json", "--account.pass", "/account.pass" \
{{if .CaptchaToken}}, "--captcha.token", "{{.CaptchaToken}}", "--captcha.secret", "{{.CaptchaSecret}}"{{end}} \
]`
// faucetComposefile is the docker-compose.yml file required to deploy and maintain
@ -74,8 +75,11 @@ services:
- ETH_NAME={{.EthName}}
- FAUCET_AMOUNT={{.FaucetAmount}}
- FAUCET_MINUTES={{.FaucetMinutes}}
- FAUCET_TIERS={{.FaucetTiers}}
- GITHUB_USER={{.GitHubUser}}
- GITHUB_TOKEN={{.GitHubToken}}{{if .VHost}}
- GITHUB_TOKEN={{.GitHubToken}}
- CAPTCHA_TOKEN={{.CaptchaToken}}
- CAPTCHA_SECRET={{.CaptchaSecret}}{{if .VHost}}
- VIRTUAL_HOST={{.VHost}}
- VIRTUAL_PORT=8080{{end}}
restart: always
@ -97,9 +101,12 @@ func deployFaucet(client *sshClient, network string, bootnodes []string, config
"EthPort": config.node.portFull,
"GitHubUser": config.githubUser,
"GitHubToken": config.githubToken,
"CaptchaToken": config.captchaToken,
"CaptchaSecret": config.captchaSecret,
"FaucetName": strings.Title(network),
"FaucetAmount": config.amount,
"FaucetMinutes": config.minutes,
"FaucetTiers": config.tiers,
})
files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes()
@ -113,8 +120,11 @@ func deployFaucet(client *sshClient, network string, bootnodes []string, config
"EthName": config.node.ethstats[:strings.Index(config.node.ethstats, ":")],
"GitHubUser": config.githubUser,
"GitHubToken": config.githubToken,
"CaptchaToken": config.captchaToken,
"CaptchaSecret": config.captchaSecret,
"FaucetAmount": config.amount,
"FaucetMinutes": config.minutes,
"FaucetTiers": config.tiers,
})
files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
@ -135,18 +145,21 @@ func deployFaucet(client *sshClient, network string, bootnodes []string, config
// faucetInfos is returned from an faucet status check to allow reporting various
// configuration parameters.
type faucetInfos struct {
node *nodeInfos
host string
port int
amount int
minutes int
githubUser string
githubToken string
node *nodeInfos
host string
port int
amount int
minutes int
tiers int
githubUser string
githubToken string
captchaToken string
captchaSecret string
}
// String implements the stringer interface.
func (info *faucetInfos) String() string {
return fmt.Sprintf("host=%s, api=%d, eth=%d, amount=%d, minutes=%d, github=%s, ethstats=%s", info.host, info.port, info.node.portFull, info.amount, info.minutes, info.githubUser, info.node.ethstats)
return fmt.Sprintf("host=%s, api=%d, eth=%d, amount=%d, minutes=%d, tiers=%d, github=%s, captcha=%v, ethstats=%s", info.host, info.port, info.node.portFull, info.amount, info.minutes, info.tiers, info.githubUser, info.captchaToken != "", info.node.ethstats)
}
// checkFaucet does a health-check against an faucet server to verify whether
@ -177,6 +190,7 @@ func checkFaucet(client *sshClient, network string) (*faucetInfos, error) {
}
amount, _ := strconv.Atoi(infos.envvars["FAUCET_AMOUNT"])
minutes, _ := strconv.Atoi(infos.envvars["FAUCET_MINUTES"])
tiers, _ := strconv.Atoi(infos.envvars["FAUCET_TIERS"])
// Retrieve the funding account informations
var out []byte
@ -200,11 +214,14 @@ func checkFaucet(client *sshClient, network string) (*faucetInfos, error) {
keyJSON: keyJSON,
keyPass: keyPass,
},
host: host,
port: port,
amount: amount,
minutes: minutes,
githubUser: infos.envvars["GITHUB_USER"],
githubToken: infos.envvars["GITHUB_TOKEN"],
host: host,
port: port,
amount: amount,
minutes: minutes,
tiers: tiers,
githubUser: infos.envvars["GITHUB_USER"],
githubToken: infos.envvars["GITHUB_TOKEN"],
captchaToken: infos.envvars["CAPTCHA_TOKEN"],
captchaSecret: infos.envvars["CAPTCHA_SECRET"],
}, nil
}

View File

@ -40,7 +40,7 @@ ADD genesis.json /genesis.json
RUN \
echo '/geth 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{{end}}{{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}}' >> geth.sh
echo $'/geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .BootV4}}--bootnodesv4 {{.BootV4}}{{end}} {{if .BootV5}}--bootnodesv5 {{.BootV5}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine{{end}}{{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> geth.sh
ENTRYPOINT ["/bin/sh", "geth.sh"]
`
@ -66,17 +66,20 @@ services:
- LIGHT_PEERS={{.LightPeers}}
- STATS_NAME={{.Ethstats}}
- MINER_NAME={{.Etherbase}}
- GAS_TARGET={{.GasTarget}}
- GAS_PRICE={{.GasPrice}}
restart: always
`
// deployNode deploys a new Ethereum node container to a remote machine via SSH,
// docker and docker-compose. If an instance with the specified network name
// already exists there, it will be overwritten!
func deployNode(client *sshClient, network string, bootnodes []string, config *nodeInfos) ([]byte, error) {
func deployNode(client *sshClient, network string, bootv4, bootv5 []string, config *nodeInfos) ([]byte, error) {
kind := "sealnode"
if config.keyJSON == "" && config.etherbase == "" {
kind = "bootnode"
bootnodes = make([]string, 0)
bootv4 = make([]string, 0)
bootv5 = make([]string, 0)
}
// Generate the content to upload to the server
workdir := fmt.Sprintf("%d", rand.Int63())
@ -92,9 +95,12 @@ func deployNode(client *sshClient, network string, bootnodes []string, config *n
"Port": config.portFull,
"Peers": config.peersTotal,
"LightFlag": lightFlag,
"Bootnodes": strings.Join(bootnodes, ","),
"BootV4": strings.Join(bootv4, ","),
"BootV5": strings.Join(bootv5, ","),
"Ethstats": config.ethstats,
"Etherbase": config.etherbase,
"GasTarget": uint64(1000000 * config.gasTarget),
"GasPrice": uint64(1000000000 * config.gasPrice),
"Unlock": config.keyJSON != "",
})
files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes()
@ -111,6 +117,8 @@ func deployNode(client *sshClient, network string, bootnodes []string, config *n
"LightPeers": config.peersLight,
"Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")],
"Etherbase": config.etherbase,
"GasTarget": config.gasTarget,
"GasPrice": config.gasPrice,
})
files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
@ -127,7 +135,7 @@ func deployNode(client *sshClient, network string, bootnodes []string, config *n
}
defer client.Run("rm -rf " + workdir)
// Build and deploy the bootnode service
// Build and deploy the boot or seal node service
return nil, client.Stream(fmt.Sprintf("cd %s && docker-compose -p %s up -d --build", workdir, network))
}
@ -147,6 +155,8 @@ type nodeInfos struct {
etherbase string
keyJSON string
keyPass string
gasTarget float64
gasPrice float64
}
// String implements the stringer interface.
@ -155,7 +165,8 @@ func (info *nodeInfos) String() string {
if info.peersLight > 0 {
discv5 = fmt.Sprintf(", portv5=%d", info.portLight)
}
return fmt.Sprintf("port=%d%s, datadir=%s, peers=%d, lights=%d, ethstats=%s", info.portFull, discv5, info.datadir, info.peersTotal, info.peersLight, info.ethstats)
return fmt.Sprintf("port=%d%s, datadir=%s, peers=%d, lights=%d, ethstats=%s, gastarget=%0.3f MGas, gasprice=%0.3f GWei",
info.portFull, discv5, info.datadir, info.peersTotal, info.peersLight, info.ethstats, info.gasTarget, info.gasPrice)
}
// checkNode does a health-check against an boot or seal node server to verify
@ -176,6 +187,8 @@ func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error)
// Resolve a few types from the environmental variables
totalPeers, _ := strconv.Atoi(infos.envvars["TOTAL_PEERS"])
lightPeers, _ := strconv.Atoi(infos.envvars["LIGHT_PEERS"])
gasTarget, _ := strconv.ParseFloat(infos.envvars["GAS_TARGET"], 64)
gasPrice, _ := strconv.ParseFloat(infos.envvars["GAS_PRICE"], 64)
// Container available, retrieve its node ID and its genesis json
var out []byte
@ -213,6 +226,8 @@ func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error)
etherbase: infos.envvars["MINER_NAME"],
keyJSON: keyJSON,
keyPass: keyPass,
gasTarget: gasTarget,
gasPrice: gasPrice,
}
stats.enodeFull = fmt.Sprintf("enode://%s@%s:%d", id, client.address, stats.portFull)
if stats.portLight != 0 {

View File

@ -17,6 +17,8 @@
package main
import (
"bufio"
"bytes"
"errors"
"fmt"
"io/ioutil"
@ -37,18 +39,26 @@ import (
type sshClient struct {
server string // Server name or IP without port number
address string // IP address of the remote server
pubkey []byte // RSA public key to authenticate the server
client *ssh.Client
logger log.Logger
}
// dial establishes an SSH connection to a remote node using the current user and
// the user's configured private RSA key.
func dial(server string) (*sshClient, error) {
// the user's configured private RSA key. If that fails, password authentication
// is fallen back to. The caller may override the login user via user@server:port.
func dial(server string, pubkey []byte) (*sshClient, error) {
// Figure out a label for the server and a logger
label := server
if strings.Contains(label, ":") {
label = label[:strings.Index(label, ":")]
}
login := ""
if strings.Contains(server, "@") {
login = label[:strings.Index(label, "@")]
label = label[strings.Index(label, "@")+1:]
server = server[strings.Index(server, "@")+1:]
}
logger := log.New("server", label)
logger.Debug("Attempting to establish SSH connection")
@ -56,6 +66,9 @@ func dial(server string) (*sshClient, error) {
if err != nil {
return nil, err
}
if login == "" {
login = user.Username
}
// Configure the supported authentication methods (private key and password)
var auths []ssh.AuthMethod
@ -71,7 +84,7 @@ func dial(server string) (*sshClient, error) {
}
}
auths = append(auths, ssh.PasswordCallback(func() (string, error) {
fmt.Printf("What's the login password for %s at %s? (won't be echoed)\n> ", user.Username, server)
fmt.Printf("What's the login password for %s at %s? (won't be echoed)\n> ", login, server)
blob, err := terminal.ReadPassword(int(syscall.Stdin))
fmt.Println()
@ -86,11 +99,36 @@ func dial(server string) (*sshClient, error) {
return nil, errors.New("no IPs associated with domain")
}
// Try to dial in to the remote server
logger.Trace("Dialing remote SSH server", "user", user.Username, "key", path)
logger.Trace("Dialing remote SSH server", "user", login)
if !strings.Contains(server, ":") {
server += ":22"
}
client, err := ssh.Dial("tcp", server, &ssh.ClientConfig{User: user.Username, Auth: auths})
keycheck := func(hostname string, remote net.Addr, key ssh.PublicKey) error {
// If no public key is known for SSH, ask the user to confirm
if pubkey == nil {
fmt.Printf("The authenticity of host '%s (%s)' can't be established.\n", hostname, remote)
fmt.Printf("SSH key fingerprint is %s [MD5]\n", ssh.FingerprintLegacyMD5(key))
fmt.Printf("Are you sure you want to continue connecting (yes/no)? ")
text, err := bufio.NewReader(os.Stdin).ReadString('\n')
switch {
case err != nil:
return err
case strings.TrimSpace(text) == "yes":
pubkey = key.Marshal()
return nil
default:
return fmt.Errorf("unknown auth choice: %v", text)
}
}
// If a public key exists for this SSH server, check that it matches
if bytes.Compare(pubkey, key.Marshal()) == 0 {
return nil
}
// We have a mismatch, forbid connecting
return errors.New("ssh key mismatch, readd the machine to update")
}
client, err := ssh.Dial("tcp", server, &ssh.ClientConfig{User: login, Auth: auths, HostKeyCallback: keycheck})
if err != nil {
return nil, err
}
@ -98,6 +136,7 @@ func dial(server string) (*sshClient, error) {
c := &sshClient{
server: label,
address: addr[0],
pubkey: pubkey,
client: client,
logger: logger,
}

View File

@ -44,14 +44,24 @@ type config struct {
bootLight []string // Bootnodes to always connect to by light nodes
ethstats string // Ethstats settings to cache for node deploys
Servers []string `json:"servers,omitempty"`
Servers map[string][]byte `json:"servers,omitempty"`
}
// servers retrieves an alphabetically sorted list of servers.
func (c config) servers() []string {
servers := make([]string, 0, len(c.Servers))
for server := range c.Servers {
servers = append(servers, server)
}
sort.Strings(servers)
return servers
}
// flush dumps the contents of config to disk.
func (c config) flush() {
os.MkdirAll(filepath.Dir(c.path), 0755)
sort.Strings(c.Servers)
out, _ := json.MarshalIndent(c, "", " ")
if err := ioutil.WriteFile(c.path, out, 0644); err != nil {
log.Warn("Failed to save puppeth configs", "file", c.path, "err", err)
@ -152,6 +162,48 @@ func (w *wizard) readDefaultInt(def int) int {
}
}
// readFloat reads a single line from stdin, trimming if from spaces, enforcing it
// to parse into a float.
func (w *wizard) readFloat() float64 {
for {
fmt.Printf("> ")
text, err := w.in.ReadString('\n')
if err != nil {
log.Crit("Failed to read user input", "err", err)
}
if text = strings.TrimSpace(text); text == "" {
continue
}
val, err := strconv.ParseFloat(strings.TrimSpace(text), 64)
if err != nil {
log.Error("Invalid input, expected float", "err", err)
continue
}
return val
}
}
// readDefaultFloat reads a single line from stdin, trimming if from spaces, enforcing
// it to parse into a float. If an empty line is entered, the default value is returned.
func (w *wizard) readDefaultFloat(def float64) float64 {
for {
fmt.Printf("> ")
text, err := w.in.ReadString('\n')
if err != nil {
log.Crit("Failed to read user input", "err", err)
}
if text = strings.TrimSpace(text); text == "" {
return def
}
val, err := strconv.ParseFloat(strings.TrimSpace(text), 64)
if err != nil {
log.Error("Invalid input, expected float", "err", err)
continue
}
return val
}
}
// readPassword reads a single line from stdin, trimming it from the trailing new
// line and returns it. The input will not be echoed.
func (w *wizard) readPassword() string {

View File

@ -44,6 +44,7 @@ func (w *wizard) deployFaucet() {
host: client.server,
amount: 1,
minutes: 1440,
tiers: 3,
}
}
infos.node.genesis, _ = json.MarshalIndent(w.conf.genesis, "", " ")
@ -68,10 +69,17 @@ func (w *wizard) deployFaucet() {
fmt.Printf("How many minutes to enforce between requests? (default = %d)\n", infos.minutes)
infos.minutes = w.readDefaultInt(infos.minutes)
fmt.Println()
fmt.Printf("How many funding tiers to feature (x2.5 amounts, x3 timeout)? (default = %d)\n", infos.tiers)
infos.tiers = w.readDefaultInt(infos.tiers)
if infos.tiers == 0 {
log.Error("At least one funding tier must be set")
return
}
// Accessing GitHub gists requires API authorization, retrieve it
if infos.githubUser != "" {
fmt.Println()
fmt.Printf("Reused previous (%s) GitHub API authorization (y/n)? (default = yes)\n", infos.githubUser)
fmt.Printf("Reuse previous (%s) GitHub API authorization (y/n)? (default = yes)\n", infos.githubUser)
if w.readDefaultString("y") != "y" {
infos.githubUser, infos.githubToken = "", ""
}
@ -109,6 +117,29 @@ func (w *wizard) deployFaucet() {
return
}
}
// Accessing the reCaptcha service requires API authorizations, request it
if infos.captchaToken != "" {
fmt.Println()
fmt.Println("Reuse previous reCaptcha API authorization (y/n)? (default = yes)")
if w.readDefaultString("y") != "y" {
infos.captchaToken, infos.captchaSecret = "", ""
}
}
if infos.captchaToken == "" {
// No previous authorization (or old one discarded)
fmt.Println()
fmt.Println("Enable reCaptcha protection against robots (y/n)? (default = no)")
if w.readDefaultString("n") == "y" {
// Captcha protection explicitly requested, read the site and secret keys
fmt.Println()
fmt.Printf("What is the reCaptcha site key to authenticate human users?\n")
infos.captchaToken = w.readString()
fmt.Println()
fmt.Printf("What is the reCaptcha secret key to verify authentications? (won't be echoed)\n")
infos.captchaSecret = w.readPassword()
}
}
// Figure out where the user wants to store the persistent data
fmt.Println()
if infos.node.datadir == "" {
@ -134,8 +165,7 @@ func (w *wizard) deployFaucet() {
}
// Load up the credential needed to release funds
if infos.node.keyJSON != "" {
var key keystore.Key
if err := json.Unmarshal([]byte(infos.node.keyJSON), &key); err != nil {
if key, err := keystore.DecryptKey([]byte(infos.node.keyJSON), infos.node.keyPass); err != nil {
infos.node.keyJSON, infos.node.keyPass = "", ""
} else {
fmt.Println()

View File

@ -120,7 +120,7 @@ func (w *wizard) makeGenesis() {
// Query the user for some custom extras
fmt.Println()
fmt.Println("Specify your chain/network ID if you want an explicit one (default = random)")
genesis.Config.ChainId = big.NewInt(int64(w.readDefaultInt(rand.Intn(65536))))
genesis.Config.ChainId = new(big.Int).SetUint64(uint64(w.readDefaultInt(rand.Intn(65536))))
fmt.Println()
fmt.Println("Anything fun to embed into the genesis block? (max 32 bytes)")

View File

@ -31,7 +31,10 @@ import (
// makeWizard creates and returns a new puppeth wizard.
func makeWizard(network string) *wizard {
return &wizard{
network: network,
network: network,
conf: config{
Servers: make(map[string][]byte),
},
servers: make(map[string]*sshClient),
services: make(map[string][]string),
in: bufio.NewReader(os.Stdin),
@ -77,9 +80,9 @@ func (w *wizard) run() {
} else if err := json.Unmarshal(blob, &w.conf); err != nil {
log.Crit("Previous configuration corrupted", "path", w.conf.path, "err", err)
} else {
for _, server := range w.conf.Servers {
for server, pubkey := range w.conf.Servers {
log.Info("Dialing previously configured server", "server", server)
client, err := dial(server)
client, err := dial(server, pubkey)
if err != nil {
log.Error("Previous server unreachable", "server", server, "err", err)
}

View File

@ -39,16 +39,16 @@ func (w *wizard) networkStats(tips bool) {
// Iterate over all the specified hosts and check their status
stats := tablewriter.NewWriter(os.Stdout)
stats.SetHeader([]string{"Server", "IP", "Status", "Service", "Details"})
stats.SetColWidth(128)
stats.SetColWidth(100)
for _, server := range w.conf.Servers {
for server, pubkey := range w.conf.Servers {
client := w.servers[server]
logger := log.New("server", server)
logger.Info("Starting remote server health-check")
// If the server is not connected, try to connect again
if client == nil {
conn, err := dial(server)
conn, err := dial(server, pubkey)
if err != nil {
logger.Error("Failed to establish remote connection", "err", err)
stats.Append([]string{server, "", err.Error(), "", ""})

View File

@ -28,7 +28,9 @@ import (
func (w *wizard) manageServers() {
// List all the servers we can disconnect, along with an entry to connect a new one
fmt.Println()
for i, server := range w.conf.Servers {
servers := w.conf.servers()
for i, server := range servers {
fmt.Printf(" %d. Disconnect %s\n", i+1, server)
}
fmt.Printf(" %d. Connect another server\n", len(w.conf.Servers)+1)
@ -40,14 +42,14 @@ func (w *wizard) manageServers() {
}
// If the user selected an existing server, drop it
if choice <= len(w.conf.Servers) {
server := w.conf.Servers[choice-1]
server := servers[choice-1]
client := w.servers[server]
delete(w.servers, server)
if client != nil {
client.Close()
}
w.conf.Servers = append(w.conf.Servers[:choice-1], w.conf.Servers[choice:]...)
delete(w.conf.Servers, server)
w.conf.flush()
log.Info("Disconnected existing server", "server", server)
@ -73,14 +75,14 @@ func (w *wizard) makeServer() string {
// Read and fial the server to ensure docker is present
input := w.readString()
client, err := dial(input)
client, err := dial(input, nil)
if err != nil {
log.Error("Server not ready for puppeth", "err", err)
return ""
}
// All checks passed, start tracking the server
w.servers[input] = client
w.conf.Servers = append(w.conf.Servers, input)
w.conf.Servers[input] = client.pubkey
w.conf.flush()
return input
@ -93,7 +95,9 @@ func (w *wizard) selectServer() string {
// List the available server to the user and wait for a choice
fmt.Println()
fmt.Println("Which server do you want to interact with?")
for i, server := range w.conf.Servers {
servers := w.conf.servers()
for i, server := range servers {
fmt.Printf(" %d. %s\n", i+1, server)
}
fmt.Printf(" %d. Connect another server\n", len(w.conf.Servers)+1)
@ -105,7 +109,7 @@ func (w *wizard) selectServer() string {
}
// If the user requested connecting to a new server, go for it
if choice <= len(w.conf.Servers) {
return w.conf.Servers[choice-1]
return servers[choice-1]
}
return w.makeServer()
}

View File

@ -50,7 +50,7 @@ func (w *wizard) deployNode(boot bool) {
if boot {
infos = &nodeInfos{portFull: 30303, peersTotal: 512, peersLight: 256}
} else {
infos = &nodeInfos{portFull: 30303, peersTotal: 50, peersLight: 0}
infos = &nodeInfos{portFull: 30303, peersTotal: 50, peersLight: 0, gasTarget: 4.7, gasPrice: 18}
}
}
infos.genesis, _ = json.MarshalIndent(w.conf.genesis, "", " ")
@ -109,8 +109,7 @@ func (w *wizard) deployNode(boot bool) {
} else if w.conf.genesis.Config.Clique != nil {
// If a previous signer was already set, offer to reuse it
if infos.keyJSON != "" {
var key keystore.Key
if err := json.Unmarshal([]byte(infos.keyJSON), &key); err != nil {
if key, err := keystore.DecryptKey([]byte(infos.keyJSON), infos.keyPass); err != nil {
infos.keyJSON, infos.keyPass = "", ""
} else {
fmt.Println()
@ -136,9 +135,17 @@ func (w *wizard) deployNode(boot bool) {
}
}
}
// Establish the gas dynamics to be enforced by the signer
fmt.Println()
fmt.Printf("What gas limit should empty blocks target (MGas)? (default = %0.3f)\n", infos.gasTarget)
infos.gasTarget = w.readDefaultFloat(infos.gasTarget)
fmt.Println()
fmt.Printf("What gas price should the signer require (GWei)? (default = %0.3f)\n", infos.gasPrice)
infos.gasPrice = w.readDefaultFloat(infos.gasPrice)
}
// Try to deploy the full node on the host
if out, err := deployNode(client, w.network, w.conf.bootFull, infos); err != nil {
if out, err := deployNode(client, w.network, w.conf.bootFull, w.conf.bootLight, infos); err != nil {
log.Error("Failed to deploy Ethereum node container", "err", err)
if len(out) > 0 {
fmt.Printf("%s\n", out)

View File

@ -17,21 +17,25 @@
package main
import (
"context"
"crypto/ecdsa"
"fmt"
"io/ioutil"
"math/big"
"os"
"os/signal"
"runtime"
"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"
@ -40,6 +44,7 @@ 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"
"gopkg.in/urfave/cli.v1"
@ -67,6 +72,10 @@ var (
Name: "bzzaccount",
Usage: "Swarm account key file",
}
SwarmListenAddrFlag = cli.StringFlag{
Name: "httpaddr",
Usage: "Swarm HTTP API listening interface",
}
SwarmPortFlag = cli.StringFlag{
Name: "bzzport",
Usage: "Swarm local http api port",
@ -83,15 +92,23 @@ var (
Name: "swap",
Usage: "Swarm SWAP enabled (default false)",
}
SwarmSwapAPIFlag = cli.StringFlag{
Name: "swap-api",
Usage: "URL of the Ethereum API provider to use to settle SWAP payments",
}
SwarmSyncEnabledFlag = cli.BoolTFlag{
Name: "sync",
Usage: "Swarm Syncing enabled (default true)",
}
EthAPIFlag = cli.StringFlag{
Name: "ethapi",
Usage: "URL of the Ethereum API provider",
EnsAPIFlag = cli.StringFlag{
Name: "ens-api",
Usage: "URL of the Ethereum API provider to use for ENS record lookups",
Value: node.DefaultIPCEndpoint("geth"),
}
EnsAddrFlag = cli.StringFlag{
Name: "ens-addr",
Usage: "ENS contract address (default is detected as testnet or mainnet using --ens-api)",
}
SwarmApiFlag = cli.StringFlag{
Name: "bzzapi",
Usage: "Swarm HTTP endpoint",
@ -121,6 +138,12 @@ var (
Name: "corsdomain",
Usage: "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')",
}
// the following flags are deprecated and should be removed in the future
DeprecatedEthAPIFlag = cli.StringFlag{
Name: "ethapi",
Usage: "DEPRECATED: please use --ens-api and --swap-api",
}
)
var defaultNodeConfig = node.DefaultConfig
@ -245,10 +268,13 @@ Cleans database of corrupted entries.
utils.PasswordFileFlag,
// bzzd-specific flags
CorsStringFlag,
EthAPIFlag,
EnsAPIFlag,
EnsAddrFlag,
SwarmConfigPathFlag,
SwarmSwapEnabledFlag,
SwarmSwapAPIFlag,
SwarmSyncEnabledFlag,
SwarmListenAddrFlag,
SwarmPortFlag,
SwarmAccountFlag,
SwarmNetworkIdFlag,
@ -260,6 +286,8 @@ Cleans database of corrupted entries.
SwarmUploadDefaultPath,
SwarmUpFromStdinFlag,
SwarmUploadMimeType,
//deprecated flags
DeprecatedEthAPIFlag,
}
app.Flags = append(app.Flags, debug.Flags...)
app.Before = func(ctx *cli.Context) error {
@ -294,6 +322,11 @@ func version(ctx *cli.Context) error {
}
func bzzd(ctx *cli.Context) error {
// exit if the deprecated --ethapi flag is set
if ctx.GlobalString(DeprecatedEthAPIFlag.Name) != "" {
utils.Fatalf("--ethapi is no longer a valid command line flag, please use --ens-api and/or --swap-api.")
}
cfg := defaultNodeConfig
utils.SetNodeConfig(ctx, &cfg)
stack, err := node.New(&cfg)
@ -328,6 +361,38 @@ 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(ctx *cli.Context, stack *node.Node) {
prvkey := getAccount(ctx, stack)
@ -345,23 +410,54 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) {
if len(bzzport) > 0 {
bzzconfig.Port = bzzport
}
if bzzaddr := ctx.GlobalString(SwarmListenAddrFlag.Name); bzzaddr != "" {
bzzconfig.ListenAddr = bzzaddr
}
swapEnabled := ctx.GlobalBool(SwarmSwapEnabledFlag.Name)
syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabledFlag.Name)
ethapi := ctx.GlobalString(EthAPIFlag.Name)
swapapi := ctx.GlobalString(SwarmSwapAPIFlag.Name)
if swapEnabled && swapapi == "" {
utils.Fatalf("SWAP is enabled but --swap-api is not set")
}
ensapi := ctx.GlobalString(EnsAPIFlag.Name)
ensAddr := ctx.GlobalString(EnsAddrFlag.Name)
cors := ctx.GlobalString(CorsStringFlag.Name)
boot := func(ctx *node.ServiceContext) (node.Service, error) {
var client *ethclient.Client
if len(ethapi) > 0 {
client, err = ethclient.Dial(ethapi)
var swapClient *ethclient.Client
if swapapi != "" {
log.Info("connecting to SWAP API", "url", swapapi)
swapClient, err = ethclient.Dial(swapapi)
if err != nil {
utils.Fatalf("Can't connect: %v", err)
return nil, fmt.Errorf("error connecting to SWAP API %s: %s", swapapi, err)
}
} else {
swapEnabled = false
}
return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled, cors)
var ensClient *ethclient.Client
if ensapi != "" {
log.Info("connecting to ENS API", "url", ensapi)
client, err := rpc.Dial(ensapi)
if err != nil {
return nil, fmt.Errorf("error connecting to ENS API %s: %s", ensapi, err)
}
ensClient = ethclient.NewClient(client)
if ensAddr != "" {
bzzconfig.EnsRoot = common.HexToAddress(ensAddr)
} else {
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, swapEnabled, syncEnabled, cors)
}
if err := stack.Register(boot); err != nil {
utils.Fatalf("Failed to register the Swarm service: %v", err)

255
cmd/swarm/run_test.go Normal file
View File

@ -0,0 +1,255 @@
// Copyright 2016 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/>.
package main
import (
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"runtime"
"testing"
"time"
"github.com/docker/docker/pkg/reexec"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/internal/cmdtest"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm"
)
func init() {
// Run the app if we've been exec'd as "swarm-test" in runSwarm.
reexec.Register("swarm-test", func() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Exit(0)
})
}
func TestMain(m *testing.M) {
// check if we have been reexec'd
if reexec.Init() {
return
}
os.Exit(m.Run())
}
func runSwarm(t *testing.T, args ...string) *cmdtest.TestCmd {
tt := cmdtest.NewTestCmd(t, nil)
// Boot "swarm". This actually runs the test binary but the TestMain
// function will prevent any tests from running.
tt.Run("swarm-test", args...)
return tt
}
type testCluster struct {
Nodes []*testNode
TmpDir string
}
// newTestCluster starts a test swarm cluster of the given size.
//
// A temporary directory is created and each node gets a data directory inside
// it.
//
// Each node listens on 127.0.0.1 with random ports for both the HTTP and p2p
// ports (assigned by first listening on 127.0.0.1:0 and then passing the ports
// as flags).
//
// When starting more than one node, they are connected together using the
// admin SetPeer RPC method.
func newTestCluster(t *testing.T, size int) *testCluster {
cluster := &testCluster{}
defer func() {
if t.Failed() {
cluster.Shutdown()
}
}()
tmpdir, err := ioutil.TempDir("", "swarm-test")
if err != nil {
t.Fatal(err)
}
cluster.TmpDir = tmpdir
// start the nodes
cluster.Nodes = make([]*testNode, 0, size)
for i := 0; i < size; i++ {
dir := filepath.Join(cluster.TmpDir, fmt.Sprintf("swarm%02d", i))
if err := os.Mkdir(dir, 0700); err != nil {
t.Fatal(err)
}
node := newTestNode(t, dir)
node.Name = fmt.Sprintf("swarm%02d", i)
cluster.Nodes = append(cluster.Nodes, node)
}
if size == 1 {
return cluster
}
// connect the nodes together
for _, node := range cluster.Nodes {
if err := node.Client.Call(nil, "admin_addPeer", cluster.Nodes[0].Enode); err != nil {
t.Fatal(err)
}
}
// wait until all nodes have the correct number of peers
outer:
for _, node := range cluster.Nodes {
var peers []*p2p.PeerInfo
for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(50 * time.Millisecond) {
if err := node.Client.Call(&peers, "admin_peers"); err != nil {
t.Fatal(err)
}
if len(peers) == len(cluster.Nodes)-1 {
continue outer
}
}
t.Fatalf("%s only has %d / %d peers", node.Name, len(peers), len(cluster.Nodes)-1)
}
return cluster
}
func (c *testCluster) Shutdown() {
for _, node := range c.Nodes {
node.Shutdown()
}
os.RemoveAll(c.TmpDir)
}
type testNode struct {
Name string
Addr string
URL string
Enode string
Dir string
Client *rpc.Client
Cmd *cmdtest.TestCmd
}
const testPassphrase = "swarm-test-passphrase"
func newTestNode(t *testing.T, dir string) *testNode {
// create key
conf := &node.Config{
DataDir: dir,
IPCPath: "bzzd.ipc",
}
n, err := node.New(conf)
if err != nil {
t.Fatal(err)
}
account, err := n.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore).NewAccount(testPassphrase)
if err != nil {
t.Fatal(err)
}
node := &testNode{Dir: dir}
// use a unique IPCPath when running tests on Windows
if runtime.GOOS == "windows" {
conf.IPCPath = fmt.Sprintf("bzzd-%s.ipc", account.Address.String())
}
// assign ports
httpPort, err := assignTCPPort()
if err != nil {
t.Fatal(err)
}
p2pPort, err := assignTCPPort()
if err != nil {
t.Fatal(err)
}
// start the node
node.Cmd = runSwarm(t,
"--port", p2pPort,
"--nodiscover",
"--datadir", dir,
"--ipcpath", conf.IPCPath,
"--ens-api", "",
"--bzzaccount", account.Address.String(),
"--bzznetworkid", "321",
"--bzzport", httpPort,
"--verbosity", "6",
)
node.Cmd.InputLine(testPassphrase)
defer func() {
if t.Failed() {
node.Shutdown()
}
}()
// wait for the node to start
for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) {
node.Client, err = rpc.Dial(conf.IPCEndpoint())
if err == nil {
break
}
}
if node.Client == nil {
t.Fatal(err)
}
// load info
var info swarm.Info
if err := node.Client.Call(&info, "bzz_info"); err != nil {
t.Fatal(err)
}
node.Addr = net.JoinHostPort("127.0.0.1", info.Port)
node.URL = "http://" + node.Addr
var nodeInfo p2p.NodeInfo
if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
t.Fatal(err)
}
node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s", nodeInfo.ID, p2pPort)
return node
}
func (n *testNode) Shutdown() {
if n.Cmd != nil {
n.Cmd.Kill()
}
}
func assignTCPPort() (string, error) {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return "", err
}
l.Close()
_, port, err := net.SplitHostPort(l.Addr().String())
if err != nil {
return "", err
}
return port, nil
}

View File

@ -18,6 +18,7 @@
package main
import (
"errors"
"fmt"
"io"
"io/ioutil"
@ -87,24 +88,32 @@ func upload(ctx *cli.Context) {
if err != nil {
utils.Fatalf("Error opening file: %s", err)
}
var hash string
// define a function which either uploads a directory or single file
// based on the type of the file being uploaded
var doUpload func() (hash string, err error)
if stat.IsDir() {
if !recursive {
utils.Fatalf("Argument is a directory and recursive upload is disabled")
doUpload = func() (string, error) {
if !recursive {
return "", errors.New("Argument is a directory and recursive upload is disabled")
}
return client.UploadDirectory(file, defaultPath, "")
}
hash, err = client.UploadDirectory(file, defaultPath, "")
} else {
if mimeType == "" {
mimeType = detectMimeType(file)
doUpload = func() (string, error) {
f, err := swarm.Open(file)
if err != nil {
return "", fmt.Errorf("error opening file: %s", err)
}
defer f.Close()
if mimeType == "" {
mimeType = detectMimeType(file)
}
f.ContentType = mimeType
return client.Upload(f, "")
}
f, err := swarm.Open(file)
if err != nil {
utils.Fatalf("Error opening file: %s", err)
}
defer f.Close()
f.ContentType = mimeType
hash, err = client.Upload(f, "")
}
hash, err := doUpload()
if err != nil {
utils.Fatalf("Upload failed: %s", err)
}

78
cmd/swarm/upload_test.go Normal file
View File

@ -0,0 +1,78 @@
// Copyright 2016 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/>.
package main
import (
"io"
"io/ioutil"
"net/http"
"os"
"testing"
)
// TestCLISwarmUp tests that running 'swarm up' makes the resulting file
// available from all nodes via the HTTP API
func TestCLISwarmUp(t *testing.T) {
t.Skip("flaky test")
// start 3 node cluster
t.Log("starting 3 node cluster")
cluster := newTestCluster(t, 3)
defer cluster.Shutdown()
// create a tmp file
tmp, err := ioutil.TempFile("", "swarm-test")
assertNil(t, err)
defer tmp.Close()
defer os.Remove(tmp.Name())
_, err = io.WriteString(tmp, "data")
assertNil(t, err)
// upload the file with 'swarm up' and expect a hash
t.Log("uploading file with 'swarm up'")
up := runSwarm(t, "--bzzapi", cluster.Nodes[0].URL, "up", tmp.Name())
_, matches := up.ExpectRegexp(`[a-f\d]{64}`)
up.ExpectExit()
hash := matches[0]
t.Logf("file uploaded with hash %s", hash)
// get the file from the HTTP API of each node
for _, node := range cluster.Nodes {
t.Logf("getting file from %s", node.Name)
res, err := http.Get(node.URL + "/bzz:/" + hash)
assertNil(t, err)
assertHTTPResponse(t, res, http.StatusOK, "data")
}
}
func assertNil(t *testing.T, err error) {
if err != nil {
t.Fatal(err)
}
}
func assertHTTPResponse(t *testing.T, res *http.Response, expectedStatus int, expectedBody string) {
defer res.Body.Close()
if res.StatusCode != expectedStatus {
t.Fatalf("expected HTTP status %d, got %s", expectedStatus, res.Status)
}
data, err := ioutil.ReadAll(res.Body)
assertNil(t, err)
if string(data) != expectedBody {
t.Fatalf("expected HTTP body %q, got %q", expectedBody, data)
}
}

View File

@ -52,10 +52,23 @@ 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/whisperv2"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
"gopkg.in/urfave/cli.v1"
)
var (
CommandHelpTemplate = `{{.cmd.Name}}{{if .cmd.Subcommands}} command{{end}}{{if .cmd.Flags}} [command options]{{end}} [arguments...]
{{if .cmd.Description}}{{.cmd.Description}}
{{end}}{{if .cmd.Subcommands}}
SUBCOMMANDS:
{{range .cmd.Subcommands}}{{.cmd.Name}}{{with .cmd.ShortName}}, {{.cmd}}{{end}}{{ "\t" }}{{.cmd.Usage}}
{{end}}{{end}}{{if .categorizedFlags}}
{{range $idx, $categorized := .categorizedFlags}}{{$categorized.Name}} OPTIONS:
{{range $categorized.Flags}}{{"\t"}}{{.}}
{{end}}
{{end}}{{end}}`
)
func init() {
cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...]
@ -70,16 +83,7 @@ GLOBAL OPTIONS:
{{end}}{{end}}
`
cli.CommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
{{if .Description}}{{.Description}}
{{end}}{{if .Subcommands}}
SUBCOMMANDS:
{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
{{end}}{{end}}{{if .Flags}}
OPTIONS:
{{range .Flags}}{{.}}
{{end}}{{end}}
`
cli.CommandHelpTemplate = CommandHelpTemplate
}
// NewApp creates an app with sane defaults.
@ -115,44 +119,23 @@ var (
Name: "keystore",
Usage: "Directory for the keystore (default = inside the datadir)",
}
EthashCacheDirFlag = DirectoryFlag{
Name: "ethash.cachedir",
Usage: "Directory to store the ethash verification caches (default = inside the datadir)",
NoUSBFlag = cli.BoolFlag{
Name: "nousb",
Usage: "Disables monitoring for and managine USB hardware wallets",
}
EthashCachesInMemoryFlag = cli.IntFlag{
Name: "ethash.cachesinmem",
Usage: "Number of recent ethash caches to keep in memory (16MB each)",
Value: eth.DefaultConfig.EthashCachesInMem,
}
EthashCachesOnDiskFlag = cli.IntFlag{
Name: "ethash.cachesondisk",
Usage: "Number of recent ethash caches to keep on disk (16MB each)",
Value: eth.DefaultConfig.EthashCachesOnDisk,
}
EthashDatasetDirFlag = DirectoryFlag{
Name: "ethash.dagdir",
Usage: "Directory to store the ethash mining DAGs (default = inside home folder)",
Value: DirectoryString{eth.DefaultConfig.EthashDatasetDir},
}
EthashDatasetsInMemoryFlag = cli.IntFlag{
Name: "ethash.dagsinmem",
Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)",
Value: eth.DefaultConfig.EthashDatasetsInMem,
}
EthashDatasetsOnDiskFlag = cli.IntFlag{
Name: "ethash.dagsondisk",
Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)",
Value: eth.DefaultConfig.EthashDatasetsOnDisk,
}
NetworkIdFlag = cli.IntFlag{
NetworkIdFlag = cli.Uint64Flag{
Name: "networkid",
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)",
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby)",
Value: eth.DefaultConfig.NetworkId,
}
TestNetFlag = cli.BoolFlag{
TestnetFlag = cli.BoolFlag{
Name: "testnet",
Usage: "Ropsten network: pre-configured proof-of-work test network",
}
RinkebyFlag = cli.BoolFlag{
Name: "rinkeby",
Usage: "Rinkeby network: pre-configured proof-of-authority test network",
}
DevModeFlag = cli.BoolFlag{
Name: "dev",
Usage: "Developer mode: pre-configured private network with several debugging flags",
@ -195,6 +178,76 @@ var (
Name: "lightkdf",
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
}
// Ethash settings
EthashCacheDirFlag = DirectoryFlag{
Name: "ethash.cachedir",
Usage: "Directory to store the ethash verification caches (default = inside the datadir)",
}
EthashCachesInMemoryFlag = cli.IntFlag{
Name: "ethash.cachesinmem",
Usage: "Number of recent ethash caches to keep in memory (16MB each)",
Value: eth.DefaultConfig.EthashCachesInMem,
}
EthashCachesOnDiskFlag = cli.IntFlag{
Name: "ethash.cachesondisk",
Usage: "Number of recent ethash caches to keep on disk (16MB each)",
Value: eth.DefaultConfig.EthashCachesOnDisk,
}
EthashDatasetDirFlag = DirectoryFlag{
Name: "ethash.dagdir",
Usage: "Directory to store the ethash mining DAGs (default = inside home folder)",
Value: DirectoryString{eth.DefaultConfig.EthashDatasetDir},
}
EthashDatasetsInMemoryFlag = cli.IntFlag{
Name: "ethash.dagsinmem",
Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)",
Value: eth.DefaultConfig.EthashDatasetsInMem,
}
EthashDatasetsOnDiskFlag = cli.IntFlag{
Name: "ethash.dagsondisk",
Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)",
Value: eth.DefaultConfig.EthashDatasetsOnDisk,
}
// Transaction pool settings
TxPoolNoLocalsFlag = cli.BoolFlag{
Name: "txpool.nolocals",
Usage: "Disables price exemptions for locally submitted transactions",
}
TxPoolPriceLimitFlag = cli.Uint64Flag{
Name: "txpool.pricelimit",
Usage: "Minimum gas price limit to enforce for acceptance into the pool",
Value: eth.DefaultConfig.TxPool.PriceLimit,
}
TxPoolPriceBumpFlag = cli.Uint64Flag{
Name: "txpool.pricebump",
Usage: "Price bump percentage to replace an already existing transaction",
Value: eth.DefaultConfig.TxPool.PriceBump,
}
TxPoolAccountSlotsFlag = cli.Uint64Flag{
Name: "txpool.accountslots",
Usage: "Minimum number of executable transaction slots guaranteed per account",
Value: eth.DefaultConfig.TxPool.AccountSlots,
}
TxPoolGlobalSlotsFlag = cli.Uint64Flag{
Name: "txpool.globalslots",
Usage: "Maximum number of executable transaction slots for all accounts",
Value: eth.DefaultConfig.TxPool.GlobalSlots,
}
TxPoolAccountQueueFlag = cli.Uint64Flag{
Name: "txpool.accountqueue",
Usage: "Maximum number of non-executable transaction slots permitted per account",
Value: eth.DefaultConfig.TxPool.AccountQueue,
}
TxPoolGlobalQueueFlag = cli.Uint64Flag{
Name: "txpool.globalqueue",
Usage: "Maximum number of non-executable transaction slots for all accounts",
Value: eth.DefaultConfig.TxPool.GlobalQueue,
}
TxPoolLifetimeFlag = cli.DurationFlag{
Name: "txpool.lifetime",
Usage: "Maximum amount of time non-executable transaction are queued",
Value: eth.DefaultConfig.TxPool.Lifetime,
}
// Performance tuning settings
CacheFlag = cli.IntFlag{
Name: "cache",
@ -229,7 +282,7 @@ var (
GasPriceFlag = BigFlag{
Name: "gasprice",
Usage: "Minimal gas price to accept for mining a transactions",
Value: big.NewInt(20 * params.Shannon),
Value: eth.DefaultConfig.GasPrice,
}
ExtraDataFlag = cli.StringFlag{
Name: "extradata",
@ -327,7 +380,7 @@ var (
}
ExecFlag = cli.StringFlag{
Name: "exec",
Usage: "Execute JavaScript statement (only in combination with console/attach)",
Usage: "Execute JavaScript statement",
}
PreloadJSFlag = cli.StringFlag{
Name: "preload",
@ -352,7 +405,17 @@ var (
}
BootnodesFlag = cli.StringFlag{
Name: "bootnodes",
Usage: "Comma separated enode URLs for P2P discovery bootstrap",
Usage: "Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers)",
Value: "",
}
BootnodesV4Flag = cli.StringFlag{
Name: "bootnodesv4",
Usage: "Comma separated enode URLs for P2P v4 discovery bootstrap (light server, full nodes)",
Value: "",
}
BootnodesV5Flag = cli.StringFlag{
Name: "bootnodesv5",
Usage: "Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes)",
Value: "",
}
NodeKeyFileFlag = cli.StringFlag{
@ -381,11 +444,6 @@ var (
Usage: "Restricts network communication to the given IP networks (CIDR masks)",
}
WhisperEnabledFlag = cli.BoolFlag{
Name: "shh",
Usage: "Enable Whisper",
}
// ATM the url is left to the user and deployment to
JSpathFlag = cli.StringFlag{
Name: "jspath",
@ -404,6 +462,20 @@ var (
Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices",
Value: eth.DefaultConfig.GPO.Percentile,
}
WhisperEnabledFlag = cli.BoolFlag{
Name: "shh",
Usage: "Enable Whisper",
}
WhisperMaxMessageSizeFlag = cli.IntFlag{
Name: "shh.maxmessagesize",
Usage: "Max message size accepted",
Value: int(whisper.DefaultMaxMessageSize),
}
WhisperMinPOWFlag = cli.Float64Flag{
Name: "shh.pow",
Usage: "Minimum POW accepted",
Value: whisper.DefaultMinimumPoW,
}
)
// MakeDataDir retrieves the currently requested data directory, terminating
@ -411,10 +483,12 @@ var (
// the a subdirectory of the specified datadir will be used.
func MakeDataDir(ctx *cli.Context) string {
if path := ctx.GlobalString(DataDirFlag.Name); path != "" {
// TODO: choose a different location outside of the regular datadir.
if ctx.GlobalBool(TestNetFlag.Name) {
if ctx.GlobalBool(TestnetFlag.Name) {
return filepath.Join(path, "testnet")
}
if ctx.GlobalBool(RinkebyFlag.Name) {
return filepath.Join(path, "rinkeby")
}
return path
}
Fatalf("Cannot determine default data directory, please set manually (--datadir)")
@ -458,10 +532,17 @@ func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
// flags, reverting to pre-configured ones if none have been specified.
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
urls := params.MainnetBootnodes
if ctx.GlobalIsSet(BootnodesFlag.Name) {
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
} else if ctx.GlobalBool(TestNetFlag.Name) {
switch {
case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(BootnodesV4Flag.Name):
if ctx.GlobalIsSet(BootnodesV4Flag.Name) {
urls = strings.Split(ctx.GlobalString(BootnodesV4Flag.Name), ",")
} else {
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
}
case ctx.GlobalBool(TestnetFlag.Name):
urls = params.TestnetBootnodes
case ctx.GlobalBool(RinkebyFlag.Name):
urls = params.RinkebyBootnodes
}
cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls))
@ -479,9 +560,16 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
// flags, reverting to pre-configured ones if none have been specified.
func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
urls := params.DiscoveryV5Bootnodes
if ctx.GlobalIsSet(BootnodesFlag.Name) {
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
} else if cfg.BootstrapNodesV5 == nil {
switch {
case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(BootnodesV5Flag.Name):
if ctx.GlobalIsSet(BootnodesV5Flag.Name) {
urls = strings.Split(ctx.GlobalString(BootnodesV5Flag.Name), ",")
} else {
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
}
case ctx.GlobalBool(RinkebyFlag.Name):
urls = params.RinkebyV5Bootnodes
case cfg.BootstrapNodesV5 != nil:
return // already set, don't apply defaults.
}
@ -643,7 +731,7 @@ func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) {
}
}
// MakePasswordList reads password lines from the file specified by --password.
// MakePasswordList reads password lines from the file specified by the global --password flag.
func MakePasswordList(ctx *cli.Context) []string {
path := ctx.GlobalString(PasswordFileFlag.Name)
if path == "" {
@ -701,6 +789,7 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
// --dev mode can't use p2p networking.
cfg.MaxPeers = 0
cfg.ListenAddr = ":0"
cfg.DiscoveryV5Addr = ":0"
cfg.NoDiscovery = true
cfg.DiscoveryV5 = false
}
@ -719,8 +808,10 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
cfg.DataDir = ctx.GlobalString(DataDirFlag.Name)
case ctx.GlobalBool(DevModeFlag.Name):
cfg.DataDir = filepath.Join(os.TempDir(), "ethereum_dev_mode")
case ctx.GlobalBool(TestNetFlag.Name):
case ctx.GlobalBool(TestnetFlag.Name):
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet")
case ctx.GlobalBool(RinkebyFlag.Name):
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
}
if ctx.GlobalIsSet(KeyStoreDirFlag.Name) {
@ -729,6 +820,9 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
if ctx.GlobalIsSet(LightKDFFlag.Name) {
cfg.UseLightweightKDF = ctx.GlobalBool(LightKDFFlag.Name)
}
if ctx.GlobalIsSet(NoUSBFlag.Name) {
cfg.NoUSB = ctx.GlobalBool(NoUSBFlag.Name)
}
}
func setGPO(ctx *cli.Context, cfg *gasprice.Config) {
@ -740,6 +834,33 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config) {
}
}
func setTxPool(ctx *cli.Context, cfg *core.TxPoolConfig) {
if ctx.GlobalIsSet(TxPoolNoLocalsFlag.Name) {
cfg.NoLocals = ctx.GlobalBool(TxPoolNoLocalsFlag.Name)
}
if ctx.GlobalIsSet(TxPoolPriceLimitFlag.Name) {
cfg.PriceLimit = ctx.GlobalUint64(TxPoolPriceLimitFlag.Name)
}
if ctx.GlobalIsSet(TxPoolPriceBumpFlag.Name) {
cfg.PriceBump = ctx.GlobalUint64(TxPoolPriceBumpFlag.Name)
}
if ctx.GlobalIsSet(TxPoolAccountSlotsFlag.Name) {
cfg.AccountSlots = ctx.GlobalUint64(TxPoolAccountSlotsFlag.Name)
}
if ctx.GlobalIsSet(TxPoolGlobalSlotsFlag.Name) {
cfg.GlobalSlots = ctx.GlobalUint64(TxPoolGlobalSlotsFlag.Name)
}
if ctx.GlobalIsSet(TxPoolAccountQueueFlag.Name) {
cfg.AccountQueue = ctx.GlobalUint64(TxPoolAccountQueueFlag.Name)
}
if ctx.GlobalIsSet(TxPoolGlobalQueueFlag.Name) {
cfg.GlobalQueue = ctx.GlobalUint64(TxPoolGlobalQueueFlag.Name)
}
if ctx.GlobalIsSet(TxPoolLifetimeFlag.Name) {
cfg.Lifetime = ctx.GlobalDuration(TxPoolLifetimeFlag.Name)
}
}
func setEthash(ctx *cli.Context, cfg *eth.Config) {
if ctx.GlobalIsSet(EthashCacheDirFlag.Name) {
cfg.EthashCacheDir = ctx.GlobalString(EthashCacheDirFlag.Name)
@ -773,15 +894,26 @@ func checkExclusive(ctx *cli.Context, flags ...cli.Flag) {
}
}
// SetShhConfig applies shh-related command line flags to the config.
func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
if ctx.GlobalIsSet(WhisperMaxMessageSizeFlag.Name) {
cfg.MaxMessageSize = uint32(ctx.GlobalUint(WhisperMaxMessageSizeFlag.Name))
}
if ctx.GlobalIsSet(WhisperMinPOWFlag.Name) {
cfg.MinimumAcceptedPOW = ctx.GlobalFloat64(WhisperMinPOWFlag.Name)
}
}
// SetEthConfig applies eth-related command line flags to the config.
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
// Avoid conflicting network flags
checkExclusive(ctx, DevModeFlag, TestNetFlag)
checkExclusive(ctx, DevModeFlag, TestnetFlag, RinkebyFlag)
checkExclusive(ctx, FastSyncFlag, LightModeFlag, SyncModeFlag)
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
setEtherbase(ctx, ks, cfg)
setGPO(ctx, &cfg.GPO)
setTxPool(ctx, &cfg.TxPool)
setEthash(ctx, cfg)
switch {
@ -799,7 +931,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name)
}
if ctx.GlobalIsSet(NetworkIdFlag.Name) {
cfg.NetworkId = ctx.GlobalInt(NetworkIdFlag.Name)
cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
}
// Ethereum needs to know maxPeers to calculate the light server peer ratio.
@ -828,13 +960,18 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name)
}
// Override any default configs for --dev and --testnet.
// Override any default configs for hard coded networks.
switch {
case ctx.GlobalBool(TestNetFlag.Name):
case ctx.GlobalBool(TestnetFlag.Name):
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 3
}
cfg.Genesis = core.DefaultTestnetGenesisBlock()
case ctx.GlobalBool(RinkebyFlag.Name):
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 4
}
cfg.Genesis = core.DefaultRinkebyGenesisBlock()
case ctx.GlobalBool(DevModeFlag.Name):
cfg.Genesis = core.DevGenesisBlock()
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
@ -872,8 +1009,10 @@ func RegisterEthService(stack *node.Node, cfg *eth.Config) {
}
// RegisterShhService configures Whisper and adds it to the given node.
func RegisterShhService(stack *node.Node) {
if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
func RegisterShhService(stack *node.Node, cfg *whisper.Config) {
if err := stack.Register(func(n *node.ServiceContext) (node.Service, error) {
return whisper.New(cfg), nil
}); err != nil {
Fatalf("Failed to register the Whisper service: %v", err)
}
}
@ -901,22 +1040,16 @@ func SetupNetwork(ctx *cli.Context) {
params.TargetGasLimit = new(big.Int).SetUint64(ctx.GlobalUint64(TargetGasLimitFlag.Name))
}
func ChainDbName(ctx *cli.Context) string {
if ctx.GlobalBool(LightModeFlag.Name) {
return "lightchaindata"
} else {
return "chaindata"
}
}
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
var (
cache = ctx.GlobalInt(CacheFlag.Name)
handles = makeDatabaseHandles()
name = ChainDbName(ctx)
)
name := "chaindata"
if ctx.GlobalBool(LightModeFlag.Name) {
name = "lightchaindata"
}
chainDb, err := stack.OpenDatabase(name, cache, handles)
if err != nil {
Fatalf("Could not open database: %v", err)
@ -927,8 +1060,10 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
func MakeGenesis(ctx *cli.Context) *core.Genesis {
var genesis *core.Genesis
switch {
case ctx.GlobalBool(TestNetFlag.Name):
case ctx.GlobalBool(TestnetFlag.Name):
genesis = core.DefaultTestnetGenesisBlock()
case ctx.GlobalBool(RinkebyFlag.Name):
genesis = core.DefaultRinkebyGenesisBlock()
case ctx.GlobalBool(DevModeFlag.Name):
genesis = core.DevGenesisBlock()
}
@ -972,3 +1107,27 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
}
return preloads
}
// MigrateFlags sets the global flag from a local flag when it's set.
// This is a temporary function used for migrating old command/flags to the
// new format.
//
// e.g. geth account new --keystore /tmp/mykeystore --lightkdf
//
// is equivalent after calling this method with:
//
// geth --keystore /tmp/mykeystore --lightkdf account new
//
// This allows the use of the existing configuration functionality.
// When all flags are migrated this function can be removed and the existing
// configuration functionality must be changed that is uses local flags
func MigrateFlags(action func(ctx *cli.Context) error) func(*cli.Context) error {
return func(ctx *cli.Context) error {
for _, name := range ctx.FlagNames() {
if ctx.IsSet(name) {
ctx.GlobalSet(name, ctx.String(name))
}
}
return action(ctx)
}
}

View File

@ -65,7 +65,7 @@ var (
pub *ecdsa.PublicKey
asymKey *ecdsa.PrivateKey
nodeid *ecdsa.PrivateKey
topic []byte
topic whisper.TopicType
asymKeyID string
filterID string
symPass string
@ -84,10 +84,10 @@ var (
testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics")
echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics")
argVerbosity = flag.Int("verbosity", int(log.LvlWarn), "log verbosity level")
argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level")
argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
argWorkTime = flag.Uint("work", 5, "work time in seconds")
argMaxSize = flag.Int("maxsize", whisper.DefaultMaxMessageLength, "max size of message")
argMaxSize = flag.Uint("maxsize", uint(whisper.DefaultMaxMessageSize), "max size of message")
argPoW = flag.Float64("pow", whisper.DefaultMinimumPoW, "PoW for normal messages in float format (e.g. 2.7)")
argServerPoW = flag.Float64("mspow", whisper.DefaultMinimumPoW, "PoW requirement for Mail Server request")
@ -129,7 +129,7 @@ func processArgs() {
if err != nil {
utils.Fatalf("Failed to parse the topic: %s", err)
}
topic = x
topic = whisper.BytesToTopic(x)
}
if *asymmetricMode && len(*argPub) > 0 {
@ -183,7 +183,7 @@ func initialize() {
if *testMode {
symPass = "wwww" // ascii code: 0x77777777
msPassword = "mail server test password"
msPassword = "wwww"
}
if *bootstrapMode {
@ -198,6 +198,11 @@ 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: ")
@ -205,11 +210,12 @@ func initialize() {
utils.Fatalf("Failed to read Mail Server password: %s", err)
}
}
shh = whisper.New()
shh = whisper.New(cfg)
shh.RegisterServer(&mailServer)
mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
} else {
shh = whisper.New()
shh = whisper.New(cfg)
}
if *argPoW != whisper.DefaultMinimumPoW {
@ -219,8 +225,8 @@ func initialize() {
}
}
if *argMaxSize != whisper.DefaultMaxMessageLength {
err := shh.SetMaxMessageLength(*argMaxSize)
if uint32(*argMaxSize) != whisper.DefaultMaxMessageSize {
err := shh.SetMaxMessageSize(uint32(*argMaxSize))
if err != nil {
utils.Fatalf("Failed to set max message size: %s", err)
}
@ -307,7 +313,11 @@ func configureNode() {
if *asymmetricMode {
if len(*argPub) == 0 {
s := scanLine("Please enter the peer's public key: ")
pub = crypto.ToECDSAPub(common.FromHex(s))
b := common.FromHex(s)
if b == nil {
utils.Fatalf("Error: can not convert hexadecimal string")
}
pub = crypto.ToECDSAPub(b)
if !isKeyValid(pub) {
utils.Fatalf("Error: invalid public key")
}
@ -326,7 +336,7 @@ func configureNode() {
if !*asymmetricMode && !*forwarderMode {
if len(symPass) == 0 {
symPass, err = console.Stdin.PromptPassword("Please enter the password: ")
symPass, err = console.Stdin.PromptPassword("Please enter the password for symmetric encryption: ")
if err != nil {
utils.Fatalf("Failed to read passphrase: %v", err)
}
@ -343,6 +353,8 @@ func configureNode() {
if len(*argTopic) == 0 {
generateTopic([]byte(symPass))
}
fmt.Printf("Filter is configured for the topic: %x \n", topic)
}
if *mailServerMode {
@ -354,18 +366,17 @@ func configureNode() {
filter := whisper.Filter{
KeySym: symKey,
KeyAsym: asymKey,
Topics: [][]byte{topic},
Topics: [][]byte{topic[:]},
AllowP2P: p2pAccept,
}
filterID, err = shh.Subscribe(&filter)
if err != nil {
utils.Fatalf("Failed to install filter: %s", err)
}
fmt.Printf("Filter is configured for the topic: %x \n", topic)
}
func generateTopic(password []byte) {
x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
x := pbkdf2.Key(password, password, 4096, 128, sha512.New)
for i := 0; i < len(x); i++ {
topic[i%whisper.TopicLength] ^= x[i]
}
@ -485,16 +496,15 @@ func sendMsg(payload []byte) common.Hash {
Dst: pub,
KeySym: symKey,
Payload: payload,
Topic: whisper.BytesToTopic(topic),
Topic: topic,
TTL: uint32(*argTTL),
PoW: *argPoW,
WorkTime: uint32(*argWorkTime),
}
msg := whisper.NewSentMessage(&params)
if msg == nil {
fmt.Printf("failed to create new message (OS level error)")
os.Exit(0)
msg, err := whisper.NewSentMessage(&params)
if err != nil {
utils.Fatalf("failed to create new message: %s", err)
}
envelope, err := msg.Wrap(&params)
if err != nil {
@ -624,9 +634,9 @@ func requestExpiredMessagesLoop() {
params.Src = nodeid
params.WorkTime = 5
msg := whisper.NewSentMessage(&params)
if msg == nil {
utils.Fatalf("failed to create new message (OS level error)")
msg, err := whisper.NewSentMessage(&params)
if err != nil {
utils.Fatalf("failed to create new message: %s", err)
}
env, err := msg.Wrap(&params)
if err != nil {

188
common/bitutil/bitutil.go Normal file
View File

@ -0,0 +1,188 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Adapted from: https://golang.org/src/crypto/cipher/xor.go
// Package bitutil implements fast bitwise operations.
package bitutil
import (
"runtime"
"unsafe"
)
const wordSize = int(unsafe.Sizeof(uintptr(0)))
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
// XORBytes xors the bytes in a and b. The destination is assumed to have enough
// space. Returns the number of bytes xor'd.
func XORBytes(dst, a, b []byte) int {
if supportsUnaligned {
return fastXORBytes(dst, a, b)
}
return safeXORBytes(dst, a, b)
}
// fastXORBytes xors in bulk. It only works on architectures that support
// unaligned read/writes.
func fastXORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
w := n / wordSize
if w > 0 {
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
aw := *(*[]uintptr)(unsafe.Pointer(&a))
bw := *(*[]uintptr)(unsafe.Pointer(&b))
for i := 0; i < w; i++ {
dw[i] = aw[i] ^ bw[i]
}
}
for i := (n - n%wordSize); i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
}
// safeXORBytes xors one by one. It works on all architectures, independent if
// it supports unaligned read/writes or not.
func safeXORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
for i := 0; i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
}
// ANDBytes ands the bytes in a and b. The destination is assumed to have enough
// space. Returns the number of bytes and'd.
func ANDBytes(dst, a, b []byte) int {
if supportsUnaligned {
return fastANDBytes(dst, a, b)
}
return safeANDBytes(dst, a, b)
}
// fastANDBytes ands in bulk. It only works on architectures that support
// unaligned read/writes.
func fastANDBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
w := n / wordSize
if w > 0 {
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
aw := *(*[]uintptr)(unsafe.Pointer(&a))
bw := *(*[]uintptr)(unsafe.Pointer(&b))
for i := 0; i < w; i++ {
dw[i] = aw[i] & bw[i]
}
}
for i := (n - n%wordSize); i < n; i++ {
dst[i] = a[i] & b[i]
}
return n
}
// safeANDBytes ands one by one. It works on all architectures, independent if
// it supports unaligned read/writes or not.
func safeANDBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
for i := 0; i < n; i++ {
dst[i] = a[i] & b[i]
}
return n
}
// ORBytes ors the bytes in a and b. The destination is assumed to have enough
// space. Returns the number of bytes or'd.
func ORBytes(dst, a, b []byte) int {
if supportsUnaligned {
return fastORBytes(dst, a, b)
}
return safeORBytes(dst, a, b)
}
// fastORBytes ors in bulk. It only works on architectures that support
// unaligned read/writes.
func fastORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
w := n / wordSize
if w > 0 {
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
aw := *(*[]uintptr)(unsafe.Pointer(&a))
bw := *(*[]uintptr)(unsafe.Pointer(&b))
for i := 0; i < w; i++ {
dw[i] = aw[i] | bw[i]
}
}
for i := (n - n%wordSize); i < n; i++ {
dst[i] = a[i] | b[i]
}
return n
}
// safeORBytes ors one by one. It works on all architectures, independent if
// it supports unaligned read/writes or not.
func safeORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
for i := 0; i < n; i++ {
dst[i] = a[i] | b[i]
}
return n
}
// TestBytes tests whether any bit is set in the input byte slice.
func TestBytes(p []byte) bool {
if supportsUnaligned {
return fastTestBytes(p)
}
return safeTestBytes(p)
}
// fastTestBytes tests for set bits in bulk. It only works on architectures that
// support unaligned read/writes.
func fastTestBytes(p []byte) bool {
n := len(p)
w := n / wordSize
if w > 0 {
pw := *(*[]uintptr)(unsafe.Pointer(&p))
for i := 0; i < w; i++ {
if pw[i] != 0 {
return true
}
}
}
for i := (n - n%wordSize); i < n; i++ {
if p[i] != 0 {
return true
}
}
return false
}
// safeTestBytes tests for set bits one byte at a time. It works on all
// architectures, independent if it supports unaligned read/writes or not.
func safeTestBytes(p []byte) bool {
for i := 0; i < len(p); i++ {
if p[i] != 0 {
return true
}
}
return false
}

View File

@ -0,0 +1,215 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Adapted from: https://golang.org/src/crypto/cipher/xor_test.go
package bitutil
import (
"bytes"
"testing"
)
// Tests that bitwise XOR works for various alignments.
func TestXOR(t *testing.T) {
for alignP := 0; alignP < 2; alignP++ {
for alignQ := 0; alignQ < 2; alignQ++ {
for alignD := 0; alignD < 2; alignD++ {
p := make([]byte, 1023)[alignP:]
q := make([]byte, 1023)[alignQ:]
for i := 0; i < len(p); i++ {
p[i] = byte(i)
}
for i := 0; i < len(q); i++ {
q[i] = byte(len(q) - i)
}
d1 := make([]byte, 1023+alignD)[alignD:]
d2 := make([]byte, 1023+alignD)[alignD:]
XORBytes(d1, p, q)
safeXORBytes(d2, p, q)
if !bytes.Equal(d1, d2) {
t.Error("not equal", d1, d2)
}
}
}
}
}
// Tests that bitwise AND works for various alignments.
func TestAND(t *testing.T) {
for alignP := 0; alignP < 2; alignP++ {
for alignQ := 0; alignQ < 2; alignQ++ {
for alignD := 0; alignD < 2; alignD++ {
p := make([]byte, 1023)[alignP:]
q := make([]byte, 1023)[alignQ:]
for i := 0; i < len(p); i++ {
p[i] = byte(i)
}
for i := 0; i < len(q); i++ {
q[i] = byte(len(q) - i)
}
d1 := make([]byte, 1023+alignD)[alignD:]
d2 := make([]byte, 1023+alignD)[alignD:]
ANDBytes(d1, p, q)
safeANDBytes(d2, p, q)
if !bytes.Equal(d1, d2) {
t.Error("not equal")
}
}
}
}
}
// Tests that bitwise OR works for various alignments.
func TestOR(t *testing.T) {
for alignP := 0; alignP < 2; alignP++ {
for alignQ := 0; alignQ < 2; alignQ++ {
for alignD := 0; alignD < 2; alignD++ {
p := make([]byte, 1023)[alignP:]
q := make([]byte, 1023)[alignQ:]
for i := 0; i < len(p); i++ {
p[i] = byte(i)
}
for i := 0; i < len(q); i++ {
q[i] = byte(len(q) - i)
}
d1 := make([]byte, 1023+alignD)[alignD:]
d2 := make([]byte, 1023+alignD)[alignD:]
ORBytes(d1, p, q)
safeORBytes(d2, p, q)
if !bytes.Equal(d1, d2) {
t.Error("not equal")
}
}
}
}
}
// Tests that bit testing works for various alignments.
func TestTest(t *testing.T) {
for align := 0; align < 2; align++ {
// Test for bits set in the bulk part
p := make([]byte, 1023)[align:]
p[100] = 1
if TestBytes(p) != safeTestBytes(p) {
t.Error("not equal")
}
// Test for bits set in the tail part
q := make([]byte, 1023)[align:]
q[len(q)-1] = 1
if TestBytes(q) != safeTestBytes(q) {
t.Error("not equal")
}
}
}
// Benchmarks the potentially optimized XOR performance.
func BenchmarkFastXOR1KB(b *testing.B) { benchmarkFastXOR(b, 1024) }
func BenchmarkFastXOR2KB(b *testing.B) { benchmarkFastXOR(b, 2048) }
func BenchmarkFastXOR4KB(b *testing.B) { benchmarkFastXOR(b, 4096) }
func benchmarkFastXOR(b *testing.B, size int) {
p, q := make([]byte, size), make([]byte, size)
for i := 0; i < b.N; i++ {
XORBytes(p, p, q)
}
}
// Benchmarks the baseline XOR performance.
func BenchmarkBaseXOR1KB(b *testing.B) { benchmarkBaseXOR(b, 1024) }
func BenchmarkBaseXOR2KB(b *testing.B) { benchmarkBaseXOR(b, 2048) }
func BenchmarkBaseXOR4KB(b *testing.B) { benchmarkBaseXOR(b, 4096) }
func benchmarkBaseXOR(b *testing.B, size int) {
p, q := make([]byte, size), make([]byte, size)
for i := 0; i < b.N; i++ {
safeXORBytes(p, p, q)
}
}
// Benchmarks the potentially optimized AND performance.
func BenchmarkFastAND1KB(b *testing.B) { benchmarkFastAND(b, 1024) }
func BenchmarkFastAND2KB(b *testing.B) { benchmarkFastAND(b, 2048) }
func BenchmarkFastAND4KB(b *testing.B) { benchmarkFastAND(b, 4096) }
func benchmarkFastAND(b *testing.B, size int) {
p, q := make([]byte, size), make([]byte, size)
for i := 0; i < b.N; i++ {
ANDBytes(p, p, q)
}
}
// Benchmarks the baseline AND performance.
func BenchmarkBaseAND1KB(b *testing.B) { benchmarkBaseAND(b, 1024) }
func BenchmarkBaseAND2KB(b *testing.B) { benchmarkBaseAND(b, 2048) }
func BenchmarkBaseAND4KB(b *testing.B) { benchmarkBaseAND(b, 4096) }
func benchmarkBaseAND(b *testing.B, size int) {
p, q := make([]byte, size), make([]byte, size)
for i := 0; i < b.N; i++ {
safeANDBytes(p, p, q)
}
}
// Benchmarks the potentially optimized OR performance.
func BenchmarkFastOR1KB(b *testing.B) { benchmarkFastOR(b, 1024) }
func BenchmarkFastOR2KB(b *testing.B) { benchmarkFastOR(b, 2048) }
func BenchmarkFastOR4KB(b *testing.B) { benchmarkFastOR(b, 4096) }
func benchmarkFastOR(b *testing.B, size int) {
p, q := make([]byte, size), make([]byte, size)
for i := 0; i < b.N; i++ {
ORBytes(p, p, q)
}
}
// Benchmarks the baseline OR performance.
func BenchmarkBaseOR1KB(b *testing.B) { benchmarkBaseOR(b, 1024) }
func BenchmarkBaseOR2KB(b *testing.B) { benchmarkBaseOR(b, 2048) }
func BenchmarkBaseOR4KB(b *testing.B) { benchmarkBaseOR(b, 4096) }
func benchmarkBaseOR(b *testing.B, size int) {
p, q := make([]byte, size), make([]byte, size)
for i := 0; i < b.N; i++ {
safeORBytes(p, p, q)
}
}
// Benchmarks the potentially optimized bit testing performance.
func BenchmarkFastTest1KB(b *testing.B) { benchmarkFastTest(b, 1024) }
func BenchmarkFastTest2KB(b *testing.B) { benchmarkFastTest(b, 2048) }
func BenchmarkFastTest4KB(b *testing.B) { benchmarkFastTest(b, 4096) }
func benchmarkFastTest(b *testing.B, size int) {
p := make([]byte, size)
for i := 0; i < b.N; i++ {
TestBytes(p)
}
}
// Benchmarks the baseline bit testing performance.
func BenchmarkBaseTest1KB(b *testing.B) { benchmarkBaseTest(b, 1024) }
func BenchmarkBaseTest2KB(b *testing.B) { benchmarkBaseTest(b, 2048) }
func BenchmarkBaseTest4KB(b *testing.B) { benchmarkBaseTest(b, 4096) }
func benchmarkBaseTest(b *testing.B, size int) {
p := make([]byte, size)
for i := 0; i < b.N; i++ {
safeTestBytes(p)
}
}

170
common/bitutil/compress.go Normal file
View File

@ -0,0 +1,170 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package bitutil
import "errors"
var (
// errMissingData is returned from decompression if the byte referenced by
// the bitset header overflows the input data.
errMissingData = errors.New("missing bytes on input")
// errUnreferencedData is returned from decompression if not all bytes were used
// up from the input data after decompressing it.
errUnreferencedData = errors.New("extra bytes on input")
// errExceededTarget is returned from decompression if the bitset header has
// more bits defined than the number of target buffer space available.
errExceededTarget = errors.New("target data size exceeded")
// errZeroContent is returned from decompression if a data byte referenced in
// the bitset header is actually a zero byte.
errZeroContent = errors.New("zero byte in input content")
)
// The compression algorithm implemented by CompressBytes and DecompressBytes is
// optimized for sparse input data which contains a lot of zero bytes. Decompression
// requires knowledge of the decompressed data length.
//
// Compression works as follows:
//
// if data only contains zeroes,
// CompressBytes(data) == nil
// otherwise if len(data) <= 1,
// CompressBytes(data) == data
// otherwise:
// CompressBytes(data) == append(CompressBytes(nonZeroBitset(data)), nonZeroBytes(data)...)
// where
// nonZeroBitset(data) is a bit vector with len(data) bits (MSB first):
// nonZeroBitset(data)[i/8] && (1 << (7-i%8)) != 0 if data[i] != 0
// len(nonZeroBitset(data)) == (len(data)+7)/8
// nonZeroBytes(data) contains the non-zero bytes of data in the same order
// CompressBytes compresses the input byte slice according to the sparse bitset
// representation algorithm. If the result is bigger than the original input, no
// compression is done.
func CompressBytes(data []byte) []byte {
if out := bitsetEncodeBytes(data); len(out) < len(data) {
return out
}
cpy := make([]byte, len(data))
copy(cpy, data)
return cpy
}
// bitsetEncodeBytes compresses the input byte slice according to the sparse
// bitset representation algorithm.
func bitsetEncodeBytes(data []byte) []byte {
// Empty slices get compressed to nil
if len(data) == 0 {
return nil
}
// One byte slices compress to nil or retain the single byte
if len(data) == 1 {
if data[0] == 0 {
return nil
}
return data
}
// Calculate the bitset of set bytes, and gather the non-zero bytes
nonZeroBitset := make([]byte, (len(data)+7)/8)
nonZeroBytes := make([]byte, 0, len(data))
for i, b := range data {
if b != 0 {
nonZeroBytes = append(nonZeroBytes, b)
nonZeroBitset[i/8] |= 1 << byte(7-i%8)
}
}
if len(nonZeroBytes) == 0 {
return nil
}
return append(bitsetEncodeBytes(nonZeroBitset), nonZeroBytes...)
}
// DecompressBytes decompresses data with a known target size. If the input data
// matches the size of the target, it means no compression was done in the first
// place.
func DecompressBytes(data []byte, target int) ([]byte, error) {
if len(data) > target {
return nil, errExceededTarget
}
if len(data) == target {
cpy := make([]byte, len(data))
copy(cpy, data)
return cpy, nil
}
return bitsetDecodeBytes(data, target)
}
// bitsetDecodeBytes decompresses data with a known target size.
func bitsetDecodeBytes(data []byte, target int) ([]byte, error) {
out, size, err := bitsetDecodePartialBytes(data, target)
if err != nil {
return nil, err
}
if size != len(data) {
return nil, errUnreferencedData
}
return out, nil
}
// bitsetDecodePartialBytes decompresses data with a known target size, but does
// not enforce consuming all the input bytes. In addition to the decompressed
// output, the function returns the length of compressed input data corresponding
// to the output as the input slice may be longer.
func bitsetDecodePartialBytes(data []byte, target int) ([]byte, int, error) {
// Sanity check 0 targets to avoid infinite recursion
if target == 0 {
return nil, 0, nil
}
// Handle the zero and single byte corner cases
decomp := make([]byte, target)
if len(data) == 0 {
return decomp, 0, nil
}
if target == 1 {
decomp[0] = data[0] // copy to avoid referencing the input slice
if data[0] != 0 {
return decomp, 1, nil
}
return decomp, 0, nil
}
// Decompress the bitset of set bytes and distribute the non zero bytes
nonZeroBitset, ptr, err := bitsetDecodePartialBytes(data, (target+7)/8)
if err != nil {
return nil, ptr, err
}
for i := 0; i < 8*len(nonZeroBitset); i++ {
if nonZeroBitset[i/8]&(1<<byte(7-i%8)) != 0 {
// Make sure we have enough data to push into the correct slot
if ptr >= len(data) {
return nil, 0, errMissingData
}
if i >= len(decomp) {
return nil, 0, errExceededTarget
}
// Make sure the data is valid and push into the slot
if data[ptr] == 0 {
return nil, 0, errZeroContent
}
decomp[i] = data[ptr]
ptr++
}
}
return decomp, ptr, nil
}

View File

@ -0,0 +1,56 @@
// 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 gofuzz
package bitutil
import "bytes"
// Fuzz implements a go-fuzz fuzzer method to test various encoding method
// invocations.
func Fuzz(data []byte) int {
if len(data) == 0 {
return -1
}
if data[0]%2 == 0 {
return fuzzEncode(data[1:])
}
return fuzzDecode(data[1:])
}
// fuzzEncode implements a go-fuzz fuzzer method to test the bitset encoding and
// decoding algorithm.
func fuzzEncode(data []byte) int {
proc, _ := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
if !bytes.Equal(data, proc) {
panic("content mismatch")
}
return 0
}
// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and
// reencoding algorithm.
func fuzzDecode(data []byte) int {
blob, err := bitsetDecodeBytes(data, 1024)
if err != nil {
return 0
}
if comp := bitsetEncodeBytes(blob); !bytes.Equal(comp, data) {
panic("content mismatch")
}
return 0
}

View File

@ -0,0 +1,181 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package bitutil
import (
"bytes"
"math/rand"
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
)
// Tests that data bitset encoding and decoding works and is bijective.
func TestEncodingCycle(t *testing.T) {
tests := []string{
// Tests generated by go-fuzz to maximize code coverage
"0x000000000000000000",
"0xef0400",
"0xdf7070533534333636313639343638373532313536346c1bc33339343837313070706336343035336336346c65fefb3930393233383838ac2f65fefb",
"0x7b64000000",
"0x000034000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0000000000000000000",
"0x4912385c0e7b64000000",
"0x000034000000000000000000000000000000",
"0x00",
"0x000003e834ff7f0000",
"0x0000",
"0x0000000000000000000000000000000000000000000000000000000000ff00",
"0x895f0c6a020f850c6a020f85f88df88d",
"0xdf7070533534333636313639343638373432313536346c1bc3315aac2f65fefb",
"0x0000000000",
"0xdf70706336346c65fefb",
"0x00006d643634000000",
"0xdf7070533534333636313639343638373532313536346c1bc333393438373130707063363430353639343638373532313536346c1bc333393438336336346c65fe",
}
for i, tt := range tests {
data := hexutil.MustDecode(tt)
proc, err := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
if err != nil {
t.Errorf("test %d: failed to decompress compressed data: %v", i, err)
continue
}
if !bytes.Equal(data, proc) {
t.Errorf("test %d: compress/decompress mismatch: have %x, want %x", i, proc, data)
}
}
}
// Tests that data bitset decoding and rencoding works and is bijective.
func TestDecodingCycle(t *testing.T) {
tests := []struct {
size int
input string
fail error
}{
{size: 0, input: "0x"},
// Crashers generated by go-fuzz
{size: 0, input: "0x0020", fail: errUnreferencedData},
{size: 0, input: "0x30", fail: errUnreferencedData},
{size: 1, input: "0x00", fail: errUnreferencedData},
{size: 2, input: "0x07", fail: errMissingData},
{size: 1024, input: "0x8000", fail: errZeroContent},
// Tests generated by go-fuzz to maximize code coverage
{size: 29490, input: "0x343137343733323134333839373334323073333930783e3078333930783e70706336346c65303e", fail: errMissingData},
{size: 59395, input: "0x00", fail: errUnreferencedData},
{size: 52574, input: "0x70706336346c65c0de", fail: errExceededTarget},
{size: 42264, input: "0x07", fail: errMissingData},
{size: 52, input: "0xa5045bad48f4", fail: errExceededTarget},
{size: 52574, input: "0xc0de", fail: errMissingData},
{size: 52574, input: "0x"},
{size: 29490, input: "0x34313734373332313433383937333432307333393078073034333839373334323073333930783e3078333937333432307333393078073061333930783e70706336346c65303e", fail: errMissingData},
{size: 29491, input: "0x3973333930783e30783e", fail: errMissingData},
{size: 1024, input: "0x808080608080"},
{size: 1024, input: "0x808470705e3632383337363033313434303137393130306c6580ef46806380635a80"},
{size: 1024, input: "0x8080808070"},
{size: 1024, input: "0x808070705e36346c6580ef46806380635a80"},
{size: 1024, input: "0x80808046802680"},
{size: 1024, input: "0x4040404035"},
{size: 1024, input: "0x4040bf3ba2b3f684402d353234373438373934409fe5b1e7ada94ebfd7d0505e27be4035"},
{size: 1024, input: "0x404040bf3ba2b3f6844035"},
{size: 1024, input: "0x40402d35323437343837393440bfd7d0505e27be4035"},
}
for i, tt := range tests {
data := hexutil.MustDecode(tt.input)
orig, err := bitsetDecodeBytes(data, tt.size)
if err != tt.fail {
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.fail)
}
if err != nil {
continue
}
if comp := bitsetEncodeBytes(orig); !bytes.Equal(comp, data) {
t.Errorf("test %d: decompress/compress mismatch: have %x, want %x", i, comp, data)
}
}
}
// TestCompression tests that compression works by returning either the bitset
// encoded input, or the actual input if the bitset version is longer.
func TestCompression(t *testing.T) {
// Check the the compression returns the bitset encoding is shorter
in := hexutil.MustDecode("0x4912385c0e7b64000000")
out := hexutil.MustDecode("0x80fe4912385c0e7b64")
if data := CompressBytes(in); bytes.Compare(data, out) != 0 {
t.Errorf("encoding mismatch for sparse data: have %x, want %x", data, out)
}
if data, err := DecompressBytes(out, len(in)); err != nil || bytes.Compare(data, in) != 0 {
t.Errorf("decoding mismatch for sparse data: have %x, want %x, error %v", data, in, err)
}
// Check the the compression returns the input if the bitset encoding is longer
in = hexutil.MustDecode("0xdf7070533534333636313639343638373532313536346c1bc33339343837313070706336343035336336346c65fefb3930393233383838ac2f65fefb")
out = hexutil.MustDecode("0xdf7070533534333636313639343638373532313536346c1bc33339343837313070706336343035336336346c65fefb3930393233383838ac2f65fefb")
if data := CompressBytes(in); bytes.Compare(data, out) != 0 {
t.Errorf("encoding mismatch for dense data: have %x, want %x", data, out)
}
if data, err := DecompressBytes(out, len(in)); err != nil || bytes.Compare(data, in) != 0 {
t.Errorf("decoding mismatch for dense data: have %x, want %x, error %v", data, in, err)
}
// Check that decompressing a longer input than the target fails
if _, err := DecompressBytes([]byte{0xc0, 0x01, 0x01}, 2); err != errExceededTarget {
t.Errorf("decoding error mismatch for long data: have %v, want %v", err, errExceededTarget)
}
}
// Crude benchmark for compressing random slices of bytes.
func BenchmarkEncoding1KBVerySparse(b *testing.B) { benchmarkEncoding(b, 1024, 0.0001) }
func BenchmarkEncoding2KBVerySparse(b *testing.B) { benchmarkEncoding(b, 2048, 0.0001) }
func BenchmarkEncoding4KBVerySparse(b *testing.B) { benchmarkEncoding(b, 4096, 0.0001) }
func BenchmarkEncoding1KBSparse(b *testing.B) { benchmarkEncoding(b, 1024, 0.001) }
func BenchmarkEncoding2KBSparse(b *testing.B) { benchmarkEncoding(b, 2048, 0.001) }
func BenchmarkEncoding4KBSparse(b *testing.B) { benchmarkEncoding(b, 4096, 0.001) }
func BenchmarkEncoding1KBDense(b *testing.B) { benchmarkEncoding(b, 1024, 0.1) }
func BenchmarkEncoding2KBDense(b *testing.B) { benchmarkEncoding(b, 2048, 0.1) }
func BenchmarkEncoding4KBDense(b *testing.B) { benchmarkEncoding(b, 4096, 0.1) }
func BenchmarkEncoding1KBSaturated(b *testing.B) { benchmarkEncoding(b, 1024, 0.5) }
func BenchmarkEncoding2KBSaturated(b *testing.B) { benchmarkEncoding(b, 2048, 0.5) }
func BenchmarkEncoding4KBSaturated(b *testing.B) { benchmarkEncoding(b, 4096, 0.5) }
func benchmarkEncoding(b *testing.B, bytes int, fill float64) {
// Generate a random slice of bytes to compress
random := rand.NewSource(0) // reproducible and comparable
data := make([]byte, bytes)
bits := int(float64(bytes) * 8 * fill)
for i := 0; i < bits; i++ {
idx := random.Int63() % int64(len(data))
bit := uint(random.Int63() % 8)
data[idx] |= 1 << bit
}
// Reset the benchmark and measure encoding/decoding
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
}
}

View File

@ -89,18 +89,18 @@ func Hex2BytesFixed(str string, flen int) []byte {
}
func RightPadBytes(slice []byte, l int) []byte {
if l < len(slice) {
if l <= len(slice) {
return slice
}
padded := make([]byte, l)
copy(padded[0:len(slice)], slice)
copy(padded, slice)
return padded
}
func LeftPadBytes(slice []byte, l int) []byte {
if l < len(slice) {
if l <= len(slice) {
return slice
}

View File

@ -32,7 +32,6 @@ package hexutil
import (
"encoding/hex"
"errors"
"fmt"
"math/big"
"strconv"
@ -41,17 +40,23 @@ import (
const uintBits = 32 << (uint64(^uint(0)) >> 63)
var (
ErrEmptyString = errors.New("empty hex string")
ErrMissingPrefix = errors.New("missing 0x prefix for hex data")
ErrSyntax = errors.New("invalid hex")
ErrEmptyNumber = errors.New("hex number has no digits after 0x")
ErrLeadingZero = errors.New("hex number has leading zero digits after 0x")
ErrOddLength = errors.New("hex string has odd length")
ErrUint64Range = errors.New("hex number does not fit into 64 bits")
ErrUintRange = fmt.Errorf("hex number does not fit into %d bits", uintBits)
ErrBig256Range = errors.New("hex number does not fit into 256 bits")
ErrEmptyString = &decError{"empty hex string"}
ErrSyntax = &decError{"invalid hex string"}
ErrMissingPrefix = &decError{"hex string without 0x prefix"}
ErrOddLength = &decError{"hex string of odd length"}
ErrEmptyNumber = &decError{"hex string \"0x\""}
ErrLeadingZero = &decError{"hex number with leading zero digits"}
ErrUint64Range = &decError{"hex number > 64 bits"}
ErrUintRange = &decError{fmt.Sprintf("hex number > %d bits", uintBits)}
ErrBig256Range = &decError{"hex number > 256 bits"}
)
type decError struct{ msg string }
func (err decError) Error() string {
return string(err.msg)
}
// Decode decodes a hex string with 0x prefix.
func Decode(input string) ([]byte, error) {
if len(input) == 0 {

View File

@ -18,15 +18,19 @@ package hexutil
import (
"encoding/hex"
"errors"
"encoding/json"
"fmt"
"math/big"
"reflect"
"strconv"
)
var (
textZero = []byte(`0x0`)
errNonString = errors.New("cannot unmarshal non-string as hex data")
textZero = []byte(`0x0`)
bytesT = reflect.TypeOf(Bytes(nil))
bigT = reflect.TypeOf((*Big)(nil))
uintT = reflect.TypeOf(Uint(0))
uint64T = reflect.TypeOf(Uint64(0))
)
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
@ -44,9 +48,9 @@ func (b Bytes) MarshalText() ([]byte, error) {
// UnmarshalJSON implements json.Unmarshaler.
func (b *Bytes) UnmarshalJSON(input []byte) error {
if !isString(input) {
return errNonString
return errNonString(bytesT)
}
return b.UnmarshalText(input[1 : len(input)-1])
return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bytesT)
}
// UnmarshalText implements encoding.TextUnmarshaler.
@ -69,6 +73,16 @@ func (b Bytes) String() string {
return Encode(b)
}
// UnmarshalFixedJSON decodes the input as a string with 0x prefix. The length of out
// determines the required input length. This function is commonly used to implement the
// UnmarshalJSON method for fixed-size types.
func UnmarshalFixedJSON(typ reflect.Type, input, out []byte) error {
if !isString(input) {
return errNonString(typ)
}
return wrapTypeError(UnmarshalFixedText(typ.String(), input[1:len(input)-1], out), typ)
}
// UnmarshalFixedText decodes the input as a string with 0x prefix. The length of out
// determines the required input length. This function is commonly used to implement the
// UnmarshalText method for fixed-size types.
@ -127,9 +141,9 @@ func (b Big) MarshalText() ([]byte, error) {
// UnmarshalJSON implements json.Unmarshaler.
func (b *Big) UnmarshalJSON(input []byte) error {
if !isString(input) {
return errNonString
return errNonString(bigT)
}
return b.UnmarshalText(input[1 : len(input)-1])
return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bigT)
}
// UnmarshalText implements encoding.TextUnmarshaler
@ -189,9 +203,9 @@ func (b Uint64) MarshalText() ([]byte, error) {
// UnmarshalJSON implements json.Unmarshaler.
func (b *Uint64) UnmarshalJSON(input []byte) error {
if !isString(input) {
return errNonString
return errNonString(uint64T)
}
return b.UnmarshalText(input[1 : len(input)-1])
return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uint64T)
}
// UnmarshalText implements encoding.TextUnmarshaler
@ -233,9 +247,9 @@ func (b Uint) MarshalText() ([]byte, error) {
// UnmarshalJSON implements json.Unmarshaler.
func (b *Uint) UnmarshalJSON(input []byte) error {
if !isString(input) {
return errNonString
return errNonString(uintT)
}
return b.UnmarshalText(input[1 : len(input)-1])
return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uintT)
}
// UnmarshalText implements encoding.TextUnmarshaler.
@ -295,3 +309,14 @@ func checkNumberText(input []byte) (raw []byte, err error) {
}
return input, nil
}
func wrapTypeError(err error, typ reflect.Type) error {
if _, ok := err.(*decError); ok {
return &json.UnmarshalTypeError{Value: err.Error(), Type: typ}
}
return err
}
func errNonString(typ reflect.Type) error {
return &json.UnmarshalTypeError{Value: "non-string", Type: typ}
}

View File

@ -62,12 +62,12 @@ var errJSONEOF = errors.New("unexpected end of JSON input")
var unmarshalBytesTests = []unmarshalTest{
// invalid encoding
{input: "", wantErr: errJSONEOF},
{input: "null", wantErr: errNonString},
{input: "10", wantErr: errNonString},
{input: `"0"`, wantErr: ErrMissingPrefix},
{input: `"0x0"`, wantErr: ErrOddLength},
{input: `"0xxx"`, wantErr: ErrSyntax},
{input: `"0x01zz01"`, wantErr: ErrSyntax},
{input: "null", wantErr: errNonString(bytesT)},
{input: "10", wantErr: errNonString(bytesT)},
{input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, bytesT)},
{input: `"0x0"`, wantErr: wrapTypeError(ErrOddLength, bytesT)},
{input: `"0xxx"`, wantErr: wrapTypeError(ErrSyntax, bytesT)},
{input: `"0x01zz01"`, wantErr: wrapTypeError(ErrSyntax, bytesT)},
// valid encoding
{input: `""`, want: referenceBytes("")},
@ -127,16 +127,16 @@ func TestMarshalBytes(t *testing.T) {
var unmarshalBigTests = []unmarshalTest{
// invalid encoding
{input: "", wantErr: errJSONEOF},
{input: "null", wantErr: errNonString},
{input: "10", wantErr: errNonString},
{input: `"0"`, wantErr: ErrMissingPrefix},
{input: `"0x"`, wantErr: ErrEmptyNumber},
{input: `"0x01"`, wantErr: ErrLeadingZero},
{input: `"0xx"`, wantErr: ErrSyntax},
{input: `"0x1zz01"`, wantErr: ErrSyntax},
{input: "null", wantErr: errNonString(bigT)},
{input: "10", wantErr: errNonString(bigT)},
{input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, bigT)},
{input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, bigT)},
{input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, bigT)},
{input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, bigT)},
{input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, bigT)},
{
input: `"0x10000000000000000000000000000000000000000000000000000000000000000"`,
wantErr: ErrBig256Range,
wantErr: wrapTypeError(ErrBig256Range, bigT),
},
// valid encoding
@ -208,14 +208,14 @@ func TestMarshalBig(t *testing.T) {
var unmarshalUint64Tests = []unmarshalTest{
// invalid encoding
{input: "", wantErr: errJSONEOF},
{input: "null", wantErr: errNonString},
{input: "10", wantErr: errNonString},
{input: `"0"`, wantErr: ErrMissingPrefix},
{input: `"0x"`, wantErr: ErrEmptyNumber},
{input: `"0x01"`, wantErr: ErrLeadingZero},
{input: `"0xfffffffffffffffff"`, wantErr: ErrUint64Range},
{input: `"0xx"`, wantErr: ErrSyntax},
{input: `"0x1zz01"`, wantErr: ErrSyntax},
{input: "null", wantErr: errNonString(uint64T)},
{input: "10", wantErr: errNonString(uint64T)},
{input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, uint64T)},
{input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, uint64T)},
{input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, uint64T)},
{input: `"0xfffffffffffffffff"`, wantErr: wrapTypeError(ErrUint64Range, uint64T)},
{input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, uint64T)},
{input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, uint64T)},
// valid encoding
{input: `""`, want: uint64(0)},
@ -298,15 +298,15 @@ var (
var unmarshalUintTests = []unmarshalTest{
// invalid encoding
{input: "", wantErr: errJSONEOF},
{input: "null", wantErr: errNonString},
{input: "10", wantErr: errNonString},
{input: `"0"`, wantErr: ErrMissingPrefix},
{input: `"0x"`, wantErr: ErrEmptyNumber},
{input: `"0x01"`, wantErr: ErrLeadingZero},
{input: `"0x100000000"`, want: uint(maxUint33bits), wantErr32bit: ErrUintRange},
{input: `"0xfffffffffffffffff"`, wantErr: ErrUintRange},
{input: `"0xx"`, wantErr: ErrSyntax},
{input: `"0x1zz01"`, wantErr: ErrSyntax},
{input: "null", wantErr: errNonString(uintT)},
{input: "10", wantErr: errNonString(uintT)},
{input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, uintT)},
{input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, uintT)},
{input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, uintT)},
{input: `"0x100000000"`, want: uint(maxUint33bits), wantErr32bit: wrapTypeError(ErrUintRange, uintT)},
{input: `"0xfffffffffffffffff"`, wantErr: wrapTypeError(ErrUintRange, uintT)},
{input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, uintT)},
{input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, uintT)},
// valid encoding
{input: `""`, want: uint(0)},
@ -317,7 +317,7 @@ var unmarshalUintTests = []unmarshalTest{
{input: `"0x1122aaff"`, want: uint(0x1122aaff)},
{input: `"0xbbb"`, want: uint(0xbbb)},
{input: `"0xffffffff"`, want: uint(0xffffffff)},
{input: `"0xffffffffffffffff"`, want: uint(maxUint64bits), wantErr32bit: ErrUintRange},
{input: `"0xffffffffffffffff"`, want: uint(maxUint64bits), wantErr32bit: wrapTypeError(ErrUintRange, uintT)},
}
func TestUnmarshalUint(t *testing.T) {

View File

@ -27,6 +27,8 @@ var (
tt256 = BigPow(2, 256)
tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1))
MaxBig256 = new(big.Int).Set(tt256m1)
tt63 = BigPow(2, 63)
MaxBig63 = new(big.Int).Sub(tt63, big.NewInt(1))
)
const (
@ -128,6 +130,34 @@ func PaddedBigBytes(bigint *big.Int, n int) []byte {
return ret
}
// bigEndianByteAt returns the byte at position n,
// in Big-Endian encoding
// So n==0 returns the least significant byte
func bigEndianByteAt(bigint *big.Int, n int) byte {
words := bigint.Bits()
// Check word-bucket the byte will reside in
i := n / wordBytes
if i >= len(words) {
return byte(0)
}
word := words[i]
// Offset of the byte
shift := 8 * uint(n%wordBytes)
return byte(word >> shift)
}
// Byte returns the byte at position n,
// with the supplied padlength in Little-Endian encoding.
// n==0 returns the MSB
// Example: bigint '5', padlength 32, n=31 => 5
func Byte(bigint *big.Int, padlength, n int) byte {
if n >= padlength {
return byte(0)
}
return bigEndianByteAt(bigint, padlength-1-n)
}
// ReadBits encodes the absolute value of bigint as big-endian bytes. Callers must ensure
// that buf has enough space. If buf is too short the result will be incomplete.
func ReadBits(bigint *big.Int, buf []byte) {

View File

@ -21,6 +21,8 @@ import (
"encoding/hex"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
)
func TestHexOrDecimal256(t *testing.T) {
@ -133,8 +135,44 @@ func TestPaddedBigBytes(t *testing.T) {
}
}
func BenchmarkPaddedBigBytes(b *testing.B) {
func BenchmarkPaddedBigBytesLargePadding(b *testing.B) {
bigint := MustParseBig256("123456789123456789123456789123456789")
for i := 0; i < b.N; i++ {
PaddedBigBytes(bigint, 200)
}
}
func BenchmarkPaddedBigBytesSmallPadding(b *testing.B) {
bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC")
for i := 0; i < b.N; i++ {
PaddedBigBytes(bigint, 5)
}
}
func BenchmarkPaddedBigBytesSmallOnePadding(b *testing.B) {
bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC")
for i := 0; i < b.N; i++ {
PaddedBigBytes(bigint, 32)
}
}
func BenchmarkByteAtBrandNew(b *testing.B) {
bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC")
for i := 0; i < b.N; i++ {
bigEndianByteAt(bigint, 15)
}
}
func BenchmarkByteAt(b *testing.B) {
bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC")
for i := 0; i < b.N; i++ {
bigEndianByteAt(bigint, 15)
}
}
func BenchmarkByteAtOld(b *testing.B) {
bigint := MustParseBig256("0x18F8F8F1000111000110011100222004330052300000000000000000FEFCF3CC")
for i := 0; i < b.N; i++ {
PaddedBigBytes(bigint, 32)
}
@ -174,6 +212,65 @@ func TestU256(t *testing.T) {
}
}
func TestBigEndianByteAt(t *testing.T) {
tests := []struct {
x string
y int
exp byte
}{
{"00", 0, 0x00},
{"01", 1, 0x00},
{"00", 1, 0x00},
{"01", 0, 0x01},
{"0000000000000000000000000000000000000000000000000000000000102030", 0, 0x30},
{"0000000000000000000000000000000000000000000000000000000000102030", 1, 0x20},
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 31, 0xAB},
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 32, 0x00},
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 500, 0x00},
}
for _, test := range tests {
v := new(big.Int).SetBytes(common.Hex2Bytes(test.x))
actual := bigEndianByteAt(v, test.y)
if actual != test.exp {
t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.x, test.y, test.exp, actual)
}
}
}
func TestLittleEndianByteAt(t *testing.T) {
tests := []struct {
x string
y int
exp byte
}{
{"00", 0, 0x00},
{"01", 1, 0x00},
{"00", 1, 0x00},
{"01", 0, 0x00},
{"0000000000000000000000000000000000000000000000000000000000102030", 0, 0x00},
{"0000000000000000000000000000000000000000000000000000000000102030", 1, 0x00},
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 31, 0x00},
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 32, 0x00},
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 0, 0xAB},
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 1, 0xCD},
{"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 0, 0x00},
{"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 1, 0xCD},
{"0000000000000000000000000000000000000000000000000000000000102030", 31, 0x30},
{"0000000000000000000000000000000000000000000000000000000000102030", 30, 0x20},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 32, 0x0},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 31, 0xFF},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0xFFFF, 0x0},
}
for _, test := range tests {
v := new(big.Int).SetBytes(common.Hex2Bytes(test.x))
actual := Byte(v, 32, test.y)
if actual != test.exp {
t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.x, test.y, test.exp, actual)
}
}
}
func TestS256(t *testing.T) {
tests := []struct{ x, y *big.Int }{
{x: big.NewInt(0), y: big.NewInt(0)},

View File

@ -31,6 +31,11 @@ const (
AddressLength = 20
)
var (
hashT = reflect.TypeOf(Hash{})
addressT = reflect.TypeOf(Address{})
)
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
type Hash [HashLength]byte
@ -72,6 +77,11 @@ func (h *Hash) UnmarshalText(input []byte) error {
return hexutil.UnmarshalFixedText("Hash", input, h[:])
}
// UnmarshalJSON parses a hash in hex syntax.
func (h *Hash) UnmarshalJSON(input []byte) error {
return hexutil.UnmarshalFixedJSON(hashT, input, h[:])
}
// MarshalText returns the hex representation of h.
func (h Hash) MarshalText() ([]byte, error) {
return hexutil.Bytes(h[:]).MarshalText()
@ -194,6 +204,11 @@ func (a *Address) UnmarshalText(input []byte) error {
return hexutil.UnmarshalFixedText("Address", input, a[:])
}
// UnmarshalJSON parses a hash in hex syntax.
func (a *Address) UnmarshalJSON(input []byte) error {
return hexutil.UnmarshalFixedJSON(addressT, input, a[:])
}
// UnprefixedHash allows marshaling an Address without 0x prefix.
type UnprefixedAddress Address

View File

@ -21,8 +21,6 @@ import (
"math/big"
"strings"
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
)
func TestBytesConversion(t *testing.T) {
@ -43,10 +41,10 @@ func TestHashJsonValidation(t *testing.T) {
Size int
Error string
}{
{"", 62, hexutil.ErrMissingPrefix.Error()},
{"0x", 66, "hex string has length 66, want 64 for Hash"},
{"0x", 63, hexutil.ErrOddLength.Error()},
{"0x", 0, "hex string has length 0, want 64 for Hash"},
{"", 62, "json: cannot unmarshal hex string without 0x prefix into Go value of type common.Hash"},
{"0x", 66, "hex string has length 66, want 64 for common.Hash"},
{"0x", 63, "json: cannot unmarshal hex string of odd length into Go value of type common.Hash"},
{"0x", 0, "hex string has length 0, want 64 for common.Hash"},
{"0x", 64, ""},
{"0X", 64, ""},
}

View File

@ -33,102 +33,18 @@ func (s *CompressionRleSuite) TestDecompressSimple(c *checker.C) {
res, err := Decompress([]byte{token, 0xfd})
c.Assert(err, checker.IsNil)
c.Assert(res, checker.DeepEquals, exp)
// if bytes.Compare(res, exp) != 0 {
// t.Error("empty sha3", res)
// }
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)
// if bytes.Compare(res, exp) != 0 {
// t.Error("0x80 sha3", res)
// }
res, err = Decompress([]byte{token, 0xff})
c.Assert(err, checker.IsNil)
c.Assert(res, checker.DeepEquals, []byte{token})
// if bytes.Compare(res, []byte{token}) != 0 {
// t.Error("token", res)
// }
res, err = Decompress([]byte{token, 12})
c.Assert(err, checker.IsNil)
c.Assert(res, checker.DeepEquals, make([]byte, 10))
// if bytes.Compare(res, make([]byte, 10)) != 0 {
// t.Error("10 * zero", res)
// }
}
// func TestDecompressMulti(t *testing.T) {
// res, err := Decompress([]byte{token, 0xfd, token, 0xfe, token, 12})
// if err != nil {
// t.Error(err)
// }
// var exp []byte
// exp = append(exp, crypto.Keccak256([]byte(""))...)
// exp = append(exp, crypto.Keccak256([]byte{0x80})...)
// exp = append(exp, make([]byte, 10)...)
// if bytes.Compare(res, res) != 0 {
// t.Error("Expected", exp, "result", res)
// }
// }
// func TestCompressSimple(t *testing.T) {
// res := Compress([]byte{0, 0, 0, 0, 0})
// if bytes.Compare(res, []byte{token, 7}) != 0 {
// t.Error("5 * zero", res)
// }
// res = Compress(crypto.Keccak256([]byte("")))
// if bytes.Compare(res, []byte{token, emptyShaToken}) != 0 {
// t.Error("empty sha", res)
// }
// res = Compress(crypto.Keccak256([]byte{0x80}))
// if bytes.Compare(res, []byte{token, emptyListShaToken}) != 0 {
// t.Error("empty list sha", res)
// }
// res = Compress([]byte{token})
// if bytes.Compare(res, []byte{token, tokenToken}) != 0 {
// t.Error("token", res)
// }
// }
// func TestCompressMulti(t *testing.T) {
// in := []byte{0, 0, 0, 0, 0}
// in = append(in, crypto.Keccak256([]byte(""))...)
// in = append(in, crypto.Keccak256([]byte{0x80})...)
// in = append(in, token)
// res := Compress(in)
// exp := []byte{token, 7, token, emptyShaToken, token, emptyListShaToken, token, tokenToken}
// if bytes.Compare(res, exp) != 0 {
// t.Error("expected", exp, "got", res)
// }
// }
// func TestCompressDecompress(t *testing.T) {
// var in []byte
// for i := 0; i < 20; i++ {
// in = append(in, []byte{0, 0, 0, 0, 0}...)
// in = append(in, crypto.Keccak256([]byte(""))...)
// in = append(in, crypto.Keccak256([]byte{0x80})...)
// in = append(in, []byte{123, 2, 19, 89, 245, 254, 255, token, 98, 233}...)
// in = append(in, token)
// }
// c := Compress(in)
// d, err := Decompress(c)
// if err != nil {
// t.Error(err)
// }
// if bytes.Compare(d, in) != 0 {
// t.Error("multi failed\n", d, "\n", in)
// }
// }

View File

@ -44,7 +44,7 @@ import (
const (
checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database
inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
inmemorySignatures = 1024 // Number of recent blocks to keep in memory
inmemorySignatures = 4096 // Number of recent block signatures to keep in memory
wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers
)
@ -76,7 +76,7 @@ var (
errUnknownBlock = errors.New("unknown block")
// errInvalidCheckpointBeneficiary is returned if a checkpoint/epoch transition
// block has a beneficiary set to non zeroes.
// block has a beneficiary set to non-zeroes.
errInvalidCheckpointBeneficiary = errors.New("beneficiary in checkpoint block non-zero")
// errInvalidVote is returned if a nonce value is something else that the two
@ -84,7 +84,7 @@ var (
errInvalidVote = errors.New("vote nonce not 0x00..0 or 0xff..f")
// errInvalidCheckpointVote is returned if a checkpoint/epoch transition block
// has a vote nonce set to non zeroes.
// has a vote nonce set to non-zeroes.
errInvalidCheckpointVote = errors.New("vote nonce in checkpoint block non-zero")
// errMissingVanity is returned if a block's extra-data section is shorter than
@ -99,12 +99,12 @@ var (
// their extra-data fields.
errExtraSigners = errors.New("non-checkpoint block contains extra signer list")
// drrInvalidCheckpointSigners is returned if a checkpoint block contains an
// errInvalidCheckpointSigners is returned if a checkpoint block contains an
// invalid list of signers (i.e. non divisible by 20 bytes, or not the correct
// ones).
drrInvalidCheckpointSigners = errors.New("invalid signer list on checkpoint block")
errInvalidCheckpointSigners = errors.New("invalid signer list on checkpoint block")
// errInvalidMixDigest is returned if a block's mix digest is non zero.
// errInvalidMixDigest is returned if a block's mix digest is non-zero.
errInvalidMixDigest = errors.New("non-zero mix digest")
// errInvalidUncleHash is returned if a block contains an non-empty uncle list.
@ -122,7 +122,7 @@ var (
// be modified via out-of-range or non-contiguous headers.
errInvalidVotingChain = errors.New("invalid voting chain")
// errUnauthorized is returned if a header is signed by a non authorized entity.
// errUnauthorized is returned if a header is signed by a non-authorized entity.
errUnauthorized = errors.New("unauthorized")
)
@ -162,7 +162,12 @@ func sigHash(header *types.Header) (hash common.Hash) {
}
// ecrecover extracts the Ethereum account address from a signed header.
func ecrecover(header *types.Header) (common.Address, error) {
func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
// If the signature's already cached, return that
hash := header.Hash()
if address, known := sigcache.Get(hash); known {
return address.(common.Address), nil
}
// Retrieve the signature from the header extra-data
if len(header.Extra) < extraSeal {
return common.Address{}, errMissingSignature
@ -177,6 +182,7 @@ func ecrecover(header *types.Header) (common.Address, error) {
var signer common.Address
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
sigcache.Add(hash, signer)
return signer, nil
}
@ -223,7 +229,7 @@ func New(config *params.CliqueConfig, db ethdb.Database) *Clique {
// Author implements consensus.Engine, returning the Ethereum address recovered
// from the signature in the header's extra-data section.
func (c *Clique) Author(header *types.Header) (common.Address, error) {
return ecrecover(header)
return ecrecover(header, c.signatures)
}
// VerifyHeader checks whether a header conforms to the consensus rules.
@ -291,7 +297,7 @@ func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header,
return errExtraSigners
}
if checkpoint && signersBytes%common.AddressLength != 0 {
return drrInvalidCheckpointSigners
return errInvalidCheckpointSigners
}
// Ensure that the mix digest is zero as we don't have fork protection currently
if header.MixDigest != (common.Hash{}) {
@ -347,7 +353,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type
}
extraSuffix := len(header.Extra) - extraSeal
if !bytes.Equal(header.Extra[extraVanity:extraSuffix], signers) {
return drrInvalidCheckpointSigners
return errInvalidCheckpointSigners
}
}
// All basic checks passed, verify the seal and return
@ -369,7 +375,7 @@ func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash commo
}
// If an on-disk checkpoint snapshot can be found, use that
if number%checkpointInterval == 0 {
if s, err := loadSnapshot(c.config, c.db, hash); err == nil {
if s, err := loadSnapshot(c.config, c.signatures, c.db, hash); err == nil {
log.Trace("Loaded voting snapshot form disk", "number", number, "hash", hash)
snap = s
break
@ -385,7 +391,7 @@ func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash commo
for i := 0; i < len(signers); i++ {
copy(signers[i][:], genesis.Extra[extraVanity+i*common.AddressLength:])
}
snap = newSnapshot(c.config, 0, genesis.Hash(), signers)
snap = newSnapshot(c.config, c.signatures, 0, genesis.Hash(), signers)
if err := snap.store(c.db); err != nil {
return nil, err
}
@ -461,10 +467,9 @@ func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, p
if err != nil {
return err
}
c.recents.Add(snap.Hash, snap)
// Resolve the authorization key and check against signers
signer, err := ecrecover(header)
signer, err := ecrecover(header, c.signatures)
if err != nil {
return err
}
@ -473,13 +478,13 @@ func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, p
}
for seen, recent := range snap.Recents {
if recent == signer {
// Signer is among recents, only fail if the current block doens't shift it out
// Signer is among recents, only fail if the current block doesn't shift it out
if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit {
return errUnauthorized
}
}
}
// Ensure that the difficulty corresponts to the turn-ness of the signer
// Ensure that the difficulty corresponds to the turn-ness of the signer
inturn := snap.inturn(header.Number.Uint64(), signer)
if inturn && header.Difficulty.Cmp(diffInTurn) != 0 {
return errInvalidDifficulty
@ -493,18 +498,29 @@ func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, p
// Prepare implements consensus.Engine, preparing all the consensus fields of the
// header for running the transactions on top.
func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) error {
// If the block isn't a checkpoint, cast a random vote (good enough fror now)
// If the block isn't a checkpoint, cast a random vote (good enough for now)
header.Coinbase = common.Address{}
header.Nonce = types.BlockNonce{}
number := header.Number.Uint64()
// Assemble the voting snapshot to check which votes make sense
snap, err := c.snapshot(chain, number-1, header.ParentHash, nil)
if err != nil {
return err
}
if number%c.config.Epoch != 0 {
c.lock.RLock()
if len(c.proposals) > 0 {
addresses := make([]common.Address, 0, len(c.proposals))
for address := range c.proposals {
// Gather all the proposals that make sense voting on
addresses := make([]common.Address, 0, len(c.proposals))
for address, authorize := range c.proposals {
if snap.validVote(address, authorize) {
addresses = append(addresses, address)
}
}
// If there's pending proposals, cast a vote on them
if len(addresses) > 0 {
header.Coinbase = addresses[rand.Intn(len(addresses))]
if c.proposals[header.Coinbase] {
copy(header.Nonce[:], nonceAuthVote)
@ -514,11 +530,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro
}
c.lock.RUnlock()
}
// Assemble the voting snapshot and set the correct difficulty
snap, err := c.snapshot(chain, number-1, header.ParentHash, nil)
if err != nil {
return err
}
// Set the correct difficulty
header.Difficulty = diffNoTurn
if snap.inturn(header.Number.Uint64(), c.signer) {
header.Difficulty = diffInTurn
@ -595,11 +607,11 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-ch
if _, authorized := snap.Signers[signer]; !authorized {
return nil, errUnauthorized
}
// If we're amongs the recent signers, wait for the next block
// If we're amongst the recent signers, wait for the next block
for seen, recent := range snap.Recents {
if recent == signer {
// Signer is among recents, only wait if the current block doens't shift it out
if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit {
// Signer is among recents, only wait if the current block doesn't shift it out
if limit := uint64(len(snap.Signers)/2 + 1); number < limit || seen > number-limit {
log.Info("Signed recently, must wait for others")
<-stop
return nil, nil

View File

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
lru "github.com/hashicorp/golang-lru"
)
// Vote represents a single vote that an authorized signer made to modify the
@ -38,13 +39,14 @@ type Vote struct {
// Tally is a simple vote tally to keep the current score of votes. Votes that
// go against the proposal aren't counted since it's equivalent to not voting.
type Tally struct {
Authorize bool `json:"authorize"` // Whether the vote it about authorizing or kicking someone
Authorize bool `json:"authorize"` // Whether the vote is about authorizing or kicking someone
Votes int `json:"votes"` // Number of votes until now wanting to pass the proposal
}
// Snapshot is the state of the authorization voting at a given point in time.
type Snapshot struct {
config *params.CliqueConfig // Consensus engine parameters to fine tune behavior
config *params.CliqueConfig // Consensus engine parameters to fine tune behavior
sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover
Number uint64 `json:"number"` // Block number where the snapshot was created
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
@ -54,17 +56,18 @@ type Snapshot struct {
Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating
}
// newSnapshot create a new snapshot with the specified startup parameters. This
// newSnapshot creates a new snapshot with the specified startup parameters. This
// method does not initialize the set of recent signers, so only ever use if for
// the genesis block.
func newSnapshot(config *params.CliqueConfig, number uint64, hash common.Hash, signers []common.Address) *Snapshot {
func newSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, signers []common.Address) *Snapshot {
snap := &Snapshot{
config: config,
Number: number,
Hash: hash,
Signers: make(map[common.Address]struct{}),
Recents: make(map[uint64]common.Address),
Tally: make(map[common.Address]Tally),
config: config,
sigcache: sigcache,
Number: number,
Hash: hash,
Signers: make(map[common.Address]struct{}),
Recents: make(map[uint64]common.Address),
Tally: make(map[common.Address]Tally),
}
for _, signer := range signers {
snap.Signers[signer] = struct{}{}
@ -73,7 +76,7 @@ func newSnapshot(config *params.CliqueConfig, number uint64, hash common.Hash, s
}
// loadSnapshot loads an existing snapshot from the database.
func loadSnapshot(config *params.CliqueConfig, db ethdb.Database, hash common.Hash) (*Snapshot, error) {
func loadSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*Snapshot, error) {
blob, err := db.Get(append([]byte("clique-"), hash[:]...))
if err != nil {
return nil, err
@ -83,6 +86,7 @@ func loadSnapshot(config *params.CliqueConfig, db ethdb.Database, hash common.Ha
return nil, err
}
snap.config = config
snap.sigcache = sigcache
return snap, nil
}
@ -99,13 +103,14 @@ func (s *Snapshot) store(db ethdb.Database) error {
// copy creates a deep copy of the snapshot, though not the individual votes.
func (s *Snapshot) copy() *Snapshot {
cpy := &Snapshot{
config: s.config,
Number: s.Number,
Hash: s.Hash,
Signers: make(map[common.Address]struct{}),
Recents: make(map[uint64]common.Address),
Votes: make([]*Vote, len(s.Votes)),
Tally: make(map[common.Address]Tally),
config: s.config,
sigcache: s.sigcache,
Number: s.Number,
Hash: s.Hash,
Signers: make(map[common.Address]struct{}),
Recents: make(map[uint64]common.Address),
Votes: make([]*Vote, len(s.Votes)),
Tally: make(map[common.Address]Tally),
}
for signer := range s.Signers {
cpy.Signers[signer] = struct{}{}
@ -121,11 +126,17 @@ func (s *Snapshot) copy() *Snapshot {
return cpy
}
// validVote returns whether it makes sense to cast the specified vote in the
// given snapshot context (e.g. don't try to add an already authorized signer).
func (s *Snapshot) validVote(address common.Address, authorize bool) bool {
_, signer := s.Signers[address]
return (signer && !authorize) || (!signer && authorize)
}
// cast adds a new vote into the tally.
func (s *Snapshot) cast(address common.Address, authorize bool) bool {
// Ensure the vote is meaningful
_, signer := s.Signers[address]
if (signer && authorize) || (!signer && !authorize) {
if !s.validVote(address, authorize) {
return false
}
// Cast the vote into an existing or new tally
@ -190,7 +201,7 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
delete(snap.Recents, number-limit)
}
// Resolve the authorization key and check against signers
signer, err := ecrecover(header)
signer, err := ecrecover(header, s.sigcache)
if err != nil {
return nil, err
}

View File

@ -243,7 +243,7 @@ func TestVoting(t *testing.T) {
},
results: []string{"A", "B"},
}, {
// Cascading changes are not allowed, only the the account being voted on may change
// Cascading changes are not allowed, only the account being voted on may change
signers: []string{"A", "B", "C", "D"},
votes: []testerVote{
{signer: "A", voted: "C", auth: false},
@ -293,7 +293,7 @@ func TestVoting(t *testing.T) {
results: []string{"A", "B", "C"},
}, {
// Ensure that pending votes don't survive authorization status changes. This
// corner case can only appear if a signer is quickly added, remove and then
// corner case can only appear if a signer is quickly added, removed and then
// readded (or the inverse), while one of the original voters dropped. If a
// past vote is left cached in the system somewhere, this will interfere with
// the final signer outcome.

View File

@ -79,8 +79,7 @@ type Engine interface {
// Finalize runs any post-transaction state modifications (e.g. block rewards)
// and assembles the final block.
//
// Note, the block header and state database might be updated to reflect any
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)

View File

@ -27,6 +27,7 @@ import (
"unsafe"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/bitutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/log"
@ -52,7 +53,6 @@ type hasher func(dest []byte, data []byte)
// makeHasher creates a repetitive hasher, allowing the same hash data structures
// to be reused between hash runs instead of requiring new ones to be created.
//
// The returned function is not thread safe!
func makeHasher(h hash.Hash) hasher {
return func(dest []byte, data []byte) {
@ -81,7 +81,6 @@ func seedHash(block uint64) []byte {
// memory, then performing two passes of Sergio Demian Lerner's RandMemoHash
// algorithm from Strict Memory Hard Hashing Functions (2014). The output is a
// set of 524288 64-byte values.
//
// This method places the result into dest in machine byte order.
func generateCache(dest []uint32, epoch uint64, seed []byte) {
// Print some debug logs to allow analysis on low end devices
@ -142,7 +141,7 @@ func generateCache(dest []uint32, epoch uint64, seed []byte) {
dstOff = j * hashBytes
xorOff = (binary.LittleEndian.Uint32(cache[dstOff:]) % uint32(rows)) * hashBytes
)
xorBytes(temp, cache[srcOff:srcOff+hashBytes], cache[xorOff:xorOff+hashBytes])
bitutil.XORBytes(temp, cache[srcOff:srcOff+hashBytes], cache[xorOff:xorOff+hashBytes])
keccak512(cache[dstOff:], temp)
atomic.AddUint32(&progress, 1)
@ -219,7 +218,6 @@ func generateDatasetItem(cache []uint32, index uint32, keccak512 hasher) []byte
}
// generateDataset generates the entire ethash dataset for mining.
//
// This method places the result into dest in machine byte order.
func generateDataset(dest []uint32, epoch uint64, cache []uint32) {
// Print some debug logs to allow analysis on low end devices

View File

@ -20,7 +20,7 @@ package ethash
import "testing"
// Tests whether the dataset size calculator work correctly by cross checking the
// 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) {
var tests []uint64

View File

@ -218,7 +218,6 @@ func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Blo
// verifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine.
//
// See YP section 4.3.4. "Block Header Validity"
func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *types.Header, uncle bool, seal bool) error {
// Ensure that the header's extra-data section is of a reasonable size
@ -239,10 +238,19 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
return errZeroBlockTime
}
// Verify the block's difficulty based in it's timestamp and parent's difficulty
expected := CalcDifficulty(chain.Config(), header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
expected := CalcDifficulty(chain.Config(), header.Time.Uint64(), parent)
if expected.Cmp(header.Difficulty) != 0 {
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected)
}
// Verify that the gas limit is <= 2^63-1
if header.GasLimit.Cmp(math.MaxBig63) > 0 {
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, math.MaxBig63)
}
// Verify that the gasUsed is <= gasLimit
if header.GasUsed.Cmp(header.GasLimit) > 0 {
return fmt.Errorf("invalid gasUsed: have %v, gasLimit %v", header.GasUsed, header.GasLimit)
}
// Verify that the gas limit remains within allowed bounds
diff := new(big.Int).Set(parent.GasLimit)
diff = diff.Sub(diff, header.GasLimit)
@ -274,16 +282,18 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
return nil
}
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
// that a new block should have when created at time given the parent block's time
// and difficulty.
//
// CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time
// given the parent block's time and difficulty.
// TODO (karalabe): Move the chain maker into this package and make this private!
func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) {
return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff)
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
next := new(big.Int).Add(parent.Number, common.Big1)
switch {
case config.IsHomestead(next):
return calcDifficultyHomestead(time, parent)
default:
return calcDifficultyFrontier(time, parent)
}
return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff)
}
// Some weird constants to avoid constant memory allocs for them.
@ -296,7 +306,7 @@ var (
// calcDifficultyHomestead is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time given the
// parent block's time and difficulty. The calculation uses the Homestead rules.
func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
func calcDifficultyHomestead(time uint64, parent *types.Header) *big.Int {
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki
// algorithm:
// diff = (parent_diff +
@ -304,7 +314,7 @@ func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *
// ) + 2^(periodCount - 2)
bigTime := new(big.Int).SetUint64(time)
bigParentTime := new(big.Int).SetUint64(parentTime)
bigParentTime := new(big.Int).Set(parent.Time)
// holds intermediate values to make the algo easier to read & audit
x := new(big.Int)
@ -320,16 +330,16 @@ func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *
x.Set(bigMinus99)
}
// (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
y.Div(parentDiff, params.DifficultyBoundDivisor)
y.Div(parent.Difficulty, params.DifficultyBoundDivisor)
x.Mul(y, x)
x.Add(parentDiff, x)
x.Add(parent.Difficulty, x)
// minimum difficulty can ever be (before exponential factor)
if x.Cmp(params.MinimumDifficulty) < 0 {
x.Set(params.MinimumDifficulty)
}
// for the exponential factor
periodCount := new(big.Int).Add(parentNumber, common.Big1)
periodCount := new(big.Int).Add(parent.Number, common.Big1)
periodCount.Div(periodCount, expDiffPeriod)
// the exponential factor, commonly referred to as "the bomb"
@ -345,25 +355,25 @@ func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *
// calcDifficultyFrontier is the difficulty adjustment algorithm. It returns the
// difficulty that a new block should have when created at time given the parent
// block's time and difficulty. The calculation uses the Frontier rules.
func calcDifficultyFrontier(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int {
diff := new(big.Int)
adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor)
adjust := new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisor)
bigTime := new(big.Int)
bigParentTime := new(big.Int)
bigTime.SetUint64(time)
bigParentTime.SetUint64(parentTime)
bigParentTime.Set(parent.Time)
if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 {
diff.Add(parentDiff, adjust)
diff.Add(parent.Difficulty, adjust)
} else {
diff.Sub(parentDiff, adjust)
diff.Sub(parent.Difficulty, adjust)
}
if diff.Cmp(params.MinimumDifficulty) < 0 {
diff.Set(params.MinimumDifficulty)
}
periodCount := new(big.Int).Add(parentNumber, common.Big1)
periodCount := new(big.Int).Add(parent.Number, common.Big1)
periodCount.Div(periodCount, expDiffPeriod)
if periodCount.Cmp(common.Big1) > 0 {
// diff = diff + 2^(periodCount - 2)
@ -425,8 +435,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header)
if parent == nil {
return consensus.ErrUnknownAncestor
}
header.Difficulty = CalcDifficulty(chain.Config(), header.Time.Uint64(),
parent.Time.Uint64(), parent.Number, parent.Difficulty)
header.Difficulty = CalcDifficulty(chain.Config(), header.Time.Uint64(), parent)
return nil
}
@ -451,7 +460,6 @@ var (
// AccumulateRewards credits the coinbase of the given block with the mining
// reward. The total reward consists of the static block reward and rewards for
// included uncles. The coinbase of each uncle block is also rewarded.
//
// TODO (karalabe): Move the chain maker into this package and make this private!
func AccumulateRewards(state *state.StateDB, header *types.Header, uncles []*types.Header) {
reward := new(big.Int).Set(blockReward)

View File

@ -20,9 +20,11 @@ import (
"encoding/json"
"math/big"
"os"
"path/filepath"
"testing"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
)
@ -56,9 +58,9 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
}
func TestCalcDifficulty(t *testing.T) {
file, err := os.Open("../../tests/files/BasicTests/difficulty.json")
file, err := os.Open(filepath.Join("..", "..", "tests", "testdata", "BasicTests", "difficulty.json"))
if err != nil {
t.Fatal(err)
t.Skip(err)
}
defer file.Close()
@ -71,7 +73,11 @@ func TestCalcDifficulty(t *testing.T) {
config := &params.ChainConfig{HomesteadBlock: big.NewInt(1150000)}
for name, test := range tests {
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
diff := CalcDifficulty(config, test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
diff := CalcDifficulty(config, test.CurrentTimestamp, &types.Header{
Number: number,
Time: new(big.Int).SetUint64(test.ParentTimestamp),
Difficulty: test.ParentDifficulty,
})
if diff.Cmp(test.CurrentDifficulty) != 0 {
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
}

View File

@ -308,14 +308,14 @@ func (d *dataset) release() {
// MakeCache generates a new ethash cache and optionally stores it to disk.
func MakeCache(block uint64, dir string) {
c := cache{epoch: block/epochLength + 1}
c := cache{epoch: block / epochLength}
c.generate(dir, math.MaxInt32, false)
c.release()
}
// MakeDataset generates a new ethash dataset and optionally stores it to disk.
func MakeDataset(block uint64, dir string) {
d := dataset{epoch: block/epochLength + 1}
d := dataset{epoch: block / epochLength}
d.generate(dir, math.MaxInt32, false)
d.release()
}
@ -355,7 +355,7 @@ type Ethash struct {
// New creates a full sized ethash PoW scheme.
func New(cachedir string, cachesinmem, cachesondisk int, dagdir string, dagsinmem, dagsondisk int) *Ethash {
if cachesinmem <= 0 {
log.Warn("One ethash cache must alwast be in memory", "requested", cachesinmem)
log.Warn("One ethash cache must always be in memory", "requested", cachesinmem)
cachesinmem = 1
}
if cachedir != "" && cachesondisk > 0 {
@ -412,7 +412,7 @@ func NewFakeDelayer(delay time.Duration) *Ethash {
return &Ethash{fakeMode: true, fakeDelay: delay}
}
// NewFullFaker creates a ethash consensus engine with a full fake scheme that
// NewFullFaker creates an ethash consensus engine with a full fake scheme that
// accepts all blocks as valid, without checking any consensus rules whatsoever.
func NewFullFaker() *Ethash {
return &Ethash{fakeMode: true, fakeFull: true}
@ -467,8 +467,9 @@ func (ethash *Ethash) cache(block uint64) []uint32 {
future = &cache{epoch: epoch + 1}
ethash.fcache = future
}
// New current cache, set its initial timestamp
current.used = time.Now()
}
current.used = time.Now()
ethash.lock.Unlock()
// Wait for generation finish, bump the timestamp and finalize the cache
@ -529,8 +530,9 @@ func (ethash *Ethash) dataset(block uint64) []uint32 {
future = &dataset{epoch: epoch + 1}
ethash.fdataset = future
}
// New current dataset, set its initial timestamp
current.used = time.Now()
}
current.used = time.Now()
ethash.lock.Unlock()
// Wait for generation finish, bump the timestamp and finalize the cache

View File

@ -1,85 +0,0 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Source: https://golang.org/src/crypto/cipher/xor.go
package ethash
import (
"runtime"
"unsafe"
)
const wordSize = int(unsafe.Sizeof(uintptr(0)))
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
// fastXORBytes xors in bulk. It only works on architectures that
// support unaligned read/writes.
func fastXORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
w := n / wordSize
if w > 0 {
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
aw := *(*[]uintptr)(unsafe.Pointer(&a))
bw := *(*[]uintptr)(unsafe.Pointer(&b))
for i := 0; i < w; i++ {
dw[i] = aw[i] ^ bw[i]
}
}
for i := (n - n%wordSize); i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
}
func safeXORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
for i := 0; i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
}
// xorBytes xors the bytes in a and b. The destination is assumed to have enough
// space. Returns the number of bytes xor'd.
func xorBytes(dst, a, b []byte) int {
if supportsUnaligned {
return fastXORBytes(dst, a, b)
}
// TODO(hanwen): if (dst, a, b) have common alignment
// we could still try fastXORBytes. It is not clear
// how often this happens, and it's only worth it if
// the block encryption itself is hardware
// accelerated.
return safeXORBytes(dst, a, b)
}
// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
// The arguments are assumed to be of equal length.
func fastXORWords(dst, a, b []byte) {
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
aw := *(*[]uintptr)(unsafe.Pointer(&a))
bw := *(*[]uintptr)(unsafe.Pointer(&b))
n := len(b) / wordSize
for i := 0; i < n; i++ {
dw[i] = aw[i] ^ bw[i]
}
}
func xorWords(dst, a, b []byte) {
if supportsUnaligned {
fastXORWords(dst, a, b)
} else {
safeXORBytes(dst, a, b)
}
}

View File

@ -54,7 +54,7 @@ func VerifyDAOHeaderExtraData(config *params.ChainConfig, header *types.Header)
if header.Number.Cmp(config.DAOForkBlock) < 0 || header.Number.Cmp(limit) >= 0 {
return nil
}
// Depending whether we support or oppose the fork, validate the extra-data contents
// Depending on whether we support or oppose the fork, validate the extra-data contents
if config.DAOForkSupport {
if !bytes.Equal(header.Extra, params.DAOForkBlockExtra) {
return ErrBadProDAOExtra

View File

@ -20,6 +20,7 @@ import (
"encoding/json"
"fmt"
"io"
"strings"
"time"
"github.com/ethereum/go-ethereum/log"
@ -240,17 +241,19 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
throwJSException(err.Error())
}
var (
rawReq = []byte(reqVal.String())
rawReq = reqVal.String()
dec = json.NewDecoder(strings.NewReader(rawReq))
reqs []jsonrpcCall
batch bool
)
dec.UseNumber() // avoid float64s
if rawReq[0] == '[' {
batch = true
json.Unmarshal(rawReq, &reqs)
dec.Decode(&reqs)
} else {
batch = false
reqs = make([]jsonrpcCall, 1)
json.Unmarshal(rawReq, &reqs[0])
dec.Decode(&reqs[0])
}
// Execute the requests.

View File

@ -2,7 +2,7 @@ FROM alpine:3.5
RUN \
apk add --update go git make gcc musl-dev linux-headers ca-certificates && \
git clone --depth 1 --branch release/1.5 https://github.com/ethereum/go-ethereum && \
git clone --depth 1 --branch release/1.6 https://github.com/ethereum/go-ethereum && \
(cd go-ethereum && make geth) && \
cp go-ethereum/build/bin/geth /geth && \
apk del go git make gcc musl-dev linux-headers && \

1
containers/vagrant/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.vagrant

View File

@ -1,29 +1,38 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
config.vm.box = "ubuntu/trusty64"
require 'yaml'
config.vm.provider "virtualbox" do |vb|
vb.memory = "2048"
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"
config.vm.synced_folder ".", "/vagrant", disabled: true
config.vm.provision "shell", inline: <<-SHELL
sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo add-apt-repository -y ppa:ethereum/ethereum-dev
sudo apt-get update
sudo apt-get install -y build-essential golang git-all
GOPATH=/home/vagrant/go go get github.com/tools/godep
sudo chown -R vagrant:vagrant ~vagrant/go
echo "export GOPATH=/home/vagrant/go" >> ~vagrant/.bashrc
echo "export PATH=\\\$PATH:\\\$GOPATH/bin:/usr/local/go/bin" >> ~vagrant/.bashrc
SHELL
end

View File

@ -0,0 +1,11 @@
#!/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

@ -0,0 +1,11 @@
#!/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

@ -0,0 +1,11 @@
#!/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

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