mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
Update Bor to geth-v1.9.16 (#78)
* Upgrade geth to v1.9.14 (#75) * fix: resolve conflict * chg: use bor url for import * Fix regressions from merge Co-authored-by: Jaynti Kanani <jdkanani@gmail.com> * Bor update 1.9.16 (#77) * cmd, consensus: add option to disable mmap for DAG caches/datasets (#20484) * cmd, consensus: add option to disable mmap for DAG caches/datasets * consensus: add benchmarks for mmap with/with lock * cmd/clef: add newaccount command (#20782) * cmd/clef: add newaccount command * cmd/clef: document clef_New, update API versioning * Update cmd/clef/intapi_changelog.md Co-Authored-By: ligi <ligi@ligi.de> * Update signer/core/uiapi.go Co-Authored-By: ligi <ligi@ligi.de> Co-authored-by: ligi <ligi@ligi.de> * eth: add debug_accountRange API (#19645) This new API allows reading accounts and their content by address range. Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Felix Lange <fjl@twurst.com> * travis: allow cocoapods deploy to fail (#20833) * whisper/whisperv6: decrease pow requirement in tests (#20815) * metrics: improve TestTimerFunc (#20818) The test failed due to what appears to be fluctuations in time.Sleep, which is not the actual method under test. This change modifies it so we compare the metered Max to the actual time instead of the desired time. * les: create utilities as common package (#20509) * les: move execqueue into utilities package execqueue is a util for executing queued functions in a serial order which is used by both les server and les client. Move it to common package. * les: move randselect to utilities package weighted_random_selector is a helpful tool for randomly select items maintained in a set but based on the item weight. It's used anywhere is LES package, mainly by les client but will be used in les server with very high chance. So move it into a common package as the second step for les separation. * les: rename to utils * README: update private network genesis spec with istanbul (#20841) * add istanbul and muirGlacier to genesis states in README * remove muirGlacier, relocate istanbul * whisper: fix whisper go routine leak with sync wait group (#20844) * cmd/evm: Rework execution stats (#20792) - Dump stats also for --bench flag. - From memory stats only show number and size of allocations. This is what `test -bench` shows. I doubt others like number of GC runs are any useful, but can be added if requested. - Now the mem stats are for single execution in case of --bench. * cmd/devp2p, cmd/wnode, whisper: add missing calls to Timer.Stop (#20843) * p2p/server: add UDP port mapping goroutine to wait group (#20846) * accounts/abi faster unpacking of int256 (#20850) * p2p/discv5: add missing Timer.Stop calls (#20853) * miner/worker: add missing timer.Stop call (#20857) * cmd/geth: fix bad genesis test (#20860) * eth/filters: add missing Ticker.Stop call (#20862) * eth/fetcher: add missing timer.Stop calls (#20861) * les: add missing Ticker.Stop call (#20864) * event: add missing timer.Stop call in TestFeed (#20868) * metrics: add missing calls to Ticker.Stop in tests (#20866) * ethstats: add missing Ticker.Stop call (#20867) * p2p/discv5, p2p/testing: add missing Timer.Stop calls in tests (#20869) * core: add missing Timer.Stop call in TestLogReorgs (#20870) * rpc: add missing timer.Stop calls in websocket tests (#20863) * crypto/ecies: improve concatKDF (#20836) This removes a bunch of weird code around the counter overflow check in concatKDF and makes it actually work for different hash output sizes. The overflow check worked as follows: concatKDF applies the hash function N times, where N is roundup(kdLen, hashsize) / hashsize. N should not overflow 32 bits because that would lead to a repetition in the KDF output. A couple issues with the overflow check: - It used the hash.BlockSize, which is wrong because the block size is about the input of the hash function. Luckily, all standard hash functions have a block size that's greater than the output size, so concatKDF didn't crash, it just generated too much key material. - The check used big.Int to compare against 2^32-1. - The calculation could still overflow before reaching the check. The new code in concatKDF doesn't check for overflow. Instead, there is a new check on ECIESParams which ensures that params.KeyLen is < 512. This removes any possibility of overflow. There are a couple of miscellaneous improvements bundled in with this change: - The key buffer is pre-allocated instead of appending the hash output to an initially empty slice. - The code that uses concatKDF to derive keys is now shared between Encrypt and Decrypt. - There was a redundant invocation of IsOnCurve in Decrypt. This is now removed because elliptic.Unmarshal already checks whether the input is a valid curve point since Go 1.5. Co-authored-by: Felix Lange <fjl@twurst.com> * rpc: metrics for JSON-RPC method calls (#20847) This adds a couple of metrics for tracking the timing and frequency of method calls: - rpc/requests gauge counts all requests - rpc/success gauge counts requests which return err == nil - rpc/failure gauge counts requests which return err != nil - rpc/duration/all timer tracks timing of all requests - rpc/duration/<method>/<success/failure> tracks per-method timing * mobile: use bind.NewKeyedTransactor instead of duplicating (#20888) It's better to reuse the existing code to create a keyed transactor than to rewrite the logic again. * all: fix a bunch of inconsequential goroutine leaks (#20667) The leaks were mostly in unit tests, and could all be resolved by adding suitably-sized channel buffers or by restructuring the test to not send on a channel after an error has occurred. There is an unavoidable goroutine leak in Console.Interactive: when we receive a signal, the line reader cannot be unblocked and will get stuck. This leak is now documented and I've tried to make it slightly less bad by adding a one-element buffer to the output channels of the line-reading loop. Should the reader eventually awake from its blocked state (i.e. when stdin is closed), at least it won't get stuck trying to send to the interpreter loop which has quit long ago. Co-authored-by: Felix Lange <fjl@twurst.com> * internal/ethapi: add CallArgs.ToMessage method (#20854) ToMessage is used to convert between ethapi.CallArgs and types.Message. It reduces the length of the DoCall method by about half by abstracting out the conversion between the CallArgs and the Message. This should improve the code's maintainability and reusability. * eth, les: fix flaky tests (#20897) * les: fix flaky test * eth: fix flaky test * cmd/geth: enable metrics for geth import command (#20738) * cmd/geth: enable metrics for geth import command * cmd/geth: enable metrics-flags for import command * core/vm: use a callcontext struct (#20761) * core/vm: use a callcontext struct * core/vm: fix tests * core/vm/runtime: benchmark * core/vm: make intpool push inlineable, unexpose callcontext * docs/audits: add discv5 protocol audits from LA and C53 (#20898) * .github: change gitter reference to discord link in issue template (#20896) * couple of fixes to docs in clef (#20900) * p2p/discover: add initial discovery v5 implementation (#20750) This adds an implementation of the current discovery v5 spec. There is full integration with cmd/devp2p and enode.Iterator in this version. In theory we could enable the new protocol as a replacement of discovery v4 at any time. In practice, there will likely be a few more changes to the spec and implementation before this can happen. * build: upgrade to golangci-lint 1.24.0 (#20901) * accounts/scwallet: remove unnecessary uses of fmt.Sprintf * cmd/puppeth: remove unnecessary uses of fmt.Sprintf * p2p/discv5: remove unnecessary use of fmt.Sprintf * whisper/mailserver: remove unnecessary uses of fmt.Sprintf * core: goimports -w tx_pool_test.go * eth/downloader: goimports -w downloader_test.go * build: upgrade to golangci-lint 1.24.0 * whisper/mailserver : recover corrupt db files before opening (#20891) * whisper/mailserver : recover db file when openfile corrupted * whisper/mailserver : fix db -> s.db * whisper/mailserver : common/errors for dbfile * accounts/abi/bind: Refactored topics (#20851) * accounts/abi/bind: refactored topics * accounts/abi/bind: use store function to remove code duplication * accounts/abi/bind: removed unused type defs * accounts/abi/bind: error on tuples in topics * Cosmetic changes to restart travis build Co-authored-by: Guillaume Ballet <gballet@gmail.com> * node: allow websocket and HTTP on the same port (#20810) This change makes it possible to run geth with JSON-RPC over HTTP and WebSocket on the same TCP port. The default port for WebSocket is still 8546. geth --rpc --rpcport 8545 --ws --wsport 8545 This also removes a lot of deprecated API surface from package rpc. The rpc package is now purely about serving JSON-RPC and no longer provides a way to start an HTTP server. * crypto: improve error messages in LoadECDSA (#20718) This improves error messages when the file is too short or too long. Also rewrite the test for SaveECDSA because LoadECDSA has its own test now. Co-authored-by: Felix Lange <fjl@twurst.com> * changed date of rpcstack.go since new file (#20904) * accounts/abi/bind: fixed erroneous filtering of negative ints (#20865) * accounts/abi/bind: fixed erroneous packing of negative ints * accounts/abi/bind: added test cases for negative ints in topics * accounts/abi/bind: fixed genIntType for go 1.12 * accounts/abi: minor nitpick * cmd: deprecate --testnet, use named networks instead (#20852) * cmd/utils: make goerli the default testnet * cmd/geth: explicitly rename testnet to ropsten * core: explicitly rename testnet to ropsten * params: explicitly rename testnet to ropsten * cmd: explicitly rename testnet to ropsten * miner: explicitly rename testnet to ropsten * mobile: allow for returning the goerli spec * tests: explicitly rename testnet to ropsten * docs: update readme to reflect changes to the default testnet * mobile: allow for configuring goerli and rinkeby nodes * cmd/geth: revert --testnet back to ropsten and mark as legacy * cmd/util: mark --testnet flag as deprecated * docs: update readme to properly reflect the 3 testnets * cmd/utils: add an explicit deprecation warning on startup * cmd/utils: swap goerli and ropsten in usage * cmd/geth: swap goerli and ropsten in usage * cmd/geth: if running a known preset, log it for convenience * docs: improve readme on usage of ropsten's testnet datadir * cmd/utils: check if legacy `testnet` datadir exists for ropsten * cmd/geth: check for legacy testnet path in console command * cmd/geth: use switch statement for complex conditions in main * cmd/geth: move known preset log statement to the very top * cmd/utils: create new ropsten configurations in the ropsten datadir * cmd/utils: makedatadir should check for existing testnet dir * cmd/geth: add legacy testnet flag to the copy db command * cmd/geth: add legacy testnet flag to the inspect command * les, les/lespay/client: add service value statistics and API (#20837) This PR adds service value measurement statistics to the light client. It also adds a private API that makes these statistics accessible. A follow-up PR will add the new server pool which uses these statistics to select servers with good performance. This document describes the function of the new components: https://gist.github.com/zsfelfoldi/3c7ace895234b7b345ab4f71dab102d4 Co-authored-by: rjl493456442 <garyrong0905@gmail.com> Co-authored-by: rjl493456442 <garyrong0905@gmail.com> * README: update min go version to 1.13 (#20911) * travis, appveyor, build, Dockerfile: bump Go to 1.14.2 (#20913) * travis, appveyor, build, Dockerfile: bump Go to 1.14.2 * travis, appveyor: force GO111MODULE=on for every build * core/rawdb: fix data race between Retrieve and Close (#20919) * core/rawdb: fixed data race between retrieve and close closes https://github.com/ethereum/go-ethereum/issues/20420 * core/rawdb: use non-atomic load while holding mutex * accounts/abi: implement new fallback functions (#20764) * accounts/abi: implement new fackball functions In Solidity v0.6.0, the original fallback is separated into two different sub types: fallback and receive. This PR addes the support for parsing new format abi and the relevant abigen functionalities. * accounts/abi: fix unit tests * accounts/abi: minor fixes * accounts/abi, mobile: support jave binding * accounts/abi: address marius's comment * accounts/abi: Work around the uin64 conversion issue Co-authored-by: Guillaume Ballet <gballet@gmail.com> * trie: fix concurrent usage of secKeyBuf, ref #20920 * all: simplify and fix database iteration with prefix/start (#20808) * core/state/snapshot: start fixing disk iterator seek * ethdb, rawdb, leveldb, memorydb: implement iterators with prefix and start * les, core/state/snapshot: iterator fixes * all: remove two iterator methods * all: rename Iteratee.NewIteratorWith -> NewIterator * ethdb: fix review concerns * params: update CHTs for the 1.9.13 release * params: release Geth v1.9.13 * params: begin v1.9.14 release cycle * p2p/discover: add helper methods to UDPv5 (#20918) This adds two new methods to UDPv5, AllNodes and LocalNode. AllNodes returns all the nodes stored in the local table; this is useful for the purposes of metrics collection and also debugging any potential issues with other discovery v5 implementations. LocalNode returns the local node object. The reason for exposing this is so that users can modify and set/delete new key-value entries in the local record. * event: fix inconsistency in Lock and Unlock (#20933) Co-authored-by: Felix Lange <fjl@twurst.com> * accounts/abi: Prevent recalculation of internal fields (#20895) * accounts/abi: prevent recalculation of ID, Sig and String * accounts/abi: fixed unpacking of no values * accounts/abi: multiple fixes to arguments * accounts/abi: refactored methodName and eventName This commit moves the complicated logic of how we assign method names and event names if they already exist into their own functions for better readability. * accounts/abi: prevent recalculation of internal In this commit, I changed the way we calculate the string representations, sig representations and the id's of methods. Before that these fields would be recalculated everytime someone called .Sig() .String() or .ID() on a method or an event. Additionally this commit fixes issue #20856 as we assign names to inputs with no name (input with name "" becomes "arg0") * accounts/abi: added unnamed event params test * accounts/abi: fixed rebasing errors in method sig * accounts/abi: fixed rebasing errors in method sig * accounts/abi: addressed comments * accounts/abi: added FunctionType enumeration * accounts/abi/bind: added test for unnamed arguments * accounts/abi: improved readability in NewMethod, nitpicks * accounts/abi: method/eventName -> overloadedMethodName * cmd, core: remove override muir glacier and override istanbul (#20942) * snapshot: add Unlock before return (#20948) * Forget Unlock in snapshot * Remove Unlock before panic * core/state/snapshot: make difflayer account iterator seek operation inclusive * p2p: defer wait group done in protocol start (#20951) * go.mod : update fastcache to 1.5.7 (#20936) * all: seperate consensus error and evm internal error (#20830) * all: seperate consensus error and evm internal error There are actually two types of error will be returned when a tranaction/message call is executed: (a) consensus error (b) evm internal error. The former should be converted to a consensus issue, e.g. The sender doesn't enough asset to purchase the gas it specifies. The latter is allowed since evm itself is a blackbox and internal error is allowed to happen. This PR emphasizes the difference by introducing a executionResult structure. The evm error is embedded inside. So if any error returned, it indicates consensus issue happens. And also this PR improve the `EstimateGas` API to return the concrete revert reason if the transaction always fails * all: polish * accounts/abi/bind/backends: add tests * accounts/abi/bind/backends, internal: cleanup error message * all: address comments * core: fix lint * accounts, core, eth, internal: address comments * accounts, internal: resolve revert reason if possible * accounts, internal: address comments * core: mirror full node reorg logic in light client too (#20931) * core: fix the condition of reorg * core: fix nitpick to only retrieve head once * core: don't reorg if received chain is longer at same diff Co-authored-by: Péter Szilágyi <peterke@gmail.com> * accounts/keystore: fix double import race (#20915) * accounts/keystore: fix race in Import/ImportECDSA * accounts/keystore: added import/export tests * cmd/geth: improved TestAccountImport test * accounts/keystore: added import/export tests * accounts/keystore: fixed naming * accounts/keystore: fixed typo * accounts/keystore: use mutex instead of rwmutex * accounts: use errors instead of fmt * trie: initial implementation for range proof (#20908) * trie: initial implementation for range proof * trie: add benchmark * trie: fix lint * trie: fix minor issue * trie: unset the edge valuenode as well * trie: unset the edge valuenode as nilValuenode * core/state/snapshot: fix binary iterator (#20970) * p2p/enode: update code comment (#20972) It is possible to specify enode URLs using domain name since commitb90cdbaa79, but the code comment still said that only IP addresses are allowed. Co-authored-by: admin@komgo.io <KomgoRocks2018!> * eth: fix shutdown regression to abort downloads, not just cancel * node: shut down all node-related HTTP servers gracefully (#20956) Rather than just closing the underlying network listener to stop our HTTP servers, use the graceful shutdown procedure, waiting for any in-process requests to finish. * accounts/abi: added abi test cases, minor bug fixes (#20903) * accounts/abi: added documentation * accounts/abi: reduced usage of arguments.LengthNonIndexed * accounts/abi: simplified reflection logic * accounts/abi: moved testjson data into global declaration * accounts/abi: removed duplicate test cases * accounts/abi: reworked abi tests * accounts/abi: added more tests for abi packing * accounts/abi/bind: refactored base tests * accounts/abi: run pack tests as subtests * accounts/abi: removed duplicate tests * accounts/abi: removed unnused arguments.LengthNonIndexed Due to refactors to the code, we do not need the arguments.LengthNonIndexed function anymore. You can still get the length by calling len(arguments.NonIndexed()) * accounts/abi: added type test * accounts/abi: modified unpack test to pack test * accounts/abi: length check on arrayTy * accounts/abi: test invalid abi * accounts/abi: fixed rebase error * accounts/abi: fixed rebase errors * accounts/abi: removed unused definition * accounts/abi: merged packing/unpacking tests * accounts/abi: fixed [][][32]bytes encoding * accounts/abi: added tuple test cases * accounts/abi: renamed getMockLog -> newMockLog * accounts/abi: removed duplicate test * accounts/abi: bools -> booleans * core: add check in AddChildIndexer to avoid double lock (#20982) This fixes a theoretical double lock condition which could occur in indexer.AddChildIndexer(indexer) Nobody would ever do that though. Co-authored-by: Felix Lange <fjl@twurst.com> * rpc: make ExampleClientSubscription work with the geth API (#19483) This corrects the call to eth_getBlockByNumber, which previously returned this error: can't get latest block: missing value for required argument 1 Co-authored-by: Felix Lange <fjl@twurst.com> * core: improve TestLogRebirth (#20961) This is a resubmit of #20668 which rewrites the problematic test without any additional goroutines. It also documents the test better. The purpose of this test is checking whether log events are sent correctly when importing blocks. The test was written at a time when blockchain events were delivered asynchronously, making the check hard to pull off. Now that core.BlockChain delivers events synchronously during the call to InsertChain, the test can be simplified. Co-authored-by: BurtonQin <bobbqqin@gmail.com> * go.mod: upgrade to golang-lru v0.5.4 (#20992) golang-lru is now a go module, and the upgrade corrects a couple of minor issues. In particular, the library could crash if you inserted nil into an LRU cache. * rpc: add explicit 200 response for empty HTTP GET (#20952) * event, whisper/whisperv6: use defer where possible (#20940) * signer, log: properly escape character sequences (#20987) * signer: properly handle terminal escape characters * log: use strconv conversion instead of custom escape function * log: remove relection tests for nil * go.mod : goupnp v1.0.0 upgrade (#20996) * core/state/snapshot: implement storage iterator (#20971) * core/state/snapshot: implement storage iterator * core/state/snapshot, tests: implement helper function * core/state/snapshot: fix storage issue If an account is deleted in the tx_1 but recreated in the tx_2, the it can happen that in this diff layer, both destructedSet and storageData records this account. In this case, the storage iterator should be able to iterate the slots belong to new account but disable further iteration in deeper layers(belong to old account) * core/state/snapshot: address peter and martin's comment * core/state: address comments * core/state/snapshot: fix test * core/state/snapshot: fix journal nil deserialziation * core/state/snapshot: fix trie generator reporter (#21004) * accounts/external: fill account-cache if that hasn't already been done, fixes #20995 (#20998) * go.sum: run go mod tidy (#21014) * les: remove invalid use of t.Fatal in TestHandshake (#21012) * accounts/external: convert signature v value to 0/1 (#20997) This fixes an issue with clef, which already transforms the signature to use the legacy 27/28 encoding. Fixes #20994 * tests: cleanup snapshot generator goroutine leak * accounts/abi: removed Kind from Type struct (#21009) * accounts/abi: removed Kind from Type struct * accounts/abi: removed unused code * accounts/abi: move U256Bytes to common/math (#21020) * core/state/snapshot: release iterator after verification * cmd/utils: renames flags related to http-rpc server (#20935) * rpc flags related to starting http server renamed to http * old rpc flags aliased and still functional * pprof flags fixed * renames gpo related flags * linted * renamed rpc flags for consistency and clarity * added warn logs * added more warn logs for all deprecated flags for consistency * moves legacy flags to separate file, hides older flags under show-deprecated-flags command * legacy prefix and moved some more legacy flags to legacy file * fixed circular import * added docs * fixed imports lint error * added notes about when flags were deprecated * cmd/utils: group flags by deprecation date + reorder by date, * modified deprecated comments for consistency, added warn log for --rpc * making sure deprecated flags are still functional * show-deprecated-flags command cleaned up * fixed lint errors * corrected merge conflict * IsSet --> GlobalIsSet * uncategorized flags, if not deprecated, displayed under misc Co-authored-by: Martin Holst Swende <martin@swende.se> * tests: skip consensus test using 1GB RAM * cmd/geth: handle memfixes on 32bit arch with large RAM * build: raise test timeout back to 10 mins (#21027) * account/abi: remove superfluous type checking (#21022) * accounts/abi: added getType func to Type struct * accounts/abi: fixed tuple unpack * accounts/abi: removed type.Type * accounts/abi: added comment * accounts/abi: removed unused types * accounts/abi: removed superfluous declarations * accounts/abi: typo * eth: skip transaction announcer goroutine on eth<65 * eth: don't inadvertently enable snapshots in archive nodes (#21025) * eth: don't reassign more cache than is being made available * eth: don't inadvertently enable snapshot in a case where --snapshot wasn't given * trie: fix TestBadRangeProof unit test (#21034) * eth/downloader: minor typo fixes in comments (#21035) * core: avoid double-lock in tx_pool_test (#20984) * core/state/snapshot: don't create storage list for non-existing accounts * core/state/snapshot: fix typo (#21037) * accounts/abi/bind: add void if no return args specified (#21002) * accounts/abi/bind: add void if no return args specified Currently the java generator generates invalid input on pure/view functions that have no return type. e.g. `function f(uint u) view public {}` This is not a problem in practice as people rarely ever write functions like this. * accounts/abi/bind: use elseif instead of nested if * core/state: include zero-address in state dump if present (#21038) * Include 0x0000 address into the dump if it is present * core/state: go fmt Co-authored-by: Alexey Akhunov <akhounov@gmail.com> * core/state: abort commit if read errors have occurred (#21039) This finally adds the error check that the documentation of StateDB.dbErr promises to do. dbErr was added in9e5f03b6c(June 2017), and the check was already missing in that commit. We somehow survived without it for three years. * core/state: avoid statedb.dbErr due to emptyCode (#21051) * core/state: more verbose stateb errors * core/state: fix flaw * core/state: fixed lint Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> * build: fix CLI params for windows LNK files (#21055) * build: Fix CLI params for windows LNK files closes #21054 * Remove parameters * core/state: make GetCodeSize mirror GetCode implementation wise * accounts/abi/bind/backend, internal/ethapi: recap gas limit with balance (#21043) * accounts/abi/bind/backend, internal/ethapi: recap gas limit with balance * accounts, internal: address comment and fix lint * accounts, internal: extend log message * tiny nits to format hexutil.Big and nil properly Co-authored-by: Péter Szilágyi <peterke@gmail.com> * params, cmd/utils: remove outdated discv5 bootnodes, deprecate flags (#20949) * params: remove outdated discv5 bootnodes * cmd/utils: deprecated bootnodesv4/v5 flags * console: fix some crashes/errors in the bridge (#21050) Fixes #21046 * core/rawdb: stop freezer process as part of freezer.Close() (#21010) * core/rawdb: Stop freezer process as part of freezer.Close() When you call db.Close(), it was closing the leveldb database first, then closing the freezer, but never stopping the freezer process. This could cause the freezer to attempt to write to leveldb after leveldb had been closed, leading to a crash with a non-zero exit code. This change adds a quit channel to the freezer, and freezer.Close() will not return until the freezer process has stopped. Additionally, when you call freezerdb.Close(), it will close the AncientStore before closing leveldb, to ensure that the freezer goroutine will be stopped before leveldb is closed. * core/rawdb: Fix formatting for golint * core/rawdb: Use backoff flag to avoid repeating select * core/rawdb: Include accidentally omitted backoff * p2p: add 0 port check in dialer (#21008) * p2p: add low port check in dialer We already have a check like this for UDP ports, add a similar one in the dialer. This prevents dials to port zero and it's also an extra layer of protection against spamming HTTP servers. * p2p/discover: use errLowPort in v4 code * p2p: change port check * p2p: add comment * p2p/simulations/adapters: ensure assigned port is in all node records * params: bump CHTs for the v1.9.14 release * cmd, core, eth: background transaction indexing (#20302) * cmd, core, eth: init tx lookup in background * core/rawdb: tiny log fixes to make it clearer what's happening * core, eth: fix rebase errors * core/rawdb: make reindexing less generic, but more optimal * rlp: implement rlp list iterator * core/rawdb: new implementation of tx indexing/unindex using generic tx iterator and hashing rlp-data * core/rawdb, cmd/utils: fix review concerns * cmd/utils: fix merge issue * core/rawdb: add some log formatting polishes Co-authored-by: rjl493456442 <garyrong0905@gmail.com> Co-authored-by: Péter Szilágyi <peterke@gmail.com> * core: fixup blockchain tests (#21062) core: fixup blockchain tests * les: drop the message if the entire p2p connection is stuck (#21033) * les: drop the message if the entire p2p connection is stuck * les: fix lint * core/rawdb : log format fix for Unindexing transaction (#21064) * core/rawdb : log format fix for Unindexing transaction * core/rawdb: tiny fixup Co-authored-by: Péter Szilágyi <peterke@gmail.com> * core/rawdb: remove unused math (#21065) * miner: support disabling empty blockprecommits form the Go API (#20736) * cmd, miner: add noempty-precommit flag * cmd, miner: get rid of external flag * miner: change bool to atomic int * miner: fix tiny typo Co-authored-by: Péter Szilágyi <peterke@gmail.com> * accounts/abi: accounts/abi/bind: Move topics to abi package (#21057) * accounts/abi/bind: added test cases for waitDeployed * accounts/abi/bind: added test case for boundContract * accounts/abi/bind: removed unnecessary resolve methods * accounts/abi: moved topics from /bind to /abi * accounts/abi/bind: cleaned up format... functions * accounts/abi: improved log message * accounts/abi: added type tests * accounts/abi/bind: remove superfluous template methods * accounts/abi: allow overloaded argument names (#21060) * accounts/abi: allow overloaded argument names In solidity it is possible to create the following contract: ``` contract Overloader { struct F { uint _f; uint __f; uint f; } function f(F memory f) public {} } ``` This however resulted in a panic in the abi package. * accounts/abi fixed error handling * core: fix missing receipt on Clique crashes (#21045) * core: fix missing receipt * core: address comment * ethstats: stop report ticker in each loop cycle #21070 (#21071) Co-authored-by: Hao Duan <duan.hao@hyperchain.cn> * params: release go-ethereum v1.9.14 * params: begin v1.9.15 release cycle * cmd: fix log contexts (#21077) * accounts/abi: simplify reflection logic (#21058) * accounts/abi: simplified reflection logic * accounts/abi: simplified reflection logic * accounts/abi: removed unpack * accounts/abi: removed comments * accounts/abi: removed uneccessary complications * accounts/abi: minor changes in error messages * accounts/abi: removed unnused code * accounts/abi: fixed indexed argument unpacking * accounts/abi: removed superfluous test cases This commit removes two test cases. The first one is trivially invalid as we have the same test cases as passing in packing_test.go L375. The second one passes now, because we don't need the mapArgNamesToStructFields in unpack_atomic anymore. Checking for purely underscored arg names generally should not be something we do as the abi/contract is generally out of the control of the user. * accounts/abi: removed comments, debug println * accounts/abi: added commented out code * accounts/abi: addressed comments * accounts/abi: remove unnecessary dst.CanSet check * accounts/abi: added dst.CanSet checks * console: fix unlockAccount argument count check (#21081) * cmd/clef, signer/core: password input fixes (#20960) * cmd/clef, signer/core: use better terminal input for passwords, make it possible to avoid boot-up warning * all: move commonly used prompter to isolated (small) package * cmd/clef: Add new --acceptWarn to clef README * cmd/clef: rename flag 'acceptWarn' to 'suppress-bootwarn' Co-authored-by: ligi <ligi@ligi.de> * accounts: add blockByNumberNoLock() to avoid double-lock (#20983) * abi/bind/backends: testcase for double-lock * accounts: add blockByNumberNoLock to avoid double-lock * backend/simulated: use stateroot, not blockhash for retrieveing state Co-authored-by: Martin Holst Swende <martin@swende.se> * whisper: use canonical import name of package go-ethereum (#21099) * trie: extend range proofs with non-existence (#21000) * trie: implement range proof with non-existent edge proof * trie: fix cornercase * trie: consider empty range * trie: add singleSide test * trie: support all-elements range proof * trie: fix typo * trie: tiny typos and formulations Co-authored-by: Péter Szilágyi <peterke@gmail.com> * les, les/lespay: implement new server pool (#20758) This PR reimplements the light client server pool. It is also a first step to move certain logic into a new lespay package. This package will contain the implementation of the lespay token sale functions, the token buying and selling logic and other components related to peer selection/prioritization and service quality evaluation. Over the long term this package will be reusable for incentivizing future protocols. Since the LES peer logic is now based on enode.Iterator, it can now use DNS-based fallback discovery to find servers. This document describes the function of the new components: https://gist.github.com/zsfelfoldi/3c7ace895234b7b345ab4f71dab102d4 * miner: replace use of 'self' as receiver name (#21113) * all: fix typos in comments (#21118) * consensus/clique: remove redundant pair of parentheses (#21104) * core/vm: improve jumpdest lookup (#21123) * consensus/ethash: fix flaky test by reading seal results (#21085) * cmd/utils: fix default DNS discovery configuration (#21124) * metrics/prometheus: define TYPE once, add tests (#21068) * metrics/prometheus: define type once for histograms * metrics/prometheus: test collector * ethdb/leveldb: use timer instead of time.After (#21066) * ethstats: avoid blocking chan when received invalid stats request (#21073) * ethstats: avoid blocking chan when received invalid stats request * ethstats: minor code polishes Co-authored-by: Péter Szilágyi <peterke@gmail.com> * trie: fix for range proof (#21107) * trie: fix for range proof * trie: fix typo * build: upgrade to golangci lint v1.27.0 (#21127) * build: upgrade to golangci-lint v1.27.0 * build: raise lint timeout to 3 minutes * eth: interrupt chain insertion on shutdown (#21114) This adds a new API method on core.BlockChain to allow interrupting running data inserts, and calls the method before shutting down the downloader. The BlockChain interrupt checks are now done through a method instead of inlining the atomic load everywhere. There is no loss of efficiency from this and it makes the interrupt protocol a lot clearer because the check is defined next to the method that sets the flag. * consensus/clique: make internal error private (#21132) Co-authored-by: linjing <linjingjing@baidu.com> * trie: enforce monotonic range in prover and return end marker (#21130) * trie: add hasRightElement indicator * trie: ensure the range is monotonic increasing * trie: address comment and fix lint * trie: address comment * trie: make linter happy Co-authored-by: Péter Szilágyi <peterke@gmail.com> * eth/downloader: more context in errors (#21067) This PR makes use of go 1.13 error handling, wrapping errors and using errors.Is to check a wrapped root-cause. It also removes the travis builders for go 1.11 and go 1.12. * metrics: replace gosigar with gopsutil (#21041) * replace gosigar with gopsutil * removed check for whether GOOS is openbsd * removed accidental import of runtime * potential fix for difference in units between gosig and gopsutil * fixed lint error * remove multiplication factor * uses cpu.ClocksPerSec as the multiplication factor * changed dependency from shirou to renaynay (#20) * updated dep * switching back from using renaynay fork to using upstream as PRs were merged on upstream * removed empty line * optimized imports * tidied go mod * core/vm: EIP-2315, JUMPSUB for the EVM (#20619) * core/vm: implement EIP 2315, subroutines for the EVM * core/vm: eip 2315 - lintfix + check jump dest validity + check ret stack size constraints logger: markdown-friendly traces, validate jumpdest, more testcase, correct opcodes * core/vm: update subroutines acc to eip: disallow walk-into * core/vm/eips: gas cost changes for subroutines * core/vm: update opcodes for EIP-2315 * core/vm: define RETURNSUB as a 'jumping' operation + review concerns Co-authored-by: Martin Holst Swende <martin@swende.se> * p2p/discover: move discv4 encoding to new 'v4wire' package (#21147) This moves all v4 protocol definitions to a new package, p2p/discover/v4wire. The new package will be used for low-level protocol tests. * rpc: send websocket ping when connection is idle (#21142) * rpc: send websocket ping when connection is idle * rpc: use non-blocking send for websocket pingReset * console: handle undefined + null in console funcs (#21160) * core: collect NewTxsEvent items without holding reorg lock (#21145) * core/vm, crypto/bls12381, params: add bls12-381 elliptic curve precompiles (#21018) * crypto: add bls12-381 elliptic curve wrapper * params: add bls12-381 precompile gas parameters * core/vm: add bls12-381 precompiles * core/vm: add bls12-381 precompile tests * go.mod, go.sum: use latest bls12381 lib * core/vm: move point encode/decode functions to base library * crypto/bls12381: introduce bls12-381 library init function * crypto/bls12381: import bls12381 elliptic curve implementation * go.mod, go.sum: remove bls12-381 library * remove unsued frobenious coeffs supress warning for inp that used in asm * add mappings tests for zero inputs fix swu g2 minus z inverse constant * crypto/bls12381: fix typo * crypto/bls12381: better comments for bls12381 constants * crypto/bls12381: swu, use single conditional for e2 * crypto/bls12381: utils, delete empty line * crypto/bls12381: utils, use FromHex for string to big * crypto/bls12381: g1, g2, strict length check for FromBytes * crypto/bls12381: field_element, comparision changes * crypto/bls12381: change swu, isogeny constants with hex values * core/vm: fix point multiplication comments * core/vm: fix multiexp gas calculation and lookup for g1 and g2 * core/vm: simpler imput length check for multiexp and pairing precompiles * core/vm: rm empty multiexp result declarations * crypto/bls12381: remove modulus type definition * crypto/bls12381: use proper init function * crypto/bls12381: get rid of new lines at fatal desciprtions * crypto/bls12-381: fix no-adx assembly multiplication * crypto/bls12-381: remove old config function * crypto/bls12381: update multiplication backend this commit changes mul backend to 6limb eip1962 backend mul assign operations are dropped * core/vm/contracts_tests: externalize test vectors for precompiles * core/vm/contracts_test: externalize failure-cases for precompiles * core/vm: linting * go.mod: tiny up sum file * core/vm: fix goimports linter issues * crypto/bls12381: build tags for plain ASM or ADX implementation Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Péter Szilágyi <peterke@gmail.com> * cmd, core, params: inital support for yolo-v1 testnet (#21154) * core,params,puppeth: inital support for yolo-v1 testnet * cmd/geth, core: add yolov1 console flag * cmd, core, params: YoloV1 bakein fixups Co-authored-by: Péter Szilágyi <peterke@gmail.com> * cmd/geth: fix the fork orders for DAO tests * core: filter out txs with invalid signatures as soon as possible Once we detect an invalid transaction during recovering signatures, we should directly exclude this transaction to avoid validating the signatures hereafter. This should optimize the validations times of transactions with invalid signatures to only one time. * acounts/keystore, cmd/faucet: fix faucet double import, fix twitter url * accounts/keystore, cmd/faucet: return old account to allow unlock * go.mod: upgrade go-duktape to hide unused function warning (#21168) * node: missing comma on toml tags (#21187) * core/vm, crypt/bls12381: fixed comments in bls (#21182) * core/vm: crypto/bls12381: minor code comments * crypto/bls12381: fix comment * core: fix typo in comments (#21181) * internal/ethapi: return revert reason for eth_call (#21083) * internal/ethapi: return revert reason for eth_call * internal/ethapi: moved revert reason logic to doCall * accounts/abi/bind/backends: added revert reason logic to simulated backend * internal/ethapi: fixed linting error * internal/ethapi: check if require reason can be unpacked * internal/ethapi: better error logic * internal/ethapi: simplify logic * internal/ethapi: return vmError() * internal/ethapi: move handling of revert out of docall * graphql: removed revert logic until spec change * rpc: internal/ethapi: added custom error types * graphql: use returndata instead of return Return() checks if there is an error. If an error is found, we return nil. For most use cases it can be beneficial to return the output even if there was an error. This code should be changed anyway once the spec supports error reasons in graphql responses * accounts/abi/bind/backends: added tests for revert reason * internal/ethapi: add errorCode to revert error * internal/ethapi: add errorCode of 3 to revertError * internal/ethapi: unified estimateGasErrors, simplified logic * internal/ethapi: unified handling of errors in DoEstimateGas * rpc: print error data field * accounts/abi/bind/backends: unify simulatedBackend and RPC * internal/ethapi: added binary data to revertError data * internal/ethapi: refactored unpacking logic into newRevertError * accounts/abi/bind/backends: fix EstimateGas * accounts, console, internal, rpc: minor error interface cleanups * Revert "accounts, console, internal, rpc: minor error interface cleanups" This reverts commit2d3ef53c53. * re-apply the good parts of2d3ef53c53* rpc: add test for returning server error data from client Co-authored-by: rjl493456442 <garyrong0905@gmail.com> Co-authored-by: Péter Szilágyi <peterke@gmail.com> Co-authored-by: Felix Lange <fjl@twurst.com> * params: update CHTs for 1.9.15 release * params: go-ethereum v1.9.15 stable * params: begin v1.9.16 release cycle * cmd/ethkey: support --passwordfile in generate command (#21183) * ethstats: use timer instead of time.sleep (#20924) * core/vm: use uint256 in EVM implementation (#20787) * core/vm: use fixed uint256 library instead of big * core/vm: remove intpools * core/vm: upgrade uint256, fixes uint256.NewFromBig * core/vm: use uint256.Int by value in Stack * core/vm: upgrade uint256 to v1.0.0 * core/vm: don't preallocate space for 1024 stack items (only 16) Co-authored-by: Martin Holst Swende <martin@swende.se> * core/state: avoid escape analysis fault when accessing cached state * accounts/keystore: fix typo in error message (#21200) * accounts/abi: make GetType public again (#21157) * eth/downloader: fix spuriously failing tests (#21149) * eth/downloader tests: fix spurious failing test due to race between receipts/headers * miner tests: fix travis failure on arm64 * eth/downloader: tests - store td in ancients too * core, eth, internal: include read storage entries in structlog output (#21204) * core, eth, internal: extend structLog tracer * core/vm, internal: add storage view * core, internal: add slots to storage directly * core: remove useless * core: address martin's comment * core/vm: fix tests * build, internal/ethapi, crypto/bls12381: fix typos (#21210) speicifc -> specific assigened -> assigned frobenious -> frobenius * go.mod: update uint256 to 1.1.0 (#21206) * internal/web3ext: add missing params to debug.accountRange (#21208) * eth/downloader: don't use defer for unlock before return (#21227) Co-authored-by: linjing <linjingjing@baidu.com> * core/rawdb: swap tailId and itemOffset for deleted items in freezer (#21220) * fix(freezer): tailId filenum offset were misplaced * core/rawdb: assume first item in freezer always start from zero * cmd, eth, internal, les: add txfee cap (#21212) * cmd, eth, internal, les: add gasprice cap * cmd/utils, eth: add default value for gasprice cap * all: use txfee cap * cmd, eth: add fix * cmd, internal: address comments * go.mod: update snappy dependency (#21237) * whisper : use timer.Ticker instead of sleep (#21240) * whisper : use timer.Ticker instead of sleep * lint: Fix linter error Co-authored-by: Guillaume Ballet <gballet@gmail.com> * core/rawdb: fix high memory usage in freezer (#21243) The ancients variable in the freezer is a list of hashes, which identifies all of the hashes to be frozen. The slice is being allocated with a capacity of `limit`, which is the number of the last block this batch will attempt to add to the freezer. That means we are allocating memory for all of the blocks in the freezer, not just the ones to be added. If instead we allocate `limit - f.frozen`, we will only allocate enough space for the blocks we're about to add to the freezer. On mainnet this reduces usage by about 320 MB. * common/fdlimit: build on DragonflyBSD (#21241) * common/fdlimit: build on DragonflyBSD * review feedback * whisper: fix time.sleep by time.ticker in whisper_test (#21251) * p2p: measure packet throughput too, not just bandwidth * cmd/devp2p: add commands for node key management (#21202) These commands mirror the key/URL generation functions of cmd/bootnode. $ devp2p key generate mynode.key $ devp2p key to-enode mynode.key -ip 203.0.113.21 -tcp 30304 enode://78a7746089baf4b8615f54a5f0b67b22b1... * crypto/secp256k1: enable 128-bit int code and endomorphism optimization (#21203) * crypto/secp256k1: enable use of __int128 This speeds up scalar & field calculations a lot. * crypto/secp256k1: enable endomorphism optimization * eth: don't block if transaction broadcast loop fails (#21255) * eth: don't block if transaction broadcast loop is returned * eth: kick out peer if we failed to send message * eth: address comment * trie: reduce allocs in insertPreimage (#21261) * core/vm: fix incorrect computation of BLS discount (#21253) * core/vm: fix incorrect computation of discount During testing on Yolov1 we found that the way geth calculates the discount is not in line with the specification. Basically what we did is calculate 128 * Bls12381GXMulGas * discount / 1000 whenever we received more than 128 pairs of values. Correct would be to calculate k * Bls12381... for k > 128. * core/vm: better logic for discount calculation * core/vm: better calculation logic, added worstcase benchmarks * core/vm: better benchmarking logic * cmd/geth: make import cmd exit with 1 if import errors occurred (#21244) The import command should not return a 0 status code if the import finishes prematurely becaues of an import error. Returning the error causes the program to exit with 1 if the err is non nil. Signed-off-by: meows <b5c6@protonmail.com> * eth: returned revert reason in traceTx (#21195) * eth: returned revert reason in traceTx * eth: return result data * utils: fix ineffectual miner config flags (#21271) Without use of global, these flags didn't actually modify miner configuration, since we weren't grabbing from the proper context scope, which should be global (vs. subcommand). Signed-off-by: meows <b5c6@protonmail.com> * cmd: abstract `getPassPhrase` functions into one (#21219) * [cmd] Abstract `getPassPhrase` functions into one. * cmd/ethkey: fix compilation failure Co-authored-by: rjl493456442 <garyrong0905@gmail.com> * cmd/evm: add state transition tool for testing (#20958) This PR implements the EVM state transition tool, which is intended to be the replacement for our retesteth client implementation. Documentation is present in the cmd/evm/README.md file. Co-authored-by: Felix Lange <fjl@twurst.com> * core: types: less allocations when hashing and tx handling (#21265) * core, crypto: various allocation savings regarding tx handling * core: reduce allocs for gas price comparison This change reduces the allocations needed for comparing different transactions to each other. A call to `tx.GasPrice()` copies the gas price as it has to be safe against modifications and also needs to be threadsafe. For comparing and ordering different transactions we don't need these guarantees * core: added tx.GasPriceIntCmp for comparison without allocation adds a method to remove unneeded allocation in comparison to tx.gasPrice * core/types: pool legacykeccak256 objects in rlpHash rlpHash is by far the most used function in core that allocates a legacyKeccak256 object on each call. Since it is so widely used it makes sense to add pooling here so we relieve the GC. On my machine these changes result in > 100 MILLION less allocations and > 30 GB less allocated memory. * reverted some changes * reverted some changes * trie: use crypto.KeccakState instead of replicating code Co-authored-by: Martin Holst Swende <martin@swende.se> * go.mod: bump gopsutil version (#21275) * go.mod: updated crypto deps causing build failure (#21276) * eth/downloader: fixes data race between synchronize and other methods (#21201) * eth/downloaded: fixed datarace between synchronize and Progress There was a race condition between `downloader.synchronize()` and `Progress` `syncWithPeer` `fetchHeight` `findAncestors` and `processHeaders` This PR changes the behavior of the downloader a bit. Previously the functions `Progress` `syncWithPeer` `fetchHeight` `findAncestors` and `processHeaders` read the syncMode anew within their loops. Now they read the syncMode at the start of their function and don't change it during their runtime. * eth/downloaded: comment * eth/downloader: added comment * cmd/geth: LES priority client test (#20719) This adds a regression test for the LES priority client API. * rlp: avoid list header allocation in encoder (#21274) List headers made up 11% of all allocations during sync. This change removes most of those allocations by keeping the list header values cached in the encoder buffer instead. Since encoder buffers are pooled, list headers are no longer allocated in the common case where an encoder buffer is available for reuse. Co-authored-by: Felix Lange <fjl@twurst.com> * core, txpool: less allocations when handling transactions (#21232) * core: use uint64 for total tx costs instead of big.Int * core: added local tx pool test case * core, crypto: various allocation savings regarding tx handling * Update core/tx_list.go * core: added tx.GasPriceIntCmp for comparison without allocation adds a method to remove unneeded allocation in comparison to tx.gasPrice * core: handle pools full of locals better * core/tests: benchmark for tx_list * core/txlist, txpool: save a reheap operation, avoid some bigint allocs Co-authored-by: Martin Holst Swende <martin@swende.se> * rpc, internal/ethapi: default rpc gascap at 25M + better error message (#21229) * rpc, internal/ethapi: default rpc gascap at 50M + better error message * eth,internal: make globalgascap uint64 * core/tests: fix compilation failure * eth/config: gascap at 25M + minor review concerns * internal/ethapi: default gas to maxgascap, not max int64 (#21284) * ethapi: don't crash when keystore-specific methods are called but external signer used (#21279) * console: prevent importRawKey from getting into CLI history * internal/ethapi: error on keystore-methods when no keystore is present * les/checkpointoracle: don't lookup checkpoint more than once per minute (#21285) * les/checkpointoracle: don't lookup checkpoint more than once per second * les/checkpoint/oracle: change oracle checktime to 1 minute * eth/gasprice: lighter gas price oracle for light client (#20409) This PR reduces the bandwidth used by the light client to compute the recommended gas price. The current mechanism for suggesting the price is: - retrieve recent 20 blocks - get the lowest gas price of these blocks - sort the price array and return the middle(60%) one This works for full nodes, which have all blocks available locally. However, this is very expensive for the light client because the light client needs to retrieve block bodies from the network. The PR changes the default options for light client. With the new config, the light client only retrieves the two latest blocks, but in order to collect more sample transactions, the 3 lowest prices are collected from each block. This PR also changes the behavior for empty blocks. If the block is empty, the lastest price is reused for sampling. * cmd/geth: allow configuring metrics HTTP server on separate endpoint (#21290) Exposing /debug/metrics and /debug/metrics/prometheus was dependent on --pprof, which also exposes other HTTP APIs. This change makes it possible to run the metrics server on an independent endpoint without enabling pprof. * build/ci: handle split up listing (#21293) * rlp: reduce allocations for big.Int and byte array encoding (#21291) This change further improves the performance of RLP encoding by removing allocations for big.Int and [...]byte types. I have added a new benchmark that measures RLP encoding of types.Block to verify that performance is improved. * cmd, node: dump empty value config (#21296) * cmd/clef: Update README with external v6.0.0 & internal v7.0.1 APIs (#21298) Changes include: * Updates response docs for `account_new`, `account_list`, `account_signTransaction` * Removes `account_import`, `account_export` docs * Adds `account_version` docs * Updates request docs for `ui_approveListing`, `ui_approveSignData`, `ui_showInfo`, `ui_showError`, `ui_onApprovedTx` * Adds `ui_approveNewAccount`, `ui_onInputRequired` docs * cmd/devp2p: add discv4 test suite (#21163) This adds a test suite for discovery v4. The test suite is a port of the Hive suite for discovery, and will replace the current suite on Hive soon-ish. The tests can be run locally with this command: devp2p discv4 test -remote enode//... Co-authored-by: Felix Lange <fjl@twurst.com> * cmd/clef: Fix broken link in README and other minor fixes (#21303) * eth: increase timeout in TestBroadcastBlock (#21299) * go.mod: upgrade to github.com/golang/snappy with arm64 asm (#21304) * eth/downloader: fix peer idleness tracking when restarting state sync (#21260) This fixes two issues with state sync restarts: When sync restarts with a new root, some peers can have in-flight requests. Since all peers with active requests were marked idle when exiting sync, the new sync would schedule more requests for those peers. When the response for the earlier request arrived, the new sync would reject it and mark the peer idle again, rendering the peer useless until it disconnected. The other issue was that peers would not be marked idle when they had delivered a response, but the response hadn't been processed before restarting the state sync. This also made the peer useless because it would be permanently marked busy. Co-authored-by: Felix Lange <fjl@twurst.com> * Revert "core, txpool: less allocations when handling transactions (#21232)" Reverting because this change started handling account balances as uint64 in the transaction pool, which is incorrect. This reverts commitaf5c97aebe. * common/math: use math/bits intrinsics for Safe* (#21316) This is a resubmit of ledgerwatch/turbo-geth#556. The performance benefit of this change is negligible, but it does remove a TODO. * cmd/geth, cmd/puppeth: replace deprecated rpc and ws flags in tests and docs (#21317) * accounts/external: remove dependency on internal/ethapi (#21319) Fixes #20535 Co-authored-by: Felix Lange <fjl@twurst.com> * params: go-ethereum v1.9.16 stable * chg: no need GO111MODULE * chg: refactor sync event Co-authored-by: Hanjiang Yu <42531996+de1acr0ix@users.noreply.github.com> Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: ligi <ligi@ligi.de> Co-authored-by: Wenbiao Zheng <delweng@gmail.com> Co-authored-by: Felix Lange <fjl@twurst.com> Co-authored-by: Adam Schmideg <adamschmideg@users.noreply.github.com> Co-authored-by: gary rong <garyrong0905@gmail.com> Co-authored-by: Jeff Wentworth <jeff@curvegrid.com> Co-authored-by: ucwong <ucwong@126.com> Co-authored-by: Paweł Bylica <chfast@gmail.com> Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: Luke Champine <luke.champine@gmail.com> Co-authored-by: Boqin Qin <Bobbqqin@gmail.com> Co-authored-by: William Morriss <wjmelements@gmail.com> Co-authored-by: rene <41963722+renaynay@users.noreply.github.com> Co-authored-by: Guillaume Ballet <gballet@gmail.com> Co-authored-by: Raw Pong Ghmoa <58883403+q9f@users.noreply.github.com> Co-authored-by: Felföldi Zsolt <zsfelfoldi@gmail.com> Co-authored-by: Péter Szilágyi <peterke@gmail.com> Co-authored-by: Nishant Das <nish1993@hotmail.com> Co-authored-by: icodezjb <icodezjb@163.com> Co-authored-by: tgyKomgo <52910426+tgyKomgo@users.noreply.github.com> Co-authored-by: Steven E. Harris <seh@panix.com> Co-authored-by: Julian Y <jyap808@users.noreply.github.com> Co-authored-by: ploui <64719999+ploui@users.noreply.github.com> Co-authored-by: Alexey Akhunov <akhounov@gmail.com> Co-authored-by: AusIV <git@ausiv.com> Co-authored-by: Hao Duan <duanhao0814@gmail.com> Co-authored-by: Hao Duan <duan.hao@hyperchain.cn> Co-authored-by: Satpal <28562234+SatpalSandhu61@users.noreply.github.com> Co-authored-by: Martin Michlmayr <tbm@cyrius.com> Co-authored-by: yutianwu <wzxingbupt@gmail.com> Co-authored-by: meowsbits <45600330+meowsbits@users.noreply.github.com> Co-authored-by: Richard Patel <me@terorie.dev> Co-authored-by: sixdays <lj491685571@126.com> Co-authored-by: linjing <linjingjing@baidu.com> Co-authored-by: Greg Colvin <greg@colvin.org> Co-authored-by: chenglin <910372762@qq.com> Co-authored-by: kilic <onurkilic1004@gmail.com> Co-authored-by: Mariano Cortesi <mcortesi@gmail.com> Co-authored-by: Ev <ev@ethereum.org> Co-authored-by: libby kent <viskovitzzz@gmail.com> Co-authored-by: Pau <pau@dabax.net> Co-authored-by: Natsu Kagami <natsukagami@gmail.com> Co-authored-by: Yang Hau <vulxj0j8j8@gmail.com> Co-authored-by: HackyMiner <hackyminer@gmail.com> Co-authored-by: zhangsoledad <787953403@qq.com> Co-authored-by: AusIV <code@ausiv.com> Co-authored-by: meowsbits <b5c6@protonmail.com> Co-authored-by: Binacs <bin646891055@gmail.com> Co-authored-by: chris-j-h <39617426+chris-j-h@users.noreply.github.com> * fix: last index for state sub (#79) * new: add without-heimdall flag for testing purpose * new: add without-heimdall flag * fix: inputs for from and to addresses for transfer log Co-authored-by: Arpit Agarwal <93arpit@gmail.com> Co-authored-by: Hanjiang Yu <42531996+de1acr0ix@users.noreply.github.com> Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: ligi <ligi@ligi.de> Co-authored-by: Wenbiao Zheng <delweng@gmail.com> Co-authored-by: Felix Lange <fjl@twurst.com> Co-authored-by: Adam Schmideg <adamschmideg@users.noreply.github.com> Co-authored-by: gary rong <garyrong0905@gmail.com> Co-authored-by: Jeff Wentworth <jeff@curvegrid.com> Co-authored-by: ucwong <ucwong@126.com> Co-authored-by: Paweł Bylica <chfast@gmail.com> Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: Luke Champine <luke.champine@gmail.com> Co-authored-by: Boqin Qin <Bobbqqin@gmail.com> Co-authored-by: William Morriss <wjmelements@gmail.com> Co-authored-by: rene <41963722+renaynay@users.noreply.github.com> Co-authored-by: Guillaume Ballet <gballet@gmail.com> Co-authored-by: Raw Pong Ghmoa <58883403+q9f@users.noreply.github.com> Co-authored-by: Felföldi Zsolt <zsfelfoldi@gmail.com> Co-authored-by: Péter Szilágyi <peterke@gmail.com> Co-authored-by: Nishant Das <nish1993@hotmail.com> Co-authored-by: icodezjb <icodezjb@163.com> Co-authored-by: tgyKomgo <52910426+tgyKomgo@users.noreply.github.com> Co-authored-by: Steven E. Harris <seh@panix.com> Co-authored-by: Julian Y <jyap808@users.noreply.github.com> Co-authored-by: ploui <64719999+ploui@users.noreply.github.com> Co-authored-by: Alexey Akhunov <akhounov@gmail.com> Co-authored-by: AusIV <git@ausiv.com> Co-authored-by: Hao Duan <duanhao0814@gmail.com> Co-authored-by: Hao Duan <duan.hao@hyperchain.cn> Co-authored-by: Satpal <28562234+SatpalSandhu61@users.noreply.github.com> Co-authored-by: Martin Michlmayr <tbm@cyrius.com> Co-authored-by: yutianwu <wzxingbupt@gmail.com> Co-authored-by: meowsbits <45600330+meowsbits@users.noreply.github.com> Co-authored-by: Richard Patel <me@terorie.dev> Co-authored-by: sixdays <lj491685571@126.com> Co-authored-by: linjing <linjingjing@baidu.com> Co-authored-by: Greg Colvin <greg@colvin.org> Co-authored-by: chenglin <910372762@qq.com> Co-authored-by: kilic <onurkilic1004@gmail.com> Co-authored-by: Mariano Cortesi <mcortesi@gmail.com> Co-authored-by: Ev <ev@ethereum.org> Co-authored-by: libby kent <viskovitzzz@gmail.com> Co-authored-by: Pau <pau@dabax.net> Co-authored-by: Natsu Kagami <natsukagami@gmail.com> Co-authored-by: Yang Hau <vulxj0j8j8@gmail.com> Co-authored-by: HackyMiner <hackyminer@gmail.com> Co-authored-by: zhangsoledad <787953403@qq.com> Co-authored-by: AusIV <code@ausiv.com> Co-authored-by: meowsbits <b5c6@protonmail.com> Co-authored-by: Binacs <bin646891055@gmail.com> Co-authored-by: chris-j-h <39617426+chris-j-h@users.noreply.github.com>
This commit is contained in:
parent
b7fced9df8
commit
cc988544f9
952 changed files with 372578 additions and 72473 deletions
6
.github/CODEOWNERS
vendored
6
.github/CODEOWNERS
vendored
|
|
@ -16,8 +16,8 @@ light/ @zsfelfoldi @rjl493456442
|
||||||
mobile/ @karalabe @ligi
|
mobile/ @karalabe @ligi
|
||||||
p2p/ @fjl @zsfelfoldi
|
p2p/ @fjl @zsfelfoldi
|
||||||
rpc/ @fjl @holiman
|
rpc/ @fjl @holiman
|
||||||
p2p/simulations @zelig @nonsense @janos @justelad
|
p2p/simulations @zelig @janos @justelad
|
||||||
p2p/protocols @zelig @nonsense @janos @justelad
|
p2p/protocols @zelig @janos @justelad
|
||||||
p2p/testing @zelig @nonsense @janos @justelad
|
p2p/testing @zelig @janos @justelad
|
||||||
signer/ @holiman
|
signer/ @holiman
|
||||||
whisper/ @gballet @gluk256
|
whisper/ @gballet @gluk256
|
||||||
|
|
|
||||||
4
.github/ISSUE_TEMPLATE.md
vendored
4
.github/ISSUE_TEMPLATE.md
vendored
|
|
@ -1,8 +1,8 @@
|
||||||
Hi there,
|
Hi there,
|
||||||
|
|
||||||
please note that this is an issue tracker reserved for bug reports and feature requests.
|
Please note that this is an issue tracker reserved for bug reports and feature requests.
|
||||||
|
|
||||||
For general questions please use the gitter channel or the Ethereum stack exchange at https://ethereum.stackexchange.com.
|
For general questions please use [discord](https://discord.gg/nthXNEv) or the Ethereum stack exchange at https://ethereum.stackexchange.com.
|
||||||
|
|
||||||
#### System information
|
#### System information
|
||||||
|
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -24,6 +24,7 @@ build/_vendor/pkg
|
||||||
|
|
||||||
# used by the Makefile
|
# used by the Makefile
|
||||||
/build/_workspace/
|
/build/_workspace/
|
||||||
|
/build/cache/
|
||||||
/build/bin/
|
/build/bin/
|
||||||
/geth*.zip
|
/geth*.zip
|
||||||
|
|
||||||
|
|
|
||||||
50
.golangci.yml
Normal file
50
.golangci.yml
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# This file configures github.com/golangci/golangci-lint.
|
||||||
|
|
||||||
|
run:
|
||||||
|
timeout: 3m
|
||||||
|
tests: true
|
||||||
|
# default is true. Enables skipping of directories:
|
||||||
|
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||||
|
skip-dirs-use-default: true
|
||||||
|
skip-files:
|
||||||
|
- core/genesis_alloc.go
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- deadcode
|
||||||
|
- goconst
|
||||||
|
- goimports
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- misspell
|
||||||
|
# - staticcheck
|
||||||
|
- unconvert
|
||||||
|
# - unused
|
||||||
|
- varcheck
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
gofmt:
|
||||||
|
simplify: true
|
||||||
|
goconst:
|
||||||
|
min-len: 3 # minimum length of string constant
|
||||||
|
min-occurrences: 6 # minimum number of occurrences
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
- path: crypto/blake2b/
|
||||||
|
linters:
|
||||||
|
- deadcode
|
||||||
|
- path: crypto/bn256/cloudflare
|
||||||
|
linters:
|
||||||
|
- deadcode
|
||||||
|
- path: p2p/discv5/
|
||||||
|
linters:
|
||||||
|
- deadcode
|
||||||
|
- path: core/vm/instructions_test.go
|
||||||
|
linters:
|
||||||
|
- goconst
|
||||||
|
- path: cmd/faucet/
|
||||||
|
linters:
|
||||||
|
- deadcode
|
||||||
114
.travis.yml
114
.travis.yml
|
|
@ -1,32 +1,69 @@
|
||||||
language: go
|
language: go
|
||||||
go_import_path: github.com/maticnetwork/bor
|
go_import_path: github.com/maticnetwork/bor
|
||||||
sudo: false
|
sudo: false
|
||||||
matrix:
|
jobs:
|
||||||
include:
|
allow_failures:
|
||||||
- os: linux
|
- stage: build
|
||||||
dist: xenial
|
os: osx
|
||||||
go: 1.10.x
|
go: 1.14.x
|
||||||
script:
|
env:
|
||||||
- go run build/ci.go install
|
- azure-osx
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
- azure-ios
|
||||||
|
- cocoapods-ios
|
||||||
|
|
||||||
- os: linux
|
include:
|
||||||
|
# This builder only tests code linters on latest version of Go
|
||||||
|
- stage: lint
|
||||||
|
os: linux
|
||||||
dist: xenial
|
dist: xenial
|
||||||
go: 1.11.x
|
go: 1.14.x
|
||||||
|
env:
|
||||||
|
- lint
|
||||||
|
git:
|
||||||
|
submodules: false # avoid cloning ethereum/tests
|
||||||
|
script:
|
||||||
|
- go run build/ci.go lint
|
||||||
|
|
||||||
|
- stage: build
|
||||||
|
os: linux
|
||||||
|
dist: xenial
|
||||||
|
go: 1.13.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go install
|
- go run build/ci.go install
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||||
|
|
||||||
# These are the latest Go versions.
|
# These are the latest Go versions.
|
||||||
- os: linux
|
- stage: build
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
dist: xenial
|
dist: xenial
|
||||||
go: 1.12.x
|
go: 1.14.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go install
|
- go run build/ci.go install
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||||
|
|
||||||
- os: osx
|
- stage: build
|
||||||
go: 1.12.x
|
if: type = pull_request
|
||||||
|
os: linux
|
||||||
|
arch: arm64
|
||||||
|
dist: xenial
|
||||||
|
go: 1.14.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
|
script:
|
||||||
|
- go run build/ci.go install
|
||||||
|
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||||
|
|
||||||
|
- stage: build
|
||||||
|
os: osx
|
||||||
|
osx_image: xcode11.3
|
||||||
|
go: 1.14.x
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
script:
|
script:
|
||||||
- echo "Increase the maximum number of open file descriptors on macOS"
|
- echo "Increase the maximum number of open file descriptors on macOS"
|
||||||
- NOFILE=20480
|
- NOFILE=20480
|
||||||
|
|
@ -40,24 +77,15 @@ matrix:
|
||||||
- go run build/ci.go install
|
- go run build/ci.go install
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||||
|
|
||||||
# This builder only tests code linters on latest version of Go
|
|
||||||
- os: linux
|
|
||||||
dist: xenial
|
|
||||||
go: 1.12.x
|
|
||||||
env:
|
|
||||||
- lint
|
|
||||||
git:
|
|
||||||
submodules: false # avoid cloning ethereum/tests
|
|
||||||
script:
|
|
||||||
- go run build/ci.go lint
|
|
||||||
|
|
||||||
# This builder does the Ubuntu PPA upload
|
# This builder does the Ubuntu PPA upload
|
||||||
- if: type = push
|
- stage: build
|
||||||
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: xenial
|
||||||
go: 1.12.x
|
go: 1.14.x
|
||||||
env:
|
env:
|
||||||
- ubuntu-ppa
|
- ubuntu-ppa
|
||||||
|
- GO111MODULE=on
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
addons:
|
addons:
|
||||||
|
|
@ -71,16 +99,18 @@ matrix:
|
||||||
- python-paramiko
|
- python-paramiko
|
||||||
script:
|
script:
|
||||||
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
|
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
|
||||||
- go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
|
- go run build/ci.go debsrc -goversion 1.14.2 -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
|
||||||
|
|
||||||
# This builder does the Linux Azure uploads
|
# This builder does the Linux Azure uploads
|
||||||
- if: type = push
|
- stage: build
|
||||||
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: xenial
|
||||||
sudo: required
|
sudo: required
|
||||||
go: 1.12.x
|
go: 1.14.x
|
||||||
env:
|
env:
|
||||||
- azure-linux
|
- azure-linux
|
||||||
|
- GO111MODULE=on
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
addons:
|
addons:
|
||||||
|
|
@ -108,14 +138,16 @@ matrix:
|
||||||
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
# This builder does the Linux Azure MIPS xgo uploads
|
# This builder does the Linux Azure MIPS xgo uploads
|
||||||
- if: type = push
|
- stage: build
|
||||||
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: xenial
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
go: 1.12.x
|
go: 1.14.x
|
||||||
env:
|
env:
|
||||||
- azure-linux-mips
|
- azure-linux-mips
|
||||||
|
- GO111MODULE=on
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
script:
|
script:
|
||||||
|
|
@ -136,7 +168,8 @@ matrix:
|
||||||
- go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
- go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
# This builder does the Android Maven and Azure uploads
|
# This builder does the Android Maven and Azure uploads
|
||||||
- if: type = push
|
- stage: build
|
||||||
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: xenial
|
||||||
addons:
|
addons:
|
||||||
|
|
@ -155,10 +188,11 @@ matrix:
|
||||||
env:
|
env:
|
||||||
- azure-android
|
- azure-android
|
||||||
- maven-android
|
- maven-android
|
||||||
|
- GO111MODULE=on
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
before_install:
|
before_install:
|
||||||
- curl https://dl.google.com/go/go1.12.linux-amd64.tar.gz | tar -xz
|
- curl https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz | tar -xz
|
||||||
- export PATH=`pwd`/go/bin:$PATH
|
- export PATH=`pwd`/go/bin:$PATH
|
||||||
- export GOROOT=`pwd`/go
|
- export GOROOT=`pwd`/go
|
||||||
- export GOPATH=$HOME/go
|
- export GOPATH=$HOME/go
|
||||||
|
|
@ -173,13 +207,15 @@ matrix:
|
||||||
- go run build/ci.go aar -signer ANDROID_SIGNING_KEY -deploy https://oss.sonatype.org -upload gethstore/builds
|
- go run build/ci.go aar -signer ANDROID_SIGNING_KEY -deploy https://oss.sonatype.org -upload gethstore/builds
|
||||||
|
|
||||||
# This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads
|
# This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads
|
||||||
- if: type = push
|
- stage: build
|
||||||
|
if: type = push
|
||||||
os: osx
|
os: osx
|
||||||
go: 1.12.x
|
go: 1.14.x
|
||||||
env:
|
env:
|
||||||
- azure-osx
|
- azure-osx
|
||||||
- azure-ios
|
- azure-ios
|
||||||
- cocoapods-ios
|
- cocoapods-ios
|
||||||
|
- GO111MODULE=on
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
script:
|
script:
|
||||||
|
|
@ -202,12 +238,14 @@ matrix:
|
||||||
- go run build/ci.go xcode -signer IOS_SIGNING_KEY -deploy trunk -upload gethstore/builds
|
- go run build/ci.go xcode -signer IOS_SIGNING_KEY -deploy trunk -upload gethstore/builds
|
||||||
|
|
||||||
# This builder does the Azure archive purges to avoid accumulating junk
|
# This builder does the Azure archive purges to avoid accumulating junk
|
||||||
- if: type = cron
|
- stage: build
|
||||||
|
if: type = cron
|
||||||
os: linux
|
os: linux
|
||||||
dist: xenial
|
dist: xenial
|
||||||
go: 1.12.x
|
go: 1.14.x
|
||||||
env:
|
env:
|
||||||
- azure-purge
|
- azure-purge
|
||||||
|
- GO111MODULE=on
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
script:
|
script:
|
||||||
|
|
|
||||||
247
AUTHORS
247
AUTHORS
|
|
@ -1,5 +1,11 @@
|
||||||
# This is the official list of go-ethereum authors for copyright purposes.
|
# This is the official list of go-ethereum authors for copyright purposes.
|
||||||
|
|
||||||
|
a e r t h <aerth@users.noreply.github.com>
|
||||||
|
Abel Nieto <abel.nieto90@gmail.com>
|
||||||
|
Abel Nieto <anietoro@uwaterloo.ca>
|
||||||
|
Adam Babik <a.babik@designfortress.com>
|
||||||
|
Aditya <adityasripal@gmail.com>
|
||||||
|
Adrià Cidre <adria.cidre@gmail.com>
|
||||||
Afri Schoedon <5chdn@users.noreply.github.com>
|
Afri Schoedon <5chdn@users.noreply.github.com>
|
||||||
Agustin Armellini Fischer <armellini13@gmail.com>
|
Agustin Armellini Fischer <armellini13@gmail.com>
|
||||||
Airead <fgh1987168@gmail.com>
|
Airead <fgh1987168@gmail.com>
|
||||||
|
|
@ -10,165 +16,354 @@ Alex Leverington <alex@ethdev.com>
|
||||||
Alex Wu <wuyiding@gmail.com>
|
Alex Wu <wuyiding@gmail.com>
|
||||||
Alexandre Van de Sande <alex.vandesande@ethdev.com>
|
Alexandre Van de Sande <alex.vandesande@ethdev.com>
|
||||||
Ali Hajimirza <Ali92hm@users.noreply.github.com>
|
Ali Hajimirza <Ali92hm@users.noreply.github.com>
|
||||||
|
am2rican5 <am2rican5@gmail.com>
|
||||||
|
Andrea Franz <andrea@gravityblast.com>
|
||||||
|
Andrey Petrov <andrey.petrov@shazow.net>
|
||||||
|
Andrey Petrov <shazow@gmail.com>
|
||||||
|
ANOTHEL <anothel1@naver.com>
|
||||||
|
Antoine Rondelet <rondelet.antoine@gmail.com>
|
||||||
Anton Evangelatov <anton.evangelatov@gmail.com>
|
Anton Evangelatov <anton.evangelatov@gmail.com>
|
||||||
|
Antonio Salazar Cardozo <savedfastcool@gmail.com>
|
||||||
Arba Sasmoyo <arba.sasmoyo@gmail.com>
|
Arba Sasmoyo <arba.sasmoyo@gmail.com>
|
||||||
Armani Ferrante <armaniferrante@berkeley.edu>
|
Armani Ferrante <armaniferrante@berkeley.edu>
|
||||||
Armin Braun <me@obrown.io>
|
Armin Braun <me@obrown.io>
|
||||||
Aron Fischer <github@aron.guru>
|
Aron Fischer <github@aron.guru>
|
||||||
|
atsushi-ishibashi <atsushi.ishibashi@finatext.com>
|
||||||
|
ayeowch <ayeowch@gmail.com>
|
||||||
|
b00ris <b00ris@mail.ru>
|
||||||
|
bailantaotao <Edwin@maicoin.com>
|
||||||
|
baizhenxuan <nkbai@163.com>
|
||||||
|
Balint Gabor <balint.g@gmail.com>
|
||||||
Bas van Kervel <bas@ethdev.com>
|
Bas van Kervel <bas@ethdev.com>
|
||||||
Benjamin Brent <benjamin@benjaminbrent.com>
|
Benjamin Brent <benjamin@benjaminbrent.com>
|
||||||
|
benma <mbencun@gmail.com>
|
||||||
Benoit Verkindt <benoit.verkindt@gmail.com>
|
Benoit Verkindt <benoit.verkindt@gmail.com>
|
||||||
|
bloonfield <bloonfield@163.com>
|
||||||
Bo <bohende@gmail.com>
|
Bo <bohende@gmail.com>
|
||||||
Bo Ye <boy.e.computer.1982@outlook.com>
|
Bo Ye <boy.e.computer.1982@outlook.com>
|
||||||
Bob Glickstein <bobg@users.noreply.github.com>
|
Bob Glickstein <bobg@users.noreply.github.com>
|
||||||
|
Brent <bmperrea@gmail.com>
|
||||||
Brian Schroeder <bts@gmail.com>
|
Brian Schroeder <bts@gmail.com>
|
||||||
|
Bruno Škvorc <bruno@skvorc.me>
|
||||||
|
C. Brown <hackdom@majoolr.io>
|
||||||
|
Caesar Chad <BLUE.WEB.GEEK@gmail.com>
|
||||||
Casey Detrio <cdetrio@gmail.com>
|
Casey Detrio <cdetrio@gmail.com>
|
||||||
|
CDsigma <cdsigma271@gmail.com>
|
||||||
|
changhong <changhong.yu@shanbay.com>
|
||||||
Chase Wright <mysticryuujin@gmail.com>
|
Chase Wright <mysticryuujin@gmail.com>
|
||||||
|
Chen Quan <terasum@163.com>
|
||||||
|
chenyufeng <yufengcode@gmail.com>
|
||||||
|
Christian Muehlhaeuser <muesli@gmail.com>
|
||||||
Christoph Jentzsch <jentzsch.software@gmail.com>
|
Christoph Jentzsch <jentzsch.software@gmail.com>
|
||||||
|
cong <ackratos@users.noreply.github.com>
|
||||||
|
Corey Lin <514971757@qq.com>
|
||||||
|
cpusoft <cpusoft@live.com>
|
||||||
|
Crispin Flowerday <crispin@bitso.com>
|
||||||
|
croath <croathliu@gmail.com>
|
||||||
|
cui <523516579@qq.com>
|
||||||
|
Dan Kinsley <dan@joincivil.com>
|
||||||
Daniel A. Nagy <nagy.da@gmail.com>
|
Daniel A. Nagy <nagy.da@gmail.com>
|
||||||
Daniel Sloof <goapsychadelic@gmail.com>
|
Daniel Sloof <goapsychadelic@gmail.com>
|
||||||
Darrel Herbst <dherbst@gmail.com>
|
Darrel Herbst <dherbst@gmail.com>
|
||||||
Dave Appleton <calistralabs@gmail.com>
|
Dave Appleton <calistralabs@gmail.com>
|
||||||
|
Dave McGregor <dave.s.mcgregor@gmail.com>
|
||||||
|
David Huie <dahuie@gmail.com>
|
||||||
|
Derek Gottfrid <derek@codecubed.com>
|
||||||
Diego Siqueira <DiSiqueira@users.noreply.github.com>
|
Diego Siqueira <DiSiqueira@users.noreply.github.com>
|
||||||
|
Diep Pham <mrfavadi@gmail.com>
|
||||||
|
dipingxian2 <39109351+dipingxian2@users.noreply.github.com>
|
||||||
|
dm4 <sunrisedm4@gmail.com>
|
||||||
|
Dmitrij Koniajev <dimchansky@gmail.com>
|
||||||
Dmitry Shulyak <yashulyak@gmail.com>
|
Dmitry Shulyak <yashulyak@gmail.com>
|
||||||
|
Domino Valdano <dominoplural@gmail.com>
|
||||||
|
Domino Valdano <jeff@okcupid.com>
|
||||||
|
Dragan Milic <dragan@netice9.com>
|
||||||
|
dragonvslinux <35779158+dragononcrypto@users.noreply.github.com>
|
||||||
Egon Elbre <egonelbre@gmail.com>
|
Egon Elbre <egonelbre@gmail.com>
|
||||||
|
Elad <theman@elad.im>
|
||||||
|
Eli <elihanover@yahoo.com>
|
||||||
Elias Naur <elias.naur@gmail.com>
|
Elias Naur <elias.naur@gmail.com>
|
||||||
Elliot Shepherd <elliot@identitii.com>
|
Elliot Shepherd <elliot@identitii.com>
|
||||||
|
Emil <mursalimovemeel@gmail.com>
|
||||||
|
emile <emile@users.noreply.github.com>
|
||||||
Enrique Fynn <enriquefynn@gmail.com>
|
Enrique Fynn <enriquefynn@gmail.com>
|
||||||
|
Enrique Fynn <me@enriquefynn.com>
|
||||||
|
EOS Classic <info@eos-classic.io>
|
||||||
|
Erichin <erichinbato@gmail.com>
|
||||||
Ernesto del Toro <ernesto.deltoro@gmail.com>
|
Ernesto del Toro <ernesto.deltoro@gmail.com>
|
||||||
Ethan Buchman <ethan@coinculture.info>
|
Ethan Buchman <ethan@coinculture.info>
|
||||||
|
ethersphere <thesw@rm.eth>
|
||||||
Eugene Valeyev <evgen.povt@gmail.com>
|
Eugene Valeyev <evgen.povt@gmail.com>
|
||||||
Evangelos Pappas <epappas@evalonlabs.com>
|
Evangelos Pappas <epappas@evalonlabs.com>
|
||||||
|
Evgeny <awesome.observer@yandex.com>
|
||||||
Evgeny Danilenko <6655321@bk.ru>
|
Evgeny Danilenko <6655321@bk.ru>
|
||||||
|
evgk <evgeniy.kamyshev@gmail.com>
|
||||||
Fabian Vogelsteller <fabian@frozeman.de>
|
Fabian Vogelsteller <fabian@frozeman.de>
|
||||||
Fabio Barone <fabio.barone.co@gmail.com>
|
Fabio Barone <fabio.barone.co@gmail.com>
|
||||||
Fabio Berger <fabioberger1991@gmail.com>
|
Fabio Berger <fabioberger1991@gmail.com>
|
||||||
FaceHo <facehoshi@gmail.com>
|
FaceHo <facehoshi@gmail.com>
|
||||||
Felix Lange <fjl@twurst.com>
|
Felix Lange <fjl@twurst.com>
|
||||||
|
Ferenc Szabo <frncmx@gmail.com>
|
||||||
|
ferhat elmas <elmas.ferhat@gmail.com>
|
||||||
Fiisio <liangcszzu@163.com>
|
Fiisio <liangcszzu@163.com>
|
||||||
|
Frank Szendzielarz <33515470+FrankSzendzielarz@users.noreply.github.com>
|
||||||
Frank Wang <eternnoir@gmail.com>
|
Frank Wang <eternnoir@gmail.com>
|
||||||
|
Franklin <mr_franklin@126.com>
|
||||||
Furkan KAMACI <furkankamaci@gmail.com>
|
Furkan KAMACI <furkankamaci@gmail.com>
|
||||||
|
GagziW <leon.stanko@rwth-aachen.de>
|
||||||
Gary Rong <garyrong0905@gmail.com>
|
Gary Rong <garyrong0905@gmail.com>
|
||||||
George Ornbo <george@shapeshed.com>
|
George Ornbo <george@shapeshed.com>
|
||||||
Gregg Dourgarian <greggd@tempworks.com>
|
Gregg Dourgarian <greggd@tempworks.com>
|
||||||
|
Guilherme Salgado <gsalgado@gmail.com>
|
||||||
Guillaume Ballet <gballet@gmail.com>
|
Guillaume Ballet <gballet@gmail.com>
|
||||||
Guillaume Nicolas <guin56@gmail.com>
|
Guillaume Nicolas <guin56@gmail.com>
|
||||||
|
GuiltyMorishita <morilliantblue@gmail.com>
|
||||||
|
Gus <yo@soygus.com>
|
||||||
Gustav Simonsson <gustav.simonsson@gmail.com>
|
Gustav Simonsson <gustav.simonsson@gmail.com>
|
||||||
|
Gísli Kristjánsson <gislik@hamstur.is>
|
||||||
|
Ha ĐANG <dvietha@gmail.com>
|
||||||
|
HackyMiner <hackyminer@gmail.com>
|
||||||
|
hadv <dvietha@gmail.com>
|
||||||
Hao Bryan Cheng <haobcheng@gmail.com>
|
Hao Bryan Cheng <haobcheng@gmail.com>
|
||||||
|
HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
|
||||||
Henning Diedrich <hd@eonblast.com>
|
Henning Diedrich <hd@eonblast.com>
|
||||||
|
holisticode <holistic.computing@gmail.com>
|
||||||
|
Hongbin Mao <hello2mao@gmail.com>
|
||||||
|
Hsien-Tang Kao <htkao@pm.me>
|
||||||
|
Husam Ibrahim <39692071+HusamIbrahim@users.noreply.github.com>
|
||||||
|
hydai <z54981220@gmail.com>
|
||||||
|
Hyung-Kyu Hqueue Choi <hyungkyu.choi@gmail.com>
|
||||||
|
Ian Macalinao <me@ian.pw>
|
||||||
|
Ian Norden <iannordenn@gmail.com>
|
||||||
Isidoro Ghezzi <isidoro.ghezzi@icloud.com>
|
Isidoro Ghezzi <isidoro.ghezzi@icloud.com>
|
||||||
|
Iskander (Alex) Sharipov <quasilyte@gmail.com>
|
||||||
Ivan Daniluk <ivan.daniluk@gmail.com>
|
Ivan Daniluk <ivan.daniluk@gmail.com>
|
||||||
|
Ivo Georgiev <ivo@strem.io>
|
||||||
Jae Kwon <jkwon.work@gmail.com>
|
Jae Kwon <jkwon.work@gmail.com>
|
||||||
Jamie Pitts <james.pitts@gmail.com>
|
Jamie Pitts <james.pitts@gmail.com>
|
||||||
|
Janos Guljas <janos@resenje.org>
|
||||||
Janoš Guljaš <janos@users.noreply.github.com>
|
Janoš Guljaš <janos@users.noreply.github.com>
|
||||||
Jason Carver <jacarver@linkedin.com>
|
Jason Carver <jacarver@linkedin.com>
|
||||||
|
Javier Peletier <jm@epiclabs.io>
|
||||||
|
Javier Peletier <jpeletier@users.noreply.github.com>
|
||||||
|
Javier Sagredo <jasataco@gmail.com>
|
||||||
|
Jay <codeholic.arena@gmail.com>
|
||||||
Jay Guo <guojiannan1101@gmail.com>
|
Jay Guo <guojiannan1101@gmail.com>
|
||||||
|
Jaynti Kanani <jdkanani@gmail.com>
|
||||||
|
Jeff Prestes <jeffprestes@gmail.com>
|
||||||
Jeff R. Allen <jra@nella.org>
|
Jeff R. Allen <jra@nella.org>
|
||||||
|
Jeffery Robert Walsh <rlxrlps@gmail.com>
|
||||||
Jeffrey Wilcke <jeffrey@ethereum.org>
|
Jeffrey Wilcke <jeffrey@ethereum.org>
|
||||||
Jens Agerberg <github@agerberg.me>
|
Jens Agerberg <github@agerberg.me>
|
||||||
|
Jeremy McNevin <jeremy.mcnevin@optum.com>
|
||||||
|
Jeremy Schlatter <jeremy.schlatter@gmail.com>
|
||||||
|
Jerzy Lasyk <jerzylasyk@gmail.com>
|
||||||
Jia Chenhui <jiachenhui1989@gmail.com>
|
Jia Chenhui <jiachenhui1989@gmail.com>
|
||||||
Jim McDonald <Jim@mcdee.net>
|
Jim McDonald <Jim@mcdee.net>
|
||||||
|
jkcomment <jkcomment@gmail.com>
|
||||||
Joel Burget <joelburget@gmail.com>
|
Joel Burget <joelburget@gmail.com>
|
||||||
|
John C. Vernaleo <john@netpurgatory.com>
|
||||||
|
Johns Beharry <johns@peakshift.com>
|
||||||
|
Jonas <felberj@users.noreply.github.com>
|
||||||
Jonathan Brown <jbrown@bluedroplet.com>
|
Jonathan Brown <jbrown@bluedroplet.com>
|
||||||
|
JoranHonig <JoranHonig@users.noreply.github.com>
|
||||||
|
Jordan Krage <jmank88@gmail.com>
|
||||||
Joseph Chow <ethereum@outlook.com>
|
Joseph Chow <ethereum@outlook.com>
|
||||||
|
jtakalai <juuso.takalainen@streamr.com>
|
||||||
|
JU HYEONG PARK <dkdkajej@gmail.com>
|
||||||
Justin Clark-Casey <justincc@justincc.org>
|
Justin Clark-Casey <justincc@justincc.org>
|
||||||
Justin Drake <drakefjustin@gmail.com>
|
Justin Drake <drakefjustin@gmail.com>
|
||||||
|
jwasinger <j-wasinger@hotmail.com>
|
||||||
|
ken10100147 <sunhongping@kanjian.com>
|
||||||
Kenji Siu <kenji@isuntv.com>
|
Kenji Siu <kenji@isuntv.com>
|
||||||
|
Kenso Trabing <kenso.trabing@bloomwebsite.com>
|
||||||
|
Kenso Trabing <ktrabing@acm.org>
|
||||||
|
Kevin <denk.kevin@web.de>
|
||||||
|
kevin.xu <cming.xu@gmail.com>
|
||||||
|
kiel barry <kiel.j.barry@gmail.com>
|
||||||
|
kimmylin <30611210+kimmylin@users.noreply.github.com>
|
||||||
|
Kitten King <53072918+kittenking@users.noreply.github.com>
|
||||||
|
knarfeh <hejun1874@gmail.com>
|
||||||
Kobi Gurkan <kobigurk@gmail.com>
|
Kobi Gurkan <kobigurk@gmail.com>
|
||||||
Konrad Feldmeier <konrad@brainbot.com>
|
Konrad Feldmeier <konrad@brainbot.com>
|
||||||
|
Kris Shinn <raggamuffin.music@gmail.com>
|
||||||
Kurkó Mihály <kurkomisi@users.noreply.github.com>
|
Kurkó Mihály <kurkomisi@users.noreply.github.com>
|
||||||
|
Kushagra Sharma <ksharm01@gmail.com>
|
||||||
|
Kwuaint <34888408+kwuaint@users.noreply.github.com>
|
||||||
Kyuntae Ethan Kim <ethan.kyuntae.kim@gmail.com>
|
Kyuntae Ethan Kim <ethan.kyuntae.kim@gmail.com>
|
||||||
|
ledgerwatch <akhounov@gmail.com>
|
||||||
Lefteris Karapetsas <lefteris@refu.co>
|
Lefteris Karapetsas <lefteris@refu.co>
|
||||||
Leif Jurvetson <leijurv@gmail.com>
|
Leif Jurvetson <leijurv@gmail.com>
|
||||||
Leo Shklovskii <leo@thermopylae.net>
|
Leo Shklovskii <leo@thermopylae.net>
|
||||||
|
LeoLiao <leofantast@gmail.com>
|
||||||
Lewis Marshall <lewis@lmars.net>
|
Lewis Marshall <lewis@lmars.net>
|
||||||
|
lhendre <lhendre2@gmail.com>
|
||||||
|
Liang Ma <liangma.ul@gmail.com>
|
||||||
|
Liang Ma <liangma@liangbit.com>
|
||||||
|
Liang ZOU <liang.d.zou@gmail.com>
|
||||||
|
libotony <liboliqi@gmail.com>
|
||||||
|
ligi <ligi@ligi.de>
|
||||||
Lio李欧 <lionello@users.noreply.github.com>
|
Lio李欧 <lionello@users.noreply.github.com>
|
||||||
|
Lorenzo Manacorda <lorenzo@kinvolk.io>
|
||||||
Louis Holbrook <dev@holbrook.no>
|
Louis Holbrook <dev@holbrook.no>
|
||||||
Luca Zeug <luclu@users.noreply.github.com>
|
Luca Zeug <luclu@users.noreply.github.com>
|
||||||
Magicking <s@6120.eu>
|
Magicking <s@6120.eu>
|
||||||
|
manlio <manlio.poltronieri@gmail.com>
|
||||||
Maran Hidskes <maran.hidskes@gmail.com>
|
Maran Hidskes <maran.hidskes@gmail.com>
|
||||||
Marek Kotewicz <marek.kotewicz@gmail.com>
|
Marek Kotewicz <marek.kotewicz@gmail.com>
|
||||||
|
Marius van der Wijden <m.vanderwijden@live.de>
|
||||||
Mark <markya0616@gmail.com>
|
Mark <markya0616@gmail.com>
|
||||||
|
Mark Rushakoff <mark.rushakoff@gmail.com>
|
||||||
|
mark.lin <mark@maicoin.com>
|
||||||
|
Martin Alex Philip Dawson <u1356770@gmail.com>
|
||||||
Martin Holst Swende <martin@swende.se>
|
Martin Holst Swende <martin@swende.se>
|
||||||
|
Martin Klepsch <martinklepsch@googlemail.com>
|
||||||
|
Mats Julian Olsen <mats@plysjbyen.net>
|
||||||
|
Matt K <1036969+mkrump@users.noreply.github.com>
|
||||||
Matthew Di Ferrante <mattdf@users.noreply.github.com>
|
Matthew Di Ferrante <mattdf@users.noreply.github.com>
|
||||||
|
Matthew Halpern <matthalp@gmail.com>
|
||||||
|
Matthew Halpern <matthalp@google.com>
|
||||||
Matthew Wampler-Doty <matthew.wampler.doty@gmail.com>
|
Matthew Wampler-Doty <matthew.wampler.doty@gmail.com>
|
||||||
|
Max Sistemich <mafrasi2@googlemail.com>
|
||||||
Maximilian Meister <mmeister@suse.de>
|
Maximilian Meister <mmeister@suse.de>
|
||||||
Micah Zoltu <micah@zoltu.net>
|
Micah Zoltu <micah@zoltu.net>
|
||||||
Michael Ruminer <michael.ruminer+github@gmail.com>
|
Michael Ruminer <michael.ruminer+github@gmail.com>
|
||||||
Miguel Mota <miguelmota2@gmail.com>
|
Miguel Mota <miguelmota2@gmail.com>
|
||||||
Miya Chen <miyatlchen@gmail.com>
|
Miya Chen <miyatlchen@gmail.com>
|
||||||
|
Mohanson <mohanson@outlook.com>
|
||||||
|
mr_franklin <mr_franklin@126.com>
|
||||||
|
Mymskmkt <1847234666@qq.com>
|
||||||
|
Nalin Bhardwaj <nalinbhardwaj@nibnalin.me>
|
||||||
Nchinda Nchinda <nchinda2@gmail.com>
|
Nchinda Nchinda <nchinda2@gmail.com>
|
||||||
|
necaremus <necaremus@gmail.com>
|
||||||
|
needkane <604476380@qq.com>
|
||||||
|
Nguyen Kien Trung <trung.n.k@gmail.com>
|
||||||
|
Nguyen Sy Thanh Son <thanhson1085@gmail.com>
|
||||||
Nick Dodson <silentcicero@outlook.com>
|
Nick Dodson <silentcicero@outlook.com>
|
||||||
Nick Johnson <arachnid@notdot.net>
|
Nick Johnson <arachnid@notdot.net>
|
||||||
Nicolas Guillaume <gunicolas@sqli.com>
|
Nicolas Guillaume <gunicolas@sqli.com>
|
||||||
|
Nilesh Trivedi <nilesh@hypertrack.io>
|
||||||
|
Nimrod Gutman <nimrod.gutman@gmail.com>
|
||||||
|
njupt-moon <1015041018@njupt.edu.cn>
|
||||||
|
nkbai <nkbai@163.com>
|
||||||
|
nobody <ddean2009@163.com>
|
||||||
Noman <noman@noman.land>
|
Noman <noman@noman.land>
|
||||||
|
Oleg Kovalov <iamolegkovalov@gmail.com>
|
||||||
Oli Bye <olibye@users.noreply.github.com>
|
Oli Bye <olibye@users.noreply.github.com>
|
||||||
|
Osuke <arget-fee.free.dgm@hotmail.co.jp>
|
||||||
|
Paul Berg <hello@paulrberg.com>
|
||||||
Paul Litvak <litvakpol@012.net.il>
|
Paul Litvak <litvakpol@012.net.il>
|
||||||
Paulo L F Casaretto <pcasaretto@gmail.com>
|
Paulo L F Casaretto <pcasaretto@gmail.com>
|
||||||
Paweł Bylica <chfast@gmail.com>
|
Paweł Bylica <chfast@gmail.com>
|
||||||
|
Pedro Pombeiro <PombeirP@users.noreply.github.com>
|
||||||
|
Peter Broadhurst <peter@themumbles.net>
|
||||||
Peter Pratscher <pratscher@gmail.com>
|
Peter Pratscher <pratscher@gmail.com>
|
||||||
Petr Mikusek <petr@mikusek.info>
|
Petr Mikusek <petr@mikusek.info>
|
||||||
|
Philip Schlump <pschlump@gmail.com>
|
||||||
|
Pierre Neter <pierreneter@gmail.com>
|
||||||
|
PilkyuJung <anothel1@naver.com>
|
||||||
|
protolambda <proto@protolambda.com>
|
||||||
Péter Szilágyi <peterke@gmail.com>
|
Péter Szilágyi <peterke@gmail.com>
|
||||||
RJ Catalano <catalanor0220@gmail.com>
|
qd-ethan <31876119+qdgogogo@users.noreply.github.com>
|
||||||
|
Raghav Sood <raghavsood@gmail.com>
|
||||||
|
Ralph Caraveo <deckarep@gmail.com>
|
||||||
|
Ralph Caraveo III <deckarep@gmail.com>
|
||||||
Ramesh Nair <ram@hiddentao.com>
|
Ramesh Nair <ram@hiddentao.com>
|
||||||
|
reinerRubin <tolstov.georgij@gmail.com>
|
||||||
|
rhaps107 <dod-source@yandex.ru>
|
||||||
Ricardo Catalinas Jiménez <r@untroubled.be>
|
Ricardo Catalinas Jiménez <r@untroubled.be>
|
||||||
Ricardo Domingos <ricardohsd@gmail.com>
|
Ricardo Domingos <ricardohsd@gmail.com>
|
||||||
Richard Hart <richardhart92@gmail.com>
|
Richard Hart <richardhart92@gmail.com>
|
||||||
|
RJ Catalano <catalanor0220@gmail.com>
|
||||||
Rob <robert@rojotek.com>
|
Rob <robert@rojotek.com>
|
||||||
|
Rob Mulholand <rmulholand@8thlight.com>
|
||||||
Robert Zaremba <robert.zaremba@scale-it.pl>
|
Robert Zaremba <robert.zaremba@scale-it.pl>
|
||||||
|
Roc Yu <rociiu0112@gmail.com>
|
||||||
|
Runchao Han <elvisage941102@gmail.com>
|
||||||
Russ Cox <rsc@golang.org>
|
Russ Cox <rsc@golang.org>
|
||||||
|
Ryan Schneider <ryanleeschneider@gmail.com>
|
||||||
Rémy Roy <remyroy@remyroy.com>
|
Rémy Roy <remyroy@remyroy.com>
|
||||||
S. Matthew English <s-matthew-english@users.noreply.github.com>
|
S. Matthew English <s-matthew-english@users.noreply.github.com>
|
||||||
|
salanfe <salanfe@users.noreply.github.com>
|
||||||
|
Samuel Marks <samuelmarks@gmail.com>
|
||||||
|
Sarlor <kinsleer@outlook.com>
|
||||||
|
Sasuke1964 <neilperry1964@gmail.com>
|
||||||
|
Saulius Grigaitis <saulius@necolt.com>
|
||||||
|
Sean <darcys22@gmail.com>
|
||||||
|
Sheldon <11510383@mail.sustc.edu.cn>
|
||||||
|
Sheldon <374662347@qq.com>
|
||||||
Shintaro Kaneko <kaneshin0120@gmail.com>
|
Shintaro Kaneko <kaneshin0120@gmail.com>
|
||||||
|
Shuai Qi <qishuai231@gmail.com>
|
||||||
|
Shunsuke Watanabe <ww.shunsuke@gmail.com>
|
||||||
|
silence <wangsai.silence@qq.com>
|
||||||
|
Simon Jentzsch <simon@slock.it>
|
||||||
|
slumber1122 <slumber1122@gmail.com>
|
||||||
|
Smilenator <yurivanenko@yandex.ru>
|
||||||
Sorin Neacsu <sorin.neacsu@gmail.com>
|
Sorin Neacsu <sorin.neacsu@gmail.com>
|
||||||
Stein Dekker <dekker.stein@gmail.com>
|
Stein Dekker <dekker.stein@gmail.com>
|
||||||
|
Steve Gattuso <steve@stevegattuso.me>
|
||||||
|
Steve Ruckdashel <steve.ruckdashel@gmail.com>
|
||||||
Steve Waldman <swaldman@mchange.com>
|
Steve Waldman <swaldman@mchange.com>
|
||||||
Steven Roose <stevenroose@gmail.com>
|
Steven Roose <stevenroose@gmail.com>
|
||||||
|
stompesi <stompesi@gmail.com>
|
||||||
|
stormpang <jialinpeng@vip.qq.com>
|
||||||
|
sunxiaojun2014 <sunxiaojun-xy@360.cn>
|
||||||
|
tamirms <tamir@trello.com>
|
||||||
Taylor Gerring <taylor.gerring@gmail.com>
|
Taylor Gerring <taylor.gerring@gmail.com>
|
||||||
|
TColl <38299499+TColl@users.noreply.github.com>
|
||||||
|
terasum <terasum@163.com>
|
||||||
Thomas Bocek <tom@tomp2p.net>
|
Thomas Bocek <tom@tomp2p.net>
|
||||||
|
thomasmodeneis <thomas.modeneis@gmail.com>
|
||||||
|
thumb8432 <thumb8432@gmail.com>
|
||||||
Ti Zhou <tizhou1986@gmail.com>
|
Ti Zhou <tizhou1986@gmail.com>
|
||||||
Tosh Camille <tochecamille@gmail.com>
|
Tosh Camille <tochecamille@gmail.com>
|
||||||
|
tsarpaul <Litvakpol@012.net.il>
|
||||||
|
tzapu <alex@tzapu.com>
|
||||||
|
ult-bobonovski <alex@ultiledger.io>
|
||||||
Valentin Wüstholz <wuestholz@gmail.com>
|
Valentin Wüstholz <wuestholz@gmail.com>
|
||||||
|
Vedhavyas Singareddi <vedhavyas.singareddi@gmail.com>
|
||||||
Victor Farazdagi <simple.square@gmail.com>
|
Victor Farazdagi <simple.square@gmail.com>
|
||||||
Victor Tran <vu.tran54@gmail.com>
|
Victor Tran <vu.tran54@gmail.com>
|
||||||
|
Vie <yangchenzhong@gmail.com>
|
||||||
Viktor Trón <viktor.tron@gmail.com>
|
Viktor Trón <viktor.tron@gmail.com>
|
||||||
Ville Sundell <github@solarius.fi>
|
Ville Sundell <github@solarius.fi>
|
||||||
|
vim88 <vim88vim88@gmail.com>
|
||||||
Vincent G <caktux@gmail.com>
|
Vincent G <caktux@gmail.com>
|
||||||
|
Vincent Serpoul <vincent@serpoul.com>
|
||||||
Vitalik Buterin <v@buterin.com>
|
Vitalik Buterin <v@buterin.com>
|
||||||
|
Vitaly Bogdanov <vsbogd@gmail.com>
|
||||||
Vitaly V <vvelikodny@gmail.com>
|
Vitaly V <vvelikodny@gmail.com>
|
||||||
Vivek Anand <vivekanand1101@users.noreply.github.com>
|
Vivek Anand <vivekanand1101@users.noreply.github.com>
|
||||||
|
Vlad <gluk256@gmail.com>
|
||||||
|
Vlad Bokov <razum2um@mail.ru>
|
||||||
Vlad Gluhovsky <gluk256@users.noreply.github.com>
|
Vlad Gluhovsky <gluk256@users.noreply.github.com>
|
||||||
|
weimumu <934657014@qq.com>
|
||||||
|
Wenbiao Zheng <delweng@gmail.com>
|
||||||
|
William Setzer <bootstrapsetzer@gmail.com>
|
||||||
|
williambannas <wrschwartz@wpi.edu>
|
||||||
|
Wuxiang <wuxiangzhou2010@gmail.com>
|
||||||
|
xiekeyang <xiekeyang@users.noreply.github.com>
|
||||||
|
xincaosu <xincaosu@126.com>
|
||||||
|
yahtoo <yahtoo.ma@gmail.com>
|
||||||
|
YaoZengzeng <yaozengzeng@zju.edu.cn>
|
||||||
|
YH-Zhou <yanhong.zhou05@gmail.com>
|
||||||
Yohann Léon <sybiload@gmail.com>
|
Yohann Léon <sybiload@gmail.com>
|
||||||
Yoichi Hirai <i@yoichihirai.com>
|
Yoichi Hirai <i@yoichihirai.com>
|
||||||
Yondon Fu <yondon.fu@gmail.com>
|
Yondon Fu <yondon.fu@gmail.com>
|
||||||
|
YOSHIDA Masanori <masanori.yoshida@gmail.com>
|
||||||
|
yoza <yoza.is12s@gmail.com>
|
||||||
|
Yusup <awklsgrep@gmail.com>
|
||||||
Zach <zach.ramsay@gmail.com>
|
Zach <zach.ramsay@gmail.com>
|
||||||
|
zah <zahary@gmail.com>
|
||||||
Zahoor Mohamed <zahoor@zahoor.in>
|
Zahoor Mohamed <zahoor@zahoor.in>
|
||||||
|
Zak Cole <zak@beattiecole.com>
|
||||||
|
zer0to0ne <36526113+zer0to0ne@users.noreply.github.com>
|
||||||
|
Zhenguo Niu <Niu.ZGlinux@gmail.com>
|
||||||
Zoe Nolan <github@zoenolan.org>
|
Zoe Nolan <github@zoenolan.org>
|
||||||
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
||||||
am2rican5 <am2rican5@gmail.com>
|
Łukasz Kurowski <crackcomm@users.noreply.github.com>
|
||||||
ayeowch <ayeowch@gmail.com>
|
|
||||||
b00ris <b00ris@mail.ru>
|
|
||||||
bailantaotao <Edwin@maicoin.com>
|
|
||||||
baizhenxuan <nkbai@163.com>
|
|
||||||
bloonfield <bloonfield@163.com>
|
|
||||||
changhong <changhong.yu@shanbay.com>
|
|
||||||
evgk <evgeniy.kamyshev@gmail.com>
|
|
||||||
ferhat elmas <elmas.ferhat@gmail.com>
|
|
||||||
holisticode <holistic.computing@gmail.com>
|
|
||||||
jtakalai <juuso.takalainen@streamr.com>
|
|
||||||
ken10100147 <sunhongping@kanjian.com>
|
|
||||||
ligi <ligi@ligi.de>
|
|
||||||
mark.lin <mark@maicoin.com>
|
|
||||||
necaremus <necaremus@gmail.com>
|
|
||||||
njupt-moon <1015041018@njupt.edu.cn>
|
|
||||||
nkbai <nkbai@163.com>
|
|
||||||
rhaps107 <dod-source@yandex.ru>
|
|
||||||
slumber1122 <slumber1122@gmail.com>
|
|
||||||
sunxiaojun2014 <sunxiaojun-xy@360.cn>
|
|
||||||
terasum <terasum@163.com>
|
|
||||||
tsarpaul <Litvakpol@012.net.il>
|
|
||||||
xiekeyang <xiekeyang@users.noreply.github.com>
|
|
||||||
yoza <yoza.is12s@gmail.com>
|
|
||||||
ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com>
|
ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com>
|
||||||
Максим Чусовлянов <mchusovlianov@gmail.com>
|
Максим Чусовлянов <mchusovlianov@gmail.com>
|
||||||
Ralph Caraveo <deckarep@gmail.com>
|
大彬 <hz_stb@163.com>
|
||||||
|
贺鹏飞 <hpf@hackerful.cn>
|
||||||
|
유용환 <33824408+eric-yoo@users.noreply.github.com>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Build Bor in a stock Go builder container
|
# Build Geth in a stock Go builder container
|
||||||
FROM golang:1.12-alpine as builder
|
FROM golang:1.14-alpine as builder
|
||||||
|
|
||||||
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
||||||
|
|
||||||
|
|
@ -12,4 +12,4 @@ FROM alpine:latest
|
||||||
RUN apk add --no-cache ca-certificates
|
RUN apk add --no-cache ca-certificates
|
||||||
COPY --from=builder /bor/build/bin/bor /usr/local/bin/
|
COPY --from=builder /bor/build/bin/bor /usr/local/bin/
|
||||||
|
|
||||||
EXPOSE 8545 8546 30303 30303/udp
|
EXPOSE 8545 8546 8547 30303 30303/udp
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Build Bor in a stock Go builder container
|
# Build Geth in a stock Go builder container
|
||||||
FROM golang:1.12-alpine as builder
|
FROM golang:1.14-alpine as builder
|
||||||
|
|
||||||
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
||||||
|
|
||||||
|
|
@ -12,4 +12,4 @@ FROM alpine:latest
|
||||||
RUN apk add --no-cache ca-certificates
|
RUN apk add --no-cache ca-certificates
|
||||||
COPY --from=builder /bor/build/bin/* /usr/local/bin/
|
COPY --from=builder /bor/build/bin/* /usr/local/bin/
|
||||||
|
|
||||||
EXPOSE 8545 8546 30303 30303/udp
|
EXPOSE 8545 8546 8547 30303 30303/udp
|
||||||
|
|
|
||||||
9
Makefile
9
Makefile
|
|
@ -8,7 +8,7 @@
|
||||||
.PHONY: bor-darwin bor-darwin-386 bor-darwin-amd64
|
.PHONY: bor-darwin bor-darwin-386 bor-darwin-amd64
|
||||||
.PHONY: bor-windows bor-windows-386 bor-windows-amd64
|
.PHONY: bor-windows bor-windows-386 bor-windows-amd64
|
||||||
|
|
||||||
GOBIN = $(shell pwd)/build/bin
|
GOBIN = ./build/bin
|
||||||
GO ?= latest
|
GO ?= latest
|
||||||
GORUN = go run
|
GORUN = go run
|
||||||
GOPATH = $(shell go env GOPATH)
|
GOPATH = $(shell go env GOPATH)
|
||||||
|
|
@ -17,8 +17,6 @@ bor:
|
||||||
$(GORUN) build/ci.go install ./cmd/bor
|
$(GORUN) build/ci.go install ./cmd/bor
|
||||||
mkdir -p $(GOPATH)/bin/
|
mkdir -p $(GOPATH)/bin/
|
||||||
cp $(GOBIN)/bor $(GOPATH)/bin/
|
cp $(GOBIN)/bor $(GOPATH)/bin/
|
||||||
@echo "Done building."
|
|
||||||
@echo "Run \"$(GOBIN)/bor\" to launch bor."
|
|
||||||
|
|
||||||
all:
|
all:
|
||||||
$(GORUN) build/ci.go install
|
$(GORUN) build/ci.go install
|
||||||
|
|
@ -39,11 +37,14 @@ test: bor
|
||||||
go test github.com/maticnetwork/bor/consensus/bor
|
go test github.com/maticnetwork/bor/consensus/bor
|
||||||
go test github.com/maticnetwork/bor/tests/bor
|
go test github.com/maticnetwork/bor/tests/bor
|
||||||
|
|
||||||
|
# test: all
|
||||||
|
# $(GORUN) build/ci.go test
|
||||||
|
|
||||||
lint: ## Run linters.
|
lint: ## Run linters.
|
||||||
$(GORUN) build/ci.go lint
|
$(GORUN) build/ci.go lint
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
./build/clean_go_build_cache.sh
|
env GO111MODULE=on go clean -cache
|
||||||
rm -fr build/_workspace/pkg/ $(GOBIN)/*
|
rm -fr build/_workspace/pkg/ $(GOBIN)/*
|
||||||
|
|
||||||
# The devtools target installs tools required for 'go generate'.
|
# The devtools target installs tools required for 'go generate'.
|
||||||
|
|
|
||||||
144
README.md
144
README.md
|
|
@ -12,9 +12,7 @@ archives are published at https://geth.ethereum.org/downloads/.
|
||||||
|
|
||||||
## Building the source
|
## Building the source
|
||||||
|
|
||||||
For prerequisites and detailed build instructions please read the [Installation Instructions](https://github.com/maticnetwork/bor/wiki/Building-Ethereum) on the wiki.
|
Building `bor` requires both a Go (version 1.13 or later) and a C compiler. You can install
|
||||||
|
|
||||||
Building `bor` requires both a Go (version 1.10 or later) and a C compiler. You can install
|
|
||||||
them using your favourite package manager. Once the dependencies are installed, run
|
them using your favourite package manager. Once the dependencies are installed, run
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
|
@ -27,148 +25,12 @@ or, to build the full suite of utilities:
|
||||||
make all
|
make all
|
||||||
```
|
```
|
||||||
|
|
||||||
## Executables
|
|
||||||
|
|
||||||
The go-ethereum project comes with several wrappers/executables found in the `cmd`
|
|
||||||
directory.
|
|
||||||
|
|
||||||
| Command | Description |
|
|
||||||
| :-----------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
||||||
| **`bor`** | 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/maticnetwork/bor/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/maticnetwork/bor/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. |
|
|
||||||
| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). |
|
|
||||||
| `gethrpctest` | Developer utility tool to support our [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) specs. Please see the [test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. |
|
|
||||||
| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ethereum/wiki/wiki/RLP)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user-friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). |
|
|
||||||
| `puppeth` | a CLI wizard that aids in creating a new Ethereum network. |
|
|
||||||
|
|
||||||
## Running `bor`
|
|
||||||
|
|
||||||
Going through all the possible command line flags is out of scope here (please consult our
|
|
||||||
[CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options)),
|
|
||||||
but we've enumerated a few common parameter combos to get you up to speed quickly
|
|
||||||
on how you can run your own `bor` instance.
|
|
||||||
|
|
||||||
### Full node on the main Ethereum network
|
|
||||||
|
|
||||||
By far the most common scenario is people wanting to simply interact with the Ethereum
|
|
||||||
network: create accounts; transfer funds; deploy and interact with contracts. For this
|
|
||||||
particular use-case the user doesn't care about years-old historical data, so we can
|
|
||||||
fast-sync quickly to the current state of the network. To do so:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ geth console
|
|
||||||
```
|
|
||||||
|
|
||||||
This command will:
|
|
||||||
|
|
||||||
- Start `geth` in fast sync mode (default, can be changed with the `--syncmode` flag),
|
|
||||||
causing it to download more data in exchange for avoiding processing the entire history
|
|
||||||
of the Ethereum network, which is very CPU intensive.
|
|
||||||
- Start up `geth`'s built-in interactive [JavaScript console](https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console),
|
|
||||||
(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 tool is optional and if you leave it out you can always attach to an already running
|
|
||||||
`geth` instance with `geth attach`.
|
|
||||||
|
|
||||||
### A Full node on the Ethereum test network
|
|
||||||
|
|
||||||
Transitioning towards developers, if you'd like to play around with creating Ethereum
|
|
||||||
contracts, you almost certainly would like to do that without any real money involved until
|
|
||||||
you get the hang of the entire system. In other words, instead of attaching to the main
|
|
||||||
network, you want to join the **test** network with your node, which is fully equivalent to
|
|
||||||
the main network, but with play-Ether only.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ geth --testnet console
|
|
||||||
```
|
|
||||||
|
|
||||||
The `console` subcommand has the exact same meaning as above and they are equally
|
|
||||||
useful on the testnet too. Please see above for their explanations if you've skipped 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). 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/bor.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, 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 `bor` binary, you can also pass a
|
|
||||||
configuration file via:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ bor --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:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ bor --your-favourite-flags dumpconfig
|
|
||||||
```
|
|
||||||
|
|
||||||
_Note: This works only with `geth` v1.6.0 and above._
|
|
||||||
|
|
||||||
### Programmatically interfacing `bor` nodes
|
|
||||||
|
|
||||||
As a developer, sooner rather than later you'll want to start interacting with `geth` and the
|
|
||||||
Ethereum network via your own programs and not manually through the console. To aid
|
|
||||||
this, `geth` has built-in support for a JSON-RPC based APIs ([standard APIs](https://github.com/ethereum/wiki/wiki/JSON-RPC)
|
|
||||||
and [`bor` specific APIs](https://github.com/maticnetwork/bor/wiki/Management-APIs)).
|
|
||||||
These can be exposed via HTTP, WebSockets and IPC (UNIX sockets on UNIX based
|
|
||||||
platforms, and named pipes on Windows).
|
|
||||||
|
|
||||||
The IPC interface is enabled by default and exposes all the APIs supported by `geth`,
|
|
||||||
whereas the HTTP and WS interfaces need to manually be enabled and only expose a
|
|
||||||
subset of APIs due to security reasons. These can be turned on/off and configured as
|
|
||||||
you'd expect.
|
|
||||||
|
|
||||||
HTTP based JSON-RPC API options:
|
|
||||||
|
|
||||||
- `--rpc` Enable the HTTP-RPC server
|
|
||||||
- `--rpcaddr` HTTP-RPC server listening interface (default: `localhost`)
|
|
||||||
- `--rpcport` HTTP-RPC server listening port (default: `8545`)
|
|
||||||
- `--rpcapi` API's offered over the HTTP-RPC interface (default: `eth,net,web3`)
|
|
||||||
- `--rpccorsdomain` Comma separated list of domains from which to accept cross origin requests (browser enforced)
|
|
||||||
- `--ws` Enable the WS-RPC server
|
|
||||||
- `--wsaddr` WS-RPC server listening interface (default: `localhost`)
|
|
||||||
- `--wsport` WS-RPC server listening port (default: `8546`)
|
|
||||||
- `--wsapi` API's offered over the WS-RPC interface (default: `eth,net,web3`)
|
|
||||||
- `--wsorigins` Origins from which to accept websockets requests
|
|
||||||
- `--ipcdisable` Disable the IPC-RPC server
|
|
||||||
- `--ipcapi` API's offered over the IPC-RPC interface (default: `admin,debug,eth,miner,net,personal,shh,txpool,web3`)
|
|
||||||
- `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it)
|
|
||||||
|
|
||||||
You'll need to use your own programming environments' capabilities (libraries, tools, etc) to
|
|
||||||
connect via HTTP, WS or IPC to a `geth` node configured with the above flags and you'll
|
|
||||||
need to speak [JSON-RPC](https://www.jsonrpc.org/specification) on all transports. You
|
|
||||||
can reuse the same connection for multiple requests!
|
|
||||||
|
|
||||||
**Note: Please understand the security implications of opening up an HTTP/WS based
|
|
||||||
transport before doing so! Hackers on the internet are actively trying to subvert
|
|
||||||
Ethereum nodes with exposed APIs! Further, all browser tabs can access locally
|
|
||||||
running web servers, so malicious web pages could try to subvert locally available
|
|
||||||
APIs!**
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The bor library (i.e. all code outside of the `cmd` directory) is licensed under the
|
The go-ethereum library (i.e. all code outside of the `cmd` directory) is licensed under the
|
||||||
[GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html),
|
[GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html),
|
||||||
also included in our repository in the `COPYING.LESSER` file.
|
also included in our repository in the `COPYING.LESSER` file.
|
||||||
|
|
||||||
The bor binaries (i.e. all code inside of the `cmd` directory) is licensed under the
|
The go-ethereum binaries (i.e. all code inside of the `cmd` directory) is licensed under the
|
||||||
[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), also
|
[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), also
|
||||||
included in our repository in the `COPYING` file.
|
included in our repository in the `COPYING` file.
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,12 @@ package abi
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
|
"github.com/maticnetwork/bor/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The ABI holds information about a contract's context and available
|
// The ABI holds information about a contract's context and available
|
||||||
|
|
@ -32,6 +34,12 @@ type ABI struct {
|
||||||
Constructor Method
|
Constructor Method
|
||||||
Methods map[string]Method
|
Methods map[string]Method
|
||||||
Events map[string]Event
|
Events map[string]Event
|
||||||
|
|
||||||
|
// Additional "special" functions introduced in solidity v0.6.0.
|
||||||
|
// It's separated from the original default fallback. Each contract
|
||||||
|
// can only define one fallback and receive function.
|
||||||
|
Fallback Method // Note it's also used to represent legacy fallback before v0.6.0
|
||||||
|
Receive Method
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON returns a parsed ABI interface and error if it failed.
|
// JSON returns a parsed ABI interface and error if it failed.
|
||||||
|
|
@ -42,7 +50,6 @@ func JSON(reader io.Reader) (ABI, error) {
|
||||||
if err := dec.Decode(&abi); err != nil {
|
if err := dec.Decode(&abi); err != nil {
|
||||||
return ABI{}, err
|
return ABI{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return abi, nil
|
return abi, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,14 +77,11 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Pack up the method ID too if not a constructor and return
|
// Pack up the method ID too if not a constructor and return
|
||||||
return append(method.Id(), arguments...), nil
|
return append(method.ID, arguments...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpack output in v according to the abi specification
|
// Unpack output in v according to the abi specification
|
||||||
func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) {
|
func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) {
|
||||||
if len(data) == 0 {
|
|
||||||
return fmt.Errorf("abi: unmarshalling empty output")
|
|
||||||
}
|
|
||||||
// since there can't be naming collisions with contracts and events,
|
// since there can't be naming collisions with contracts and events,
|
||||||
// we need to decide whether we're calling a method or an event
|
// we need to decide whether we're calling a method or an event
|
||||||
if method, ok := abi.Methods[name]; ok {
|
if method, ok := abi.Methods[name]; ok {
|
||||||
|
|
@ -94,9 +98,6 @@ func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) {
|
||||||
|
|
||||||
// UnpackIntoMap unpacks a log into the provided map[string]interface{}
|
// UnpackIntoMap unpacks a log into the provided map[string]interface{}
|
||||||
func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
|
func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
|
||||||
if len(data) == 0 {
|
|
||||||
return fmt.Errorf("abi: unmarshalling empty output")
|
|
||||||
}
|
|
||||||
// since there can't be naming collisions with contracts and events,
|
// since there can't be naming collisions with contracts and events,
|
||||||
// we need to decide whether we're calling a method or an event
|
// we need to decide whether we're calling a method or an event
|
||||||
if method, ok := abi.Methods[name]; ok {
|
if method, ok := abi.Methods[name]; ok {
|
||||||
|
|
@ -114,60 +115,92 @@ func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte)
|
||||||
// UnmarshalJSON implements json.Unmarshaler interface
|
// UnmarshalJSON implements json.Unmarshaler interface
|
||||||
func (abi *ABI) UnmarshalJSON(data []byte) error {
|
func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||||
var fields []struct {
|
var fields []struct {
|
||||||
Type string
|
Type string
|
||||||
Name string
|
Name string
|
||||||
Constant bool
|
Inputs []Argument
|
||||||
StateMutability string
|
Outputs []Argument
|
||||||
Anonymous bool
|
|
||||||
Inputs []Argument
|
|
||||||
Outputs []Argument
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Status indicator which can be: "pure", "view",
|
||||||
|
// "nonpayable" or "payable".
|
||||||
|
StateMutability string
|
||||||
|
|
||||||
|
// Deprecated Status indicators, but removed in v0.6.0.
|
||||||
|
Constant bool // True if function is either pure or view
|
||||||
|
Payable bool // True if function is payable
|
||||||
|
|
||||||
|
// Event relevant indicator represents the event is
|
||||||
|
// declared as anonymous.
|
||||||
|
Anonymous bool
|
||||||
|
}
|
||||||
if err := json.Unmarshal(data, &fields); err != nil {
|
if err := json.Unmarshal(data, &fields); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
abi.Methods = make(map[string]Method)
|
abi.Methods = make(map[string]Method)
|
||||||
abi.Events = make(map[string]Event)
|
abi.Events = make(map[string]Event)
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
switch field.Type {
|
switch field.Type {
|
||||||
case "constructor":
|
case "constructor":
|
||||||
abi.Constructor = Method{
|
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
|
||||||
Inputs: field.Inputs,
|
case "function":
|
||||||
|
name := abi.overloadedMethodName(field.Name)
|
||||||
|
abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
|
||||||
|
case "fallback":
|
||||||
|
// New introduced function type in v0.6.0, check more detail
|
||||||
|
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
|
||||||
|
if abi.HasFallback() {
|
||||||
|
return errors.New("only single fallback is allowed")
|
||||||
}
|
}
|
||||||
// empty defaults to function according to the abi spec
|
abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
||||||
case "function", "":
|
case "receive":
|
||||||
name := field.Name
|
// New introduced function type in v0.6.0, check more detail
|
||||||
_, ok := abi.Methods[name]
|
// here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
|
||||||
for idx := 0; ok; idx++ {
|
if abi.HasReceive() {
|
||||||
name = fmt.Sprintf("%s%d", field.Name, idx)
|
return errors.New("only single receive is allowed")
|
||||||
_, ok = abi.Methods[name]
|
|
||||||
}
|
}
|
||||||
isConst := field.Constant || field.StateMutability == "pure" || field.StateMutability == "view"
|
if field.StateMutability != "payable" {
|
||||||
abi.Methods[name] = Method{
|
return errors.New("the statemutability of receive can only be payable")
|
||||||
Name: name,
|
|
||||||
Const: isConst,
|
|
||||||
Inputs: field.Inputs,
|
|
||||||
Outputs: field.Outputs,
|
|
||||||
}
|
}
|
||||||
|
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
||||||
case "event":
|
case "event":
|
||||||
name := field.Name
|
name := abi.overloadedEventName(field.Name)
|
||||||
_, ok := abi.Events[name]
|
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
|
||||||
for idx := 0; ok; idx++ {
|
default:
|
||||||
name = fmt.Sprintf("%s%d", field.Name, idx)
|
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
|
||||||
_, ok = abi.Events[name]
|
|
||||||
}
|
|
||||||
abi.Events[name] = Event{
|
|
||||||
Name: name,
|
|
||||||
Anonymous: field.Anonymous,
|
|
||||||
Inputs: field.Inputs,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// overloadedMethodName returns the next available name for a given function.
|
||||||
|
// Needed since solidity allows for function overload.
|
||||||
|
//
|
||||||
|
// e.g. if the abi contains Methods send, send1
|
||||||
|
// overloadedMethodName would return send2 for input send.
|
||||||
|
func (abi *ABI) overloadedMethodName(rawName string) string {
|
||||||
|
name := rawName
|
||||||
|
_, ok := abi.Methods[name]
|
||||||
|
for idx := 0; ok; idx++ {
|
||||||
|
name = fmt.Sprintf("%s%d", rawName, idx)
|
||||||
|
_, ok = abi.Methods[name]
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// overloadedEventName returns the next available name for a given event.
|
||||||
|
// Needed since solidity allows for event overload.
|
||||||
|
//
|
||||||
|
// e.g. if the abi contains events received, received1
|
||||||
|
// overloadedEventName would return received2 for input received.
|
||||||
|
func (abi *ABI) overloadedEventName(rawName string) string {
|
||||||
|
name := rawName
|
||||||
|
_, ok := abi.Events[name]
|
||||||
|
for idx := 0; ok; idx++ {
|
||||||
|
name = fmt.Sprintf("%s%d", rawName, idx)
|
||||||
|
_, ok = abi.Events[name]
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
// MethodById looks up a method by the 4-byte id
|
// MethodById looks up a method by the 4-byte id
|
||||||
// returns nil if none found
|
// returns nil if none found
|
||||||
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
||||||
|
|
@ -175,7 +208,7 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
||||||
return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
|
return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
|
||||||
}
|
}
|
||||||
for _, method := range abi.Methods {
|
for _, method := range abi.Methods {
|
||||||
if bytes.Equal(method.Id(), sigdata[:4]) {
|
if bytes.Equal(method.ID, sigdata[:4]) {
|
||||||
return &method, nil
|
return &method, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -186,9 +219,41 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
||||||
// ABI and returns nil if none found.
|
// ABI and returns nil if none found.
|
||||||
func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
|
func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
|
||||||
for _, event := range abi.Events {
|
for _, event := range abi.Events {
|
||||||
if bytes.Equal(event.Id().Bytes(), topic.Bytes()) {
|
if bytes.Equal(event.ID.Bytes(), topic.Bytes()) {
|
||||||
return &event, nil
|
return &event, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
|
return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasFallback returns an indicator whether a fallback function is included.
|
||||||
|
func (abi *ABI) HasFallback() bool {
|
||||||
|
return abi.Fallback.Type == Fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasReceive returns an indicator whether a receive function is included.
|
||||||
|
func (abi *ABI) HasReceive() bool {
|
||||||
|
return abi.Receive.Type == Receive
|
||||||
|
}
|
||||||
|
|
||||||
|
// revertSelector is a special function selector for revert reason unpacking.
|
||||||
|
var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
|
||||||
|
|
||||||
|
// UnpackRevert resolves the abi-encoded revert reason. According to the solidity
|
||||||
|
// spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert,
|
||||||
|
// the provided revert reason is abi-encoded as if it were a call to a function
|
||||||
|
// `Error(string)`. So it's a special tool for it.
|
||||||
|
func UnpackRevert(data []byte) (string, error) {
|
||||||
|
if len(data) < 4 {
|
||||||
|
return "", errors.New("invalid data for unpacking")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(data[:4], revertSelector) {
|
||||||
|
return "", errors.New("invalid data for unpacking")
|
||||||
|
}
|
||||||
|
var reason string
|
||||||
|
typ, _ := NewType("string", "", nil)
|
||||||
|
if err := (Arguments{{Type: typ}}).Unpack(&reason, data[4:]); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return reason, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,65 +19,113 @@ package abi
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
|
"github.com/maticnetwork/bor/common/math"
|
||||||
"github.com/maticnetwork/bor/crypto"
|
"github.com/maticnetwork/bor/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
const jsondata = `
|
const jsondata = `
|
||||||
[
|
[
|
||||||
{ "type" : "function", "name" : "balance", "constant" : true },
|
{ "type" : "function", "name" : "", "stateMutability" : "view" },
|
||||||
{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }
|
{ "type" : "function", "name" : "balance", "stateMutability" : "view" },
|
||||||
|
{ "type" : "function", "name" : "send", "inputs" : [ { "name" : "amount", "type" : "uint256" } ] },
|
||||||
|
{ "type" : "function", "name" : "test", "inputs" : [ { "name" : "number", "type" : "uint32" } ] },
|
||||||
|
{ "type" : "function", "name" : "string", "inputs" : [ { "name" : "inputs", "type" : "string" } ] },
|
||||||
|
{ "type" : "function", "name" : "bool", "inputs" : [ { "name" : "inputs", "type" : "bool" } ] },
|
||||||
|
{ "type" : "function", "name" : "address", "inputs" : [ { "name" : "inputs", "type" : "address" } ] },
|
||||||
|
{ "type" : "function", "name" : "uint64[2]", "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] },
|
||||||
|
{ "type" : "function", "name" : "uint64[]", "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] },
|
||||||
|
{ "type" : "function", "name" : "int8", "inputs" : [ { "name" : "inputs", "type" : "int8" } ] },
|
||||||
|
{ "type" : "function", "name" : "foo", "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] },
|
||||||
|
{ "type" : "function", "name" : "bar", "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
|
||||||
|
{ "type" : "function", "name" : "slice", "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
|
||||||
|
{ "type" : "function", "name" : "slice256", "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
|
||||||
|
{ "type" : "function", "name" : "sliceAddress", "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] },
|
||||||
|
{ "type" : "function", "name" : "sliceMultiAddress", "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] },
|
||||||
|
{ "type" : "function", "name" : "nestedArray", "inputs" : [ { "name" : "a", "type" : "uint256[2][2]" }, { "name" : "b", "type" : "address[]" } ] },
|
||||||
|
{ "type" : "function", "name" : "nestedArray2", "inputs" : [ { "name" : "a", "type" : "uint8[][2]" } ] },
|
||||||
|
{ "type" : "function", "name" : "nestedSlice", "inputs" : [ { "name" : "a", "type" : "uint8[][]" } ] },
|
||||||
|
{ "type" : "function", "name" : "receive", "inputs" : [ { "name" : "memo", "type" : "bytes" }], "outputs" : [], "payable" : true, "stateMutability" : "payable" },
|
||||||
|
{ "type" : "function", "name" : "fixedArrStr", "stateMutability" : "view", "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
|
||||||
|
{ "type" : "function", "name" : "fixedArrBytes", "stateMutability" : "view", "inputs" : [ { "name" : "bytes", "type" : "bytes" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
|
||||||
|
{ "type" : "function", "name" : "mixedArrStr", "stateMutability" : "view", "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type" : "uint256[2]" }, { "name" : "dynArr", "type" : "uint256[]" } ] },
|
||||||
|
{ "type" : "function", "name" : "doubleFixedArrStr", "stateMutability" : "view", "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type" : "uint256[2]" }, { "name" : "fixedArr2", "type" : "uint256[3]" } ] },
|
||||||
|
{ "type" : "function", "name" : "multipleMixedArrStr", "stateMutability" : "view", "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type" : "uint256[2]" }, { "name" : "dynArr", "type" : "uint256[]" }, { "name" : "fixedArr2", "type" : "uint256[3]" } ] },
|
||||||
|
{ "type" : "function", "name" : "overloadedNames", "stateMutability" : "view", "inputs": [ { "components": [ { "internalType": "uint256", "name": "_f", "type": "uint256" }, { "internalType": "uint256", "name": "__f", "type": "uint256"}, { "internalType": "uint256", "name": "f", "type": "uint256"}],"internalType": "struct Overloader.F", "name": "f","type": "tuple"}]}
|
||||||
]`
|
]`
|
||||||
|
|
||||||
const jsondata2 = `
|
var (
|
||||||
[
|
Uint256, _ = NewType("uint256", "", nil)
|
||||||
{ "type" : "function", "name" : "balance", "constant" : true },
|
Uint32, _ = NewType("uint32", "", nil)
|
||||||
{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] },
|
Uint16, _ = NewType("uint16", "", nil)
|
||||||
{ "type" : "function", "name" : "test", "constant" : false, "inputs" : [ { "name" : "number", "type" : "uint32" } ] },
|
String, _ = NewType("string", "", nil)
|
||||||
{ "type" : "function", "name" : "string", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "string" } ] },
|
Bool, _ = NewType("bool", "", nil)
|
||||||
{ "type" : "function", "name" : "bool", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" } ] },
|
Bytes, _ = NewType("bytes", "", nil)
|
||||||
{ "type" : "function", "name" : "address", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address" } ] },
|
Address, _ = NewType("address", "", nil)
|
||||||
{ "type" : "function", "name" : "uint64[2]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] },
|
Uint64Arr, _ = NewType("uint64[]", "", nil)
|
||||||
{ "type" : "function", "name" : "uint64[]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] },
|
AddressArr, _ = NewType("address[]", "", nil)
|
||||||
{ "type" : "function", "name" : "foo", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] },
|
Int8, _ = NewType("int8", "", nil)
|
||||||
{ "type" : "function", "name" : "bar", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
|
// Special types for testing
|
||||||
{ "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
|
Uint32Arr2, _ = NewType("uint32[2]", "", nil)
|
||||||
{ "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
|
Uint64Arr2, _ = NewType("uint64[2]", "", nil)
|
||||||
{ "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] },
|
Uint256Arr, _ = NewType("uint256[]", "", nil)
|
||||||
{ "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] },
|
Uint256Arr2, _ = NewType("uint256[2]", "", nil)
|
||||||
{ "type" : "function", "name" : "nestedArray", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint256[2][2]" }, { "name" : "b", "type" : "address[]" } ] },
|
Uint256Arr3, _ = NewType("uint256[3]", "", nil)
|
||||||
{ "type" : "function", "name" : "nestedArray2", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint8[][2]" } ] },
|
Uint256ArrNested, _ = NewType("uint256[2][2]", "", nil)
|
||||||
{ "type" : "function", "name" : "nestedSlice", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint8[][]" } ] }
|
Uint8ArrNested, _ = NewType("uint8[][2]", "", nil)
|
||||||
]`
|
Uint8SliceNested, _ = NewType("uint8[][]", "", nil)
|
||||||
|
TupleF, _ = NewType("tuple", "struct Overloader.F", []ArgumentMarshaling{
|
||||||
|
{Name: "_f", Type: "uint256"},
|
||||||
|
{Name: "__f", Type: "uint256"},
|
||||||
|
{Name: "f", Type: "uint256"}})
|
||||||
|
)
|
||||||
|
|
||||||
|
var methods = map[string]Method{
|
||||||
|
"": NewMethod("", "", Function, "view", false, false, nil, nil),
|
||||||
|
"balance": NewMethod("balance", "balance", Function, "view", false, false, nil, nil),
|
||||||
|
"send": NewMethod("send", "send", Function, "", false, false, []Argument{{"amount", Uint256, false}}, nil),
|
||||||
|
"test": NewMethod("test", "test", Function, "", false, false, []Argument{{"number", Uint32, false}}, nil),
|
||||||
|
"string": NewMethod("string", "string", Function, "", false, false, []Argument{{"inputs", String, false}}, nil),
|
||||||
|
"bool": NewMethod("bool", "bool", Function, "", false, false, []Argument{{"inputs", Bool, false}}, nil),
|
||||||
|
"address": NewMethod("address", "address", Function, "", false, false, []Argument{{"inputs", Address, false}}, nil),
|
||||||
|
"uint64[]": NewMethod("uint64[]", "uint64[]", Function, "", false, false, []Argument{{"inputs", Uint64Arr, false}}, nil),
|
||||||
|
"uint64[2]": NewMethod("uint64[2]", "uint64[2]", Function, "", false, false, []Argument{{"inputs", Uint64Arr2, false}}, nil),
|
||||||
|
"int8": NewMethod("int8", "int8", Function, "", false, false, []Argument{{"inputs", Int8, false}}, nil),
|
||||||
|
"foo": NewMethod("foo", "foo", Function, "", false, false, []Argument{{"inputs", Uint32, false}}, nil),
|
||||||
|
"bar": NewMethod("bar", "bar", Function, "", false, false, []Argument{{"inputs", Uint32, false}, {"string", Uint16, false}}, nil),
|
||||||
|
"slice": NewMethod("slice", "slice", Function, "", false, false, []Argument{{"inputs", Uint32Arr2, false}}, nil),
|
||||||
|
"slice256": NewMethod("slice256", "slice256", Function, "", false, false, []Argument{{"inputs", Uint256Arr2, false}}, nil),
|
||||||
|
"sliceAddress": NewMethod("sliceAddress", "sliceAddress", Function, "", false, false, []Argument{{"inputs", AddressArr, false}}, nil),
|
||||||
|
"sliceMultiAddress": NewMethod("sliceMultiAddress", "sliceMultiAddress", Function, "", false, false, []Argument{{"a", AddressArr, false}, {"b", AddressArr, false}}, nil),
|
||||||
|
"nestedArray": NewMethod("nestedArray", "nestedArray", Function, "", false, false, []Argument{{"a", Uint256ArrNested, false}, {"b", AddressArr, false}}, nil),
|
||||||
|
"nestedArray2": NewMethod("nestedArray2", "nestedArray2", Function, "", false, false, []Argument{{"a", Uint8ArrNested, false}}, nil),
|
||||||
|
"nestedSlice": NewMethod("nestedSlice", "nestedSlice", Function, "", false, false, []Argument{{"a", Uint8SliceNested, false}}, nil),
|
||||||
|
"receive": NewMethod("receive", "receive", Function, "payable", false, true, []Argument{{"memo", Bytes, false}}, []Argument{}),
|
||||||
|
"fixedArrStr": NewMethod("fixedArrStr", "fixedArrStr", Function, "view", false, false, []Argument{{"str", String, false}, {"fixedArr", Uint256Arr2, false}}, nil),
|
||||||
|
"fixedArrBytes": NewMethod("fixedArrBytes", "fixedArrBytes", Function, "view", false, false, []Argument{{"bytes", Bytes, false}, {"fixedArr", Uint256Arr2, false}}, nil),
|
||||||
|
"mixedArrStr": NewMethod("mixedArrStr", "mixedArrStr", Function, "view", false, false, []Argument{{"str", String, false}, {"fixedArr", Uint256Arr2, false}, {"dynArr", Uint256Arr, false}}, nil),
|
||||||
|
"doubleFixedArrStr": NewMethod("doubleFixedArrStr", "doubleFixedArrStr", Function, "view", false, false, []Argument{{"str", String, false}, {"fixedArr1", Uint256Arr2, false}, {"fixedArr2", Uint256Arr3, false}}, nil),
|
||||||
|
"multipleMixedArrStr": NewMethod("multipleMixedArrStr", "multipleMixedArrStr", Function, "view", false, false, []Argument{{"str", String, false}, {"fixedArr1", Uint256Arr2, false}, {"dynArr", Uint256Arr, false}, {"fixedArr2", Uint256Arr3, false}}, nil),
|
||||||
|
"overloadedNames": NewMethod("overloadedNames", "overloadedNames", Function, "view", false, false, []Argument{{"f", TupleF, false}}, nil),
|
||||||
|
}
|
||||||
|
|
||||||
func TestReader(t *testing.T) {
|
func TestReader(t *testing.T) {
|
||||||
Uint256, _ := NewType("uint256", nil)
|
abi := ABI{
|
||||||
exp := ABI{
|
Methods: methods,
|
||||||
Methods: map[string]Method{
|
|
||||||
"balance": {
|
|
||||||
"balance", true, nil, nil,
|
|
||||||
},
|
|
||||||
"send": {
|
|
||||||
"send", false, []Argument{
|
|
||||||
{"amount", Uint256, false},
|
|
||||||
}, nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(jsondata))
|
exp, err := JSON(strings.NewReader(jsondata))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// deep equal fails for some reason
|
|
||||||
for name, expM := range exp.Methods {
|
for name, expM := range exp.Methods {
|
||||||
gotM, exist := abi.Methods[name]
|
gotM, exist := abi.Methods[name]
|
||||||
if !exist {
|
if !exist {
|
||||||
|
|
@ -99,11 +147,60 @@ func TestReader(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTestNumbers(t *testing.T) {
|
func TestInvalidABI(t *testing.T) {
|
||||||
abi, err := JSON(strings.NewReader(jsondata2))
|
json := `[{ "type" : "function", "name" : "", "constant" : fals }]`
|
||||||
|
_, err := JSON(strings.NewReader(json))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("invalid json should produce error")
|
||||||
|
}
|
||||||
|
json2 := `[{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "typ" : "uint256" } ] }]`
|
||||||
|
_, err = JSON(strings.NewReader(json2))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("invalid json should produce error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestConstructor tests a constructor function.
|
||||||
|
// The test is based on the following contract:
|
||||||
|
// contract TestConstructor {
|
||||||
|
// constructor(uint256 a, uint256 b) public{}
|
||||||
|
// }
|
||||||
|
func TestConstructor(t *testing.T) {
|
||||||
|
json := `[{ "inputs": [{"internalType": "uint256","name": "a","type": "uint256" },{ "internalType": "uint256","name": "b","type": "uint256"}],"stateMutability": "nonpayable","type": "constructor"}]`
|
||||||
|
method := NewMethod("", "", Constructor, "nonpayable", false, false, []Argument{{"a", Uint256, false}, {"b", Uint256, false}}, nil)
|
||||||
|
// Test from JSON
|
||||||
|
abi, err := JSON(strings.NewReader(json))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(abi.Constructor, method) {
|
||||||
|
t.Error("Missing expected constructor")
|
||||||
|
}
|
||||||
|
// Test pack/unpack
|
||||||
|
packed, err := abi.Pack("", big.NewInt(1), big.NewInt(2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
t.FailNow()
|
}
|
||||||
|
v := struct {
|
||||||
|
A *big.Int
|
||||||
|
B *big.Int
|
||||||
|
}{new(big.Int), new(big.Int)}
|
||||||
|
//abi.Unpack(&v, "", packed)
|
||||||
|
if err := abi.Constructor.Inputs.Unpack(&v, packed); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(v.A, big.NewInt(1)) {
|
||||||
|
t.Error("Unable to pack/unpack from constructor")
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(v.B, big.NewInt(2)) {
|
||||||
|
t.Error("Unable to pack/unpack from constructor")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTestNumbers(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := abi.Pack("balance"); err != nil {
|
if _, err := abi.Pack("balance"); err != nil {
|
||||||
|
|
@ -137,69 +234,26 @@ func TestTestNumbers(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTestString(t *testing.T) {
|
|
||||||
abi, err := JSON(strings.NewReader(jsondata2))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := abi.Pack("string", "hello world"); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTestBool(t *testing.T) {
|
|
||||||
abi, err := JSON(strings.NewReader(jsondata2))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := abi.Pack("bool", true); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTestSlice(t *testing.T) {
|
|
||||||
abi, err := JSON(strings.NewReader(jsondata2))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
slice := make([]uint64, 2)
|
|
||||||
if _, err := abi.Pack("uint64[2]", slice); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := abi.Pack("uint64[]", slice); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMethodSignature(t *testing.T) {
|
func TestMethodSignature(t *testing.T) {
|
||||||
String, _ := NewType("string", nil)
|
m := NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil)
|
||||||
m := Method{"foo", false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil}
|
|
||||||
exp := "foo(string,string)"
|
exp := "foo(string,string)"
|
||||||
if m.Sig() != exp {
|
if m.Sig != exp {
|
||||||
t.Error("signature mismatch", exp, "!=", m.Sig())
|
t.Error("signature mismatch", exp, "!=", m.Sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
idexp := crypto.Keccak256([]byte(exp))[:4]
|
idexp := crypto.Keccak256([]byte(exp))[:4]
|
||||||
if !bytes.Equal(m.Id(), idexp) {
|
if !bytes.Equal(m.ID, idexp) {
|
||||||
t.Errorf("expected ids to match %x != %x", m.Id(), idexp)
|
t.Errorf("expected ids to match %x != %x", m.ID, idexp)
|
||||||
}
|
}
|
||||||
|
|
||||||
uintt, _ := NewType("uint256", nil)
|
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", Uint256, false}}, nil)
|
||||||
m = Method{"foo", false, []Argument{{"bar", uintt, false}}, nil}
|
|
||||||
exp = "foo(uint256)"
|
exp = "foo(uint256)"
|
||||||
if m.Sig() != exp {
|
if m.Sig != exp {
|
||||||
t.Error("signature mismatch", exp, "!=", m.Sig())
|
t.Error("signature mismatch", exp, "!=", m.Sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method with tuple arguments
|
// Method with tuple arguments
|
||||||
s, _ := NewType("tuple", []ArgumentMarshaling{
|
s, _ := NewType("tuple", "", []ArgumentMarshaling{
|
||||||
{Name: "a", Type: "int256"},
|
{Name: "a", Type: "int256"},
|
||||||
{Name: "b", Type: "int256[]"},
|
{Name: "b", Type: "int256[]"},
|
||||||
{Name: "c", Type: "tuple[]", Components: []ArgumentMarshaling{
|
{Name: "c", Type: "tuple[]", Components: []ArgumentMarshaling{
|
||||||
|
|
@ -211,18 +265,40 @@ func TestMethodSignature(t *testing.T) {
|
||||||
{Name: "y", Type: "int256"},
|
{Name: "y", Type: "int256"},
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
m = Method{"foo", false, []Argument{{"s", s, false}, {"bar", String, false}}, nil}
|
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"s", s, false}, {"bar", String, false}}, nil)
|
||||||
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
|
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
|
||||||
if m.Sig() != exp {
|
if m.Sig != exp {
|
||||||
t.Error("signature mismatch", exp, "!=", m.Sig())
|
t.Error("signature mismatch", exp, "!=", m.Sig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiPack(t *testing.T) {
|
func TestOverloadedMethodSignature(t *testing.T) {
|
||||||
abi, err := JSON(strings.NewReader(jsondata2))
|
json := `[{"constant":true,"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"i","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"}],"name":"bar","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"},{"indexed":false,"name":"j","type":"uint256"}],"name":"bar","type":"event"}]`
|
||||||
|
abi, err := JSON(strings.NewReader(json))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
t.FailNow()
|
}
|
||||||
|
check := func(name string, expect string, method bool) {
|
||||||
|
if method {
|
||||||
|
if abi.Methods[name].Sig != expect {
|
||||||
|
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if abi.Events[name].Sig != expect {
|
||||||
|
t.Fatalf("The signature of overloaded event mismatch, want %s, have %s", expect, abi.Events[name].Sig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check("foo", "foo(uint256,uint256)", true)
|
||||||
|
check("foo0", "foo(uint256)", true)
|
||||||
|
check("bar", "bar(uint256)", false)
|
||||||
|
check("bar0", "bar(uint256,uint256)", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiPack(t *testing.T) {
|
||||||
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig := crypto.Keccak256([]byte("bar(uint32,uint16)"))[:4]
|
sig := crypto.Keccak256([]byte("bar(uint32,uint16)"))[:4]
|
||||||
|
|
@ -232,10 +308,8 @@ func TestMultiPack(t *testing.T) {
|
||||||
|
|
||||||
packed, err := abi.Pack("bar", uint32(10), uint16(11))
|
packed, err := abi.Pack("bar", uint32(10), uint16(11))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
t.FailNow()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(packed, sig) {
|
if !bytes.Equal(packed, sig) {
|
||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
@ -246,11 +320,11 @@ func ExampleJSON() {
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
out, err := abi.Pack("isBar", common.HexToAddress("01"))
|
out, err := abi.Pack("isBar", common.HexToAddress("01"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%x\n", out)
|
fmt.Printf("%x\n", out)
|
||||||
|
|
@ -387,15 +461,7 @@ func TestInputVariableInputLength(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
const definition = `[
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
{ "type" : "function", "name" : "fixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
|
|
||||||
{ "type" : "function", "name" : "fixedArrBytes", "constant" : true, "inputs" : [ { "name" : "str", "type" : "bytes" }, { "name" : "fixedArr", "type" : "uint256[2]" } ] },
|
|
||||||
{ "type" : "function", "name" : "mixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr", "type": "uint256[2]" }, { "name" : "dynArr", "type": "uint256[]" } ] },
|
|
||||||
{ "type" : "function", "name" : "doubleFixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type": "uint256[2]" }, { "name" : "fixedArr2", "type": "uint256[3]" } ] },
|
|
||||||
{ "type" : "function", "name" : "multipleMixedArrStr", "constant" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "fixedArr1", "type": "uint256[2]" }, { "name" : "dynArr", "type" : "uint256[]" }, { "name" : "fixedArr2", "type" : "uint256[3]" } ] }
|
|
||||||
]`
|
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
@ -542,7 +608,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
strvalue = common.RightPadBytes([]byte(strin), 32)
|
strvalue = common.RightPadBytes([]byte(strin), 32)
|
||||||
fixedarrin1value1 = common.LeftPadBytes(fixedarrin1[0].Bytes(), 32)
|
fixedarrin1value1 = common.LeftPadBytes(fixedarrin1[0].Bytes(), 32)
|
||||||
fixedarrin1value2 = common.LeftPadBytes(fixedarrin1[1].Bytes(), 32)
|
fixedarrin1value2 = common.LeftPadBytes(fixedarrin1[1].Bytes(), 32)
|
||||||
dynarroffset = U256(big.NewInt(int64(256 + ((len(strin)/32)+1)*32)))
|
dynarroffset = math.U256Bytes(big.NewInt(int64(256 + ((len(strin)/32)+1)*32)))
|
||||||
dynarrlength = make([]byte, 32)
|
dynarrlength = make([]byte, 32)
|
||||||
dynarrlength[31] = byte(len(dynarrin))
|
dynarrlength[31] = byte(len(dynarrin))
|
||||||
dynarrinvalue1 = common.LeftPadBytes(dynarrin[0].Bytes(), 32)
|
dynarrinvalue1 = common.LeftPadBytes(dynarrin[0].Bytes(), 32)
|
||||||
|
|
@ -569,7 +635,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultFunctionParsing(t *testing.T) {
|
func TestDefaultFunctionParsing(t *testing.T) {
|
||||||
const definition = `[{ "name" : "balance" }]`
|
const definition = `[{ "name" : "balance", "type" : "function" }]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -589,9 +655,7 @@ func TestBareEvents(t *testing.T) {
|
||||||
{ "type" : "event", "name" : "tuple", "inputs" : [{ "indexed":false, "name":"t", "type":"tuple", "components":[{"name":"a", "type":"uint256"}] }, { "indexed":true, "name":"arg1", "type":"address" }] }
|
{ "type" : "event", "name" : "tuple", "inputs" : [{ "indexed":false, "name":"t", "type":"tuple", "components":[{"name":"a", "type":"uint256"}] }, { "indexed":true, "name":"arg1", "type":"address" }] }
|
||||||
]`
|
]`
|
||||||
|
|
||||||
arg0, _ := NewType("uint256", nil)
|
tuple, _ := NewType("tuple", "", []ArgumentMarshaling{{Name: "a", Type: "uint256"}})
|
||||||
arg1, _ := NewType("address", nil)
|
|
||||||
tuple, _ := NewType("tuple", []ArgumentMarshaling{{Name: "a", Type: "uint256"}})
|
|
||||||
|
|
||||||
expectedEvents := map[string]struct {
|
expectedEvents := map[string]struct {
|
||||||
Anonymous bool
|
Anonymous bool
|
||||||
|
|
@ -600,12 +664,12 @@ func TestBareEvents(t *testing.T) {
|
||||||
"balance": {false, nil},
|
"balance": {false, nil},
|
||||||
"anon": {true, nil},
|
"anon": {true, nil},
|
||||||
"args": {false, []Argument{
|
"args": {false, []Argument{
|
||||||
{Name: "arg0", Type: arg0, Indexed: false},
|
{Name: "arg0", Type: Uint256, Indexed: false},
|
||||||
{Name: "arg1", Type: arg1, Indexed: true},
|
{Name: "arg1", Type: Address, Indexed: true},
|
||||||
}},
|
}},
|
||||||
"tuple": {false, []Argument{
|
"tuple": {false, []Argument{
|
||||||
{Name: "t", Type: tuple, Indexed: false},
|
{Name: "t", Type: tuple, Indexed: false},
|
||||||
{Name: "arg1", Type: arg1, Indexed: true},
|
{Name: "arg1", Type: Address, Indexed: true},
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -878,45 +942,25 @@ func TestUnpackIntoMapNamingConflict(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestABI_MethodById(t *testing.T) {
|
func TestABI_MethodById(t *testing.T) {
|
||||||
const abiJSON = `[
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
{"type":"function","name":"receive","constant":false,"inputs":[{"name":"memo","type":"bytes"}],"outputs":[],"payable":true,"stateMutability":"payable"},
|
|
||||||
{"type":"event","name":"received","anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}]},
|
|
||||||
{"type":"function","name":"fixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr","type":"uint256[2]"}]},
|
|
||||||
{"type":"function","name":"fixedArrBytes","constant":true,"inputs":[{"name":"str","type":"bytes"},{"name":"fixedArr","type":"uint256[2]"}]},
|
|
||||||
{"type":"function","name":"mixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr","type":"uint256[2]"},{"name":"dynArr","type":"uint256[]"}]},
|
|
||||||
{"type":"function","name":"doubleFixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr1","type":"uint256[2]"},{"name":"fixedArr2","type":"uint256[3]"}]},
|
|
||||||
{"type":"function","name":"multipleMixedArrStr","constant":true,"inputs":[{"name":"str","type":"string"},{"name":"fixedArr1","type":"uint256[2]"},{"name":"dynArr","type":"uint256[]"},{"name":"fixedArr2","type":"uint256[3]"}]},
|
|
||||||
{"type":"function","name":"balance","constant":true},
|
|
||||||
{"type":"function","name":"send","constant":false,"inputs":[{"name":"amount","type":"uint256"}]},
|
|
||||||
{"type":"function","name":"test","constant":false,"inputs":[{"name":"number","type":"uint32"}]},
|
|
||||||
{"type":"function","name":"string","constant":false,"inputs":[{"name":"inputs","type":"string"}]},
|
|
||||||
{"type":"function","name":"bool","constant":false,"inputs":[{"name":"inputs","type":"bool"}]},
|
|
||||||
{"type":"function","name":"address","constant":false,"inputs":[{"name":"inputs","type":"address"}]},
|
|
||||||
{"type":"function","name":"uint64[2]","constant":false,"inputs":[{"name":"inputs","type":"uint64[2]"}]},
|
|
||||||
{"type":"function","name":"uint64[]","constant":false,"inputs":[{"name":"inputs","type":"uint64[]"}]},
|
|
||||||
{"type":"function","name":"foo","constant":false,"inputs":[{"name":"inputs","type":"uint32"}]},
|
|
||||||
{"type":"function","name":"bar","constant":false,"inputs":[{"name":"inputs","type":"uint32"},{"name":"string","type":"uint16"}]},
|
|
||||||
{"type":"function","name":"_slice","constant":false,"inputs":[{"name":"inputs","type":"uint32[2]"}]},
|
|
||||||
{"type":"function","name":"__slice256","constant":false,"inputs":[{"name":"inputs","type":"uint256[2]"}]},
|
|
||||||
{"type":"function","name":"sliceAddress","constant":false,"inputs":[{"name":"inputs","type":"address[]"}]},
|
|
||||||
{"type":"function","name":"sliceMultiAddress","constant":false,"inputs":[{"name":"a","type":"address[]"},{"name":"b","type":"address[]"}]}
|
|
||||||
]
|
|
||||||
`
|
|
||||||
abi, err := JSON(strings.NewReader(abiJSON))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
for name, m := range abi.Methods {
|
for name, m := range abi.Methods {
|
||||||
a := fmt.Sprintf("%v", m)
|
a := fmt.Sprintf("%v", m)
|
||||||
m2, err := abi.MethodById(m.Id())
|
m2, err := abi.MethodById(m.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to look up ABI method: %v", err)
|
t.Fatalf("Failed to look up ABI method: %v", err)
|
||||||
}
|
}
|
||||||
b := fmt.Sprintf("%v", m2)
|
b := fmt.Sprintf("%v", m2)
|
||||||
if a != b {
|
if a != b {
|
||||||
t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, common.ToHex(m.Id()))
|
t.Errorf("Method %v (id %x) not 'findable' by id in ABI", name, m.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// test unsuccessful lookups
|
||||||
|
if _, err = abi.MethodById(crypto.Keccak256()); err == nil {
|
||||||
|
t.Error("Expected error: no method with this id")
|
||||||
|
}
|
||||||
// Also test empty
|
// Also test empty
|
||||||
if _, err := abi.MethodById([]byte{0x00}); err == nil {
|
if _, err := abi.MethodById([]byte{0x00}); err == nil {
|
||||||
t.Errorf("Expected error, too short to decode data")
|
t.Errorf("Expected error, too short to decode data")
|
||||||
|
|
@ -982,8 +1026,8 @@ func TestABI_EventById(t *testing.T) {
|
||||||
t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum)
|
t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.Id() != topicID {
|
if event.ID != topicID {
|
||||||
t.Errorf("Event id %s does not match topic %s, test #%d", event.Id().Hex(), topicID.Hex(), testnum)
|
t.Errorf("Event id %s does not match topic %s, test #%d", event.ID.Hex(), topicID.Hex(), testnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
unknowntopicID := crypto.Keccak256Hash([]byte("unknownEvent"))
|
unknowntopicID := crypto.Keccak256Hash([]byte("unknownEvent"))
|
||||||
|
|
@ -997,26 +1041,6 @@ func TestABI_EventById(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDuplicateMethodNames(t *testing.T) {
|
|
||||||
abiJSON := `[{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"},{"name":"customFallback","type":"string"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]`
|
|
||||||
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if _, ok := contractAbi.Methods["transfer"]; !ok {
|
|
||||||
t.Fatalf("Could not find original method")
|
|
||||||
}
|
|
||||||
if _, ok := contractAbi.Methods["transfer0"]; !ok {
|
|
||||||
t.Fatalf("Could not find duplicate method")
|
|
||||||
}
|
|
||||||
if _, ok := contractAbi.Methods["transfer1"]; !ok {
|
|
||||||
t.Fatalf("Could not find duplicate method")
|
|
||||||
}
|
|
||||||
if _, ok := contractAbi.Methods["transfer2"]; ok {
|
|
||||||
t.Fatalf("Should not have found extra method")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestDoubleDuplicateMethodNames checks that if transfer0 already exists, there won't be a name
|
// TestDoubleDuplicateMethodNames checks that if transfer0 already exists, there won't be a name
|
||||||
// conflict and that the second transfer method will be renamed transfer1.
|
// conflict and that the second transfer method will be renamed transfer1.
|
||||||
func TestDoubleDuplicateMethodNames(t *testing.T) {
|
func TestDoubleDuplicateMethodNames(t *testing.T) {
|
||||||
|
|
@ -1038,3 +1062,87 @@ func TestDoubleDuplicateMethodNames(t *testing.T) {
|
||||||
t.Fatalf("Should not have found extra method")
|
t.Fatalf("Should not have found extra method")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDoubleDuplicateEventNames checks that if send0 already exists, there won't be a name
|
||||||
|
// conflict and that the second send event will be renamed send1.
|
||||||
|
// The test runs the abi of the following contract.
|
||||||
|
// contract DuplicateEvent {
|
||||||
|
// event send(uint256 a);
|
||||||
|
// event send0();
|
||||||
|
// event send();
|
||||||
|
// }
|
||||||
|
func TestDoubleDuplicateEventNames(t *testing.T) {
|
||||||
|
abiJSON := `[{"anonymous": false,"inputs": [{"indexed": false,"internalType": "uint256","name": "a","type": "uint256"}],"name": "send","type": "event"},{"anonymous": false,"inputs": [],"name": "send0","type": "event"},{ "anonymous": false, "inputs": [],"name": "send","type": "event"}]`
|
||||||
|
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, ok := contractAbi.Events["send"]; !ok {
|
||||||
|
t.Fatalf("Could not find original event")
|
||||||
|
}
|
||||||
|
if _, ok := contractAbi.Events["send0"]; !ok {
|
||||||
|
t.Fatalf("Could not find duplicate event")
|
||||||
|
}
|
||||||
|
if _, ok := contractAbi.Events["send1"]; !ok {
|
||||||
|
t.Fatalf("Could not find duplicate event")
|
||||||
|
}
|
||||||
|
if _, ok := contractAbi.Events["send2"]; ok {
|
||||||
|
t.Fatalf("Should not have found extra event")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestUnnamedEventParam checks that an event with unnamed parameters is
|
||||||
|
// correctly handled
|
||||||
|
// The test runs the abi of the following contract.
|
||||||
|
// contract TestEvent {
|
||||||
|
// event send(uint256, uint256);
|
||||||
|
// }
|
||||||
|
func TestUnnamedEventParam(t *testing.T) {
|
||||||
|
abiJSON := `[{ "anonymous": false, "inputs": [{ "indexed": false,"internalType": "uint256", "name": "","type": "uint256"},{"indexed": false,"internalType": "uint256","name": "","type": "uint256"}],"name": "send","type": "event"}]`
|
||||||
|
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
event, ok := contractAbi.Events["send"]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Could not find event")
|
||||||
|
}
|
||||||
|
if event.Inputs[0].Name != "arg0" {
|
||||||
|
t.Fatalf("Could not find input")
|
||||||
|
}
|
||||||
|
if event.Inputs[1].Name != "arg1" {
|
||||||
|
t.Fatalf("Could not find input")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnpackRevert(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var cases = []struct {
|
||||||
|
input string
|
||||||
|
expect string
|
||||||
|
expectErr error
|
||||||
|
}{
|
||||||
|
{"", "", errors.New("invalid data for unpacking")},
|
||||||
|
{"08c379a1", "", errors.New("invalid data for unpacking")},
|
||||||
|
{"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil},
|
||||||
|
}
|
||||||
|
for index, c := range cases {
|
||||||
|
t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) {
|
||||||
|
got, err := UnpackRevert(common.Hex2Bytes(c.input))
|
||||||
|
if c.expectErr != nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected non-nil error")
|
||||||
|
}
|
||||||
|
if err.Error() != c.expectErr.Error() {
|
||||||
|
t.Fatalf("Expected error mismatch, want %v, got %v", c.expectErr, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.expect != got {
|
||||||
|
t.Fatalf("Output mismatch, want %v, got %v", c.expect, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,11 @@ type Argument struct {
|
||||||
type Arguments []Argument
|
type Arguments []Argument
|
||||||
|
|
||||||
type ArgumentMarshaling struct {
|
type ArgumentMarshaling struct {
|
||||||
Name string
|
Name string
|
||||||
Type string
|
Type string
|
||||||
Components []ArgumentMarshaling
|
InternalType string
|
||||||
Indexed bool
|
Components []ArgumentMarshaling
|
||||||
|
Indexed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler interface
|
// UnmarshalJSON implements json.Unmarshaler interface
|
||||||
|
|
@ -48,7 +49,7 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
|
||||||
return fmt.Errorf("argument json err: %v", err)
|
return fmt.Errorf("argument json err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
argument.Type, err = NewType(arg.Type, arg.Components)
|
argument.Type, err = NewType(arg.Type, arg.InternalType, arg.Components)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -58,18 +59,6 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LengthNonIndexed returns the number of arguments when not counting 'indexed' ones. Only events
|
|
||||||
// can ever have 'indexed' arguments, it should always be false on arguments for method input/output
|
|
||||||
func (arguments Arguments) LengthNonIndexed() int {
|
|
||||||
out := 0
|
|
||||||
for _, arg := range arguments {
|
|
||||||
if !arg.Indexed {
|
|
||||||
out++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// NonIndexed returns the arguments with indexed arguments filtered out
|
// NonIndexed returns the arguments with indexed arguments filtered out
|
||||||
func (arguments Arguments) NonIndexed() Arguments {
|
func (arguments Arguments) NonIndexed() Arguments {
|
||||||
var ret []Argument
|
var ret []Argument
|
||||||
|
|
@ -88,6 +77,12 @@ func (arguments Arguments) isTuple() bool {
|
||||||
|
|
||||||
// Unpack performs the operation hexdata -> Go format
|
// Unpack performs the operation hexdata -> Go format
|
||||||
func (arguments Arguments) Unpack(v interface{}, data []byte) error {
|
func (arguments Arguments) Unpack(v interface{}, data []byte) error {
|
||||||
|
if len(data) == 0 {
|
||||||
|
if len(arguments) != 0 {
|
||||||
|
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||||
|
}
|
||||||
|
return nil // Nothing to unmarshal, return
|
||||||
|
}
|
||||||
// make sure the passed value is arguments pointer
|
// make sure the passed value is arguments pointer
|
||||||
if reflect.Ptr != reflect.ValueOf(v).Kind() {
|
if reflect.Ptr != reflect.ValueOf(v).Kind() {
|
||||||
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
|
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
|
||||||
|
|
@ -96,6 +91,9 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(marshalledValues) == 0 {
|
||||||
|
return fmt.Errorf("abi: Unpack(no-values unmarshalled %T)", v)
|
||||||
|
}
|
||||||
if arguments.isTuple() {
|
if arguments.isTuple() {
|
||||||
return arguments.unpackTuple(v, marshalledValues)
|
return arguments.unpackTuple(v, marshalledValues)
|
||||||
}
|
}
|
||||||
|
|
@ -104,90 +102,20 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
|
||||||
|
|
||||||
// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value
|
// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value
|
||||||
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
|
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
|
||||||
marshalledValues, err := arguments.UnpackValues(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return arguments.unpackIntoMap(v, marshalledValues)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpack sets the unmarshalled value to go format.
|
|
||||||
// Note the dst here must be settable.
|
|
||||||
func unpack(t *Type, dst interface{}, src interface{}) error {
|
|
||||||
var (
|
|
||||||
dstVal = reflect.ValueOf(dst).Elem()
|
|
||||||
srcVal = reflect.ValueOf(src)
|
|
||||||
)
|
|
||||||
tuple, typ := false, t
|
|
||||||
for {
|
|
||||||
if typ.T == SliceTy || typ.T == ArrayTy {
|
|
||||||
typ = typ.Elem
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tuple = typ.T == TupleTy
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if !tuple {
|
|
||||||
return set(dstVal, srcVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dereferences interface or pointer wrapper
|
|
||||||
dstVal = indirectInterfaceOrPtr(dstVal)
|
|
||||||
|
|
||||||
switch t.T {
|
|
||||||
case TupleTy:
|
|
||||||
if dstVal.Kind() != reflect.Struct {
|
|
||||||
return fmt.Errorf("abi: invalid dst value for unpack, want struct, got %s", dstVal.Kind())
|
|
||||||
}
|
|
||||||
fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, dstVal)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for i, elem := range t.TupleElems {
|
|
||||||
fname := fieldmap[t.TupleRawNames[i]]
|
|
||||||
field := dstVal.FieldByName(fname)
|
|
||||||
if !field.IsValid() {
|
|
||||||
return fmt.Errorf("abi: field %s can't found in the given value", t.TupleRawNames[i])
|
|
||||||
}
|
|
||||||
if err := unpack(elem, field.Addr().Interface(), srcVal.Field(i).Interface()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
case SliceTy:
|
|
||||||
if dstVal.Kind() != reflect.Slice {
|
|
||||||
return fmt.Errorf("abi: invalid dst value for unpack, want slice, got %s", dstVal.Kind())
|
|
||||||
}
|
|
||||||
slice := reflect.MakeSlice(dstVal.Type(), srcVal.Len(), srcVal.Len())
|
|
||||||
for i := 0; i < slice.Len(); i++ {
|
|
||||||
if err := unpack(t.Elem, slice.Index(i).Addr().Interface(), srcVal.Index(i).Interface()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dstVal.Set(slice)
|
|
||||||
case ArrayTy:
|
|
||||||
if dstVal.Kind() != reflect.Array {
|
|
||||||
return fmt.Errorf("abi: invalid dst value for unpack, want array, got %s", dstVal.Kind())
|
|
||||||
}
|
|
||||||
array := reflect.New(dstVal.Type()).Elem()
|
|
||||||
for i := 0; i < array.Len(); i++ {
|
|
||||||
if err := unpack(t.Elem, array.Index(i).Addr().Interface(), srcVal.Index(i).Interface()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dstVal.Set(array)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpackIntoMap unpacks marshalledValues into the provided map[string]interface{}
|
|
||||||
func (arguments Arguments) unpackIntoMap(v map[string]interface{}, marshalledValues []interface{}) error {
|
|
||||||
// Make sure map is not nil
|
// Make sure map is not nil
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return fmt.Errorf("abi: cannot unpack into a nil map")
|
return fmt.Errorf("abi: cannot unpack into a nil map")
|
||||||
}
|
}
|
||||||
|
if len(data) == 0 {
|
||||||
|
if len(arguments) != 0 {
|
||||||
|
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||||
|
}
|
||||||
|
return nil // Nothing to unmarshal, return
|
||||||
|
}
|
||||||
|
marshalledValues, err := arguments.UnpackValues(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for i, arg := range arguments.NonIndexed() {
|
for i, arg := range arguments.NonIndexed() {
|
||||||
v[arg.Name] = marshalledValues[i]
|
v[arg.Name] = marshalledValues[i]
|
||||||
}
|
}
|
||||||
|
|
@ -196,88 +124,63 @@ func (arguments Arguments) unpackIntoMap(v map[string]interface{}, marshalledVal
|
||||||
|
|
||||||
// unpackAtomic unpacks ( hexdata -> go ) a single value
|
// unpackAtomic unpacks ( hexdata -> go ) a single value
|
||||||
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
|
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
|
||||||
if arguments.LengthNonIndexed() == 0 {
|
dst := reflect.ValueOf(v).Elem()
|
||||||
return nil
|
src := reflect.ValueOf(marshalledValues)
|
||||||
}
|
|
||||||
argument := arguments.NonIndexed()[0]
|
|
||||||
elem := reflect.ValueOf(v).Elem()
|
|
||||||
|
|
||||||
if elem.Kind() == reflect.Struct && argument.Type.T != TupleTy {
|
if dst.Kind() == reflect.Struct && src.Kind() != reflect.Struct {
|
||||||
fieldmap, err := mapArgNamesToStructFields([]string{argument.Name}, elem)
|
return set(dst.Field(0), src)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
field := elem.FieldByName(fieldmap[argument.Name])
|
|
||||||
if !field.IsValid() {
|
|
||||||
return fmt.Errorf("abi: field %s can't be found in the given value", argument.Name)
|
|
||||||
}
|
|
||||||
return unpack(&argument.Type, field.Addr().Interface(), marshalledValues)
|
|
||||||
}
|
}
|
||||||
return unpack(&argument.Type, elem.Addr().Interface(), marshalledValues)
|
return set(dst, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unpackTuple unpacks ( hexdata -> go ) a batch of values.
|
// unpackTuple unpacks ( hexdata -> go ) a batch of values.
|
||||||
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
|
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
|
||||||
var (
|
value := reflect.ValueOf(v).Elem()
|
||||||
value = reflect.ValueOf(v).Elem()
|
nonIndexedArgs := arguments.NonIndexed()
|
||||||
typ = value.Type()
|
|
||||||
kind = value.Kind()
|
|
||||||
)
|
|
||||||
if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the interface is a struct, get of abi->struct_field mapping
|
switch value.Kind() {
|
||||||
var abi2struct map[string]string
|
case reflect.Struct:
|
||||||
if kind == reflect.Struct {
|
argNames := make([]string, len(nonIndexedArgs))
|
||||||
var (
|
for i, arg := range nonIndexedArgs {
|
||||||
argNames []string
|
argNames[i] = arg.Name
|
||||||
err error
|
|
||||||
)
|
|
||||||
for _, arg := range arguments.NonIndexed() {
|
|
||||||
argNames = append(argNames, arg.Name)
|
|
||||||
}
|
}
|
||||||
abi2struct, err = mapArgNamesToStructFields(argNames, value)
|
var err error
|
||||||
|
abi2struct, err := mapArgNamesToStructFields(argNames, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
for i, arg := range nonIndexedArgs {
|
||||||
for i, arg := range arguments.NonIndexed() {
|
|
||||||
switch kind {
|
|
||||||
case reflect.Struct:
|
|
||||||
field := value.FieldByName(abi2struct[arg.Name])
|
field := value.FieldByName(abi2struct[arg.Name])
|
||||||
if !field.IsValid() {
|
if !field.IsValid() {
|
||||||
return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
|
return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
|
||||||
}
|
}
|
||||||
if err := unpack(&arg.Type, field.Addr().Interface(), marshalledValues[i]); err != nil {
|
if err := set(field, reflect.ValueOf(marshalledValues[i])); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case reflect.Slice, reflect.Array:
|
|
||||||
if value.Len() < i {
|
|
||||||
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
|
|
||||||
}
|
|
||||||
v := value.Index(i)
|
|
||||||
if err := requireAssignable(v, reflect.ValueOf(marshalledValues[i])); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := unpack(&arg.Type, v.Addr().Interface(), marshalledValues[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", typ)
|
|
||||||
}
|
}
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
if value.Len() < len(marshalledValues) {
|
||||||
|
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
|
||||||
|
}
|
||||||
|
for i := range nonIndexedArgs {
|
||||||
|
if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", value.Type())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
|
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
|
||||||
// without supplying a struct to unpack into. Instead, this method returns a list containing the
|
// without supplying a struct to unpack into. Instead, this method returns a list containing the
|
||||||
// values. An atomic argument will be a list with one element.
|
// values. An atomic argument will be a list with one element.
|
||||||
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
||||||
retval := make([]interface{}, 0, arguments.LengthNonIndexed())
|
nonIndexedArgs := arguments.NonIndexed()
|
||||||
|
retval := make([]interface{}, 0, len(nonIndexedArgs))
|
||||||
virtualArgs := 0
|
virtualArgs := 0
|
||||||
for index, arg := range arguments.NonIndexed() {
|
for index, arg := range nonIndexedArgs {
|
||||||
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
|
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
|
||||||
if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
|
if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
|
||||||
// If we have a static array, like [3]uint256, these are coded as
|
// If we have a static array, like [3]uint256, these are coded as
|
||||||
|
|
@ -315,7 +218,7 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
||||||
// Make sure arguments match up and pack them
|
// Make sure arguments match up and pack them
|
||||||
abiArgs := arguments
|
abiArgs := arguments
|
||||||
if len(args) != len(abiArgs) {
|
if len(args) != len(abiArgs) {
|
||||||
return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(abiArgs))
|
return nil, fmt.Errorf("argument count mismatch: got %d for %d", len(args), len(abiArgs))
|
||||||
}
|
}
|
||||||
// variable input is the output appended at the end of packed
|
// variable input is the output appended at the end of packed
|
||||||
// output. This is used for strings and bytes types input.
|
// output. This is used for strings and bytes types input.
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/maticnetwork/bor"
|
ethereum "github.com/maticnetwork/bor"
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
"github.com/maticnetwork/bor/core/types"
|
"github.com/maticnetwork/bor/core/types"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,11 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/maticnetwork/bor"
|
ethereum "github.com/maticnetwork/bor"
|
||||||
|
"github.com/maticnetwork/bor/accounts/abi"
|
||||||
"github.com/maticnetwork/bor/accounts/abi/bind"
|
"github.com/maticnetwork/bor/accounts/abi/bind"
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
|
"github.com/maticnetwork/bor/common/hexutil"
|
||||||
"github.com/maticnetwork/bor/common/math"
|
"github.com/maticnetwork/bor/common/math"
|
||||||
"github.com/maticnetwork/bor/consensus/ethash"
|
"github.com/maticnetwork/bor/consensus/ethash"
|
||||||
"github.com/maticnetwork/bor/core"
|
"github.com/maticnetwork/bor/core"
|
||||||
|
|
@ -38,6 +40,7 @@ import (
|
||||||
"github.com/maticnetwork/bor/eth/filters"
|
"github.com/maticnetwork/bor/eth/filters"
|
||||||
"github.com/maticnetwork/bor/ethdb"
|
"github.com/maticnetwork/bor/ethdb"
|
||||||
"github.com/maticnetwork/bor/event"
|
"github.com/maticnetwork/bor/event"
|
||||||
|
"github.com/maticnetwork/bor/log"
|
||||||
"github.com/maticnetwork/bor/params"
|
"github.com/maticnetwork/bor/params"
|
||||||
"github.com/maticnetwork/bor/rpc"
|
"github.com/maticnetwork/bor/rpc"
|
||||||
)
|
)
|
||||||
|
|
@ -46,19 +49,23 @@ import (
|
||||||
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
|
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
|
||||||
errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
|
errBlockDoesNotExist = errors.New("block does not exist in blockchain")
|
||||||
|
errTransactionDoesNotExist = errors.New("transaction does not exist")
|
||||||
)
|
)
|
||||||
|
|
||||||
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
||||||
// the background. Its main purpose is to allow easily testing contract bindings.
|
// the background. Its main purpose is to allow easily testing contract bindings.
|
||||||
|
// Simulated backend implements the following interfaces:
|
||||||
|
// ChainReader, ChainStateReader, ContractBackend, ContractCaller, ContractFilterer, ContractTransactor,
|
||||||
|
// DeployBackend, GasEstimator, GasPricer, LogFilterer, PendingContractCaller, TransactionReader, and TransactionSender
|
||||||
type SimulatedBackend struct {
|
type SimulatedBackend struct {
|
||||||
database ethdb.Database // In memory database to store our testing data
|
database ethdb.Database // In memory database to store our testing data
|
||||||
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
|
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
pendingBlock *types.Block // Currently pending block that will be imported on request
|
pendingBlock *types.Block // Currently pending block that will be imported on request
|
||||||
pendingState *state.StateDB // Currently pending state that will be the active on on request
|
pendingState *state.StateDB // Currently pending state that will be the active on request
|
||||||
|
|
||||||
events *filters.EventSystem // Event system for filtering log events live
|
events *filters.EventSystem // Event system for filtering log events live
|
||||||
|
|
||||||
|
|
@ -70,13 +77,13 @@ type SimulatedBackend struct {
|
||||||
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
||||||
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
||||||
genesis.MustCommit(database)
|
genesis.MustCommit(database)
|
||||||
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil)
|
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||||
|
|
||||||
backend := &SimulatedBackend{
|
backend := &SimulatedBackend{
|
||||||
database: database,
|
database: database,
|
||||||
blockchain: blockchain,
|
blockchain: blockchain,
|
||||||
config: genesis.Config,
|
config: genesis.Config,
|
||||||
events: filters.NewEventSystem(new(event.TypeMux), &filterBackend{database, blockchain}, false),
|
events: filters.NewEventSystem(&filterBackend{database, blockchain}, false),
|
||||||
}
|
}
|
||||||
backend.rollback()
|
backend.rollback()
|
||||||
return backend
|
return backend
|
||||||
|
|
@ -88,6 +95,12 @@ func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBac
|
||||||
return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit)
|
return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close terminates the underlying blockchain's update loop.
|
||||||
|
func (b *SimulatedBackend) Close() error {
|
||||||
|
b.blockchain.Stop()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Commit imports all the pending transactions as a single block and starts a
|
// Commit imports all the pending transactions as a single block and starts a
|
||||||
// fresh new state.
|
// fresh new state.
|
||||||
func (b *SimulatedBackend) Commit() {
|
func (b *SimulatedBackend) Commit() {
|
||||||
|
|
@ -113,7 +126,19 @@ func (b *SimulatedBackend) rollback() {
|
||||||
statedb, _ := b.blockchain.State()
|
statedb, _ := b.blockchain.State()
|
||||||
|
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateByBlockNumber retrieves a state by a given blocknumber.
|
||||||
|
func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) {
|
||||||
|
if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) == 0 {
|
||||||
|
return b.blockchain.State()
|
||||||
|
}
|
||||||
|
block, err := b.blockByNumberNoLock(ctx, blockNumber)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b.blockchain.StateAt(block.Root())
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeAt returns the code associated with a certain account in the blockchain.
|
// CodeAt returns the code associated with a certain account in the blockchain.
|
||||||
|
|
@ -121,10 +146,11 @@ func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address,
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
|
||||||
return nil, errBlockNumberUnsupported
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
statedb, _ := b.blockchain.State()
|
|
||||||
return statedb.GetCode(contract), nil
|
return statedb.GetCode(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,10 +159,11 @@ func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Addres
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
|
||||||
return nil, errBlockNumberUnsupported
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
statedb, _ := b.blockchain.State()
|
|
||||||
return statedb.GetBalance(contract), nil
|
return statedb.GetBalance(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,10 +172,11 @@ func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address,
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
|
||||||
return 0, errBlockNumberUnsupported
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
}
|
}
|
||||||
statedb, _ := b.blockchain.State()
|
|
||||||
return statedb.GetNonce(contract), nil
|
return statedb.GetNonce(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,16 +185,20 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
|
||||||
return nil, errBlockNumberUnsupported
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
statedb, _ := b.blockchain.State()
|
|
||||||
val := statedb.GetState(contract, key)
|
val := statedb.GetState(contract, key)
|
||||||
return val[:], nil
|
return val[:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionReceipt returns the receipt of a transaction.
|
// TransactionReceipt returns the receipt of a transaction.
|
||||||
func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash, b.config)
|
receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash, b.config)
|
||||||
return receipt, nil
|
return receipt, nil
|
||||||
}
|
}
|
||||||
|
|
@ -190,6 +222,121 @@ func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.
|
||||||
return nil, false, ethereum.NotFound
|
return nil, false, ethereum.NotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlockByHash retrieves a block based on the block hash
|
||||||
|
func (b *SimulatedBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if hash == b.pendingBlock.Hash() {
|
||||||
|
return b.pendingBlock, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
block := b.blockchain.GetBlockByHash(hash)
|
||||||
|
if block != nil {
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errBlockDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockByNumber retrieves a block from the database by number, caching it
|
||||||
|
// (associated with its hash) if found.
|
||||||
|
func (b *SimulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
return b.blockByNumberNoLock(ctx, number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockByNumberNoLock retrieves a block from the database by number, caching it
|
||||||
|
// (associated with its hash) if found without Lock.
|
||||||
|
func (b *SimulatedBackend) blockByNumberNoLock(ctx context.Context, number *big.Int) (*types.Block, error) {
|
||||||
|
if number == nil || number.Cmp(b.pendingBlock.Number()) == 0 {
|
||||||
|
return b.blockchain.CurrentBlock(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
block := b.blockchain.GetBlockByNumber(uint64(number.Int64()))
|
||||||
|
if block == nil {
|
||||||
|
return nil, errBlockDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderByHash returns a block header from the current canonical chain.
|
||||||
|
func (b *SimulatedBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if hash == b.pendingBlock.Hash() {
|
||||||
|
return b.pendingBlock.Header(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
header := b.blockchain.GetHeaderByHash(hash)
|
||||||
|
if header == nil {
|
||||||
|
return nil, errBlockDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
return header, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderByNumber returns a block header from the current canonical chain. If number is
|
||||||
|
// nil, the latest known header is returned.
|
||||||
|
func (b *SimulatedBackend) HeaderByNumber(ctx context.Context, block *big.Int) (*types.Header, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if block == nil || block.Cmp(b.pendingBlock.Number()) == 0 {
|
||||||
|
return b.blockchain.CurrentHeader(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.blockchain.GetHeaderByNumber(uint64(block.Int64())), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionCount returns the number of transactions in a given block
|
||||||
|
func (b *SimulatedBackend) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if blockHash == b.pendingBlock.Hash() {
|
||||||
|
return uint(b.pendingBlock.Transactions().Len()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
block := b.blockchain.GetBlockByHash(blockHash)
|
||||||
|
if block == nil {
|
||||||
|
return uint(0), errBlockDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint(block.Transactions().Len()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionInBlock returns the transaction for a specific block at a specific index
|
||||||
|
func (b *SimulatedBackend) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if blockHash == b.pendingBlock.Hash() {
|
||||||
|
transactions := b.pendingBlock.Transactions()
|
||||||
|
if uint(len(transactions)) < index+1 {
|
||||||
|
return nil, errTransactionDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactions[index], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
block := b.blockchain.GetBlockByHash(blockHash)
|
||||||
|
if block == nil {
|
||||||
|
return nil, errBlockDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions := block.Transactions()
|
||||||
|
if uint(len(transactions)) < index+1 {
|
||||||
|
return nil, errTransactionDoesNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactions[index], nil
|
||||||
|
}
|
||||||
|
|
||||||
// PendingCodeAt returns the code associated with an account in the pending state.
|
// PendingCodeAt returns the code associated with an account in the pending state.
|
||||||
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
|
|
@ -198,6 +345,36 @@ func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Ad
|
||||||
return b.pendingState.GetCode(contract), nil
|
return b.pendingState.GetCode(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newRevertError(result *core.ExecutionResult) *revertError {
|
||||||
|
reason, errUnpack := abi.UnpackRevert(result.Revert())
|
||||||
|
err := errors.New("execution reverted")
|
||||||
|
if errUnpack == nil {
|
||||||
|
err = fmt.Errorf("execution reverted: %v", reason)
|
||||||
|
}
|
||||||
|
return &revertError{
|
||||||
|
error: err,
|
||||||
|
reason: hexutil.Encode(result.Revert()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// revertError is an API error that encompassas an EVM revertal with JSON error
|
||||||
|
// code and a binary data blob.
|
||||||
|
type revertError struct {
|
||||||
|
error
|
||||||
|
reason string // revert reason hex encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorCode returns the JSON error code for a revertal.
|
||||||
|
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal
|
||||||
|
func (e *revertError) ErrorCode() int {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorData returns the hex encoded revert reason.
|
||||||
|
func (e *revertError) ErrorData() interface{} {
|
||||||
|
return e.reason
|
||||||
|
}
|
||||||
|
|
||||||
// CallContract executes a contract call.
|
// CallContract executes a contract call.
|
||||||
func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
|
|
@ -210,8 +387,15 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
|
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
|
||||||
return rval, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// If the result contains a revert reason, try to unpack and return it.
|
||||||
|
if len(res.Revert()) > 0 {
|
||||||
|
return nil, newRevertError(res)
|
||||||
|
}
|
||||||
|
return res.Return(), res.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingCallContract executes a contract call on the pending state.
|
// PendingCallContract executes a contract call on the pending state.
|
||||||
|
|
@ -220,8 +404,15 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
||||||
|
|
||||||
rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||||
return rval, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// If the result contains a revert reason, try to unpack and return it.
|
||||||
|
if len(res.Revert()) > 0 {
|
||||||
|
return nil, newRevertError(res)
|
||||||
|
}
|
||||||
|
return res.Return(), res.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
||||||
|
|
@ -256,25 +447,57 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
} else {
|
} else {
|
||||||
hi = b.pendingBlock.GasLimit()
|
hi = b.pendingBlock.GasLimit()
|
||||||
}
|
}
|
||||||
|
// Recap the highest gas allowance with account's balance.
|
||||||
|
if call.GasPrice != nil && call.GasPrice.Uint64() != 0 {
|
||||||
|
balance := b.pendingState.GetBalance(call.From) // from can't be nil
|
||||||
|
available := new(big.Int).Set(balance)
|
||||||
|
if call.Value != nil {
|
||||||
|
if call.Value.Cmp(available) >= 0 {
|
||||||
|
return 0, errors.New("insufficient funds for transfer")
|
||||||
|
}
|
||||||
|
available.Sub(available, call.Value)
|
||||||
|
}
|
||||||
|
allowance := new(big.Int).Div(available, call.GasPrice)
|
||||||
|
if hi > allowance.Uint64() {
|
||||||
|
transfer := call.Value
|
||||||
|
if transfer == nil {
|
||||||
|
transfer = new(big.Int)
|
||||||
|
}
|
||||||
|
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
|
||||||
|
"sent", transfer, "gasprice", call.GasPrice, "fundable", allowance)
|
||||||
|
hi = allowance.Uint64()
|
||||||
|
}
|
||||||
|
}
|
||||||
cap = hi
|
cap = hi
|
||||||
|
|
||||||
// Create a helper to check if a gas allowance results in an executable transaction
|
// Create a helper to check if a gas allowance results in an executable transaction
|
||||||
executable := func(gas uint64) bool {
|
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
||||||
call.Gas = gas
|
call.Gas = gas
|
||||||
|
|
||||||
snapshot := b.pendingState.Snapshot()
|
snapshot := b.pendingState.Snapshot()
|
||||||
_, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||||
b.pendingState.RevertToSnapshot(snapshot)
|
b.pendingState.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
if err != nil || failed {
|
if err != nil {
|
||||||
return false
|
if err == core.ErrIntrinsicGas {
|
||||||
|
return true, nil, nil // Special case, raise gas limit
|
||||||
|
}
|
||||||
|
return true, nil, err // Bail out
|
||||||
}
|
}
|
||||||
return true
|
return res.Failed(), res, nil
|
||||||
}
|
}
|
||||||
// Execute the binary search and hone in on an executable gas limit
|
// Execute the binary search and hone in on an executable gas limit
|
||||||
for lo+1 < hi {
|
for lo+1 < hi {
|
||||||
mid := (hi + lo) / 2
|
mid := (hi + lo) / 2
|
||||||
if !executable(mid) {
|
failed, _, err := executable(mid)
|
||||||
|
|
||||||
|
// If the error is not nil(consensus error), it means the provided message
|
||||||
|
// call or transaction will never be accepted no matter how much gas it is
|
||||||
|
// assigned. Return the error directly, don't struggle any more
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if failed {
|
||||||
lo = mid
|
lo = mid
|
||||||
} else {
|
} else {
|
||||||
hi = mid
|
hi = mid
|
||||||
|
|
@ -282,8 +505,19 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
}
|
}
|
||||||
// Reject the transaction as invalid if it still fails at the highest allowance
|
// Reject the transaction as invalid if it still fails at the highest allowance
|
||||||
if hi == cap {
|
if hi == cap {
|
||||||
if !executable(hi) {
|
failed, result, err := executable(hi)
|
||||||
return 0, errGasEstimationFailed
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if failed {
|
||||||
|
if result != nil && result.Err != vm.ErrOutOfGas {
|
||||||
|
if len(result.Revert()) > 0 {
|
||||||
|
return 0, newRevertError(result)
|
||||||
|
}
|
||||||
|
return 0, result.Err
|
||||||
|
}
|
||||||
|
// Otherwise, the specified gas cap is too low
|
||||||
|
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hi, nil
|
return hi, nil
|
||||||
|
|
@ -291,7 +525,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
|
|
||||||
// callContract implements common code between normal and pending contract calls.
|
// callContract implements common code between normal and pending contract calls.
|
||||||
// state is modified during execution, make sure to copy it if necessary.
|
// state is modified during execution, make sure to copy it if necessary.
|
||||||
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, uint64, bool, error) {
|
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) (*core.ExecutionResult, error) {
|
||||||
// Ensure message is initialized properly.
|
// Ensure message is initialized properly.
|
||||||
if call.GasPrice == nil {
|
if call.GasPrice == nil {
|
||||||
call.GasPrice = big.NewInt(1)
|
call.GasPrice = big.NewInt(1)
|
||||||
|
|
@ -341,7 +575,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
||||||
statedb, _ := b.blockchain.State()
|
statedb, _ := b.blockchain.State()
|
||||||
|
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database(), nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,10 +647,38 @@ func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethere
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SubscribeNewHead returns an event subscription for a new header
|
||||||
|
func (b *SimulatedBackend) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
|
||||||
|
// subscribe to a new head
|
||||||
|
sink := make(chan *types.Header)
|
||||||
|
sub := b.events.SubscribeNewHeads(sink)
|
||||||
|
|
||||||
|
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case head := <-sink:
|
||||||
|
select {
|
||||||
|
case ch <- head:
|
||||||
|
case err := <-sub.Err():
|
||||||
|
return err
|
||||||
|
case <-quit:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case err := <-sub.Err():
|
||||||
|
return err
|
||||||
|
case <-quit:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
// AdjustTime adds a time shift to the simulated clock.
|
// AdjustTime adds a time shift to the simulated clock.
|
||||||
func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||||
for _, tx := range b.pendingBlock.Transactions() {
|
for _, tx := range b.pendingBlock.Transactions() {
|
||||||
block.AddTx(tx)
|
block.AddTx(tx)
|
||||||
|
|
@ -426,7 +688,7 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
||||||
statedb, _ := b.blockchain.State()
|
statedb, _ := b.blockchain.State()
|
||||||
|
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database(), nil)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -496,25 +758,38 @@ func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*ty
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||||
|
return nullSubscription()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
||||||
|
return fb.bc.SubscribeChainEvent(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
||||||
|
return fb.bc.SubscribeRemovedLogsEvent(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
||||||
|
return fb.bc.SubscribeLogsEvent(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fb *filterBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
||||||
|
return nullSubscription()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fb *filterBackend) SubscribeStateSyncEvent(ch chan<- core.StateSyncEvent) event.Subscription {
|
||||||
|
return fb.bc.SubscribeStateSyncEvent(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
|
||||||
|
|
||||||
|
func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
|
||||||
|
panic("not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func nullSubscription() event.Subscription {
|
||||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||||
<-quit
|
<-quit
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
|
||||||
return fb.bc.SubscribeChainEvent(ch)
|
|
||||||
}
|
|
||||||
func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
|
||||||
return fb.bc.SubscribeRemovedLogsEvent(ch)
|
|
||||||
}
|
|
||||||
func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
|
||||||
return fb.bc.SubscribeLogsEvent(ch)
|
|
||||||
}
|
|
||||||
func (fb *filterBackend) SubscribeStateEvent(ch chan<- core.NewStateChangeEvent) event.Subscription {
|
|
||||||
return fb.bc.SubscribeStateEvent(ch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
|
|
||||||
func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
|
|
||||||
panic("not supported")
|
|
||||||
}
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/maticnetwork/bor"
|
ethereum "github.com/maticnetwork/bor"
|
||||||
"github.com/maticnetwork/bor/accounts/abi"
|
"github.com/maticnetwork/bor/accounts/abi"
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
"github.com/maticnetwork/bor/core/types"
|
"github.com/maticnetwork/bor/core/types"
|
||||||
|
|
@ -49,7 +49,7 @@ type TransactOpts struct {
|
||||||
Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
|
Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
|
||||||
Signer SignerFn // Method to use for signing the transaction (mandatory)
|
Signer SignerFn // Method to use for signing the transaction (mandatory)
|
||||||
|
|
||||||
Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds)
|
Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds)
|
||||||
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
|
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
|
||||||
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
|
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
|
||||||
|
|
||||||
|
|
@ -171,12 +171,24 @@ func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...in
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// todo(rjl493456442) check the method is payable or not,
|
||||||
|
// reject invalid transaction at the first place
|
||||||
return c.transact(opts, &c.address, input)
|
return c.transact(opts, &c.address, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RawTransact initiates a transaction with the given raw calldata as the input.
|
||||||
|
// It's usually used to initiates transaction for invoking **Fallback** function.
|
||||||
|
func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) {
|
||||||
|
// todo(rjl493456442) check the method is payable or not,
|
||||||
|
// reject invalid transaction at the first place
|
||||||
|
return c.transact(opts, &c.address, calldata)
|
||||||
|
}
|
||||||
|
|
||||||
// Transfer initiates a plain transaction to move funds to the contract, calling
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
// its default method if one is available.
|
// its default method if one is available.
|
||||||
func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) {
|
func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) {
|
||||||
|
// todo(rjl493456442) check the payable fallback or receive is defined
|
||||||
|
// or not, reject invalid transaction at the first place
|
||||||
return c.transact(opts, &c.address, nil)
|
return c.transact(opts, &c.address, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -218,7 +230,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the contract surely has code (or code is not needed), estimate the transaction
|
// If the contract surely has code (or code is not needed), estimate the transaction
|
||||||
msg := ethereum.CallMsg{From: opts.From, To: contract, Value: value, Data: input}
|
msg := ethereum.CallMsg{From: opts.From, To: contract, GasPrice: gasPrice, Value: value, Data: input}
|
||||||
gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
|
gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
|
return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
|
||||||
|
|
@ -252,9 +264,9 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
|
||||||
opts = new(FilterOpts)
|
opts = new(FilterOpts)
|
||||||
}
|
}
|
||||||
// Append the event selector to the query parameters and construct the topic set
|
// Append the event selector to the query parameters and construct the topic set
|
||||||
query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...)
|
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
|
||||||
|
|
||||||
topics, err := makeTopics(query...)
|
topics, err := abi.MakeTopics(query...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -301,9 +313,9 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter
|
||||||
opts = new(WatchOpts)
|
opts = new(WatchOpts)
|
||||||
}
|
}
|
||||||
// Append the event selector to the query parameters and construct the topic set
|
// Append the event selector to the query parameters and construct the topic set
|
||||||
query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...)
|
query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
|
||||||
|
|
||||||
topics, err := makeTopics(query...)
|
topics, err := abi.MakeTopics(query...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -337,7 +349,7 @@ func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log)
|
||||||
indexed = append(indexed, arg)
|
indexed = append(indexed, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return parseTopics(out, indexed, log.Topics[1:])
|
return abi.ParseTopics(out, indexed, log.Topics[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnpackLogIntoMap unpacks a retrieved log into the provided map.
|
// UnpackLogIntoMap unpacks a retrieved log into the provided map.
|
||||||
|
|
@ -353,7 +365,7 @@ func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event strin
|
||||||
indexed = append(indexed, arg)
|
indexed = append(indexed, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return parseTopicsIntoMap(out, indexed, log.Topics[1:])
|
return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensureContext is a helper method to ensure a context is not nil, even if the
|
// ensureContext is a helper method to ensure a context is not nil, even if the
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,13 @@
|
||||||
package bind_test
|
package bind_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/maticnetwork/bor"
|
ethereum "github.com/maticnetwork/bor"
|
||||||
"github.com/maticnetwork/bor/accounts/abi"
|
"github.com/maticnetwork/bor/accounts/abi"
|
||||||
"github.com/maticnetwork/bor/accounts/abi/bind"
|
"github.com/maticnetwork/bor/accounts/abi/bind"
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
|
|
@ -34,8 +34,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockCaller struct {
|
type mockCaller struct {
|
||||||
codeAtBlockNumber *big.Int
|
codeAtBlockNumber *big.Int
|
||||||
callContractBlockNumber *big.Int
|
callContractBlockNumber *big.Int
|
||||||
|
pendingCodeAtCalled bool
|
||||||
|
pendingCallContractCalled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||||
|
|
@ -47,6 +49,16 @@ func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, b
|
||||||
mc.callContractBlockNumber = blockNumber
|
mc.callContractBlockNumber = blockNumber
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mc *mockCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||||
|
mc.pendingCodeAtCalled = true
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mockCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
||||||
|
mc.pendingCallContractCalled = true
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
func TestPassingBlockNumber(t *testing.T) {
|
func TestPassingBlockNumber(t *testing.T) {
|
||||||
|
|
||||||
mc := &mockCaller{}
|
mc := &mockCaller{}
|
||||||
|
|
@ -82,57 +94,39 @@ func TestPassingBlockNumber(t *testing.T) {
|
||||||
if mc.codeAtBlockNumber != nil {
|
if mc.codeAtBlockNumber != nil {
|
||||||
t.Fatalf("CodeAt() was passed a block number when it should not have been")
|
t.Fatalf("CodeAt() was passed a block number when it should not have been")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, &ret, "something")
|
||||||
|
|
||||||
|
if !mc.pendingCallContractCalled {
|
||||||
|
t.Fatalf("CallContract() was not passed the block number")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mc.pendingCodeAtCalled {
|
||||||
|
t.Fatalf("CodeAt() was not passed the block number")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158"
|
const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158"
|
||||||
|
|
||||||
func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
|
func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
|
||||||
hash := crypto.Keccak256Hash([]byte("testName"))
|
hash := crypto.Keccak256Hash([]byte("testName"))
|
||||||
mockLog := types.Log{
|
topics := []common.Hash{
|
||||||
Address: common.HexToAddress("0x0"),
|
common.HexToHash("0x0"),
|
||||||
Topics: []common.Hash{
|
hash,
|
||||||
common.HexToHash("0x0"),
|
|
||||||
hash,
|
|
||||||
},
|
|
||||||
Data: hexutil.MustDecode(hexData),
|
|
||||||
BlockNumber: uint64(26),
|
|
||||||
TxHash: common.HexToHash("0x0"),
|
|
||||||
TxIndex: 111,
|
|
||||||
BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
|
||||||
Index: 7,
|
|
||||||
Removed: false,
|
|
||||||
}
|
}
|
||||||
|
mockLog := newMockLog(topics, common.HexToHash("0x0"))
|
||||||
|
|
||||||
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
||||||
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
||||||
|
|
||||||
receivedMap := make(map[string]interface{})
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
expectedReceivedMap := map[string]interface{}{
|
||||||
"name": hash,
|
"name": hash,
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
||||||
"amount": big.NewInt(1),
|
"amount": big.NewInt(1),
|
||||||
"memo": []byte{88},
|
"memo": []byte{88},
|
||||||
}
|
}
|
||||||
if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
|
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(receivedMap) != 4 {
|
|
||||||
t.Fatal("unpacked map expected to have length 4")
|
|
||||||
}
|
|
||||||
if receivedMap["name"] != expectedReceivedMap["name"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
|
func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
|
||||||
|
|
@ -141,51 +135,23 @@ func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
hash := crypto.Keccak256Hash(sliceBytes)
|
hash := crypto.Keccak256Hash(sliceBytes)
|
||||||
mockLog := types.Log{
|
topics := []common.Hash{
|
||||||
Address: common.HexToAddress("0x0"),
|
common.HexToHash("0x0"),
|
||||||
Topics: []common.Hash{
|
hash,
|
||||||
common.HexToHash("0x0"),
|
|
||||||
hash,
|
|
||||||
},
|
|
||||||
Data: hexutil.MustDecode(hexData),
|
|
||||||
BlockNumber: uint64(26),
|
|
||||||
TxHash: common.HexToHash("0x0"),
|
|
||||||
TxIndex: 111,
|
|
||||||
BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
|
||||||
Index: 7,
|
|
||||||
Removed: false,
|
|
||||||
}
|
}
|
||||||
|
mockLog := newMockLog(topics, common.HexToHash("0x0"))
|
||||||
|
|
||||||
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"names","type":"string[]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"names","type":"string[]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
||||||
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
||||||
|
|
||||||
receivedMap := make(map[string]interface{})
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
expectedReceivedMap := map[string]interface{}{
|
||||||
"names": hash,
|
"names": hash,
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
||||||
"amount": big.NewInt(1),
|
"amount": big.NewInt(1),
|
||||||
"memo": []byte{88},
|
"memo": []byte{88},
|
||||||
}
|
}
|
||||||
if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
|
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(receivedMap) != 4 {
|
|
||||||
t.Fatal("unpacked map expected to have length 4")
|
|
||||||
}
|
|
||||||
if receivedMap["names"] != expectedReceivedMap["names"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
|
func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
|
||||||
|
|
@ -194,51 +160,23 @@ func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
hash := crypto.Keccak256Hash(arrBytes)
|
hash := crypto.Keccak256Hash(arrBytes)
|
||||||
mockLog := types.Log{
|
topics := []common.Hash{
|
||||||
Address: common.HexToAddress("0x0"),
|
common.HexToHash("0x0"),
|
||||||
Topics: []common.Hash{
|
hash,
|
||||||
common.HexToHash("0x0"),
|
|
||||||
hash,
|
|
||||||
},
|
|
||||||
Data: hexutil.MustDecode(hexData),
|
|
||||||
BlockNumber: uint64(26),
|
|
||||||
TxHash: common.HexToHash("0x0"),
|
|
||||||
TxIndex: 111,
|
|
||||||
BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
|
||||||
Index: 7,
|
|
||||||
Removed: false,
|
|
||||||
}
|
}
|
||||||
|
mockLog := newMockLog(topics, common.HexToHash("0x0"))
|
||||||
|
|
||||||
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"addresses","type":"address[2]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"addresses","type":"address[2]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
||||||
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
||||||
|
|
||||||
receivedMap := make(map[string]interface{})
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
expectedReceivedMap := map[string]interface{}{
|
||||||
"addresses": hash,
|
"addresses": hash,
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
||||||
"amount": big.NewInt(1),
|
"amount": big.NewInt(1),
|
||||||
"memo": []byte{88},
|
"memo": []byte{88},
|
||||||
}
|
}
|
||||||
if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
|
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(receivedMap) != 4 {
|
|
||||||
t.Fatal("unpacked map expected to have length 4")
|
|
||||||
}
|
|
||||||
if receivedMap["addresses"] != expectedReceivedMap["addresses"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
|
func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
|
||||||
|
|
@ -249,99 +187,72 @@ func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
|
||||||
functionTyBytes := append(addrBytes, functionSelector...)
|
functionTyBytes := append(addrBytes, functionSelector...)
|
||||||
var functionTy [24]byte
|
var functionTy [24]byte
|
||||||
copy(functionTy[:], functionTyBytes[0:24])
|
copy(functionTy[:], functionTyBytes[0:24])
|
||||||
mockLog := types.Log{
|
topics := []common.Hash{
|
||||||
Address: common.HexToAddress("0x0"),
|
common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
|
||||||
Topics: []common.Hash{
|
common.BytesToHash(functionTyBytes),
|
||||||
common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
|
|
||||||
common.BytesToHash(functionTyBytes),
|
|
||||||
},
|
|
||||||
Data: hexutil.MustDecode(hexData),
|
|
||||||
BlockNumber: uint64(26),
|
|
||||||
TxHash: common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"),
|
|
||||||
TxIndex: 111,
|
|
||||||
BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
|
||||||
Index: 7,
|
|
||||||
Removed: false,
|
|
||||||
}
|
}
|
||||||
|
mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
|
||||||
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"function","type":"function"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"function","type":"function"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
||||||
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
||||||
|
|
||||||
receivedMap := make(map[string]interface{})
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
expectedReceivedMap := map[string]interface{}{
|
||||||
"function": functionTy,
|
"function": functionTy,
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
||||||
"amount": big.NewInt(1),
|
"amount": big.NewInt(1),
|
||||||
"memo": []byte{88},
|
"memo": []byte{88},
|
||||||
}
|
}
|
||||||
if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
|
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(receivedMap) != 4 {
|
|
||||||
t.Fatal("unpacked map expected to have length 4")
|
|
||||||
}
|
|
||||||
if receivedMap["function"] != expectedReceivedMap["function"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
|
func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
|
||||||
byts := []byte{1, 2, 3, 4, 5}
|
bytes := []byte{1, 2, 3, 4, 5}
|
||||||
hash := crypto.Keccak256Hash(byts)
|
hash := crypto.Keccak256Hash(bytes)
|
||||||
mockLog := types.Log{
|
topics := []common.Hash{
|
||||||
Address: common.HexToAddress("0x0"),
|
common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
|
||||||
Topics: []common.Hash{
|
hash,
|
||||||
common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
|
|
||||||
hash,
|
|
||||||
},
|
|
||||||
Data: hexutil.MustDecode(hexData),
|
|
||||||
BlockNumber: uint64(26),
|
|
||||||
TxHash: common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"),
|
|
||||||
TxIndex: 111,
|
|
||||||
BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
|
||||||
Index: 7,
|
|
||||||
Removed: false,
|
|
||||||
}
|
}
|
||||||
|
mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
|
||||||
|
|
||||||
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"content","type":"bytes"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"content","type":"bytes"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
|
||||||
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
||||||
|
|
||||||
receivedMap := make(map[string]interface{})
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
expectedReceivedMap := map[string]interface{}{
|
||||||
"content": hash,
|
"content": hash,
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
||||||
"amount": big.NewInt(1),
|
"amount": big.NewInt(1),
|
||||||
"memo": []byte{88},
|
"memo": []byte{88},
|
||||||
}
|
}
|
||||||
if err := bc.UnpackLogIntoMap(receivedMap, "received", mockLog); err != nil {
|
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) {
|
||||||
|
received := make(map[string]interface{})
|
||||||
|
if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(receivedMap) != 4 {
|
if len(received) != len(expected) {
|
||||||
t.Fatal("unpacked map expected to have length 4")
|
t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected))
|
||||||
}
|
}
|
||||||
if receivedMap["content"] != expectedReceivedMap["content"] {
|
for name, elem := range expected {
|
||||||
t.Error("unpacked map does not match expected map")
|
if !reflect.DeepEqual(elem, received[name]) {
|
||||||
}
|
t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name])
|
||||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
}
|
||||||
t.Error("unpacked map does not match expected map")
|
}
|
||||||
}
|
}
|
||||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
|
||||||
t.Error("unpacked map does not match expected map")
|
func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
|
||||||
}
|
return types.Log{
|
||||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
Address: common.HexToAddress("0x0"),
|
||||||
t.Error("unpacked map does not match expected map")
|
Topics: topics,
|
||||||
|
Data: hexutil.MustDecode(hexData),
|
||||||
|
BlockNumber: uint64(26),
|
||||||
|
TxHash: txHash,
|
||||||
|
TxIndex: 111,
|
||||||
|
BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
||||||
|
Index: 7,
|
||||||
|
Removed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
// Package bind generates Ethereum contract Go bindings.
|
// Package bind generates Ethereum contract Go bindings.
|
||||||
//
|
//
|
||||||
// Detailed usage document and tutorial available on the go-ethereum Wiki page:
|
// Detailed usage document and tutorial available on the go-ethereum Wiki page:
|
||||||
// https://github.com/maticnetwork/bor/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts
|
// https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts
|
||||||
package bind
|
package bind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -47,13 +47,17 @@ const (
|
||||||
// to be used as is in client code, but rather as an intermediate struct which
|
// to be used as is in client code, but rather as an intermediate struct which
|
||||||
// enforces compile time type safety and naming convention opposed to having to
|
// enforces compile time type safety and naming convention opposed to having to
|
||||||
// manually maintain hard coded strings that break on runtime.
|
// manually maintain hard coded strings that break on runtime.
|
||||||
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string) (string, error) {
|
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
|
||||||
// Process each individual contract requested binding
|
var (
|
||||||
contracts := make(map[string]*tmplContract)
|
// contracts is the map of each individual contract requested binding
|
||||||
|
contracts = make(map[string]*tmplContract)
|
||||||
|
|
||||||
// Map used to flag each encountered library as such
|
// structs is the map of all reclared structs shared by passed contracts.
|
||||||
isLib := make(map[string]struct{})
|
structs = make(map[string]*tmplStruct)
|
||||||
|
|
||||||
|
// isLib is the map used to flag each encountered library as such
|
||||||
|
isLib = make(map[string]struct{})
|
||||||
|
)
|
||||||
for i := 0; i < len(types); i++ {
|
for i := 0; i < len(types); i++ {
|
||||||
// Parse the actual ABI to generate the binding for
|
// Parse the actual ABI to generate the binding for
|
||||||
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
|
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
|
||||||
|
|
@ -73,20 +77,38 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
calls = make(map[string]*tmplMethod)
|
calls = make(map[string]*tmplMethod)
|
||||||
transacts = make(map[string]*tmplMethod)
|
transacts = make(map[string]*tmplMethod)
|
||||||
events = make(map[string]*tmplEvent)
|
events = make(map[string]*tmplEvent)
|
||||||
structs = make(map[string]*tmplStruct)
|
fallback *tmplMethod
|
||||||
|
receive *tmplMethod
|
||||||
|
|
||||||
|
// identifiers are used to detect duplicated identifier of function
|
||||||
|
// and event. For all calls, transacts and events, abigen will generate
|
||||||
|
// corresponding bindings. However we have to ensure there is no
|
||||||
|
// identifier coliision in the bindings of these categories.
|
||||||
|
callIdentifiers = make(map[string]bool)
|
||||||
|
transactIdentifiers = make(map[string]bool)
|
||||||
|
eventIdentifiers = make(map[string]bool)
|
||||||
)
|
)
|
||||||
for _, original := range evmABI.Methods {
|
for _, original := range evmABI.Methods {
|
||||||
// Normalize the method for capital cases and non-anonymous inputs/outputs
|
// Normalize the method for capital cases and non-anonymous inputs/outputs
|
||||||
normalized := original
|
normalized := original
|
||||||
normalized.Name = methodNormalizer[lang](original.Name)
|
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
||||||
|
// Ensure there is no duplicated identifier
|
||||||
|
var identifiers = callIdentifiers
|
||||||
|
if !original.IsConstant() {
|
||||||
|
identifiers = transactIdentifiers
|
||||||
|
}
|
||||||
|
if identifiers[normalizedName] {
|
||||||
|
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
|
||||||
|
}
|
||||||
|
identifiers[normalizedName] = true
|
||||||
|
normalized.Name = normalizedName
|
||||||
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
|
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
|
||||||
copy(normalized.Inputs, original.Inputs)
|
copy(normalized.Inputs, original.Inputs)
|
||||||
for j, input := range normalized.Inputs {
|
for j, input := range normalized.Inputs {
|
||||||
if input.Name == "" {
|
if input.Name == "" {
|
||||||
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
||||||
}
|
}
|
||||||
if _, exist := structs[input.Type.String()]; input.Type.T == abi.TupleTy && !exist {
|
if hasStruct(input.Type) {
|
||||||
bindStructType[lang](input.Type, structs)
|
bindStructType[lang](input.Type, structs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -96,12 +118,12 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
if output.Name != "" {
|
if output.Name != "" {
|
||||||
normalized.Outputs[j].Name = capitalise(output.Name)
|
normalized.Outputs[j].Name = capitalise(output.Name)
|
||||||
}
|
}
|
||||||
if _, exist := structs[output.Type.String()]; output.Type.T == abi.TupleTy && !exist {
|
if hasStruct(output.Type) {
|
||||||
bindStructType[lang](output.Type, structs)
|
bindStructType[lang](output.Type, structs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Append the methods to the call or transact lists
|
// Append the methods to the call or transact lists
|
||||||
if original.Const {
|
if original.IsConstant() {
|
||||||
calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
||||||
} else {
|
} else {
|
||||||
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
||||||
|
|
@ -114,25 +136,35 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
}
|
}
|
||||||
// Normalize the event for capital cases and non-anonymous outputs
|
// Normalize the event for capital cases and non-anonymous outputs
|
||||||
normalized := original
|
normalized := original
|
||||||
normalized.Name = methodNormalizer[lang](original.Name)
|
|
||||||
|
// Ensure there is no duplicated identifier
|
||||||
|
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
||||||
|
if eventIdentifiers[normalizedName] {
|
||||||
|
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
|
||||||
|
}
|
||||||
|
eventIdentifiers[normalizedName] = true
|
||||||
|
normalized.Name = normalizedName
|
||||||
|
|
||||||
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
|
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
|
||||||
copy(normalized.Inputs, original.Inputs)
|
copy(normalized.Inputs, original.Inputs)
|
||||||
for j, input := range normalized.Inputs {
|
for j, input := range normalized.Inputs {
|
||||||
// Indexed fields are input, non-indexed ones are outputs
|
if input.Name == "" {
|
||||||
if input.Indexed {
|
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
||||||
if input.Name == "" {
|
}
|
||||||
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
if hasStruct(input.Type) {
|
||||||
}
|
bindStructType[lang](input.Type, structs)
|
||||||
if _, exist := structs[input.Type.String()]; input.Type.T == abi.TupleTy && !exist {
|
|
||||||
bindStructType[lang](input.Type, structs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Append the event to the accumulator list
|
// Append the event to the accumulator list
|
||||||
events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
|
events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
|
||||||
}
|
}
|
||||||
|
// Add two special fallback functions if they exist
|
||||||
|
if evmABI.HasFallback() {
|
||||||
|
fallback = &tmplMethod{Original: evmABI.Fallback}
|
||||||
|
}
|
||||||
|
if evmABI.HasReceive() {
|
||||||
|
receive = &tmplMethod{Original: evmABI.Receive}
|
||||||
|
}
|
||||||
// There is no easy way to pass arbitrary java objects to the Go side.
|
// There is no easy way to pass arbitrary java objects to the Go side.
|
||||||
if len(structs) > 0 && lang == LangJava {
|
if len(structs) > 0 && lang == LangJava {
|
||||||
return "", errors.New("java binding for tuple arguments is not supported yet")
|
return "", errors.New("java binding for tuple arguments is not supported yet")
|
||||||
|
|
@ -145,9 +177,10 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
Constructor: evmABI.Constructor,
|
Constructor: evmABI.Constructor,
|
||||||
Calls: calls,
|
Calls: calls,
|
||||||
Transacts: transacts,
|
Transacts: transacts,
|
||||||
|
Fallback: fallback,
|
||||||
|
Receive: receive,
|
||||||
Events: events,
|
Events: events,
|
||||||
Libraries: make(map[string]string),
|
Libraries: make(map[string]string),
|
||||||
Structs: structs,
|
|
||||||
}
|
}
|
||||||
// Function 4-byte signatures are stored in the same sequence
|
// Function 4-byte signatures are stored in the same sequence
|
||||||
// as types, if available.
|
// as types, if available.
|
||||||
|
|
@ -179,6 +212,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
Package: pkg,
|
Package: pkg,
|
||||||
Contracts: contracts,
|
Contracts: contracts,
|
||||||
Libraries: libs,
|
Libraries: libs,
|
||||||
|
Structs: structs,
|
||||||
}
|
}
|
||||||
buffer := new(bytes.Buffer)
|
buffer := new(bytes.Buffer)
|
||||||
|
|
||||||
|
|
@ -186,8 +220,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
"bindtype": bindType[lang],
|
"bindtype": bindType[lang],
|
||||||
"bindtopictype": bindTopicType[lang],
|
"bindtopictype": bindTopicType[lang],
|
||||||
"namedtype": namedType[lang],
|
"namedtype": namedType[lang],
|
||||||
"formatmethod": formatMethod,
|
|
||||||
"formatevent": formatEvent,
|
|
||||||
"capitalise": capitalise,
|
"capitalise": capitalise,
|
||||||
"decapitalise": decapitalise,
|
"decapitalise": decapitalise,
|
||||||
}
|
}
|
||||||
|
|
@ -244,7 +276,7 @@ func bindBasicTypeGo(kind abi.Type) string {
|
||||||
func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
switch kind.T {
|
switch kind.T {
|
||||||
case abi.TupleTy:
|
case abi.TupleTy:
|
||||||
return structs[kind.String()].Name
|
return structs[kind.TupleRawName+kind.String()].Name
|
||||||
case abi.ArrayTy:
|
case abi.ArrayTy:
|
||||||
return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs)
|
return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs)
|
||||||
case abi.SliceTy:
|
case abi.SliceTy:
|
||||||
|
|
@ -321,7 +353,7 @@ func pluralizeJavaType(typ string) string {
|
||||||
func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
|
func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
switch kind.T {
|
switch kind.T {
|
||||||
case abi.TupleTy:
|
case abi.TupleTy:
|
||||||
return structs[kind.String()].Name
|
return structs[kind.TupleRawName+kind.String()].Name
|
||||||
case abi.ArrayTy, abi.SliceTy:
|
case abi.ArrayTy, abi.SliceTy:
|
||||||
return pluralizeJavaType(bindTypeJava(*kind.Elem, structs))
|
return pluralizeJavaType(bindTypeJava(*kind.Elem, structs))
|
||||||
default:
|
default:
|
||||||
|
|
@ -340,6 +372,13 @@ var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct)
|
||||||
// funcionality as for simple types, but dynamic types get converted to hashes.
|
// funcionality as for simple types, but dynamic types get converted to hashes.
|
||||||
func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
bound := bindTypeGo(kind, structs)
|
bound := bindTypeGo(kind, structs)
|
||||||
|
|
||||||
|
// todo(rjl493456442) according solidity documentation, indexed event
|
||||||
|
// parameters that are not value types i.e. arrays and structs are not
|
||||||
|
// stored directly but instead a keccak256-hash of an encoding is stored.
|
||||||
|
//
|
||||||
|
// We only convert stringS and bytes to hash, still need to deal with
|
||||||
|
// array(both fixed-size and dynamic-size) and struct.
|
||||||
if bound == "string" || bound == "[]byte" {
|
if bound == "string" || bound == "[]byte" {
|
||||||
bound = "common.Hash"
|
bound = "common.Hash"
|
||||||
}
|
}
|
||||||
|
|
@ -350,6 +389,13 @@ func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
// funcionality as for simple types, but dynamic types get converted to hashes.
|
// funcionality as for simple types, but dynamic types get converted to hashes.
|
||||||
func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
|
func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
bound := bindTypeJava(kind, structs)
|
bound := bindTypeJava(kind, structs)
|
||||||
|
|
||||||
|
// todo(rjl493456442) according solidity documentation, indexed event
|
||||||
|
// parameters that are not value types i.e. arrays and structs are not
|
||||||
|
// stored directly but instead a keccak256-hash of an encoding is stored.
|
||||||
|
//
|
||||||
|
// We only convert stringS and bytes to hash, still need to deal with
|
||||||
|
// array(both fixed-size and dynamic-size) and struct.
|
||||||
if bound == "String" || bound == "byte[]" {
|
if bound == "String" || bound == "byte[]" {
|
||||||
bound = "Hash"
|
bound = "Hash"
|
||||||
}
|
}
|
||||||
|
|
@ -369,7 +415,14 @@ var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct
|
||||||
func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
switch kind.T {
|
switch kind.T {
|
||||||
case abi.TupleTy:
|
case abi.TupleTy:
|
||||||
if s, exist := structs[kind.String()]; exist {
|
// We compose raw struct name and canonical parameter expression
|
||||||
|
// together here. The reason is before solidity v0.5.11, kind.TupleRawName
|
||||||
|
// is empty, so we use canonical parameter expression to distinguish
|
||||||
|
// different struct definition. From the consideration of backward
|
||||||
|
// compatibility, we concat these two together so that if kind.TupleRawName
|
||||||
|
// is not empty, it can have unique id.
|
||||||
|
id := kind.TupleRawName + kind.String()
|
||||||
|
if s, exist := structs[id]; exist {
|
||||||
return s.Name
|
return s.Name
|
||||||
}
|
}
|
||||||
var fields []*tmplField
|
var fields []*tmplField
|
||||||
|
|
@ -377,8 +430,11 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
field := bindStructTypeGo(*elem, structs)
|
field := bindStructTypeGo(*elem, structs)
|
||||||
fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem})
|
fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem})
|
||||||
}
|
}
|
||||||
name := fmt.Sprintf("Struct%d", len(structs))
|
name := kind.TupleRawName
|
||||||
structs[kind.String()] = &tmplStruct{
|
if name == "" {
|
||||||
|
name = fmt.Sprintf("Struct%d", len(structs))
|
||||||
|
}
|
||||||
|
structs[id] = &tmplStruct{
|
||||||
Name: name,
|
Name: name,
|
||||||
Fields: fields,
|
Fields: fields,
|
||||||
}
|
}
|
||||||
|
|
@ -398,7 +454,14 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
|
func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
switch kind.T {
|
switch kind.T {
|
||||||
case abi.TupleTy:
|
case abi.TupleTy:
|
||||||
if s, exist := structs[kind.String()]; exist {
|
// We compose raw struct name and canonical parameter expression
|
||||||
|
// together here. The reason is before solidity v0.5.11, kind.TupleRawName
|
||||||
|
// is empty, so we use canonical parameter expression to distinguish
|
||||||
|
// different struct definition. From the consideration of backward
|
||||||
|
// compatibility, we concat these two together so that if kind.TupleRawName
|
||||||
|
// is not empty, it can have unique id.
|
||||||
|
id := kind.TupleRawName + kind.String()
|
||||||
|
if s, exist := structs[id]; exist {
|
||||||
return s.Name
|
return s.Name
|
||||||
}
|
}
|
||||||
var fields []*tmplField
|
var fields []*tmplField
|
||||||
|
|
@ -406,8 +469,11 @@ func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
field := bindStructTypeJava(*elem, structs)
|
field := bindStructTypeJava(*elem, structs)
|
||||||
fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem})
|
fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem})
|
||||||
}
|
}
|
||||||
name := fmt.Sprintf("Class%d", len(structs))
|
name := kind.TupleRawName
|
||||||
structs[kind.String()] = &tmplStruct{
|
if name == "" {
|
||||||
|
name = fmt.Sprintf("Class%d", len(structs))
|
||||||
|
}
|
||||||
|
structs[id] = &tmplStruct{
|
||||||
Name: name,
|
Name: name,
|
||||||
Fields: fields,
|
Fields: fields,
|
||||||
}
|
}
|
||||||
|
|
@ -452,6 +518,15 @@ func namedTypeJava(javaKind string, solKind abi.Type) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// alias returns an alias of the given string based on the aliasing rules
|
||||||
|
// or returns itself if no rule is matched.
|
||||||
|
func alias(aliases map[string]string, n string) string {
|
||||||
|
if alias, exist := aliases[n]; exist {
|
||||||
|
return alias
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
// methodNormalizer is a name transformer that modifies Solidity method names to
|
// methodNormalizer is a name transformer that modifies Solidity method names to
|
||||||
// conform to target language naming concentions.
|
// conform to target language naming concentions.
|
||||||
var methodNormalizer = map[Lang]func(string) string{
|
var methodNormalizer = map[Lang]func(string) string{
|
||||||
|
|
@ -460,9 +535,7 @@ var methodNormalizer = map[Lang]func(string) string{
|
||||||
}
|
}
|
||||||
|
|
||||||
// capitalise makes a camel-case string which starts with an upper case character.
|
// capitalise makes a camel-case string which starts with an upper case character.
|
||||||
func capitalise(input string) string {
|
var capitalise = abi.ToCamelCase
|
||||||
return abi.ToCamelCase(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
// decapitalise makes a camel-case string which starts with a lower case character.
|
// decapitalise makes a camel-case string which starts with a lower case character.
|
||||||
func decapitalise(input string) string {
|
func decapitalise(input string) string {
|
||||||
|
|
@ -497,62 +570,17 @@ func structured(args abi.Arguments) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveArgName converts a raw argument representation into a user friendly format.
|
// hasStruct returns an indicator whether the given type is struct, struct slice
|
||||||
func resolveArgName(arg abi.Argument, structs map[string]*tmplStruct) string {
|
// or struct array.
|
||||||
var (
|
func hasStruct(t abi.Type) bool {
|
||||||
prefix string
|
switch t.T {
|
||||||
embedded string
|
case abi.SliceTy:
|
||||||
typ = &arg.Type
|
return hasStruct(*t.Elem)
|
||||||
)
|
case abi.ArrayTy:
|
||||||
loop:
|
return hasStruct(*t.Elem)
|
||||||
for {
|
case abi.TupleTy:
|
||||||
switch typ.T {
|
return true
|
||||||
case abi.SliceTy:
|
default:
|
||||||
prefix += "[]"
|
return false
|
||||||
case abi.ArrayTy:
|
|
||||||
prefix += fmt.Sprintf("[%d]", typ.Size)
|
|
||||||
default:
|
|
||||||
embedded = typ.String()
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
typ = typ.Elem
|
|
||||||
}
|
|
||||||
if s, exist := structs[embedded]; exist {
|
|
||||||
return prefix + s.Name
|
|
||||||
} else {
|
|
||||||
return arg.Type.String()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatMethod transforms raw method representation into a user friendly one.
|
|
||||||
func formatMethod(method abi.Method, structs map[string]*tmplStruct) string {
|
|
||||||
inputs := make([]string, len(method.Inputs))
|
|
||||||
for i, input := range method.Inputs {
|
|
||||||
inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name)
|
|
||||||
}
|
|
||||||
outputs := make([]string, len(method.Outputs))
|
|
||||||
for i, output := range method.Outputs {
|
|
||||||
outputs[i] = resolveArgName(output, structs)
|
|
||||||
if len(output.Name) > 0 {
|
|
||||||
outputs[i] += fmt.Sprintf(" %v", output.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
constant := ""
|
|
||||||
if method.Const {
|
|
||||||
constant = "constant "
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.Name, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
// formatEvent transforms raw event representation into a user friendly one.
|
|
||||||
func formatEvent(event abi.Event, structs map[string]*tmplStruct) string {
|
|
||||||
inputs := make([]string, len(event.Inputs))
|
|
||||||
for i, input := range event.Inputs {
|
|
||||||
if input.Indexed {
|
|
||||||
inputs[i] = fmt.Sprintf("%v indexed %v", resolveArgName(input, structs), input.Name)
|
|
||||||
} else {
|
|
||||||
inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("event %v(%v)", event.Name, strings.Join(inputs, ", "))
|
|
||||||
}
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -23,6 +23,7 @@ type tmplData struct {
|
||||||
Package string // Name of the package to place the generated file in
|
Package string // Name of the package to place the generated file in
|
||||||
Contracts map[string]*tmplContract // List of contracts to generate into this file
|
Contracts map[string]*tmplContract // List of contracts to generate into this file
|
||||||
Libraries map[string]string // Map the bytecode's link pattern to the library name
|
Libraries map[string]string // Map the bytecode's link pattern to the library name
|
||||||
|
Structs map[string]*tmplStruct // Contract struct type definitions
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmplContract contains the data needed to generate an individual contract binding.
|
// tmplContract contains the data needed to generate an individual contract binding.
|
||||||
|
|
@ -34,10 +35,11 @@ type tmplContract struct {
|
||||||
Constructor abi.Method // Contract constructor for deploy parametrization
|
Constructor abi.Method // Contract constructor for deploy parametrization
|
||||||
Calls map[string]*tmplMethod // Contract calls that only read state data
|
Calls map[string]*tmplMethod // Contract calls that only read state data
|
||||||
Transacts map[string]*tmplMethod // Contract calls that write state data
|
Transacts map[string]*tmplMethod // Contract calls that write state data
|
||||||
|
Fallback *tmplMethod // Additional special fallback function
|
||||||
|
Receive *tmplMethod // Additional special receive function
|
||||||
Events map[string]*tmplEvent // Contract events accessors
|
Events map[string]*tmplEvent // Contract events accessors
|
||||||
Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
|
Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
|
||||||
Structs map[string]*tmplStruct // Contract struct type definitions
|
Library bool // Indicator whether the contract is a library
|
||||||
Library bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
|
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
|
||||||
|
|
@ -62,10 +64,10 @@ type tmplField struct {
|
||||||
SolKind abi.Type // Raw abi type information
|
SolKind abi.Type // Raw abi type information
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmplStruct is a wrapper around an abi.tuple contains a auto-generated
|
// tmplStruct is a wrapper around an abi.tuple contains an auto-generated
|
||||||
// struct name.
|
// struct name.
|
||||||
type tmplStruct struct {
|
type tmplStruct struct {
|
||||||
Name string // Auto-generated struct name(We can't obtain the raw struct name through abi)
|
Name string // Auto-generated struct name(before solidity v0.5.11) or raw name.
|
||||||
Fields []*tmplField // Struct fields definition depends on the binding language.
|
Fields []*tmplField // Struct fields definition depends on the binding language.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,15 +103,22 @@ var (
|
||||||
_ = big.NewInt
|
_ = big.NewInt
|
||||||
_ = strings.NewReader
|
_ = strings.NewReader
|
||||||
_ = ethereum.NotFound
|
_ = ethereum.NotFound
|
||||||
_ = abi.U256
|
|
||||||
_ = bind.Bind
|
_ = bind.Bind
|
||||||
_ = common.Big1
|
_ = common.Big1
|
||||||
_ = types.BloomLookup
|
_ = types.BloomLookup
|
||||||
_ = event.NewSubscription
|
_ = event.NewSubscription
|
||||||
)
|
)
|
||||||
|
|
||||||
|
{{$structs := .Structs}}
|
||||||
|
{{range $structs}}
|
||||||
|
// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
|
||||||
|
type {{.Name}} struct {
|
||||||
|
{{range $field := .Fields}}
|
||||||
|
{{$field.Name}} {{$field.Type}}{{end}}
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{range $contract := .Contracts}}
|
{{range $contract := .Contracts}}
|
||||||
{{$structs := $contract.Structs}}
|
|
||||||
// {{.Type}}ABI is the input ABI used to generate the binding from.
|
// {{.Type}}ABI is the input ABI used to generate the binding from.
|
||||||
const {{.Type}}ABI = "{{.InputABI}}"
|
const {{.Type}}ABI = "{{.InputABI}}"
|
||||||
|
|
||||||
|
|
@ -285,18 +294,10 @@ var (
|
||||||
return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...)
|
return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
{{range .Structs}}
|
|
||||||
// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
|
|
||||||
type {{.Name}} struct {
|
|
||||||
{{range $field := .Fields}}
|
|
||||||
{{$field.Name}} {{$field.Type}}{{end}}
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range .Calls}}
|
{{range .Calls}}
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}.
|
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{formatmethod .Original $structs}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) {
|
||||||
{{if .Structured}}ret := new(struct{
|
{{if .Structured}}ret := new(struct{
|
||||||
{{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}}
|
{{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}}
|
||||||
|
|
@ -313,44 +314,90 @@ var (
|
||||||
return {{if .Structured}}*ret,{{else}}{{range $i, $_ := .Normalized.Outputs}}*ret{{$i}},{{end}}{{end}} err
|
return {{if .Structured}}*ret,{{else}}{{range $i, $_ := .Normalized.Outputs}}*ret{{$i}},{{end}}{{end}} err
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}.
|
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{formatmethod .Original $structs}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
|
||||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}.
|
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{formatmethod .Original $structs}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
|
||||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{range .Transacts}}
|
{{range .Transacts}}
|
||||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}.
|
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{formatmethod .Original $structs}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
||||||
return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}.
|
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{formatmethod .Original $structs}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
||||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}.
|
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{formatmethod .Original $structs}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
||||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Fallback}}
|
||||||
|
// Fallback is a paid mutator transaction binding the contract fallback function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Fallback.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.contract.RawTransact(opts, calldata)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback is a paid mutator transaction binding the contract fallback function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Fallback.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback is a paid mutator transaction binding the contract fallback function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Fallback.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Fallback(calldata []byte) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Receive}}
|
||||||
|
// Receive is a paid mutator transaction binding the contract receive function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Receive.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.contract.RawTransact(opts, nil) // calldata is disallowed for receive function
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive is a paid mutator transaction binding the contract receive function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Receive.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive is a paid mutator transaction binding the contract receive function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Receive.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Receive() (*types.Transaction, error) {
|
||||||
|
return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{range .Events}}
|
{{range .Events}}
|
||||||
// {{$contract.Type}}{{.Normalized.Name}}Iterator is returned from Filter{{.Normalized.Name}} and is used to iterate over the raw logs and unpacked data for {{.Normalized.Name}} events raised by the {{$contract.Type}} contract.
|
// {{$contract.Type}}{{.Normalized.Name}}Iterator is returned from Filter{{.Normalized.Name}} and is used to iterate over the raw logs and unpacked data for {{.Normalized.Name}} events raised by the {{$contract.Type}} contract.
|
||||||
type {{$contract.Type}}{{.Normalized.Name}}Iterator struct {
|
type {{$contract.Type}}{{.Normalized.Name}}Iterator struct {
|
||||||
|
|
@ -422,9 +469,9 @@ var (
|
||||||
Raw types.Log // Blockchain specific contextual infos
|
Raw types.Log // Blockchain specific contextual infos
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.Id}}.
|
// Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{formatevent .Original $structs}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
|
||||||
{{range .Normalized.Inputs}}
|
{{range .Normalized.Inputs}}
|
||||||
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
||||||
|
|
@ -439,9 +486,9 @@ var (
|
||||||
return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
|
return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.Id}}.
|
// Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{formatevent .Original $structs}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) {
|
||||||
{{range .Normalized.Inputs}}
|
{{range .Normalized.Inputs}}
|
||||||
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
{{if .Indexed}}var {{.Name}}Rule []interface{}
|
||||||
|
|
@ -481,7 +528,7 @@ var (
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.Id}}.
|
// Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
|
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
|
||||||
|
|
@ -507,8 +554,8 @@ package {{.Package}};
|
||||||
import org.ethereum.geth.*;
|
import org.ethereum.geth.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
{{$structs := .Structs}}
|
||||||
{{range $contract := .Contracts}}
|
{{range $contract := .Contracts}}
|
||||||
{{$structs := $contract.Structs}}
|
|
||||||
{{if not .Library}}public {{end}}class {{.Type}} {
|
{{if not .Library}}public {{end}}class {{.Type}} {
|
||||||
// ABI is the input ABI used to generate the binding from.
|
// ABI is the input ABI used to generate the binding from.
|
||||||
public final static String ABI = "{{.InputABI}}";
|
public final static String ABI = "{{.InputABI}}";
|
||||||
|
|
@ -574,10 +621,10 @@ import java.util.*;
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}.
|
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
|
public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else if eq (len .Normalized.Outputs) 0}}void{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
|
||||||
Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
|
Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
|
||||||
{{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
|
{{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
@ -601,7 +648,7 @@ import java.util.*;
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{range .Transacts}}
|
{{range .Transacts}}
|
||||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}.
|
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||||
//
|
//
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
|
public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
|
||||||
|
|
@ -611,6 +658,24 @@ import java.util.*;
|
||||||
return this.Contract.transact(opts, "{{.Original.Name}}" , args);
|
return this.Contract.transact(opts, "{{.Original.Name}}" , args);
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Fallback}}
|
||||||
|
// Fallback is a paid mutator transaction binding the contract fallback function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Fallback.Original.String}}
|
||||||
|
public Transaction Fallback(TransactOpts opts, byte[] calldata) throws Exception {
|
||||||
|
return this.Contract.rawTransact(opts, calldata);
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if .Receive}}
|
||||||
|
// Receive is a paid mutator transaction binding the contract receive function.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Receive.Original.String}}
|
||||||
|
public Transaction Receive(TransactOpts opts) throws Exception {
|
||||||
|
return this.Contract.rawTransact(opts, null);
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
`
|
`
|
||||||
|
|
|
||||||
|
|
@ -1,241 +0,0 @@
|
||||||
// Copyright 2018 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 bind
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/maticnetwork/bor/accounts/abi"
|
|
||||||
"github.com/maticnetwork/bor/common"
|
|
||||||
"github.com/maticnetwork/bor/crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
// makeTopics converts a filter query argument list into a filter topic set.
|
|
||||||
func makeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
|
||||||
topics := make([][]common.Hash, len(query))
|
|
||||||
for i, filter := range query {
|
|
||||||
for _, rule := range filter {
|
|
||||||
var topic common.Hash
|
|
||||||
|
|
||||||
// Try to generate the topic based on simple types
|
|
||||||
switch rule := rule.(type) {
|
|
||||||
case common.Hash:
|
|
||||||
copy(topic[:], rule[:])
|
|
||||||
case common.Address:
|
|
||||||
copy(topic[common.HashLength-common.AddressLength:], rule[:])
|
|
||||||
case *big.Int:
|
|
||||||
blob := rule.Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case bool:
|
|
||||||
if rule {
|
|
||||||
topic[common.HashLength-1] = 1
|
|
||||||
}
|
|
||||||
case int8:
|
|
||||||
blob := big.NewInt(int64(rule)).Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case int16:
|
|
||||||
blob := big.NewInt(int64(rule)).Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case int32:
|
|
||||||
blob := big.NewInt(int64(rule)).Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case int64:
|
|
||||||
blob := big.NewInt(rule).Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case uint8:
|
|
||||||
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case uint16:
|
|
||||||
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case uint32:
|
|
||||||
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case uint64:
|
|
||||||
blob := new(big.Int).SetUint64(rule).Bytes()
|
|
||||||
copy(topic[common.HashLength-len(blob):], blob)
|
|
||||||
case string:
|
|
||||||
hash := crypto.Keccak256Hash([]byte(rule))
|
|
||||||
copy(topic[:], hash[:])
|
|
||||||
case []byte:
|
|
||||||
hash := crypto.Keccak256Hash(rule)
|
|
||||||
copy(topic[:], hash[:])
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Attempt to generate the topic from funky types
|
|
||||||
val := reflect.ValueOf(rule)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
|
|
||||||
// static byte array
|
|
||||||
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
|
|
||||||
reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val)
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported indexed type: %T", rule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
topics[i] = append(topics[i], topic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return topics, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Big batch of reflect types for topic reconstruction.
|
|
||||||
var (
|
|
||||||
reflectHash = reflect.TypeOf(common.Hash{})
|
|
||||||
reflectAddress = reflect.TypeOf(common.Address{})
|
|
||||||
reflectBigInt = reflect.TypeOf(new(big.Int))
|
|
||||||
)
|
|
||||||
|
|
||||||
// parseTopics converts the indexed topic fields into actual log field values.
|
|
||||||
//
|
|
||||||
// Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
|
|
||||||
// hashes as the topic value!
|
|
||||||
func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) error {
|
|
||||||
// Sanity check that the fields and topics match up
|
|
||||||
if len(fields) != len(topics) {
|
|
||||||
return errors.New("topic/field count mismatch")
|
|
||||||
}
|
|
||||||
// Iterate over all the fields and reconstruct them from topics
|
|
||||||
for _, arg := range fields {
|
|
||||||
if !arg.Indexed {
|
|
||||||
return errors.New("non-indexed field in topic reconstruction")
|
|
||||||
}
|
|
||||||
field := reflect.ValueOf(out).Elem().FieldByName(capitalise(arg.Name))
|
|
||||||
|
|
||||||
// Try to parse the topic back into the fields based on primitive types
|
|
||||||
switch field.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
if topics[0][common.HashLength-1] == 1 {
|
|
||||||
field.Set(reflect.ValueOf(true))
|
|
||||||
}
|
|
||||||
case reflect.Int8:
|
|
||||||
num := new(big.Int).SetBytes(topics[0][:])
|
|
||||||
field.Set(reflect.ValueOf(int8(num.Int64())))
|
|
||||||
|
|
||||||
case reflect.Int16:
|
|
||||||
num := new(big.Int).SetBytes(topics[0][:])
|
|
||||||
field.Set(reflect.ValueOf(int16(num.Int64())))
|
|
||||||
|
|
||||||
case reflect.Int32:
|
|
||||||
num := new(big.Int).SetBytes(topics[0][:])
|
|
||||||
field.Set(reflect.ValueOf(int32(num.Int64())))
|
|
||||||
|
|
||||||
case reflect.Int64:
|
|
||||||
num := new(big.Int).SetBytes(topics[0][:])
|
|
||||||
field.Set(reflect.ValueOf(num.Int64()))
|
|
||||||
|
|
||||||
case reflect.Uint8:
|
|
||||||
num := new(big.Int).SetBytes(topics[0][:])
|
|
||||||
field.Set(reflect.ValueOf(uint8(num.Uint64())))
|
|
||||||
|
|
||||||
case reflect.Uint16:
|
|
||||||
num := new(big.Int).SetBytes(topics[0][:])
|
|
||||||
field.Set(reflect.ValueOf(uint16(num.Uint64())))
|
|
||||||
|
|
||||||
case reflect.Uint32:
|
|
||||||
num := new(big.Int).SetBytes(topics[0][:])
|
|
||||||
field.Set(reflect.ValueOf(uint32(num.Uint64())))
|
|
||||||
|
|
||||||
case reflect.Uint64:
|
|
||||||
num := new(big.Int).SetBytes(topics[0][:])
|
|
||||||
field.Set(reflect.ValueOf(num.Uint64()))
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Ran out of plain primitive types, try custom types
|
|
||||||
switch field.Type() {
|
|
||||||
case reflectHash: // Also covers all dynamic types
|
|
||||||
field.Set(reflect.ValueOf(topics[0]))
|
|
||||||
|
|
||||||
case reflectAddress:
|
|
||||||
var addr common.Address
|
|
||||||
copy(addr[:], topics[0][common.HashLength-common.AddressLength:])
|
|
||||||
field.Set(reflect.ValueOf(addr))
|
|
||||||
|
|
||||||
case reflectBigInt:
|
|
||||||
num := new(big.Int).SetBytes(topics[0][:])
|
|
||||||
field.Set(reflect.ValueOf(num))
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Ran out of custom types, try the crazies
|
|
||||||
switch {
|
|
||||||
|
|
||||||
// static byte array
|
|
||||||
case arg.Type.T == abi.FixedBytesTy:
|
|
||||||
reflect.Copy(field, reflect.ValueOf(topics[0][:arg.Type.Size]))
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported indexed type: %v", arg.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
topics = topics[1:]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs
|
|
||||||
func parseTopicsIntoMap(out map[string]interface{}, fields abi.Arguments, topics []common.Hash) error {
|
|
||||||
// Sanity check that the fields and topics match up
|
|
||||||
if len(fields) != len(topics) {
|
|
||||||
return errors.New("topic/field count mismatch")
|
|
||||||
}
|
|
||||||
// Iterate over all the fields and reconstruct them from topics
|
|
||||||
for _, arg := range fields {
|
|
||||||
if !arg.Indexed {
|
|
||||||
return errors.New("non-indexed field in topic reconstruction")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch arg.Type.T {
|
|
||||||
case abi.BoolTy:
|
|
||||||
out[arg.Name] = topics[0][common.HashLength-1] == 1
|
|
||||||
case abi.IntTy, abi.UintTy:
|
|
||||||
num := new(big.Int).SetBytes(topics[0][:])
|
|
||||||
out[arg.Name] = num
|
|
||||||
case abi.AddressTy:
|
|
||||||
var addr common.Address
|
|
||||||
copy(addr[:], topics[0][common.HashLength-common.AddressLength:])
|
|
||||||
out[arg.Name] = addr
|
|
||||||
case abi.HashTy:
|
|
||||||
out[arg.Name] = topics[0]
|
|
||||||
case abi.FixedBytesTy:
|
|
||||||
out[arg.Name] = topics[0][:]
|
|
||||||
case abi.StringTy, abi.BytesTy, abi.SliceTy, abi.ArrayTy:
|
|
||||||
// Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash
|
|
||||||
// whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash
|
|
||||||
out[arg.Name] = topics[0]
|
|
||||||
case abi.FunctionTy:
|
|
||||||
if garbage := binary.BigEndian.Uint64(topics[0][0:8]); garbage != 0 {
|
|
||||||
return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[0].Bytes())
|
|
||||||
}
|
|
||||||
var tmp [24]byte
|
|
||||||
copy(tmp[:], topics[0][8:32])
|
|
||||||
out[arg.Name] = tmp
|
|
||||||
default: // Not handling tuples
|
|
||||||
return fmt.Errorf("unsupported indexed type: %v", arg.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
topics = topics[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
// Copyright 2018 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 bind
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/maticnetwork/bor/accounts/abi"
|
|
||||||
"github.com/maticnetwork/bor/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMakeTopics(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
query [][]interface{}
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want [][]common.Hash
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"support fixed byte types, right padded to 32 bytes",
|
|
||||||
args{[][]interface{}{{[5]byte{1, 2, 3, 4, 5}}}},
|
|
||||||
[][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, err := makeTopics(tt.args.query...)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("makeTopics() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("makeTopics() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseTopics(t *testing.T) {
|
|
||||||
type bytesStruct struct {
|
|
||||||
StaticBytes [5]byte
|
|
||||||
}
|
|
||||||
bytesType, _ := abi.NewType("bytes5", nil)
|
|
||||||
type args struct {
|
|
||||||
createObj func() interface{}
|
|
||||||
resultObj func() interface{}
|
|
||||||
fields abi.Arguments
|
|
||||||
topics []common.Hash
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "support fixed byte types, right padded to 32 bytes",
|
|
||||||
args: args{
|
|
||||||
createObj: func() interface{} { return &bytesStruct{} },
|
|
||||||
resultObj: func() interface{} { return &bytesStruct{StaticBytes: [5]byte{1, 2, 3, 4, 5}} },
|
|
||||||
fields: abi.Arguments{abi.Argument{
|
|
||||||
Name: "staticBytes",
|
|
||||||
Type: bytesType,
|
|
||||||
Indexed: true,
|
|
||||||
}},
|
|
||||||
topics: []common.Hash{
|
|
||||||
{1, 2, 3, 4, 5},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
createObj := tt.args.createObj()
|
|
||||||
if err := parseTopics(createObj, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("parseTopics() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
resultObj := tt.args.resultObj()
|
|
||||||
if !reflect.DeepEqual(createObj, resultObj) {
|
|
||||||
t.Errorf("parseTopics() = %v, want %v", createObj, resultObj)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -18,7 +18,7 @@ package bind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
|
|
@ -56,14 +56,14 @@ func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*ty
|
||||||
// contract address when it is mined. It stops waiting when ctx is canceled.
|
// contract address when it is mined. It stops waiting when ctx is canceled.
|
||||||
func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) {
|
func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) {
|
||||||
if tx.To() != nil {
|
if tx.To() != nil {
|
||||||
return common.Address{}, fmt.Errorf("tx is not contract creation")
|
return common.Address{}, errors.New("tx is not contract creation")
|
||||||
}
|
}
|
||||||
receipt, err := WaitMined(ctx, b, tx)
|
receipt, err := WaitMined(ctx, b, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
if receipt.ContractAddress == (common.Address{}) {
|
if receipt.ContractAddress == (common.Address{}) {
|
||||||
return common.Address{}, fmt.Errorf("zero address")
|
return common.Address{}, errors.New("zero address")
|
||||||
}
|
}
|
||||||
// Check that code has indeed been deployed at the address.
|
// Check that code has indeed been deployed at the address.
|
||||||
// This matters on pre-Homestead chains: OOG in the constructor
|
// This matters on pre-Homestead chains: OOG in the constructor
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package bind_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -59,6 +60,7 @@ func TestWaitDeployed(t *testing.T) {
|
||||||
},
|
},
|
||||||
10000000,
|
10000000,
|
||||||
)
|
)
|
||||||
|
defer backend.Close()
|
||||||
|
|
||||||
// Create the transaction.
|
// Create the transaction.
|
||||||
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code))
|
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code))
|
||||||
|
|
@ -83,7 +85,7 @@ func TestWaitDeployed(t *testing.T) {
|
||||||
select {
|
select {
|
||||||
case <-mined:
|
case <-mined:
|
||||||
if err != test.wantErr {
|
if err != test.wantErr {
|
||||||
t.Errorf("test %q: error mismatch: got %q, want %q", name, err, test.wantErr)
|
t.Errorf("test %q: error mismatch: want %q, got %q", name, test.wantErr, err)
|
||||||
}
|
}
|
||||||
if address != test.wantAddress {
|
if address != test.wantAddress {
|
||||||
t.Errorf("test %q: unexpected contract address %s", name, address.Hex())
|
t.Errorf("test %q: unexpected contract address %s", name, address.Hex())
|
||||||
|
|
@ -93,3 +95,40 @@ func TestWaitDeployed(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWaitDeployedCornerCases(t *testing.T) {
|
||||||
|
backend := backends.NewSimulatedBackend(
|
||||||
|
core.GenesisAlloc{
|
||||||
|
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)},
|
||||||
|
},
|
||||||
|
10000000,
|
||||||
|
)
|
||||||
|
defer backend.Close()
|
||||||
|
|
||||||
|
// Create a transaction to an account.
|
||||||
|
code := "6060604052600a8060106000396000f360606040526008565b00"
|
||||||
|
tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, big.NewInt(1), common.FromHex(code))
|
||||||
|
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
backend.SendTransaction(ctx, tx)
|
||||||
|
backend.Commit()
|
||||||
|
notContentCreation := errors.New("tx is not contract creation")
|
||||||
|
if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContentCreation.Error() {
|
||||||
|
t.Errorf("error missmatch: want %q, got %q, ", notContentCreation, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a transaction that is not mined.
|
||||||
|
tx = types.NewContractCreation(1, big.NewInt(0), 3000000, big.NewInt(1), common.FromHex(code))
|
||||||
|
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
contextCanceled := errors.New("context canceled")
|
||||||
|
if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != contextCanceled.Error() {
|
||||||
|
t.Errorf("error missmatch: want %q, got %q, ", contextCanceled, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
backend.SendTransaction(ctx, tx)
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,23 +39,21 @@ func formatSliceString(kind reflect.Kind, sliceSize int) string {
|
||||||
// type in t.
|
// type in t.
|
||||||
func sliceTypeCheck(t Type, val reflect.Value) error {
|
func sliceTypeCheck(t Type, val reflect.Value) error {
|
||||||
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
|
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
|
||||||
return typeErr(formatSliceString(t.Kind, t.Size), val.Type())
|
return typeErr(formatSliceString(t.GetType().Kind(), t.Size), val.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.T == ArrayTy && val.Len() != t.Size {
|
if t.T == ArrayTy && val.Len() != t.Size {
|
||||||
return typeErr(formatSliceString(t.Elem.Kind, t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
|
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.Elem.T == SliceTy {
|
if t.Elem.T == SliceTy || t.Elem.T == ArrayTy {
|
||||||
if val.Len() > 0 {
|
if val.Len() > 0 {
|
||||||
return sliceTypeCheck(*t.Elem, val.Index(0))
|
return sliceTypeCheck(*t.Elem, val.Index(0))
|
||||||
}
|
}
|
||||||
} else if t.Elem.T == ArrayTy {
|
|
||||||
return sliceTypeCheck(*t.Elem, val.Index(0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.Kind {
|
if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.GetType().Kind() {
|
||||||
return typeErr(formatSliceString(t.Elem.Kind, t.Size), val.Type())
|
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -68,10 +66,10 @@ func typeCheck(t Type, value reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check base type validity. Element types will be checked later on.
|
// Check base type validity. Element types will be checked later on.
|
||||||
if t.Kind != value.Kind() {
|
if t.GetType().Kind() != value.Kind() {
|
||||||
return typeErr(t.Kind, value.Kind())
|
return typeErr(t.GetType().Kind(), value.Kind())
|
||||||
} else if t.T == FixedBytesTy && t.Size != value.Len() {
|
} else if t.T == FixedBytesTy && t.Size != value.Len() {
|
||||||
return typeErr(t.Type, value.Type())
|
return typeErr(t.GetType(), value.Type())
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,30 +28,73 @@ import (
|
||||||
// holds type information (inputs) about the yielded output. Anonymous events
|
// holds type information (inputs) about the yielded output. Anonymous events
|
||||||
// don't get the signature canonical representation as the first LOG topic.
|
// don't get the signature canonical representation as the first LOG topic.
|
||||||
type Event struct {
|
type Event struct {
|
||||||
Name string
|
// Name is the event name used for internal representation. It's derived from
|
||||||
|
// the raw name and a suffix will be added in the case of a event overload.
|
||||||
|
//
|
||||||
|
// e.g.
|
||||||
|
// There are two events have same name:
|
||||||
|
// * foo(int,int)
|
||||||
|
// * foo(uint,uint)
|
||||||
|
// The event name of the first one wll be resolved as foo while the second one
|
||||||
|
// will be resolved as foo0.
|
||||||
|
Name string
|
||||||
|
// RawName is the raw event name parsed from ABI.
|
||||||
|
RawName string
|
||||||
Anonymous bool
|
Anonymous bool
|
||||||
Inputs Arguments
|
Inputs Arguments
|
||||||
|
str string
|
||||||
|
// Sig contains the string signature according to the ABI spec.
|
||||||
|
// e.g. event foo(uint32 a, int b) = "foo(uint32,int256)"
|
||||||
|
// Please note that "int" is substitute for its canonical representation "int256"
|
||||||
|
Sig string
|
||||||
|
// ID returns the canonical representation of the event's signature used by the
|
||||||
|
// abi definition to identify event names and types.
|
||||||
|
ID common.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEvent creates a new Event.
|
||||||
|
// It sanitizes the input arguments to remove unnamed arguments.
|
||||||
|
// It also precomputes the id, signature and string representation
|
||||||
|
// of the event.
|
||||||
|
func NewEvent(name, rawName string, anonymous bool, inputs Arguments) Event {
|
||||||
|
// sanitize inputs to remove inputs without names
|
||||||
|
// and precompute string and sig representation.
|
||||||
|
names := make([]string, len(inputs))
|
||||||
|
types := make([]string, len(inputs))
|
||||||
|
for i, input := range inputs {
|
||||||
|
if input.Name == "" {
|
||||||
|
inputs[i] = Argument{
|
||||||
|
Name: fmt.Sprintf("arg%d", i),
|
||||||
|
Indexed: input.Indexed,
|
||||||
|
Type: input.Type,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inputs[i] = input
|
||||||
|
}
|
||||||
|
// string representation
|
||||||
|
names[i] = fmt.Sprintf("%v %v", input.Type, inputs[i].Name)
|
||||||
|
if input.Indexed {
|
||||||
|
names[i] = fmt.Sprintf("%v indexed %v", input.Type, inputs[i].Name)
|
||||||
|
}
|
||||||
|
// sig representation
|
||||||
|
types[i] = input.Type.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
str := fmt.Sprintf("event %v(%v)", rawName, strings.Join(names, ", "))
|
||||||
|
sig := fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
|
||||||
|
id := common.BytesToHash(crypto.Keccak256([]byte(sig)))
|
||||||
|
|
||||||
|
return Event{
|
||||||
|
Name: name,
|
||||||
|
RawName: rawName,
|
||||||
|
Anonymous: anonymous,
|
||||||
|
Inputs: inputs,
|
||||||
|
str: str,
|
||||||
|
Sig: sig,
|
||||||
|
ID: id,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Event) String() string {
|
func (e Event) String() string {
|
||||||
inputs := make([]string, len(e.Inputs))
|
return e.str
|
||||||
for i, input := range e.Inputs {
|
|
||||||
inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
|
||||||
if input.Indexed {
|
|
||||||
inputs[i] = fmt.Sprintf("%v indexed %v", input.Type, input.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("event %v(%v)", e.Name, strings.Join(inputs, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Id returns the canonical representation of the event's signature used by the
|
|
||||||
// abi definition to identify event names and types.
|
|
||||||
func (e Event) Id() common.Hash {
|
|
||||||
types := make([]string, len(e.Inputs))
|
|
||||||
i := 0
|
|
||||||
for _, input := range e.Inputs {
|
|
||||||
types[i] = input.Type.String()
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return common.BytesToHash(crypto.Keccak256([]byte(fmt.Sprintf("%v(%v)", e.Name, strings.Join(types, ",")))))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,8 +104,8 @@ func TestEventId(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, event := range abi.Events {
|
for name, event := range abi.Events {
|
||||||
if event.Id() != test.expectations[name] {
|
if event.ID != test.expectations[name] {
|
||||||
t.Errorf("expected id to be %x, got %x", test.expectations[name], event.Id())
|
t.Errorf("expected id to be %x, got %x", test.expectations[name], event.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -173,7 +173,7 @@ func TestEventTupleUnpack(t *testing.T) {
|
||||||
type EventTransferWithTag struct {
|
type EventTransferWithTag struct {
|
||||||
// this is valid because `value` is not exportable,
|
// this is valid because `value` is not exportable,
|
||||||
// so value is only unmarshalled into `Value1`.
|
// so value is only unmarshalled into `Value1`.
|
||||||
value *big.Int
|
value *big.Int //lint:ignore U1000 unused field is part of test
|
||||||
Value1 *big.Int `abi:"value"`
|
Value1 *big.Int `abi:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -312,14 +312,14 @@ func TestEventTupleUnpack(t *testing.T) {
|
||||||
&[]interface{}{common.Address{}, new(big.Int)},
|
&[]interface{}{common.Address{}, new(big.Int)},
|
||||||
&[]interface{}{},
|
&[]interface{}{},
|
||||||
jsonEventPledge,
|
jsonEventPledge,
|
||||||
"abi: insufficient number of elements in the list/array for unpack, want 3, got 2",
|
"abi: insufficient number of arguments for unpack, want 3, got 2",
|
||||||
"Can not unpack Pledge event into too short slice",
|
"Can not unpack Pledge event into too short slice",
|
||||||
}, {
|
}, {
|
||||||
pledgeData1,
|
pledgeData1,
|
||||||
new(map[string]interface{}),
|
new(map[string]interface{}),
|
||||||
&[]interface{}{},
|
&[]interface{}{},
|
||||||
jsonEventPledge,
|
jsonEventPledge,
|
||||||
"abi: cannot unmarshal tuple into map[string]interface {}",
|
"abi:[2] cannot unmarshal tuple in to map[string]interface {}",
|
||||||
"Can not unpack Pledge event into map",
|
"Can not unpack Pledge event into map",
|
||||||
}, {
|
}, {
|
||||||
mixedCaseData1,
|
mixedCaseData1,
|
||||||
|
|
@ -354,40 +354,6 @@ func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, ass
|
||||||
return a.Unpack(dest, "e", data)
|
return a.Unpack(dest, "e", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Taken from
|
|
||||||
https://github.com/maticnetwork/bor/pull/15568
|
|
||||||
*/
|
|
||||||
|
|
||||||
type testResult struct {
|
|
||||||
Values [2]*big.Int
|
|
||||||
Value1 *big.Int
|
|
||||||
Value2 *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type testCase struct {
|
|
||||||
definition string
|
|
||||||
want testResult
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc testCase) encoded(intType, arrayType Type) []byte {
|
|
||||||
var b bytes.Buffer
|
|
||||||
if tc.want.Value1 != nil {
|
|
||||||
val, _ := intType.pack(reflect.ValueOf(tc.want.Value1))
|
|
||||||
b.Write(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(tc.want.Values, [2]*big.Int{nil, nil}) {
|
|
||||||
val, _ := arrayType.pack(reflect.ValueOf(tc.want.Values))
|
|
||||||
b.Write(val)
|
|
||||||
}
|
|
||||||
if tc.want.Value2 != nil {
|
|
||||||
val, _ := intType.pack(reflect.ValueOf(tc.want.Value2))
|
|
||||||
b.Write(val)
|
|
||||||
}
|
|
||||||
return b.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
|
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
|
||||||
func TestEventUnpackIndexed(t *testing.T) {
|
func TestEventUnpackIndexed(t *testing.T) {
|
||||||
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,24 @@ import (
|
||||||
"github.com/maticnetwork/bor/crypto"
|
"github.com/maticnetwork/bor/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FunctionType represents different types of functions a contract might have.
|
||||||
|
type FunctionType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Constructor represents the constructor of the contract.
|
||||||
|
// The constructor function is called while deploying a contract.
|
||||||
|
Constructor FunctionType = iota
|
||||||
|
// Fallback represents the fallback function.
|
||||||
|
// This function is executed if no other function matches the given function
|
||||||
|
// signature and no receive function is specified.
|
||||||
|
Fallback
|
||||||
|
// Receive represents the receive function.
|
||||||
|
// This function is executed on plain Ether transfers.
|
||||||
|
Receive
|
||||||
|
// Function represents a normal function.
|
||||||
|
Function
|
||||||
|
)
|
||||||
|
|
||||||
// Method represents a callable given a `Name` and whether the method is a constant.
|
// Method represents a callable given a `Name` and whether the method is a constant.
|
||||||
// If the method is `Const` no transaction needs to be created for this
|
// If the method is `Const` no transaction needs to be created for this
|
||||||
// particular Method call. It can easily be simulated using a local VM.
|
// particular Method call. It can easily be simulated using a local VM.
|
||||||
|
|
@ -32,46 +50,118 @@ import (
|
||||||
// be flagged `false`.
|
// be flagged `false`.
|
||||||
// Input specifies the required input parameters for this gives method.
|
// Input specifies the required input parameters for this gives method.
|
||||||
type Method struct {
|
type Method struct {
|
||||||
|
// Name is the method name used for internal representation. It's derived from
|
||||||
|
// the raw name and a suffix will be added in the case of a function overload.
|
||||||
|
//
|
||||||
|
// e.g.
|
||||||
|
// There are two functions have same name:
|
||||||
|
// * foo(int,int)
|
||||||
|
// * foo(uint,uint)
|
||||||
|
// The method name of the first one will be resolved as foo while the second one
|
||||||
|
// will be resolved as foo0.
|
||||||
Name string
|
Name string
|
||||||
Const bool
|
RawName string // RawName is the raw method name parsed from ABI
|
||||||
|
|
||||||
|
// Type indicates whether the method is a
|
||||||
|
// special fallback introduced in solidity v0.6.0
|
||||||
|
Type FunctionType
|
||||||
|
|
||||||
|
// StateMutability indicates the mutability state of method,
|
||||||
|
// the default value is nonpayable. It can be empty if the abi
|
||||||
|
// is generated by legacy compiler.
|
||||||
|
StateMutability string
|
||||||
|
|
||||||
|
// Legacy indicators generated by compiler before v0.6.0
|
||||||
|
Constant bool
|
||||||
|
Payable bool
|
||||||
|
|
||||||
Inputs Arguments
|
Inputs Arguments
|
||||||
Outputs Arguments
|
Outputs Arguments
|
||||||
|
str string
|
||||||
|
// Sig returns the methods string signature according to the ABI spec.
|
||||||
|
// e.g. function foo(uint32 a, int b) = "foo(uint32,int256)"
|
||||||
|
// Please note that "int" is substitute for its canonical representation "int256"
|
||||||
|
Sig string
|
||||||
|
// ID returns the canonical representation of the method's signature used by the
|
||||||
|
// abi definition to identify method names and types.
|
||||||
|
ID []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sig returns the methods string signature according to the ABI spec.
|
// NewMethod creates a new Method.
|
||||||
//
|
// A method should always be created using NewMethod.
|
||||||
// Example
|
// It also precomputes the sig representation and the string representation
|
||||||
//
|
// of the method.
|
||||||
// function foo(uint32 a, int b) = "foo(uint32,int256)"
|
func NewMethod(name string, rawName string, funType FunctionType, mutability string, isConst, isPayable bool, inputs Arguments, outputs Arguments) Method {
|
||||||
//
|
var (
|
||||||
// Please note that "int" is substitute for its canonical representation "int256"
|
types = make([]string, len(inputs))
|
||||||
func (method Method) Sig() string {
|
inputNames = make([]string, len(inputs))
|
||||||
types := make([]string, len(method.Inputs))
|
outputNames = make([]string, len(outputs))
|
||||||
for i, input := range method.Inputs {
|
)
|
||||||
|
for i, input := range inputs {
|
||||||
|
inputNames[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
||||||
types[i] = input.Type.String()
|
types[i] = input.Type.String()
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%v(%v)", method.Name, strings.Join(types, ","))
|
for i, output := range outputs {
|
||||||
|
outputNames[i] = output.Type.String()
|
||||||
|
if len(output.Name) > 0 {
|
||||||
|
outputNames[i] += fmt.Sprintf(" %v", output.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// calculate the signature and method id. Note only function
|
||||||
|
// has meaningful signature and id.
|
||||||
|
var (
|
||||||
|
sig string
|
||||||
|
id []byte
|
||||||
|
)
|
||||||
|
if funType == Function {
|
||||||
|
sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
|
||||||
|
id = crypto.Keccak256([]byte(sig))[:4]
|
||||||
|
}
|
||||||
|
// Extract meaningful state mutability of solidity method.
|
||||||
|
// If it's default value, never print it.
|
||||||
|
state := mutability
|
||||||
|
if state == "nonpayable" {
|
||||||
|
state = ""
|
||||||
|
}
|
||||||
|
if state != "" {
|
||||||
|
state = state + " "
|
||||||
|
}
|
||||||
|
identity := fmt.Sprintf("function %v", rawName)
|
||||||
|
if funType == Fallback {
|
||||||
|
identity = "fallback"
|
||||||
|
} else if funType == Receive {
|
||||||
|
identity = "receive"
|
||||||
|
} else if funType == Constructor {
|
||||||
|
identity = "constructor"
|
||||||
|
}
|
||||||
|
str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))
|
||||||
|
|
||||||
|
return Method{
|
||||||
|
Name: name,
|
||||||
|
RawName: rawName,
|
||||||
|
Type: funType,
|
||||||
|
StateMutability: mutability,
|
||||||
|
Constant: isConst,
|
||||||
|
Payable: isPayable,
|
||||||
|
Inputs: inputs,
|
||||||
|
Outputs: outputs,
|
||||||
|
str: str,
|
||||||
|
Sig: sig,
|
||||||
|
ID: id,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (method Method) String() string {
|
func (method Method) String() string {
|
||||||
inputs := make([]string, len(method.Inputs))
|
return method.str
|
||||||
for i, input := range method.Inputs {
|
|
||||||
inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
|
||||||
}
|
|
||||||
outputs := make([]string, len(method.Outputs))
|
|
||||||
for i, output := range method.Outputs {
|
|
||||||
outputs[i] = output.Type.String()
|
|
||||||
if len(output.Name) > 0 {
|
|
||||||
outputs[i] += fmt.Sprintf(" %v", output.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
constant := ""
|
|
||||||
if method.Const {
|
|
||||||
constant = "constant "
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.Name, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (method Method) Id() []byte {
|
// IsConstant returns the indicator whether the method is read-only.
|
||||||
return crypto.Keccak256([]byte(method.Sig()))[:4]
|
func (method Method) IsConstant() bool {
|
||||||
|
return method.StateMutability == "view" || method.StateMutability == "pure" || method.Constant
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPayable returns the indicator whether the method can process
|
||||||
|
// plain ether transfers.
|
||||||
|
func (method Method) IsPayable() bool {
|
||||||
|
return method.StateMutability == "payable" || method.Payable
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2016 The go-ethereum Authors
|
// Copyright 2018 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -23,13 +23,15 @@ import (
|
||||||
|
|
||||||
const methoddata = `
|
const methoddata = `
|
||||||
[
|
[
|
||||||
{"type": "function", "name": "balance", "constant": true },
|
{"type": "function", "name": "balance", "stateMutability": "view"},
|
||||||
{"type": "function", "name": "send", "constant": false, "inputs": [{ "name": "amount", "type": "uint256" }]},
|
{"type": "function", "name": "send", "inputs": [{ "name": "amount", "type": "uint256" }]},
|
||||||
{"type": "function", "name": "transfer", "constant": false, "inputs": [{"name": "from", "type": "address"}, {"name": "to", "type": "address"}, {"name": "value", "type": "uint256"}], "outputs": [{"name": "success", "type": "bool"}]},
|
{"type": "function", "name": "transfer", "inputs": [{"name": "from", "type": "address"}, {"name": "to", "type": "address"}, {"name": "value", "type": "uint256"}], "outputs": [{"name": "success", "type": "bool"}]},
|
||||||
{"constant":false,"inputs":[{"components":[{"name":"x","type":"uint256"},{"name":"y","type":"uint256"}],"name":"a","type":"tuple"}],"name":"tuple","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
|
{"constant":false,"inputs":[{"components":[{"name":"x","type":"uint256"},{"name":"y","type":"uint256"}],"name":"a","type":"tuple"}],"name":"tuple","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
|
||||||
{"constant":false,"inputs":[{"components":[{"name":"x","type":"uint256"},{"name":"y","type":"uint256"}],"name":"a","type":"tuple[]"}],"name":"tupleSlice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
|
{"constant":false,"inputs":[{"components":[{"name":"x","type":"uint256"},{"name":"y","type":"uint256"}],"name":"a","type":"tuple[]"}],"name":"tupleSlice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
|
||||||
{"constant":false,"inputs":[{"components":[{"name":"x","type":"uint256"},{"name":"y","type":"uint256"}],"name":"a","type":"tuple[5]"}],"name":"tupleArray","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
|
{"constant":false,"inputs":[{"components":[{"name":"x","type":"uint256"},{"name":"y","type":"uint256"}],"name":"a","type":"tuple[5]"}],"name":"tupleArray","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
|
||||||
{"constant":false,"inputs":[{"components":[{"name":"x","type":"uint256"},{"name":"y","type":"uint256"}],"name":"a","type":"tuple[5][]"}],"name":"complexTuple","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}
|
{"constant":false,"inputs":[{"components":[{"name":"x","type":"uint256"},{"name":"y","type":"uint256"}],"name":"a","type":"tuple[5][]"}],"name":"complexTuple","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
|
||||||
|
{"stateMutability":"nonpayable","type":"fallback"},
|
||||||
|
{"stateMutability":"payable","type":"receive"}
|
||||||
]`
|
]`
|
||||||
|
|
||||||
func TestMethodString(t *testing.T) {
|
func TestMethodString(t *testing.T) {
|
||||||
|
|
@ -39,7 +41,7 @@ func TestMethodString(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
method: "balance",
|
method: "balance",
|
||||||
expectation: "function balance() constant returns()",
|
expectation: "function balance() view returns()",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
method: "send",
|
method: "send",
|
||||||
|
|
@ -65,6 +67,14 @@ func TestMethodString(t *testing.T) {
|
||||||
method: "complexTuple",
|
method: "complexTuple",
|
||||||
expectation: "function complexTuple((uint256,uint256)[5][] a) returns()",
|
expectation: "function complexTuple((uint256,uint256)[5][] a) returns()",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
method: "fallback",
|
||||||
|
expectation: "fallback() returns()",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "receive",
|
||||||
|
expectation: "receive() payable returns()",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(methoddata))
|
abi, err := JSON(strings.NewReader(methoddata))
|
||||||
|
|
@ -73,7 +83,14 @@ func TestMethodString(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
got := abi.Methods[test.method].String()
|
var got string
|
||||||
|
if test.method == "fallback" {
|
||||||
|
got = abi.Fallback.String()
|
||||||
|
} else if test.method == "receive" {
|
||||||
|
got = abi.Receive.String()
|
||||||
|
} else {
|
||||||
|
got = abi.Methods[test.method].String()
|
||||||
|
}
|
||||||
if got != test.expectation {
|
if got != test.expectation {
|
||||||
t.Errorf("expected string to be %s, got %s", test.expectation, got)
|
t.Errorf("expected string to be %s, got %s", test.expectation, got)
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +137,7 @@ func TestMethodSig(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range cases {
|
for _, test := range cases {
|
||||||
got := abi.Methods[test.method].Sig()
|
got := abi.Methods[test.method].Sig
|
||||||
if got != test.expect {
|
if got != test.expect {
|
||||||
t.Errorf("expected string to be %s, got %s", test.expect, got)
|
t.Errorf("expected string to be %s, got %s", test.expect, got)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,11 +69,11 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
|
||||||
func packNum(value reflect.Value) []byte {
|
func packNum(value reflect.Value) []byte {
|
||||||
switch kind := value.Kind(); kind {
|
switch kind := value.Kind(); kind {
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
return U256(new(big.Int).SetUint64(value.Uint()))
|
return math.U256Bytes(new(big.Int).SetUint64(value.Uint()))
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
return U256(big.NewInt(value.Int()))
|
return math.U256Bytes(big.NewInt(value.Int()))
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
return U256(value.Interface().(*big.Int))
|
return math.U256Bytes(new(big.Int).Set(value.Interface().(*big.Int)))
|
||||||
default:
|
default:
|
||||||
panic("abi: fatal error")
|
panic("abi: fatal error")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,623 +18,62 @@ package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestPack tests the general pack/unpack tests in packing_test.go
|
||||||
func TestPack(t *testing.T) {
|
func TestPack(t *testing.T) {
|
||||||
for i, test := range []struct {
|
for i, test := range packUnpackTests {
|
||||||
typ string
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
components []ArgumentMarshaling
|
encb, err := hex.DecodeString(test.packed)
|
||||||
input interface{}
|
if err != nil {
|
||||||
output []byte
|
t.Fatalf("invalid hex %s: %v", test.packed, err)
|
||||||
}{
|
}
|
||||||
{
|
inDef := fmt.Sprintf(`[{ "name" : "method", "type": "function", "inputs": %s}]`, test.def)
|
||||||
"uint8",
|
inAbi, err := JSON(strings.NewReader(inDef))
|
||||||
nil,
|
if err != nil {
|
||||||
uint8(2),
|
t.Fatalf("invalid ABI definition %s, %v", inDef, err)
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
}
|
||||||
},
|
var packed []byte
|
||||||
{
|
if reflect.TypeOf(test.unpacked).Kind() != reflect.Struct {
|
||||||
"uint8[]",
|
packed, err = inAbi.Pack("method", test.unpacked)
|
||||||
nil,
|
} else {
|
||||||
[]uint8{1, 2},
|
// if want is a struct we need to use the components.
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
elem := reflect.ValueOf(test.unpacked)
|
||||||
},
|
var values []interface{}
|
||||||
{
|
for i := 0; i < elem.NumField(); i++ {
|
||||||
"uint16",
|
field := elem.Field(i)
|
||||||
nil,
|
values = append(values, field.Interface())
|
||||||
uint16(2),
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint16[]",
|
|
||||||
nil,
|
|
||||||
[]uint16{1, 2},
|
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint32",
|
|
||||||
nil,
|
|
||||||
uint32(2),
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint32[]",
|
|
||||||
nil,
|
|
||||||
[]uint32{1, 2},
|
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint64",
|
|
||||||
nil,
|
|
||||||
uint64(2),
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint64[]",
|
|
||||||
nil,
|
|
||||||
[]uint64{1, 2},
|
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint256",
|
|
||||||
nil,
|
|
||||||
big.NewInt(2),
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint256[]",
|
|
||||||
nil,
|
|
||||||
[]*big.Int{big.NewInt(1), big.NewInt(2)},
|
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int8",
|
|
||||||
nil,
|
|
||||||
int8(2),
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int8[]",
|
|
||||||
nil,
|
|
||||||
[]int8{1, 2},
|
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int16",
|
|
||||||
nil,
|
|
||||||
int16(2),
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int16[]",
|
|
||||||
nil,
|
|
||||||
[]int16{1, 2},
|
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int32",
|
|
||||||
nil,
|
|
||||||
int32(2),
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int32[]",
|
|
||||||
nil,
|
|
||||||
[]int32{1, 2},
|
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int64",
|
|
||||||
nil,
|
|
||||||
int64(2),
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int64[]",
|
|
||||||
nil,
|
|
||||||
[]int64{1, 2},
|
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int256",
|
|
||||||
nil,
|
|
||||||
big.NewInt(2),
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"int256[]",
|
|
||||||
nil,
|
|
||||||
[]*big.Int{big.NewInt(1), big.NewInt(2)},
|
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes1",
|
|
||||||
nil,
|
|
||||||
[1]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes2",
|
|
||||||
nil,
|
|
||||||
[2]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes3",
|
|
||||||
nil,
|
|
||||||
[3]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes4",
|
|
||||||
nil,
|
|
||||||
[4]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes5",
|
|
||||||
nil,
|
|
||||||
[5]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes6",
|
|
||||||
nil,
|
|
||||||
[6]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes7",
|
|
||||||
nil,
|
|
||||||
[7]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes8",
|
|
||||||
nil,
|
|
||||||
[8]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes9",
|
|
||||||
nil,
|
|
||||||
[9]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes10",
|
|
||||||
nil,
|
|
||||||
[10]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes11",
|
|
||||||
nil,
|
|
||||||
[11]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes12",
|
|
||||||
nil,
|
|
||||||
[12]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes13",
|
|
||||||
nil,
|
|
||||||
[13]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes14",
|
|
||||||
nil,
|
|
||||||
[14]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes15",
|
|
||||||
nil,
|
|
||||||
[15]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes16",
|
|
||||||
nil,
|
|
||||||
[16]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes17",
|
|
||||||
nil,
|
|
||||||
[17]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes18",
|
|
||||||
nil,
|
|
||||||
[18]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes19",
|
|
||||||
nil,
|
|
||||||
[19]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes20",
|
|
||||||
nil,
|
|
||||||
[20]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes21",
|
|
||||||
nil,
|
|
||||||
[21]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes22",
|
|
||||||
nil,
|
|
||||||
[22]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes23",
|
|
||||||
nil,
|
|
||||||
[23]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes24",
|
|
||||||
nil,
|
|
||||||
[24]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes25",
|
|
||||||
nil,
|
|
||||||
[25]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes26",
|
|
||||||
nil,
|
|
||||||
[26]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes27",
|
|
||||||
nil,
|
|
||||||
[27]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes28",
|
|
||||||
nil,
|
|
||||||
[28]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes29",
|
|
||||||
nil,
|
|
||||||
[29]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes30",
|
|
||||||
nil,
|
|
||||||
[30]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes31",
|
|
||||||
nil,
|
|
||||||
[31]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes32",
|
|
||||||
nil,
|
|
||||||
[32]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uint32[2][3][4]",
|
|
||||||
nil,
|
|
||||||
[4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
|
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address[]",
|
|
||||||
nil,
|
|
||||||
[]common.Address{{1}, {2}},
|
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes32[]",
|
|
||||||
nil,
|
|
||||||
[]common.Hash{{1}, {2}},
|
|
||||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000201000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"function",
|
|
||||||
nil,
|
|
||||||
[24]byte{1},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"string",
|
|
||||||
nil,
|
|
||||||
"foobar",
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000006666f6f6261720000000000000000000000000000000000000000000000000000"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"string[]",
|
|
||||||
nil,
|
|
||||||
[]string{"hello", "foobar"},
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000080" + // offset 128 to i = 1
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
|
|
||||||
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
|
|
||||||
"666f6f6261720000000000000000000000000000000000000000000000000000"), // str[1]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"string[2]",
|
|
||||||
nil,
|
|
||||||
[]string{"hello", "foobar"},
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset to i = 0
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000080" + // offset to i = 1
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
|
|
||||||
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
|
|
||||||
"666f6f6261720000000000000000000000000000000000000000000000000000"), // str[1]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bytes32[][]",
|
|
||||||
nil,
|
|
||||||
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
|
|
||||||
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
|
||||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
|
|
||||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
|
||||||
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
|
||||||
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"bytes32[][2]",
|
|
||||||
nil,
|
|
||||||
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
|
|
||||||
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
|
||||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
|
|
||||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
|
||||||
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
|
||||||
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"bytes32[3][2]",
|
|
||||||
nil,
|
|
||||||
[][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}},
|
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
|
||||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
|
||||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[0][2]
|
|
||||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
|
||||||
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
|
||||||
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// static tuple
|
|
||||||
"tuple",
|
|
||||||
[]ArgumentMarshaling{
|
|
||||||
{Name: "a", Type: "int64"},
|
|
||||||
{Name: "b", Type: "int256"},
|
|
||||||
{Name: "c", Type: "int256"},
|
|
||||||
{Name: "d", Type: "bool"},
|
|
||||||
{Name: "e", Type: "bytes32[3][2]"},
|
|
||||||
},
|
|
||||||
struct {
|
|
||||||
A int64
|
|
||||||
B *big.Int
|
|
||||||
C *big.Int
|
|
||||||
D bool
|
|
||||||
E [][]common.Hash
|
|
||||||
}{1, big.NewInt(1), big.NewInt(-1), true, [][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}}},
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001" + // struct[a]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // struct[c]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[d]
|
|
||||||
"0100000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][0]
|
|
||||||
"0200000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][1]
|
|
||||||
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][2]
|
|
||||||
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][0]
|
|
||||||
"0400000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][1]
|
|
||||||
"0500000000000000000000000000000000000000000000000000000000000000"), // struct[e] array[1][2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// dynamic tuple
|
|
||||||
"tuple",
|
|
||||||
[]ArgumentMarshaling{
|
|
||||||
{Name: "a", Type: "string"},
|
|
||||||
{Name: "b", Type: "int64"},
|
|
||||||
{Name: "c", Type: "bytes"},
|
|
||||||
{Name: "d", Type: "string[]"},
|
|
||||||
{Name: "e", Type: "int256[]"},
|
|
||||||
{Name: "f", Type: "address[]"},
|
|
||||||
},
|
|
||||||
struct {
|
|
||||||
FieldA string `abi:"a"` // Test whether abi tag works
|
|
||||||
FieldB int64 `abi:"b"`
|
|
||||||
C []byte
|
|
||||||
D []string
|
|
||||||
E []*big.Int
|
|
||||||
F []common.Address
|
|
||||||
}{"foobar", 1, []byte{1}, []string{"foo", "bar"}, []*big.Int{big.NewInt(1), big.NewInt(-1)}, []common.Address{{1}, {2}}},
|
|
||||||
common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000c0" + // struct[a] offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000100" + // struct[c] offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000140" + // struct[d] offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000220" + // struct[e] offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000280" + // struct[f] offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000006" + // struct[a] length
|
|
||||||
"666f6f6261720000000000000000000000000000000000000000000000000000" + // struct[a] "foobar"
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[c] length
|
|
||||||
"0100000000000000000000000000000000000000000000000000000000000000" + // []byte{1}
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[d] length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // foo offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000080" + // bar offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" + // foo length
|
|
||||||
"666f6f0000000000000000000000000000000000000000000000000000000000" + // foo
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000003" + // bar offset
|
|
||||||
"6261720000000000000000000000000000000000000000000000000000000000" + // bar
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[e] length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // 1
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // -1
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[f] length
|
|
||||||
"0000000000000000000000000100000000000000000000000000000000000000" + // common.Address{1}
|
|
||||||
"0000000000000000000000000200000000000000000000000000000000000000"), // common.Address{2}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// nested tuple
|
|
||||||
"tuple",
|
|
||||||
[]ArgumentMarshaling{
|
|
||||||
{Name: "a", Type: "tuple", Components: []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256[]"}}},
|
|
||||||
{Name: "b", Type: "int256[]"},
|
|
||||||
},
|
|
||||||
struct {
|
|
||||||
A struct {
|
|
||||||
FieldA *big.Int `abi:"a"`
|
|
||||||
B []*big.Int
|
|
||||||
}
|
}
|
||||||
B []*big.Int
|
packed, err = inAbi.Pack("method", values...)
|
||||||
}{
|
}
|
||||||
A: struct {
|
|
||||||
FieldA *big.Int `abi:"a"` // Test whether abi tag works for nested tuple
|
|
||||||
B []*big.Int
|
|
||||||
}{big.NewInt(1), []*big.Int{big.NewInt(1), big.NewInt(0)}},
|
|
||||||
B: []*big.Int{big.NewInt(1), big.NewInt(0)}},
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // a offset
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000e0" + // b offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // a.a value
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // a.b offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // a.b length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // a.b[0] value
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000" + // a.b[1] value
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // b length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // b[0] value
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000"), // b[1] value
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// tuple slice
|
|
||||||
"tuple[]",
|
|
||||||
[]ArgumentMarshaling{
|
|
||||||
{Name: "a", Type: "int256"},
|
|
||||||
{Name: "b", Type: "int256[]"},
|
|
||||||
},
|
|
||||||
[]struct {
|
|
||||||
A *big.Int
|
|
||||||
B []*big.Int
|
|
||||||
}{
|
|
||||||
{big.NewInt(-1), []*big.Int{big.NewInt(1), big.NewInt(0)}},
|
|
||||||
{big.NewInt(1), []*big.Int{big.NewInt(2), big.NewInt(-1)}},
|
|
||||||
},
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // tuple length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000e0" + // tuple[1] offset
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0].B offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].B length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].B[0] value
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000" + // tuple[0].B[1] value
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[1].B offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B[0] value
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].B[1] value
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// static tuple array
|
|
||||||
"tuple[2]",
|
|
||||||
[]ArgumentMarshaling{
|
|
||||||
{Name: "a", Type: "int256"},
|
|
||||||
{Name: "b", Type: "int256"},
|
|
||||||
},
|
|
||||||
[2]struct {
|
|
||||||
A *big.Int
|
|
||||||
B *big.Int
|
|
||||||
}{
|
|
||||||
{big.NewInt(-1), big.NewInt(1)},
|
|
||||||
{big.NewInt(1), big.NewInt(-1)},
|
|
||||||
},
|
|
||||||
common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].a
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].b
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].a
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].b
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// dynamic tuple array
|
|
||||||
"tuple[2]",
|
|
||||||
[]ArgumentMarshaling{
|
|
||||||
{Name: "a", Type: "int256[]"},
|
|
||||||
},
|
|
||||||
[2]struct {
|
|
||||||
A []*big.Int
|
|
||||||
}{
|
|
||||||
{[]*big.Int{big.NewInt(-1), big.NewInt(1)}},
|
|
||||||
{[]*big.Int{big.NewInt(1), big.NewInt(-1)}},
|
|
||||||
},
|
|
||||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000c0" + // tuple[1] offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[0].A offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].A length
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A[0]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].A[1]
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[1].A offset
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].A length
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A[0]
|
|
||||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].A[1]
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
typ, err := NewType(test.typ, test.components)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%v failed. Unexpected parse error: %v", i, err)
|
|
||||||
}
|
|
||||||
output, err := typ.pack(reflect.ValueOf(test.input))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%v failed. Unexpected pack error: %v", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(output, test.output) {
|
if err != nil {
|
||||||
t.Errorf("input %d for typ: %v failed. Expected bytes: '%x' Got: '%x'", i, typ.String(), test.output, output)
|
t.Fatalf("test %d (%v) failed: %v", i, test.def, err)
|
||||||
}
|
}
|
||||||
|
if !reflect.DeepEqual(packed[4:], encb) {
|
||||||
|
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, encb, packed[4:])
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMethodPack(t *testing.T) {
|
func TestMethodPack(t *testing.T) {
|
||||||
abi, err := JSON(strings.NewReader(jsondata2))
|
abi, err := JSON(strings.NewReader(jsondata))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig := abi.Methods["slice"].Id()
|
sig := abi.Methods["slice"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
||||||
|
|
@ -648,7 +87,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var addrA, addrB = common.Address{1}, common.Address{2}
|
var addrA, addrB = common.Address{1}, common.Address{2}
|
||||||
sig = abi.Methods["sliceAddress"].Id()
|
sig = abi.Methods["sliceAddress"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
|
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
|
||||||
|
|
@ -663,7 +102,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var addrC, addrD = common.Address{3}, common.Address{4}
|
var addrC, addrD = common.Address{3}, common.Address{4}
|
||||||
sig = abi.Methods["sliceMultiAddress"].Id()
|
sig = abi.Methods["sliceMultiAddress"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
@ -681,7 +120,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = abi.Methods["slice256"].Id()
|
sig = abi.Methods["slice256"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
||||||
|
|
@ -695,7 +134,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}}
|
a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}}
|
||||||
sig = abi.Methods["nestedArray"].Id()
|
sig = abi.Methods["nestedArray"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
|
|
@ -712,7 +151,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = abi.Methods["nestedArray2"].Id()
|
sig = abi.Methods["nestedArray2"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x80}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x80}, 32)...)
|
||||||
|
|
@ -728,7 +167,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = abi.Methods["nestedSlice"].Id()
|
sig = abi.Methods["nestedSlice"].ID
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x02}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x02}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
|
||||||
|
|
|
||||||
988
accounts/abi/packing_test.go
Normal file
988
accounts/abi/packing_test.go
Normal file
|
|
@ -0,0 +1,988 @@
|
||||||
|
// Copyright 2020 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 (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/maticnetwork/bor/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type packUnpackTest struct {
|
||||||
|
def string
|
||||||
|
unpacked interface{}
|
||||||
|
packed string
|
||||||
|
}
|
||||||
|
|
||||||
|
var packUnpackTests = []packUnpackTest{
|
||||||
|
// Booleans
|
||||||
|
{
|
||||||
|
def: `[{ "type": "bool" }]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
unpacked: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{ "type": "bool" }]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
unpacked: false,
|
||||||
|
},
|
||||||
|
// Integers
|
||||||
|
{
|
||||||
|
def: `[{ "type": "uint8" }]`,
|
||||||
|
unpacked: uint8(2),
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{ "type": "uint8[]" }]`,
|
||||||
|
unpacked: []uint8{1, 2},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{ "type": "uint16" }]`,
|
||||||
|
unpacked: uint16(2),
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{ "type": "uint16[]" }]`,
|
||||||
|
unpacked: []uint16{1, 2},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint17"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
unpacked: big.NewInt(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint32"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
unpacked: uint32(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint32[]"}]`,
|
||||||
|
unpacked: []uint32{1, 2},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint64"}]`,
|
||||||
|
unpacked: uint64(2),
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint64[]"}]`,
|
||||||
|
unpacked: []uint64{1, 2},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint256"}]`,
|
||||||
|
unpacked: big.NewInt(2),
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint256[]"}]`,
|
||||||
|
unpacked: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int8"}]`,
|
||||||
|
unpacked: int8(2),
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int8[]"}]`,
|
||||||
|
unpacked: []int8{1, 2},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int16"}]`,
|
||||||
|
unpacked: int16(2),
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int16[]"}]`,
|
||||||
|
unpacked: []int16{1, 2},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int17"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
unpacked: big.NewInt(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int32"}]`,
|
||||||
|
unpacked: int32(2),
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int32"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
unpacked: int32(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int32[]"}]`,
|
||||||
|
unpacked: []int32{1, 2},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int64"}]`,
|
||||||
|
unpacked: int64(2),
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int64[]"}]`,
|
||||||
|
unpacked: []int64{1, 2},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int256"}]`,
|
||||||
|
unpacked: big.NewInt(2),
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int256"}]`,
|
||||||
|
packed: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
unpacked: big.NewInt(-1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int256[]"}]`,
|
||||||
|
unpacked: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
},
|
||||||
|
// Address
|
||||||
|
{
|
||||||
|
def: `[{"type": "address"}]`,
|
||||||
|
packed: "0000000000000000000000000100000000000000000000000000000000000000",
|
||||||
|
unpacked: common.Address{1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "address[]"}]`,
|
||||||
|
unpacked: []common.Address{{1}, {2}},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000100000000000000000000000000000000000000" +
|
||||||
|
"0000000000000000000000000200000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
// Bytes
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes1"}]`,
|
||||||
|
unpacked: [1]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes2"}]`,
|
||||||
|
unpacked: [2]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes3"}]`,
|
||||||
|
unpacked: [3]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes4"}]`,
|
||||||
|
unpacked: [4]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes5"}]`,
|
||||||
|
unpacked: [5]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes6"}]`,
|
||||||
|
unpacked: [6]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes7"}]`,
|
||||||
|
unpacked: [7]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes8"}]`,
|
||||||
|
unpacked: [8]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes9"}]`,
|
||||||
|
unpacked: [9]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes10"}]`,
|
||||||
|
unpacked: [10]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes11"}]`,
|
||||||
|
unpacked: [11]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes12"}]`,
|
||||||
|
unpacked: [12]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes13"}]`,
|
||||||
|
unpacked: [13]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes14"}]`,
|
||||||
|
unpacked: [14]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes15"}]`,
|
||||||
|
unpacked: [15]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes16"}]`,
|
||||||
|
unpacked: [16]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes17"}]`,
|
||||||
|
unpacked: [17]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes18"}]`,
|
||||||
|
unpacked: [18]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes19"}]`,
|
||||||
|
unpacked: [19]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes20"}]`,
|
||||||
|
unpacked: [20]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes21"}]`,
|
||||||
|
unpacked: [21]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes22"}]`,
|
||||||
|
unpacked: [22]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes23"}]`,
|
||||||
|
unpacked: [23]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes24"}]`,
|
||||||
|
unpacked: [24]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes25"}]`,
|
||||||
|
unpacked: [25]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes26"}]`,
|
||||||
|
unpacked: [26]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes27"}]`,
|
||||||
|
unpacked: [27]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes28"}]`,
|
||||||
|
unpacked: [28]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes29"}]`,
|
||||||
|
unpacked: [29]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes30"}]`,
|
||||||
|
unpacked: [30]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes31"}]`,
|
||||||
|
unpacked: [31]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes32"}]`,
|
||||||
|
unpacked: [32]byte{1},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes32"}]`,
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
unpacked: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
unpacked: common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes32"}]`,
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
unpacked: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
},
|
||||||
|
// Functions
|
||||||
|
{
|
||||||
|
def: `[{"type": "function"}]`,
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
unpacked: [24]byte{1},
|
||||||
|
},
|
||||||
|
// Slice and Array
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: []uint8{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
unpacked: []uint8{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint256[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
unpacked: []*big.Int{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[2]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: [2]uint8{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int8[2]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: [2]int8{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int16[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: []int16{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int16[2]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: [2]int16{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int32[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: []int32{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int32[2]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: [2]int32{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int64[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: []int64{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int64[2]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: [2]int64{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int256[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "int256[3]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003",
|
||||||
|
unpacked: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
|
||||||
|
},
|
||||||
|
// multi dimensional, if these pass, all types that don't require length prefix should pass
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[][]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
unpacked: [][]uint8{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[][]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" +
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000a0" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: [][]uint8{{1, 2}, {1, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[][]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" +
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000a0" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003",
|
||||||
|
unpacked: [][]uint8{{1, 2}, {1, 2, 3}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[2][2]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: [2][2]uint8{{1, 2}, {1, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[][2]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000060" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
unpacked: [2][]uint8{{}, {}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[][2]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000080" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
unpacked: [2][]uint8{{1}, {1}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[2][]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
unpacked: [][2]uint8{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[2][]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: [][2]uint8{{1, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint8[2][]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: [][2]uint8{{1, 2}, {1, 2}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint16[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: []uint16{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint16[2]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: [2]uint16{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint32[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: []uint32{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint32[2][3][4]"}]`,
|
||||||
|
unpacked: [4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000004" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000006" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000007" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000008" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000009" +
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000a" +
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000b" +
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000c" +
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000d" +
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000e" +
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000f" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000010" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000011" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000012" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000013" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000014" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000015" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000016" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000017" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000018",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes32[]"}]`,
|
||||||
|
unpacked: []common.Hash{{1}, {2}},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000" +
|
||||||
|
"0200000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint32[2]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: [2]uint32{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint64[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: []uint64{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint64[2]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: [2]uint64{1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint256[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint256[3]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003",
|
||||||
|
unpacked: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "string[4]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000080" +
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000c0" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000100" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000140" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"48656c6c6f000000000000000000000000000000000000000000000000000000" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" +
|
||||||
|
"576f726c64000000000000000000000000000000000000000000000000000000" +
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000b" +
|
||||||
|
"476f2d657468657265756d000000000000000000000000000000000000000000" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000008" +
|
||||||
|
"457468657265756d000000000000000000000000000000000000000000000000",
|
||||||
|
unpacked: [4]string{"Hello", "World", "Go-ethereum", "Ethereum"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "string[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000080" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000008" +
|
||||||
|
"457468657265756d000000000000000000000000000000000000000000000000" +
|
||||||
|
"000000000000000000000000000000000000000000000000000000000000000b" +
|
||||||
|
"676f2d657468657265756d000000000000000000000000000000000000000000",
|
||||||
|
unpacked: []string{"Ethereum", "go-ethereum"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes[]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000080" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" +
|
||||||
|
"f0f0f00000000000000000000000000000000000000000000000000000000000" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" +
|
||||||
|
"f0f0f00000000000000000000000000000000000000000000000000000000000",
|
||||||
|
unpacked: [][]byte{{0xf0, 0xf0, 0xf0}, {0xf0, 0xf0, 0xf0}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "uint256[2][][]"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" +
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000e0" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000c8" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000003e8" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000c8" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000003e8",
|
||||||
|
unpacked: [][][2]*big.Int{{{big.NewInt(1), big.NewInt(200)}, {big.NewInt(1), big.NewInt(1000)}}, {{big.NewInt(1), big.NewInt(200)}, {big.NewInt(1), big.NewInt(1000)}}},
|
||||||
|
},
|
||||||
|
// struct outputs
|
||||||
|
{
|
||||||
|
def: `[{"name":"int1","type":"int256"},{"name":"int2","type":"int256"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: struct {
|
||||||
|
Int1 *big.Int
|
||||||
|
Int2 *big.Int
|
||||||
|
}{big.NewInt(1), big.NewInt(2)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int_one","type":"int256"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
unpacked: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
}{big.NewInt(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int__one","type":"int256"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
unpacked: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
}{big.NewInt(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int_one_","type":"int256"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
unpacked: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
}{big.NewInt(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}]`,
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
unpacked: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
Intone *big.Int
|
||||||
|
}{big.NewInt(1), big.NewInt(2)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "string"}]`,
|
||||||
|
unpacked: "foobar",
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000006" +
|
||||||
|
"666f6f6261720000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "string[]"}]`,
|
||||||
|
unpacked: []string{"hello", "foobar"},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000080" + // offset 128 to i = 1
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
|
||||||
|
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
|
||||||
|
"666f6f6261720000000000000000000000000000000000000000000000000000", // str[1]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "string[2]"}]`,
|
||||||
|
unpacked: [2]string{"hello", "foobar"},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // offset to i = 0
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000080" + // offset to i = 1
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
|
||||||
|
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
|
||||||
|
"666f6f6261720000000000000000000000000000000000000000000000000000", // str[1]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes32[][]"}]`,
|
||||||
|
unpacked: [][][32]byte{{{1}, {2}}, {{3}, {4}, {5}}},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||||
|
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
||||||
|
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
||||||
|
"0500000000000000000000000000000000000000000000000000000000000000", // array[1][2]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes32[][2]"}]`,
|
||||||
|
unpacked: [2][][32]byte{{{1}, {2}}, {{3}, {4}, {5}}},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||||
|
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
||||||
|
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
||||||
|
"0500000000000000000000000000000000000000000000000000000000000000", // array[1][2]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type": "bytes32[3][2]"}]`,
|
||||||
|
unpacked: [2][3][32]byte{{{1}, {2}, {3}}, {{3}, {4}, {5}}},
|
||||||
|
packed: "0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||||
|
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000" + // array[0][2]
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
||||||
|
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
||||||
|
"0500000000000000000000000000000000000000000000000000000000000000", // array[1][2]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// static tuple
|
||||||
|
def: `[{"name":"a","type":"int64"},
|
||||||
|
{"name":"b","type":"int256"},
|
||||||
|
{"name":"c","type":"int256"},
|
||||||
|
{"name":"d","type":"bool"},
|
||||||
|
{"name":"e","type":"bytes32[3][2]"}]`,
|
||||||
|
unpacked: struct {
|
||||||
|
A int64
|
||||||
|
B *big.Int
|
||||||
|
C *big.Int
|
||||||
|
D bool
|
||||||
|
E [2][3][32]byte
|
||||||
|
}{1, big.NewInt(1), big.NewInt(-1), true, [2][3][32]byte{{{1}, {2}, {3}}, {{3}, {4}, {5}}}},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000001" + // struct[a]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // struct[c]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[d]
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][0]
|
||||||
|
"0200000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][1]
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][2]
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][0]
|
||||||
|
"0400000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][1]
|
||||||
|
"0500000000000000000000000000000000000000000000000000000000000000", // struct[e] array[1][2]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"a","type":"string"},
|
||||||
|
{"name":"b","type":"int64"},
|
||||||
|
{"name":"c","type":"bytes"},
|
||||||
|
{"name":"d","type":"string[]"},
|
||||||
|
{"name":"e","type":"int256[]"},
|
||||||
|
{"name":"f","type":"address[]"}]`,
|
||||||
|
unpacked: struct {
|
||||||
|
FieldA string `abi:"a"` // Test whether abi tag works
|
||||||
|
FieldB int64 `abi:"b"`
|
||||||
|
C []byte
|
||||||
|
D []string
|
||||||
|
E []*big.Int
|
||||||
|
F []common.Address
|
||||||
|
}{"foobar", 1, []byte{1}, []string{"foo", "bar"}, []*big.Int{big.NewInt(1), big.NewInt(-1)}, []common.Address{{1}, {2}}},
|
||||||
|
packed: "00000000000000000000000000000000000000000000000000000000000000c0" + // struct[a] offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000100" + // struct[c] offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000140" + // struct[d] offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000220" + // struct[e] offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000280" + // struct[f] offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000006" + // struct[a] length
|
||||||
|
"666f6f6261720000000000000000000000000000000000000000000000000000" + // struct[a] "foobar"
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[c] length
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000" + // []byte{1}
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[d] length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // foo offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000080" + // bar offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" + // foo length
|
||||||
|
"666f6f0000000000000000000000000000000000000000000000000000000000" + // foo
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" + // bar offset
|
||||||
|
"6261720000000000000000000000000000000000000000000000000000000000" + // bar
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[e] length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // 1
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // -1
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[f] length
|
||||||
|
"0000000000000000000000000100000000000000000000000000000000000000" + // common.Address{1}
|
||||||
|
"0000000000000000000000000200000000000000000000000000000000000000", // common.Address{2}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"components": [{"name": "a","type": "uint256"},
|
||||||
|
{"name": "b","type": "uint256[]"}],
|
||||||
|
"name": "a","type": "tuple"},
|
||||||
|
{"name": "b","type": "uint256[]"}]`,
|
||||||
|
unpacked: struct {
|
||||||
|
A struct {
|
||||||
|
FieldA *big.Int `abi:"a"`
|
||||||
|
B []*big.Int
|
||||||
|
}
|
||||||
|
B []*big.Int
|
||||||
|
}{
|
||||||
|
A: struct {
|
||||||
|
FieldA *big.Int `abi:"a"` // Test whether abi tag works for nested tuple
|
||||||
|
B []*big.Int
|
||||||
|
}{big.NewInt(1), []*big.Int{big.NewInt(1), big.NewInt(2)}},
|
||||||
|
B: []*big.Int{big.NewInt(1), big.NewInt(2)}},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000040" + // a offset
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000e0" + // b offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // a.a value
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // a.b offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // a.b length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // a.b[0] value
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // a.b[1] value
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // b length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // b[0] value
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002", // b[1] value
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
def: `[{"components": [{"name": "a","type": "int256"},
|
||||||
|
{"name": "b","type": "int256[]"}],
|
||||||
|
"name": "a","type": "tuple[]"}]`,
|
||||||
|
unpacked: []struct {
|
||||||
|
A *big.Int
|
||||||
|
B []*big.Int
|
||||||
|
}{
|
||||||
|
{big.NewInt(-1), []*big.Int{big.NewInt(1), big.NewInt(3)}},
|
||||||
|
{big.NewInt(1), []*big.Int{big.NewInt(2), big.NewInt(-1)}},
|
||||||
|
},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000e0" + // tuple[1] offset
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0].B offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].B length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].B[0] value
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000003" + // tuple[0].B[1] value
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[1].B offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B[0] value
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // tuple[1].B[1] value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"components": [{"name": "a","type": "int256"},
|
||||||
|
{"name": "b","type": "int256"}],
|
||||||
|
"name": "a","type": "tuple[2]"}]`,
|
||||||
|
unpacked: [2]struct {
|
||||||
|
A *big.Int
|
||||||
|
B *big.Int
|
||||||
|
}{
|
||||||
|
{big.NewInt(-1), big.NewInt(1)},
|
||||||
|
{big.NewInt(1), big.NewInt(-1)},
|
||||||
|
},
|
||||||
|
packed: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].a
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].b
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].a
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // tuple[1].b
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"components": [{"name": "a","type": "int256[]"}],
|
||||||
|
"name": "a","type": "tuple[2]"}]`,
|
||||||
|
unpacked: [2]struct {
|
||||||
|
A []*big.Int
|
||||||
|
}{
|
||||||
|
{[]*big.Int{big.NewInt(-1), big.NewInt(1)}},
|
||||||
|
{[]*big.Int{big.NewInt(1), big.NewInt(-1)}},
|
||||||
|
},
|
||||||
|
packed: "0000000000000000000000000000000000000000000000000000000000000020" +
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000c0" + // tuple[1] offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[0].A offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].A length
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A[0]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].A[1]
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[1].A offset
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].A length
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A[0]
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // tuple[1].A[1]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,9 @@
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
@ -25,46 +27,38 @@ import (
|
||||||
// indirect recursively dereferences the value until it either gets the value
|
// indirect recursively dereferences the value until it either gets the value
|
||||||
// or finds a big.Int
|
// or finds a big.Int
|
||||||
func indirect(v reflect.Value) reflect.Value {
|
func indirect(v reflect.Value) reflect.Value {
|
||||||
if v.Kind() == reflect.Ptr && v.Elem().Type() != derefbigT {
|
if v.Kind() == reflect.Ptr && v.Elem().Type() != reflect.TypeOf(big.Int{}) {
|
||||||
return indirect(v.Elem())
|
return indirect(v.Elem())
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// indirectInterfaceOrPtr recursively dereferences the value until value is not interface.
|
// reflectIntType returns the reflect using the given size and
|
||||||
func indirectInterfaceOrPtr(v reflect.Value) reflect.Value {
|
|
||||||
if (v.Kind() == reflect.Interface || v.Kind() == reflect.Ptr) && v.Elem().IsValid() {
|
|
||||||
return indirect(v.Elem())
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// reflectIntKind returns the reflect using the given size and
|
|
||||||
// unsignedness.
|
// unsignedness.
|
||||||
func reflectIntKindAndType(unsigned bool, size int) (reflect.Kind, reflect.Type) {
|
func reflectIntType(unsigned bool, size int) reflect.Type {
|
||||||
|
if unsigned {
|
||||||
|
switch size {
|
||||||
|
case 8:
|
||||||
|
return reflect.TypeOf(uint8(0))
|
||||||
|
case 16:
|
||||||
|
return reflect.TypeOf(uint16(0))
|
||||||
|
case 32:
|
||||||
|
return reflect.TypeOf(uint32(0))
|
||||||
|
case 64:
|
||||||
|
return reflect.TypeOf(uint64(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
switch size {
|
switch size {
|
||||||
case 8:
|
case 8:
|
||||||
if unsigned {
|
return reflect.TypeOf(int8(0))
|
||||||
return reflect.Uint8, uint8T
|
|
||||||
}
|
|
||||||
return reflect.Int8, int8T
|
|
||||||
case 16:
|
case 16:
|
||||||
if unsigned {
|
return reflect.TypeOf(int16(0))
|
||||||
return reflect.Uint16, uint16T
|
|
||||||
}
|
|
||||||
return reflect.Int16, int16T
|
|
||||||
case 32:
|
case 32:
|
||||||
if unsigned {
|
return reflect.TypeOf(int32(0))
|
||||||
return reflect.Uint32, uint32T
|
|
||||||
}
|
|
||||||
return reflect.Int32, int32T
|
|
||||||
case 64:
|
case 64:
|
||||||
if unsigned {
|
return reflect.TypeOf(int64(0))
|
||||||
return reflect.Uint64, uint64T
|
|
||||||
}
|
|
||||||
return reflect.Int64, int64T
|
|
||||||
}
|
}
|
||||||
return reflect.Ptr, bigT
|
return reflect.TypeOf(&big.Int{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// mustArrayToBytesSlice creates a new byte slice with the exact same size as value
|
// mustArrayToBytesSlice creates a new byte slice with the exact same size as value
|
||||||
|
|
@ -84,12 +78,16 @@ func set(dst, src reflect.Value) error {
|
||||||
switch {
|
switch {
|
||||||
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid():
|
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid():
|
||||||
return set(dst.Elem(), src)
|
return set(dst.Elem(), src)
|
||||||
case dstType.Kind() == reflect.Ptr && dstType.Elem() != derefbigT:
|
case dstType.Kind() == reflect.Ptr && dstType.Elem() != reflect.TypeOf(big.Int{}):
|
||||||
return set(dst.Elem(), src)
|
return set(dst.Elem(), src)
|
||||||
case srcType.AssignableTo(dstType) && dst.CanSet():
|
case srcType.AssignableTo(dstType) && dst.CanSet():
|
||||||
dst.Set(src)
|
dst.Set(src)
|
||||||
case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice:
|
case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice && dst.CanSet():
|
||||||
return setSlice(dst, src)
|
return setSlice(dst, src)
|
||||||
|
case dstType.Kind() == reflect.Array:
|
||||||
|
return setArray(dst, src)
|
||||||
|
case dstType.Kind() == reflect.Struct:
|
||||||
|
return setStruct(dst, src)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
|
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
|
||||||
}
|
}
|
||||||
|
|
@ -98,38 +96,56 @@ func set(dst, src reflect.Value) error {
|
||||||
|
|
||||||
// setSlice attempts to assign src to dst when slices are not assignable by default
|
// setSlice attempts to assign src to dst when slices are not assignable by default
|
||||||
// e.g. src: [][]byte -> dst: [][15]byte
|
// e.g. src: [][]byte -> dst: [][15]byte
|
||||||
|
// setSlice ignores if we cannot copy all of src' elements.
|
||||||
func setSlice(dst, src reflect.Value) error {
|
func setSlice(dst, src reflect.Value) error {
|
||||||
slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
|
slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
|
||||||
for i := 0; i < src.Len(); i++ {
|
for i := 0; i < src.Len(); i++ {
|
||||||
v := src.Index(i)
|
if src.Index(i).Kind() == reflect.Struct {
|
||||||
reflect.Copy(slice.Index(i), v)
|
if err := set(slice.Index(i), src.Index(i)); err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
dst.Set(slice)
|
} else {
|
||||||
return nil
|
// e.g. [][32]uint8 to []common.Hash
|
||||||
}
|
if err := set(slice.Index(i), src.Index(i)); err != nil {
|
||||||
|
return err
|
||||||
// requireAssignable assures that `dest` is a pointer and it's not an interface.
|
}
|
||||||
func requireAssignable(dst, src reflect.Value) error {
|
}
|
||||||
if dst.Kind() != reflect.Ptr && dst.Kind() != reflect.Interface {
|
}
|
||||||
return fmt.Errorf("abi: cannot unmarshal %v into %v", src.Type(), dst.Type())
|
if dst.CanSet() {
|
||||||
}
|
dst.Set(slice)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return errors.New("Cannot set slice, destination not settable")
|
||||||
// requireUnpackKind verifies preconditions for unpacking `args` into `kind`
|
}
|
||||||
func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
|
|
||||||
args Arguments) error {
|
func setArray(dst, src reflect.Value) error {
|
||||||
|
array := reflect.New(dst.Type()).Elem()
|
||||||
switch k {
|
min := src.Len()
|
||||||
case reflect.Struct:
|
if src.Len() > dst.Len() {
|
||||||
case reflect.Slice, reflect.Array:
|
min = dst.Len()
|
||||||
if minLen := args.LengthNonIndexed(); v.Len() < minLen {
|
}
|
||||||
return fmt.Errorf("abi: insufficient number of elements in the list/array for unpack, want %d, got %d",
|
for i := 0; i < min; i++ {
|
||||||
minLen, v.Len())
|
if err := set(array.Index(i), src.Index(i)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dst.CanSet() {
|
||||||
|
dst.Set(array)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("Cannot set array, destination not settable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func setStruct(dst, src reflect.Value) error {
|
||||||
|
for i := 0; i < src.NumField(); i++ {
|
||||||
|
srcField := src.Field(i)
|
||||||
|
dstField := dst.Field(i)
|
||||||
|
if !dstField.IsValid() || !srcField.IsValid() {
|
||||||
|
return fmt.Errorf("Could not find src field: %v value: %v in destination", srcField.Type().Name(), srcField)
|
||||||
|
}
|
||||||
|
if err := set(dstField, srcField); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
return fmt.Errorf("abi: cannot unmarshal tuple into %v", t)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -156,9 +172,8 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// skip fields that have no abi:"" tag.
|
// skip fields that have no abi:"" tag.
|
||||||
var ok bool
|
tagName, ok := typ.Field(i).Tag.Lookup("abi")
|
||||||
var tagName string
|
if !ok {
|
||||||
if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// check if tag is empty.
|
// check if tag is empty.
|
||||||
|
|
|
||||||
173
accounts/abi/topics.go
Normal file
173
accounts/abi/topics.go
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
// Copyright 2018 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"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/maticnetwork/bor/common"
|
||||||
|
"github.com/maticnetwork/bor/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MakeTopics converts a filter query argument list into a filter topic set.
|
||||||
|
func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
||||||
|
topics := make([][]common.Hash, len(query))
|
||||||
|
for i, filter := range query {
|
||||||
|
for _, rule := range filter {
|
||||||
|
var topic common.Hash
|
||||||
|
|
||||||
|
// Try to generate the topic based on simple types
|
||||||
|
switch rule := rule.(type) {
|
||||||
|
case common.Hash:
|
||||||
|
copy(topic[:], rule[:])
|
||||||
|
case common.Address:
|
||||||
|
copy(topic[common.HashLength-common.AddressLength:], rule[:])
|
||||||
|
case *big.Int:
|
||||||
|
blob := rule.Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case bool:
|
||||||
|
if rule {
|
||||||
|
topic[common.HashLength-1] = 1
|
||||||
|
}
|
||||||
|
case int8:
|
||||||
|
copy(topic[:], genIntType(int64(rule), 1))
|
||||||
|
case int16:
|
||||||
|
copy(topic[:], genIntType(int64(rule), 2))
|
||||||
|
case int32:
|
||||||
|
copy(topic[:], genIntType(int64(rule), 4))
|
||||||
|
case int64:
|
||||||
|
copy(topic[:], genIntType(rule, 8))
|
||||||
|
case uint8:
|
||||||
|
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case uint16:
|
||||||
|
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case uint32:
|
||||||
|
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case uint64:
|
||||||
|
blob := new(big.Int).SetUint64(rule).Bytes()
|
||||||
|
copy(topic[common.HashLength-len(blob):], blob)
|
||||||
|
case string:
|
||||||
|
hash := crypto.Keccak256Hash([]byte(rule))
|
||||||
|
copy(topic[:], hash[:])
|
||||||
|
case []byte:
|
||||||
|
hash := crypto.Keccak256Hash(rule)
|
||||||
|
copy(topic[:], hash[:])
|
||||||
|
|
||||||
|
default:
|
||||||
|
// todo(rjl493456442) according solidity documentation, indexed event
|
||||||
|
// parameters that are not value types i.e. arrays and structs are not
|
||||||
|
// stored directly but instead a keccak256-hash of an encoding is stored.
|
||||||
|
//
|
||||||
|
// We only convert stringS and bytes to hash, still need to deal with
|
||||||
|
// array(both fixed-size and dynamic-size) and struct.
|
||||||
|
|
||||||
|
// Attempt to generate the topic from funky types
|
||||||
|
val := reflect.ValueOf(rule)
|
||||||
|
switch {
|
||||||
|
// static byte array
|
||||||
|
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
|
||||||
|
reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported indexed type: %T", rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
topics[i] = append(topics[i], topic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return topics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func genIntType(rule int64, size uint) []byte {
|
||||||
|
var topic [common.HashLength]byte
|
||||||
|
if rule < 0 {
|
||||||
|
// if a rule is negative, we need to put it into two's complement.
|
||||||
|
// extended to common.Hashlength bytes.
|
||||||
|
topic = [common.HashLength]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}
|
||||||
|
}
|
||||||
|
for i := uint(0); i < size; i++ {
|
||||||
|
topic[common.HashLength-i-1] = byte(rule >> (i * 8))
|
||||||
|
}
|
||||||
|
return topic[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseTopics converts the indexed topic fields into actual log field values.
|
||||||
|
func ParseTopics(out interface{}, fields Arguments, topics []common.Hash) error {
|
||||||
|
return parseTopicWithSetter(fields, topics,
|
||||||
|
func(arg Argument, reconstr interface{}) {
|
||||||
|
field := reflect.ValueOf(out).Elem().FieldByName(ToCamelCase(arg.Name))
|
||||||
|
field.Set(reflect.ValueOf(reconstr))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs
|
||||||
|
func ParseTopicsIntoMap(out map[string]interface{}, fields Arguments, topics []common.Hash) error {
|
||||||
|
return parseTopicWithSetter(fields, topics,
|
||||||
|
func(arg Argument, reconstr interface{}) {
|
||||||
|
out[arg.Name] = reconstr
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseTopicWithSetter converts the indexed topic field-value pairs and stores them using the
|
||||||
|
// provided set function.
|
||||||
|
//
|
||||||
|
// Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
|
||||||
|
// hashes as the topic value!
|
||||||
|
func parseTopicWithSetter(fields Arguments, topics []common.Hash, setter func(Argument, interface{})) error {
|
||||||
|
// Sanity check that the fields and topics match up
|
||||||
|
if len(fields) != len(topics) {
|
||||||
|
return errors.New("topic/field count mismatch")
|
||||||
|
}
|
||||||
|
// Iterate over all the fields and reconstruct them from topics
|
||||||
|
for i, arg := range fields {
|
||||||
|
if !arg.Indexed {
|
||||||
|
return errors.New("non-indexed field in topic reconstruction")
|
||||||
|
}
|
||||||
|
var reconstr interface{}
|
||||||
|
switch arg.Type.T {
|
||||||
|
case TupleTy:
|
||||||
|
return errors.New("tuple type in topic reconstruction")
|
||||||
|
case StringTy, BytesTy, SliceTy, ArrayTy:
|
||||||
|
// Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash
|
||||||
|
// whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash
|
||||||
|
reconstr = topics[i]
|
||||||
|
case FunctionTy:
|
||||||
|
if garbage := binary.BigEndian.Uint64(topics[i][0:8]); garbage != 0 {
|
||||||
|
return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[i].Bytes())
|
||||||
|
}
|
||||||
|
var tmp [24]byte
|
||||||
|
copy(tmp[:], topics[i][8:32])
|
||||||
|
reconstr = tmp
|
||||||
|
default:
|
||||||
|
var err error
|
||||||
|
reconstr, err = toGoType(0, arg.Type, topics[i].Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Use the setter function to store the value
|
||||||
|
setter(arg, reconstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
381
accounts/abi/topics_test.go
Normal file
381
accounts/abi/topics_test.go
Normal file
|
|
@ -0,0 +1,381 @@
|
||||||
|
// Copyright 2019 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 (
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/maticnetwork/bor/common"
|
||||||
|
"github.com/maticnetwork/bor/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMakeTopics(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
query [][]interface{}
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want [][]common.Hash
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"support fixed byte types, right padded to 32 bytes",
|
||||||
|
args{[][]interface{}{{[5]byte{1, 2, 3, 4, 5}}}},
|
||||||
|
[][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"support common hash types in topics",
|
||||||
|
args{[][]interface{}{{common.Hash{1, 2, 3, 4, 5}}}},
|
||||||
|
[][]common.Hash{{common.Hash{1, 2, 3, 4, 5}}},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"support address types in topics",
|
||||||
|
args{[][]interface{}{{common.Address{1, 2, 3, 4, 5}}}},
|
||||||
|
[][]common.Hash{{common.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5}}},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"support *big.Int types in topics",
|
||||||
|
args{[][]interface{}{{big.NewInt(1).Lsh(big.NewInt(2), 254)}}},
|
||||||
|
[][]common.Hash{{common.Hash{128}}},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"support boolean types in topics",
|
||||||
|
args{[][]interface{}{
|
||||||
|
{true},
|
||||||
|
{false},
|
||||||
|
}},
|
||||||
|
[][]common.Hash{
|
||||||
|
{common.Hash{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}},
|
||||||
|
{common.Hash{0}},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"support int/uint(8/16/32/64) types in topics",
|
||||||
|
args{[][]interface{}{
|
||||||
|
{int8(-2)},
|
||||||
|
{int16(-3)},
|
||||||
|
{int32(-4)},
|
||||||
|
{int64(-5)},
|
||||||
|
{int8(1)},
|
||||||
|
{int16(256)},
|
||||||
|
{int32(65536)},
|
||||||
|
{int64(4294967296)},
|
||||||
|
{uint8(1)},
|
||||||
|
{uint16(256)},
|
||||||
|
{uint32(65536)},
|
||||||
|
{uint64(4294967296)},
|
||||||
|
}},
|
||||||
|
[][]common.Hash{
|
||||||
|
{common.Hash{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, 254}},
|
||||||
|
{common.Hash{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, 253}},
|
||||||
|
{common.Hash{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, 252}},
|
||||||
|
{common.Hash{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, 251}},
|
||||||
|
{common.Hash{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}},
|
||||||
|
{common.Hash{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, 0}},
|
||||||
|
{common.Hash{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, 0, 0}},
|
||||||
|
{common.Hash{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, 0, 0, 0, 0}},
|
||||||
|
{common.Hash{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}},
|
||||||
|
{common.Hash{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, 0}},
|
||||||
|
{common.Hash{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, 0, 0}},
|
||||||
|
{common.Hash{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, 0, 0, 0, 0}},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"support string types in topics",
|
||||||
|
args{[][]interface{}{{"hello world"}}},
|
||||||
|
[][]common.Hash{{crypto.Keccak256Hash([]byte("hello world"))}},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"support byte slice types in topics",
|
||||||
|
args{[][]interface{}{{[]byte{1, 2, 3}}}},
|
||||||
|
[][]common.Hash{{crypto.Keccak256Hash([]byte{1, 2, 3})}},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := MakeTopics(tt.args.query...)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("makeTopics() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("makeTopics() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
createObj func() interface{}
|
||||||
|
resultObj func() interface{}
|
||||||
|
resultMap func() map[string]interface{}
|
||||||
|
fields Arguments
|
||||||
|
topics []common.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
type bytesStruct struct {
|
||||||
|
StaticBytes [5]byte
|
||||||
|
}
|
||||||
|
type int8Struct struct {
|
||||||
|
Int8Value int8
|
||||||
|
}
|
||||||
|
type int256Struct struct {
|
||||||
|
Int256Value *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
type hashStruct struct {
|
||||||
|
HashValue common.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
type funcStruct struct {
|
||||||
|
FuncValue [24]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type topicTest struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTopicsTests() []topicTest {
|
||||||
|
bytesType, _ := NewType("bytes5", "", nil)
|
||||||
|
int8Type, _ := NewType("int8", "", nil)
|
||||||
|
int256Type, _ := NewType("int256", "", nil)
|
||||||
|
tupleType, _ := NewType("tuple(int256,int8)", "", nil)
|
||||||
|
stringType, _ := NewType("string", "", nil)
|
||||||
|
funcType, _ := NewType("function", "", nil)
|
||||||
|
|
||||||
|
tests := []topicTest{
|
||||||
|
{
|
||||||
|
name: "support fixed byte types, right padded to 32 bytes",
|
||||||
|
args: args{
|
||||||
|
createObj: func() interface{} { return &bytesStruct{} },
|
||||||
|
resultObj: func() interface{} { return &bytesStruct{StaticBytes: [5]byte{1, 2, 3, 4, 5}} },
|
||||||
|
resultMap: func() map[string]interface{} {
|
||||||
|
return map[string]interface{}{"staticBytes": [5]byte{1, 2, 3, 4, 5}}
|
||||||
|
},
|
||||||
|
fields: Arguments{Argument{
|
||||||
|
Name: "staticBytes",
|
||||||
|
Type: bytesType,
|
||||||
|
Indexed: true,
|
||||||
|
}},
|
||||||
|
topics: []common.Hash{
|
||||||
|
{1, 2, 3, 4, 5},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "int8 with negative value",
|
||||||
|
args: args{
|
||||||
|
createObj: func() interface{} { return &int8Struct{} },
|
||||||
|
resultObj: func() interface{} { return &int8Struct{Int8Value: -1} },
|
||||||
|
resultMap: func() map[string]interface{} {
|
||||||
|
return map[string]interface{}{"int8Value": int8(-1)}
|
||||||
|
},
|
||||||
|
fields: Arguments{Argument{
|
||||||
|
Name: "int8Value",
|
||||||
|
Type: int8Type,
|
||||||
|
Indexed: true,
|
||||||
|
}},
|
||||||
|
topics: []common.Hash{
|
||||||
|
{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},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "int256 with negative value",
|
||||||
|
args: args{
|
||||||
|
createObj: func() interface{} { return &int256Struct{} },
|
||||||
|
resultObj: func() interface{} { return &int256Struct{Int256Value: big.NewInt(-1)} },
|
||||||
|
resultMap: func() map[string]interface{} {
|
||||||
|
return map[string]interface{}{"int256Value": big.NewInt(-1)}
|
||||||
|
},
|
||||||
|
fields: Arguments{Argument{
|
||||||
|
Name: "int256Value",
|
||||||
|
Type: int256Type,
|
||||||
|
Indexed: true,
|
||||||
|
}},
|
||||||
|
topics: []common.Hash{
|
||||||
|
{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},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hash type",
|
||||||
|
args: args{
|
||||||
|
createObj: func() interface{} { return &hashStruct{} },
|
||||||
|
resultObj: func() interface{} { return &hashStruct{crypto.Keccak256Hash([]byte("stringtopic"))} },
|
||||||
|
resultMap: func() map[string]interface{} {
|
||||||
|
return map[string]interface{}{"hashValue": crypto.Keccak256Hash([]byte("stringtopic"))}
|
||||||
|
},
|
||||||
|
fields: Arguments{Argument{
|
||||||
|
Name: "hashValue",
|
||||||
|
Type: stringType,
|
||||||
|
Indexed: true,
|
||||||
|
}},
|
||||||
|
topics: []common.Hash{
|
||||||
|
crypto.Keccak256Hash([]byte("stringtopic")),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "function type",
|
||||||
|
args: args{
|
||||||
|
createObj: func() interface{} { return &funcStruct{} },
|
||||||
|
resultObj: func() interface{} {
|
||||||
|
return &funcStruct{[24]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}}
|
||||||
|
},
|
||||||
|
resultMap: func() map[string]interface{} {
|
||||||
|
return map[string]interface{}{"funcValue": [24]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}}
|
||||||
|
},
|
||||||
|
fields: Arguments{Argument{
|
||||||
|
Name: "funcValue",
|
||||||
|
Type: funcType,
|
||||||
|
Indexed: true,
|
||||||
|
}},
|
||||||
|
topics: []common.Hash{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error on topic/field count mismatch",
|
||||||
|
args: args{
|
||||||
|
createObj: func() interface{} { return nil },
|
||||||
|
resultObj: func() interface{} { return nil },
|
||||||
|
resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
|
||||||
|
fields: Arguments{Argument{
|
||||||
|
Name: "tupletype",
|
||||||
|
Type: tupleType,
|
||||||
|
Indexed: true,
|
||||||
|
}},
|
||||||
|
topics: []common.Hash{},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error on unindexed arguments",
|
||||||
|
args: args{
|
||||||
|
createObj: func() interface{} { return &int256Struct{} },
|
||||||
|
resultObj: func() interface{} { return &int256Struct{} },
|
||||||
|
resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
|
||||||
|
fields: Arguments{Argument{
|
||||||
|
Name: "int256Value",
|
||||||
|
Type: int256Type,
|
||||||
|
Indexed: false,
|
||||||
|
}},
|
||||||
|
topics: []common.Hash{
|
||||||
|
{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},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error on tuple in topic reconstruction",
|
||||||
|
args: args{
|
||||||
|
createObj: func() interface{} { return &tupleType },
|
||||||
|
resultObj: func() interface{} { return &tupleType },
|
||||||
|
resultMap: func() map[string]interface{} { return make(map[string]interface{}) },
|
||||||
|
fields: Arguments{Argument{
|
||||||
|
Name: "tupletype",
|
||||||
|
Type: tupleType,
|
||||||
|
Indexed: true,
|
||||||
|
}},
|
||||||
|
topics: []common.Hash{{0}},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error on improper encoded function",
|
||||||
|
args: args{
|
||||||
|
createObj: func() interface{} { return &funcStruct{} },
|
||||||
|
resultObj: func() interface{} { return &funcStruct{} },
|
||||||
|
resultMap: func() map[string]interface{} {
|
||||||
|
return make(map[string]interface{})
|
||||||
|
},
|
||||||
|
fields: Arguments{Argument{
|
||||||
|
Name: "funcValue",
|
||||||
|
Type: funcType,
|
||||||
|
Indexed: true,
|
||||||
|
}},
|
||||||
|
topics: []common.Hash{
|
||||||
|
{0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return tests
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseTopics(t *testing.T) {
|
||||||
|
tests := setupTopicsTests()
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
createObj := tt.args.createObj()
|
||||||
|
if err := ParseTopics(createObj, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("parseTopics() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
resultObj := tt.args.resultObj()
|
||||||
|
if !reflect.DeepEqual(createObj, resultObj) {
|
||||||
|
t.Errorf("parseTopics() = %v, want %v", createObj, resultObj)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseTopicsIntoMap(t *testing.T) {
|
||||||
|
tests := setupTopicsTests()
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
outMap := make(map[string]interface{})
|
||||||
|
if err := ParseTopicsIntoMap(outMap, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("parseTopicsIntoMap() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
resultMap := tt.args.resultMap()
|
||||||
|
if !reflect.DeepEqual(outMap, resultMap) {
|
||||||
|
t.Errorf("parseTopicsIntoMap() = %v, want %v", outMap, resultMap)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,8 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/maticnetwork/bor/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Type enumerator
|
// Type enumerator
|
||||||
|
|
@ -45,16 +47,16 @@ const (
|
||||||
// Type is the reflection of the supported argument type
|
// Type is the reflection of the supported argument type
|
||||||
type Type struct {
|
type Type struct {
|
||||||
Elem *Type
|
Elem *Type
|
||||||
Kind reflect.Kind
|
|
||||||
Type reflect.Type
|
|
||||||
Size int
|
Size int
|
||||||
T byte // Our own type checking
|
T byte // Our own type checking
|
||||||
|
|
||||||
stringKind string // holds the unparsed string for deriving signatures
|
stringKind string // holds the unparsed string for deriving signatures
|
||||||
|
|
||||||
// Tuple relative fields
|
// Tuple relative fields
|
||||||
TupleElems []*Type // Type information of all tuple fields
|
TupleRawName string // Raw struct name defined in source code, may be empty.
|
||||||
TupleRawNames []string // Raw field name of all tuple fields
|
TupleElems []*Type // Type information of all tuple fields
|
||||||
|
TupleRawNames []string // Raw field name of all tuple fields
|
||||||
|
TupleType reflect.Type // Underlying struct of the tuple
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -63,7 +65,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewType creates a new reflection type of abi type given in t.
|
// NewType creates a new reflection type of abi type given in t.
|
||||||
func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
|
func NewType(t string, internalType string, components []ArgumentMarshaling) (typ Type, err error) {
|
||||||
// check that array brackets are equal if they exist
|
// check that array brackets are equal if they exist
|
||||||
if strings.Count(t, "[") != strings.Count(t, "]") {
|
if strings.Count(t, "[") != strings.Count(t, "]") {
|
||||||
return Type{}, fmt.Errorf("invalid arg type in abi")
|
return Type{}, fmt.Errorf("invalid arg type in abi")
|
||||||
|
|
@ -73,9 +75,14 @@ func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
|
||||||
// if there are brackets, get ready to go into slice/array mode and
|
// if there are brackets, get ready to go into slice/array mode and
|
||||||
// recursively create the type
|
// recursively create the type
|
||||||
if strings.Count(t, "[") != 0 {
|
if strings.Count(t, "[") != 0 {
|
||||||
i := strings.LastIndex(t, "[")
|
// Note internalType can be empty here.
|
||||||
|
subInternal := internalType
|
||||||
|
if i := strings.LastIndex(internalType, "["); i != -1 {
|
||||||
|
subInternal = subInternal[:i]
|
||||||
|
}
|
||||||
// recursively embed the type
|
// recursively embed the type
|
||||||
embeddedType, err := NewType(t[:i], components)
|
i := strings.LastIndex(t, "[")
|
||||||
|
embeddedType, err := NewType(t[:i], subInternal, components)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Type{}, err
|
return Type{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -88,20 +95,16 @@ func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
|
||||||
if len(intz) == 0 {
|
if len(intz) == 0 {
|
||||||
// is a slice
|
// is a slice
|
||||||
typ.T = SliceTy
|
typ.T = SliceTy
|
||||||
typ.Kind = reflect.Slice
|
|
||||||
typ.Elem = &embeddedType
|
typ.Elem = &embeddedType
|
||||||
typ.Type = reflect.SliceOf(embeddedType.Type)
|
|
||||||
typ.stringKind = embeddedType.stringKind + sliced
|
typ.stringKind = embeddedType.stringKind + sliced
|
||||||
} else if len(intz) == 1 {
|
} else if len(intz) == 1 {
|
||||||
// is a array
|
// is an array
|
||||||
typ.T = ArrayTy
|
typ.T = ArrayTy
|
||||||
typ.Kind = reflect.Array
|
|
||||||
typ.Elem = &embeddedType
|
typ.Elem = &embeddedType
|
||||||
typ.Size, err = strconv.Atoi(intz[0])
|
typ.Size, err = strconv.Atoi(intz[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
|
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
|
||||||
}
|
}
|
||||||
typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type)
|
|
||||||
typ.stringKind = embeddedType.stringKind + sliced
|
typ.stringKind = embeddedType.stringKind + sliced
|
||||||
} else {
|
} else {
|
||||||
return Type{}, fmt.Errorf("invalid formatting of array type")
|
return Type{}, fmt.Errorf("invalid formatting of array type")
|
||||||
|
|
@ -133,36 +136,24 @@ func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
|
||||||
// varType is the parsed abi type
|
// varType is the parsed abi type
|
||||||
switch varType := parsedType[1]; varType {
|
switch varType := parsedType[1]; varType {
|
||||||
case "int":
|
case "int":
|
||||||
typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
|
|
||||||
typ.Size = varSize
|
typ.Size = varSize
|
||||||
typ.T = IntTy
|
typ.T = IntTy
|
||||||
case "uint":
|
case "uint":
|
||||||
typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
|
|
||||||
typ.Size = varSize
|
typ.Size = varSize
|
||||||
typ.T = UintTy
|
typ.T = UintTy
|
||||||
case "bool":
|
case "bool":
|
||||||
typ.Kind = reflect.Bool
|
|
||||||
typ.T = BoolTy
|
typ.T = BoolTy
|
||||||
typ.Type = reflect.TypeOf(bool(false))
|
|
||||||
case "address":
|
case "address":
|
||||||
typ.Kind = reflect.Array
|
|
||||||
typ.Type = addressT
|
|
||||||
typ.Size = 20
|
typ.Size = 20
|
||||||
typ.T = AddressTy
|
typ.T = AddressTy
|
||||||
case "string":
|
case "string":
|
||||||
typ.Kind = reflect.String
|
|
||||||
typ.Type = reflect.TypeOf("")
|
|
||||||
typ.T = StringTy
|
typ.T = StringTy
|
||||||
case "bytes":
|
case "bytes":
|
||||||
if varSize == 0 {
|
if varSize == 0 {
|
||||||
typ.T = BytesTy
|
typ.T = BytesTy
|
||||||
typ.Kind = reflect.Slice
|
|
||||||
typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0)))
|
|
||||||
} else {
|
} else {
|
||||||
typ.T = FixedBytesTy
|
typ.T = FixedBytesTy
|
||||||
typ.Kind = reflect.Array
|
|
||||||
typ.Size = varSize
|
typ.Size = varSize
|
||||||
typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
|
|
||||||
}
|
}
|
||||||
case "tuple":
|
case "tuple":
|
||||||
var (
|
var (
|
||||||
|
|
@ -172,17 +163,20 @@ func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
|
||||||
expression string // canonical parameter expression
|
expression string // canonical parameter expression
|
||||||
)
|
)
|
||||||
expression += "("
|
expression += "("
|
||||||
|
overloadedNames := make(map[string]string)
|
||||||
for idx, c := range components {
|
for idx, c := range components {
|
||||||
cType, err := NewType(c.Type, c.Components)
|
cType, err := NewType(c.Type, c.InternalType, c.Components)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Type{}, err
|
return Type{}, err
|
||||||
}
|
}
|
||||||
if ToCamelCase(c.Name) == "" {
|
fieldName, err := overloadedArgName(c.Name, overloadedNames)
|
||||||
return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
|
if err != nil {
|
||||||
|
return Type{}, err
|
||||||
}
|
}
|
||||||
|
overloadedNames[fieldName] = fieldName
|
||||||
fields = append(fields, reflect.StructField{
|
fields = append(fields, reflect.StructField{
|
||||||
Name: ToCamelCase(c.Name), // reflect.StructOf will panic for any exported field.
|
Name: fieldName, // reflect.StructOf will panic for any exported field.
|
||||||
Type: cType.Type,
|
Type: cType.GetType(),
|
||||||
Tag: reflect.StructTag("json:\"" + c.Name + "\""),
|
Tag: reflect.StructTag("json:\"" + c.Name + "\""),
|
||||||
})
|
})
|
||||||
elems = append(elems, &cType)
|
elems = append(elems, &cType)
|
||||||
|
|
@ -193,17 +187,26 @@ func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expression += ")"
|
expression += ")"
|
||||||
typ.Kind = reflect.Struct
|
|
||||||
typ.Type = reflect.StructOf(fields)
|
typ.TupleType = reflect.StructOf(fields)
|
||||||
typ.TupleElems = elems
|
typ.TupleElems = elems
|
||||||
typ.TupleRawNames = names
|
typ.TupleRawNames = names
|
||||||
typ.T = TupleTy
|
typ.T = TupleTy
|
||||||
typ.stringKind = expression
|
typ.stringKind = expression
|
||||||
|
|
||||||
|
const structPrefix = "struct "
|
||||||
|
// After solidity 0.5.10, a new field of abi "internalType"
|
||||||
|
// is introduced. From that we can obtain the struct name
|
||||||
|
// user defined in the source code.
|
||||||
|
if internalType != "" && strings.HasPrefix(internalType, structPrefix) {
|
||||||
|
// Foo.Bar type definition is not allowed in golang,
|
||||||
|
// convert the format to FooBar
|
||||||
|
typ.TupleRawName = strings.Replace(internalType[len(structPrefix):], ".", "", -1)
|
||||||
|
}
|
||||||
|
|
||||||
case "function":
|
case "function":
|
||||||
typ.Kind = reflect.Array
|
|
||||||
typ.T = FunctionTy
|
typ.T = FunctionTy
|
||||||
typ.Size = 24
|
typ.Size = 24
|
||||||
typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
|
|
||||||
default:
|
default:
|
||||||
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
||||||
}
|
}
|
||||||
|
|
@ -211,6 +214,56 @@ func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetType returns the reflection type of the ABI type.
|
||||||
|
func (t Type) GetType() reflect.Type {
|
||||||
|
switch t.T {
|
||||||
|
case IntTy:
|
||||||
|
return reflectIntType(false, t.Size)
|
||||||
|
case UintTy:
|
||||||
|
return reflectIntType(true, t.Size)
|
||||||
|
case BoolTy:
|
||||||
|
return reflect.TypeOf(false)
|
||||||
|
case StringTy:
|
||||||
|
return reflect.TypeOf("")
|
||||||
|
case SliceTy:
|
||||||
|
return reflect.SliceOf(t.Elem.GetType())
|
||||||
|
case ArrayTy:
|
||||||
|
return reflect.ArrayOf(t.Size, t.Elem.GetType())
|
||||||
|
case TupleTy:
|
||||||
|
return t.TupleType
|
||||||
|
case AddressTy:
|
||||||
|
return reflect.TypeOf(common.Address{})
|
||||||
|
case FixedBytesTy:
|
||||||
|
return reflect.ArrayOf(t.Size, reflect.TypeOf(byte(0)))
|
||||||
|
case BytesTy:
|
||||||
|
return reflect.SliceOf(reflect.TypeOf(byte(0)))
|
||||||
|
case HashTy:
|
||||||
|
// hashtype currently not used
|
||||||
|
return reflect.ArrayOf(32, reflect.TypeOf(byte(0)))
|
||||||
|
case FixedPointTy:
|
||||||
|
// fixedpoint type currently not used
|
||||||
|
return reflect.ArrayOf(32, reflect.TypeOf(byte(0)))
|
||||||
|
case FunctionTy:
|
||||||
|
return reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
|
||||||
|
default:
|
||||||
|
panic("Invalid type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func overloadedArgName(rawName string, names map[string]string) (string, error) {
|
||||||
|
fieldName := ToCamelCase(rawName)
|
||||||
|
if fieldName == "" {
|
||||||
|
return "", errors.New("abi: purely anonymous or underscored field is not supported")
|
||||||
|
}
|
||||||
|
// Handle overloaded fieldNames
|
||||||
|
_, ok := names[fieldName]
|
||||||
|
for idx := 0; ok; idx++ {
|
||||||
|
fieldName = fmt.Sprintf("%s%d", ToCamelCase(rawName), idx)
|
||||||
|
_, ok = names[fieldName]
|
||||||
|
}
|
||||||
|
return fieldName, nil
|
||||||
|
}
|
||||||
|
|
||||||
// String implements Stringer
|
// String implements Stringer
|
||||||
func (t Type) String() (out string) {
|
func (t Type) String() (out string) {
|
||||||
return t.stringKind
|
return t.stringKind
|
||||||
|
|
|
||||||
|
|
@ -36,58 +36,58 @@ func TestTypeRegexp(t *testing.T) {
|
||||||
components []ArgumentMarshaling
|
components []ArgumentMarshaling
|
||||||
kind Type
|
kind Type
|
||||||
}{
|
}{
|
||||||
{"bool", nil, Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}},
|
{"bool", nil, Type{T: BoolTy, stringKind: "bool"}},
|
||||||
{"bool[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool(nil)), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}},
|
{"bool[]", nil, Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}},
|
||||||
{"bool[2]", nil, Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}},
|
{"bool[2]", nil, Type{Size: 2, T: ArrayTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}},
|
||||||
{"bool[2][]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}},
|
{"bool[2][]", nil, Type{T: SliceTy, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}},
|
||||||
{"bool[][]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}},
|
{"bool[][]", nil, Type{T: SliceTy, Elem: &Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}},
|
||||||
{"bool[][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}},
|
{"bool[][2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}},
|
||||||
{"bool[2][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}},
|
{"bool[2][2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}},
|
||||||
{"bool[2][][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][][2]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}, stringKind: "bool[2][][2]"}},
|
{"bool[2][][2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: SliceTy, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}, stringKind: "bool[2][][2]"}},
|
||||||
{"bool[2][2][2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}},
|
{"bool[2][2][2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}},
|
||||||
{"bool[][][]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}},
|
{"bool[][][]", nil, Type{T: SliceTy, Elem: &Type{T: SliceTy, Elem: &Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}},
|
||||||
{"bool[][2][]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][2][]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}},
|
{"bool[][2][]", nil, Type{T: SliceTy, Elem: &Type{T: ArrayTy, Size: 2, Elem: &Type{T: SliceTy, Elem: &Type{T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}},
|
||||||
{"int8", nil, Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}},
|
{"int8", nil, Type{Size: 8, T: IntTy, stringKind: "int8"}},
|
||||||
{"int16", nil, Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}},
|
{"int16", nil, Type{Size: 16, T: IntTy, stringKind: "int16"}},
|
||||||
{"int32", nil, Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}},
|
{"int32", nil, Type{Size: 32, T: IntTy, stringKind: "int32"}},
|
||||||
{"int64", nil, Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}},
|
{"int64", nil, Type{Size: 64, T: IntTy, stringKind: "int64"}},
|
||||||
{"int256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}},
|
{"int256", nil, Type{Size: 256, T: IntTy, stringKind: "int256"}},
|
||||||
{"int8[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
|
{"int8[]", nil, Type{T: SliceTy, Elem: &Type{Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
|
||||||
{"int8[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
|
{"int8[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
|
||||||
{"int16[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
|
{"int16[]", nil, Type{T: SliceTy, Elem: &Type{Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
|
||||||
{"int16[2]", nil, Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
|
{"int16[2]", nil, Type{Size: 2, T: ArrayTy, Elem: &Type{Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
|
||||||
{"int32[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
|
{"int32[]", nil, Type{T: SliceTy, Elem: &Type{Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
|
||||||
{"int32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
|
{"int32[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
|
||||||
{"int64[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
|
{"int64[]", nil, Type{T: SliceTy, Elem: &Type{Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
|
||||||
{"int64[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
|
{"int64[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
|
||||||
{"int256[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
|
{"int256[]", nil, Type{T: SliceTy, Elem: &Type{Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
|
||||||
{"int256[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
|
{"int256[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
|
||||||
{"uint8", nil, Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}},
|
{"uint8", nil, Type{Size: 8, T: UintTy, stringKind: "uint8"}},
|
||||||
{"uint16", nil, Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}},
|
{"uint16", nil, Type{Size: 16, T: UintTy, stringKind: "uint16"}},
|
||||||
{"uint32", nil, Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}},
|
{"uint32", nil, Type{Size: 32, T: UintTy, stringKind: "uint32"}},
|
||||||
{"uint64", nil, Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}},
|
{"uint64", nil, Type{Size: 64, T: UintTy, stringKind: "uint64"}},
|
||||||
{"uint256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}},
|
{"uint256", nil, Type{Size: 256, T: UintTy, stringKind: "uint256"}},
|
||||||
{"uint8[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
|
{"uint8[]", nil, Type{T: SliceTy, Elem: &Type{Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
|
||||||
{"uint8[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
|
{"uint8[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
|
||||||
{"uint16[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
|
{"uint16[]", nil, Type{T: SliceTy, Elem: &Type{Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
|
||||||
{"uint16[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
|
{"uint16[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
|
||||||
{"uint32[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
|
{"uint32[]", nil, Type{T: SliceTy, Elem: &Type{Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
|
||||||
{"uint32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
|
{"uint32[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
|
||||||
{"uint64[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
|
{"uint64[]", nil, Type{T: SliceTy, Elem: &Type{Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
|
||||||
{"uint64[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
|
{"uint64[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
|
||||||
{"uint256[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
|
{"uint256[]", nil, Type{T: SliceTy, Elem: &Type{Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
|
||||||
{"uint256[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
|
{"uint256[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
|
||||||
{"bytes32", nil, Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}},
|
{"bytes32", nil, Type{T: FixedBytesTy, Size: 32, stringKind: "bytes32"}},
|
||||||
{"bytes[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]byte{}), Elem: &Type{Kind: reflect.Slice, Type: reflect.TypeOf([]byte{}), T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
|
{"bytes[]", nil, Type{T: SliceTy, Elem: &Type{T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
|
||||||
{"bytes[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]byte{}), Elem: &Type{T: BytesTy, Type: reflect.TypeOf([]byte{}), Kind: reflect.Slice, stringKind: "bytes"}, stringKind: "bytes[2]"}},
|
{"bytes[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[2]"}},
|
||||||
{"bytes32[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][32]byte{}), Elem: &Type{Kind: reflect.Array, Type: reflect.TypeOf([32]byte{}), T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
|
{"bytes32[]", nil, Type{T: SliceTy, Elem: &Type{T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
|
||||||
{"bytes32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][32]byte{}), Elem: &Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
|
{"bytes32[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
|
||||||
{"string", nil, Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}},
|
{"string", nil, Type{T: StringTy, stringKind: "string"}},
|
||||||
{"string[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]string{}), Elem: &Type{Kind: reflect.String, Type: reflect.TypeOf(""), T: StringTy, stringKind: "string"}, stringKind: "string[]"}},
|
{"string[]", nil, Type{T: SliceTy, Elem: &Type{T: StringTy, stringKind: "string"}, stringKind: "string[]"}},
|
||||||
{"string[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]string{}), Elem: &Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}, stringKind: "string[2]"}},
|
{"string[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{T: StringTy, stringKind: "string"}, stringKind: "string[2]"}},
|
||||||
{"address", nil, Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}},
|
{"address", nil, Type{Size: 20, T: AddressTy, stringKind: "address"}},
|
||||||
{"address[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
|
{"address[]", nil, Type{T: SliceTy, Elem: &Type{Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
|
||||||
{"address[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
|
{"address[2]", nil, Type{T: ArrayTy, Size: 2, Elem: &Type{Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
|
||||||
// TODO when fixed types are implemented properly
|
// TODO when fixed types are implemented properly
|
||||||
// {"fixed", nil, Type{}},
|
// {"fixed", nil, Type{}},
|
||||||
// {"fixed128x128", nil, Type{}},
|
// {"fixed128x128", nil, Type{}},
|
||||||
|
|
@ -95,18 +95,18 @@ func TestTypeRegexp(t *testing.T) {
|
||||||
// {"fixed[2]", nil, Type{}},
|
// {"fixed[2]", nil, Type{}},
|
||||||
// {"fixed128x128[]", nil, Type{}},
|
// {"fixed128x128[]", nil, Type{}},
|
||||||
// {"fixed128x128[2]", nil, Type{}},
|
// {"fixed128x128[2]", nil, Type{}},
|
||||||
{"tuple", []ArgumentMarshaling{{Name: "a", Type: "int64"}}, Type{Kind: reflect.Struct, T: TupleTy, Type: reflect.TypeOf(struct {
|
{"tuple", []ArgumentMarshaling{{Name: "a", Type: "int64"}}, Type{T: TupleTy, TupleType: reflect.TypeOf(struct {
|
||||||
A int64 `json:"a"`
|
A int64 `json:"a"`
|
||||||
}{}), stringKind: "(int64)",
|
}{}), stringKind: "(int64)",
|
||||||
TupleElems: []*Type{{Kind: reflect.Int64, T: IntTy, Type: reflect.TypeOf(int64(0)), Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"a"}}},
|
TupleElems: []*Type{{T: IntTy, Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"a"}}},
|
||||||
{"tuple with long name", []ArgumentMarshaling{{Name: "aTypicalParamName", Type: "int64"}}, Type{Kind: reflect.Struct, T: TupleTy, Type: reflect.TypeOf(struct {
|
{"tuple with long name", []ArgumentMarshaling{{Name: "aTypicalParamName", Type: "int64"}}, Type{T: TupleTy, TupleType: reflect.TypeOf(struct {
|
||||||
ATypicalParamName int64 `json:"aTypicalParamName"`
|
ATypicalParamName int64 `json:"aTypicalParamName"`
|
||||||
}{}), stringKind: "(int64)",
|
}{}), stringKind: "(int64)",
|
||||||
TupleElems: []*Type{{Kind: reflect.Int64, T: IntTy, Type: reflect.TypeOf(int64(0)), Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"aTypicalParamName"}}},
|
TupleElems: []*Type{{T: IntTy, Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"aTypicalParamName"}}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
typ, err := NewType(tt.blob, tt.components)
|
typ, err := NewType(tt.blob, "", tt.components)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
|
t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
|
||||||
}
|
}
|
||||||
|
|
@ -281,7 +281,7 @@ func TestTypeCheck(t *testing.T) {
|
||||||
B *big.Int
|
B *big.Int
|
||||||
}{{big.NewInt(0), big.NewInt(0)}, {big.NewInt(0), big.NewInt(0)}}, ""},
|
}{{big.NewInt(0), big.NewInt(0)}, {big.NewInt(0), big.NewInt(0)}}, ""},
|
||||||
} {
|
} {
|
||||||
typ, err := NewType(test.typ, test.components)
|
typ, err := NewType(test.typ, "", test.components)
|
||||||
if err != nil && len(test.err) == 0 {
|
if err != nil && len(test.err) == 0 {
|
||||||
t.Fatal("unexpected parse error:", err)
|
t.Fatal("unexpected parse error:", err)
|
||||||
} else if err != nil && len(test.err) != 0 {
|
} else if err != nil && len(test.err) != 0 {
|
||||||
|
|
@ -306,3 +306,27 @@ func TestTypeCheck(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInternalType(t *testing.T) {
|
||||||
|
components := []ArgumentMarshaling{{Name: "a", Type: "int64"}}
|
||||||
|
internalType := "struct a.b[]"
|
||||||
|
kind := Type{
|
||||||
|
T: TupleTy,
|
||||||
|
TupleType: reflect.TypeOf(struct {
|
||||||
|
A int64 `json:"a"`
|
||||||
|
}{}),
|
||||||
|
stringKind: "(int64)",
|
||||||
|
TupleRawName: "ab[]",
|
||||||
|
TupleElems: []*Type{{T: IntTy, Size: 64, stringKind: "int64"}},
|
||||||
|
TupleRawNames: []string{"a"},
|
||||||
|
}
|
||||||
|
|
||||||
|
blob := "tuple"
|
||||||
|
typ, err := NewType(blob, internalType, components)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("type %q: failed to parse type string: %v", blob, err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(typ, kind) {
|
||||||
|
t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(kind)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,45 +26,47 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
maxUint256 = big.NewInt(0).Add(
|
// MaxUint256 is the maximum value that can be represented by a uint256
|
||||||
big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil),
|
MaxUint256 = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 256), common.Big1)
|
||||||
big.NewInt(-1))
|
// MaxInt256 is the maximum value that can be represented by a int256
|
||||||
maxInt256 = big.NewInt(0).Add(
|
MaxInt256 = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 255), common.Big1)
|
||||||
big.NewInt(0).Exp(big.NewInt(2), big.NewInt(255), nil),
|
|
||||||
big.NewInt(-1))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// reads the integer based on its kind
|
// ReadInteger reads the integer based on its kind and returns the appropriate value
|
||||||
func readInteger(typ byte, kind reflect.Kind, b []byte) interface{} {
|
func ReadInteger(typ Type, b []byte) interface{} {
|
||||||
switch kind {
|
if typ.T == UintTy {
|
||||||
case reflect.Uint8:
|
switch typ.Size {
|
||||||
return b[len(b)-1]
|
case 8:
|
||||||
case reflect.Uint16:
|
return b[len(b)-1]
|
||||||
return binary.BigEndian.Uint16(b[len(b)-2:])
|
case 16:
|
||||||
case reflect.Uint32:
|
return binary.BigEndian.Uint16(b[len(b)-2:])
|
||||||
return binary.BigEndian.Uint32(b[len(b)-4:])
|
case 32:
|
||||||
case reflect.Uint64:
|
return binary.BigEndian.Uint32(b[len(b)-4:])
|
||||||
return binary.BigEndian.Uint64(b[len(b)-8:])
|
case 64:
|
||||||
case reflect.Int8:
|
return binary.BigEndian.Uint64(b[len(b)-8:])
|
||||||
|
default:
|
||||||
|
// the only case left for unsigned integer is uint256.
|
||||||
|
return new(big.Int).SetBytes(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch typ.Size {
|
||||||
|
case 8:
|
||||||
return int8(b[len(b)-1])
|
return int8(b[len(b)-1])
|
||||||
case reflect.Int16:
|
case 16:
|
||||||
return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
|
return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
|
||||||
case reflect.Int32:
|
case 32:
|
||||||
return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
|
return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
|
||||||
case reflect.Int64:
|
case 64:
|
||||||
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
|
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
|
||||||
default:
|
default:
|
||||||
// the only case lefts for integer is int256/uint256.
|
// the only case left for integer is int256
|
||||||
// big.SetBytes can't tell if a number is negative, positive on itself.
|
// big.SetBytes can't tell if a number is negative or positive in itself.
|
||||||
// On EVM, if the returned number > max int256, it is negative.
|
// On EVM, if the returned number > max int256, it is negative.
|
||||||
|
// A number is > max int256 if the bit at position 255 is set.
|
||||||
ret := new(big.Int).SetBytes(b)
|
ret := new(big.Int).SetBytes(b)
|
||||||
if typ == UintTy {
|
if ret.Bit(255) == 1 {
|
||||||
return ret
|
ret.Add(MaxUint256, new(big.Int).Neg(ret))
|
||||||
}
|
ret.Add(ret, common.Big1)
|
||||||
|
|
||||||
if ret.Cmp(maxInt256) > 0 {
|
|
||||||
ret.Add(maxUint256, big.NewInt(0).Neg(ret))
|
|
||||||
ret.Add(ret, big.NewInt(1))
|
|
||||||
ret.Neg(ret)
|
ret.Neg(ret)
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
|
@ -102,13 +104,13 @@ func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// through reflection, creates a fixed array to be read from
|
// ReadFixedBytes uses reflection to create a fixed array to be read from
|
||||||
func readFixedBytes(t Type, word []byte) (interface{}, error) {
|
func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
|
||||||
if t.T != FixedBytesTy {
|
if t.T != FixedBytesTy {
|
||||||
return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array")
|
return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array")
|
||||||
}
|
}
|
||||||
// convert
|
// convert
|
||||||
array := reflect.New(t.Type).Elem()
|
array := reflect.New(t.GetType()).Elem()
|
||||||
|
|
||||||
reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
|
reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
|
||||||
return array.Interface(), nil
|
return array.Interface(), nil
|
||||||
|
|
@ -129,10 +131,10 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
||||||
|
|
||||||
if t.T == SliceTy {
|
if t.T == SliceTy {
|
||||||
// declare our slice
|
// declare our slice
|
||||||
refSlice = reflect.MakeSlice(t.Type, size, size)
|
refSlice = reflect.MakeSlice(t.GetType(), size, size)
|
||||||
} else if t.T == ArrayTy {
|
} else if t.T == ArrayTy {
|
||||||
// declare our array
|
// declare our array
|
||||||
refSlice = reflect.New(t.Type).Elem()
|
refSlice = reflect.New(t.GetType()).Elem()
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
|
return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
|
||||||
}
|
}
|
||||||
|
|
@ -156,7 +158,7 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func forTupleUnpack(t Type, output []byte) (interface{}, error) {
|
func forTupleUnpack(t Type, output []byte) (interface{}, error) {
|
||||||
retval := reflect.New(t.Type).Elem()
|
retval := reflect.New(t.GetType()).Elem()
|
||||||
virtualArgs := 0
|
virtualArgs := 0
|
||||||
for index, elem := range t.TupleElems {
|
for index, elem := range t.TupleElems {
|
||||||
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
|
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
|
||||||
|
|
@ -216,9 +218,8 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return forTupleUnpack(t, output[begin:])
|
return forTupleUnpack(t, output[begin:])
|
||||||
} else {
|
|
||||||
return forTupleUnpack(t, output[index:])
|
|
||||||
}
|
}
|
||||||
|
return forTupleUnpack(t, output[index:])
|
||||||
case SliceTy:
|
case SliceTy:
|
||||||
return forEachUnpack(t, output[begin:], 0, length)
|
return forEachUnpack(t, output[begin:], 0, length)
|
||||||
case ArrayTy:
|
case ArrayTy:
|
||||||
|
|
@ -230,7 +231,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||||
case StringTy: // variable arrays are written at the end of the return bytes
|
case StringTy: // variable arrays are written at the end of the return bytes
|
||||||
return string(output[begin : begin+length]), nil
|
return string(output[begin : begin+length]), nil
|
||||||
case IntTy, UintTy:
|
case IntTy, UintTy:
|
||||||
return readInteger(t.T, t.Kind, returnOutput), nil
|
return ReadInteger(t, returnOutput), nil
|
||||||
case BoolTy:
|
case BoolTy:
|
||||||
return readBool(returnOutput)
|
return readBool(returnOutput)
|
||||||
case AddressTy:
|
case AddressTy:
|
||||||
|
|
@ -240,7 +241,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||||
case BytesTy:
|
case BytesTy:
|
||||||
return output[begin : begin+length], nil
|
return output[begin : begin+length], nil
|
||||||
case FixedBytesTy:
|
case FixedBytesTy:
|
||||||
return readFixedBytes(t, returnOutput)
|
return ReadFixedBytes(t, returnOutput)
|
||||||
case FunctionTy:
|
case FunctionTy:
|
||||||
return readFunctionType(t, returnOutput)
|
return readFunctionType(t, returnOutput)
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,34 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestUnpack tests the general pack/unpack tests in packing_test.go
|
||||||
|
func TestUnpack(t *testing.T) {
|
||||||
|
for i, test := range packUnpackTests {
|
||||||
|
t.Run(strconv.Itoa(i)+" "+test.def, func(t *testing.T) {
|
||||||
|
//Unpack
|
||||||
|
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
||||||
|
abi, err := JSON(strings.NewReader(def))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||||
|
}
|
||||||
|
encb, err := hex.DecodeString(test.packed)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("invalid hex %s: %v", test.packed, err)
|
||||||
|
}
|
||||||
|
outptr := reflect.New(reflect.TypeOf(test.unpacked))
|
||||||
|
err = abi.Unpack(outptr.Interface(), "method", encb)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d (%v) failed: %v", i, test.def, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out := outptr.Elem().Interface()
|
||||||
|
if !reflect.DeepEqual(test.unpacked, out) {
|
||||||
|
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type unpackTest struct {
|
type unpackTest struct {
|
||||||
def string // ABI definition JSON
|
def string // ABI definition JSON
|
||||||
enc string // evm return data
|
enc string // evm return data
|
||||||
|
|
@ -51,16 +79,7 @@ func (test unpackTest) checkError(err error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var unpackTests = []unpackTest{
|
var unpackTests = []unpackTest{
|
||||||
{
|
// Bools
|
||||||
def: `[{ "type": "bool" }]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{ "type": "bool" }]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
def: `[{ "type": "bool" }]`,
|
def: `[{ "type": "bool" }]`,
|
||||||
enc: "0000000000000000000000000000000000000000000000000001000000000001",
|
enc: "0000000000000000000000000000000000000000000000000001000000000001",
|
||||||
|
|
@ -73,11 +92,7 @@ var unpackTests = []unpackTest{
|
||||||
want: false,
|
want: false,
|
||||||
err: "abi: improperly encoded boolean value",
|
err: "abi: improperly encoded boolean value",
|
||||||
},
|
},
|
||||||
{
|
// Integers
|
||||||
def: `[{"type": "uint32"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
want: uint32(1),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
def: `[{"type": "uint32"}]`,
|
def: `[{"type": "uint32"}]`,
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
|
@ -90,16 +105,6 @@ var unpackTests = []unpackTest{
|
||||||
want: uint16(0),
|
want: uint16(0),
|
||||||
err: "abi: cannot unmarshal *big.Int in to uint16",
|
err: "abi: cannot unmarshal *big.Int in to uint16",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
def: `[{"type": "uint17"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
want: big.NewInt(1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int32"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
want: int32(1),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
def: `[{"type": "int32"}]`,
|
def: `[{"type": "int32"}]`,
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
|
@ -112,36 +117,10 @@ var unpackTests = []unpackTest{
|
||||||
want: int16(0),
|
want: int16(0),
|
||||||
err: "abi: cannot unmarshal *big.Int in to int16",
|
err: "abi: cannot unmarshal *big.Int in to int16",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
def: `[{"type": "int17"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
want: big.NewInt(1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int256"}]`,
|
|
||||||
enc: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
|
||||||
want: big.NewInt(-1),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "address"}]`,
|
|
||||||
enc: "0000000000000000000000000100000000000000000000000000000000000000",
|
|
||||||
want: common.Address{1},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes32"}]`,
|
|
||||||
enc: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
def: `[{"type": "bytes"}]`,
|
def: `[{"type": "bytes"}]`,
|
||||||
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
|
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
|
||||||
want: common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
want: [32]byte{1},
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes"}]`,
|
|
||||||
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: [32]byte{},
|
|
||||||
err: "abi: cannot unmarshal []uint8 in to [32]uint8",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
def: `[{"type": "bytes32"}]`,
|
def: `[{"type": "bytes32"}]`,
|
||||||
|
|
@ -149,219 +128,13 @@ var unpackTests = []unpackTest{
|
||||||
want: []byte(nil),
|
want: []byte(nil),
|
||||||
err: "abi: cannot unmarshal [32]uint8 in to []uint8",
|
err: "abi: cannot unmarshal [32]uint8 in to []uint8",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
def: `[{"type": "bytes32"}]`,
|
|
||||||
enc: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "function"}]`,
|
|
||||||
enc: "0100000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: [24]byte{1},
|
|
||||||
},
|
|
||||||
// slices
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: []uint8{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[2]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: [2]uint8{1, 2},
|
|
||||||
},
|
|
||||||
// multi dimensional, if these pass, all types that don't require length prefix should pass
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[][]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: [][]uint8{{1, 2}, {1, 2}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[][]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
|
|
||||||
want: [][]uint8{{1, 2}, {1, 2, 3}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[2][2]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: [2][2]uint8{{1, 2}, {1, 2}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[][2]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
|
|
||||||
want: [2][]uint8{{1}, {1}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[2][]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: [][2]uint8{{1, 2}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint8[2][]"}]`,
|
|
||||||
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: [][2]uint8{{1, 2}, {1, 2}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint16[]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: []uint16{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint16[2]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: [2]uint16{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint32[]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: []uint32{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint32[2]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: [2]uint32{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint32[2][3][4]"}]`,
|
|
||||||
enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018",
|
|
||||||
want: [4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint64[]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: []uint64{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint64[2]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: [2]uint64{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint256[]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint256[3]"}]`,
|
|
||||||
enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
|
|
||||||
want: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "string[4]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b476f2d657468657265756d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000",
|
|
||||||
want: [4]string{"Hello", "World", "Go-ethereum", "Ethereum"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "string[]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b676f2d657468657265756d000000000000000000000000000000000000000000",
|
|
||||||
want: []string{"Ethereum", "go-ethereum"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "bytes[]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000003f0f0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f0f0f00000000000000000000000000000000000000000000000000000000000",
|
|
||||||
want: [][]byte{{0xf0, 0xf0, 0xf0}, {0xf0, 0xf0, 0xf0}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "uint256[2][][]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000003e8",
|
|
||||||
want: [][][2]*big.Int{{{big.NewInt(1), big.NewInt(200)}, {big.NewInt(1), big.NewInt(1000)}}, {{big.NewInt(1), big.NewInt(200)}, {big.NewInt(1), big.NewInt(1000)}}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int8[]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: []int8{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int8[2]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: [2]int8{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int16[]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: []int16{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int16[2]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: [2]int16{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int32[]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: []int32{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int32[2]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: [2]int32{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int64[]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: []int64{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int64[2]"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: [2]int64{1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int256[]"}]`,
|
|
||||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: []*big.Int{big.NewInt(1), big.NewInt(2)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"type": "int256[3]"}]`,
|
|
||||||
enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
|
|
||||||
want: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
|
|
||||||
},
|
|
||||||
// struct outputs
|
|
||||||
{
|
|
||||||
def: `[{"name":"int1","type":"int256"},{"name":"int2","type":"int256"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: struct {
|
|
||||||
Int1 *big.Int
|
|
||||||
Int2 *big.Int
|
|
||||||
}{big.NewInt(1), big.NewInt(2)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"name":"int_one","type":"int256"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: struct {
|
|
||||||
IntOne *big.Int
|
|
||||||
}{big.NewInt(1)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"name":"int__one","type":"int256"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: struct {
|
|
||||||
IntOne *big.Int
|
|
||||||
}{big.NewInt(1)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"name":"int_one_","type":"int256"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: struct {
|
|
||||||
IntOne *big.Int
|
|
||||||
}{big.NewInt(1)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
def: `[{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}]`,
|
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
|
||||||
want: struct {
|
|
||||||
IntOne *big.Int
|
|
||||||
Intone *big.Int
|
|
||||||
}{big.NewInt(1), big.NewInt(2)},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
def: `[{"name":"___","type":"int256"}]`,
|
def: `[{"name":"___","type":"int256"}]`,
|
||||||
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
want: struct {
|
want: struct {
|
||||||
IntOne *big.Int
|
IntOne *big.Int
|
||||||
Intone *big.Int
|
Intone *big.Int
|
||||||
}{},
|
}{IntOne: big.NewInt(1)},
|
||||||
err: "abi: purely underscored output cannot unpack to struct",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
def: `[{"name":"int_one","type":"int256"},{"name":"IntOne","type":"int256"}]`,
|
def: `[{"name":"int_one","type":"int256"},{"name":"IntOne","type":"int256"}]`,
|
||||||
|
|
@ -408,19 +181,44 @@ var unpackTests = []unpackTest{
|
||||||
}{},
|
}{},
|
||||||
err: "abi: purely underscored output cannot unpack to struct",
|
err: "abi: purely underscored output cannot unpack to struct",
|
||||||
},
|
},
|
||||||
|
// Make sure only the first argument is consumed
|
||||||
|
{
|
||||||
|
def: `[{"name":"int_one","type":"int256"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
}{big.NewInt(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int__one","type":"int256"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
}{big.NewInt(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"name":"int_one_","type":"int256"}]`,
|
||||||
|
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
want: struct {
|
||||||
|
IntOne *big.Int
|
||||||
|
}{big.NewInt(1)},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpack(t *testing.T) {
|
// TestLocalUnpackTests runs test specially designed only for unpacking.
|
||||||
|
// All test cases that can be used to test packing and unpacking should move to packing_test.go
|
||||||
|
func TestLocalUnpackTests(t *testing.T) {
|
||||||
for i, test := range unpackTests {
|
for i, test := range unpackTests {
|
||||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
|
//Unpack
|
||||||
|
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
||||||
abi, err := JSON(strings.NewReader(def))
|
abi, err := JSON(strings.NewReader(def))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||||
}
|
}
|
||||||
encb, err := hex.DecodeString(test.enc)
|
encb, err := hex.DecodeString(test.enc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid hex: %s" + test.enc)
|
t.Fatalf("invalid hex %s: %v", test.enc, err)
|
||||||
}
|
}
|
||||||
outptr := reflect.New(reflect.TypeOf(test.want))
|
outptr := reflect.New(reflect.TypeOf(test.want))
|
||||||
err = abi.Unpack(outptr.Interface(), "method", encb)
|
err = abi.Unpack(outptr.Interface(), "method", encb)
|
||||||
|
|
@ -492,7 +290,7 @@ type methodMultiOutput struct {
|
||||||
|
|
||||||
func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOutput) {
|
func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOutput) {
|
||||||
const definition = `[
|
const definition = `[
|
||||||
{ "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
|
{ "name" : "multi", "type": "function", "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
|
||||||
var expected = methodMultiOutput{big.NewInt(1), "hello"}
|
var expected = methodMultiOutput{big.NewInt(1), "hello"}
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
|
|
@ -562,7 +360,7 @@ func TestMethodMultiReturn(t *testing.T) {
|
||||||
}, {
|
}, {
|
||||||
&[]interface{}{new(int)},
|
&[]interface{}{new(int)},
|
||||||
&[]interface{}{},
|
&[]interface{}{},
|
||||||
"abi: insufficient number of elements in the list/array for unpack, want 2, got 1",
|
"abi: insufficient number of arguments for unpack, want 2, got 1",
|
||||||
"Can not unpack into a slice with wrong types",
|
"Can not unpack into a slice with wrong types",
|
||||||
}}
|
}}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
|
@ -581,7 +379,7 @@ func TestMethodMultiReturn(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiReturnWithArray(t *testing.T) {
|
func TestMultiReturnWithArray(t *testing.T) {
|
||||||
const definition = `[{"name" : "multi", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
|
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -604,7 +402,7 @@ func TestMultiReturnWithArray(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiReturnWithStringArray(t *testing.T) {
|
func TestMultiReturnWithStringArray(t *testing.T) {
|
||||||
const definition = `[{"name" : "multi", "outputs": [{"name": "","type": "uint256[3]"},{"name": "","type": "address"},{"name": "","type": "string[2]"},{"name": "","type": "bool"}]}]`
|
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "uint256[3]"},{"name": "","type": "address"},{"name": "","type": "string[2]"},{"name": "","type": "bool"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -634,7 +432,7 @@ func TestMultiReturnWithStringArray(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiReturnWithStringSlice(t *testing.T) {
|
func TestMultiReturnWithStringSlice(t *testing.T) {
|
||||||
const definition = `[{"name" : "multi", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]`
|
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -670,7 +468,7 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
|
||||||
// values of nested static arrays count towards the size as well, and any element following
|
// values of nested static arrays count towards the size as well, and any element following
|
||||||
// after such nested array argument should be read with the correct offset,
|
// after such nested array argument should be read with the correct offset,
|
||||||
// so that it does not read content from the previous array argument.
|
// so that it does not read content from the previous array argument.
|
||||||
const definition = `[{"name" : "multi", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
|
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -707,15 +505,15 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
|
||||||
|
|
||||||
func TestUnmarshal(t *testing.T) {
|
func TestUnmarshal(t *testing.T) {
|
||||||
const definition = `[
|
const definition = `[
|
||||||
{ "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] },
|
{ "name" : "int", "type": "function", "outputs": [ { "type": "uint256" } ] },
|
||||||
{ "name" : "bool", "constant" : false, "outputs": [ { "type": "bool" } ] },
|
{ "name" : "bool", "type": "function", "outputs": [ { "type": "bool" } ] },
|
||||||
{ "name" : "bytes", "constant" : false, "outputs": [ { "type": "bytes" } ] },
|
{ "name" : "bytes", "type": "function", "outputs": [ { "type": "bytes" } ] },
|
||||||
{ "name" : "fixed", "constant" : false, "outputs": [ { "type": "bytes32" } ] },
|
{ "name" : "fixed", "type": "function", "outputs": [ { "type": "bytes32" } ] },
|
||||||
{ "name" : "multi", "constant" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
|
{ "name" : "multi", "type": "function", "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
|
||||||
{ "name" : "intArraySingle", "constant" : false, "outputs": [ { "type": "uint256[3]" } ] },
|
{ "name" : "intArraySingle", "type": "function", "outputs": [ { "type": "uint256[3]" } ] },
|
||||||
{ "name" : "addressSliceSingle", "constant" : false, "outputs": [ { "type": "address[]" } ] },
|
{ "name" : "addressSliceSingle", "type": "function", "outputs": [ { "type": "address[]" } ] },
|
||||||
{ "name" : "addressSliceDouble", "constant" : false, "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] },
|
{ "name" : "addressSliceDouble", "type": "function", "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] },
|
||||||
{ "name" : "mixedBytes", "constant" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]`
|
{ "name" : "mixedBytes", "type": "function", "stateMutability" : "view", "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -955,7 +753,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackTuple(t *testing.T) {
|
func TestUnpackTuple(t *testing.T) {
|
||||||
const simpleTuple = `[{"name":"tuple","constant":false,"outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
|
const simpleTuple = `[{"name":"tuple","type":"function","outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
|
||||||
abi, err := JSON(strings.NewReader(simpleTuple))
|
abi, err := JSON(strings.NewReader(simpleTuple))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -979,12 +777,12 @@ func TestUnpackTuple(t *testing.T) {
|
||||||
t.Errorf("unexpected value unpacked: want %x, got %x", 1, v.A)
|
t.Errorf("unexpected value unpacked: want %x, got %x", 1, v.A)
|
||||||
}
|
}
|
||||||
if v.B.Cmp(big.NewInt(-1)) != 0 {
|
if v.B.Cmp(big.NewInt(-1)) != 0 {
|
||||||
t.Errorf("unexpected value unpacked: want %x, got %x", v.B, -1)
|
t.Errorf("unexpected value unpacked: want %x, got %x", -1, v.B)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test nested tuple
|
// Test nested tuple
|
||||||
const nestedTuple = `[{"name":"tuple","constant":false,"outputs":[
|
const nestedTuple = `[{"name":"tuple","type":"function","outputs":[
|
||||||
{"type":"tuple","name":"s","components":[{"type":"uint256","name":"a"},{"type":"uint256[]","name":"b"},{"type":"tuple[]","name":"c","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]}]},
|
{"type":"tuple","name":"s","components":[{"type":"uint256","name":"a"},{"type":"uint256[]","name":"b"},{"type":"tuple[]","name":"c","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]}]},
|
||||||
{"type":"tuple","name":"t","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]},
|
{"type":"tuple","name":"t","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]},
|
||||||
{"type":"uint256","name":"a"}
|
{"type":"uint256","name":"a"}
|
||||||
|
|
@ -1106,7 +904,7 @@ func TestOOMMaliciousInput(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, test := range oomTests {
|
for i, test := range oomTests {
|
||||||
def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
|
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
||||||
abi, err := JSON(strings.NewReader(def))
|
abi, err := JSON(strings.NewReader(def))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,8 @@ type Wallet interface {
|
||||||
// about which fields or actions are needed. The user may retry by providing
|
// about which fields or actions are needed. The user may retry by providing
|
||||||
// the needed details via SignHashWithPassphrase, or by other means (e.g. unlock
|
// the needed details via SignHashWithPassphrase, or by other means (e.g. unlock
|
||||||
// the account in a keystore).
|
// the account in a keystore).
|
||||||
|
//
|
||||||
|
// This method should return the signature in 'canonical' format, with v 0 or 1
|
||||||
SignText(account Account, text []byte) ([]byte, error)
|
SignText(account Account, text []byte) ([]byte, error)
|
||||||
|
|
||||||
// SignTextWithPassphrase is identical to Signtext, but also takes a password
|
// SignTextWithPassphrase is identical to Signtext, but also takes a password
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2019 The go-ethereum Authors
|
// Copyright 2015 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ var ErrNotSupported = errors.New("not supported")
|
||||||
|
|
||||||
// ErrInvalidPassphrase is returned when a decryption operation receives a bad
|
// ErrInvalidPassphrase is returned when a decryption operation receives a bad
|
||||||
// passphrase.
|
// passphrase.
|
||||||
var ErrInvalidPassphrase = errors.New("invalid passphrase")
|
var ErrInvalidPassphrase = errors.New("invalid password")
|
||||||
|
|
||||||
// ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the
|
// ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the
|
||||||
// second time.
|
// second time.
|
||||||
|
|
|
||||||
52
accounts/external/backend.go
vendored
52
accounts/external/backend.go
vendored
|
|
@ -1,18 +1,18 @@
|
||||||
// Copyright 2018 The go-ethereum Authors
|
// Copyright 2019 The go-ethereum Authors
|
||||||
// This file is part of go-ethereum.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// 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
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// go-ethereum is distributed in the hope that it will be useful,
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU Lesser General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package external
|
package external
|
||||||
|
|
||||||
|
|
@ -21,13 +21,12 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/maticnetwork/bor"
|
ethereum "github.com/maticnetwork/bor"
|
||||||
"github.com/maticnetwork/bor/accounts"
|
"github.com/maticnetwork/bor/accounts"
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
"github.com/maticnetwork/bor/common/hexutil"
|
"github.com/maticnetwork/bor/common/hexutil"
|
||||||
"github.com/maticnetwork/bor/core/types"
|
"github.com/maticnetwork/bor/core/types"
|
||||||
"github.com/maticnetwork/bor/event"
|
"github.com/maticnetwork/bor/event"
|
||||||
"github.com/maticnetwork/bor/internal/ethapi"
|
|
||||||
"github.com/maticnetwork/bor/log"
|
"github.com/maticnetwork/bor/log"
|
||||||
"github.com/maticnetwork/bor/rpc"
|
"github.com/maticnetwork/bor/rpc"
|
||||||
"github.com/maticnetwork/bor/signer/core"
|
"github.com/maticnetwork/bor/signer/core"
|
||||||
|
|
@ -131,6 +130,12 @@ func (api *ExternalSigner) Accounts() []accounts.Account {
|
||||||
func (api *ExternalSigner) Contains(account accounts.Account) bool {
|
func (api *ExternalSigner) Contains(account accounts.Account) bool {
|
||||||
api.cacheMu.RLock()
|
api.cacheMu.RLock()
|
||||||
defer api.cacheMu.RUnlock()
|
defer api.cacheMu.RUnlock()
|
||||||
|
if api.cache == nil {
|
||||||
|
// If we haven't already fetched the accounts, it's time to do so now
|
||||||
|
api.cacheMu.RUnlock()
|
||||||
|
api.Accounts()
|
||||||
|
api.cacheMu.RLock()
|
||||||
|
}
|
||||||
for _, a := range api.cache {
|
for _, a := range api.cache {
|
||||||
if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) {
|
if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) {
|
||||||
return true
|
return true
|
||||||
|
|
@ -161,7 +166,7 @@ func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, d
|
||||||
hexutil.Encode(data)); err != nil {
|
hexutil.Encode(data)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// If V is on 27/28-form, convert to to 0/1 for Clique
|
// If V is on 27/28-form, convert to 0/1 for Clique
|
||||||
if mimeType == accounts.MimetypeClique && (res[64] == 27 || res[64] == 28) {
|
if mimeType == accounts.MimetypeClique && (res[64] == 27 || res[64] == 28) {
|
||||||
res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique use
|
res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique use
|
||||||
}
|
}
|
||||||
|
|
@ -169,19 +174,29 @@ func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) {
|
func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) {
|
||||||
var res hexutil.Bytes
|
var signature hexutil.Bytes
|
||||||
var signAddress = common.NewMixedcaseAddress(account.Address)
|
var signAddress = common.NewMixedcaseAddress(account.Address)
|
||||||
if err := api.client.Call(&res, "account_signData",
|
if err := api.client.Call(&signature, "account_signData",
|
||||||
accounts.MimetypeTextPlain,
|
accounts.MimetypeTextPlain,
|
||||||
&signAddress, // Need to use the pointer here, because of how MarshalJSON is defined
|
&signAddress, // Need to use the pointer here, because of how MarshalJSON is defined
|
||||||
hexutil.Encode(text)); err != nil {
|
hexutil.Encode(text)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return res, nil
|
if signature[64] == 27 || signature[64] == 28 {
|
||||||
|
// If clef is used as a backend, it may already have transformed
|
||||||
|
// the signature to ethereum-type signature.
|
||||||
|
signature[64] -= 27 // Transform V from Ethereum-legacy to 0/1
|
||||||
|
}
|
||||||
|
return signature, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// signTransactionResult represents the signinig result returned by clef.
|
||||||
|
type signTransactionResult struct {
|
||||||
|
Raw hexutil.Bytes `json:"raw"`
|
||||||
|
Tx *types.Transaction `json:"tx"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||||
res := ethapi.SignTransactionResult{}
|
|
||||||
data := hexutil.Bytes(tx.Data())
|
data := hexutil.Bytes(tx.Data())
|
||||||
var to *common.MixedcaseAddress
|
var to *common.MixedcaseAddress
|
||||||
if tx.To() != nil {
|
if tx.To() != nil {
|
||||||
|
|
@ -197,6 +212,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
||||||
To: to,
|
To: to,
|
||||||
From: common.NewMixedcaseAddress(account.Address),
|
From: common.NewMixedcaseAddress(account.Address),
|
||||||
}
|
}
|
||||||
|
var res signTransactionResult
|
||||||
if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
|
if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -204,14 +220,14 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
|
func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
|
||||||
return []byte{}, fmt.Errorf("passphrase-operations not supported on external signers")
|
return []byte{}, fmt.Errorf("password-operations not supported on external signers")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||||
return nil, fmt.Errorf("passphrase-operations not supported on external signers")
|
return nil, fmt.Errorf("password-operations not supported on external signers")
|
||||||
}
|
}
|
||||||
func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
|
func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
|
||||||
return nil, fmt.Errorf("passphrase-operations not supported on external signers")
|
return nil, fmt.Errorf("password-operations not supported on external signers")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
|
func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ func TestHDPathParsing(t *testing.T) {
|
||||||
// Weird inputs just to ensure they work
|
// Weird inputs just to ensure they work
|
||||||
{" m / 44 '\n/\n 60 \n\n\t' /\n0 ' /\t\t 0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
|
{" m / 44 '\n/\n 60 \n\n\t' /\n0 ' /\t\t 0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
|
||||||
|
|
||||||
// Invaid derivation paths
|
// Invalid derivation paths
|
||||||
{"", nil}, // Empty relative derivation path
|
{"", nil}, // Empty relative derivation path
|
||||||
{"m", nil}, // Empty absolute derivation path
|
{"m", nil}, // Empty absolute derivation path
|
||||||
{"m/", nil}, // Missing last derivation component
|
{"m/", nil}, // Missing last derivation component
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -43,7 +42,11 @@ import (
|
||||||
var (
|
var (
|
||||||
ErrLocked = accounts.NewAuthNeededError("password or unlock")
|
ErrLocked = accounts.NewAuthNeededError("password or unlock")
|
||||||
ErrNoMatch = errors.New("no key for given address or file")
|
ErrNoMatch = errors.New("no key for given address or file")
|
||||||
ErrDecrypt = errors.New("could not decrypt key with given passphrase")
|
ErrDecrypt = errors.New("could not decrypt key with given password")
|
||||||
|
|
||||||
|
// ErrAccountAlreadyExists is returned if an account attempted to import is
|
||||||
|
// already present in the keystore.
|
||||||
|
ErrAccountAlreadyExists = errors.New("account already exists")
|
||||||
)
|
)
|
||||||
|
|
||||||
// KeyStoreType is the reflect type of a keystore backend.
|
// KeyStoreType is the reflect type of a keystore backend.
|
||||||
|
|
@ -67,7 +70,8 @@ type KeyStore struct {
|
||||||
updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
|
updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
|
||||||
updating bool // Whether the event notification loop is running
|
updating bool // Whether the event notification loop is running
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
importMu sync.Mutex // Import Mutex locks the import to prevent two insertions from racing
|
||||||
}
|
}
|
||||||
|
|
||||||
type unlocked struct {
|
type unlocked struct {
|
||||||
|
|
@ -443,14 +447,27 @@ func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (ac
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
|
ks.importMu.Lock()
|
||||||
|
defer ks.importMu.Unlock()
|
||||||
|
|
||||||
|
if ks.cache.hasAddress(key.Address) {
|
||||||
|
return accounts.Account{
|
||||||
|
Address: key.Address,
|
||||||
|
}, ErrAccountAlreadyExists
|
||||||
|
}
|
||||||
return ks.importKey(key, newPassphrase)
|
return ks.importKey(key, newPassphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
|
// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
|
||||||
func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) {
|
func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) {
|
||||||
|
ks.importMu.Lock()
|
||||||
|
defer ks.importMu.Unlock()
|
||||||
|
|
||||||
key := newKeyFromECDSA(priv)
|
key := newKeyFromECDSA(priv)
|
||||||
if ks.cache.hasAddress(key.Address) {
|
if ks.cache.hasAddress(key.Address) {
|
||||||
return accounts.Account{}, fmt.Errorf("account already exists")
|
return accounts.Account{
|
||||||
|
Address: key.Address,
|
||||||
|
}, ErrAccountAlreadyExists
|
||||||
}
|
}
|
||||||
return ks.importKey(key, passphrase)
|
return ks.importKey(key, passphrase)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,14 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/maticnetwork/bor/accounts"
|
"github.com/maticnetwork/bor/accounts"
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
|
"github.com/maticnetwork/bor/crypto"
|
||||||
"github.com/maticnetwork/bor/event"
|
"github.com/maticnetwork/bor/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -338,6 +341,88 @@ func TestWalletNotifications(t *testing.T) {
|
||||||
checkEvents(t, wantEvents, events)
|
checkEvents(t, wantEvents, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestImportExport tests the import functionality of a keystore.
|
||||||
|
func TestImportECDSA(t *testing.T) {
|
||||||
|
dir, ks := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate key: %v", key)
|
||||||
|
}
|
||||||
|
if _, err = ks.ImportECDSA(key, "old"); err != nil {
|
||||||
|
t.Errorf("importing failed: %v", err)
|
||||||
|
}
|
||||||
|
if _, err = ks.ImportECDSA(key, "old"); err == nil {
|
||||||
|
t.Errorf("importing same key twice succeeded")
|
||||||
|
}
|
||||||
|
if _, err = ks.ImportECDSA(key, "new"); err == nil {
|
||||||
|
t.Errorf("importing same key twice succeeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestImportECDSA tests the import and export functionality of a keystore.
|
||||||
|
func TestImportExport(t *testing.T) {
|
||||||
|
dir, ks := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
acc, err := ks.NewAccount("old")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create account: %v", acc)
|
||||||
|
}
|
||||||
|
json, err := ks.Export(acc, "old", "new")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to export account: %v", acc)
|
||||||
|
}
|
||||||
|
dir2, ks2 := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir2)
|
||||||
|
if _, err = ks2.Import(json, "old", "old"); err == nil {
|
||||||
|
t.Errorf("importing with invalid password succeeded")
|
||||||
|
}
|
||||||
|
acc2, err := ks2.Import(json, "new", "new")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("importing failed: %v", err)
|
||||||
|
}
|
||||||
|
if acc.Address != acc2.Address {
|
||||||
|
t.Error("imported account does not match exported account")
|
||||||
|
}
|
||||||
|
if _, err = ks2.Import(json, "new", "new"); err == nil {
|
||||||
|
t.Errorf("importing a key twice succeeded")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestImportRace tests the keystore on races.
|
||||||
|
// This test should fail under -race if importing races.
|
||||||
|
func TestImportRace(t *testing.T) {
|
||||||
|
dir, ks := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
acc, err := ks.NewAccount("old")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create account: %v", acc)
|
||||||
|
}
|
||||||
|
json, err := ks.Export(acc, "old", "new")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to export account: %v", acc)
|
||||||
|
}
|
||||||
|
dir2, ks2 := tmpKeyStore(t, true)
|
||||||
|
defer os.RemoveAll(dir2)
|
||||||
|
var atom uint32
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if _, err := ks2.Import(json, "new", "new"); err != nil {
|
||||||
|
atomic.AddUint32(&atom, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
if atom != 1 {
|
||||||
|
t.Errorf("Import is racy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// checkAccounts checks that all known live accounts are present in the wallet list.
|
// checkAccounts checks that all known live accounts are present in the wallet list.
|
||||||
func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, wallets []accounts.Wallet) {
|
func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, wallets []accounts.Wallet) {
|
||||||
if len(live) != len(wallets) {
|
if len(live) != len(wallets) {
|
||||||
|
|
|
||||||
|
|
@ -121,8 +121,9 @@ func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) er
|
||||||
"This indicates that the keystore is corrupted. \n" +
|
"This indicates that the keystore is corrupted. \n" +
|
||||||
"The corrupted file is stored at \n%v\n" +
|
"The corrupted file is stored at \n%v\n" +
|
||||||
"Please file a ticket at:\n\n" +
|
"Please file a ticket at:\n\n" +
|
||||||
"https://github.com/maticnetwork/bor/issues." +
|
"https://github.com/ethereum/go-ethereum/issues." +
|
||||||
"The error was : %s"
|
"The error was : %s"
|
||||||
|
//lint:ignore ST1005 This is a message for the user
|
||||||
return fmt.Errorf(msg, tmpName, err)
|
return fmt.Errorf(msg, tmpName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -237,7 +238,7 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
|
||||||
|
|
||||||
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
||||||
if cryptoJson.Cipher != "aes-128-ctr" {
|
if cryptoJson.Cipher != "aes-128-ctr" {
|
||||||
return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
|
return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
|
||||||
}
|
}
|
||||||
mac, err := hex.DecodeString(cryptoJson.MAC)
|
mac, err := hex.DecodeString(cryptoJson.MAC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -273,7 +274,7 @@ func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
||||||
|
|
||||||
func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
|
func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
|
||||||
if keyProtected.Version != version {
|
if keyProtected.Version != version {
|
||||||
return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
|
return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
|
||||||
}
|
}
|
||||||
keyId = uuid.Parse(keyProtected.Id)
|
keyId = uuid.Parse(keyProtected.Id)
|
||||||
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
|
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
|
||||||
|
|
@ -335,13 +336,13 @@ func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
|
||||||
c := ensureInt(cryptoJSON.KDFParams["c"])
|
c := ensureInt(cryptoJSON.KDFParams["c"])
|
||||||
prf := cryptoJSON.KDFParams["prf"].(string)
|
prf := cryptoJSON.KDFParams["prf"].(string)
|
||||||
if prf != "hmac-sha256" {
|
if prf != "hmac-sha256" {
|
||||||
return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
|
return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
|
||||||
}
|
}
|
||||||
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
|
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
|
return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: can we do without this when unmarshalling dynamic JSON?
|
// TODO: can we do without this when unmarshalling dynamic JSON?
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if _, err = ks.GetKey(k1.Address, account.URL.Path, "bar"); err != ErrDecrypt {
|
if _, err = ks.GetKey(k1.Address, account.URL.Path, "bar"); err != ErrDecrypt {
|
||||||
t.Fatalf("wrong error for invalid passphrase\ngot %q\nwant %q", err, ErrDecrypt)
|
t.Fatalf("wrong error for invalid password\ngot %q\nwant %q", err, ErrDecrypt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
accounts/keystore/testdata/keystore/README
vendored
2
accounts/keystore/testdata/keystore/README
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
This directory contains accounts for testing.
|
This directory contains accounts for testing.
|
||||||
The passphrase that unlocks them is "foobar".
|
The password that unlocks them is "foobar".
|
||||||
|
|
||||||
The "good" key files which are supposed to be loadable are:
|
The "good" key files which are supposed to be loadable are:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/maticnetwork/bor/common"
|
||||||
"github.com/maticnetwork/bor/event"
|
"github.com/maticnetwork/bor/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -140,6 +141,11 @@ func (am *Manager) Wallets() []Wallet {
|
||||||
am.lock.RLock()
|
am.lock.RLock()
|
||||||
defer am.lock.RUnlock()
|
defer am.lock.RUnlock()
|
||||||
|
|
||||||
|
return am.walletsNoLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// walletsNoLock returns all registered wallets. Callers must hold am.lock.
|
||||||
|
func (am *Manager) walletsNoLock() []Wallet {
|
||||||
cpy := make([]Wallet, len(am.wallets))
|
cpy := make([]Wallet, len(am.wallets))
|
||||||
copy(cpy, am.wallets)
|
copy(cpy, am.wallets)
|
||||||
return cpy
|
return cpy
|
||||||
|
|
@ -154,7 +160,7 @@ func (am *Manager) Wallet(url string) (Wallet, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, wallet := range am.Wallets() {
|
for _, wallet := range am.walletsNoLock() {
|
||||||
if wallet.URL() == parsed {
|
if wallet.URL() == parsed {
|
||||||
return wallet, nil
|
return wallet, nil
|
||||||
}
|
}
|
||||||
|
|
@ -162,6 +168,20 @@ func (am *Manager) Wallet(url string) (Wallet, error) {
|
||||||
return nil, ErrUnknownWallet
|
return nil, ErrUnknownWallet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accounts returns all account addresses of all wallets within the account manager
|
||||||
|
func (am *Manager) Accounts() []common.Address {
|
||||||
|
am.lock.RLock()
|
||||||
|
defer am.lock.RUnlock()
|
||||||
|
|
||||||
|
addresses := make([]common.Address, 0) // return [] instead of nil if empty
|
||||||
|
for _, wallet := range am.wallets {
|
||||||
|
for _, account := range wallet.Accounts() {
|
||||||
|
addresses = append(addresses, account.Address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addresses
|
||||||
|
}
|
||||||
|
|
||||||
// Find attempts to locate the wallet corresponding to a specific account. Since
|
// Find attempts to locate the wallet corresponding to a specific account. Since
|
||||||
// accounts can be dynamically added to and removed from wallets, this method has
|
// accounts can be dynamically added to and removed from wallets, this method has
|
||||||
// a linear runtime in the number of wallets.
|
// a linear runtime in the number of wallets.
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ func (hub *Hub) refreshWallets() {
|
||||||
// Mark the reader as present
|
// Mark the reader as present
|
||||||
seen[reader] = struct{}{}
|
seen[reader] = struct{}{}
|
||||||
|
|
||||||
// If we alreay know about this card, skip to the next reader, otherwise clean up
|
// If we already know about this card, skip to the next reader, otherwise clean up
|
||||||
if wallet, ok := hub.wallets[reader]; ok {
|
if wallet, ok := hub.wallets[reader]; ok {
|
||||||
if err := wallet.ping(); err == nil {
|
if err := wallet.ping(); err == nil {
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSes
|
||||||
|
|
||||||
cardPublic, ok := gen.Unmarshal(keyData)
|
cardPublic, ok := gen.Unmarshal(keyData)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("Could not unmarshal public key from card")
|
return nil, fmt.Errorf("could not unmarshal public key from card")
|
||||||
}
|
}
|
||||||
|
|
||||||
secret, err := gen.GenerateSharedSecret(private, cardPublic)
|
secret, err := gen.GenerateSharedSecret(private, cardPublic)
|
||||||
|
|
@ -109,7 +109,7 @@ func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
|
||||||
cardChallenge := response.Data[32:64]
|
cardChallenge := response.Data[32:64]
|
||||||
|
|
||||||
if !bytes.Equal(expectedCryptogram, cardCryptogram) {
|
if !bytes.Equal(expectedCryptogram, cardCryptogram) {
|
||||||
return fmt.Errorf("Invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram)
|
return fmt.Errorf("invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram)
|
||||||
}
|
}
|
||||||
|
|
||||||
md.Reset()
|
md.Reset()
|
||||||
|
|
@ -132,7 +132,7 @@ func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
|
||||||
// Unpair disestablishes an existing pairing.
|
// Unpair disestablishes an existing pairing.
|
||||||
func (s *SecureChannelSession) Unpair() error {
|
func (s *SecureChannelSession) Unpair() error {
|
||||||
if s.PairingKey == nil {
|
if s.PairingKey == nil {
|
||||||
return fmt.Errorf("Cannot unpair: not paired")
|
return fmt.Errorf("cannot unpair: not paired")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{})
|
_, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{})
|
||||||
|
|
@ -148,7 +148,7 @@ func (s *SecureChannelSession) Unpair() error {
|
||||||
// Open initializes the secure channel.
|
// Open initializes the secure channel.
|
||||||
func (s *SecureChannelSession) Open() error {
|
func (s *SecureChannelSession) Open() error {
|
||||||
if s.iv != nil {
|
if s.iv != nil {
|
||||||
return fmt.Errorf("Session already opened")
|
return fmt.Errorf("session already opened")
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := s.open()
|
response, err := s.open()
|
||||||
|
|
@ -185,11 +185,11 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
|
if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
|
||||||
return fmt.Errorf("Got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
|
return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(response.Data) != scSecretLength {
|
if len(response.Data) != scSecretLength {
|
||||||
return fmt.Errorf("Response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength)
|
return fmt.Errorf("response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -222,7 +222,7 @@ func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*responseAPDU, error
|
||||||
// transmitEncrypted sends an encrypted message, and decrypts and returns the response.
|
// transmitEncrypted sends an encrypted message, and decrypts and returns the response.
|
||||||
func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) {
|
func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) {
|
||||||
if s.iv == nil {
|
if s.iv == nil {
|
||||||
return nil, fmt.Errorf("Channel not open")
|
return nil, fmt.Errorf("channel not open")
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := s.encryptAPDU(data)
|
data, err := s.encryptAPDU(data)
|
||||||
|
|
@ -261,14 +261,14 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !bytes.Equal(s.iv, rmac) {
|
if !bytes.Equal(s.iv, rmac) {
|
||||||
return nil, fmt.Errorf("Invalid MAC in response")
|
return nil, fmt.Errorf("invalid MAC in response")
|
||||||
}
|
}
|
||||||
|
|
||||||
rapdu := &responseAPDU{}
|
rapdu := &responseAPDU{}
|
||||||
rapdu.deserialize(plainData)
|
rapdu.deserialize(plainData)
|
||||||
|
|
||||||
if rapdu.Sw1 != sw1Ok {
|
if rapdu.Sw1 != sw1Ok {
|
||||||
return nil, fmt.Errorf("Unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
|
return nil, fmt.Errorf("unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return rapdu, nil
|
return rapdu, nil
|
||||||
|
|
@ -277,7 +277,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
||||||
// encryptAPDU is an internal method that serializes and encrypts an APDU.
|
// encryptAPDU is an internal method that serializes and encrypts an APDU.
|
||||||
func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
|
func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
|
||||||
if len(data) > maxPayloadSize {
|
if len(data) > maxPayloadSize {
|
||||||
return nil, fmt.Errorf("Payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
|
return nil, fmt.Errorf("payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
|
||||||
}
|
}
|
||||||
data = pad(data, 0x80)
|
data = pad(data, 0x80)
|
||||||
|
|
||||||
|
|
@ -323,10 +323,10 @@ func unpad(data []byte, terminator byte) ([]byte, error) {
|
||||||
case terminator:
|
case terminator:
|
||||||
return data[:len(data)-i], nil
|
return data[:len(data)-i], nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Expected end of padding, got %d", data[len(data)-i])
|
return nil, fmt.Errorf("expected end of padding, got %d", data[len(data)-i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Expected end of padding, got 0")
|
return nil, fmt.Errorf("expected end of padding, got 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateIV is an internal method that updates the initialization vector after
|
// updateIV is an internal method that updates the initialization vector after
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ func transmit(card *pcsc.Card, command *commandAPDU) (*responseAPDU, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.Sw1 != sw1Ok {
|
if response.Sw1 != sw1Ok {
|
||||||
return nil, fmt.Errorf("Unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
|
return nil, fmt.Errorf("unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
|
|
@ -252,7 +252,7 @@ func (w *Wallet) release() error {
|
||||||
// with the wallet.
|
// with the wallet.
|
||||||
func (w *Wallet) pair(puk []byte) error {
|
func (w *Wallet) pair(puk []byte) error {
|
||||||
if w.session.paired() {
|
if w.session.paired() {
|
||||||
return fmt.Errorf("Wallet already paired")
|
return fmt.Errorf("wallet already paired")
|
||||||
}
|
}
|
||||||
pairing, err := w.session.pair(puk)
|
pairing, err := w.session.pair(puk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -312,15 +312,15 @@ func (w *Wallet) Status() (string, error) {
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case !w.session.verified && status.PinRetryCount == 0 && status.PukRetryCount == 0:
|
case !w.session.verified && status.PinRetryCount == 0 && status.PukRetryCount == 0:
|
||||||
return fmt.Sprintf("Bricked, waiting for full wipe"), nil
|
return "Bricked, waiting for full wipe", nil
|
||||||
case !w.session.verified && status.PinRetryCount == 0:
|
case !w.session.verified && status.PinRetryCount == 0:
|
||||||
return fmt.Sprintf("Blocked, waiting for PUK (%d attempts left) and new PIN", status.PukRetryCount), nil
|
return fmt.Sprintf("Blocked, waiting for PUK (%d attempts left) and new PIN", status.PukRetryCount), nil
|
||||||
case !w.session.verified:
|
case !w.session.verified:
|
||||||
return fmt.Sprintf("Locked, waiting for PIN (%d attempts left)", status.PinRetryCount), nil
|
return fmt.Sprintf("Locked, waiting for PIN (%d attempts left)", status.PinRetryCount), nil
|
||||||
case !status.Initialized:
|
case !status.Initialized:
|
||||||
return fmt.Sprintf("Empty, waiting for initialization"), nil
|
return "Empty, waiting for initialization", nil
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("Online"), nil
|
return "Online", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,7 +362,7 @@ func (w *Wallet) Open(passphrase string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Pairing succeeded, fall through to PIN checks. This will of course fail,
|
// Pairing succeeded, fall through to PIN checks. This will of course fail,
|
||||||
// but we can't return ErrPINNeeded directly here becase we don't know whether
|
// but we can't return ErrPINNeeded directly here because we don't know whether
|
||||||
// a PIN check or a PIN reset is needed.
|
// a PIN check or a PIN reset is needed.
|
||||||
passphrase = ""
|
passphrase = ""
|
||||||
}
|
}
|
||||||
|
|
@ -773,12 +773,12 @@ func (w *Wallet) findAccountPath(account accounts.Account) (accounts.DerivationP
|
||||||
|
|
||||||
// Look for the path in the URL
|
// Look for the path in the URL
|
||||||
if account.URL.Scheme != w.Hub.scheme {
|
if account.URL.Scheme != w.Hub.scheme {
|
||||||
return nil, fmt.Errorf("Scheme %s does not match wallet scheme %s", account.URL.Scheme, w.Hub.scheme)
|
return nil, fmt.Errorf("scheme %s does not match wallet scheme %s", account.URL.Scheme, w.Hub.scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.SplitN(account.URL.Path, "/", 2)
|
parts := strings.SplitN(account.URL.Path, "/", 2)
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return nil, fmt.Errorf("Invalid URL format: %s", account.URL)
|
return nil, fmt.Errorf("invalid URL format: %s", account.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
if parts[0] != fmt.Sprintf("%x", w.PublicKey[1:3]) {
|
if parts[0] != fmt.Sprintf("%x", w.PublicKey[1:3]) {
|
||||||
|
|
@ -813,7 +813,7 @@ func (s *Session) pair(secret []byte) (smartcardPairing, error) {
|
||||||
// unpair deletes an existing pairing.
|
// unpair deletes an existing pairing.
|
||||||
func (s *Session) unpair() error {
|
func (s *Session) unpair() error {
|
||||||
if !s.verified {
|
if !s.verified {
|
||||||
return fmt.Errorf("Unpair requires that the PIN be verified")
|
return fmt.Errorf("unpair requires that the PIN be verified")
|
||||||
}
|
}
|
||||||
return s.Channel.Unpair()
|
return s.Channel.Unpair()
|
||||||
}
|
}
|
||||||
|
|
@ -850,7 +850,7 @@ func (s *Session) paired() bool {
|
||||||
// authenticate uses an existing pairing to establish a secure channel.
|
// authenticate uses an existing pairing to establish a secure channel.
|
||||||
func (s *Session) authenticate(pairing smartcardPairing) error {
|
func (s *Session) authenticate(pairing smartcardPairing) error {
|
||||||
if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) {
|
if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) {
|
||||||
return fmt.Errorf("Cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey)
|
return fmt.Errorf("cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey)
|
||||||
}
|
}
|
||||||
s.Channel.PairingKey = pairing.PairingKey
|
s.Channel.PairingKey = pairing.PairingKey
|
||||||
s.Channel.PairingIndex = pairing.PairingIndex
|
s.Channel.PairingIndex = pairing.PairingIndex
|
||||||
|
|
@ -879,6 +879,7 @@ func (s *Session) walletStatus() (*walletStatus, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// derivationPath fetches the wallet's current derivation path from the card.
|
// derivationPath fetches the wallet's current derivation path from the card.
|
||||||
|
//lint:ignore U1000 needs to be added to the console interface
|
||||||
func (s *Session) derivationPath() (accounts.DerivationPath, error) {
|
func (s *Session) derivationPath() (accounts.DerivationPath, error) {
|
||||||
response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil)
|
response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -993,12 +994,14 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyExport contains information on an exported keypair.
|
// keyExport contains information on an exported keypair.
|
||||||
|
//lint:ignore U1000 needs to be added to the console interface
|
||||||
type keyExport struct {
|
type keyExport struct {
|
||||||
PublicKey []byte `asn1:"tag:0"`
|
PublicKey []byte `asn1:"tag:0"`
|
||||||
PrivateKey []byte `asn1:"tag:1,optional"`
|
PrivateKey []byte `asn1:"tag:1,optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// publicKey returns the public key for the current derivation path.
|
// publicKey returns the public key for the current derivation path.
|
||||||
|
//lint:ignore U1000 needs to be added to the console interface
|
||||||
func (s *Session) publicKey() ([]byte, error) {
|
func (s *Session) publicKey() ([]byte, error) {
|
||||||
response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil)
|
response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
// Copyright 2018 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
"github.com/maticnetwork/bor/common/hexutil"
|
"github.com/maticnetwork/bor/common/hexutil"
|
||||||
"github.com/maticnetwork/bor/core/types"
|
"github.com/maticnetwork/bor/core/types"
|
||||||
|
"github.com/maticnetwork/bor/crypto"
|
||||||
"github.com/maticnetwork/bor/log"
|
"github.com/maticnetwork/bor/log"
|
||||||
"github.com/maticnetwork/bor/rlp"
|
"github.com/maticnetwork/bor/rlp"
|
||||||
)
|
)
|
||||||
|
|
@ -161,7 +162,8 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
|
||||||
return common.Address{}, nil, accounts.ErrWalletClosed
|
return common.Address{}, nil, accounts.ErrWalletClosed
|
||||||
}
|
}
|
||||||
// Ensure the wallet is capable of signing the given transaction
|
// Ensure the wallet is capable of signing the given transaction
|
||||||
if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 {
|
if chainID != nil && w.version[0] <= 1 && w.version[2] <= 2 {
|
||||||
|
//lint:ignore ST1005 brand name displayed on the console
|
||||||
return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
|
return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
|
||||||
}
|
}
|
||||||
// All infos gathered and metadata checks out, request signing
|
// All infos gathered and metadata checks out, request signing
|
||||||
|
|
@ -341,7 +343,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
||||||
op = ledgerP1ContTransactionData
|
op = ledgerP1ContTransactionData
|
||||||
}
|
}
|
||||||
// Extract the Ethereum signature and do a sanity validation
|
// Extract the Ethereum signature and do a sanity validation
|
||||||
if len(reply) != 65 {
|
if len(reply) != crypto.SignatureLength {
|
||||||
return common.Address{}, nil, errors.New("reply lacks signature")
|
return common.Address{}, nil, errors.New("reply lacks signature")
|
||||||
}
|
}
|
||||||
signature := append(reply[1:], reply[0])
|
signature := append(reply[1:], reply[0])
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2019 The go-ethereum Authors
|
// Copyright 2017 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -479,7 +479,8 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
||||||
|
|
||||||
if _, ok := w.paths[address]; !ok {
|
if _, ok := w.paths[address]; !ok {
|
||||||
w.accounts = append(w.accounts, account)
|
w.accounts = append(w.accounts, account)
|
||||||
w.paths[address] = path
|
w.paths[address] = make(accounts.DerivationPath, len(path))
|
||||||
|
copy(w.paths[address], path)
|
||||||
}
|
}
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ clone_depth: 5
|
||||||
version: "{branch}.{build}"
|
version: "{branch}.{build}"
|
||||||
environment:
|
environment:
|
||||||
global:
|
global:
|
||||||
|
GO111MODULE: on
|
||||||
GOPATH: C:\gopath
|
GOPATH: C:\gopath
|
||||||
CC: gcc.exe
|
CC: gcc.exe
|
||||||
matrix:
|
matrix:
|
||||||
|
|
@ -23,8 +24,8 @@ environment:
|
||||||
install:
|
install:
|
||||||
- git submodule update --init
|
- git submodule update --init
|
||||||
- rmdir C:\go /s /q
|
- rmdir C:\go /s /q
|
||||||
- appveyor DownloadFile https://dl.google.com/go/go1.12.7.windows-%GETH_ARCH%.zip
|
- appveyor DownloadFile https://dl.google.com/go/go1.14.2.windows-%GETH_ARCH%.zip
|
||||||
- 7z x go1.12.7.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
- 7z x go1.14.2.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||||
- go version
|
- go version
|
||||||
- gcc --version
|
- gcc --version
|
||||||
|
|
||||||
|
|
|
||||||
20
build/checksums.txt
Normal file
20
build/checksums.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# This file contains sha256 checksums of optional build dependencies.
|
||||||
|
|
||||||
|
98de84e69726a66da7b4e58eac41b99cbe274d7e8906eeb8a5b7eb0aadee7f7c go1.14.2.src.tar.gz
|
||||||
|
|
||||||
|
d998a84eea42f2271aca792a7b027ca5c1edfcba229e8e5a844c9ac3f336df35 golangci-lint-1.27.0-linux-armv7.tar.gz
|
||||||
|
bf781f05b0d393b4bf0a327d9e62926949a4f14d7774d950c4e009fc766ed1d4 golangci-lint.exe-1.27.0-windows-amd64.zip
|
||||||
|
bf781f05b0d393b4bf0a327d9e62926949a4f14d7774d950c4e009fc766ed1d4 golangci-lint-1.27.0-windows-amd64.zip
|
||||||
|
0e2a57d6ba709440d3ed018ef1037465fa010ed02595829092860e5cf863042e golangci-lint-1.27.0-freebsd-386.tar.gz
|
||||||
|
90205fc42ab5ed0096413e790d88ac9b4ed60f4c47e576d13dc0660f7ed4b013 golangci-lint-1.27.0-linux-arm64.tar.gz
|
||||||
|
8d345e4e88520e21c113d81978e89ad77fc5b13bfdf20e5bca86b83fc4261272 golangci-lint-1.27.0-linux-amd64.tar.gz
|
||||||
|
cc619634a77f18dc73df2a0725be13116d64328dc35131ca1737a850d6f76a59 golangci-lint-1.27.0-freebsd-armv7.tar.gz
|
||||||
|
fe683583cfc9eeec83e498c0d6159d87b5e1919dbe4b6c3b3913089642906069 golangci-lint-1.27.0-linux-s390x.tar.gz
|
||||||
|
058f5579bee75bdaacbaf75b75e1369f7ad877fd8b3b145aed17a17545de913e golangci-lint-1.27.0-freebsd-armv6.tar.gz
|
||||||
|
38e1e3dadbe3f56ab62b4de82ee0b88e8fad966d8dfd740a26ef94c2edef9818 golangci-lint-1.27.0-linux-armv6.tar.gz
|
||||||
|
071b34af5516f4e1ddcaea6011e18208f4f043e1af8ba21eeccad4585cb3d095 golangci-lint.exe-1.27.0-windows-386.zip
|
||||||
|
071b34af5516f4e1ddcaea6011e18208f4f043e1af8ba21eeccad4585cb3d095 golangci-lint-1.27.0-windows-386.zip
|
||||||
|
5f37e2b33914ecddb7cad38186ef4ec61d88172fc04f930fa0267c91151ff306 golangci-lint-1.27.0-linux-386.tar.gz
|
||||||
|
4d94cfb51fdebeb205f1d5a349ac2b683c30591c5150708073c1c329e15965f0 golangci-lint-1.27.0-freebsd-amd64.tar.gz
|
||||||
|
52572ba8ff07d5169c2365d3de3fec26dc55a97522094d13d1596199580fa281 golangci-lint-1.27.0-linux-ppc64le.tar.gz
|
||||||
|
3fb1a1683a29c6c0a8cd76135f62b606fbdd538d5a7aeab94af1af70ffdc2fd4 golangci-lint-1.27.0-darwin-amd64.tar.gz
|
||||||
|
|
@ -22,19 +22,18 @@ variables `PPA_SIGNING_KEY` and `PPA_SSH_KEY` on Travis.
|
||||||
|
|
||||||
We want to build go-ethereum with the most recent version of Go, irrespective of the Go
|
We want to build go-ethereum with the most recent version of Go, irrespective of the Go
|
||||||
version that is available in the main Ubuntu repository. In order to make this possible,
|
version that is available in the main Ubuntu repository. In order to make this possible,
|
||||||
our PPA depends on the ~gophers/ubuntu/archive PPA. Our source package build-depends on
|
we bundle the entire Go sources into our own source archive and start the built job by
|
||||||
golang-1.11, which is co-installable alongside the regular golang package. PPA dependencies
|
compiling Go and then using that to build go-ethereum. On Trusty we have a special case
|
||||||
can be edited at https://launchpad.net/%7Eethereum/+archive/ubuntu/ethereum/+edit-dependencies
|
requiring the `~gophers/ubuntu/archive` PPA since Trusty can't even build Go itself. PPA
|
||||||
|
deps are set at https://launchpad.net/%7Eethereum/+archive/ubuntu/ethereum/+edit-dependencies
|
||||||
|
|
||||||
## Building Packages Locally (for testing)
|
## Building Packages Locally (for testing)
|
||||||
|
|
||||||
You need to run Ubuntu to do test packaging.
|
You need to run Ubuntu to do test packaging.
|
||||||
|
|
||||||
Add the gophers PPA and install Go 1.11 and Debian packaging tools:
|
Install any version of Go and Debian packaging tools:
|
||||||
|
|
||||||
$ sudo apt-add-repository ppa:gophers/ubuntu/archive
|
$ sudo apt-get install build-essential golang-go devscripts debhelper python-bzrlib python-paramiko
|
||||||
$ sudo apt-get update
|
|
||||||
$ sudo apt-get install build-essential golang-1.11 devscripts debhelper python-bzrlib python-paramiko
|
|
||||||
|
|
||||||
Create the source packages:
|
Create the source packages:
|
||||||
|
|
||||||
|
|
@ -42,10 +41,10 @@ Create the source packages:
|
||||||
|
|
||||||
Then go into the source package directory for your running distribution and build the package:
|
Then go into the source package directory for your running distribution and build the package:
|
||||||
|
|
||||||
$ cd dist/ethereum-unstable-1.6.0+xenial
|
$ cd dist/ethereum-unstable-1.9.6+bionic
|
||||||
$ dpkg-buildpackage
|
$ dpkg-buildpackage
|
||||||
|
|
||||||
Built packages are placed in the dist/ directory.
|
Built packages are placed in the dist/ directory.
|
||||||
|
|
||||||
$ cd ..
|
$ cd ..
|
||||||
$ dpkg-deb -c geth-unstable_1.6.0+xenial_amd64.deb
|
$ dpkg-deb -c geth-unstable_1.9.6+bionic_amd64.deb
|
||||||
|
|
|
||||||
23
build/ci.go
23
build/ci.go
|
|
@ -67,7 +67,7 @@ var (
|
||||||
// Files that end up in the geth*.zip archive.
|
// Files that end up in the geth*.zip archive.
|
||||||
gethArchiveFiles = []string{
|
gethArchiveFiles = []string{
|
||||||
"COPYING",
|
"COPYING",
|
||||||
executablePath("bor"),
|
executablePath("geth"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Files that end up in the geth-alltools*.zip archive.
|
// Files that end up in the geth-alltools*.zip archive.
|
||||||
|
|
@ -76,7 +76,7 @@ var (
|
||||||
executablePath("abigen"),
|
executablePath("abigen"),
|
||||||
executablePath("bootnode"),
|
executablePath("bootnode"),
|
||||||
executablePath("evm"),
|
executablePath("evm"),
|
||||||
executablePath("bor"),
|
executablePath("geth"),
|
||||||
executablePath("puppeth"),
|
executablePath("puppeth"),
|
||||||
executablePath("rlpdump"),
|
executablePath("rlpdump"),
|
||||||
executablePath("wnode"),
|
executablePath("wnode"),
|
||||||
|
|
@ -98,7 +98,7 @@ var (
|
||||||
Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.",
|
Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
BinaryName: "bor",
|
BinaryName: "geth",
|
||||||
Description: "Ethereum CLI client.",
|
Description: "Ethereum CLI client.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -325,7 +325,7 @@ func doTest(cmdline []string) {
|
||||||
// Test a single package at a time. CI builders are slow
|
// Test a single package at a time. CI builders are slow
|
||||||
// and some tests run into timeouts under load.
|
// and some tests run into timeouts under load.
|
||||||
gotest := goTool("test", buildFlags(env)...)
|
gotest := goTool("test", buildFlags(env)...)
|
||||||
gotest.Args = append(gotest.Args, "-p", "1", "-timeout", "5m")
|
gotest.Args = append(gotest.Args, "-p", "1")
|
||||||
if *coverage {
|
if *coverage {
|
||||||
gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover")
|
gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover")
|
||||||
}
|
}
|
||||||
|
|
@ -356,7 +356,7 @@ func doLint(cmdline []string) {
|
||||||
|
|
||||||
// downloadLinter downloads and unpacks golangci-lint.
|
// downloadLinter downloads and unpacks golangci-lint.
|
||||||
func downloadLinter(cachedir string) string {
|
func downloadLinter(cachedir string) string {
|
||||||
const version = "1.22.2"
|
const version = "1.27.0"
|
||||||
|
|
||||||
csdb := build.MustLoadChecksums("build/checksums.txt")
|
csdb := build.MustLoadChecksums("build/checksums.txt")
|
||||||
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, runtime.GOARCH)
|
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, runtime.GOARCH)
|
||||||
|
|
@ -768,7 +768,7 @@ func doWindowsInstaller(cmdline []string) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
allTools = append(allTools, filepath.Base(file))
|
allTools = append(allTools, filepath.Base(file))
|
||||||
if filepath.Base(file) == "bor.exe" {
|
if filepath.Base(file) == "geth.exe" {
|
||||||
gethTool = file
|
gethTool = file
|
||||||
} else {
|
} else {
|
||||||
devTools = append(devTools, file)
|
devTools = append(devTools, file)
|
||||||
|
|
@ -782,7 +782,7 @@ func doWindowsInstaller(cmdline []string) {
|
||||||
"Geth": gethTool,
|
"Geth": gethTool,
|
||||||
"DevTools": devTools,
|
"DevTools": devTools,
|
||||||
}
|
}
|
||||||
build.Render("build/nsis.bor.nsi", filepath.Join(*workdir, "bor.nsi"), 0644, nil)
|
build.Render("build/nsis.geth.nsi", filepath.Join(*workdir, "geth.nsi"), 0644, nil)
|
||||||
build.Render("build/nsis.install.nsh", filepath.Join(*workdir, "install.nsh"), 0644, templateData)
|
build.Render("build/nsis.install.nsh", filepath.Join(*workdir, "install.nsh"), 0644, templateData)
|
||||||
build.Render("build/nsis.uninstall.nsh", filepath.Join(*workdir, "uninstall.nsh"), 0644, allTools)
|
build.Render("build/nsis.uninstall.nsh", filepath.Join(*workdir, "uninstall.nsh"), 0644, allTools)
|
||||||
build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil)
|
build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil)
|
||||||
|
|
@ -800,14 +800,14 @@ func doWindowsInstaller(cmdline []string) {
|
||||||
if env.Commit != "" {
|
if env.Commit != "" {
|
||||||
version[2] += "-" + env.Commit[:8]
|
version[2] += "-" + env.Commit[:8]
|
||||||
}
|
}
|
||||||
installer, _ := filepath.Abs("bor-" + archiveBasename(*arch, params.ArchiveVersion(env.Commit)) + ".exe")
|
installer, _ := filepath.Abs("geth-" + archiveBasename(*arch, params.ArchiveVersion(env.Commit)) + ".exe")
|
||||||
build.MustRunCommand("makensis.exe",
|
build.MustRunCommand("makensis.exe",
|
||||||
"/DOUTPUTFILE="+installer,
|
"/DOUTPUTFILE="+installer,
|
||||||
"/DMAJORVERSION="+version[0],
|
"/DMAJORVERSION="+version[0],
|
||||||
"/DMINORVERSION="+version[1],
|
"/DMINORVERSION="+version[1],
|
||||||
"/DBUILDVERSION="+version[2],
|
"/DBUILDVERSION="+version[2],
|
||||||
"/DARCH="+*arch,
|
"/DARCH="+*arch,
|
||||||
filepath.Join(*workdir, "bor.nsi"),
|
filepath.Join(*workdir, "geth.nsi"),
|
||||||
)
|
)
|
||||||
// Sign and publish installer.
|
// Sign and publish installer.
|
||||||
if err := archiveUpload(installer, *upload, *signer); err != nil {
|
if err := archiveUpload(installer, *upload, *signer); err != nil {
|
||||||
|
|
@ -961,7 +961,7 @@ func doXCodeFramework(cmdline []string) {
|
||||||
build.MustRun(bind)
|
build.MustRun(bind)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
archive := "bor-" + archiveBasename("ios", params.ArchiveVersion(env.Commit))
|
archive := "geth-" + archiveBasename("ios", params.ArchiveVersion(env.Commit))
|
||||||
if err := os.Mkdir(archive, os.ModePerm); err != nil {
|
if err := os.Mkdir(archive, os.ModePerm); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -1098,6 +1098,8 @@ func doPurge(cmdline []string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
fmt.Printf("Found %d blobs\n", len(blobs))
|
||||||
|
|
||||||
// Iterate over the blobs, collect and sort all unstable builds
|
// Iterate over the blobs, collect and sort all unstable builds
|
||||||
for i := 0; i < len(blobs); i++ {
|
for i := 0; i < len(blobs); i++ {
|
||||||
if !strings.Contains(blobs[i].Name, "unstable") {
|
if !strings.Contains(blobs[i].Name, "unstable") {
|
||||||
|
|
@ -1119,6 +1121,7 @@ func doPurge(cmdline []string) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fmt.Printf("Deleting %d blobs\n", len(blobs))
|
||||||
// Delete all marked as such and return
|
// Delete all marked as such and return
|
||||||
if err := build.AzureBlobstoreDelete(auth, blobs); err != nil {
|
if err := build.AzureBlobstoreDelete(auth, blobs); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Cleaning the Go cache only makes sense if we actually have Go installed... or
|
|
||||||
# if Go is actually callable. This does not hold true during deb packaging, so
|
|
||||||
# we need an explicit check to avoid build failures.
|
|
||||||
if ! command -v go > /dev/null; then
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
version_gt() {
|
|
||||||
test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
golang_version=$(go version |cut -d' ' -f3 |sed 's/go//')
|
|
||||||
|
|
||||||
# Clean go build cache when go version is greater than or equal to 1.10
|
|
||||||
if !(version_gt 1.10 $golang_version); then
|
|
||||||
go clean -cache
|
|
||||||
fi
|
|
||||||
|
|
@ -2,7 +2,7 @@ Source: {{.Name}}
|
||||||
Section: science
|
Section: science
|
||||||
Priority: extra
|
Priority: extra
|
||||||
Maintainer: {{.Author}}
|
Maintainer: {{.Author}}
|
||||||
Build-Depends: debhelper (>= 8.0.0), golang-1.11
|
Build-Depends: debhelper (>= 8.0.0), {{.GoBootPackage}}
|
||||||
Standards-Version: 3.9.5
|
Standards-Version: 3.9.5
|
||||||
Homepage: https://ethereum.org
|
Homepage: https://ethereum.org
|
||||||
Vcs-Git: git://github.com/maticnetwork/bor.git
|
Vcs-Git: git://github.com/maticnetwork/bor.git
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,27 @@
|
||||||
# Uncomment this to turn on verbose mode.
|
# Uncomment this to turn on verbose mode.
|
||||||
#export DH_VERBOSE=1
|
#export DH_VERBOSE=1
|
||||||
|
|
||||||
# Launchpad rejects Go's access to $HOME/.cache, use custom folder
|
# Launchpad rejects Go's access to $HOME, use custom folders
|
||||||
export GOCACHE=/tmp/go-build
|
export GOCACHE=/tmp/go-build
|
||||||
|
export GOPATH=/tmp/gopath
|
||||||
|
export GOROOT_BOOTSTRAP={{.GoBootPath}}
|
||||||
|
|
||||||
|
override_dh_auto_clean:
|
||||||
|
# Don't try to be smart Launchpad, we know our build rules better than you
|
||||||
|
|
||||||
override_dh_auto_build:
|
override_dh_auto_build:
|
||||||
build/env.sh /usr/lib/go-1.11/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
|
# We can't download a fresh Go within Launchpad, so we're shipping and building
|
||||||
|
# one on the fly. However, we can't build it inside the go-ethereum folder as
|
||||||
|
# bootstrapping clashes with go modules, so build in a sibling folder.
|
||||||
|
(mv .go ../ && cd ../.go/src && ./make.bash)
|
||||||
|
|
||||||
|
# We can't download external go modules within Launchpad, so we're shipping the
|
||||||
|
# entire dependency source cache with go-ethereum.
|
||||||
|
mkdir -p $(GOPATH)/pkg
|
||||||
|
mv .mod $(GOPATH)/pkg/mod
|
||||||
|
|
||||||
|
# A fresh Go was built, all dependency downloads faked, hope build works now
|
||||||
|
../.go/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
|
||||||
|
|
||||||
override_dh_auto_test:
|
override_dh_auto_test:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ Section "Geth" GETH_IDX
|
||||||
|
|
||||||
# Create start menu launcher
|
# Create start menu launcher
|
||||||
createDirectory "$SMPROGRAMS\${APPNAME}"
|
createDirectory "$SMPROGRAMS\${APPNAME}"
|
||||||
createShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\geth.exe" "--fast" "--cache=512"
|
createShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\geth.exe"
|
||||||
createShortCut "$SMPROGRAMS\${APPNAME}\Attach.lnk" "$INSTDIR\geth.exe" "attach" "" ""
|
createShortCut "$SMPROGRAMS\${APPNAME}\Attach.lnk" "$INSTDIR\geth.exe" "attach"
|
||||||
createShortCut "$SMPROGRAMS\${APPNAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "" ""
|
createShortCut "$SMPROGRAMS\${APPNAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe"
|
||||||
|
|
||||||
# Firewall - remove rules (if exists)
|
# Firewall - remove rules (if exists)
|
||||||
SimpleFC::AdvRemoveRule "Geth incoming peers (TCP:30303)"
|
SimpleFC::AdvRemoveRule "Geth incoming peers (TCP:30303)"
|
||||||
|
|
|
||||||
|
|
@ -62,16 +62,22 @@ var (
|
||||||
skipPrefixes = []string{
|
skipPrefixes = []string{
|
||||||
// boring stuff
|
// boring stuff
|
||||||
"vendor/", "tests/testdata/", "build/",
|
"vendor/", "tests/testdata/", "build/",
|
||||||
|
|
||||||
// don't relicense vendored sources
|
// don't relicense vendored sources
|
||||||
"cmd/internal/browser",
|
"cmd/internal/browser",
|
||||||
|
"common/bitutil/bitutil",
|
||||||
|
"common/prque/",
|
||||||
"consensus/ethash/xor.go",
|
"consensus/ethash/xor.go",
|
||||||
"crypto/bn256/",
|
"crypto/bn256/",
|
||||||
"crypto/ecies/",
|
"crypto/ecies/",
|
||||||
"crypto/secp256k1/curve.go",
|
"graphql/graphiql.go",
|
||||||
"crypto/sha3/",
|
|
||||||
"internal/jsre/deps",
|
"internal/jsre/deps",
|
||||||
"log/",
|
"log/",
|
||||||
"common/bitutil/bitutil",
|
"metrics/",
|
||||||
|
"signer/rules/deps",
|
||||||
|
|
||||||
|
// skip special licenses
|
||||||
|
"crypto/secp256k1", // Relicensed to BSD-3 via https://github.com/ethereum/go-ethereum/pull/17225
|
||||||
}
|
}
|
||||||
|
|
||||||
// paths with this prefix are licensed as GPL. all other files are LGPL.
|
// paths with this prefix are licensed as GPL. all other files are LGPL.
|
||||||
|
|
@ -144,6 +150,13 @@ func (i info) gpl() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// authors implements the sort.Interface for strings in case-insensitive mode.
|
||||||
|
type authors []string
|
||||||
|
|
||||||
|
func (as authors) Len() int { return len(as) }
|
||||||
|
func (as authors) Less(i, j int) bool { return strings.ToLower(as[i]) < strings.ToLower(as[j]) }
|
||||||
|
func (as authors) Swap(i, j int) { as[i], as[j] = as[j], as[i] }
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
files = getFiles()
|
files = getFiles()
|
||||||
|
|
@ -262,27 +275,32 @@ func mailmapLookup(authors []string) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeAuthors(files []string) {
|
func writeAuthors(files []string) {
|
||||||
merge := make(map[string]bool)
|
var (
|
||||||
// Add authors that Git reports as contributorxs.
|
dedup = make(map[string]bool)
|
||||||
|
list []string
|
||||||
|
)
|
||||||
|
// Add authors that Git reports as contributors.
|
||||||
// This is the primary source of author information.
|
// This is the primary source of author information.
|
||||||
for _, a := range gitAuthors(files) {
|
for _, a := range gitAuthors(files) {
|
||||||
merge[a] = true
|
if la := strings.ToLower(a); !dedup[la] {
|
||||||
|
list = append(list, a)
|
||||||
|
dedup[la] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Add existing authors from the file. This should ensure that we
|
// Add existing authors from the file. This should ensure that we
|
||||||
// never lose authors, even if Git stops listing them. We can also
|
// never lose authors, even if Git stops listing them. We can also
|
||||||
// add authors manually this way.
|
// add authors manually this way.
|
||||||
for _, a := range readAuthors() {
|
for _, a := range readAuthors() {
|
||||||
merge[a] = true
|
if la := strings.ToLower(a); !dedup[la] {
|
||||||
|
list = append(list, a)
|
||||||
|
dedup[la] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Write sorted list of authors back to the file.
|
// Write sorted list of authors back to the file.
|
||||||
var result []string
|
sort.Sort(authors(list))
|
||||||
for a := range merge {
|
|
||||||
result = append(result, a)
|
|
||||||
}
|
|
||||||
sort.Strings(result)
|
|
||||||
content := new(bytes.Buffer)
|
content := new(bytes.Buffer)
|
||||||
content.WriteString(authorsFileHeader)
|
content.WriteString(authorsFileHeader)
|
||||||
for _, a := range result {
|
for _, a := range list {
|
||||||
content.WriteString(a)
|
content.WriteString(a)
|
||||||
content.WriteString("\n")
|
content.WriteString("\n")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
74
cmd/abidump/main.go
Normal file
74
cmd/abidump/main.go
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2019 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/hex"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/maticnetwork/bor/signer/core"
|
||||||
|
"github.com/maticnetwork/bor/signer/fourbyte"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "<hexdata>")
|
||||||
|
flag.PrintDefaults()
|
||||||
|
fmt.Fprintln(os.Stderr, `
|
||||||
|
Parses the given ABI data and tries to interpret it from the fourbyte database.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(data []byte) {
|
||||||
|
db, err := fourbyte.New()
|
||||||
|
if err != nil {
|
||||||
|
die(err)
|
||||||
|
}
|
||||||
|
messages := core.ValidationMessages{}
|
||||||
|
db.ValidateCallData(nil, data, &messages)
|
||||||
|
for _, m := range messages.Messages {
|
||||||
|
fmt.Printf("%v: %v\n", m.Typ, m.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example
|
||||||
|
// ./abidump a9059cbb000000000000000000000000ea0e2dc7d65a50e77fc7e84bff3fd2a9e781ff5c0000000000000000000000000000000000000000000000015af1d78b58c40000
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case flag.NArg() == 1:
|
||||||
|
hexdata := flag.Arg(0)
|
||||||
|
data, err := hex.DecodeString(strings.TrimPrefix(hexdata, "0x"))
|
||||||
|
if err != nil {
|
||||||
|
die(err)
|
||||||
|
}
|
||||||
|
parse(data)
|
||||||
|
default:
|
||||||
|
fmt.Fprintln(os.Stderr, "Error: one argument needed")
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func die(args ...interface{}) {
|
||||||
|
fmt.Fprintln(os.Stderr, args...)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2019 The go-ethereum Authors
|
// Copyright 2016 The go-ethereum Authors
|
||||||
// This file is part of go-ethereum.
|
// This file is part of go-ethereum.
|
||||||
//
|
//
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -21,8 +21,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/maticnetwork/bor/accounts/abi"
|
||||||
"github.com/maticnetwork/bor/accounts/abi/bind"
|
"github.com/maticnetwork/bor/accounts/abi/bind"
|
||||||
"github.com/maticnetwork/bor/cmd/utils"
|
"github.com/maticnetwork/bor/cmd/utils"
|
||||||
"github.com/maticnetwork/bor/common/compiler"
|
"github.com/maticnetwork/bor/common/compiler"
|
||||||
|
|
@ -31,19 +34,6 @@ import (
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
commandHelperTemplate = `{{.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}}{{"\t"}}{{.}}
|
|
||||||
{{end}}
|
|
||||||
{{end}}`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Git SHA1 commit hash of the release (set via linker flags)
|
// Git SHA1 commit hash of the release (set via linker flags)
|
||||||
gitCommit = ""
|
gitCommit = ""
|
||||||
|
|
@ -103,6 +93,10 @@ var (
|
||||||
Usage: "Destination language for the bindings (go, java, objc)",
|
Usage: "Destination language for the bindings (go, java, objc)",
|
||||||
Value: "go",
|
Value: "go",
|
||||||
}
|
}
|
||||||
|
aliasFlag = cli.StringFlag{
|
||||||
|
Name: "alias",
|
||||||
|
Usage: "Comma separated aliases for function and event renaming, e.g. foo=bar",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -120,9 +114,10 @@ func init() {
|
||||||
pkgFlag,
|
pkgFlag,
|
||||||
outFlag,
|
outFlag,
|
||||||
langFlag,
|
langFlag,
|
||||||
|
aliasFlag,
|
||||||
}
|
}
|
||||||
app.Action = utils.MigrateFlags(abigen)
|
app.Action = utils.MigrateFlags(abigen)
|
||||||
cli.CommandHelpTemplate = commandHelperTemplate
|
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
func abigen(c *cli.Context) error {
|
func abigen(c *cli.Context) error {
|
||||||
|
|
@ -144,11 +139,12 @@ func abigen(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
// If the entire solidity code was specified, build and bind based on that
|
// If the entire solidity code was specified, build and bind based on that
|
||||||
var (
|
var (
|
||||||
abis []string
|
abis []string
|
||||||
bins []string
|
bins []string
|
||||||
types []string
|
types []string
|
||||||
sigs []map[string]string
|
sigs []map[string]string
|
||||||
libs = make(map[string]string)
|
libs = make(map[string]string)
|
||||||
|
aliases = make(map[string]string)
|
||||||
)
|
)
|
||||||
if c.GlobalString(abiFlag.Name) != "" {
|
if c.GlobalString(abiFlag.Name) != "" {
|
||||||
// Load up the ABI, optional bytecode and type name from the parameters
|
// Load up the ABI, optional bytecode and type name from the parameters
|
||||||
|
|
@ -199,10 +195,22 @@ func abigen(c *cli.Context) error {
|
||||||
utils.Fatalf("Failed to build Solidity contract: %v", err)
|
utils.Fatalf("Failed to build Solidity contract: %v", err)
|
||||||
}
|
}
|
||||||
case c.GlobalIsSet(vyFlag.Name):
|
case c.GlobalIsSet(vyFlag.Name):
|
||||||
contracts, err = compiler.CompileVyper(c.GlobalString(vyperFlag.Name), c.GlobalString(vyFlag.Name))
|
output, err := compiler.CompileVyper(c.GlobalString(vyperFlag.Name), c.GlobalString(vyFlag.Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to build Vyper contract: %v", err)
|
utils.Fatalf("Failed to build Vyper contract: %v", err)
|
||||||
}
|
}
|
||||||
|
contracts = make(map[string]*compiler.Contract)
|
||||||
|
for n, contract := range output {
|
||||||
|
name := n
|
||||||
|
// Sanitize the combined json names to match the
|
||||||
|
// format expected by solidity.
|
||||||
|
if !strings.Contains(n, ":") {
|
||||||
|
// Remove extra path components
|
||||||
|
name = abi.ToCamelCase(strings.TrimSuffix(filepath.Base(name), ".vy"))
|
||||||
|
}
|
||||||
|
contracts[name] = contract
|
||||||
|
}
|
||||||
|
|
||||||
case c.GlobalIsSet(jsonFlag.Name):
|
case c.GlobalIsSet(jsonFlag.Name):
|
||||||
jsonOutput, err := ioutil.ReadFile(c.GlobalString(jsonFlag.Name))
|
jsonOutput, err := ioutil.ReadFile(c.GlobalString(jsonFlag.Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -232,8 +240,20 @@ func abigen(c *cli.Context) error {
|
||||||
libs[libPattern] = nameParts[len(nameParts)-1]
|
libs[libPattern] = nameParts[len(nameParts)-1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Extract all aliases from the flags
|
||||||
|
if c.GlobalIsSet(aliasFlag.Name) {
|
||||||
|
// We support multi-versions for aliasing
|
||||||
|
// e.g.
|
||||||
|
// foo=bar,foo2=bar2
|
||||||
|
// foo:bar,foo2:bar2
|
||||||
|
re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
|
||||||
|
submatches := re.FindAllStringSubmatch(c.GlobalString(aliasFlag.Name), -1)
|
||||||
|
for _, match := range submatches {
|
||||||
|
aliases[match[1]] = match[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
// Generate the contract binding
|
// Generate the contract binding
|
||||||
code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs)
|
code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs, aliases)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to generate ABI binding: %v", err)
|
utils.Fatalf("Failed to generate ABI binding: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,9 @@ func main() {
|
||||||
if err = crypto.SaveECDSA(*genKey, nodeKey); err != nil {
|
if err = crypto.SaveECDSA(*genKey, nodeKey); err != nil {
|
||||||
utils.Fatalf("%v", err)
|
utils.Fatalf("%v", err)
|
||||||
}
|
}
|
||||||
return
|
if !*writeAddr {
|
||||||
|
return
|
||||||
|
}
|
||||||
case *nodeKeyFile == "" && *nodeKeyHex == "":
|
case *nodeKeyFile == "" && *nodeKeyHex == "":
|
||||||
utils.Fatalf("Use -nodekey or -nodekeyhex to specify a private key")
|
utils.Fatalf("Use -nodekey or -nodekeyhex to specify a private key")
|
||||||
case *nodeKeyFile != "" && *nodeKeyHex != "":
|
case *nodeKeyFile != "" && *nodeKeyHex != "":
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"github.com/maticnetwork/bor/accounts"
|
"github.com/maticnetwork/bor/accounts"
|
||||||
"github.com/maticnetwork/bor/accounts/keystore"
|
"github.com/maticnetwork/bor/accounts/keystore"
|
||||||
"github.com/maticnetwork/bor/cmd/utils"
|
"github.com/maticnetwork/bor/cmd/utils"
|
||||||
"github.com/maticnetwork/bor/console"
|
|
||||||
"github.com/maticnetwork/bor/crypto"
|
"github.com/maticnetwork/bor/crypto"
|
||||||
"github.com/maticnetwork/bor/log"
|
"github.com/maticnetwork/bor/log"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
|
@ -116,11 +115,11 @@ Print a short summary of all accounts`,
|
||||||
|
|
||||||
Creates a new account and prints the address.
|
Creates a new account and prints the address.
|
||||||
|
|
||||||
The account is saved in encrypted format, you are prompted for a passphrase.
|
The account is saved in encrypted format, you are prompted for a password.
|
||||||
|
|
||||||
You must remember this passphrase to unlock your account in the future.
|
You must remember this password to unlock your account in the future.
|
||||||
|
|
||||||
For non-interactive use the passphrase can be specified with the --password flag:
|
For non-interactive use the password can be specified with the --password flag:
|
||||||
|
|
||||||
Note, this is meant to be used for testing only, it is a bad idea to save your
|
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.
|
password to file or expose in any other way.
|
||||||
|
|
@ -142,12 +141,12 @@ password to file or expose in any other way.
|
||||||
Update an existing account.
|
Update an existing account.
|
||||||
|
|
||||||
The account is saved in the newest version in encrypted format, you are prompted
|
The account is saved in the newest version in encrypted format, you are prompted
|
||||||
for a passphrase to unlock the account and another to save the updated file.
|
for a password to unlock the account and another to save the updated file.
|
||||||
|
|
||||||
This same command can therefore be used to migrate an account of a deprecated
|
This same command can therefore be used to migrate an account of a deprecated
|
||||||
format to the newest format or change the password for an account.
|
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:
|
For non-interactive use the password can be specified with the --password flag:
|
||||||
|
|
||||||
geth account update [options] <address>
|
geth account update [options] <address>
|
||||||
|
|
||||||
|
|
@ -174,11 +173,11 @@ Prints the address.
|
||||||
|
|
||||||
The keyfile is assumed to contain an unencrypted private key in hexadecimal format.
|
The keyfile is assumed to contain an unencrypted private key in hexadecimal format.
|
||||||
|
|
||||||
The account is saved in encrypted format, you are prompted for a passphrase.
|
The account is saved in encrypted format, you are prompted for a password.
|
||||||
|
|
||||||
You must remember this passphrase to unlock your account in the future.
|
You must remember this password to unlock your account in the future.
|
||||||
|
|
||||||
For non-interactive use the passphrase can be specified with the -password flag:
|
For non-interactive use the password can be specified with the -password flag:
|
||||||
|
|
||||||
geth account import [options] <keyfile>
|
geth account import [options] <keyfile>
|
||||||
|
|
||||||
|
|
@ -212,7 +211,7 @@ func unlockAccount(ks *keystore.KeyStore, address string, i int, passwords []str
|
||||||
}
|
}
|
||||||
for trials := 0; trials < 3; trials++ {
|
for trials := 0; trials < 3; trials++ {
|
||||||
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
|
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
|
||||||
password := getPassPhrase(prompt, false, i, passwords)
|
password := utils.GetPassPhraseWithList(prompt, false, i, passwords)
|
||||||
err = ks.Unlock(account, password)
|
err = ks.Unlock(account, password)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Info("Unlocked account", "address", account.Address.Hex())
|
log.Info("Unlocked account", "address", account.Address.Hex())
|
||||||
|
|
@ -233,42 +232,12 @@ func unlockAccount(ks *keystore.KeyStore, address string, i int, passwords []str
|
||||||
return accounts.Account{}, ""
|
return accounts.Account{}, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
|
||||||
if len(passwords) > 0 {
|
|
||||||
if i < len(passwords) {
|
|
||||||
return passwords[i]
|
|
||||||
}
|
|
||||||
return passwords[len(passwords)-1]
|
|
||||||
}
|
|
||||||
// Otherwise prompt the user for the password
|
|
||||||
if prompt != "" {
|
|
||||||
fmt.Println(prompt)
|
|
||||||
}
|
|
||||||
password, err := console.Stdin.PromptPassword("Passphrase: ")
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to read passphrase: %v", err)
|
|
||||||
}
|
|
||||||
if confirmation {
|
|
||||||
confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ")
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to read passphrase confirmation: %v", err)
|
|
||||||
}
|
|
||||||
if password != confirm {
|
|
||||||
utils.Fatalf("Passphrases do not match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return password
|
|
||||||
}
|
|
||||||
|
|
||||||
func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account {
|
func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account {
|
||||||
fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
|
fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
|
||||||
for _, a := range err.Matches {
|
for _, a := range err.Matches {
|
||||||
fmt.Println(" ", a.URL)
|
fmt.Println(" ", a.URL)
|
||||||
}
|
}
|
||||||
fmt.Println("Testing your passphrase against all of them...")
|
fmt.Println("Testing your password against all of them...")
|
||||||
var match *accounts.Account
|
var match *accounts.Account
|
||||||
for _, a := range err.Matches {
|
for _, a := range err.Matches {
|
||||||
if err := ks.Unlock(a, auth); err == nil {
|
if err := ks.Unlock(a, auth); err == nil {
|
||||||
|
|
@ -279,7 +248,7 @@ func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrErr
|
||||||
if match == nil {
|
if match == nil {
|
||||||
utils.Fatalf("None of the listed files could be unlocked.")
|
utils.Fatalf("None of the listed files could be unlocked.")
|
||||||
}
|
}
|
||||||
fmt.Printf("Your passphrase unlocked %s\n", match.URL)
|
fmt.Printf("Your password unlocked %s\n", match.URL)
|
||||||
fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
|
fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
|
||||||
for _, a := range err.Matches {
|
for _, a := range err.Matches {
|
||||||
if a != *match {
|
if a != *match {
|
||||||
|
|
@ -305,7 +274,7 @@ func accountCreate(ctx *cli.Context) error {
|
||||||
utils.Fatalf("Failed to read configuration: %v", err)
|
utils.Fatalf("Failed to read configuration: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
password := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
||||||
|
|
||||||
account, err := keystore.StoreKey(keydir, password, scryptN, scryptP)
|
account, err := keystore.StoreKey(keydir, password, scryptN, scryptP)
|
||||||
|
|
||||||
|
|
@ -333,7 +302,7 @@ func accountUpdate(ctx *cli.Context) error {
|
||||||
|
|
||||||
for _, addr := range ctx.Args() {
|
for _, addr := range ctx.Args() {
|
||||||
account, oldPassword := unlockAccount(ks, addr, 0, nil)
|
account, oldPassword := unlockAccount(ks, addr, 0, nil)
|
||||||
newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
|
newPassword := utils.GetPassPhraseWithList("Please give a new password. Do not forget this password.", true, 0, nil)
|
||||||
if err := ks.Update(account, oldPassword, newPassword); err != nil {
|
if err := ks.Update(account, oldPassword, newPassword); err != nil {
|
||||||
utils.Fatalf("Could not update the account: %v", err)
|
utils.Fatalf("Could not update the account: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -352,7 +321,7 @@ func importWallet(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, _ := makeConfigNode(ctx)
|
stack, _ := makeConfigNode(ctx)
|
||||||
passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
|
passphrase := utils.GetPassPhraseWithList("", false, 0, utils.MakePasswordList(ctx))
|
||||||
|
|
||||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
||||||
acct, err := ks.ImportPreSaleKey(keyJSON, passphrase)
|
acct, err := ks.ImportPreSaleKey(keyJSON, passphrase)
|
||||||
|
|
@ -373,7 +342,7 @@ func accountImport(ctx *cli.Context) error {
|
||||||
utils.Fatalf("Failed to load the private key: %v", err)
|
utils.Fatalf("Failed to load the private key: %v", err)
|
||||||
}
|
}
|
||||||
stack, _ := makeConfigNode(ctx)
|
stack, _ := makeConfigNode(ctx)
|
||||||
passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
passphrase := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
||||||
|
|
||||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
||||||
acct, err := ks.ImportECDSA(key, passphrase)
|
acct, err := ks.ImportECDSA(key, passphrase)
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,8 @@ func TestAccountNew(t *testing.T) {
|
||||||
geth.Expect(`
|
geth.Expect(`
|
||||||
Your new account is locked with a password. Please give a password. Do not forget this password.
|
Your new account is locked with a password. Please give a password. Do not forget this password.
|
||||||
!! Unsupported terminal, password will be echoed.
|
!! Unsupported terminal, password will be echoed.
|
||||||
Passphrase: {{.InputLine "foobar"}}
|
Password: {{.InputLine "foobar"}}
|
||||||
Repeat passphrase: {{.InputLine "foobar"}}
|
Repeat password: {{.InputLine "foobar"}}
|
||||||
|
|
||||||
Your new key was generated
|
Your new key was generated
|
||||||
`)
|
`)
|
||||||
|
|
@ -88,15 +88,51 @@ Path of the secret key file: .*UTC--.+--[0-9a-f]{40}
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccountImport(t *testing.T) {
|
||||||
|
tests := []struct{ name, key, output string }{
|
||||||
|
{
|
||||||
|
name: "correct account",
|
||||||
|
key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||||
|
output: "Address: {fcad0b19bb29d4674531d6f115237e16afce377c}\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid character",
|
||||||
|
key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1",
|
||||||
|
output: "Fatal: Failed to load the private key: invalid character '1' at end of key file\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
importAccountWithExpect(t, test.key, test.output)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func importAccountWithExpect(t *testing.T, key string, expected string) {
|
||||||
|
dir := tmpdir(t)
|
||||||
|
keyfile := filepath.Join(dir, "key.prv")
|
||||||
|
if err := ioutil.WriteFile(keyfile, []byte(key), 0600); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
passwordFile := filepath.Join(dir, "password.txt")
|
||||||
|
if err := ioutil.WriteFile(passwordFile, []byte("foobar"), 0600); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
geth := runGeth(t, "account", "import", keyfile, "-password", passwordFile)
|
||||||
|
defer geth.ExpectExit()
|
||||||
|
geth.Expect(expected)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccountNewBadRepeat(t *testing.T) {
|
func TestAccountNewBadRepeat(t *testing.T) {
|
||||||
geth := runGeth(t, "account", "new", "--lightkdf")
|
geth := runGeth(t, "account", "new", "--lightkdf")
|
||||||
defer geth.ExpectExit()
|
defer geth.ExpectExit()
|
||||||
geth.Expect(`
|
geth.Expect(`
|
||||||
Your new account is locked with a password. Please give a password. Do not forget this password.
|
Your new account is locked with a password. Please give a password. Do not forget this password.
|
||||||
!! Unsupported terminal, password will be echoed.
|
!! Unsupported terminal, password will be echoed.
|
||||||
Passphrase: {{.InputLine "something"}}
|
Password: {{.InputLine "something"}}
|
||||||
Repeat passphrase: {{.InputLine "something else"}}
|
Repeat password: {{.InputLine "something else"}}
|
||||||
Fatal: Passphrases do not match
|
Fatal: Passwords do not match
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,10 +145,10 @@ func TestAccountUpdate(t *testing.T) {
|
||||||
geth.Expect(`
|
geth.Expect(`
|
||||||
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
||||||
!! Unsupported terminal, password will be echoed.
|
!! Unsupported terminal, password will be echoed.
|
||||||
Passphrase: {{.InputLine "foobar"}}
|
Password: {{.InputLine "foobar"}}
|
||||||
Please give a new password. Do not forget this password.
|
Please give a new password. Do not forget this password.
|
||||||
Passphrase: {{.InputLine "foobar2"}}
|
Password: {{.InputLine "foobar2"}}
|
||||||
Repeat passphrase: {{.InputLine "foobar2"}}
|
Repeat password: {{.InputLine "foobar2"}}
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,7 +157,7 @@ func TestWalletImport(t *testing.T) {
|
||||||
defer geth.ExpectExit()
|
defer geth.ExpectExit()
|
||||||
geth.Expect(`
|
geth.Expect(`
|
||||||
!! Unsupported terminal, password will be echoed.
|
!! Unsupported terminal, password will be echoed.
|
||||||
Passphrase: {{.InputLine "foo"}}
|
Password: {{.InputLine "foo"}}
|
||||||
Address: {d4584b5f6229b7be90727b0fc8c6b91bb427821f}
|
Address: {d4584b5f6229b7be90727b0fc8c6b91bb427821f}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
@ -136,8 +172,8 @@ func TestWalletImportBadPassword(t *testing.T) {
|
||||||
defer geth.ExpectExit()
|
defer geth.ExpectExit()
|
||||||
geth.Expect(`
|
geth.Expect(`
|
||||||
!! Unsupported terminal, password will be echoed.
|
!! Unsupported terminal, password will be echoed.
|
||||||
Passphrase: {{.InputLine "wrong"}}
|
Password: {{.InputLine "wrong"}}
|
||||||
Fatal: could not decrypt key with given passphrase
|
Fatal: could not decrypt key with given password
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,7 +186,7 @@ func TestUnlockFlag(t *testing.T) {
|
||||||
geth.Expect(`
|
geth.Expect(`
|
||||||
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
||||||
!! Unsupported terminal, password will be echoed.
|
!! Unsupported terminal, password will be echoed.
|
||||||
Passphrase: {{.InputLine "foobar"}}
|
Password: {{.InputLine "foobar"}}
|
||||||
`)
|
`)
|
||||||
geth.ExpectExit()
|
geth.ExpectExit()
|
||||||
|
|
||||||
|
|
@ -174,12 +210,12 @@ func TestUnlockFlagWrongPassword(t *testing.T) {
|
||||||
geth.Expect(`
|
geth.Expect(`
|
||||||
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
||||||
!! Unsupported terminal, password will be echoed.
|
!! Unsupported terminal, password will be echoed.
|
||||||
Passphrase: {{.InputLine "wrong1"}}
|
Password: {{.InputLine "wrong1"}}
|
||||||
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 2/3
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 2/3
|
||||||
Passphrase: {{.InputLine "wrong2"}}
|
Password: {{.InputLine "wrong2"}}
|
||||||
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 3/3
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 3/3
|
||||||
Passphrase: {{.InputLine "wrong3"}}
|
Password: {{.InputLine "wrong3"}}
|
||||||
Fatal: Failed to unlock account f466859ead1932d743d622cb74fc058882e8648a (could not decrypt key with given passphrase)
|
Fatal: Failed to unlock account f466859ead1932d743d622cb74fc058882e8648a (could not decrypt key with given password)
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,9 +229,9 @@ func TestUnlockFlagMultiIndex(t *testing.T) {
|
||||||
geth.Expect(`
|
geth.Expect(`
|
||||||
Unlocking account 0 | Attempt 1/3
|
Unlocking account 0 | Attempt 1/3
|
||||||
!! Unsupported terminal, password will be echoed.
|
!! Unsupported terminal, password will be echoed.
|
||||||
Passphrase: {{.InputLine "foobar"}}
|
Password: {{.InputLine "foobar"}}
|
||||||
Unlocking account 2 | Attempt 1/3
|
Unlocking account 2 | Attempt 1/3
|
||||||
Passphrase: {{.InputLine "foobar"}}
|
Password: {{.InputLine "foobar"}}
|
||||||
`)
|
`)
|
||||||
geth.ExpectExit()
|
geth.ExpectExit()
|
||||||
|
|
||||||
|
|
@ -238,7 +274,7 @@ func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) {
|
||||||
"--password", "testdata/wrong-passwords.txt", "--unlock", "0,2")
|
"--password", "testdata/wrong-passwords.txt", "--unlock", "0,2")
|
||||||
defer geth.ExpectExit()
|
defer geth.ExpectExit()
|
||||||
geth.Expect(`
|
geth.Expect(`
|
||||||
Fatal: Failed to unlock account 0 (could not decrypt key with given passphrase)
|
Fatal: Failed to unlock account 0 (could not decrypt key with given password)
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -258,12 +294,12 @@ func TestUnlockFlagAmbiguous(t *testing.T) {
|
||||||
geth.Expect(`
|
geth.Expect(`
|
||||||
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
||||||
!! Unsupported terminal, password will be echoed.
|
!! Unsupported terminal, password will be echoed.
|
||||||
Passphrase: {{.InputLine "foobar"}}
|
Password: {{.InputLine "foobar"}}
|
||||||
Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
|
Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
|
||||||
keystore://{{keypath "1"}}
|
keystore://{{keypath "1"}}
|
||||||
keystore://{{keypath "2"}}
|
keystore://{{keypath "2"}}
|
||||||
Testing your passphrase against all of them...
|
Testing your password against all of them...
|
||||||
Your passphrase unlocked keystore://{{keypath "1"}}
|
Your password unlocked keystore://{{keypath "1"}}
|
||||||
In order to avoid this warning, you need to remove the following duplicate key files:
|
In order to avoid this warning, you need to remove the following duplicate key files:
|
||||||
keystore://{{keypath "2"}}
|
keystore://{{keypath "2"}}
|
||||||
`)
|
`)
|
||||||
|
|
@ -295,11 +331,11 @@ func TestUnlockFlagAmbiguousWrongPassword(t *testing.T) {
|
||||||
geth.Expect(`
|
geth.Expect(`
|
||||||
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
|
||||||
!! Unsupported terminal, password will be echoed.
|
!! Unsupported terminal, password will be echoed.
|
||||||
Passphrase: {{.InputLine "wrong"}}
|
Password: {{.InputLine "wrong"}}
|
||||||
Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
|
Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
|
||||||
keystore://{{keypath "1"}}
|
keystore://{{keypath "1"}}
|
||||||
keystore://{{keypath "2"}}
|
keystore://{{keypath "2"}}
|
||||||
Testing your passphrase against all of them...
|
Testing your password against all of them...
|
||||||
Fatal: None of the listed files could be unlocked.
|
Fatal: None of the listed files could be unlocked.
|
||||||
`)
|
`)
|
||||||
geth.ExpectExit()
|
geth.ExpectExit()
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import (
|
||||||
|
|
||||||
"github.com/maticnetwork/bor/cmd/utils"
|
"github.com/maticnetwork/bor/cmd/utils"
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
"github.com/maticnetwork/bor/console"
|
"github.com/maticnetwork/bor/console/prompt"
|
||||||
"github.com/maticnetwork/bor/core"
|
"github.com/maticnetwork/bor/core"
|
||||||
"github.com/maticnetwork/bor/core/rawdb"
|
"github.com/maticnetwork/bor/core/rawdb"
|
||||||
"github.com/maticnetwork/bor/core/state"
|
"github.com/maticnetwork/bor/core/state"
|
||||||
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"github.com/maticnetwork/bor/eth/downloader"
|
"github.com/maticnetwork/bor/eth/downloader"
|
||||||
"github.com/maticnetwork/bor/event"
|
"github.com/maticnetwork/bor/event"
|
||||||
"github.com/maticnetwork/bor/log"
|
"github.com/maticnetwork/bor/log"
|
||||||
|
"github.com/maticnetwork/bor/metrics"
|
||||||
"github.com/maticnetwork/bor/trie"
|
"github.com/maticnetwork/bor/trie"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
@ -56,6 +57,18 @@ This is a destructive action and changes the network in which you will be
|
||||||
participating.
|
participating.
|
||||||
|
|
||||||
It expects the genesis file as argument.`,
|
It expects the genesis file as argument.`,
|
||||||
|
}
|
||||||
|
dumpGenesisCommand = cli.Command{
|
||||||
|
Action: utils.MigrateFlags(dumpGenesis),
|
||||||
|
Name: "dumpgenesis",
|
||||||
|
Usage: "Dumps genesis block JSON configuration to stdout",
|
||||||
|
ArgsUsage: "",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
utils.DataDirFlag,
|
||||||
|
},
|
||||||
|
Category: "BLOCKCHAIN COMMANDS",
|
||||||
|
Description: `
|
||||||
|
The dumpgenesis command dumps the genesis block configuration in JSON format to stdout.`,
|
||||||
}
|
}
|
||||||
importCommand = cli.Command{
|
importCommand = cli.Command{
|
||||||
Action: utils.MigrateFlags(importChain),
|
Action: utils.MigrateFlags(importChain),
|
||||||
|
|
@ -65,11 +78,24 @@ It expects the genesis file as argument.`,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
utils.DataDirFlag,
|
utils.DataDirFlag,
|
||||||
utils.HeimdallURLFlag,
|
utils.HeimdallURLFlag,
|
||||||
|
utils.WithoutHeimdallFlag,
|
||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
utils.GCModeFlag,
|
utils.GCModeFlag,
|
||||||
|
utils.SnapshotFlag,
|
||||||
utils.CacheDatabaseFlag,
|
utils.CacheDatabaseFlag,
|
||||||
utils.CacheGCFlag,
|
utils.CacheGCFlag,
|
||||||
|
utils.MetricsEnabledFlag,
|
||||||
|
utils.MetricsEnabledExpensiveFlag,
|
||||||
|
utils.MetricsHTTPFlag,
|
||||||
|
utils.MetricsPortFlag,
|
||||||
|
utils.MetricsEnableInfluxDBFlag,
|
||||||
|
utils.MetricsInfluxDBEndpointFlag,
|
||||||
|
utils.MetricsInfluxDBDatabaseFlag,
|
||||||
|
utils.MetricsInfluxDBUsernameFlag,
|
||||||
|
utils.MetricsInfluxDBPasswordFlag,
|
||||||
|
utils.MetricsInfluxDBTagsFlag,
|
||||||
|
utils.TxLookupLimitFlag,
|
||||||
},
|
},
|
||||||
Category: "BLOCKCHAIN COMMANDS",
|
Category: "BLOCKCHAIN COMMANDS",
|
||||||
Description: `
|
Description: `
|
||||||
|
|
@ -135,8 +161,12 @@ The export-preimages command export hash preimages to an RLP encoded stream`,
|
||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
utils.FakePoWFlag,
|
utils.FakePoWFlag,
|
||||||
utils.TestnetFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
|
utils.TxLookupLimitFlag,
|
||||||
|
utils.GoerliFlag,
|
||||||
|
utils.YoloV1Flag,
|
||||||
|
utils.LegacyTestnetFlag,
|
||||||
},
|
},
|
||||||
Category: "BLOCKCHAIN COMMANDS",
|
Category: "BLOCKCHAIN COMMANDS",
|
||||||
Description: `
|
Description: `
|
||||||
|
|
@ -182,9 +212,11 @@ Use "ethereum dump 0" to dump the genesis block.`,
|
||||||
utils.DataDirFlag,
|
utils.DataDirFlag,
|
||||||
utils.AncientFlag,
|
utils.AncientFlag,
|
||||||
utils.CacheFlag,
|
utils.CacheFlag,
|
||||||
utils.TestnetFlag,
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
|
utils.YoloV1Flag,
|
||||||
|
utils.LegacyTestnetFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
},
|
},
|
||||||
Category: "BLOCKCHAIN COMMANDS",
|
Category: "BLOCKCHAIN COMMANDS",
|
||||||
|
|
@ -228,14 +260,29 @@ func initGenesis(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dumpGenesis(ctx *cli.Context) error {
|
||||||
|
genesis := utils.MakeGenesis(ctx)
|
||||||
|
if genesis == nil {
|
||||||
|
genesis = core.DefaultGenesisBlock()
|
||||||
|
}
|
||||||
|
if err := json.NewEncoder(os.Stdout).Encode(genesis); err != nil {
|
||||||
|
utils.Fatalf("could not encode genesis")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func importChain(ctx *cli.Context) error {
|
func importChain(ctx *cli.Context) error {
|
||||||
if len(ctx.Args()) < 2 {
|
if len(ctx.Args()) < 2 {
|
||||||
utils.Fatalf("This command requires atleast 2 arguments.")
|
utils.Fatalf("This command requires atleast 2 arguments.")
|
||||||
}
|
}
|
||||||
|
// Start metrics export if enabled
|
||||||
|
utils.SetupMetrics(ctx)
|
||||||
|
// Start system runtime metrics collection
|
||||||
|
go metrics.CollectProcessMetrics(3 * time.Second)
|
||||||
stack := makeFullNode(ctx)
|
stack := makeFullNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
chain, db := utils.MakeChain(ctx, stack)
|
chain, db := utils.MakeChain(ctx, stack, false)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// Start periodically gathering memory profiles
|
// Start periodically gathering memory profiles
|
||||||
|
|
@ -256,14 +303,17 @@ func importChain(ctx *cli.Context) error {
|
||||||
// Import the chain
|
// Import the chain
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
// ArgsUsage: "<filename> (<filename 2> ... <filename N>) <genesisPath>",
|
var importErr error
|
||||||
if len(ctx.Args()) == 2 {
|
|
||||||
|
if len(ctx.Args()) == 1 {
|
||||||
if err := utils.ImportChain(chain, ctx.Args().First()); err != nil {
|
if err := utils.ImportChain(chain, ctx.Args().First()); err != nil {
|
||||||
|
importErr = err
|
||||||
log.Error("Import error", "err", err)
|
log.Error("Import error", "err", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, arg := range ctx.Args()[:len(ctx.Args())-1] {
|
for _, arg := range ctx.Args()[:len(ctx.Args())-1] {
|
||||||
if err := utils.ImportChain(chain, arg); err != nil {
|
if err := utils.ImportChain(chain, arg); err != nil {
|
||||||
|
importErr = err
|
||||||
log.Error("Import error", "file", arg, "err", err)
|
log.Error("Import error", "file", arg, "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +366,7 @@ func importChain(ctx *cli.Context) error {
|
||||||
utils.Fatalf("Failed to read database iostats: %v", err)
|
utils.Fatalf("Failed to read database iostats: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Println(ioStats)
|
fmt.Println(ioStats)
|
||||||
return nil
|
return importErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportChain(ctx *cli.Context) error {
|
func exportChain(ctx *cli.Context) error {
|
||||||
|
|
@ -326,7 +376,7 @@ func exportChain(ctx *cli.Context) error {
|
||||||
stack := makeFullNode(ctx)
|
stack := makeFullNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
chain, _ := utils.MakeChain(ctx, stack)
|
chain, _ := utils.MakeChain(ctx, stack, true)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
@ -401,7 +451,7 @@ func copyDb(ctx *cli.Context) error {
|
||||||
stack := makeFullNode(ctx)
|
stack := makeFullNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
chain, chainDb := utils.MakeChain(ctx, stack)
|
chain, chainDb := utils.MakeChain(ctx, stack, false)
|
||||||
syncMode := *utils.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode)
|
syncMode := *utils.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode)
|
||||||
|
|
||||||
var syncBloom *trie.SyncBloom
|
var syncBloom *trie.SyncBloom
|
||||||
|
|
@ -481,7 +531,7 @@ func removeDB(ctx *cli.Context) error {
|
||||||
// confirmAndRemoveDB prompts the user for a last confirmation and removes the
|
// confirmAndRemoveDB prompts the user for a last confirmation and removes the
|
||||||
// folder if accepted.
|
// folder if accepted.
|
||||||
func confirmAndRemoveDB(database string, kind string) {
|
func confirmAndRemoveDB(database string, kind string) {
|
||||||
confirm, err := console.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
|
confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
utils.Fatalf("%v", err)
|
utils.Fatalf("%v", err)
|
||||||
|
|
@ -509,7 +559,7 @@ func dump(ctx *cli.Context) error {
|
||||||
stack := makeFullNode(ctx)
|
stack := makeFullNode(ctx)
|
||||||
defer stack.Close()
|
defer stack.Close()
|
||||||
|
|
||||||
chain, chainDb := utils.MakeChain(ctx, stack)
|
chain, chainDb := utils.MakeChain(ctx, stack, true)
|
||||||
defer chainDb.Close()
|
defer chainDb.Close()
|
||||||
for _, arg := range ctx.Args() {
|
for _, arg := range ctx.Args() {
|
||||||
var block *types.Block
|
var block *types.Block
|
||||||
|
|
@ -523,7 +573,7 @@ func dump(ctx *cli.Context) error {
|
||||||
fmt.Println("{}")
|
fmt.Println("{}")
|
||||||
utils.Fatalf("block not found")
|
utils.Fatalf("block not found")
|
||||||
} else {
|
} else {
|
||||||
state, err := state.New(block.Root(), state.NewDatabase(chainDb))
|
state, err := state.New(block.Root(), state.NewDatabase(chainDb), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("could not create new state: %v", err)
|
utils.Fatalf("could not create new state: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -548,7 +598,7 @@ func inspect(ctx *cli.Context) error {
|
||||||
node, _ := makeConfigNode(ctx)
|
node, _ := makeConfigNode(ctx)
|
||||||
defer node.Close()
|
defer node.Close()
|
||||||
|
|
||||||
_, chainDb := utils.MakeChain(ctx, node)
|
_, chainDb := utils.MakeChain(ctx, node, true)
|
||||||
defer chainDb.Close()
|
defer chainDb.Close()
|
||||||
|
|
||||||
return rawdb.InspectDatabase(chainDb)
|
return rawdb.InspectDatabase(chainDb)
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import (
|
||||||
cli "gopkg.in/urfave/cli.v1"
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
|
|
||||||
"github.com/maticnetwork/bor/cmd/utils"
|
"github.com/maticnetwork/bor/cmd/utils"
|
||||||
"github.com/maticnetwork/bor/dashboard"
|
|
||||||
"github.com/maticnetwork/bor/eth"
|
"github.com/maticnetwork/bor/eth"
|
||||||
"github.com/maticnetwork/bor/log"
|
"github.com/maticnetwork/bor/log"
|
||||||
"github.com/maticnetwork/bor/node"
|
"github.com/maticnetwork/bor/node"
|
||||||
|
|
@ -75,11 +74,10 @@ type ethstatsConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type gethConfig struct {
|
type gethConfig struct {
|
||||||
Eth eth.Config
|
Eth eth.Config
|
||||||
Shh whisper.Config
|
Shh whisper.Config
|
||||||
Node node.Config
|
Node node.Config
|
||||||
Ethstats ethstatsConfig
|
Ethstats ethstatsConfig
|
||||||
Dashboard dashboard.Config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfig(file string, cfg *gethConfig) error {
|
func loadConfig(file string, cfg *gethConfig) error {
|
||||||
|
|
@ -101,8 +99,8 @@ func defaultNodeConfig() node.Config {
|
||||||
cfg := node.DefaultConfig
|
cfg := node.DefaultConfig
|
||||||
cfg.Name = clientIdentifier
|
cfg.Name = clientIdentifier
|
||||||
cfg.Version = params.VersionWithCommit(gitCommit, gitDate)
|
cfg.Version = params.VersionWithCommit(gitCommit, gitDate)
|
||||||
cfg.HTTPModules = append(cfg.HTTPModules, "eth", "shh")
|
cfg.HTTPModules = append(cfg.HTTPModules, "eth")
|
||||||
cfg.WSModules = append(cfg.WSModules, "eth", "shh")
|
cfg.WSModules = append(cfg.WSModules, "eth")
|
||||||
cfg.IPCPath = "bor.ipc"
|
cfg.IPCPath = "bor.ipc"
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
@ -110,10 +108,9 @@ func defaultNodeConfig() node.Config {
|
||||||
func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
||||||
// Load defaults.
|
// Load defaults.
|
||||||
cfg := gethConfig{
|
cfg := gethConfig{
|
||||||
Eth: eth.DefaultConfig,
|
Eth: eth.DefaultConfig,
|
||||||
Shh: whisper.DefaultConfig,
|
Shh: whisper.DefaultConfig,
|
||||||
Node: defaultNodeConfig(),
|
Node: defaultNodeConfig(),
|
||||||
Dashboard: dashboard.DefaultConfig,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load config file.
|
// Load config file.
|
||||||
|
|
@ -134,7 +131,6 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
||||||
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
|
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
|
||||||
}
|
}
|
||||||
utils.SetShhConfig(ctx, stack, &cfg.Shh)
|
utils.SetShhConfig(ctx, stack, &cfg.Shh)
|
||||||
utils.SetDashboardConfig(ctx, &cfg.Dashboard)
|
|
||||||
|
|
||||||
return stack, cfg
|
return stack, cfg
|
||||||
}
|
}
|
||||||
|
|
@ -152,12 +148,14 @@ func enableWhisper(ctx *cli.Context) bool {
|
||||||
func makeFullNode(ctx *cli.Context) *node.Node {
|
func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
stack, cfg := makeConfigNode(ctx)
|
stack, cfg := makeConfigNode(ctx)
|
||||||
cfg.Eth.HeimdallURL = ctx.GlobalString(utils.HeimdallURLFlag.Name)
|
cfg.Eth.HeimdallURL = ctx.GlobalString(utils.HeimdallURLFlag.Name)
|
||||||
log.Info("Connecting to heimdall service on...", "heimdallURL", cfg.Eth.HeimdallURL)
|
cfg.Eth.WithoutHeimdall = ctx.GlobalBool(utils.WithoutHeimdallFlag.Name)
|
||||||
|
if cfg.Eth.WithoutHeimdall {
|
||||||
|
log.Info("Running without Heimdall node")
|
||||||
|
} else {
|
||||||
|
log.Info("Connecting to Heimdall node", "heimdallURL", cfg.Eth.HeimdallURL)
|
||||||
|
}
|
||||||
utils.RegisterEthService(stack, &cfg.Eth)
|
utils.RegisterEthService(stack, &cfg.Eth)
|
||||||
|
|
||||||
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
|
|
||||||
utils.RegisterDashboardService(stack, &cfg.Dashboard, gitCommit)
|
|
||||||
}
|
|
||||||
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
|
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
|
||||||
shhEnabled := enableWhisper(ctx)
|
shhEnabled := enableWhisper(ctx)
|
||||||
shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DeveloperFlag.Name)
|
shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DeveloperFlag.Name)
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ JavaScript API. See https://github.com/maticnetwork/bor/wiki/JavaScript-Console`
|
||||||
// same time.
|
// same time.
|
||||||
func localConsole(ctx *cli.Context) error {
|
func localConsole(ctx *cli.Context) error {
|
||||||
// Create and start the node based on the CLI flags
|
// Create and start the node based on the CLI flags
|
||||||
|
prepare(ctx)
|
||||||
node := makeFullNode(ctx)
|
node := makeFullNode(ctx)
|
||||||
startNode(ctx, node)
|
startNode(ctx, node)
|
||||||
defer node.Close()
|
defer node.Close()
|
||||||
|
|
@ -122,10 +123,21 @@ func remoteConsole(ctx *cli.Context) error {
|
||||||
path = ctx.GlobalString(utils.DataDirFlag.Name)
|
path = ctx.GlobalString(utils.DataDirFlag.Name)
|
||||||
}
|
}
|
||||||
if path != "" {
|
if path != "" {
|
||||||
if ctx.GlobalBool(utils.TestnetFlag.Name) {
|
if ctx.GlobalBool(utils.LegacyTestnetFlag.Name) || ctx.GlobalBool(utils.RopstenFlag.Name) {
|
||||||
path = filepath.Join(path, "testnet")
|
// Maintain compatibility with older Geth configurations storing the
|
||||||
|
// Ropsten database in `testnet` instead of `ropsten`.
|
||||||
|
legacyPath := filepath.Join(path, "testnet")
|
||||||
|
if _, err := os.Stat(legacyPath); !os.IsNotExist(err) {
|
||||||
|
path = legacyPath
|
||||||
|
} else {
|
||||||
|
path = filepath.Join(path, "ropsten")
|
||||||
|
}
|
||||||
} else if ctx.GlobalBool(utils.RinkebyFlag.Name) {
|
} else if ctx.GlobalBool(utils.RinkebyFlag.Name) {
|
||||||
path = filepath.Join(path, "rinkeby")
|
path = filepath.Join(path, "rinkeby")
|
||||||
|
} else if ctx.GlobalBool(utils.GoerliFlag.Name) {
|
||||||
|
path = filepath.Join(path, "goerli")
|
||||||
|
} else if ctx.GlobalBool(utils.YoloV1Flag.Name) {
|
||||||
|
path = filepath.Join(path, "yolo-v1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
endpoint = fmt.Sprintf("%s/bor.ipc", path)
|
endpoint = fmt.Sprintf("%s/bor.ipc", path)
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,9 @@ func TestConsoleWelcome(t *testing.T) {
|
||||||
geth.SetTemplateFunc("goarch", func() string { return runtime.GOARCH })
|
geth.SetTemplateFunc("goarch", func() string { return runtime.GOARCH })
|
||||||
geth.SetTemplateFunc("gover", runtime.Version)
|
geth.SetTemplateFunc("gover", runtime.Version)
|
||||||
geth.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
geth.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
||||||
geth.SetTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
|
geth.SetTemplateFunc("niltime", func() string {
|
||||||
|
return time.Unix(0, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
||||||
|
})
|
||||||
geth.SetTemplateFunc("apis", func() string { return ipcAPIs })
|
geth.SetTemplateFunc("apis", func() string { return ipcAPIs })
|
||||||
|
|
||||||
// Verify the actual welcome message to the required template
|
// Verify the actual welcome message to the required template
|
||||||
|
|
@ -71,7 +73,7 @@ at block: 0 ({{niltime}})
|
||||||
|
|
||||||
// Tests that a console can be attached to a running node via various means.
|
// Tests that a console can be attached to a running node via various means.
|
||||||
func TestIPCAttachWelcome(t *testing.T) {
|
func TestIPCAttachWelcome(t *testing.T) {
|
||||||
// Configure the instance for IPC attachement
|
// Configure the instance for IPC attachment
|
||||||
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
|
||||||
var ipc string
|
var ipc string
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
|
|
@ -87,11 +89,14 @@ func TestIPCAttachWelcome(t *testing.T) {
|
||||||
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
|
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
|
||||||
"--etherbase", coinbase, "--shh", "--ipcpath", ipc)
|
"--etherbase", coinbase, "--shh", "--ipcpath", ipc)
|
||||||
|
|
||||||
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
|
defer func() {
|
||||||
|
geth.Interrupt()
|
||||||
|
geth.ExpectExit()
|
||||||
|
}()
|
||||||
|
|
||||||
|
waitForEndpoint(t, ipc, 3*time.Second)
|
||||||
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
|
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
|
||||||
|
|
||||||
geth.Interrupt()
|
|
||||||
geth.ExpectExit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHTTPAttachWelcome(t *testing.T) {
|
func TestHTTPAttachWelcome(t *testing.T) {
|
||||||
|
|
@ -99,13 +104,15 @@ func TestHTTPAttachWelcome(t *testing.T) {
|
||||||
port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P
|
port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P
|
||||||
geth := runGeth(t,
|
geth := runGeth(t,
|
||||||
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
|
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
|
||||||
"--etherbase", coinbase, "--rpc", "--rpcport", port)
|
"--etherbase", coinbase, "--http", "--http.port", port)
|
||||||
|
defer func() {
|
||||||
|
geth.Interrupt()
|
||||||
|
geth.ExpectExit()
|
||||||
|
}()
|
||||||
|
|
||||||
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
|
endpoint := "http://127.0.0.1:" + port
|
||||||
testAttachWelcome(t, geth, "http://localhost:"+port, httpAPIs)
|
waitForEndpoint(t, endpoint, 3*time.Second)
|
||||||
|
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||||
geth.Interrupt()
|
|
||||||
geth.ExpectExit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWSAttachWelcome(t *testing.T) {
|
func TestWSAttachWelcome(t *testing.T) {
|
||||||
|
|
@ -114,13 +121,15 @@ func TestWSAttachWelcome(t *testing.T) {
|
||||||
|
|
||||||
geth := runGeth(t,
|
geth := runGeth(t,
|
||||||
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
|
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
|
||||||
"--etherbase", coinbase, "--ws", "--wsport", port)
|
"--etherbase", coinbase, "--ws", "--ws.port", port)
|
||||||
|
defer func() {
|
||||||
|
geth.Interrupt()
|
||||||
|
geth.ExpectExit()
|
||||||
|
}()
|
||||||
|
|
||||||
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
|
endpoint := "ws://127.0.0.1:" + port
|
||||||
testAttachWelcome(t, geth, "ws://localhost:"+port, httpAPIs)
|
waitForEndpoint(t, endpoint, 3*time.Second)
|
||||||
|
testAttachWelcome(t, geth, endpoint, httpAPIs)
|
||||||
geth.Interrupt()
|
|
||||||
geth.ExpectExit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
|
func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
|
||||||
|
|
@ -135,7 +144,9 @@ func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
|
||||||
attach.SetTemplateFunc("gover", runtime.Version)
|
attach.SetTemplateFunc("gover", runtime.Version)
|
||||||
attach.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
attach.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
||||||
attach.SetTemplateFunc("etherbase", func() string { return geth.Etherbase })
|
attach.SetTemplateFunc("etherbase", func() string { return geth.Etherbase })
|
||||||
attach.SetTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
|
attach.SetTemplateFunc("niltime", func() string {
|
||||||
|
return time.Unix(0, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
||||||
|
})
|
||||||
attach.SetTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
|
attach.SetTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
|
||||||
attach.SetTemplateFunc("datadir", func() string { return geth.Datadir })
|
attach.SetTemplateFunc("datadir", func() string { return geth.Datadir })
|
||||||
attach.SetTemplateFunc("apis", func() string { return apis })
|
attach.SetTemplateFunc("apis", func() string { return apis })
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,9 @@ var daoOldGenesis = `{
|
||||||
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"timestamp" : "0x00",
|
"timestamp" : "0x00",
|
||||||
"config" : {}
|
"config" : {
|
||||||
|
"homesteadBlock" : 0
|
||||||
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
// Genesis block for nodes which actively oppose the DAO fork
|
// Genesis block for nodes which actively oppose the DAO fork
|
||||||
|
|
@ -54,6 +56,7 @@ var daoNoForkGenesis = `{
|
||||||
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"timestamp" : "0x00",
|
"timestamp" : "0x00",
|
||||||
"config" : {
|
"config" : {
|
||||||
|
"homesteadBlock" : 0,
|
||||||
"daoForkBlock" : 314,
|
"daoForkBlock" : 314,
|
||||||
"daoForkSupport" : false
|
"daoForkSupport" : false
|
||||||
}
|
}
|
||||||
|
|
@ -71,6 +74,7 @@ var daoProForkGenesis = `{
|
||||||
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"timestamp" : "0x00",
|
"timestamp" : "0x00",
|
||||||
"config" : {
|
"config" : {
|
||||||
|
"homesteadBlock" : 0,
|
||||||
"daoForkBlock" : 314,
|
"daoForkBlock" : 314,
|
||||||
"daoForkSupport" : true
|
"daoForkSupport" : true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,22 +28,6 @@ var customGenesisTests = []struct {
|
||||||
query string
|
query string
|
||||||
result string
|
result string
|
||||||
}{
|
}{
|
||||||
// Plain genesis file without anything extra
|
|
||||||
{
|
|
||||||
genesis: `{
|
|
||||||
"alloc" : {},
|
|
||||||
"coinbase" : "0x0000000000000000000000000000000000000000",
|
|
||||||
"difficulty" : "0x20000",
|
|
||||||
"extraData" : "",
|
|
||||||
"gasLimit" : "0x2fefd8",
|
|
||||||
"nonce" : "0x0000000000000042",
|
|
||||||
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
"timestamp" : "0x00"
|
|
||||||
}`,
|
|
||||||
query: "eth.getBlock(0).nonce",
|
|
||||||
result: "0x0000000000000042",
|
|
||||||
},
|
|
||||||
// Genesis file with an empty chain configuration (ensure missing fields work)
|
// Genesis file with an empty chain configuration (ensure missing fields work)
|
||||||
{
|
{
|
||||||
genesis: `{
|
genesis: `{
|
||||||
|
|
@ -52,14 +36,14 @@ var customGenesisTests = []struct {
|
||||||
"difficulty" : "0x20000",
|
"difficulty" : "0x20000",
|
||||||
"extraData" : "",
|
"extraData" : "",
|
||||||
"gasLimit" : "0x2fefd8",
|
"gasLimit" : "0x2fefd8",
|
||||||
"nonce" : "0x0000000000000042",
|
"nonce" : "0x0000000000001338",
|
||||||
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"timestamp" : "0x00",
|
"timestamp" : "0x00",
|
||||||
"config" : {}
|
"config" : {}
|
||||||
}`,
|
}`,
|
||||||
query: "eth.getBlock(0).nonce",
|
query: "eth.getBlock(0).nonce",
|
||||||
result: "0x0000000000000042",
|
result: "0x0000000000001338",
|
||||||
},
|
},
|
||||||
// Genesis file with specific chain configurations
|
// Genesis file with specific chain configurations
|
||||||
{
|
{
|
||||||
|
|
@ -69,18 +53,18 @@ var customGenesisTests = []struct {
|
||||||
"difficulty" : "0x20000",
|
"difficulty" : "0x20000",
|
||||||
"extraData" : "",
|
"extraData" : "",
|
||||||
"gasLimit" : "0x2fefd8",
|
"gasLimit" : "0x2fefd8",
|
||||||
"nonce" : "0x0000000000000042",
|
"nonce" : "0x0000000000001339",
|
||||||
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
"timestamp" : "0x00",
|
"timestamp" : "0x00",
|
||||||
"config" : {
|
"config" : {
|
||||||
"homesteadBlock" : 314,
|
"homesteadBlock" : 42,
|
||||||
"daoForkBlock" : 141,
|
"daoForkBlock" : 141,
|
||||||
"daoForkSupport" : true
|
"daoForkSupport" : true
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
query: "eth.getBlock(0).nonce",
|
query: "eth.getBlock(0).nonce",
|
||||||
result: "0x0000000000000042",
|
result: "0x0000000000001339",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,10 +81,10 @@ func TestCustomGenesis(t *testing.T) {
|
||||||
if err := ioutil.WriteFile(json, []byte(tt.genesis), 0600); err != nil {
|
if err := ioutil.WriteFile(json, []byte(tt.genesis), 0600); err != nil {
|
||||||
t.Fatalf("test %d: failed to write genesis file: %v", i, err)
|
t.Fatalf("test %d: failed to write genesis file: %v", i, err)
|
||||||
}
|
}
|
||||||
runGeth(t, "--datadir", datadir, "init", json).WaitExit()
|
runGeth(t, "--nousb", "--datadir", datadir, "init", json).WaitExit()
|
||||||
|
|
||||||
// Query the custom genesis block
|
// Query the custom genesis block
|
||||||
geth := runGeth(t,
|
geth := runGeth(t, "--nousb",
|
||||||
"--datadir", datadir, "--maxpeers", "0", "--port", "0",
|
"--datadir", datadir, "--maxpeers", "0", "--port", "0",
|
||||||
"--nodiscover", "--nat", "none", "--ipcdisable",
|
"--nodiscover", "--nat", "none", "--ipcdisable",
|
||||||
"--exec", tt.query, "console")
|
"--exec", tt.query, "console")
|
||||||
|
|
|
||||||
180
cmd/bor/les_test.go
Normal file
180
cmd/bor/les_test.go
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/maticnetwork/bor/p2p"
|
||||||
|
"github.com/maticnetwork/bor/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type gethrpc struct {
|
||||||
|
name string
|
||||||
|
rpc *rpc.Client
|
||||||
|
geth *testgeth
|
||||||
|
nodeInfo *p2p.NodeInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gethrpc) killAndWait() {
|
||||||
|
g.geth.Kill()
|
||||||
|
g.geth.WaitExit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gethrpc) callRPC(result interface{}, method string, args ...interface{}) {
|
||||||
|
if err := g.rpc.Call(&result, method, args...); err != nil {
|
||||||
|
g.geth.Fatalf("callRPC %v: %v", method, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gethrpc) addPeer(peer *gethrpc) {
|
||||||
|
g.geth.Logf("%v.addPeer(%v)", g.name, peer.name)
|
||||||
|
enode := peer.getNodeInfo().Enode
|
||||||
|
peerCh := make(chan *p2p.PeerEvent)
|
||||||
|
sub, err := g.rpc.Subscribe(context.Background(), "admin", peerCh, "peerEvents")
|
||||||
|
if err != nil {
|
||||||
|
g.geth.Fatalf("subscribe %v: %v", g.name, err)
|
||||||
|
}
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
g.callRPC(nil, "admin_addPeer", enode)
|
||||||
|
dur := 14 * time.Second
|
||||||
|
timeout := time.After(dur)
|
||||||
|
select {
|
||||||
|
case ev := <-peerCh:
|
||||||
|
g.geth.Logf("%v received event: type=%v, peer=%v", g.name, ev.Type, ev.Peer)
|
||||||
|
case err := <-sub.Err():
|
||||||
|
g.geth.Fatalf("%v sub error: %v", g.name, err)
|
||||||
|
case <-timeout:
|
||||||
|
g.geth.Error("timeout adding peer after", dur)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use this function instead of `g.nodeInfo` directly
|
||||||
|
func (g *gethrpc) getNodeInfo() *p2p.NodeInfo {
|
||||||
|
if g.nodeInfo != nil {
|
||||||
|
return g.nodeInfo
|
||||||
|
}
|
||||||
|
g.nodeInfo = &p2p.NodeInfo{}
|
||||||
|
g.callRPC(&g.nodeInfo, "admin_nodeInfo")
|
||||||
|
return g.nodeInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gethrpc) waitSynced() {
|
||||||
|
// Check if it's synced now
|
||||||
|
var result interface{}
|
||||||
|
g.callRPC(&result, "eth_syncing")
|
||||||
|
syncing, ok := result.(bool)
|
||||||
|
if ok && !syncing {
|
||||||
|
g.geth.Logf("%v already synced", g.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually wait, subscribe to the event
|
||||||
|
ch := make(chan interface{})
|
||||||
|
sub, err := g.rpc.Subscribe(context.Background(), "eth", ch, "syncing")
|
||||||
|
if err != nil {
|
||||||
|
g.geth.Fatalf("%v syncing: %v", g.name, err)
|
||||||
|
}
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
timeout := time.After(4 * time.Second)
|
||||||
|
select {
|
||||||
|
case ev := <-ch:
|
||||||
|
g.geth.Log("'syncing' event", ev)
|
||||||
|
syncing, ok := ev.(bool)
|
||||||
|
if ok && !syncing {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
g.geth.Log("Other 'syncing' event", ev)
|
||||||
|
case err := <-sub.Err():
|
||||||
|
g.geth.Fatalf("%v notification: %v", g.name, err)
|
||||||
|
break
|
||||||
|
case <-timeout:
|
||||||
|
g.geth.Fatalf("%v timeout syncing", g.name)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startGethWithRpc(t *testing.T, name string, args ...string) *gethrpc {
|
||||||
|
g := &gethrpc{name: name}
|
||||||
|
args = append([]string{"--networkid=42", "--port=0", "--nousb", "--http", "--http.port=0", "--http.api=admin,eth,les"}, args...)
|
||||||
|
t.Logf("Starting %v with rpc: %v", name, args)
|
||||||
|
g.geth = runGeth(t, args...)
|
||||||
|
// wait before we can attach to it. TODO: probe for it properly
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
var err error
|
||||||
|
ipcpath := filepath.Join(g.geth.Datadir, "geth.ipc")
|
||||||
|
g.rpc, err = rpc.Dial(ipcpath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v rpc connect: %v", name, err)
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func initGeth(t *testing.T) string {
|
||||||
|
g := runGeth(t, "--networkid=42", "init", "./testdata/clique.json")
|
||||||
|
datadir := g.Datadir
|
||||||
|
g.WaitExit()
|
||||||
|
return datadir
|
||||||
|
}
|
||||||
|
|
||||||
|
func startLightServer(t *testing.T) *gethrpc {
|
||||||
|
datadir := initGeth(t)
|
||||||
|
runGeth(t, "--datadir", datadir, "--password", "./testdata/password.txt", "account", "import", "./testdata/key.prv").WaitExit()
|
||||||
|
account := "0x02f0d131f1f97aef08aec6e3291b957d9efe7105"
|
||||||
|
server := startGethWithRpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", "--unlock", account, "--mine", "--light.serve=100", "--light.maxpeers=1", "--nodiscover", "--nat=extip:127.0.0.1")
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
func startClient(t *testing.T, name string) *gethrpc {
|
||||||
|
datadir := initGeth(t)
|
||||||
|
return startGethWithRpc(t, name, "--datadir", datadir, "--nodiscover", "--syncmode=light", "--nat=extip:127.0.0.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPriorityClient(t *testing.T) {
|
||||||
|
lightServer := startLightServer(t)
|
||||||
|
defer lightServer.killAndWait()
|
||||||
|
|
||||||
|
// Start client and add lightServer as peer
|
||||||
|
freeCli := startClient(t, "freeCli")
|
||||||
|
defer freeCli.killAndWait()
|
||||||
|
freeCli.addPeer(lightServer)
|
||||||
|
|
||||||
|
var peers []*p2p.PeerInfo
|
||||||
|
freeCli.callRPC(&peers, "admin_peers")
|
||||||
|
if len(peers) != 1 {
|
||||||
|
t.Errorf("Expected: # of client peers == 1, actual: %v", len(peers))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up priority client, get its nodeID, increase its balance on the lightServer
|
||||||
|
prioCli := startClient(t, "prioCli")
|
||||||
|
defer prioCli.killAndWait()
|
||||||
|
// 3_000_000_000 once we move to Go 1.13
|
||||||
|
tokens := 3000000000
|
||||||
|
lightServer.callRPC(nil, "les_addBalance", prioCli.getNodeInfo().ID, tokens, "foobar")
|
||||||
|
prioCli.addPeer(lightServer)
|
||||||
|
|
||||||
|
// Check if priority client is actually syncing and the regular client got kicked out
|
||||||
|
prioCli.callRPC(&peers, "admin_peers")
|
||||||
|
if len(peers) != 1 {
|
||||||
|
t.Errorf("Expected: # of prio peers == 1, actual: %v", len(peers))
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes := map[string]*gethrpc{
|
||||||
|
lightServer.getNodeInfo().ID: lightServer,
|
||||||
|
freeCli.getNodeInfo().ID: freeCli,
|
||||||
|
prioCli.getNodeInfo().ID: prioCli,
|
||||||
|
}
|
||||||
|
lightServer.callRPC(&peers, "admin_peers")
|
||||||
|
peersWithNames := make(map[string]string)
|
||||||
|
for _, p := range peers {
|
||||||
|
peersWithNames[nodes[p.ID].name] = p.ID
|
||||||
|
}
|
||||||
|
if _, freeClientFound := peersWithNames[freeCli.name]; freeClientFound {
|
||||||
|
t.Error("client is still a peer of lightServer", peersWithNames)
|
||||||
|
}
|
||||||
|
if _, prioClientFound := peersWithNames[prioCli.name]; !prioClientFound {
|
||||||
|
t.Error("prio client is not among lightServer peers", peersWithNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
206
cmd/bor/main.go
206
cmd/bor/main.go
|
|
@ -21,19 +21,17 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
|
||||||
godebug "runtime/debug"
|
godebug "runtime/debug"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/elastic/gosigar"
|
|
||||||
"github.com/maticnetwork/bor/accounts"
|
"github.com/maticnetwork/bor/accounts"
|
||||||
"github.com/maticnetwork/bor/accounts/keystore"
|
"github.com/maticnetwork/bor/accounts/keystore"
|
||||||
"github.com/maticnetwork/bor/cmd/utils"
|
"github.com/maticnetwork/bor/cmd/utils"
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
"github.com/maticnetwork/bor/console"
|
"github.com/maticnetwork/bor/console/prompt"
|
||||||
"github.com/maticnetwork/bor/eth"
|
"github.com/maticnetwork/bor/eth"
|
||||||
"github.com/maticnetwork/bor/eth/downloader"
|
"github.com/maticnetwork/bor/eth/downloader"
|
||||||
"github.com/maticnetwork/bor/ethclient"
|
"github.com/maticnetwork/bor/ethclient"
|
||||||
|
|
@ -42,6 +40,7 @@ import (
|
||||||
"github.com/maticnetwork/bor/log"
|
"github.com/maticnetwork/bor/log"
|
||||||
"github.com/maticnetwork/bor/metrics"
|
"github.com/maticnetwork/bor/metrics"
|
||||||
"github.com/maticnetwork/bor/node"
|
"github.com/maticnetwork/bor/node"
|
||||||
|
gopsutil "github.com/shirou/gopsutil/mem"
|
||||||
cli "gopkg.in/urfave/cli.v1"
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -61,24 +60,22 @@ var (
|
||||||
utils.UnlockedAccountFlag,
|
utils.UnlockedAccountFlag,
|
||||||
utils.PasswordFileFlag,
|
utils.PasswordFileFlag,
|
||||||
utils.BootnodesFlag,
|
utils.BootnodesFlag,
|
||||||
utils.BootnodesV4Flag,
|
utils.LegacyBootnodesV4Flag,
|
||||||
utils.BootnodesV5Flag,
|
utils.LegacyBootnodesV5Flag,
|
||||||
utils.DataDirFlag,
|
utils.DataDirFlag,
|
||||||
utils.AncientFlag,
|
utils.AncientFlag,
|
||||||
utils.KeyStoreDirFlag,
|
utils.KeyStoreDirFlag,
|
||||||
utils.ExternalSignerFlag,
|
utils.ExternalSignerFlag,
|
||||||
utils.NoUSBFlag,
|
utils.NoUSBFlag,
|
||||||
utils.SmartCardDaemonPathFlag,
|
utils.SmartCardDaemonPathFlag,
|
||||||
utils.DashboardEnabledFlag,
|
|
||||||
utils.DashboardAddrFlag,
|
|
||||||
utils.DashboardPortFlag,
|
|
||||||
utils.DashboardRefreshFlag,
|
|
||||||
utils.EthashCacheDirFlag,
|
utils.EthashCacheDirFlag,
|
||||||
utils.EthashCachesInMemoryFlag,
|
utils.EthashCachesInMemoryFlag,
|
||||||
utils.EthashCachesOnDiskFlag,
|
utils.EthashCachesOnDiskFlag,
|
||||||
|
utils.EthashCachesLockMmapFlag,
|
||||||
utils.EthashDatasetDirFlag,
|
utils.EthashDatasetDirFlag,
|
||||||
utils.EthashDatasetsInMemoryFlag,
|
utils.EthashDatasetsInMemoryFlag,
|
||||||
utils.EthashDatasetsOnDiskFlag,
|
utils.EthashDatasetsOnDiskFlag,
|
||||||
|
utils.EthashDatasetsLockMmapFlag,
|
||||||
utils.TxPoolLocalsFlag,
|
utils.TxPoolLocalsFlag,
|
||||||
utils.TxPoolNoLocalsFlag,
|
utils.TxPoolNoLocalsFlag,
|
||||||
utils.TxPoolJournalFlag,
|
utils.TxPoolJournalFlag,
|
||||||
|
|
@ -93,12 +90,14 @@ var (
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
utils.ExitWhenSyncedFlag,
|
utils.ExitWhenSyncedFlag,
|
||||||
utils.GCModeFlag,
|
utils.GCModeFlag,
|
||||||
|
utils.SnapshotFlag,
|
||||||
|
utils.TxLookupLimitFlag,
|
||||||
utils.LightServeFlag,
|
utils.LightServeFlag,
|
||||||
utils.LightLegacyServFlag,
|
utils.LegacyLightServFlag,
|
||||||
utils.LightIngressFlag,
|
utils.LightIngressFlag,
|
||||||
utils.LightEgressFlag,
|
utils.LightEgressFlag,
|
||||||
utils.LightMaxPeersFlag,
|
utils.LightMaxPeersFlag,
|
||||||
utils.LightLegacyPeersFlag,
|
utils.LegacyLightPeersFlag,
|
||||||
utils.LightKDFFlag,
|
utils.LightKDFFlag,
|
||||||
utils.UltraLightServersFlag,
|
utils.UltraLightServersFlag,
|
||||||
utils.UltraLightFractionFlag,
|
utils.UltraLightFractionFlag,
|
||||||
|
|
@ -108,23 +107,24 @@ var (
|
||||||
utils.CacheDatabaseFlag,
|
utils.CacheDatabaseFlag,
|
||||||
utils.CacheTrieFlag,
|
utils.CacheTrieFlag,
|
||||||
utils.CacheGCFlag,
|
utils.CacheGCFlag,
|
||||||
|
utils.CacheSnapshotFlag,
|
||||||
utils.CacheNoPrefetchFlag,
|
utils.CacheNoPrefetchFlag,
|
||||||
utils.ListenPortFlag,
|
utils.ListenPortFlag,
|
||||||
utils.MaxPeersFlag,
|
utils.MaxPeersFlag,
|
||||||
utils.MaxPendingPeersFlag,
|
utils.MaxPendingPeersFlag,
|
||||||
utils.MiningEnabledFlag,
|
utils.MiningEnabledFlag,
|
||||||
utils.MinerThreadsFlag,
|
utils.MinerThreadsFlag,
|
||||||
utils.MinerLegacyThreadsFlag,
|
utils.LegacyMinerThreadsFlag,
|
||||||
utils.MinerNotifyFlag,
|
utils.MinerNotifyFlag,
|
||||||
utils.MinerGasTargetFlag,
|
utils.MinerGasTargetFlag,
|
||||||
utils.MinerLegacyGasTargetFlag,
|
utils.LegacyMinerGasTargetFlag,
|
||||||
utils.MinerGasLimitFlag,
|
utils.MinerGasLimitFlag,
|
||||||
utils.MinerGasPriceFlag,
|
utils.MinerGasPriceFlag,
|
||||||
utils.MinerLegacyGasPriceFlag,
|
utils.LegacyMinerGasPriceFlag,
|
||||||
utils.MinerEtherbaseFlag,
|
utils.MinerEtherbaseFlag,
|
||||||
utils.MinerLegacyEtherbaseFlag,
|
utils.LegacyMinerEtherbaseFlag,
|
||||||
utils.MinerExtraDataFlag,
|
utils.MinerExtraDataFlag,
|
||||||
utils.MinerLegacyExtraDataFlag,
|
utils.LegacyMinerExtraDataFlag,
|
||||||
utils.MinerRecommitIntervalFlag,
|
utils.MinerRecommitIntervalFlag,
|
||||||
utils.MinerNoVerfiyFlag,
|
utils.MinerNoVerfiyFlag,
|
||||||
utils.NATFlag,
|
utils.NATFlag,
|
||||||
|
|
@ -133,44 +133,60 @@ var (
|
||||||
utils.NetrestrictFlag,
|
utils.NetrestrictFlag,
|
||||||
utils.NodeKeyFileFlag,
|
utils.NodeKeyFileFlag,
|
||||||
utils.NodeKeyHexFlag,
|
utils.NodeKeyHexFlag,
|
||||||
|
utils.DNSDiscoveryFlag,
|
||||||
utils.DeveloperFlag,
|
utils.DeveloperFlag,
|
||||||
utils.DeveloperPeriodFlag,
|
utils.DeveloperPeriodFlag,
|
||||||
utils.TestnetFlag,
|
utils.LegacyTestnetFlag,
|
||||||
|
utils.RopstenFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
|
utils.YoloV1Flag,
|
||||||
utils.VMEnableDebugFlag,
|
utils.VMEnableDebugFlag,
|
||||||
utils.NetworkIdFlag,
|
utils.NetworkIdFlag,
|
||||||
utils.EthStatsURLFlag,
|
utils.EthStatsURLFlag,
|
||||||
utils.FakePoWFlag,
|
utils.FakePoWFlag,
|
||||||
utils.NoCompactionFlag,
|
utils.NoCompactionFlag,
|
||||||
utils.GpoBlocksFlag,
|
utils.GpoBlocksFlag,
|
||||||
|
utils.LegacyGpoBlocksFlag,
|
||||||
utils.GpoPercentileFlag,
|
utils.GpoPercentileFlag,
|
||||||
|
utils.LegacyGpoPercentileFlag,
|
||||||
utils.EWASMInterpreterFlag,
|
utils.EWASMInterpreterFlag,
|
||||||
utils.EVMInterpreterFlag,
|
utils.EVMInterpreterFlag,
|
||||||
configFileFlag,
|
configFileFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcFlags = []cli.Flag{
|
rpcFlags = []cli.Flag{
|
||||||
utils.RPCEnabledFlag,
|
utils.HTTPEnabledFlag,
|
||||||
utils.RPCListenAddrFlag,
|
utils.HTTPListenAddrFlag,
|
||||||
utils.RPCPortFlag,
|
utils.HTTPPortFlag,
|
||||||
utils.RPCCORSDomainFlag,
|
utils.HTTPCORSDomainFlag,
|
||||||
utils.RPCVirtualHostsFlag,
|
utils.HTTPVirtualHostsFlag,
|
||||||
|
utils.LegacyRPCEnabledFlag,
|
||||||
|
utils.LegacyRPCListenAddrFlag,
|
||||||
|
utils.LegacyRPCPortFlag,
|
||||||
|
utils.LegacyRPCCORSDomainFlag,
|
||||||
|
utils.LegacyRPCVirtualHostsFlag,
|
||||||
utils.GraphQLEnabledFlag,
|
utils.GraphQLEnabledFlag,
|
||||||
utils.GraphQLListenAddrFlag,
|
utils.GraphQLListenAddrFlag,
|
||||||
utils.GraphQLPortFlag,
|
utils.GraphQLPortFlag,
|
||||||
utils.GraphQLCORSDomainFlag,
|
utils.GraphQLCORSDomainFlag,
|
||||||
utils.GraphQLVirtualHostsFlag,
|
utils.GraphQLVirtualHostsFlag,
|
||||||
utils.RPCApiFlag,
|
utils.HTTPApiFlag,
|
||||||
|
utils.LegacyRPCApiFlag,
|
||||||
utils.WSEnabledFlag,
|
utils.WSEnabledFlag,
|
||||||
utils.WSListenAddrFlag,
|
utils.WSListenAddrFlag,
|
||||||
|
utils.LegacyWSListenAddrFlag,
|
||||||
utils.WSPortFlag,
|
utils.WSPortFlag,
|
||||||
|
utils.LegacyWSPortFlag,
|
||||||
utils.WSApiFlag,
|
utils.WSApiFlag,
|
||||||
|
utils.LegacyWSApiFlag,
|
||||||
utils.WSAllowedOriginsFlag,
|
utils.WSAllowedOriginsFlag,
|
||||||
|
utils.LegacyWSAllowedOriginsFlag,
|
||||||
utils.IPCDisabledFlag,
|
utils.IPCDisabledFlag,
|
||||||
utils.IPCPathFlag,
|
utils.IPCPathFlag,
|
||||||
utils.InsecureUnlockAllowedFlag,
|
utils.InsecureUnlockAllowedFlag,
|
||||||
utils.RPCGlobalGasCap,
|
utils.RPCGlobalGasCap,
|
||||||
|
utils.RPCGlobalTxFeeCap,
|
||||||
}
|
}
|
||||||
|
|
||||||
whisperFlags = []cli.Flag{
|
whisperFlags = []cli.Flag{
|
||||||
|
|
@ -183,6 +199,8 @@ var (
|
||||||
metricsFlags = []cli.Flag{
|
metricsFlags = []cli.Flag{
|
||||||
utils.MetricsEnabledFlag,
|
utils.MetricsEnabledFlag,
|
||||||
utils.MetricsEnabledExpensiveFlag,
|
utils.MetricsEnabledExpensiveFlag,
|
||||||
|
utils.MetricsHTTPFlag,
|
||||||
|
utils.MetricsPortFlag,
|
||||||
utils.MetricsEnableInfluxDBFlag,
|
utils.MetricsEnableInfluxDBFlag,
|
||||||
utils.MetricsInfluxDBEndpointFlag,
|
utils.MetricsInfluxDBEndpointFlag,
|
||||||
utils.MetricsInfluxDBDatabaseFlag,
|
utils.MetricsInfluxDBDatabaseFlag,
|
||||||
|
|
@ -193,6 +211,7 @@ var (
|
||||||
|
|
||||||
borFlags = []cli.Flag{
|
borFlags = []cli.Flag{
|
||||||
utils.HeimdallURLFlag,
|
utils.HeimdallURLFlag,
|
||||||
|
utils.WithoutHeimdallFlag,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -200,7 +219,7 @@ func init() {
|
||||||
// Initialize the CLI app and start Geth
|
// Initialize the CLI app and start Geth
|
||||||
app.Action = geth
|
app.Action = geth
|
||||||
app.HideVersion = true // we have a command to print the version
|
app.HideVersion = true // we have a command to print the version
|
||||||
app.Copyright = "Copyright 2013-2019 The go-ethereum Authors"
|
app.Copyright = "Copyright 2013-2020 The go-ethereum Authors"
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
// See chaincmd.go:
|
// See chaincmd.go:
|
||||||
initCommand,
|
initCommand,
|
||||||
|
|
@ -211,6 +230,7 @@ func init() {
|
||||||
copydbCommand,
|
copydbCommand,
|
||||||
removedbCommand,
|
removedbCommand,
|
||||||
dumpCommand,
|
dumpCommand,
|
||||||
|
dumpGenesisCommand,
|
||||||
inspectCommand,
|
inspectCommand,
|
||||||
// See accountcmd.go:
|
// See accountcmd.go:
|
||||||
accountCommand,
|
accountCommand,
|
||||||
|
|
@ -228,6 +248,8 @@ func init() {
|
||||||
dumpConfigCommand,
|
dumpConfigCommand,
|
||||||
// See retesteth.go
|
// See retesteth.go
|
||||||
retestethCommand,
|
retestethCommand,
|
||||||
|
// See cmd/utils/flags_legacy.go
|
||||||
|
utils.ShowDeprecated,
|
||||||
}
|
}
|
||||||
sort.Sort(cli.CommandsByName(app.Commands))
|
sort.Sort(cli.CommandsByName(app.Commands))
|
||||||
|
|
||||||
|
|
@ -235,64 +257,17 @@ func init() {
|
||||||
app.Flags = append(app.Flags, rpcFlags...)
|
app.Flags = append(app.Flags, rpcFlags...)
|
||||||
app.Flags = append(app.Flags, consoleFlags...)
|
app.Flags = append(app.Flags, consoleFlags...)
|
||||||
app.Flags = append(app.Flags, debug.Flags...)
|
app.Flags = append(app.Flags, debug.Flags...)
|
||||||
|
app.Flags = append(app.Flags, debug.DeprecatedFlags...)
|
||||||
app.Flags = append(app.Flags, whisperFlags...)
|
app.Flags = append(app.Flags, whisperFlags...)
|
||||||
app.Flags = append(app.Flags, metricsFlags...)
|
app.Flags = append(app.Flags, metricsFlags...)
|
||||||
app.Flags = append(app.Flags, borFlags...)
|
app.Flags = append(app.Flags, borFlags...)
|
||||||
|
|
||||||
app.Before = func(ctx *cli.Context) error {
|
app.Before = func(ctx *cli.Context) error {
|
||||||
logdir := ""
|
return debug.Setup(ctx)
|
||||||
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
|
|
||||||
logdir = (&node.Config{DataDir: utils.MakeDataDir(ctx)}).ResolvePath("logs")
|
|
||||||
}
|
|
||||||
if err := debug.Setup(ctx, logdir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// If we're a full node on mainnet without --cache specified, bump default cache allowance
|
|
||||||
if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) {
|
|
||||||
// Make sure we're not on any supported preconfigured testnet either
|
|
||||||
if !ctx.GlobalIsSet(utils.TestnetFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) {
|
|
||||||
// Nope, we're really on mainnet. Bump that cache up!
|
|
||||||
log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096)
|
|
||||||
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we're running a light client on any network, drop the cache to some meaningfully low amount
|
|
||||||
if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) {
|
|
||||||
log.Info("Dropping default light client cache", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 128)
|
|
||||||
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(128))
|
|
||||||
}
|
|
||||||
// Cap the cache allowance and tune the garbage collector
|
|
||||||
var mem gosigar.Mem
|
|
||||||
// Workaround until OpenBSD support lands into gosigar
|
|
||||||
// Check https://github.com/elastic/gosigar#supported-platforms
|
|
||||||
if runtime.GOOS != "openbsd" {
|
|
||||||
if err := mem.Get(); err == nil {
|
|
||||||
allowance := int(mem.Total / 1024 / 1024 / 3)
|
|
||||||
if cache := ctx.GlobalInt(utils.CacheFlag.Name); cache > allowance {
|
|
||||||
log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
|
|
||||||
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(allowance))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Ensure Go's GC ignores the database cache for trigger percentage
|
|
||||||
cache := ctx.GlobalInt(utils.CacheFlag.Name)
|
|
||||||
gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024)))
|
|
||||||
|
|
||||||
log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc))
|
|
||||||
godebug.SetGCPercent(int(gogc))
|
|
||||||
|
|
||||||
// Start metrics export if enabled
|
|
||||||
utils.SetupMetrics(ctx)
|
|
||||||
|
|
||||||
// Start system runtime metrics collection
|
|
||||||
go metrics.CollectProcessMetrics(3 * time.Second)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.After = func(ctx *cli.Context) error {
|
app.After = func(ctx *cli.Context) error {
|
||||||
debug.Exit()
|
debug.Exit()
|
||||||
console.Stdin.Close() // Resets terminal mode.
|
prompt.Stdin.Close() // Resets terminal mode.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -304,6 +279,72 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prepare manipulates memory cache allowance and setups metric system.
|
||||||
|
// This function should be called before launching devp2p stack.
|
||||||
|
func prepare(ctx *cli.Context) {
|
||||||
|
// If we're running a known preset, log it for convenience.
|
||||||
|
switch {
|
||||||
|
case ctx.GlobalIsSet(utils.LegacyTestnetFlag.Name):
|
||||||
|
log.Info("Starting Geth on Ropsten testnet...")
|
||||||
|
log.Warn("The --testnet flag is ambiguous! Please specify one of --goerli, --rinkeby, or --ropsten.")
|
||||||
|
log.Warn("The generic --testnet flag is deprecated and will be removed in the future!")
|
||||||
|
|
||||||
|
case ctx.GlobalIsSet(utils.RopstenFlag.Name):
|
||||||
|
log.Info("Starting Geth on Ropsten testnet...")
|
||||||
|
|
||||||
|
case ctx.GlobalIsSet(utils.RinkebyFlag.Name):
|
||||||
|
log.Info("Starting Geth on Rinkeby testnet...")
|
||||||
|
|
||||||
|
case ctx.GlobalIsSet(utils.GoerliFlag.Name):
|
||||||
|
log.Info("Starting Geth on Görli testnet...")
|
||||||
|
|
||||||
|
case ctx.GlobalIsSet(utils.DeveloperFlag.Name):
|
||||||
|
log.Info("Starting Geth in ephemeral dev mode...")
|
||||||
|
|
||||||
|
case !ctx.GlobalIsSet(utils.NetworkIdFlag.Name):
|
||||||
|
log.Info("Starting Geth on Ethereum mainnet...")
|
||||||
|
}
|
||||||
|
// If we're a full node on mainnet without --cache specified, bump default cache allowance
|
||||||
|
if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) {
|
||||||
|
// Make sure we're not on any supported preconfigured testnet either
|
||||||
|
if !ctx.GlobalIsSet(utils.LegacyTestnetFlag.Name) && !ctx.GlobalIsSet(utils.RopstenFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) && !ctx.GlobalIsSet(utils.DeveloperFlag.Name) {
|
||||||
|
// Nope, we're really on mainnet. Bump that cache up!
|
||||||
|
log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096)
|
||||||
|
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we're running a light client on any network, drop the cache to some meaningfully low amount
|
||||||
|
if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) {
|
||||||
|
log.Info("Dropping default light client cache", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 128)
|
||||||
|
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(128))
|
||||||
|
}
|
||||||
|
// Cap the cache allowance and tune the garbage collector
|
||||||
|
mem, err := gopsutil.VirtualMemory()
|
||||||
|
if err == nil {
|
||||||
|
if 32<<(^uintptr(0)>>63) == 32 && mem.Total > 2*1024*1024*1024 {
|
||||||
|
log.Warn("Lowering memory allowance on 32bit arch", "available", mem.Total/1024/1024, "addressable", 2*1024)
|
||||||
|
mem.Total = 2 * 1024 * 1024 * 1024
|
||||||
|
}
|
||||||
|
allowance := int(mem.Total / 1024 / 1024 / 3)
|
||||||
|
if cache := ctx.GlobalInt(utils.CacheFlag.Name); cache > allowance {
|
||||||
|
log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
|
||||||
|
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(allowance))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Ensure Go's GC ignores the database cache for trigger percentage
|
||||||
|
cache := ctx.GlobalInt(utils.CacheFlag.Name)
|
||||||
|
gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024)))
|
||||||
|
|
||||||
|
log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc))
|
||||||
|
godebug.SetGCPercent(int(gogc))
|
||||||
|
|
||||||
|
// Start metrics export if enabled
|
||||||
|
utils.SetupMetrics(ctx)
|
||||||
|
|
||||||
|
// Start system runtime metrics collection
|
||||||
|
go metrics.CollectProcessMetrics(3 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
// geth is the main entry point into the system if no special subcommand is ran.
|
// geth is the main entry point into the system if no special subcommand is ran.
|
||||||
// It creates a default node based on the command line arguments and runs it in
|
// It creates a default node based on the command line arguments and runs it in
|
||||||
// blocking mode, waiting for it to be shut down.
|
// blocking mode, waiting for it to be shut down.
|
||||||
|
|
@ -311,6 +352,7 @@ func geth(ctx *cli.Context) error {
|
||||||
if args := ctx.Args(); len(args) > 0 {
|
if args := ctx.Args(); len(args) > 0 {
|
||||||
return fmt.Errorf("invalid command: %q", args[0])
|
return fmt.Errorf("invalid command: %q", args[0])
|
||||||
}
|
}
|
||||||
|
prepare(ctx)
|
||||||
node := makeFullNode(ctx)
|
node := makeFullNode(ctx)
|
||||||
defer node.Close()
|
defer node.Close()
|
||||||
startNode(ctx, node)
|
startNode(ctx, node)
|
||||||
|
|
@ -343,7 +385,7 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
||||||
|
|
||||||
// Set contract backend for ethereum service if local node
|
// Set contract backend for ethereum service if local node
|
||||||
// is serving LES requests.
|
// is serving LES requests.
|
||||||
if ctx.GlobalInt(utils.LightLegacyServFlag.Name) > 0 || ctx.GlobalInt(utils.LightServeFlag.Name) > 0 {
|
if ctx.GlobalInt(utils.LegacyLightServFlag.Name) > 0 || ctx.GlobalInt(utils.LightServeFlag.Name) > 0 {
|
||||||
var ethService *eth.Ethereum
|
var ethService *eth.Ethereum
|
||||||
if err := stack.Service(ðService); err != nil {
|
if err := stack.Service(ðService); err != nil {
|
||||||
utils.Fatalf("Failed to retrieve ethereum service: %v", err)
|
utils.Fatalf("Failed to retrieve ethereum service: %v", err)
|
||||||
|
|
@ -428,16 +470,18 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
||||||
utils.Fatalf("Ethereum service not running: %v", err)
|
utils.Fatalf("Ethereum service not running: %v", err)
|
||||||
}
|
}
|
||||||
// Set the gas price to the limits from the CLI and start mining
|
// Set the gas price to the limits from the CLI and start mining
|
||||||
gasprice := utils.GlobalBig(ctx, utils.MinerLegacyGasPriceFlag.Name)
|
gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
|
||||||
if ctx.IsSet(utils.MinerGasPriceFlag.Name) {
|
if ctx.GlobalIsSet(utils.LegacyMinerGasPriceFlag.Name) && !ctx.GlobalIsSet(utils.MinerGasPriceFlag.Name) {
|
||||||
gasprice = utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
|
gasprice = utils.GlobalBig(ctx, utils.LegacyMinerGasPriceFlag.Name)
|
||||||
}
|
}
|
||||||
ethereum.TxPool().SetGasPrice(gasprice)
|
ethereum.TxPool().SetGasPrice(gasprice)
|
||||||
|
|
||||||
threads := ctx.GlobalInt(utils.MinerLegacyThreadsFlag.Name)
|
threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name)
|
||||||
if ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) {
|
if ctx.GlobalIsSet(utils.LegacyMinerThreadsFlag.Name) && !ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) {
|
||||||
threads = ctx.GlobalInt(utils.MinerThreadsFlag.Name)
|
threads = ctx.GlobalInt(utils.LegacyMinerThreadsFlag.Name)
|
||||||
|
log.Warn("The flag --minerthreads is deprecated and will be removed in the future, please use --miner.threads")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ethereum.StartMining(threads); err != nil {
|
if err := ethereum.StartMining(threads); err != nil {
|
||||||
utils.Fatalf("Failed to start mining: %v", err)
|
utils.Fatalf("Failed to start mining: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,6 @@ func version(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
fmt.Println("Architecture:", runtime.GOARCH)
|
fmt.Println("Architecture:", runtime.GOARCH)
|
||||||
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
|
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
|
||||||
fmt.Println("Network Id:", eth.DefaultConfig.NetworkId)
|
|
||||||
fmt.Println("Go Version:", runtime.Version())
|
fmt.Println("Go Version:", runtime.Version())
|
||||||
fmt.Println("Operating System:", runtime.GOOS)
|
fmt.Println("Operating System:", runtime.GOOS)
|
||||||
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
|
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
|
||||||
|
|
|
||||||
|
|
@ -80,13 +80,14 @@ type RetestethEthAPI interface {
|
||||||
SendRawTransaction(ctx context.Context, rawTx hexutil.Bytes) (common.Hash, error)
|
SendRawTransaction(ctx context.Context, rawTx hexutil.Bytes) (common.Hash, error)
|
||||||
BlockNumber(ctx context.Context) (uint64, error)
|
BlockNumber(ctx context.Context) (uint64, error)
|
||||||
GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error)
|
GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error)
|
||||||
|
GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error)
|
||||||
GetBalance(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (*math.HexOrDecimal256, error)
|
GetBalance(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (*math.HexOrDecimal256, error)
|
||||||
GetCode(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (hexutil.Bytes, error)
|
GetCode(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (hexutil.Bytes, error)
|
||||||
GetTransactionCount(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (uint64, error)
|
GetTransactionCount(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (uint64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type RetestethDebugAPI interface {
|
type RetestethDebugAPI interface {
|
||||||
AccountRangeAt(ctx context.Context,
|
AccountRange(ctx context.Context,
|
||||||
blockHashOrNumber *math.HexOrDecimal256, txIndex uint64,
|
blockHashOrNumber *math.HexOrDecimal256, txIndex uint64,
|
||||||
addressHash *math.HexOrDecimal256, maxResults uint64,
|
addressHash *math.HexOrDecimal256, maxResults uint64,
|
||||||
) (AccountRangeResult, error)
|
) (AccountRangeResult, error)
|
||||||
|
|
@ -110,7 +111,6 @@ type RetestethAPI struct {
|
||||||
genesisHash common.Hash
|
genesisHash common.Hash
|
||||||
engine *NoRewardEngine
|
engine *NoRewardEngine
|
||||||
blockchain *core.BlockChain
|
blockchain *core.BlockChain
|
||||||
blockNumber uint64
|
|
||||||
txMap map[common.Address]map[uint64]*types.Transaction // Sender -> Nonce -> Transaction
|
txMap map[common.Address]map[uint64]*types.Transaction // Sender -> Nonce -> Transaction
|
||||||
txSenders map[common.Address]struct{} // Set of transaction senders
|
txSenders map[common.Address]struct{} // Set of transaction senders
|
||||||
blockInterval uint64
|
blockInterval uint64
|
||||||
|
|
@ -132,6 +132,7 @@ type CParamsParams struct {
|
||||||
ByzantiumForkBlock *math.HexOrDecimal64 `json:"byzantiumForkBlock"`
|
ByzantiumForkBlock *math.HexOrDecimal64 `json:"byzantiumForkBlock"`
|
||||||
ConstantinopleForkBlock *math.HexOrDecimal64 `json:"constantinopleForkBlock"`
|
ConstantinopleForkBlock *math.HexOrDecimal64 `json:"constantinopleForkBlock"`
|
||||||
ConstantinopleFixForkBlock *math.HexOrDecimal64 `json:"constantinopleFixForkBlock"`
|
ConstantinopleFixForkBlock *math.HexOrDecimal64 `json:"constantinopleFixForkBlock"`
|
||||||
|
IstanbulBlock *math.HexOrDecimal64 `json:"istanbulForkBlock"`
|
||||||
ChainID *math.HexOrDecimal256 `json:"chainID"`
|
ChainID *math.HexOrDecimal256 `json:"chainID"`
|
||||||
MaximumExtraDataSize math.HexOrDecimal64 `json:"maximumExtraDataSize"`
|
MaximumExtraDataSize math.HexOrDecimal64 `json:"maximumExtraDataSize"`
|
||||||
TieBreakingGas bool `json:"tieBreakingGas"`
|
TieBreakingGas bool `json:"tieBreakingGas"`
|
||||||
|
|
@ -319,6 +320,7 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
|
||||||
byzantiumBlock *big.Int
|
byzantiumBlock *big.Int
|
||||||
constantinopleBlock *big.Int
|
constantinopleBlock *big.Int
|
||||||
petersburgBlock *big.Int
|
petersburgBlock *big.Int
|
||||||
|
istanbulBlock *big.Int
|
||||||
)
|
)
|
||||||
if chainParams.Params.HomesteadForkBlock != nil {
|
if chainParams.Params.HomesteadForkBlock != nil {
|
||||||
homesteadBlock = big.NewInt(int64(*chainParams.Params.HomesteadForkBlock))
|
homesteadBlock = big.NewInt(int64(*chainParams.Params.HomesteadForkBlock))
|
||||||
|
|
@ -345,18 +347,23 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
|
||||||
if constantinopleBlock != nil && petersburgBlock == nil {
|
if constantinopleBlock != nil && petersburgBlock == nil {
|
||||||
petersburgBlock = big.NewInt(100000000000)
|
petersburgBlock = big.NewInt(100000000000)
|
||||||
}
|
}
|
||||||
|
if chainParams.Params.IstanbulBlock != nil {
|
||||||
|
istanbulBlock = big.NewInt(int64(*chainParams.Params.IstanbulBlock))
|
||||||
|
}
|
||||||
|
|
||||||
genesis := &core.Genesis{
|
genesis := &core.Genesis{
|
||||||
Config: ¶ms.ChainConfig{
|
Config: ¶ms.ChainConfig{
|
||||||
ChainID: chainId,
|
ChainID: chainId,
|
||||||
HomesteadBlock: homesteadBlock,
|
HomesteadBlock: homesteadBlock,
|
||||||
DAOForkBlock: daoForkBlock,
|
DAOForkBlock: daoForkBlock,
|
||||||
DAOForkSupport: false,
|
DAOForkSupport: true,
|
||||||
EIP150Block: eip150Block,
|
EIP150Block: eip150Block,
|
||||||
EIP155Block: eip155Block,
|
EIP155Block: eip155Block,
|
||||||
EIP158Block: eip158Block,
|
EIP158Block: eip158Block,
|
||||||
ByzantiumBlock: byzantiumBlock,
|
ByzantiumBlock: byzantiumBlock,
|
||||||
ConstantinopleBlock: constantinopleBlock,
|
ConstantinopleBlock: constantinopleBlock,
|
||||||
PetersburgBlock: petersburgBlock,
|
PetersburgBlock: petersburgBlock,
|
||||||
|
IstanbulBlock: istanbulBlock,
|
||||||
},
|
},
|
||||||
Nonce: uint64(chainParams.Genesis.Nonce),
|
Nonce: uint64(chainParams.Genesis.Nonce),
|
||||||
Timestamp: uint64(chainParams.Genesis.Timestamp),
|
Timestamp: uint64(chainParams.Genesis.Timestamp),
|
||||||
|
|
@ -380,18 +387,20 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
|
||||||
inner = ethash.NewFaker()
|
inner = ethash.NewFaker()
|
||||||
case "Ethash":
|
case "Ethash":
|
||||||
inner = ethash.New(ethash.Config{
|
inner = ethash.New(ethash.Config{
|
||||||
CacheDir: "ethash",
|
CacheDir: "ethash",
|
||||||
CachesInMem: 2,
|
CachesInMem: 2,
|
||||||
CachesOnDisk: 3,
|
CachesOnDisk: 3,
|
||||||
DatasetsInMem: 1,
|
CachesLockMmap: false,
|
||||||
DatasetsOnDisk: 2,
|
DatasetsInMem: 1,
|
||||||
|
DatasetsOnDisk: 2,
|
||||||
|
DatasetsLockMmap: false,
|
||||||
}, nil, false)
|
}, nil, false)
|
||||||
default:
|
default:
|
||||||
return false, fmt.Errorf("unrecognised seal engine: %s", chainParams.SealEngine)
|
return false, fmt.Errorf("unrecognised seal engine: %s", chainParams.SealEngine)
|
||||||
}
|
}
|
||||||
engine := &NoRewardEngine{inner: inner, rewardsOn: chainParams.SealEngine != "NoReward"}
|
engine := &NoRewardEngine{inner: inner, rewardsOn: chainParams.SealEngine != "NoReward"}
|
||||||
|
|
||||||
blockchain, err := core.NewBlockChain(ethDb, nil, chainConfig, engine, vm.Config{}, nil)
|
blockchain, err := core.NewBlockChain(ethDb, nil, chainConfig, engine, vm.Config{}, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
@ -404,7 +413,6 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
|
||||||
api.engine = engine
|
api.engine = engine
|
||||||
api.blockchain = blockchain
|
api.blockchain = blockchain
|
||||||
api.db = state.NewDatabase(api.ethDb)
|
api.db = state.NewDatabase(api.ethDb)
|
||||||
api.blockNumber = 0
|
|
||||||
api.txMap = make(map[common.Address]map[uint64]*types.Transaction)
|
api.txMap = make(map[common.Address]map[uint64]*types.Transaction)
|
||||||
api.txSenders = make(map[common.Address]struct{})
|
api.txSenders = make(map[common.Address]struct{})
|
||||||
api.blockInterval = 0
|
api.blockInterval = 0
|
||||||
|
|
@ -417,7 +425,7 @@ func (api *RetestethAPI) SendRawTransaction(ctx context.Context, rawTx hexutil.B
|
||||||
// Return nil is not by mistake - some tests include sending transaction where gasLimit overflows uint64
|
// Return nil is not by mistake - some tests include sending transaction where gasLimit overflows uint64
|
||||||
return common.Hash{}, nil
|
return common.Hash{}, nil
|
||||||
}
|
}
|
||||||
signer := types.MakeSigner(api.chainConfig, big.NewInt(int64(api.blockNumber)))
|
signer := types.MakeSigner(api.chainConfig, big.NewInt(int64(api.currentNumber())))
|
||||||
sender, err := types.Sender(signer, tx)
|
sender, err := types.Sender(signer, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
|
|
@ -443,9 +451,17 @@ func (api *RetestethAPI) MineBlocks(ctx context.Context, number uint64) (bool, e
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *RetestethAPI) currentNumber() uint64 {
|
||||||
|
if current := api.blockchain.CurrentBlock(); current != nil {
|
||||||
|
return current.NumberU64()
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func (api *RetestethAPI) mineBlock() error {
|
func (api *RetestethAPI) mineBlock() error {
|
||||||
parentHash := rawdb.ReadCanonicalHash(api.ethDb, api.blockNumber)
|
number := api.currentNumber()
|
||||||
parent := rawdb.ReadBlock(api.ethDb, parentHash, api.blockNumber)
|
parentHash := rawdb.ReadCanonicalHash(api.ethDb, number)
|
||||||
|
parent := rawdb.ReadBlock(api.ethDb, parentHash, number)
|
||||||
var timestamp uint64
|
var timestamp uint64
|
||||||
if api.blockInterval == 0 {
|
if api.blockInterval == 0 {
|
||||||
timestamp = uint64(time.Now().Unix())
|
timestamp = uint64(time.Now().Unix())
|
||||||
|
|
@ -455,7 +471,7 @@ func (api *RetestethAPI) mineBlock() error {
|
||||||
gasLimit := core.CalcGasLimit(parent, 9223372036854775807, 9223372036854775807)
|
gasLimit := core.CalcGasLimit(parent, 9223372036854775807, 9223372036854775807)
|
||||||
header := &types.Header{
|
header := &types.Header{
|
||||||
ParentHash: parent.Hash(),
|
ParentHash: parent.Hash(),
|
||||||
Number: big.NewInt(int64(api.blockNumber + 1)),
|
Number: big.NewInt(int64(number + 1)),
|
||||||
GasLimit: gasLimit,
|
GasLimit: gasLimit,
|
||||||
Extra: api.extraData,
|
Extra: api.extraData,
|
||||||
Time: timestamp,
|
Time: timestamp,
|
||||||
|
|
@ -488,7 +504,6 @@ func (api *RetestethAPI) mineBlock() error {
|
||||||
txCount := 0
|
txCount := 0
|
||||||
var txs []*types.Transaction
|
var txs []*types.Transaction
|
||||||
var receipts []*types.Receipt
|
var receipts []*types.Receipt
|
||||||
var coalescedLogs []*types.Log
|
|
||||||
var blockFull = gasPool.Gas() < params.TxGas
|
var blockFull = gasPool.Gas() < params.TxGas
|
||||||
for address := range api.txSenders {
|
for address := range api.txSenders {
|
||||||
if blockFull {
|
if blockFull {
|
||||||
|
|
@ -501,7 +516,7 @@ func (api *RetestethAPI) mineBlock() error {
|
||||||
statedb.Prepare(tx.Hash(), common.Hash{}, txCount)
|
statedb.Prepare(tx.Hash(), common.Hash{}, txCount)
|
||||||
snap := statedb.Snapshot()
|
snap := statedb.Snapshot()
|
||||||
|
|
||||||
receipt, _, err := core.ApplyTransaction(
|
receipt, err := core.ApplyTransaction(
|
||||||
api.chainConfig,
|
api.chainConfig,
|
||||||
api.blockchain,
|
api.blockchain,
|
||||||
&api.author,
|
&api.author,
|
||||||
|
|
@ -515,7 +530,6 @@ func (api *RetestethAPI) mineBlock() error {
|
||||||
}
|
}
|
||||||
txs = append(txs, tx)
|
txs = append(txs, tx)
|
||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
coalescedLogs = append(coalescedLogs, receipt.Logs...)
|
|
||||||
delete(m, nonce)
|
delete(m, nonce)
|
||||||
if len(m) == 0 {
|
if len(m) == 0 {
|
||||||
// Last tx for the sender
|
// Last tx for the sender
|
||||||
|
|
@ -533,6 +547,9 @@ func (api *RetestethAPI) mineBlock() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
block, err := api.engine.FinalizeAndAssemble(api.blockchain, header, statedb, txs, []*types.Header{}, receipts)
|
block, err := api.engine.FinalizeAndAssemble(api.blockchain, header, statedb, txs, []*types.Header{}, receipts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return api.importBlock(block)
|
return api.importBlock(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -540,8 +557,7 @@ func (api *RetestethAPI) importBlock(block *types.Block) error {
|
||||||
if _, err := api.blockchain.InsertChain([]*types.Block{block}); err != nil {
|
if _, err := api.blockchain.InsertChain([]*types.Block{block}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
api.blockNumber = block.NumberU64()
|
fmt.Printf("Imported block %d, head is %d\n", block.NumberU64(), api.currentNumber())
|
||||||
fmt.Printf("Imported block %d\n", block.NumberU64())
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -566,7 +582,9 @@ func (api *RetestethAPI) RewindToBlock(ctx context.Context, newHead uint64) (boo
|
||||||
if err := api.blockchain.SetHead(newHead); err != nil {
|
if err := api.blockchain.SetHead(newHead); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
api.blockNumber = newHead
|
// When we rewind, the transaction pool should be cleaned out.
|
||||||
|
api.txMap = make(map[common.Address]map[uint64]*types.Transaction)
|
||||||
|
api.txSenders = make(map[common.Address]struct{})
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -586,8 +604,7 @@ func (api *RetestethAPI) GetLogHash(ctx context.Context, txHash common.Hash) (co
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *RetestethAPI) BlockNumber(ctx context.Context) (uint64, error) {
|
func (api *RetestethAPI) BlockNumber(ctx context.Context) (uint64, error) {
|
||||||
//fmt.Printf("BlockNumber, response: %d\n", api.blockNumber)
|
return api.currentNumber(), nil
|
||||||
return api.blockNumber, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *RetestethAPI) GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error) {
|
func (api *RetestethAPI) GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error) {
|
||||||
|
|
@ -604,7 +621,21 @@ func (api *RetestethAPI) GetBlockByNumber(ctx context.Context, blockNr math.HexO
|
||||||
return nil, fmt.Errorf("block %d not found", blockNr)
|
return nil, fmt.Errorf("block %d not found", blockNr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *RetestethAPI) AccountRangeAt(ctx context.Context,
|
func (api *RetestethAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) {
|
||||||
|
block := api.blockchain.GetBlockByHash(blockHash)
|
||||||
|
if block != nil {
|
||||||
|
response, err := RPCMarshalBlock(block, true, fullTx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response["author"] = response["miner"]
|
||||||
|
response["totalDifficulty"] = (*hexutil.Big)(api.blockchain.GetTd(block.Hash(), block.Number().Uint64()))
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("block 0x%x not found", blockHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *RetestethAPI) AccountRange(ctx context.Context,
|
||||||
blockHashOrNumber *math.HexOrDecimal256, txIndex uint64,
|
blockHashOrNumber *math.HexOrDecimal256, txIndex uint64,
|
||||||
addressHash *math.HexOrDecimal256, maxResults uint64,
|
addressHash *math.HexOrDecimal256, maxResults uint64,
|
||||||
) (AccountRangeResult, error) {
|
) (AccountRangeResult, error) {
|
||||||
|
|
@ -647,7 +678,7 @@ func (api *RetestethAPI) AccountRangeAt(ctx context.Context,
|
||||||
context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
|
context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
|
||||||
// Not yet the searched for transaction, execute on top of the current state
|
// Not yet the searched for transaction, execute on top of the current state
|
||||||
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
|
||||||
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
||||||
return AccountRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
return AccountRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||||
}
|
}
|
||||||
// Ensure any modifications are committed to the state
|
// Ensure any modifications are committed to the state
|
||||||
|
|
@ -669,12 +700,9 @@ func (api *RetestethAPI) AccountRangeAt(ctx context.Context,
|
||||||
}
|
}
|
||||||
it := trie.NewIterator(accountTrie.NodeIterator(common.BigToHash((*big.Int)(addressHash)).Bytes()))
|
it := trie.NewIterator(accountTrie.NodeIterator(common.BigToHash((*big.Int)(addressHash)).Bytes()))
|
||||||
result := AccountRangeResult{AddressMap: make(map[common.Hash]common.Address)}
|
result := AccountRangeResult{AddressMap: make(map[common.Hash]common.Address)}
|
||||||
for i := 0; /*i < int(maxResults) && */ it.Next(); i++ {
|
for i := 0; i < int(maxResults) && it.Next(); i++ {
|
||||||
if preimage := accountTrie.GetKey(it.Key); preimage != nil {
|
if preimage := accountTrie.GetKey(it.Key); preimage != nil {
|
||||||
result.AddressMap[common.BytesToHash(it.Key)] = common.BytesToAddress(preimage)
|
result.AddressMap[common.BytesToHash(it.Key)] = common.BytesToAddress(preimage)
|
||||||
//fmt.Printf("%x: %x\n", it.Key, preimage)
|
|
||||||
} else {
|
|
||||||
//fmt.Printf("could not find preimage for %x\n", it.Key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//fmt.Printf("Number of entries returned: %d\n", len(result.AddressMap))
|
//fmt.Printf("Number of entries returned: %d\n", len(result.AddressMap))
|
||||||
|
|
@ -760,15 +788,15 @@ func (api *RetestethAPI) StorageRangeAt(ctx context.Context,
|
||||||
context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
|
context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
|
||||||
// Not yet the searched for transaction, execute on top of the current state
|
// Not yet the searched for transaction, execute on top of the current state
|
||||||
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
|
||||||
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
||||||
return StorageRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
return StorageRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||||
}
|
}
|
||||||
// Ensure any modifications are committed to the state
|
// Ensure any modifications are committed to the state
|
||||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||||
root = statedb.IntermediateRoot(vmenv.ChainConfig().IsEIP158(block.Number()))
|
_ = statedb.IntermediateRoot(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||||
if idx == int(txIndex) {
|
if idx == int(txIndex) {
|
||||||
// This is to make sure root can be opened by OpenTrie
|
// This is to make sure root can be opened by OpenTrie
|
||||||
root, err = statedb.Commit(vmenv.ChainConfig().IsEIP158(block.Number()))
|
_, err = statedb.Commit(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return StorageRangeResult{}, err
|
return StorageRangeResult{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -798,9 +826,6 @@ func (api *RetestethAPI) StorageRangeAt(ctx context.Context,
|
||||||
Key: string(ks),
|
Key: string(ks),
|
||||||
Value: string(vs),
|
Value: string(vs),
|
||||||
}
|
}
|
||||||
//fmt.Printf("Key: %s, Value: %s\n", ks, vs)
|
|
||||||
} else {
|
|
||||||
//fmt.Printf("Did not find preimage for %x\n", it.Key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if it.Next() {
|
if it.Next() {
|
||||||
|
|
@ -829,7 +854,7 @@ func retesteth(ctx *cli.Context) error {
|
||||||
log.Info("Welcome to retesteth!")
|
log.Info("Welcome to retesteth!")
|
||||||
// register signer API with server
|
// register signer API with server
|
||||||
var (
|
var (
|
||||||
extapiURL = "n/a"
|
extapiURL string
|
||||||
)
|
)
|
||||||
apiImpl := &RetestethAPI{}
|
apiImpl := &RetestethAPI{}
|
||||||
var testApi RetestethTestAPI = apiImpl
|
var testApi RetestethTestAPI = apiImpl
|
||||||
|
|
@ -862,12 +887,25 @@ func retesteth(ctx *cli.Context) error {
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
vhosts := splitAndTrim(ctx.GlobalString(utils.RPCVirtualHostsFlag.Name))
|
vhosts := splitAndTrim(ctx.GlobalString(utils.HTTPVirtualHostsFlag.Name))
|
||||||
cors := splitAndTrim(ctx.GlobalString(utils.RPCCORSDomainFlag.Name))
|
cors := splitAndTrim(ctx.GlobalString(utils.HTTPCORSDomainFlag.Name))
|
||||||
|
|
||||||
|
// register apis and create handler stack
|
||||||
|
srv := rpc.NewServer()
|
||||||
|
err := node.RegisterApisFromWhitelist(rpcAPI, []string{"test", "eth", "debug", "web3"}, srv, false)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Could not register RPC apis: %w", err)
|
||||||
|
}
|
||||||
|
handler := node.NewHTTPHandlerStack(srv, cors, vhosts)
|
||||||
|
|
||||||
// start http server
|
// start http server
|
||||||
httpEndpoint := fmt.Sprintf("%s:%d", ctx.GlobalString(utils.RPCListenAddrFlag.Name), ctx.Int(rpcPortFlag.Name))
|
var RetestethHTTPTimeouts = rpc.HTTPTimeouts{
|
||||||
listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"test", "eth", "debug", "web3"}, cors, vhosts, rpc.DefaultHTTPTimeouts)
|
ReadTimeout: 120 * time.Second,
|
||||||
|
WriteTimeout: 120 * time.Second,
|
||||||
|
IdleTimeout: 120 * time.Second,
|
||||||
|
}
|
||||||
|
httpEndpoint := fmt.Sprintf("%s:%d", ctx.GlobalString(utils.HTTPListenAddrFlag.Name), ctx.Int(rpcPortFlag.Name))
|
||||||
|
httpServer, _, err := node.StartHTTPEndpoint(httpEndpoint, RetestethHTTPTimeouts, handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Could not start RPC api: %v", err)
|
utils.Fatalf("Could not start RPC api: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -875,11 +913,12 @@ func retesteth(ctx *cli.Context) error {
|
||||||
log.Info("HTTP endpoint opened", "url", extapiURL)
|
log.Info("HTTP endpoint opened", "url", extapiURL)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
listener.Close()
|
// Don't bother imposing a timeout here.
|
||||||
|
httpServer.Shutdown(context.Background())
|
||||||
log.Info("HTTP endpoint closed", "url", httpEndpoint)
|
log.Info("HTTP endpoint closed", "url", httpEndpoint)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
abortChan := make(chan os.Signal)
|
abortChan := make(chan os.Signal, 11)
|
||||||
signal.Notify(abortChan, os.Interrupt)
|
signal.Notify(abortChan, os.Interrupt)
|
||||||
|
|
||||||
sig := <-abortChan
|
sig := <-abortChan
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,16 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/reexec"
|
"github.com/docker/docker/pkg/reexec"
|
||||||
"github.com/maticnetwork/bor/internal/cmdtest"
|
"github.com/maticnetwork/bor/internal/cmdtest"
|
||||||
|
"github.com/maticnetwork/bor/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func tmpdir(t *testing.T) string {
|
func tmpdir(t *testing.T) string {
|
||||||
|
|
@ -96,3 +99,28 @@ func runGeth(t *testing.T, args ...string) *testgeth {
|
||||||
|
|
||||||
return tt
|
return tt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// waitForEndpoint attempts to connect to an RPC endpoint until it succeeds.
|
||||||
|
func waitForEndpoint(t *testing.T, endpoint string, timeout time.Duration) {
|
||||||
|
probe := func() bool {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
c, err := rpc.DialContext(ctx, endpoint)
|
||||||
|
if c != nil {
|
||||||
|
_, err = c.SupportedModules()
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
for {
|
||||||
|
if probe() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if time.Since(start) > timeout {
|
||||||
|
t.Fatal("endpoint", endpoint, "did not open within", timeout)
|
||||||
|
}
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
BIN
cmd/bor/testdata/blockchain.blocks
vendored
Normal file
BIN
cmd/bor/testdata/blockchain.blocks
vendored
Normal file
Binary file not shown.
24
cmd/bor/testdata/clique.json
vendored
Normal file
24
cmd/bor/testdata/clique.json
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"chainId": 15,
|
||||||
|
"homesteadBlock": 0,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip155Block": 0,
|
||||||
|
"eip158Block": 0,
|
||||||
|
"byzantiumBlock": 0,
|
||||||
|
"constantinopleBlock": 0,
|
||||||
|
"petersburgBlock": 0,
|
||||||
|
"clique": {
|
||||||
|
"period": 5,
|
||||||
|
"epoch": 30000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"difficulty": "1",
|
||||||
|
"gasLimit": "8000000",
|
||||||
|
"extradata": "0x000000000000000000000000000000000000000000000000000000000000000002f0d131f1f97aef08aec6e3291b957d9efe71050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"alloc": {
|
||||||
|
"02f0d131f1f97aef08aec6e3291b957d9efe7105": {
|
||||||
|
"balance": "300000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
cmd/bor/testdata/key.prv
vendored
Normal file
1
cmd/bor/testdata/key.prv
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
48aa455c373ec5ce7fefb0e54f44a215decdc85b9047bc4d09801e038909bdbe
|
||||||
1
cmd/bor/testdata/password.txt
vendored
Normal file
1
cmd/bor/testdata/password.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
foobar
|
||||||
|
|
@ -22,8 +22,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/maticnetwork/bor/cmd/utils"
|
"github.com/maticnetwork/bor/cmd/utils"
|
||||||
"github.com/maticnetwork/bor/internal/debug"
|
"github.com/maticnetwork/bor/internal/debug"
|
||||||
cli "gopkg.in/urfave/cli.v1"
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
|
|
@ -74,12 +72,14 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
utils.NoUSBFlag,
|
utils.NoUSBFlag,
|
||||||
utils.SmartCardDaemonPathFlag,
|
utils.SmartCardDaemonPathFlag,
|
||||||
utils.NetworkIdFlag,
|
utils.NetworkIdFlag,
|
||||||
utils.TestnetFlag,
|
|
||||||
utils.RinkebyFlag,
|
|
||||||
utils.GoerliFlag,
|
utils.GoerliFlag,
|
||||||
|
utils.RinkebyFlag,
|
||||||
|
utils.YoloV1Flag,
|
||||||
|
utils.RopstenFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
utils.ExitWhenSyncedFlag,
|
utils.ExitWhenSyncedFlag,
|
||||||
utils.GCModeFlag,
|
utils.GCModeFlag,
|
||||||
|
utils.TxLookupLimitFlag,
|
||||||
utils.EthStatsURLFlag,
|
utils.EthStatsURLFlag,
|
||||||
utils.IdentityFlag,
|
utils.IdentityFlag,
|
||||||
utils.LightKDFFlag,
|
utils.LightKDFFlag,
|
||||||
|
|
@ -111,21 +111,13 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
utils.EthashCacheDirFlag,
|
utils.EthashCacheDirFlag,
|
||||||
utils.EthashCachesInMemoryFlag,
|
utils.EthashCachesInMemoryFlag,
|
||||||
utils.EthashCachesOnDiskFlag,
|
utils.EthashCachesOnDiskFlag,
|
||||||
|
utils.EthashCachesLockMmapFlag,
|
||||||
utils.EthashDatasetDirFlag,
|
utils.EthashDatasetDirFlag,
|
||||||
utils.EthashDatasetsInMemoryFlag,
|
utils.EthashDatasetsInMemoryFlag,
|
||||||
utils.EthashDatasetsOnDiskFlag,
|
utils.EthashDatasetsOnDiskFlag,
|
||||||
|
utils.EthashDatasetsLockMmapFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
//{
|
|
||||||
// Name: "DASHBOARD",
|
|
||||||
// Flags: []cli.Flag{
|
|
||||||
// utils.DashboardEnabledFlag,
|
|
||||||
// utils.DashboardAddrFlag,
|
|
||||||
// utils.DashboardPortFlag,
|
|
||||||
// utils.DashboardRefreshFlag,
|
|
||||||
// utils.DashboardAssetsFlag,
|
|
||||||
// },
|
|
||||||
//},
|
|
||||||
{
|
{
|
||||||
Name: "TRANSACTION POOL",
|
Name: "TRANSACTION POOL",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
|
|
@ -149,6 +141,7 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
utils.CacheDatabaseFlag,
|
utils.CacheDatabaseFlag,
|
||||||
utils.CacheTrieFlag,
|
utils.CacheTrieFlag,
|
||||||
utils.CacheGCFlag,
|
utils.CacheGCFlag,
|
||||||
|
utils.CacheSnapshotFlag,
|
||||||
utils.CacheNoPrefetchFlag,
|
utils.CacheNoPrefetchFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -166,13 +159,12 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
utils.IPCDisabledFlag,
|
utils.IPCDisabledFlag,
|
||||||
utils.IPCPathFlag,
|
utils.IPCPathFlag,
|
||||||
utils.RPCEnabledFlag,
|
utils.HTTPEnabledFlag,
|
||||||
utils.RPCListenAddrFlag,
|
utils.HTTPListenAddrFlag,
|
||||||
utils.RPCPortFlag,
|
utils.HTTPPortFlag,
|
||||||
utils.RPCApiFlag,
|
utils.HTTPApiFlag,
|
||||||
utils.RPCGlobalGasCap,
|
utils.HTTPCORSDomainFlag,
|
||||||
utils.RPCCORSDomainFlag,
|
utils.HTTPVirtualHostsFlag,
|
||||||
utils.RPCVirtualHostsFlag,
|
|
||||||
utils.WSEnabledFlag,
|
utils.WSEnabledFlag,
|
||||||
utils.WSListenAddrFlag,
|
utils.WSListenAddrFlag,
|
||||||
utils.WSPortFlag,
|
utils.WSPortFlag,
|
||||||
|
|
@ -183,6 +175,8 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
utils.GraphQLPortFlag,
|
utils.GraphQLPortFlag,
|
||||||
utils.GraphQLCORSDomainFlag,
|
utils.GraphQLCORSDomainFlag,
|
||||||
utils.GraphQLVirtualHostsFlag,
|
utils.GraphQLVirtualHostsFlag,
|
||||||
|
utils.RPCGlobalGasCap,
|
||||||
|
utils.RPCGlobalTxFeeCap,
|
||||||
utils.JSpathFlag,
|
utils.JSpathFlag,
|
||||||
utils.ExecFlag,
|
utils.ExecFlag,
|
||||||
utils.PreloadJSFlag,
|
utils.PreloadJSFlag,
|
||||||
|
|
@ -192,8 +186,9 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
Name: "NETWORKING",
|
Name: "NETWORKING",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
utils.BootnodesFlag,
|
utils.BootnodesFlag,
|
||||||
utils.BootnodesV4Flag,
|
utils.LegacyBootnodesV4Flag,
|
||||||
utils.BootnodesV5Flag,
|
utils.LegacyBootnodesV5Flag,
|
||||||
|
utils.DNSDiscoveryFlag,
|
||||||
utils.ListenPortFlag,
|
utils.ListenPortFlag,
|
||||||
utils.MaxPeersFlag,
|
utils.MaxPeersFlag,
|
||||||
utils.MaxPendingPeersFlag,
|
utils.MaxPendingPeersFlag,
|
||||||
|
|
@ -255,19 +250,28 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
Flags: whisperFlags,
|
Flags: whisperFlags,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "DEPRECATED",
|
Name: "ALIASED (deprecated)",
|
||||||
Flags: []cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
utils.LightLegacyServFlag,
|
utils.LegacyRPCEnabledFlag,
|
||||||
utils.LightLegacyPeersFlag,
|
utils.LegacyRPCListenAddrFlag,
|
||||||
utils.MinerLegacyThreadsFlag,
|
utils.LegacyRPCPortFlag,
|
||||||
utils.MinerLegacyGasTargetFlag,
|
utils.LegacyRPCCORSDomainFlag,
|
||||||
utils.MinerLegacyGasPriceFlag,
|
utils.LegacyRPCVirtualHostsFlag,
|
||||||
utils.MinerLegacyEtherbaseFlag,
|
utils.LegacyRPCApiFlag,
|
||||||
utils.MinerLegacyExtraDataFlag,
|
utils.LegacyWSListenAddrFlag,
|
||||||
},
|
utils.LegacyWSPortFlag,
|
||||||
|
utils.LegacyWSAllowedOriginsFlag,
|
||||||
|
utils.LegacyWSApiFlag,
|
||||||
|
utils.LegacyGpoBlocksFlag,
|
||||||
|
utils.LegacyGpoPercentileFlag,
|
||||||
|
}, debug.DeprecatedFlags...),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "MISC",
|
Name: "MISC",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
utils.SnapshotFlag,
|
||||||
|
cli.HelpFlag,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -325,13 +329,17 @@ func init() {
|
||||||
categorized[flag.String()] = struct{}{}
|
categorized[flag.String()] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
deprecated := make(map[string]struct{})
|
||||||
|
for _, flag := range utils.DeprecatedFlags {
|
||||||
|
deprecated[flag.String()] = struct{}{}
|
||||||
|
}
|
||||||
|
// Only add uncategorized flags if they are not deprecated
|
||||||
var uncategorized []cli.Flag
|
var uncategorized []cli.Flag
|
||||||
for _, flag := range data.(*cli.App).Flags {
|
for _, flag := range data.(*cli.App).Flags {
|
||||||
if _, ok := categorized[flag.String()]; !ok {
|
if _, ok := categorized[flag.String()]; !ok {
|
||||||
if strings.HasPrefix(flag.GetName(), "dashboard") {
|
if _, ok := deprecated[flag.String()]; !ok {
|
||||||
continue
|
uncategorized = append(uncategorized, flag)
|
||||||
}
|
}
|
||||||
uncategorized = append(uncategorized, flag)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(uncategorized) > 0 {
|
if len(uncategorized) > 0 {
|
||||||
|
|
|
||||||
103
cmd/checkpoint-admin/README.md
Normal file
103
cmd/checkpoint-admin/README.md
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
## Checkpoint-admin
|
||||||
|
|
||||||
|
Checkpoint-admin is a tool for updating checkpoint oracle status. It provides a series of functions including deploying checkpoint oracle contract, signing for new checkpoints, and updating checkpoints in the checkpoint oracle contract.
|
||||||
|
|
||||||
|
### Checkpoint
|
||||||
|
|
||||||
|
In the LES protocol, there is an important concept called checkpoint. In simple terms, whenever a certain number of blocks are generated on the blockchain, a new checkpoint is generated which contains some important information such as
|
||||||
|
|
||||||
|
* Block hash at checkpoint
|
||||||
|
* Canonical hash trie root at checkpoint
|
||||||
|
* Bloom trie root at checkpoint
|
||||||
|
|
||||||
|
*For a more detailed introduction to checkpoint, please see the LES [spec](https://github.com/ethereum/devp2p/blob/master/caps/les.md).*
|
||||||
|
|
||||||
|
Using this information, light clients can skip all historical block headers when synchronizing data and start synchronization from this checkpoint. Therefore, as long as the light client can obtain some latest and correct checkpoints, the amount of data and time for synchronization will be greatly reduced.
|
||||||
|
|
||||||
|
However, from a security perspective, the most critical step in a synchronization algorithm based on checkpoints is to determine whether the checkpoint used by the light client is correct. Otherwise, all blockchain data synchronized based on this checkpoint may be wrong. For this we provide two different ways to ensure the correctness of the checkpoint used by the light client.
|
||||||
|
|
||||||
|
#### Hardcoded checkpoint
|
||||||
|
|
||||||
|
There are several hardcoded checkpoints in the [source code](https://github.com/ethereum/go-ethereum/blob/master/params/config.go#L38) of the go-ethereum project. These checkpoints are updated by go-ethereum developers when new versions of software are released. Because light client users trust Geth developers to some extent, hardcoded checkpoints in the code can also be considered correct.
|
||||||
|
|
||||||
|
#### Checkpoint oracle
|
||||||
|
|
||||||
|
Hardcoded checkpoints can solve the problem of verifying the correctness of checkpoints (although this is a more centralized solution). But the pain point of this solution is that developers can only update checkpoints when a new version of software is released. In addition, light client users usually do not keep the Geth version they use always up to date. So hardcoded checkpoints used by users are generally stale. Therefore, it still needs to download a large amount of blockchain data during synchronization.
|
||||||
|
|
||||||
|
Checkpoint oracle is a more flexible solution. In simple terms, this is a smart contract that is deployed on the blockchain. The smart contract records several designated trusted signers. Whenever enough trusted signers have issued their signatures for the same checkpoint, it can be considered that the checkpoint has been authenticated by the signers. Checkpoints authenticated by trusted signers can be considered correct.
|
||||||
|
|
||||||
|
So this way, even without updating the software version, as long as the trusted signers regularly update the checkpoint in oracle on time, the light client can always use the latest and verified checkpoint for data synchronization.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
Checkpoint-admin is a command line tool designed for checkpoint oracle. Users can easily deploy contracts and update checkpoints through this tool.
|
||||||
|
|
||||||
|
#### Install
|
||||||
|
|
||||||
|
```shell
|
||||||
|
go get github.com/ethereum/go-ethereum/cmd/checkpoint-admin
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Deploy
|
||||||
|
|
||||||
|
Deploy checkpoint oracle contract. `--signers` indicates the specified trusted signer, and `--threshold` indicates the minimum number of signatures required by trusted signers to update a checkpoint.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
checkpoint-admin deploy --rpc <NODE_RPC_ENDPOINT> --clef <CLEF_ENDPOINT> --signer <SIGNER_TO_SIGN_TX> --signers <TRUSTED_SIGNER_LIST> --threshold 1
|
||||||
|
```
|
||||||
|
|
||||||
|
It is worth noting that checkpoint-admin only supports clef as a signer for transactions and plain text(checkpoint). For more clef usage, please see the clef [tutorial](https://geth.ethereum.org/docs/clef/tutorial) .
|
||||||
|
|
||||||
|
#### Sign
|
||||||
|
|
||||||
|
Checkpoint-admin provides two different modes of signing. You can automatically obtain the current stable checkpoint and sign it interactively, and you can also use the information provided by the command line flags to sign checkpoint offline.
|
||||||
|
|
||||||
|
**Interactive mode**
|
||||||
|
|
||||||
|
```shell
|
||||||
|
checkpoint-admin sign --clef <CLEF_ENDPOINT> --signer <SIGNER_TO_SIGN_CHECKPOINT> --rpc <NODE_RPC_ENDPOINT>
|
||||||
|
```
|
||||||
|
|
||||||
|
*It is worth noting that the connected Geth node can be a fullnode or a light client. If it is fullnode, you must enable the LES protocol. E.G. add `--light.serv 50` to the startup command line flags*.
|
||||||
|
|
||||||
|
**Offline mode**
|
||||||
|
|
||||||
|
```shell
|
||||||
|
checkpoint-admin sign --clef <CLEF_ENDPOINT> --signer <SIGNER_TO_SIGN_CHECKPOINT> --index <CHECKPOINT_INDEX> --hash <CHECKPOINT_HASH> --oracle <CHECKPOINT_ORACLE_ADDRESS>
|
||||||
|
```
|
||||||
|
|
||||||
|
*CHECKPOINT_HASH is obtained based on this [calculation method](https://github.com/ethereum/go-ethereum/blob/master/params/config.go#L251).*
|
||||||
|
|
||||||
|
#### Publish
|
||||||
|
|
||||||
|
Collect enough signatures from different trusted signers for the same checkpoint and submit them to oracle to update the "authenticated" checkpoint in the contract.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
checkpoint-admin publish --clef <CLEF_ENDPOINT> --rpc <NODE_RPC_ENDPOINT> --signer <SIGNER_TO_SIGN_TX> --index <CHECKPOINT_INDEX> --signatures <CHECKPOINT_SIGNATURE_LIST>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Status query
|
||||||
|
|
||||||
|
Check the latest status of checkpoint oracle.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
checkpoint-admin status --rpc <NODE_RPC_ENDPOINT>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enable checkpoint oracle in your private network
|
||||||
|
|
||||||
|
Currently, only the Ethereum mainnet and the default supported test networks (ropsten, rinkeby, goerli) activate this feature. If you want to activate this feature in your private network, you can overwrite the relevant checkpoint oracle settings through the configuration file after deploying the oracle contract.
|
||||||
|
|
||||||
|
* Get your node configuration file `geth dumpconfig OTHER_COMMAND_LINE_OPTIONS > config.toml`
|
||||||
|
* Edit the configuration file and add the following information
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[Eth.CheckpointOracle]
|
||||||
|
Address = CHECKPOINT_ORACLE_ADDRESS
|
||||||
|
Signers = [TRUSTED_SIGNER_1, ..., TRUSTED_SIGNER_N]
|
||||||
|
Threshold = THRESHOLD
|
||||||
|
```
|
||||||
|
|
||||||
|
* Start geth with the modified configuration file
|
||||||
|
|
||||||
|
*In the private network, all fullnodes and light clients need to be started using the same checkpoint oracle settings.*
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 The go-ethereum Authors
|
// Copyright 2019 The go-ethereum Authors
|
||||||
// This file is part of go-ethereum.
|
// This file is part of go-ethereum.
|
||||||
//
|
//
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 The go-ethereum Authors
|
// Copyright 2019 The go-ethereum Authors
|
||||||
// This file is part of go-ethereum.
|
// This file is part of go-ethereum.
|
||||||
//
|
//
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 The go-ethereum Authors
|
// Copyright 2019 The go-ethereum Authors
|
||||||
// This file is part of go-ethereum.
|
// This file is part of go-ethereum.
|
||||||
//
|
//
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -28,19 +28,6 @@ import (
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
commandHelperTemplate = `{{.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}}{{"\t"}}{{.}}
|
|
||||||
{{end}}
|
|
||||||
{{end}}`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Git SHA1 commit hash of the release (set via linker flags)
|
// Git SHA1 commit hash of the release (set via linker flags)
|
||||||
gitCommit = ""
|
gitCommit = ""
|
||||||
|
|
@ -61,7 +48,7 @@ func init() {
|
||||||
oracleFlag,
|
oracleFlag,
|
||||||
nodeURLFlag,
|
nodeURLFlag,
|
||||||
}
|
}
|
||||||
cli.CommandHelpTemplate = commandHelperTemplate
|
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commonly used command line flags.
|
// Commonly used command line flags.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 The go-ethereum Authors
|
// Copyright 2019 The go-ethereum Authors
|
||||||
// This file is part of go-ethereum.
|
// This file is part of go-ethereum.
|
||||||
//
|
//
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ Clef can run as a daemon on the same machine, off a usb-stick like [USB armory](
|
||||||
Check out the
|
Check out the
|
||||||
|
|
||||||
* [CLI tutorial](tutorial.md) for some concrete examples on how Clef works.
|
* [CLI tutorial](tutorial.md) for some concrete examples on how Clef works.
|
||||||
* [Setup docs](docs/setup.md) for infos on how to configure Clef on QubesOS or USB Armory.
|
* [Setup docs](docs/setup.md) for information on how to configure Clef on QubesOS or USB Armory.
|
||||||
* [Data types](datatypes.md) for details on the communication messages between Clef and an external UI.
|
* [Data types](datatypes.md) for details on the communication messages between Clef and an external UI.
|
||||||
|
|
||||||
## Command line flags
|
## Command line flags
|
||||||
|
|
@ -46,6 +46,7 @@ GLOBAL OPTIONS:
|
||||||
--stdio-ui Use STDIN/STDOUT as a channel for an external UI. This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user interface, and can be used when Clef is started by an external process.
|
--stdio-ui Use STDIN/STDOUT as a channel for an external UI. This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user interface, and can be used when Clef is started by an external process.
|
||||||
--stdio-ui-test Mechanism to test interface between Clef and UI. Requires 'stdio-ui'.
|
--stdio-ui-test Mechanism to test interface between Clef and UI. Requires 'stdio-ui'.
|
||||||
--advanced If enabled, issues warnings instead of rejections for suspicious requests. Default off
|
--advanced If enabled, issues warnings instead of rejections for suspicious requests. Default off
|
||||||
|
--suppress-bootwarn If set, does not show the warning during boot
|
||||||
--help, -h show help
|
--help, -h show help
|
||||||
--version, -v print the version
|
--version, -v print the version
|
||||||
```
|
```
|
||||||
|
|
@ -114,9 +115,9 @@ Some snags and todos
|
||||||
|
|
||||||
Clef listens to HTTP requests on `rpcaddr`:`rpcport` (or to IPC on `ipcpath`), with the same JSON-RPC standard as Geth. The messages are expected to be [JSON-RPC 2.0 standard](https://www.jsonrpc.org/specification).
|
Clef listens to HTTP requests on `rpcaddr`:`rpcport` (or to IPC on `ipcpath`), with the same JSON-RPC standard as Geth. The messages are expected to be [JSON-RPC 2.0 standard](https://www.jsonrpc.org/specification).
|
||||||
|
|
||||||
Some of these call can require user interaction. Clients must be aware that responses may be delayed significantly or may never be received if a users decides to ignore the confirmation request.
|
Some of these calls can require user interaction. Clients must be aware that responses may be delayed significantly or may never be received if a user decides to ignore the confirmation request.
|
||||||
|
|
||||||
The External API is **untrusted**: it does not accept credentials over this API, nor does it expect that requests have any authority.
|
The External API is **untrusted**: it does not accept credentials, nor does it expect that requests have any authority.
|
||||||
|
|
||||||
### Internal UI API
|
### Internal UI API
|
||||||
|
|
||||||
|
|
@ -145,13 +146,11 @@ See the [external API changelog](extapi_changelog.md) for information about chan
|
||||||
|
|
||||||
All hex encoded values must be prefixed with `0x`.
|
All hex encoded values must be prefixed with `0x`.
|
||||||
|
|
||||||
## Methods
|
|
||||||
|
|
||||||
### account_new
|
### account_new
|
||||||
|
|
||||||
#### Create new password protected account
|
#### Create new password protected account
|
||||||
|
|
||||||
The signer will generate a new private key, encrypts it according to [web3 keystore spec](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) and stores it in the keystore directory.
|
The signer will generate a new private key, encrypt it according to [web3 keystore spec](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) and store it in the keystore directory.
|
||||||
The client is responsible for creating a backup of the keystore. If the keystore is lost there is no method of retrieving lost accounts.
|
The client is responsible for creating a backup of the keystore. If the keystore is lost there is no method of retrieving lost accounts.
|
||||||
|
|
||||||
#### Arguments
|
#### Arguments
|
||||||
|
|
@ -160,7 +159,6 @@ None
|
||||||
|
|
||||||
#### Result
|
#### Result
|
||||||
- address [string]: account address that is derived from the generated key
|
- address [string]: account address that is derived from the generated key
|
||||||
- url [string]: location of the keyfile
|
|
||||||
|
|
||||||
#### Sample call
|
#### Sample call
|
||||||
```json
|
```json
|
||||||
|
|
@ -172,14 +170,11 @@ None
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Response
|
Response
|
||||||
```
|
```json
|
||||||
{
|
{
|
||||||
"id": 0,
|
"id": 0,
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"result": {
|
"result": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133"
|
||||||
"address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133",
|
|
||||||
"url": "keystore:///my/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -195,8 +190,6 @@ None
|
||||||
#### Result
|
#### Result
|
||||||
- array with account records:
|
- array with account records:
|
||||||
- account.address [string]: account address that is derived from the generated key
|
- account.address [string]: account address that is derived from the generated key
|
||||||
- account.type [string]: type of the
|
|
||||||
- account.url [string]: location of the account
|
|
||||||
|
|
||||||
#### Sample call
|
#### Sample call
|
||||||
```json
|
```json
|
||||||
|
|
@ -207,21 +200,13 @@ None
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Response
|
Response
|
||||||
```
|
```json
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"result": [
|
"result": [
|
||||||
{
|
"0xafb2f771f58513609765698f65d3f2f0224a956f",
|
||||||
"address": "0xafb2f771f58513609765698f65d3f2f0224a956f",
|
"0xbea9183f8f4f03d427f6bcea17388bdff1cab133"
|
||||||
"type": "account",
|
|
||||||
"url": "keystore:///tmp/keystore/UTC--2017-08-24T07-26-47.162109726Z--afb2f771f58513609765698f65d3f2f0224a956f"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133",
|
|
||||||
"type": "account",
|
|
||||||
"url": "keystore:///tmp/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -229,10 +214,10 @@ Response
|
||||||
### account_signTransaction
|
### account_signTransaction
|
||||||
|
|
||||||
#### Sign transactions
|
#### Sign transactions
|
||||||
Signs a transactions and responds with the signed transaction in RLP encoded form.
|
Signs a transaction and responds with the signed transaction in RLP-encoded and JSON forms.
|
||||||
|
|
||||||
#### Arguments
|
#### Arguments
|
||||||
2. transaction object:
|
1. transaction object:
|
||||||
- `from` [address]: account to send the transaction from
|
- `from` [address]: account to send the transaction from
|
||||||
- `to` [address]: receiver account. If omitted or `0x`, will cause contract creation.
|
- `to` [address]: receiver account. If omitted or `0x`, will cause contract creation.
|
||||||
- `gas` [number]: maximum amount of gas to burn
|
- `gas` [number]: maximum amount of gas to burn
|
||||||
|
|
@ -240,12 +225,13 @@ Response
|
||||||
- `value` [number:optional]: amount of Wei to send with the transaction
|
- `value` [number:optional]: amount of Wei to send with the transaction
|
||||||
- `data` [data:optional]: input data
|
- `data` [data:optional]: input data
|
||||||
- `nonce` [number]: account nonce
|
- `nonce` [number]: account nonce
|
||||||
3. method signature [string:optional]
|
1. method signature [string:optional]
|
||||||
- The method signature, if present, is to aid decoding the calldata. Should consist of `methodname(paramtype,...)`, e.g. `transfer(uint256,address)`. The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected.
|
- The method signature, if present, is to aid decoding the calldata. Should consist of `methodname(paramtype,...)`, e.g. `transfer(uint256,address)`. The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected.
|
||||||
|
|
||||||
|
|
||||||
#### Result
|
#### Result
|
||||||
- signed transaction in RLP encoded form [data]
|
- raw [data]: signed transaction in RLP encoded form
|
||||||
|
- tx [json]: signed transaction in JSON form
|
||||||
|
|
||||||
#### Sample call
|
#### Sample call
|
||||||
```json
|
```json
|
||||||
|
|
@ -270,11 +256,22 @@ Response
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": 2,
|
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"error": {
|
"id": 2,
|
||||||
"code": -32000,
|
"result": {
|
||||||
"message": "Request denied"
|
"raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
|
||||||
|
"tx": {
|
||||||
|
"nonce": "0x0",
|
||||||
|
"gasPrice": "0x1234",
|
||||||
|
"gas": "0x55555",
|
||||||
|
"to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
|
||||||
|
"value": "0x1234",
|
||||||
|
"input": "0xabcd",
|
||||||
|
"v": "0x26",
|
||||||
|
"r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",
|
||||||
|
"s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
|
||||||
|
"hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -326,7 +323,7 @@ Response
|
||||||
|
|
||||||
Bash example:
|
Bash example:
|
||||||
```bash
|
```bash
|
||||||
#curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
|
> curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
|
||||||
|
|
||||||
{"jsonrpc":"2.0","id":67,"result":{"raw":"0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","tx":{"nonce":"0x0","gasPrice":"0x1","gas":"0x333","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0","value":"0x0","input":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012","v":"0x26","r":"0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e","s":"0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","hash":"0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"}}}
|
{"jsonrpc":"2.0","id":67,"result":{"raw":"0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","tx":{"nonce":"0x0","gasPrice":"0x1","gas":"0x333","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0","value":"0x0","input":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012","v":"0x26","r":"0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e","s":"0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","hash":"0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"}}}
|
||||||
```
|
```
|
||||||
|
|
@ -373,7 +370,7 @@ Response
|
||||||
### account_signTypedData
|
### account_signTypedData
|
||||||
|
|
||||||
#### Sign data
|
#### Sign data
|
||||||
Signs a chunk of structured data conformant to [EIP712]([EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md)) and returns the calculated signature.
|
Signs a chunk of structured data conformant to [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) and returns the calculated signature.
|
||||||
|
|
||||||
#### Arguments
|
#### Arguments
|
||||||
- account [address]: account to sign with
|
- account [address]: account to sign with
|
||||||
|
|
@ -469,7 +466,7 @@ Response
|
||||||
|
|
||||||
### account_ecRecover
|
### account_ecRecover
|
||||||
|
|
||||||
#### Sign data
|
#### Recover the signing address
|
||||||
|
|
||||||
Derive the address from the account that was used to sign data with content type `text/plain` and the signature.
|
Derive the address from the account that was used to sign data with content type `text/plain` and the signature.
|
||||||
|
|
||||||
|
|
@ -487,7 +484,6 @@ Derive the address from the account that was used to sign data with content type
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "account_ecRecover",
|
"method": "account_ecRecover",
|
||||||
"params": [
|
"params": [
|
||||||
"data/plain",
|
|
||||||
"0xaabbccdd",
|
"0xaabbccdd",
|
||||||
"0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
|
"0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
|
||||||
]
|
]
|
||||||
|
|
@ -503,117 +499,36 @@ Response
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### account_import
|
### account_version
|
||||||
|
|
||||||
#### Import account
|
#### Get external API version
|
||||||
Import a private key into the keystore. The imported key is expected to be encrypted according to the web3 keystore
|
|
||||||
format.
|
Get the version of the external API used by Clef.
|
||||||
|
|
||||||
#### Arguments
|
#### Arguments
|
||||||
- account [object]: key in [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) (retrieved with account_export)
|
|
||||||
|
None
|
||||||
|
|
||||||
#### Result
|
#### Result
|
||||||
- imported key [object]:
|
|
||||||
- key.address [address]: address of the imported key
|
* external API version [string]
|
||||||
- key.type [string]: type of the account
|
|
||||||
- key.url [string]: key URL
|
|
||||||
|
|
||||||
#### Sample call
|
#### Sample call
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": 6,
|
"id": 0,
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "account_import",
|
"method": "account_version",
|
||||||
"params": [
|
"params": []
|
||||||
{
|
|
||||||
"address": "c7412fc59930fd90099c917a50e5f11d0934b2f5",
|
|
||||||
"crypto": {
|
|
||||||
"cipher": "aes-128-ctr",
|
|
||||||
"cipherparams": {
|
|
||||||
"iv": "401c39a7c7af0388491c3d3ecb39f532"
|
|
||||||
},
|
|
||||||
"ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204",
|
|
||||||
"kdf": "scrypt",
|
|
||||||
"kdfparams": {
|
|
||||||
"dklen": 32,
|
|
||||||
"n": 262144,
|
|
||||||
"p": 1,
|
|
||||||
"r": 8,
|
|
||||||
"salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a"
|
|
||||||
},
|
|
||||||
"mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806"
|
|
||||||
},
|
|
||||||
"id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9",
|
|
||||||
"version": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Response
|
Response
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": 6,
|
"id": 0,
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"result": {
|
"result": "6.0.0"
|
||||||
"address": "0xc7412fc59930fd90099c917a50e5f11d0934b2f5",
|
|
||||||
"type": "account",
|
|
||||||
"url": "keystore:///tmp/keystore/UTC--2017-08-24T11-00-42.032024108Z--c7412fc59930fd90099c917a50e5f11d0934b2f5"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### account_export
|
|
||||||
|
|
||||||
#### Export account from keystore
|
|
||||||
Export a private key from the keystore. The exported private key is encrypted with the original passphrase. When the
|
|
||||||
key is imported later this passphrase is required.
|
|
||||||
|
|
||||||
#### Arguments
|
|
||||||
- account [address]: export private key that is associated with this account
|
|
||||||
|
|
||||||
#### Result
|
|
||||||
- exported key, see [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) for
|
|
||||||
more information
|
|
||||||
|
|
||||||
#### Sample call
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"method": "account_export",
|
|
||||||
"params": [
|
|
||||||
"0xc7412fc59930fd90099c917a50e5f11d0934b2f5"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Response
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"result": {
|
|
||||||
"address": "c7412fc59930fd90099c917a50e5f11d0934b2f5",
|
|
||||||
"crypto": {
|
|
||||||
"cipher": "aes-128-ctr",
|
|
||||||
"cipherparams": {
|
|
||||||
"iv": "401c39a7c7af0388491c3d3ecb39f532"
|
|
||||||
},
|
|
||||||
"ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204",
|
|
||||||
"kdf": "scrypt",
|
|
||||||
"kdfparams": {
|
|
||||||
"dklen": 32,
|
|
||||||
"n": 262144,
|
|
||||||
"p": 1,
|
|
||||||
"r": 8,
|
|
||||||
"salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a"
|
|
||||||
},
|
|
||||||
"mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806"
|
|
||||||
},
|
|
||||||
"id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9",
|
|
||||||
"version": 3
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -625,7 +540,7 @@ By starting the signer with the switch `--stdio-ui-test`, the signer will invoke
|
||||||
denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented.
|
denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented.
|
||||||
See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'.
|
See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'.
|
||||||
|
|
||||||
All methods in this API uses object-based parameters, so that there can be no mixups of parameters: each piece of data is accessed by key.
|
All methods in this API use object-based parameters, so that there can be no mixup of parameters: each piece of data is accessed by key.
|
||||||
|
|
||||||
See the [ui API changelog](intapi_changelog.md) for information about changes to this API.
|
See the [ui API changelog](intapi_changelog.md) for information about changes to this API.
|
||||||
|
|
||||||
|
|
@ -784,12 +699,10 @@ Invoked when a request for account listing has been made.
|
||||||
{
|
{
|
||||||
"accounts": [
|
"accounts": [
|
||||||
{
|
{
|
||||||
"type": "Account",
|
|
||||||
"url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-20T14-44-54.089682944Z--123409812340981234098123409812deadbeef42",
|
"url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-20T14-44-54.089682944Z--123409812340981234098123409812deadbeef42",
|
||||||
"address": "0x123409812340981234098123409812deadbeef42"
|
"address": "0x123409812340981234098123409812deadbeef42"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Account",
|
|
||||||
"url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-23T21-59-03.199240693Z--cafebabedeadbeef34098123409812deadbeef42",
|
"url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-23T21-59-03.199240693Z--cafebabedeadbeef34098123409812deadbeef42",
|
||||||
"address": "0xcafebabedeadbeef34098123409812deadbeef42"
|
"address": "0xcafebabedeadbeef34098123409812deadbeef42"
|
||||||
}
|
}
|
||||||
|
|
@ -819,7 +732,13 @@ Invoked when a request for account listing has been made.
|
||||||
{
|
{
|
||||||
"address": "0x123409812340981234098123409812deadbeef42",
|
"address": "0x123409812340981234098123409812deadbeef42",
|
||||||
"raw_data": "0x01020304",
|
"raw_data": "0x01020304",
|
||||||
"message": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004",
|
"messages": [
|
||||||
|
{
|
||||||
|
"name": "message",
|
||||||
|
"value": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004",
|
||||||
|
"type": "text/plain"
|
||||||
|
}
|
||||||
|
],
|
||||||
"hash": "0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310",
|
"hash": "0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310",
|
||||||
"meta": {
|
"meta": {
|
||||||
"remote": "signer binary",
|
"remote": "signer binary",
|
||||||
|
|
@ -829,12 +748,34 @@ Invoked when a request for account listing has been made.
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ApproveNewAccount / `ui_approveNewAccount`
|
||||||
|
|
||||||
|
Invoked when a request for creating a new account has been made.
|
||||||
|
|
||||||
|
#### Sample call
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 4,
|
||||||
|
"method": "ui_approveNewAccount",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"remote": "signer binary",
|
||||||
|
"local": "main",
|
||||||
|
"scheme": "in-proc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### ShowInfo / `ui_showInfo`
|
### ShowInfo / `ui_showInfo`
|
||||||
|
|
||||||
The UI should show the info to the user. Does not expect response.
|
The UI should show the info (a single message) to the user. Does not expect response.
|
||||||
|
|
||||||
#### Sample call
|
#### Sample call
|
||||||
|
|
||||||
|
|
@ -844,9 +785,7 @@ The UI should show the info to the user. Does not expect response.
|
||||||
"id": 9,
|
"id": 9,
|
||||||
"method": "ui_showInfo",
|
"method": "ui_showInfo",
|
||||||
"params": [
|
"params": [
|
||||||
{
|
"Tests completed"
|
||||||
"text": "Tests completed"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -854,18 +793,16 @@ The UI should show the info to the user. Does not expect response.
|
||||||
|
|
||||||
### ShowError / `ui_showError`
|
### ShowError / `ui_showError`
|
||||||
|
|
||||||
The UI should show the info to the user. Does not expect response.
|
The UI should show the error (a single message) to the user. Does not expect response.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"method": "ShowError",
|
"method": "ui_showError",
|
||||||
"params": [
|
"params": [
|
||||||
{
|
"Something bad happened!"
|
||||||
"text": "Testing 'ShowError'"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -879,9 +816,36 @@ When implementing rate-limited rules, this callback should be used.
|
||||||
|
|
||||||
TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`.
|
TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`.
|
||||||
|
|
||||||
|
Example call:
|
||||||
|
```json
|
||||||
|
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "ui_onApprovedTx",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
|
||||||
|
"tx": {
|
||||||
|
"nonce": "0x0",
|
||||||
|
"gasPrice": "0x1",
|
||||||
|
"gas": "0x333",
|
||||||
|
"to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
|
||||||
|
"value": "0x0",
|
||||||
|
"input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012",
|
||||||
|
"v": "0x26",
|
||||||
|
"r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",
|
||||||
|
"s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
|
||||||
|
"hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### OnSignerStartup / `ui_onSignerStartup`
|
### OnSignerStartup / `ui_onSignerStartup`
|
||||||
|
|
||||||
This method provide the UI with information about what API version the signer uses (both internal and external) aswell as build-info and external API,
|
This method provides the UI with information about what API version the signer uses (both internal and external) as well as build-info and external API,
|
||||||
in k/v-form.
|
in k/v-form.
|
||||||
|
|
||||||
Example call:
|
Example call:
|
||||||
|
|
@ -905,6 +869,27 @@ Example call:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### OnInputRequired / `ui_onInputRequired`
|
||||||
|
|
||||||
|
Invoked when Clef requires user input (e.g. a password).
|
||||||
|
|
||||||
|
Example call:
|
||||||
|
```json
|
||||||
|
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "ui_onInputRequired",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"title": "Account password",
|
||||||
|
"prompt": "Please enter the password for account 0x694267f14675d7e1b9494fd8d72fefe1755710fa",
|
||||||
|
"isPassword": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Rules for UI apis
|
### Rules for UI apis
|
||||||
|
|
||||||
|
|
@ -914,7 +899,7 @@ A UI should conform to the following rules.
|
||||||
* For example, not load icons, stylesheets from the internet
|
* For example, not load icons, stylesheets from the internet
|
||||||
* Not load files from the filesystem, unless they reside in the same local directory (e.g. config files)
|
* Not load files from the filesystem, unless they reside in the same local directory (e.g. config files)
|
||||||
* A Graphical UI MUST show the blocky-identicon for ethereum addresses.
|
* A Graphical UI MUST show the blocky-identicon for ethereum addresses.
|
||||||
* A UI MUST warn display approproate warning if the destination-account is formatted with invalid checksum.
|
* A UI MUST warn display appropriate warning if the destination-account is formatted with invalid checksum.
|
||||||
* A UI MUST NOT open any ports or services
|
* A UI MUST NOT open any ports or services
|
||||||
* The signer opens the public port
|
* The signer opens the public port
|
||||||
* A UI SHOULD verify the permissions on the signer binary, and refuse to execute or warn if permissions allow non-user write.
|
* A UI SHOULD verify the permissions on the signer binary, and refuse to execute or warn if permissions allow non-user write.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
These data types are defined in the channel between clef and the UI
|
These data types are defined in the channel between clef and the UI
|
||||||
### SignDataRequest
|
### SignDataRequest
|
||||||
|
|
||||||
SignDataRequest contains information about a pending request to sign some data. The data to be signed can be of various types, defined by content-type. Clef has done most of the work in canonicalizing and making sense of the data, and it's up to the UI to presentthe user with the contents of the `message`
|
SignDataRequest contains information about a pending request to sign some data. The data to be signed can be of various types, defined by content-type. Clef has done most of the work in canonicalizing and making sense of the data, and it's up to the UI to present the user with the contents of the `message`
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ There are two ways that this can be achieved: integrated via Qubes or integrated
|
||||||
|
|
||||||
#### 1. Qubes Integrated
|
#### 1. Qubes Integrated
|
||||||
|
|
||||||
Qubes provdes a facility for inter-qubes communication via `qrexec`. A qube can request to make a cross-qube RPC request
|
Qubes provides a facility for inter-qubes communication via `qrexec`. A qube can request to make a cross-qube RPC request
|
||||||
to another qube. The OS then asks the user if the call is permitted.
|
to another qube. The OS then asks the user if the call is permitted.
|
||||||
|
|
||||||

|

|
||||||
|
|
@ -48,7 +48,7 @@ This is how [Split GPG](https://www.qubes-os.org/doc/split-gpg/) is implemented.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
On the `target` qubes, we need to define the rpc service.
|
On the `target` qubes, we need to define the RPC service.
|
||||||
|
|
||||||
[qubes.Clefsign](qubes/qubes.Clefsign):
|
[qubes.Clefsign](qubes/qubes.Clefsign):
|
||||||
|
|
||||||
|
|
@ -135,11 +135,11 @@ $ cat newaccnt.json
|
||||||
$ cat newaccnt.json| qrexec-client-vm debian-work qubes.Clefsign
|
$ cat newaccnt.json| qrexec-client-vm debian-work qubes.Clefsign
|
||||||
```
|
```
|
||||||
|
|
||||||
This should pop up first a dialog to allow the IPC call:
|
A dialog should pop up first to allow the IPC call:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Followed by a GTK-dialog to approve the operation
|
Followed by a GTK-dialog to approve the operation:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
@ -169,7 +169,7 @@ However, it comes with a couple of drawbacks:
|
||||||
- The `Origin` header must be forwarded
|
- The `Origin` header must be forwarded
|
||||||
- Information about the remote ip must be added as a `X-Forwarded-For`. However, Clef cannot always trust an `XFF` header,
|
- Information about the remote ip must be added as a `X-Forwarded-For`. However, Clef cannot always trust an `XFF` header,
|
||||||
since malicious clients may lie about `XFF` in order to fool the http server into believing it comes from another address.
|
since malicious clients may lie about `XFF` in order to fool the http server into believing it comes from another address.
|
||||||
- Even with a policy in place to allow rpc-calls between `caller` and `target`, there will be several popups:
|
- Even with a policy in place to allow RPC calls between `caller` and `target`, there will be several popups:
|
||||||
- One qubes-specific where the user specifies the `target` vm
|
- One qubes-specific where the user specifies the `target` vm
|
||||||
- One clef-specific to approve the transaction
|
- One clef-specific to approve the transaction
|
||||||
|
|
||||||
|
|
@ -177,7 +177,7 @@ However, it comes with a couple of drawbacks:
|
||||||
#### 2. Network integrated
|
#### 2. Network integrated
|
||||||
|
|
||||||
The second way to set up Clef on a qubes system is to allow networking, and have Clef listen to a port which is accessible
|
The second way to set up Clef on a qubes system is to allow networking, and have Clef listen to a port which is accessible
|
||||||
form other qubes.
|
from other qubes.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
@ -186,13 +186,13 @@ form other qubes.
|
||||||
|
|
||||||
## USBArmory
|
## USBArmory
|
||||||
|
|
||||||
The [USB armory](https://inversepath.com/usbarmory) is an open source hardware design with an 800 Mhz ARM processor. It is a pocket-size
|
The [USB armory](https://inversepath.com/usbarmory) is an open source hardware design with an 800 MHz ARM processor. It is a pocket-size
|
||||||
computer. When inserted into a laptop, it identifies itself as a USB network interface, basically adding another network
|
computer. When inserted into a laptop, it identifies itself as a USB network interface, basically adding another network
|
||||||
to your computer. Over this new network interface, you can SSH into the device.
|
to your computer. Over this new network interface, you can SSH into the device.
|
||||||
|
|
||||||
Running Clef off a USB armory means that you can use the armory as a very versatile offline computer, which only
|
Running Clef off a USB armory means that you can use the armory as a very versatile offline computer, which only
|
||||||
ever connects to a local network between your computer and the device itself.
|
ever connects to a local network between your computer and the device itself.
|
||||||
|
|
||||||
Needless to say, the while this model should be fairly secure against remote attacks, an attacker with physical access
|
Needless to say, while this model should be fairly secure against remote attacks, an attacker with physical access
|
||||||
to the USB Armory would trivially be able to extract the contents of the device filesystem.
|
to the USB Armory would trivially be able to extract the contents of the device filesystem.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,17 @@ TL;DR: Given a version number MAJOR.MINOR.PATCH, increment the:
|
||||||
|
|
||||||
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
|
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
|
||||||
|
|
||||||
|
### 7.0.1
|
||||||
|
|
||||||
|
Added `clef_New` to the internal API callable from a UI.
|
||||||
|
|
||||||
|
> `New` creates a new password protected Account. The private key is protected with
|
||||||
|
> the given password. Users are responsible to backup the private key that is stored
|
||||||
|
> in the keystore location that was specified when this API was created.
|
||||||
|
> This method is the same as New on the external API, the difference being that
|
||||||
|
> this implementation does not ask for confirmation, since it's initiated by
|
||||||
|
> the user
|
||||||
|
|
||||||
### 7.0.0
|
### 7.0.0
|
||||||
|
|
||||||
- The `message` field was renamed to `messages` in all data signing request methods to better reflect that it's a list, not a value.
|
- The `message` field was renamed to `messages` in all data signing request methods to better reflect that it's a list, not a value.
|
||||||
|
|
@ -150,7 +161,7 @@ UserInputResponse struct {
|
||||||
#### 1.2.0
|
#### 1.2.0
|
||||||
|
|
||||||
* Add `OnStartup` method, to provide the UI with information about what API version
|
* Add `OnStartup` method, to provide the UI with information about what API version
|
||||||
the signer uses (both internal and external) aswell as build-info and external api.
|
the signer uses (both internal and external) as well as build-info and external api.
|
||||||
|
|
||||||
Example call:
|
Example call:
|
||||||
```json
|
```json
|
||||||
|
|
|
||||||
216
cmd/clef/main.go
216
cmd/clef/main.go
|
|
@ -40,7 +40,6 @@ import (
|
||||||
"github.com/maticnetwork/bor/cmd/utils"
|
"github.com/maticnetwork/bor/cmd/utils"
|
||||||
"github.com/maticnetwork/bor/common"
|
"github.com/maticnetwork/bor/common"
|
||||||
"github.com/maticnetwork/bor/common/hexutil"
|
"github.com/maticnetwork/bor/common/hexutil"
|
||||||
"github.com/maticnetwork/bor/console"
|
|
||||||
"github.com/maticnetwork/bor/core/types"
|
"github.com/maticnetwork/bor/core/types"
|
||||||
"github.com/maticnetwork/bor/crypto"
|
"github.com/maticnetwork/bor/crypto"
|
||||||
"github.com/maticnetwork/bor/internal/ethapi"
|
"github.com/maticnetwork/bor/internal/ethapi"
|
||||||
|
|
@ -53,6 +52,8 @@ import (
|
||||||
"github.com/maticnetwork/bor/signer/fourbyte"
|
"github.com/maticnetwork/bor/signer/fourbyte"
|
||||||
"github.com/maticnetwork/bor/signer/rules"
|
"github.com/maticnetwork/bor/signer/rules"
|
||||||
"github.com/maticnetwork/bor/signer/storage"
|
"github.com/maticnetwork/bor/signer/storage"
|
||||||
|
colorable "github.com/mattn/go-colorable"
|
||||||
|
"github.com/mattn/go-isatty"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -80,6 +81,10 @@ var (
|
||||||
Name: "advanced",
|
Name: "advanced",
|
||||||
Usage: "If enabled, issues warnings instead of rejections for suspicious requests. Default off",
|
Usage: "If enabled, issues warnings instead of rejections for suspicious requests. Default off",
|
||||||
}
|
}
|
||||||
|
acceptFlag = cli.BoolFlag{
|
||||||
|
Name: "suppress-bootwarn",
|
||||||
|
Usage: "If set, does not show the warning during boot",
|
||||||
|
}
|
||||||
keystoreFlag = cli.StringFlag{
|
keystoreFlag = cli.StringFlag{
|
||||||
Name: "keystore",
|
Name: "keystore",
|
||||||
Value: filepath.Join(node.DefaultDataDir(), "keystore"),
|
Value: filepath.Join(node.DefaultDataDir(), "keystore"),
|
||||||
|
|
@ -185,6 +190,22 @@ The setpw command stores a password for a given address (keyfile).
|
||||||
Description: `
|
Description: `
|
||||||
The delpw command removes a password for a given address (keyfile).
|
The delpw command removes a password for a given address (keyfile).
|
||||||
`}
|
`}
|
||||||
|
newAccountCommand = cli.Command{
|
||||||
|
Action: utils.MigrateFlags(newAccount),
|
||||||
|
Name: "newaccount",
|
||||||
|
Usage: "Create a new account",
|
||||||
|
ArgsUsage: "",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
logLevelFlag,
|
||||||
|
keystoreFlag,
|
||||||
|
utils.LightKDFFlag,
|
||||||
|
acceptFlag,
|
||||||
|
},
|
||||||
|
Description: `
|
||||||
|
The newaccount command creates a new keystore-backed account. It is a convenience-method
|
||||||
|
which can be used in lieu of an external UI.`,
|
||||||
|
}
|
||||||
|
|
||||||
gendocCommand = cli.Command{
|
gendocCommand = cli.Command{
|
||||||
Action: GenDoc,
|
Action: GenDoc,
|
||||||
Name: "gendoc",
|
Name: "gendoc",
|
||||||
|
|
@ -205,11 +226,11 @@ func init() {
|
||||||
utils.LightKDFFlag,
|
utils.LightKDFFlag,
|
||||||
utils.NoUSBFlag,
|
utils.NoUSBFlag,
|
||||||
utils.SmartCardDaemonPathFlag,
|
utils.SmartCardDaemonPathFlag,
|
||||||
utils.RPCListenAddrFlag,
|
utils.HTTPListenAddrFlag,
|
||||||
utils.RPCVirtualHostsFlag,
|
utils.HTTPVirtualHostsFlag,
|
||||||
utils.IPCDisabledFlag,
|
utils.IPCDisabledFlag,
|
||||||
utils.IPCPathFlag,
|
utils.IPCPathFlag,
|
||||||
utils.RPCEnabledFlag,
|
utils.HTTPEnabledFlag,
|
||||||
rpcPortFlag,
|
rpcPortFlag,
|
||||||
signerSecretFlag,
|
signerSecretFlag,
|
||||||
customDBFlag,
|
customDBFlag,
|
||||||
|
|
@ -218,9 +239,16 @@ func init() {
|
||||||
stdiouiFlag,
|
stdiouiFlag,
|
||||||
testFlag,
|
testFlag,
|
||||||
advancedMode,
|
advancedMode,
|
||||||
|
acceptFlag,
|
||||||
}
|
}
|
||||||
app.Action = signer
|
app.Action = signer
|
||||||
app.Commands = []cli.Command{initCommand, attestCommand, setCredentialCommand, delCredentialCommand, gendocCommand}
|
app.Commands = []cli.Command{initCommand,
|
||||||
|
attestCommand,
|
||||||
|
setCredentialCommand,
|
||||||
|
delCredentialCommand,
|
||||||
|
newAccountCommand,
|
||||||
|
gendocCommand}
|
||||||
|
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -260,7 +288,7 @@ func initializeSecrets(c *cli.Context) error {
|
||||||
text := "The master seed of clef will be locked with a password.\nPlease specify a password. Do not forget this password!"
|
text := "The master seed of clef will be locked with a password.\nPlease specify a password. Do not forget this password!"
|
||||||
var password string
|
var password string
|
||||||
for {
|
for {
|
||||||
password = getPassPhrase(text, true)
|
password = utils.GetPassPhrase(text, true)
|
||||||
if err := core.ValidatePasswordFormat(password); err != nil {
|
if err := core.ValidatePasswordFormat(password); err != nil {
|
||||||
fmt.Printf("invalid password: %v\n", err)
|
fmt.Printf("invalid password: %v\n", err)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -333,7 +361,7 @@ func setCredential(ctx *cli.Context) error {
|
||||||
utils.Fatalf("Invalid address specified: %s", addr)
|
utils.Fatalf("Invalid address specified: %s", addr)
|
||||||
}
|
}
|
||||||
address := common.HexToAddress(addr)
|
address := common.HexToAddress(addr)
|
||||||
password := getPassPhrase("Please enter a passphrase to store for this address:", true)
|
password := utils.GetPassPhrase("Please enter a password to store for this address:", true)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
stretchedKey, err := readMasterKey(ctx, nil)
|
stretchedKey, err := readMasterKey(ctx, nil)
|
||||||
|
|
@ -379,23 +407,77 @@ func removeCredential(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newAccount(c *cli.Context) error {
|
||||||
|
if err := initialize(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// The newaccount is meant for users using the CLI, since 'real' external
|
||||||
|
// UIs can use the UI-api instead. So we'll just use the native CLI UI here.
|
||||||
|
var (
|
||||||
|
ui = core.NewCommandlineUI()
|
||||||
|
pwStorage storage.Storage = &storage.NoStorage{}
|
||||||
|
ksLoc = c.GlobalString(keystoreFlag.Name)
|
||||||
|
lightKdf = c.GlobalBool(utils.LightKDFFlag.Name)
|
||||||
|
)
|
||||||
|
log.Info("Starting clef", "keystore", ksLoc, "light-kdf", lightKdf)
|
||||||
|
am := core.StartClefAccountManager(ksLoc, true, lightKdf, "")
|
||||||
|
// This gives is us access to the external API
|
||||||
|
apiImpl := core.NewSignerAPI(am, 0, true, ui, nil, false, pwStorage)
|
||||||
|
// This gives us access to the internal API
|
||||||
|
internalApi := core.NewUIServerAPI(apiImpl)
|
||||||
|
addr, err := internalApi.New(context.Background())
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("Generated account %v\n", addr.String())
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func initialize(c *cli.Context) error {
|
func initialize(c *cli.Context) error {
|
||||||
// Set up the logger to print everything
|
// Set up the logger to print everything
|
||||||
logOutput := os.Stdout
|
logOutput := os.Stdout
|
||||||
if c.GlobalBool(stdiouiFlag.Name) {
|
if c.GlobalBool(stdiouiFlag.Name) {
|
||||||
logOutput = os.Stderr
|
logOutput = os.Stderr
|
||||||
// If using the stdioui, we can't do the 'confirm'-flow
|
// If using the stdioui, we can't do the 'confirm'-flow
|
||||||
fmt.Fprintf(logOutput, legalWarning)
|
if !c.GlobalBool(acceptFlag.Name) {
|
||||||
} else {
|
fmt.Fprint(logOutput, legalWarning)
|
||||||
|
}
|
||||||
|
} else if !c.GlobalBool(acceptFlag.Name) {
|
||||||
if !confirm(legalWarning) {
|
if !confirm(legalWarning) {
|
||||||
return fmt.Errorf("aborted by user")
|
return fmt.Errorf("aborted by user")
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(logOutput, log.TerminalFormat(true))))
|
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
|
||||||
|
output := io.Writer(logOutput)
|
||||||
|
if usecolor {
|
||||||
|
output = colorable.NewColorable(logOutput)
|
||||||
|
}
|
||||||
|
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(output, log.TerminalFormat(usecolor))))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ipcEndpoint resolves an IPC endpoint based on a configured value, taking into
|
||||||
|
// account the set data folders as well as the designated platform we're currently
|
||||||
|
// running on.
|
||||||
|
func ipcEndpoint(ipcPath, datadir string) string {
|
||||||
|
// On windows we can only use plain top-level pipes
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
if strings.HasPrefix(ipcPath, `\\.\pipe\`) {
|
||||||
|
return ipcPath
|
||||||
|
}
|
||||||
|
return `\\.\pipe\` + ipcPath
|
||||||
|
}
|
||||||
|
// Resolve names into the data directory full paths otherwise
|
||||||
|
if filepath.Base(ipcPath) == ipcPath {
|
||||||
|
if datadir == "" {
|
||||||
|
return filepath.Join(os.TempDir(), ipcPath)
|
||||||
|
}
|
||||||
|
return filepath.Join(datadir, ipcPath)
|
||||||
|
}
|
||||||
|
return ipcPath
|
||||||
|
}
|
||||||
|
|
||||||
func signer(c *cli.Context) error {
|
func signer(c *cli.Context) error {
|
||||||
// If we have some unrecognized command, bail out
|
// If we have some unrecognized command, bail out
|
||||||
if args := c.Args(); len(args) > 0 {
|
if args := c.Args(); len(args) > 0 {
|
||||||
|
|
@ -427,7 +509,6 @@ func signer(c *cli.Context) error {
|
||||||
api core.ExternalAPI
|
api core.ExternalAPI
|
||||||
pwStorage storage.Storage = &storage.NoStorage{}
|
pwStorage storage.Storage = &storage.NoStorage{}
|
||||||
)
|
)
|
||||||
|
|
||||||
configDir := c.GlobalString(configdirFlag.Name)
|
configDir := c.GlobalString(configdirFlag.Name)
|
||||||
if stretchedKey, err := readMasterKey(c, ui); err != nil {
|
if stretchedKey, err := readMasterKey(c, ui); err != nil {
|
||||||
log.Warn("Failed to open master, rules disabled", "err", err)
|
log.Warn("Failed to open master, rules disabled", "err", err)
|
||||||
|
|
@ -505,31 +586,35 @@ func signer(c *cli.Context) error {
|
||||||
Service: api,
|
Service: api,
|
||||||
Version: "1.0"},
|
Version: "1.0"},
|
||||||
}
|
}
|
||||||
if c.GlobalBool(utils.RPCEnabledFlag.Name) {
|
if c.GlobalBool(utils.HTTPEnabledFlag.Name) {
|
||||||
vhosts := splitAndTrim(c.GlobalString(utils.RPCVirtualHostsFlag.Name))
|
vhosts := splitAndTrim(c.GlobalString(utils.HTTPVirtualHostsFlag.Name))
|
||||||
cors := splitAndTrim(c.GlobalString(utils.RPCCORSDomainFlag.Name))
|
cors := splitAndTrim(c.GlobalString(utils.HTTPCORSDomainFlag.Name))
|
||||||
|
|
||||||
|
srv := rpc.NewServer()
|
||||||
|
err := node.RegisterApisFromWhitelist(rpcAPI, []string{"account"}, srv, false)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Could not register API: %w", err)
|
||||||
|
}
|
||||||
|
handler := node.NewHTTPHandlerStack(srv, cors, vhosts)
|
||||||
|
|
||||||
// start http server
|
// start http server
|
||||||
httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
|
httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.HTTPListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
|
||||||
listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"account"}, cors, vhosts, rpc.DefaultHTTPTimeouts)
|
httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Could not start RPC api: %v", err)
|
utils.Fatalf("Could not start RPC api: %v", err)
|
||||||
}
|
}
|
||||||
extapiURL = fmt.Sprintf("http://%s", httpEndpoint)
|
extapiURL = fmt.Sprintf("http://%v/", addr)
|
||||||
log.Info("HTTP endpoint opened", "url", extapiURL)
|
log.Info("HTTP endpoint opened", "url", extapiURL)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
listener.Close()
|
// Don't bother imposing a timeout here.
|
||||||
log.Info("HTTP endpoint closed", "url", httpEndpoint)
|
httpServer.Shutdown(context.Background())
|
||||||
|
log.Info("HTTP endpoint closed", "url", extapiURL)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
if !c.GlobalBool(utils.IPCDisabledFlag.Name) {
|
if !c.GlobalBool(utils.IPCDisabledFlag.Name) {
|
||||||
if c.IsSet(utils.IPCPathFlag.Name) {
|
givenPath := c.GlobalString(utils.IPCPathFlag.Name)
|
||||||
ipcapiURL = c.GlobalString(utils.IPCPathFlag.Name)
|
ipcapiURL = ipcEndpoint(filepath.Join(givenPath, "clef.ipc"), configDir)
|
||||||
} else {
|
|
||||||
ipcapiURL = filepath.Join(configDir, "clef.ipc")
|
|
||||||
}
|
|
||||||
|
|
||||||
listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI)
|
listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Could not start IPC api: %v", err)
|
utils.Fatalf("Could not start IPC api: %v", err)
|
||||||
|
|
@ -539,7 +624,6 @@ func signer(c *cli.Context) error {
|
||||||
listener.Close()
|
listener.Close()
|
||||||
log.Info("IPC endpoint closed", "url", ipcapiURL)
|
log.Info("IPC endpoint closed", "url", ipcapiURL)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.GlobalBool(testFlag.Name) {
|
if c.GlobalBool(testFlag.Name) {
|
||||||
|
|
@ -555,7 +639,7 @@ func signer(c *cli.Context) error {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
abortChan := make(chan os.Signal)
|
abortChan := make(chan os.Signal, 1)
|
||||||
signal.Notify(abortChan, os.Interrupt)
|
signal.Notify(abortChan, os.Interrupt)
|
||||||
|
|
||||||
sig := <-abortChan
|
sig := <-abortChan
|
||||||
|
|
@ -635,7 +719,7 @@ func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) {
|
||||||
}
|
}
|
||||||
password = resp.Text
|
password = resp.Text
|
||||||
} else {
|
} else {
|
||||||
password = getPassPhrase("Decrypt master seed of clef", false)
|
password = utils.GetPassPhrase("Decrypt master seed of clef", false)
|
||||||
}
|
}
|
||||||
masterSeed, err := decryptSeed(cipherKey, password)
|
masterSeed, err := decryptSeed(cipherKey, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -670,7 +754,7 @@ func checkFile(filename string) error {
|
||||||
|
|
||||||
// confirm displays a text and asks for user confirmation
|
// confirm displays a text and asks for user confirmation
|
||||||
func confirm(text string) bool {
|
func confirm(text string) bool {
|
||||||
fmt.Printf(text)
|
fmt.Print(text)
|
||||||
fmt.Printf("\nEnter 'ok' to proceed:\n> ")
|
fmt.Printf("\nEnter 'ok' to proceed:\n> ")
|
||||||
|
|
||||||
text, err := bufio.NewReader(os.Stdin).ReadString('\n')
|
text, err := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||||
|
|
@ -736,21 +820,19 @@ func testExternalUI(api *core.SignerAPI) {
|
||||||
api.UI.ShowInfo("Please approve the next request for signing a clique header")
|
api.UI.ShowInfo("Please approve the next request for signing a clique header")
|
||||||
time.Sleep(delay)
|
time.Sleep(delay)
|
||||||
cliqueHeader := types.Header{
|
cliqueHeader := types.Header{
|
||||||
common.HexToHash("0000H45H"),
|
ParentHash: common.HexToHash("0000H45H"),
|
||||||
common.HexToHash("0000H45H"),
|
UncleHash: common.HexToHash("0000H45H"),
|
||||||
common.HexToAddress("0000H45H"),
|
Coinbase: common.HexToAddress("0000H45H"),
|
||||||
common.HexToHash("0000H00H"),
|
Root: common.HexToHash("0000H00H"),
|
||||||
common.HexToHash("0000H45H"),
|
TxHash: common.HexToHash("0000H45H"),
|
||||||
common.HexToHash("0000H45H"),
|
ReceiptHash: common.HexToHash("0000H45H"),
|
||||||
types.Bloom{},
|
Difficulty: big.NewInt(1337),
|
||||||
big.NewInt(1337),
|
Number: big.NewInt(1337),
|
||||||
big.NewInt(1337),
|
GasLimit: 1338,
|
||||||
1338,
|
GasUsed: 1338,
|
||||||
1338,
|
Time: 1338,
|
||||||
1338,
|
Extra: []byte("Extra data Extra data Extra data Extra data Extra data Extra data Extra data Extra data"),
|
||||||
[]byte("Extra data Extra data Extra data Extra data Extra data Extra data Extra data Extra data"),
|
MixDigest: common.HexToHash("0x0000H45H"),
|
||||||
common.HexToHash("0x0000H45H"),
|
|
||||||
types.BlockNonce{},
|
|
||||||
}
|
}
|
||||||
cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader)
|
cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -767,8 +849,8 @@ func testExternalUI(api *core.SignerAPI) {
|
||||||
data := `{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"test","type":"uint8"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":"1","verifyingContract":"0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","test":"3","wallet":"0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","test":"2"},"contents":"Hello, Bob!"}}`
|
data := `{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"test","type":"uint8"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":"1","verifyingContract":"0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","test":"3","wallet":"0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","test":"2"},"contents":"Hello, Bob!"}}`
|
||||||
//_, err := api.SignData(ctx, accounts.MimetypeTypedData, *addr, hexutil.Encode([]byte(data)))
|
//_, err := api.SignData(ctx, accounts.MimetypeTypedData, *addr, hexutil.Encode([]byte(data)))
|
||||||
var typedData core.TypedData
|
var typedData core.TypedData
|
||||||
err := json.Unmarshal([]byte(data), &typedData)
|
json.Unmarshal([]byte(data), &typedData)
|
||||||
_, err = api.SignTypedData(ctx, *addr, typedData)
|
_, err := api.SignTypedData(ctx, *addr, typedData)
|
||||||
expectApprove("sign 712 typed data", err)
|
expectApprove("sign 712 typed data", err)
|
||||||
}
|
}
|
||||||
{ // Sign data test - plain text
|
{ // Sign data test - plain text
|
||||||
|
|
@ -832,27 +914,6 @@ func testExternalUI(api *core.SignerAPI) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPassPhrase retrieves the password associated with clef, either fetched
|
|
||||||
// from a list of preloaded passphrases, or requested interactively from the user.
|
|
||||||
// TODO: there are many `getPassPhrase` functions, it will be better to abstract them into one.
|
|
||||||
func getPassPhrase(prompt string, confirmation bool) string {
|
|
||||||
fmt.Println(prompt)
|
|
||||||
password, err := console.Stdin.PromptPassword("Passphrase: ")
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to read passphrase: %v", err)
|
|
||||||
}
|
|
||||||
if confirmation {
|
|
||||||
confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ")
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Failed to read passphrase confirmation: %v", err)
|
|
||||||
}
|
|
||||||
if password != confirm {
|
|
||||||
utils.Fatalf("Passphrases do not match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return password
|
|
||||||
}
|
|
||||||
|
|
||||||
type encryptedSeedStorage struct {
|
type encryptedSeedStorage struct {
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
|
|
@ -903,7 +964,7 @@ func GenDoc(ctx *cli.Context) {
|
||||||
if data, err := json.MarshalIndent(v, "", " "); err == nil {
|
if data, err := json.MarshalIndent(v, "", " "); err == nil {
|
||||||
output = append(output, fmt.Sprintf("### %s\n\n%s\n\nExample:\n```json\n%s\n```", name, desc, data))
|
output = append(output, fmt.Sprintf("### %s\n\n%s\n\nExample:\n```json\n%s\n```", name, desc, data))
|
||||||
} else {
|
} else {
|
||||||
log.Error("Error generating output", err)
|
log.Error("Error generating output", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -914,7 +975,7 @@ func GenDoc(ctx *cli.Context) {
|
||||||
"of the work in canonicalizing and making sense of the data, and it's up to the UI to present" +
|
"of the work in canonicalizing and making sense of the data, and it's up to the UI to present" +
|
||||||
"the user with the contents of the `message`"
|
"the user with the contents of the `message`"
|
||||||
sighash, msg := accounts.TextAndHash([]byte("hello world"))
|
sighash, msg := accounts.TextAndHash([]byte("hello world"))
|
||||||
messages := []*core.NameValueType{{"message", msg, accounts.MimetypeTextPlain}}
|
messages := []*core.NameValueType{{Name: "message", Value: msg, Typ: accounts.MimetypeTextPlain}}
|
||||||
|
|
||||||
add("SignDataRequest", desc, &core.SignDataRequest{
|
add("SignDataRequest", desc, &core.SignDataRequest{
|
||||||
Address: common.NewMixedcaseAddress(a),
|
Address: common.NewMixedcaseAddress(a),
|
||||||
|
|
@ -945,8 +1006,8 @@ func GenDoc(ctx *cli.Context) {
|
||||||
add("SignTxRequest", desc, &core.SignTxRequest{
|
add("SignTxRequest", desc, &core.SignTxRequest{
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
Callinfo: []core.ValidationInfo{
|
Callinfo: []core.ValidationInfo{
|
||||||
{"Warning", "Something looks odd, show this message as a warning"},
|
{Typ: "Warning", Message: "Something looks odd, show this message as a warning"},
|
||||||
{"Info", "User should see this aswell"},
|
{Typ: "Info", Message: "User should see this as well"},
|
||||||
},
|
},
|
||||||
Transaction: core.SendTxArgs{
|
Transaction: core.SendTxArgs{
|
||||||
Data: &data,
|
Data: &data,
|
||||||
|
|
@ -1012,16 +1073,21 @@ func GenDoc(ctx *cli.Context) {
|
||||||
&core.ListRequest{
|
&core.ListRequest{
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
Accounts: []accounts.Account{
|
Accounts: []accounts.Account{
|
||||||
{a, accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/a"}},
|
{Address: a, URL: accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/a"}},
|
||||||
{b, accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/b"}}},
|
{Address: b, URL: accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/b"}}},
|
||||||
})
|
})
|
||||||
|
|
||||||
add("ListResponse", "Response to list request. The response contains a list of all addresses to show to the caller. "+
|
add("ListResponse", "Response to list request. The response contains a list of all addresses to show to the caller. "+
|
||||||
"Note: the UI is free to respond with any address the caller, regardless of whether it exists or not",
|
"Note: the UI is free to respond with any address the caller, regardless of whether it exists or not",
|
||||||
&core.ListResponse{
|
&core.ListResponse{
|
||||||
Accounts: []accounts.Account{
|
Accounts: []accounts.Account{
|
||||||
{common.HexToAddress("0xcowbeef000000cowbeef00000000000000000c0w"), accounts.URL{Path: ".. ignored .."}},
|
{
|
||||||
{common.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff"), accounts.URL{}},
|
Address: common.HexToAddress("0xcowbeef000000cowbeef00000000000000000c0w"),
|
||||||
|
URL: accounts.URL{Path: ".. ignored .."},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Address: common.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff"),
|
||||||
|
},
|
||||||
}})
|
}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ The Otto vm has a few [caveats](https://github.com/robertkrimen/otto):
|
||||||
Additionally, a few more have been added
|
Additionally, a few more have been added
|
||||||
|
|
||||||
* The rule execution cannot load external javascript files.
|
* The rule execution cannot load external javascript files.
|
||||||
* The only preloaded libary is [`bignumber.js`](https://github.com/MikeMcl/bignumber.js) version `2.0.3`. This one is fairly old, and is not aligned with the documentation at the github repository.
|
* The only preloaded library is [`bignumber.js`](https://github.com/MikeMcl/bignumber.js) version `2.0.3`. This one is fairly old, and is not aligned with the documentation at the github repository.
|
||||||
* Each invocation is made in a fresh virtual machine. This means that you cannot store data in global variables between invocations. This is a deliberate choice -- if you want to store data, use the disk-backed `storage`, since rules should not rely on ephemeral data.
|
* Each invocation is made in a fresh virtual machine. This means that you cannot store data in global variables between invocations. This is a deliberate choice -- if you want to store data, use the disk-backed `storage`, since rules should not rely on ephemeral data.
|
||||||
* Javascript API parameters are _always_ an object. This is also a design choice, to ensure that parameters are accessed by _key_ and not by order. This is to prevent mistakes due to missing parameters or parameter changes.
|
* Javascript API parameters are _always_ an object. This is also a design choice, to ensure that parameters are accessed by _key_ and not by order. This is to prevent mistakes due to missing parameters or parameter changes.
|
||||||
* The JS engine has access to `storage` and `console`.
|
* The JS engine has access to `storage` and `console`.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,19 @@
|
||||||
|
// Copyright 2019 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/>.
|
||||||
|
|
||||||
// This file is a test-utility for testing clef-functionality
|
// This file is a test-utility for testing clef-functionality
|
||||||
//
|
//
|
||||||
// Start clef with
|
// Start clef with
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue