From fee40523a6ce4e92c54a0fc59ff2d21f4e09eebf Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Tue, 23 Jun 2020 14:49:46 -0600 Subject: [PATCH] Drew.merge upstream (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix typo: eror -> error (#6293) * Fix typo: PRORITY -> PRIORITY (#6291) * Intent to release rc3 (#6290) * Fix transaction pool & network issues (#6288) * fix & tweaks * address review * line width * Use `sign_with` for signing grandpa's outgoing message (#6178) * Use sign_with and stop using `Pair` * PR feedback * Remove clone * Transfer ownership of public to sign_message * Use Option * Simplify code * Fix error message * Pass keystore as ref * Pass keystore properly * Fix tests * Revalidation tweak & logging for transaction pool (#6258) * updates and logging * fix length * Update client/transaction-pool/src/lib.rs * rename * Update client/transaction-pool/src/lib.rs Co-authored-by: Bastian Köcher Co-authored-by: Bastian Köcher * Update README.md * Allow adding a prefix to the informant (#6174) * Initial commit Forked at: 49b15615184fad010749d3e34282f70a3845da34 Parent branch: origin/master * Add a Service Configuration's field + adapt informant + provide means to CLI * CLEANUP Forked at: 49b15615184fad010749d3e34282f70a3845da34 Parent branch: origin/master * fix tests * fixed bad path to object * Change OutputFormat enum to struct * Add informant_prefix to builder and service * Revert "Change OutputFormat enum to struct" This reverts commit cd86c583c92668426c35cc174401155bf2880c1f. * Revert "fix tests" This reverts commit a3c306ebe94720f350c5bc74b9c5fcde2565d340. * Revert "Add a Service Configuration's field + adapt informant + provide means to CLI" This reverts commit 9c2e7267423305705916c30d605893524113c8e3. * Implementation using the ServiceBuilder * reduce line length * fix line width again * WIP Forked at: 49b15615184fad010749d3e34282f70a3845da34 Parent branch: origin/master * WIP Forked at: 49b15615184fad010749d3e34282f70a3845da34 Parent branch: origin/master * WIP Forked at: 49b15615184fad010749d3e34282f70a3845da34 Parent branch: origin/master * use struct instead of enum * WIP Forked at: 49b15615184fad010749d3e34282f70a3845da34 Parent branch: origin/master * Update client/service/src/lib.rs Co-authored-by: Bastian Köcher * improve doc * Update client/service/src/builder.rs Co-authored-by: Bastian Köcher * Update client/service/src/builder.rs Co-authored-by: Bastian Köcher * change code * Update client/informant/src/lib.rs Co-authored-by: Bastian Köcher * enable_color * reorg log * remove macro * Removed builder for informant prefix * fix doc * Update client/informant/src/lib.rs Co-authored-by: Bastian Köcher * Update client/informant/src/lib.rs Co-authored-by: Bastian Köcher * Update client/informant/src/lib.rs Co-authored-by: Bastian Köcher * Update client/informant/src/lib.rs Co-authored-by: Bastian Köcher * Update client/service/src/builder.rs Co-authored-by: Bastian Köcher * Update client/service/src/builder.rs Co-authored-by: Bastian Köcher * Update client/service/src/builder.rs Co-authored-by: Bastian Köcher Co-authored-by: Bastian Köcher * Transaction pool added missed comment (#6308) * Add a test for lots of nodes connecting at the same time (#6247) * Add a test for lots of nodes connecting at the same time * Do small change * Introduce frozen indices. (#6307) * Introduce frozen indices. * Fix. * Bump runtime * Benchmark for freeze * Fix * fix benchmarks * update freeze weights * remove copy pasta Co-authored-by: Shawn Tabrizi * new crate sc-light (#6235) * sc-light * remove unused deps * fix line width * move more fns to sc_light * Fix ui tests for latest rust stable (#6310) * Expose light client. (#6313) * Fix nits in rpc error display. (#6302) * Improve rpc error display. * Apply review suggestion. * Apply review suggestion. * Update client/rpc-api/src/author/error.rs * Fix custom. Co-authored-by: Bastian Köcher * "OR gate" for EnsureOrigin (#6237) * 'OR gate' for EnsureOrigin. * Formatting. * More formatting. * Add docstring; Update 'Success' type. * Bump runtime impl_version. * Fix successful_origin. * Add either into std feature list. * Update docs. * New CI image (#6223) * fix (ci): hotfix Docker release * change (ci): moving to the tested CI image with a proper name * change (ci): rename substrate-ci-linux * Reduce the lots_of_incoming_peers_works test load (#6314) * change (ci): moving to the tested CI image with a proper name * change (ci): rename substrate-ci-linux * Reduce the lots_of_incoming_peers_works test load (#6314) Co-authored-by: Bastian Köcher Co-authored-by: Pierre Krieger * Add a feature to create automatically a random temporary directory for base path & remove `Clone` (#6221) * Initial commit Forked at: 342caad3074076a4fde0472719f6a473df839e42 Parent branch: origin/master * Add a feature to create automatically a temporary directory for base path * doc fix and todos * use parking_lot instead * use refcell instead since we stay in the main thread * remove Clone derives * add test * solving dependency issue * clarifying doc * conflict argument with base-path * WIP Forked at: 342caad3074076a4fde0472719f6a473df839e42 Parent branch: origin/master * revert dep deletion * fixing test and making base_path optional * hold basepath while the service is running * fixes * Update client/cli/src/params/shared_params.rs Co-authored-by: Bastian Köcher * Update client/service/Cargo.toml Co-authored-by: Bastian Köcher * Update client/cli/src/commands/mod.rs Co-authored-by: Bastian Köcher * Update client/service/src/config.rs Co-authored-by: Bastian Köcher * WIP Forked at: 342caad3074076a4fde0472719f6a473df839e42 Parent branch: origin/master * improve doc Co-authored-by: Bastian Köcher * Add a [prefix]_process_start_time_seconds metric (#6315) * Make NumberOrHex a common primitive. (#6321) * Make NumberOrHex a common primitive. * Update primitives/rpc/src/number.rs Co-authored-by: Nikolay Volf Co-authored-by: Nikolay Volf * Avoid self-lookups in Authority Discovery (#6317) * Ensure authority discovery avoids self-lookups. Thereby additionally guard the `NetworkService` against adding the local peer to the PSM or registering a "known address" for the local peer. * Clarify comments. * See if returning errors is ok. * Fix quadratic iterations in transaction pool ready set (#6256) * refactor ready set size calc * Update client/transaction-pool/graph/src/ready.rs Co-authored-by: Bastian Köcher * remove pub * update to new variat * rename Co-authored-by: Bastian Köcher * Find the alive incoming entry on disconnect. (#6320) When a peer in `Incoming` state disconnects, the "alive" entry in the `incoming` list for that peer must be updated (set to `false`). Currently the entry that is updated may be an earlier entry for the same peer that is already no longer alive. This can happen if a peer repeatedly connects (incoming) and disconnects between invocations to `poll()` of the behaviour. * Impl Debug and Display for Ss58AddressFormat when compiled with std (#6327) * Initial commit Forked at: 606c56d2e2f69f68f3947551224be6a3515dff60 Parent branch: origin/master * Impl Debug and Display for Ss58AddressFormat when compiled with std Fixes #6289 * Use write! instead of writeln! * transaction-pool: expose blocking api for tx submission (#6325) * transaction-pool: expose blocking api for tx submission * service: separate ServiceBuilder::build for full and light * service: add ServiceBuilder::build_common * transaction-pool: extend docs Co-authored-by: Tomasz Drwięga Co-authored-by: Tomasz Drwięga * Pruned and resubmitted metrics in transaction pool (#6322) * pruned and resubmitted metrics * update counter once * Enable wasmtime on node-template (#6336) * Enable wasmtime on node-template * Apply suggestions from code review syntax Co-authored-by: Nikolay Volf Co-authored-by: Nikolay Volf * Adds support for storage parameter types (#6296) * Adds support for storage parameter types This pr adds a new parameter types type, the storage parameter types. This parameter type supports loading the value from the storage or returning the given default value. * Use twox_128 * Update docs * Update frame/support/src/lib.rs Co-authored-by: Alexander Popiak Co-authored-by: Alexander Popiak * Basic documentation for Scheduler pallet (#6338) Closes #5912 * Fix check-line-width CI script (#6326) * Compare lines to the hash that the PR branched off from * Use git merge-base to determine common ancestor * Fixup * client: use appropriate ExecutionContext for initial sync / regular import (#6180) * client: use appropriate ExecutionContext for sync/import * client: remove dead code * client: ExecutionContext: distinguish between own and foreign imports * client: fix cli parameter doc * Revert "client: ExecutionContext: distinguish between own and foreign imports" This reverts commit 0fac11520704c364a82432c5b927e987ba043cdb. * primitives: add docs for ExecutionContext * cli: execution strategy docs * cli: use different execution context for importing block on validator * cli: remove defaults from execution context flags * Fix transaction pool event sending (#6341) This pr fixes a bug with the transaction pool not sending certain events like finalized and also fixes the order of events. The problem with the finalized event was that we did not extracted pruned extrinsics if there were not ready transactions in the pool. However this is wrong, if we have a re-org, a tx is clearly not ready anymore and we still need to send a pruned event for it because it is in a new block included. This also lead to sending "ready" events and tx being re-validated. The listener also only send the "finalized" event if it has seen a block as being included, which did not happen before with the old code. The second fix of the pr is the order of events. If we prune and retract the same transaction in the same block, we first need to send the "retract" event and after that the "pruned" event, because finalization takes longer and this would lead to the UI showing "retract" while it actually is included. * Deprecate FunctionOf and remove its users (#6340) * Deprecate FunctionOf and remove users * Remove unused import * Add events for balance reserve and unreserve functions (#6330) * almost works * add clone to BalanceStatus * reserve event * fix staking tests * fix balances tests * Update frame/balances/src/tests.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * restore tests and move event emission * move repatriate reserved event outside of mutate_account * clean up events in tests Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update contributing guide with new label policy (#6333) * mention C and M labels in contributing guide * update PR template with more specific instructions * update PR template with updated label rules and contributing guide link * update contibuting guide * adding a ss58 format for Stafi Network (#6347) * add extend_lock for StorageLock (#6323) * add extend_lock for StorageLock * changes * changes * Introduce in-origin filtering (#6318) * impl filter in origin * remove IsCallable usage. Breaking: utility::batch(root, calls) no longer bypass BasicCallFilter * rename BasicCallFilter -> BaseCallFilter * refactor code * Apply suggestions from code review Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * remove forgotten temporar comment * better add suggestion in another PR * refactor: use Clone instead of mem::replace * fix tests * fix tests * fix tests * fix benchmarks * Make root bypass filter in utility::batch * fix unused imports Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * pallet-evm add get(fn) to AccountStorages (#6279) * Add IPC support (#6348) This is useful for both security and performance reasons. IPC is faster than TCP, and it is subject to OS access controls. * expose constants of pallet_recovery trait (#6363) * Impl integrity test for runtime (#6356) * impl integrity test for runtime * Update frame/support/src/traits.rs Co-authored-by: Bastian Köcher * Update frame/support/procedural/src/construct_runtime/mod.rs Co-authored-by: Bastian Köcher * use thread local * update doc * Apply suggestions from code review Co-authored-by: Bastian Köcher Co-authored-by: Gavin Wood * historical slashing w ocw w adhoc tree creation (#6220) * draft * steps * chore: fmt * step by step * more details * make test public * refactor: split into on and offchain * test stab * tabs my friend * offchain overlay: split key into prefix and true key Simplifies inspection and makes key actually unique. * test: share state * fix & test * docs improv * address review comments * cleanup test chore * refactor, abbrev link text * chore: linewidth * fix prefix key split fallout * minor fallout * minor changes * addresses review comments * rename historical.rs -> historical/mod.rs * avoid shared::* wildcard import * fix: add missing call to store_session_validator_set_to_offchain * fix/compile: missing shared:: prefix * fix/test: flow * fix/review: Apply suggestions from code review Co-authored-by: Tomasz Drwięga * fix/review: more review comment fixes * fix/review: make ValidatorSet private * fix/include: core -> sp_core * fix/review: fallout * fix/visbility: make them public API Ref #6358 * fix/review: review changes fallout - again Co-authored-by: Bernhard Schuster Co-authored-by: Tomasz Drwięga * [CI] Auto-label new PRs according to draft status (#6361) * add auto-label github action * Add missing 'remove-labels' line * Split the service initialisation up into seperate functions (#6332) * Seperate out the complexity in ServiceBuilder::build_common into seperate functions * Fix line widths * Move some functions to their respective crates * [CI] Add label enforcement (#6365) * Add label enforcement * fix .gitlab-ci.yml * update check_labels.sh * vesting: Force Vested Transfer (#6368) * force-vested-transfer * Tweak weights * Update frame/vesting/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * client/authority-discovery: Don't add own address to priority group (#6370) * client/authority-discovery: Don't add own address to priority group In the scenario of a validator publishing the address of its sentry node to the DHT, said sentry node should not add its own Multiaddr to the peerset "authority" priority group. Related to 70cfeff. * client/authority-discovery: Remove unused import PeerId * client/authority-discovery/tests: Add tcp protocol to multiaddresses * .gitlab-ci.yml: Run promtool on Prometheus alerting rules (#6344) * .gitlab-ci.yml: Run promtool on Prometheus alerting rules Add a CI stage to test the Prometheus alerting rules within `.maintain/monitoring`. * .gitlab-ci.yml: Switch Prometheus stage to paritytech/tools image * .gitlab-ci.yml: Follow http redirects in Prometheus stage * .gitlab-ci.yml: Fix Prometheus stage promtool folder name * Use /dns/ instead of /dns4/ (#6369) * add system_dryRun (#6300) * add system_dryRun * fix build error * delete unneeded code * return ApplyExtrinsicResult directly * line width * mark dry run unsafe * line width * fix test * add test * update comment * fix BlockAttributes encoding (#6281) * Allow Sudo to do anything (#6375) * All Sudo to do anything. * Rename old labels. * Stored call in multisig (#6319) * Stored call in multisig * Docs. * Benchmarks. * Fix * Update frame/multisig/src/lib.rs Co-authored-by: Bastian Köcher * patch benchmarks * Minor grumbles. * Update as_multi weight * Fixes and refactoring. * Split out threshold=1 and opaquify Call. * Compiles, tests pass, weights are broken * Update benchmarks, add working tests * Add benchmark to threshold 1, add event too * suppress warning for now * @xlc improvment nit * Update weight and tests * Test for weight check * Fix line width * one more line width error * Apply suggestions from code review Co-authored-by: Alexander Popiak * fix merge * more @apopiak feedback * Multisig handles no preimage * Optimize return weight after dispatch * Error on failed deposit. Co-authored-by: Bastian Köcher Co-authored-by: Shawn Tabrizi Co-authored-by: Alexander Popiak * Fix the broken weight multiplier update function (#6334) * Initial draft, has some todos left * remove ununsed import * Apply suggestions from code review * Some refactors with migration * Fix more test and cleanup * Fix for companion * Apply suggestions from code review Co-authored-by: Alexander Popiak * Update bin/node/runtime/src/impls.rs * Fix weight * Add integrity test * length is not affected. Co-authored-by: Alexander Popiak * Restrict remove_proxies (#6383) * Remove penalty on duplicate Status message (#6377) * `decl_module!` print better error on duplicate reserved keyword (#6384) * `decl_module!` print better error on duplicate reserved keyword This prints a better error message on duplicated reserved keywords, instead of complaining because of missing `origin`. * Review feedback * FixedPointNumber: zero is not positive. (#6385) * Allow empty values in the storage (#6364) * Allow empty values in the storage * Bump trie-bench * Bump trie-bench * Pallet: Atomic Swap (#6349) * Init atomic swap pallet * Implement module swap operations * Add successful swap test * Bump node spec_version * Fix storage name * Add ProofLimit parameter to prevent proof size being too large * Add missing events * Basic weight support * Add basic docs * Mark swap on claim This handles the additional case if `repatriate_reserved` fails. * Add additional expire handler * Update frame/atomic-swap/src/lib.rs Co-authored-by: Shawn Tabrizi * Add docs on ProofLimit * Fix test * Return Ok(()) even when the transfer fails Because we need to mark the swap as claimed no matter what. * Remove retry logic It's overkill. Swap is about something being executed, not necessarily successful. Although there should be logic (reserve and unreserve) to make it so that both parties *believes* that the execution is successful. * succeed -> succeeded * Add docs on duration -- revealer should use duration shorter than counterparty * Missing trait type Co-authored-by: Shawn Tabrizi * Runtime interface to add support for tracing from wasm (#6381) * Add span recording to tracing implementation * Add tracing proxy * switch to rustc_hash::FxHashMap * Replace lazy_static and hashmap with thread_local and vec. * fix marking valid span as invalid while removing invalid spans * refactor, add wasm_tracing module in `support` * update registered spans * tidy up * typos * refactor * update flag name to signal lost trace - `is_valid_trace` * update flag name to signal lost trace - `is_valid_trace` * update docs * update docs * Use tracing Field recording to store the actual `name` and `target` from wasm traces. * fix debug log in subscriber + small refactor * add tests * handle misuse in case trying to exit span not held * Implement filter for wasm traces, simplify field recording for primitive types * remove superfluous warning * update docs * Update primitives/tracing/src/proxy.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Bastian Köcher * update docs, apply suggestions * move Proxy from thread_local to `Extension`, rename macro * fix test * unify native & wasm span macro calls * implement wasm tracing control facility in primitives and frame * add cli flag `--wasm-tracing` * fix * switch to `Option` (possible performance degradation), switch to static mut bool * performance improvement using u64 vs Option * performance improvement moving concat to client * update docs * Update client/cli/src/params/import_params.rs Co-authored-by: Cecile Tonglet * performance improvement * Revert "performance improvement" This reverts commit cff0aa2670cd1d380f1893f0a6f4d498b384e7b7. * small refactor * formatting * bump impl_version * Update client/cli/src/config.rs Co-authored-by: Bastian Köcher * update docs * small fixes, remove pub static * nit * add integration tests and refactor Subscriber * tests * revert formatting * try fix test that works locally but not in CI * try fix test that works locally but not in CI * debug test that works locally but not in CI * fix test that works locally but not in CI * remove pub visibility from bool in runtime * make TracingSpanGuard #[cfg(not(feature = "std"))], update docs, comments * make TracingProxy drop implementation conditional on !empty state * add docs for TraceHandler * remove blank line * update expect message * update tests * rename cli option to tracing_enable_wasm * rename cli option to tracing_enable_wasm * fix * ensure wasm-tracing features are wasm only * bump impl_version * bump impl_version * add `"pallet-scheduler/std"` to `[features]` `std` in node/runtime * refactor service to remove sp_tracing dependency * refactor: line width, trait bounds * improve LogTraceHandler output * fix test * improve tracing log output * Apply suggestions from code review * Apply suggestions from code review Co-authored-by: Bastian Köcher * swap wasm indication from trace name to a separate value * Update client/tracing/src/lib.rs * add docs * remove runtime features remove wasm_tracing option from CLI remove wasm_tracing flag from ProfilingSubscriber Co-authored-by: Matt Rutherford Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Bastian Köcher Co-authored-by: Cecile Tonglet * Block packet size limit * Revert "Block packet size limit" This reverts commit 9a5892e187f7d9b3f058b549ad5859793d117d7b. * Update s3 artifact url (#6399) * Increase network buffer sizes even more (#6080) * Remove pallet-balances from non-dev-deps (#6407) * Babe VRF Signing in keystore (#6225) * Introduce trait * Implement VRFSigner in keystore * Use vrf_sign from keystore * Convert output to VRFInOut * Simplify conversion * vrf_sign secondary slot using keystore * Fix RPC call to claim_slot * Use Public instead of Pair * Check primary threshold in signer * Fix interface to return error * Move vrf_sign to BareCryptoStore * Fix authorship_works test * Fix BABE logic leaks * Acquire a read lock once * Also fix RPC acquiring the read lock once * Implement a generic way to construct VRF Transcript * Use make_transcript_data to call sr25519_vrf_sign * Make sure VRFTranscriptData is serializable * Cleanup * Move VRF to it's own module * Implement & test VRF signing in testing module * Remove leftover * Fix feature requirements * Revert removing vec macro * Drop keystore pointer to prevent deadlock * Nitpicks * Add test to make sure make_transcript works * Fix mismatch in VRF transcript * Add a test to verify transcripts match in babe * Return VRFOutput and VRFProof from keystore * Update `libp2p-ping`. (#6412) Bugfix release, see [CHANGELOG]. [CHANGELOG]: https://github.com/libp2p/rust-libp2p/blob/master/protocols/ping/CHANGELOG.md * Remove --legacy-network-protocol CLI flag (#6411) * Scale and increase validator count (#6417) * Expose constants from Proxy Pallet (#6420) * .maintain/monitoring: Add alerting rule tests (#6343) * .maintain/monitoring: Add alerting rule tests * .maintain/monitoring/alerting-rules/alerting-rules.yaml: Break lines * .gitlab-ci.yml: Add promtool rule testing step * [CI] Label PRs if polkadot companion build fails (#6410) * add polkadot-companion-labels.yml * fix polkadot companion job name * add opened event to polkadot-companion-labels.yml * Dont label on timeouts * increase timeouts * increase timeouts again... to be sure * Switch to s3krit/await-status-action Turns out Sibz/await-status-action looks at /ref/statuses, which lists ALL statuses (i.e., if you send a pending and a failure for the same context, it will see both and assume the job is still pending.). I forked and point at /ref/status, which shows a combined summary of each status (i.e., only ever shows the most recent status of a single context). * Print bad mandatory error (#6416) * Print bad mandatory error This prints the error that leads to bad mandatory. * Update frame/system/src/lib.rs Co-authored-by: Shawn Tabrizi * Adds missing trait import Co-authored-by: Shawn Tabrizi * Track last blocks in informant display (#6429) This implements tracking of the last seen blocks in informant display to prevent printing the import message twice. In Cumulus we first import blocks as part of the block building with `new_best == false` and set the best block after we know which one was included by the relay chain. This leads to printing the import messages two times. This pr solves the problem by track the latest seen blocks to not print the message twice. * Simple Docs for Atomic Swap Pallet (#6434) * Simple Docs for Atomic Swap Pallet * Fix copy-and-paste error * More descriptive error message when invalid slot duration is used (#6430) * Initial commit Forked at: d735e4d0b5378c227f81a5127a1d4544de112fd8 No parent branch. * Errors if slot_duration is zero * Errors if slot_duration is zero * Revert "Errors if slot_duration is zero" This reverts commit a9e9820e124571f73d3e498e969a74d01fd3fe96. * Update client/consensus/slots/src/lib.rs Co-authored-by: Bastian Köcher * Root origin use no filter by default. Scheduler and Democracy dispatch without asserting BaseCallFilter (#6408) * make system root origin build runtime origin with no filter * additional doc * llow decl-module to have a where clause with trailing comma (#6431) * .gitlab-ci.yml: Use promtool from paritytech/tools:latest image (#6425) * Update sync chain info on own block import (#6424) Before we only updated the chain info of sync when we have imported something using the import queue. However, if you import your own blocks, this is not done using the import queue and so sync is not updated. If we don't do this, it can lead to sync switching to "major sync" mode because sync is not informed about new blocks. This especially happens on Cumulus, where a collator is selected multiple times to include its block into the relay chain and thus, sync switches to major sync mode while the node is still building blocks. * client/authority-discovery: Compare PeerIds and not Multihashes (#6414) In order to tell whether an address is the local nodes address the authority discovery module previously compared the Multihash within the `p2p` Multiaddr protocol. rust-libp2p recently switched to a new PeerId representation (see [1]). Multihashes of the same PeerId in the new and the old format don't equal. Instead of comparing the Multihashes, this patch ensures the module compares the PeerIds [1] https://github.com/libp2p/rust-libp2p/issues/555 * add network propagated metrics (#6438) * change (ci): add interruptible to kubernetes jobs (#6441) * Avoid multisig reentrancy (#6445) * Validate encoding of extrinsics passed to runtime (#6442) * Validate encoding of extrinsics passed to runtime * Bump codec version explicitly * Fix Babe secondary plain slots claiming (#6451) We need to check that the public key of an authority exists in our keystore before we can successfully claim a plain secondary slot. * sp-npos-elections should not depend on itself (#6444) This removes the `dev-dependency` onto `sp-npos-elections` from itself. A crate should not depend on itself directly, especially not to make any macros work. * Don't autolabel insubstantial PRs 'pleasereview' (#6447) * change everything to transaction (#6440) * node: spawn block authoring and grandpa voter as blocking tasks (#6446) * service: add spawner for essential tasks * node: spawn block authoring and grandpa voter as blocking tasks * Apply suggestions from code review Co-authored-by: Bastian Köcher * pallet-atomic-swap: generialized swap action (#6421) * pallet-atomic-swap: generialized swap action * Bump spec_version * Fix weight calculation * Remove unnecessary type aliases * Fix issues with `Operational` transactions validity and prioritization. (#6435) * Fix weight limit for operational transactions. * Include BlockExecutionWeight. * `pallet-staking`: Expose missing consts (#6456) * `pallet-staking`: Expose missing consts * Apply suggestions from code review Co-authored-by: Nikolay Volf Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update the source docs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Nikolay Volf Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * update collective events docs to be consistent with changes (#6463) * [CI] Don't tag PRs on companion job cancels (#6470) * network: remove unused variable (#6460) * Avoid panic on dropping a `sc_network::service::out_events::Receiver`. (#6458) * Avoid panic on dropping a `Receiver`. * CI * Implement nested storage transactions (#6269) * Add transactional storage functionality to OverlayChanges A collection already has a natural None state. No need to wrap it with an option. * Add storage transactions runtime interface * Add frame support for transactions * Fix committed typo * Rename 'changes' variable to 'overlay' * Fix renaming change * Fixed strange line break * Rename clear to clear_where * Add comment regarding delete value on mutation * Add comment which changes are covered by a transaction * Do force the arg to with_transaction return a Result * Use rust doc comments on every documentable place * Fix wording of insert_diry doc * Improve doc on start_transaction * Rename value to overlayed in close_transaction * Inline negation * Improve wording of close_transaction comments * Get rid of an expect by using get_or_insert_with * Remove trailing whitespace * Rename should to expected in tests * Rolling back a transaction must mark the overlay as dirty * Protect client initiated storage tx from being droped by runtime * Review nits * Return Err when entering or exiting runtime fails * Documentation fixup * Remove close type * Move enter/exit runtime to excute_aux in the state-machine * Rename Discard -> Rollback * Move child changeset creation to constructor * Move child spawning into the closure * Apply suggestions from code review Co-authored-by: Bastian Köcher * Fixup for code suggestion * Unify re-exports * Rename overlay_changes to mod.rs and move into subdir * Change proof wording * Adapt a new test from master to storage-tx * Suggestions from the latest round of review * Fix warning message Co-authored-by: Bastian Köcher * Optimize offchain worker api by re-using http-client (#6454) * Fix typo in offchain's docs * Use Self keyword in AsyncApi::new() * Move httpclient to be part of OffchainWorkers to optimize block import * Fix compilation errors for tests * Add wrapper struct for HyperClient * Use lazy_static share SharedClient amongst OffchainWorkers. Remove the need to raise the fd limit * Revert "Use lazy_static share SharedClient amongst OffchainWorkers. Remove the need to raise the fd limit" This reverts commit 7af97498a2383b5d7405e27823db8fd97245da41. * Add lazy_static for tests * Remove lingering runtime upgrades (#6476) * Remove lingering runtime upgrades * remove unused warnings * remove tests * impl Debug for sc_service::Configuration (#6400) * Initial commit Forked at: d735e4d0b5378c227f81a5127a1d4544de112fd8 No parent branch. * Make sc_service::Configuration derive Debug * Replace task_executor fn's input by proper TaskExecutor type (cleaner) * impl From for TaskExecutor * Update client/cli/src/runner.rs * Add some doc, examples and tests * Replace Deref by fn spawn as suggested Co-authored-by: Bastian Köcher * Fix `sp-api` handling of multiple arguments (#6484) With the switch to `decode_all_with_depth_limit` we silently broken support for functions with multiple arguments. The old generated code tried to decode each parameter separately, which does not play well with `decode_all`. This pr adds a test to ensure that this does not happen again and fixes the bug by decoding everything at once by wrapping it into tuples. * Fix the browser node and ensure it doesn't colour the informant output (#6457) * Fix browser informant * Fix documentation * Add an informant_output_format function to the cli config * Wrap informant output format in an option * Revert batch verifier * Remove wasm-timer from primitives io cargo lock * Drop informant_output_format function * derive debug for output format * bound some missing bound for elevated trait (#6487) * `pallet-scheduler`: Check that `when` is not in the past (#6480) * `pallet-scheduler`: Check that `when` is not in the past * Break some lines * client/network/service: Add primary dimension to connection metrics (#6472) * client/network/service: Add primary dimension to connection metrics Two nodes can be interconnected via one or more connections. The first of those connections is called the primary connection. This commit adds another dimension to the `sub_libp2p_connections_{closed,opened}_total` metrics to differentiate primary and non-primary connections being opened / closed. By intuition more than one connection between two nodes is rare. Tracking the fact whether a connection is primary or not will help prove or disprove this intuition. * .maintain/monitoring: Ensure to sum over all connections_closed variants * client/network/service: Rename is_primary to is_first * client/network/service: Split by metric name with two additional metrics * Revert ".maintain/monitoring: Ensure to sum over all connections_closed variants" This reverts commit 2d2f93e414440b9fc9e8f7fae6fe48bd95af6b8f. * client/network/service: Remove labels from distinct metrics * Ensure the listen addresses are consistent with the transport (#6436) * Initial commit Forked at: 0c42cedaac0b1bf3a608031ee3e494b51bfaa0fe No parent branch. * Ensure the listen addresses are consistent with the transport * Update client/network/src/error.rs * Update client/network/src/service.rs * Better implementation * Fix bad previous impl * add boot_nodes * reserved nodes * test boot nodes * reserved nodes tests * add public_addresses and make specific error type * Update client/network/src/error.rs Co-authored-by: Pierre Krieger Co-authored-by: Pierre Krieger * pallet-contracts: migrate to nested storage transaction mechanism (#6382) * Add a simple direct storage access module * WIP * Completely migrate to the transactional system. * Format * Fix wasm compilation * Get rid of account_db module * Make deposit event eager * Make restore_to eager * It almost compiles. * Make it compile. * Make the tests compile * Get rid of account_db * Drop the result. * Backport the book keeping. * Fix all remaining tests. * Make it compile for std * Remove a stale TODO marker * Remove another stale TODO * Add proof for `terminate` * Remove a stale comment. * Make restoration diverging. * Remove redudnant trait: `ComputeDispatchFee` * Update frame/contracts/src/exec.rs Co-authored-by: Alexander Theißen * Introduce proper errors into the storage module. * Adds comments for contract storage module. * Inline `ExecutionContext::terminate`. * Restore_to should not let sacrifice itself if the contract present on the stack. * Inline `transfer` function * Update doc - add "if succeeded" * Adapt to TransactionOutcome changes * Updates the docs for `ext_restore_to` * Add a proper assert. * Update frame/contracts/src/wasm/runtime.rs Co-authored-by: Alexander Theißen Co-authored-by: Alexander Theißen Co-authored-by: Alexander Theißen * Update lock Co-authored-by: Subsocial <62490051+subsocialdev@users.noreply.github.com> Co-authored-by: Benjamin Kampmann Co-authored-by: Nikolay Volf Co-authored-by: Rakan Alhneiti Co-authored-by: Bastian Köcher Co-authored-by: Gavin Wood Co-authored-by: Cecile Tonglet Co-authored-by: Pierre Krieger Co-authored-by: Shawn Tabrizi Co-authored-by: Seun Lanlege Co-authored-by: David Craven Co-authored-by: Marcio Diaz Co-authored-by: Shaopeng Wang Co-authored-by: Denis Pisarev Co-authored-by: Bastian Köcher Co-authored-by: Sergei Shulepov Co-authored-by: Roman Borschel Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> Co-authored-by: Tomasz Drwięga Co-authored-by: Alexander Popiak Co-authored-by: Dan Forbes Co-authored-by: Alexander Theißen Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Tore19 <289649077@qq.com> Co-authored-by: wangjj9219 <183318287@qq.com> Co-authored-by: Guillaume Thiolliere Co-authored-by: tgmichel Co-authored-by: Demi Obenour Co-authored-by: Bernhard Schuster Co-authored-by: Bernhard Schuster Co-authored-by: s3krit Co-authored-by: Ashley Co-authored-by: Max Inden Co-authored-by: Xiliang Chen Co-authored-by: Svyatoslav Nikolsky Co-authored-by: Arkadiy Paronyan Co-authored-by: Wei Tang Co-authored-by: mattrutherford <44339188+mattrutherford@users.noreply.github.com> Co-authored-by: Matt Rutherford Co-authored-by: ddorgan Co-authored-by: Toralf Wittner Co-authored-by: pscott <30843220+pscott@users.noreply.github.com> Co-authored-by: Alexander Theißen --- .github/workflows/auto-label-prs.yml | 21 + .../workflows/polkadot-companion-labels.yml | 31 + .gitlab-ci.yml | 22 +- .maintain/flamingfir-deploy.sh | 2 +- .maintain/gitlab/check_labels.sh | 46 + .maintain/gitlab/check_line_width.sh | 13 +- .maintain/gitlab/check_runtime.sh | 2 +- .maintain/gitlab/lib.sh | 8 +- .../alerting-rules/alerting-rule-tests.yaml | 239 +++ .../alerting-rules/alerting-rules.yaml | 46 +- .maintain/sentry-node/docker-compose.yml | 14 +- Cargo.lock | 1428 ++++++++++------- Cargo.toml | 2 + README.md | 7 +- bin/node-template/node/Cargo.toml | 6 +- bin/node-template/node/src/lib.rs | 2 + bin/node-template/node/src/service.rs | 10 +- bin/node-template/pallets/template/Cargo.toml | 3 +- .../pallets/template/src/mock.rs | 4 +- bin/node-template/runtime/Cargo.toml | 2 +- bin/node-template/runtime/src/lib.rs | 5 +- bin/node/cli/Cargo.toml | 2 +- bin/node/cli/res/flaming-fir.json | 2 +- bin/node/cli/src/cli.rs | 4 +- bin/node/cli/src/service.rs | 14 +- bin/node/cli/tests/temp_base_path_works.rs | 72 + bin/node/executor/Cargo.toml | 2 +- bin/node/executor/tests/basic.rs | 55 +- bin/node/executor/tests/fees.rs | 12 +- bin/node/inspect/Cargo.toml | 2 +- bin/node/inspect/src/cli.rs | 4 +- bin/node/primitives/Cargo.toml | 2 +- bin/node/rpc/Cargo.toml | 1 + bin/node/rpc/src/lib.rs | 9 +- bin/node/runtime/Cargo.toml | 3 +- bin/node/runtime/src/impls.rs | 294 ++-- bin/node/runtime/src/lib.rs | 78 +- bin/node/testing/Cargo.toml | 2 +- bin/utils/subkey/Cargo.toml | 2 +- client/api/Cargo.toml | 2 +- client/authority-discovery/Cargo.toml | 2 +- client/authority-discovery/src/lib.rs | 61 +- client/authority-discovery/src/tests.rs | 107 +- client/basic-authorship/Cargo.toml | 2 +- .../basic-authorship/src/basic_authorship.rs | 3 +- client/block-builder/Cargo.toml | 2 +- client/chain-spec/src/lib.rs | 6 + client/cli/Cargo.toml | 1 - client/cli/src/arg_enums.rs | 2 + client/cli/src/commands/build_spec_cmd.rs | 2 +- client/cli/src/commands/check_block_cmd.rs | 2 +- client/cli/src/commands/export_blocks_cmd.rs | 2 +- client/cli/src/commands/export_state_cmd.rs | 4 +- client/cli/src/commands/import_blocks_cmd.rs | 2 +- client/cli/src/commands/mod.rs | 17 +- client/cli/src/commands/purge_chain_cmd.rs | 2 +- client/cli/src/commands/revert_cmd.rs | 2 +- client/cli/src/commands/run_cmd.rs | 32 +- client/cli/src/config.rs | 49 +- client/cli/src/lib.rs | 7 +- client/cli/src/params/database_params.rs | 2 +- client/cli/src/params/import_params.rs | 52 +- client/cli/src/params/keystore_params.rs | 2 +- client/cli/src/params/mod.rs | 4 +- client/cli/src/params/network_params.rs | 8 +- client/cli/src/params/node_key_params.rs | 2 +- .../cli/src/params/offchain_worker_params.rs | 2 +- client/cli/src/params/pruning_params.rs | 2 +- client/cli/src/params/shared_params.rs | 14 +- .../cli/src/params/transaction_pool_params.rs | 2 +- client/cli/src/runner.rs | 33 +- client/consensus/aura/Cargo.toml | 2 +- client/consensus/aura/src/lib.rs | 2 +- client/consensus/babe/Cargo.toml | 3 +- client/consensus/babe/rpc/Cargo.toml | 2 +- client/consensus/babe/rpc/src/lib.rs | 25 +- client/consensus/babe/src/authorship.rs | 173 +- client/consensus/babe/src/lib.rs | 2 +- client/consensus/babe/src/tests.rs | 48 +- client/consensus/epochs/Cargo.toml | 2 +- client/consensus/manual-seal/src/lib.rs | 2 +- client/consensus/pow/Cargo.toml | 2 +- client/consensus/pow/src/lib.rs | 2 +- client/consensus/slots/Cargo.toml | 2 +- client/consensus/slots/src/lib.rs | 10 +- client/db/Cargo.toml | 2 +- client/db/src/lib.rs | 9 +- client/db/src/parity_db.rs | 2 +- client/executor/Cargo.toml | 5 +- client/executor/common/Cargo.toml | 2 +- client/executor/runtime-test/src/lib.rs | 10 +- client/executor/src/integration_tests/mod.rs | 99 ++ client/executor/wasmi/Cargo.toml | 2 +- client/executor/wasmtime/Cargo.toml | 2 +- client/finality-grandpa/Cargo.toml | 3 +- .../finality-grandpa/src/communication/mod.rs | 36 +- client/finality-grandpa/src/environment.rs | 20 +- client/finality-grandpa/src/import.rs | 1 + client/finality-grandpa/src/lib.rs | 51 +- client/finality-grandpa/src/observer.rs | 8 +- client/finality-grandpa/src/tests.rs | 9 +- client/informant/Cargo.toml | 4 +- client/informant/src/display.rs | 40 +- client/informant/src/lib.rs | 140 +- client/keystore/Cargo.toml | 3 +- client/keystore/src/lib.rs | 25 +- client/light/Cargo.toml | 27 + .../src/client/light => light/src}/backend.rs | 0 .../client/light => light/src}/blockchain.rs | 5 +- .../light => light/src}/call_executor.rs | 0 .../src/client/light => light/src}/fetcher.rs | 4 +- client/light/src/lib.rs | 57 + client/network/Cargo.toml | 2 +- client/network/src/block_requests.rs | 49 +- client/network/src/config.rs | 4 - client/network/src/discovery.rs | 3 +- client/network/src/error.rs | 15 +- client/network/src/lib.rs | 22 +- client/network/src/light_client_handler.rs | 35 +- client/network/src/protocol.rs | 306 ++-- .../src/protocol/generic_proto/behaviour.rs | 4 +- client/network/src/protocol/message.rs | 14 + client/network/src/protocol/sync.rs | 3 - client/network/src/service.rs | 172 +- client/network/src/service/out_events.rs | 8 +- client/network/src/service/tests.rs | 188 +++ client/offchain/Cargo.toml | 4 +- client/offchain/src/api.rs | 11 +- client/offchain/src/api/http.rs | 37 +- client/offchain/src/api/http_dummy.rs | 12 +- client/offchain/src/lib.rs | 47 +- client/rpc-api/Cargo.toml | 2 +- client/rpc-api/src/author/error.rs | 13 +- client/rpc-api/src/chain/mod.rs | 2 +- client/rpc-servers/Cargo.toml | 1 + client/rpc-servers/src/lib.rs | 19 + client/rpc/Cargo.toml | 2 +- client/rpc/src/chain/mod.rs | 34 +- client/rpc/src/chain/tests.rs | 2 +- client/service/Cargo.toml | 8 +- client/service/src/builder.rs | 759 +++++---- client/service/src/client/client.rs | 23 +- .../src/client/{light/mod.rs => light.rs} | 44 +- client/service/src/config.rs | 142 +- client/service/src/lib.rs | 87 +- client/service/src/metrics.rs | 21 +- client/service/src/task_manager.rs | 80 +- client/service/test/Cargo.toml | 3 +- client/service/test/src/client/light.rs | 2 +- client/service/test/src/lib.rs | 49 +- client/state-db/Cargo.toml | 2 +- client/tracing/Cargo.toml | 2 + client/tracing/src/lib.rs | 237 ++- client/transaction-pool/Cargo.toml | 2 +- client/transaction-pool/graph/Cargo.toml | 2 +- client/transaction-pool/graph/src/lib.rs | 1 + client/transaction-pool/graph/src/ready.rs | 22 +- .../transaction-pool/graph/src/tracked_map.rs | 189 +++ client/transaction-pool/src/api.rs | 90 +- client/transaction-pool/src/lib.rs | 145 +- client/transaction-pool/src/metrics.rs | 16 + client/transaction-pool/src/revalidation.rs | 22 +- client/transaction-pool/src/testing/pool.rs | 79 + docs/CONTRIBUTING.adoc | 43 +- docs/PULL_REQUEST_TEMPLATE.md | 13 +- frame/assets/Cargo.toml | 2 +- frame/assets/src/lib.rs | 4 +- frame/atomic-swap/Cargo.toml | 38 + frame/atomic-swap/src/lib.rs | 327 ++++ frame/atomic-swap/src/tests.rs | 157 ++ frame/aura/Cargo.toml | 2 +- frame/aura/src/mock.rs | 4 +- frame/authority-discovery/Cargo.toml | 2 +- frame/authority-discovery/src/lib.rs | 4 +- frame/authorship/Cargo.toml | 2 +- frame/authorship/src/lib.rs | 4 +- frame/babe/Cargo.toml | 2 +- frame/babe/src/mock.rs | 4 +- frame/balances/Cargo.toml | 2 +- frame/balances/src/lib.rs | 40 +- frame/balances/src/tests.rs | 51 +- frame/balances/src/tests_composite.rs | 4 +- frame/balances/src/tests_local.rs | 4 +- frame/benchmarking/Cargo.toml | 2 +- frame/benchmarking/src/lib.rs | 11 +- frame/benchmarking/src/tests.rs | 4 +- frame/collective/Cargo.toml | 2 +- frame/collective/src/lib.rs | 24 +- frame/contracts/Cargo.toml | 3 +- frame/contracts/common/Cargo.toml | 2 +- frame/contracts/fixtures/restoration.wat | 6 +- frame/contracts/rpc/Cargo.toml | 2 +- frame/contracts/rpc/runtime-api/Cargo.toml | 2 +- frame/contracts/rpc/src/lib.rs | 28 +- frame/contracts/src/account_db.rs | 450 ------ frame/contracts/src/exec.rs | 495 +++--- frame/contracts/src/lib.rs | 168 +- frame/contracts/src/rent.rs | 91 +- frame/contracts/src/storage.rs | 195 +++ frame/contracts/src/tests.rs | 163 +- frame/contracts/src/wasm/mod.rs | 20 +- frame/contracts/src/wasm/runtime.rs | 40 +- frame/democracy/Cargo.toml | 2 +- frame/democracy/src/benchmarking.rs | 18 +- frame/democracy/src/lib.rs | 20 - frame/democracy/src/tests.rs | 21 +- frame/democracy/src/tests/cancellation.rs | 6 +- frame/democracy/src/tests/migration.rs | 45 - frame/elections-phragmen/Cargo.toml | 2 +- frame/elections-phragmen/src/lib.rs | 21 +- frame/elections/Cargo.toml | 2 +- frame/elections/src/mock.rs | 6 +- frame/elections/src/tests.rs | 4 +- frame/evm/Cargo.toml | 2 +- frame/evm/src/lib.rs | 26 +- frame/example-offchain-worker/Cargo.toml | 2 +- frame/example-offchain-worker/src/tests.rs | 4 +- frame/example/Cargo.toml | 2 +- frame/example/src/lib.rs | 35 +- frame/executive/Cargo.toml | 2 +- frame/executive/src/lib.rs | 3 +- frame/finality-tracker/Cargo.toml | 2 +- frame/finality-tracker/src/lib.rs | 9 +- frame/generic-asset/Cargo.toml | 2 +- frame/generic-asset/src/lib.rs | 4 +- frame/generic-asset/src/mock.rs | 4 +- frame/generic-asset/src/tests.rs | 2 +- frame/grandpa/Cargo.toml | 2 +- frame/grandpa/src/mock.rs | 1 + frame/grandpa/src/tests.rs | 6 +- frame/identity/Cargo.toml | 2 +- frame/identity/src/lib.rs | 19 +- frame/im-online/Cargo.toml | 2 +- frame/im-online/src/benchmarking.rs | 5 +- frame/im-online/src/mock.rs | 4 +- frame/im-online/src/tests.rs | 2 +- frame/indices/Cargo.toml | 2 +- frame/indices/src/benchmarking.rs | 17 +- frame/indices/src/lib.rs | 58 +- frame/indices/src/mock.rs | 4 +- frame/indices/src/tests.rs | 18 +- frame/membership/Cargo.toml | 2 +- frame/membership/src/lib.rs | 30 +- frame/metadata/Cargo.toml | 2 +- frame/multisig/Cargo.toml | 2 +- frame/multisig/src/benchmarking.rs | 176 +- frame/multisig/src/lib.rs | 501 ++++-- frame/multisig/src/tests.rs | 312 +++- frame/nicks/Cargo.toml | 2 +- frame/nicks/src/lib.rs | 14 +- frame/offences/Cargo.toml | 2 +- frame/offences/benchmarking/Cargo.toml | 4 +- frame/offences/benchmarking/src/mock.rs | 1 + frame/offences/src/mock.rs | 4 +- frame/proxy/Cargo.toml | 2 +- frame/proxy/src/lib.rs | 43 +- frame/proxy/src/tests.rs | 51 +- frame/randomness-collective-flip/Cargo.toml | 2 +- frame/randomness-collective-flip/src/lib.rs | 4 +- frame/recovery/Cargo.toml | 2 +- frame/recovery/src/lib.rs | 20 +- frame/recovery/src/mock.rs | 4 +- frame/recovery/src/tests.rs | 2 +- frame/scheduler/src/lib.rs | 173 +- frame/scored-pool/Cargo.toml | 2 +- frame/scored-pool/src/lib.rs | 8 +- frame/scored-pool/src/mock.rs | 4 +- frame/session/Cargo.toml | 8 +- frame/session/benchmarking/Cargo.toml | 2 +- frame/session/benchmarking/src/mock.rs | 1 + .../src/{historical.rs => historical/mod.rs} | 17 +- frame/session/src/historical/offchain.rs | 263 +++ frame/session/src/historical/onchain.rs | 62 + frame/session/src/historical/shared.rs | 39 + frame/session/src/mock.rs | 4 +- frame/society/Cargo.toml | 2 +- frame/society/src/mock.rs | 4 +- frame/society/src/tests.rs | 2 +- frame/staking/Cargo.toml | 2 +- frame/staking/fuzzer/Cargo.toml | 2 +- frame/staking/fuzzer/src/mock.rs | 1 + frame/staking/fuzzer/src/submit_solution.rs | 8 +- frame/staking/src/benchmarking.rs | 10 +- frame/staking/src/lib.rs | 79 +- frame/staking/src/mock.rs | 1 + frame/staking/src/tests.rs | 38 +- frame/sudo/Cargo.toml | 2 +- frame/sudo/src/lib.rs | 34 +- frame/sudo/src/mock.rs | 31 +- frame/support/Cargo.toml | 2 +- .../procedural/src/construct_runtime/mod.rs | 17 + frame/support/src/dispatch.rs | 354 +++- frame/support/src/lib.rs | 123 +- frame/support/src/metadata.rs | 4 + frame/support/src/origin.rs | 177 +- frame/support/src/storage/mod.rs | 27 + frame/support/src/traits.rs | 81 +- frame/support/src/weights.rs | 11 +- frame/support/test/Cargo.toml | 4 +- .../{decl_error.rs => construct_runtime.rs} | 22 +- frame/support/test/tests/decl_module_ui.rs | 26 + ...served_keyword_two_times_integrity_test.rs | 7 + ...ed_keyword_two_times_integrity_test.stderr | 25 + ...eserved_keyword_two_times_on_initialize.rs | 11 + ...ved_keyword_two_times_on_initialize.stderr | 25 + frame/support/test/tests/instance.rs | 2 + frame/support/test/tests/issue2219.rs | 2 + .../support/test/tests/storage_transaction.rs | 159 ++ frame/support/test/tests/system.rs | 4 +- frame/system/Cargo.toml | 2 +- frame/system/benches/bench.rs | 4 +- frame/system/benchmarking/Cargo.toml | 2 +- frame/system/benchmarking/src/mock.rs | 1 + frame/system/rpc/runtime-api/Cargo.toml | 2 +- frame/system/src/lib.rs | 171 +- frame/timestamp/Cargo.toml | 2 +- frame/timestamp/src/lib.rs | 12 +- frame/transaction-payment/Cargo.toml | 4 +- frame/transaction-payment/rpc/Cargo.toml | 2 +- .../rpc/runtime-api/Cargo.toml | 2 +- frame/transaction-payment/src/lib.rs | 237 ++- frame/transaction-payment/src/migration.rs | 3 +- frame/treasury/Cargo.toml | 2 +- frame/treasury/src/lib.rs | 10 +- frame/treasury/src/tests.rs | 31 +- frame/utility/Cargo.toml | 2 +- frame/utility/src/lib.rs | 93 +- frame/utility/src/tests.rs | 28 +- frame/vesting/Cargo.toml | 2 +- frame/vesting/src/lib.rs | 140 +- primitives/api/Cargo.toml | 2 +- .../api/proc-macro/src/decl_runtime_apis.rs | 3 +- .../api/proc-macro/src/impl_runtime_apis.rs | 31 +- primitives/api/src/lib.rs | 5 +- primitives/api/test/Cargo.toml | 2 +- primitives/api/test/tests/runtime_calls.rs | 11 + .../tests/ui/mock_only_one_error_type.stderr | 41 +- primitives/application-crypto/Cargo.toml | 2 +- primitives/arithmetic/Cargo.toml | 2 +- primitives/arithmetic/src/fixed_point.rs | 40 +- primitives/authority-discovery/Cargo.toml | 2 +- primitives/authorship/Cargo.toml | 2 +- primitives/block-builder/Cargo.toml | 2 +- primitives/blockchain/Cargo.toml | 2 +- primitives/consensus/aura/Cargo.toml | 2 +- primitives/consensus/babe/Cargo.toml | 4 +- primitives/consensus/babe/src/lib.rs | 19 + primitives/consensus/common/Cargo.toml | 3 +- .../consensus/common/src/import_queue.rs | 2 +- .../common/src/import_queue/basic_queue.rs | 2 +- .../consensus/common/src/offline_tracker.rs | 3 +- primitives/consensus/pow/Cargo.toml | 2 +- primitives/core/Cargo.toml | 3 +- primitives/core/src/crypto.rs | 10 + primitives/core/src/lib.rs | 13 +- primitives/core/src/offchain/storage.rs | 35 +- primitives/core/src/offchain/testing.rs | 87 +- primitives/core/src/testing.rs | 91 +- primitives/core/src/traits.rs | 53 +- primitives/core/src/vrf.rs | 99 ++ primitives/database/src/lib.rs | 6 + primitives/externalities/Cargo.toml | 2 +- primitives/externalities/src/lib.rs | 23 + primitives/finality-grandpa/Cargo.toml | 2 +- primitives/finality-grandpa/src/lib.rs | 23 +- primitives/finality-tracker/Cargo.toml | 2 +- primitives/inherents/Cargo.toml | 2 +- primitives/io/Cargo.toml | 3 +- primitives/io/src/lib.rs | 92 +- primitives/npos-elections/Cargo.toml | 1 - .../npos-elections/compact/src/assignment.rs | 4 +- primitives/npos-elections/compact/src/lib.rs | 22 +- primitives/npos-elections/src/tests.rs | 10 +- primitives/rpc/src/number.rs | 97 +- primitives/runtime-interface/Cargo.toml | 2 +- primitives/runtime-interface/test/src/lib.rs | 1 - primitives/runtime/Cargo.toml | 4 +- primitives/runtime/src/lib.rs | 2 + .../runtime/src/offchain/storage_lock.rs | 78 + primitives/runtime/src/traits.rs | 2 +- .../runtime/src/transaction_validity.rs | 2 +- primitives/sandbox/Cargo.toml | 2 +- primitives/session/Cargo.toml | 2 +- primitives/staking/Cargo.toml | 2 +- primitives/state-machine/Cargo.toml | 7 +- primitives/state-machine/src/basic.rs | 12 + .../state-machine/src/changes_trie/build.rs | 41 +- primitives/state-machine/src/ext.rs | 48 +- primitives/state-machine/src/lib.rs | 73 +- .../src/overlayed_changes/changeset.rs | 752 +++++++++ .../mod.rs} | 719 +++------ primitives/state-machine/src/read_only.rs | 12 + primitives/state-machine/src/testing.rs | 31 +- primitives/test-primitives/Cargo.toml | 2 +- primitives/timestamp/Cargo.toml | 2 +- primitives/tracing/Cargo.toml | 4 +- primitives/tracing/src/lib.rs | 34 +- primitives/tracing/src/proxy.rs | 165 ++ primitives/transaction-pool/Cargo.toml | 2 +- primitives/transaction-pool/src/pool.rs | 37 +- primitives/trie/Cargo.toml | 8 +- primitives/trie/src/lib.rs | 1 + primitives/utils/Cargo.toml | 1 + primitives/utils/src/lib.rs | 3 +- .../utils}/src/status_sinks.rs | 4 +- primitives/version/Cargo.toml | 2 +- primitives/wasm-interface/Cargo.toml | 2 +- test-utils/client/Cargo.toml | 3 +- test-utils/client/src/lib.rs | 2 +- test-utils/runtime/Cargo.toml | 6 +- test-utils/runtime/client/Cargo.toml | 3 +- test-utils/runtime/client/src/lib.rs | 10 +- test-utils/runtime/src/lib.rs | 22 +- .../runtime/transaction-pool/Cargo.toml | 2 +- utils/browser/src/lib.rs | 14 +- utils/fork-tree/Cargo.toml | 2 +- utils/frame/benchmarking-cli/Cargo.toml | 2 +- utils/frame/benchmarking-cli/src/lib.rs | 2 +- utils/frame/rpc/support/Cargo.toml | 2 +- utils/frame/rpc/system/Cargo.toml | 4 +- utils/frame/rpc/system/src/lib.rs | 182 ++- 421 files changed, 11763 insertions(+), 4823 deletions(-) create mode 100644 .github/workflows/auto-label-prs.yml create mode 100644 .github/workflows/polkadot-companion-labels.yml create mode 100755 .maintain/gitlab/check_labels.sh create mode 100644 .maintain/monitoring/alerting-rules/alerting-rule-tests.yaml create mode 100644 bin/node-template/node/src/lib.rs create mode 100644 bin/node/cli/tests/temp_base_path_works.rs create mode 100644 client/light/Cargo.toml rename client/{service/src/client/light => light/src}/backend.rs (100%) rename client/{service/src/client/light => light/src}/blockchain.rs (97%) rename client/{service/src/client/light => light/src}/call_executor.rs (100%) rename client/{service/src/client/light => light/src}/fetcher.rs (99%) create mode 100644 client/light/src/lib.rs rename client/service/src/client/{light/mod.rs => light.rs} (63%) create mode 100644 client/transaction-pool/graph/src/tracked_map.rs create mode 100644 frame/atomic-swap/Cargo.toml create mode 100644 frame/atomic-swap/src/lib.rs create mode 100644 frame/atomic-swap/src/tests.rs delete mode 100644 frame/contracts/src/account_db.rs create mode 100644 frame/contracts/src/storage.rs delete mode 100644 frame/democracy/src/tests/migration.rs rename frame/session/src/{historical.rs => historical/mod.rs} (97%) create mode 100644 frame/session/src/historical/offchain.rs create mode 100644 frame/session/src/historical/onchain.rs create mode 100644 frame/session/src/historical/shared.rs rename frame/support/test/tests/{decl_error.rs => construct_runtime.rs} (86%) create mode 100644 frame/support/test/tests/decl_module_ui.rs create mode 100644 frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs create mode 100644 frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.stderr create mode 100644 frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs create mode 100644 frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.stderr create mode 100644 frame/support/test/tests/storage_transaction.rs create mode 100644 primitives/core/src/vrf.rs create mode 100644 primitives/state-machine/src/overlayed_changes/changeset.rs rename primitives/state-machine/src/{overlayed_changes.rs => overlayed_changes/mod.rs} (50%) create mode 100644 primitives/tracing/src/proxy.rs rename {client/service => primitives/utils}/src/status_sinks.rs (98%) diff --git a/.github/workflows/auto-label-prs.yml b/.github/workflows/auto-label-prs.yml new file mode 100644 index 0000000000000..f0b8e9b343e29 --- /dev/null +++ b/.github/workflows/auto-label-prs.yml @@ -0,0 +1,21 @@ +name: Label PRs +on: + pull_request: + types: [opened,ready_for_review] + +jobs: + label-new-prs: + runs-on: ubuntu-latest + steps: + - name: Label drafts + uses: andymckay/labeler@master + if: github.event.pull_request.draft == true + with: + add-labels: 'A3-inprogress' + remove-labels: 'A0-pleasereview' + - name: Label PRs + uses: andymckay/labeler@master + if: github.event.pull_request.draft == false && ! contains(github.event.pull_request.labels.*.name, 'A2-insubstantial') + with: + add-labels: 'A0-pleasereview' + remove-labels: 'A3-inprogress' diff --git a/.github/workflows/polkadot-companion-labels.yml b/.github/workflows/polkadot-companion-labels.yml new file mode 100644 index 0000000000000..20aaa98a239cd --- /dev/null +++ b/.github/workflows/polkadot-companion-labels.yml @@ -0,0 +1,31 @@ +name: Check Polkadot Companion and Label + +on: + pull_request: + types: [opened, synchronize] + +jobs: + check_status: + runs-on: ubuntu-latest + steps: + - name: Monitor the status of the gitlab-check-companion-build job + uses: s3krit/await-status-action@v1.0.1 + id: 'check-companion-status' + with: + authToken: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.event.pull_request.head.sha }} + contexts: 'continuous-integration/gitlab-check-polkadot-companion-build' + timeout: 1800 + notPresentTimeout: 3600 # It can take quite a while before the job starts... + failedStates: failure + interruptedStates: error # Error = job was probably cancelled. We don't want to label the PR in that case + - name: Label success + uses: andymckay/labeler@master + if: steps.check-companion-status.outputs.result == 'success' + with: + remove-labels: 'A7-needspolkadotpr' + - name: Label failure + uses: andymckay/labeler@master + if: steps.check-companion-status.outputs.result == 'failure' + with: + add-labels: 'A7-needspolkadotpr' diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bdf614063a7b1..594c9d1dded4f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -56,9 +56,10 @@ default: - kubernetes-parity-build environment: name: parity-build + interruptible: true .docker-env: &docker-env - image: parity/rust-builder:latest + image: paritytech/ci-linux:production before_script: - rustup show - cargo --version @@ -359,6 +360,14 @@ cargo-check-macos: tags: - osx +test-prometheus-alerting-rules: + stage: test + image: paritytech/tools:latest + <<: *kubernetes-build + script: + - promtool check rules .maintain/monitoring/alerting-rules/alerting-rules.yaml + - cat .maintain/monitoring/alerting-rules/alerting-rules.yaml | promtool test rules .maintain/monitoring/alerting-rules/alerting-rule-tests.yaml + #### stage: build check-polkadot-companion-status: @@ -721,3 +730,14 @@ validator 4 4: <<: *validator-deploy script: - ./.maintain/flamingfir-deploy.sh flamingfir-validator4 + +#### stage: .post + +check-labels: + stage: .post + image: paritytech/tools:latest + <<: *kubernetes-build + only: + - /^[0-9]+$/ + script: + - ./.maintain/gitlab/check_labels.sh diff --git a/.maintain/flamingfir-deploy.sh b/.maintain/flamingfir-deploy.sh index 596bb04ece091..8f0fb3a2bc016 100755 --- a/.maintain/flamingfir-deploy.sh +++ b/.maintain/flamingfir-deploy.sh @@ -5,7 +5,7 @@ RETRY_ATTEMPT=0 SLEEP_TIME=15 TARGET_HOST="$1" COMMIT=$(cat artifacts/substrate/VERSION) -DOWNLOAD_URL="https://releases.parity.io/substrate/x86_64-debian:stretch/${COMMIT}/substrate" +DOWNLOAD_URL="https://releases.parity.io/substrate/x86_64-debian:stretch/${COMMIT}/substrate/substrate" POST_DATA='{"extra_vars":{"artifact_path":"'${DOWNLOAD_URL}'","target_host":"'${TARGET_HOST}'"}}' JOB_ID=$(wget -O - --header "Authorization: Bearer ${AWX_TOKEN}" --header "Content-type: application/json" --post-data "${POST_DATA}" https://ansible-awx.parity.io/api/v2/job_templates/32/launch/ | jq .job) diff --git a/.maintain/gitlab/check_labels.sh b/.maintain/gitlab/check_labels.sh new file mode 100755 index 0000000000000..5ab099b38291c --- /dev/null +++ b/.maintain/gitlab/check_labels.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +#shellcheck source=lib.sh +source "$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/lib.sh" + +ensure_labels() { + for label in "$@"; do + if has_label 'paritytech/substrate' "$CI_COMMIT_BRANCH" "$label"; then + return 0 + fi + done + return 1 +} + +# Must have one of the following labels +releasenotes_labels=( + 'B0-silent' + 'B3-apinoteworthy' + 'B5-clientnoteworthy' + 'B7-runtimenoteworthy' +) + +criticality_labels=( + 'C1-low' + 'C3-medium' + 'C7-high' + 'C9-critical' +) + +echo "[+] Checking release notes (B) labels for $CI_COMMIT_BRANCH" +if ensure_labels "${releasenotes_labels[@]}"; then + echo "[+] Release notes label detected. All is well." +else + echo "[!] Release notes label not detected. Please add one of: ${releasenotes_labels[*]}" + exit 1 +fi + +echo "[+] Checking release criticality (C) labels for $CI_COMMIT_BRANCH" +if ensure_labels "${criticality_labels[@]}"; then + echo "[+] Release criticality label detected. All is well." +else + echo "[!] Release criticality label not detected. Please add one of: ${criticality_labels[*]}" + exit 1 +fi + +exit 0 diff --git a/.maintain/gitlab/check_line_width.sh b/.maintain/gitlab/check_line_width.sh index 85092260b6a64..611d3ae2681e2 100755 --- a/.maintain/gitlab/check_line_width.sh +++ b/.maintain/gitlab/check_line_width.sh @@ -10,14 +10,15 @@ BASE_BRANCH_NAME="master" LINE_WIDTH="120" GOOD_LINE_WIDTH="100" BASE_BRANCH="${BASE_ORIGIN}/${BASE_BRANCH_NAME}" +git fetch ${BASE_ORIGIN} ${BASE_BRANCH_NAME} --depth 100 +BASE_HASH=$(git merge-base ${BASE_BRANCH} HEAD) -git fetch ${BASE_ORIGIN} ${BASE_BRANCH_NAME} --depth 1 -git diff --name-only ${BASE_BRANCH} -- \*.rs | ( while read file +git diff --name-only ${BASE_HASH} -- \*.rs | ( while read file do if [ ! -f ${file} ]; then echo "Skipping removed file." - elif git diff ${BASE_BRANCH} -- ${file} | grep -q "^+.\{$(( $LINE_WIDTH + 1 ))\}" + elif git diff ${BASE_HASH} -- ${file} | grep -q "^+.\{$(( $LINE_WIDTH + 1 ))\}" then if [ -z "${FAIL}" ] then @@ -29,11 +30,11 @@ do FAIL="true" fi echo "| file: ${file}" - git diff ${BASE_BRANCH} -- ${file} \ + git diff ${BASE_HASH} -- ${file} \ | grep -n "^+.\{$(( $LINE_WIDTH + 1))\}" echo "|" else - if git diff ${BASE_BRANCH} -- ${file} | grep -q "^+.\{$(( $GOOD_LINE_WIDTH + 1 ))\}" + if git diff ${BASE_HASH} -- ${file} | grep -q "^+.\{$(( $GOOD_LINE_WIDTH + 1 ))\}" then if [ -z "${FAIL}" ] then @@ -44,7 +45,7 @@ do echo "|" fi echo "| file: ${file}" - git diff ${BASE_BRANCH} -- ${file} | grep -n "^+.\{$(( $GOOD_LINE_WIDTH + 1 ))\}" + git diff ${BASE_HASH} -- ${file} | grep -n "^+.\{$(( $GOOD_LINE_WIDTH + 1 ))\}" echo "|" fi fi diff --git a/.maintain/gitlab/check_runtime.sh b/.maintain/gitlab/check_runtime.sh index 5b7e25e3afc4e..6d009c5aafc6a 100755 --- a/.maintain/gitlab/check_runtime.sh +++ b/.maintain/gitlab/check_runtime.sh @@ -67,7 +67,7 @@ sub_spec_version="$(git diff tags/release...${CI_COMMIT_SHA} ${VERSIONS_FILE} \ if [ "${add_spec_version}" != "${sub_spec_version}" ] then - github_label "B2-breaksapi" + github_label "D2-breaksapi" boldcat <<-EOT diff --git a/.maintain/gitlab/lib.sh b/.maintain/gitlab/lib.sh index ecc9a5f54288c..a7a83baaea746 100755 --- a/.maintain/gitlab/lib.sh +++ b/.maintain/gitlab/lib.sh @@ -66,8 +66,12 @@ has_label(){ repo="$1" pr_id="$2" label="$3" - out=$(curl -H "Authorization: token $GITHUB_RELEASE_TOKEN" -s "$api_base/$repo/pulls/$pr_id") - [ -n "$(echo "$out" | jq ".labels | .[] | select(.name==\"$label\")")" ] + if [ -n "$GITHUB_RELEASE_TOKEN" ]; then + out=$(curl -H "Authorization: token $GITHUB_RELEASE_TOKEN" -s "$api_base/$repo/pulls/$pr_id") + else + out=$(curl -H "Authorization: token $GITHUB_PR_TOKEN" -s "$api_base/$repo/pulls/$pr_id") + fi + [ -n "$(echo "$out" | tr -d '\r\n' | jq ".labels | .[] | select(.name==\"$label\")")" ] } # Formats a message into a JSON string for posting to Matrix diff --git a/.maintain/monitoring/alerting-rules/alerting-rule-tests.yaml b/.maintain/monitoring/alerting-rules/alerting-rule-tests.yaml new file mode 100644 index 0000000000000..069cfaf977b50 --- /dev/null +++ b/.maintain/monitoring/alerting-rules/alerting-rule-tests.yaml @@ -0,0 +1,239 @@ +rule_files: + - /dev/stdin + +evaluation_interval: 1m + +tests: + - interval: 1m + input_series: + - series: 'polkadot_sub_libp2p_peers_count{ + job="polkadot", + pod="polkadot-abcdef01234-abcdef", + instance="polkadot-abcdef01234-abcdef", + }' + values: '3 2+0x4 1+0x9' # 3 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + + - series: 'polkadot_sub_txpool_validations_scheduled{ + job="polkadot", + pod="polkadot-abcdef01234-abcdef", + instance="polkadot-abcdef01234-abcdef", + }' + values: '10+1x30' # 10 11 12 13 .. 40 + + - series: 'polkadot_sub_txpool_validations_finished{ + job="polkadot", + pod="polkadot-abcdef01234-abcdef", + instance="polkadot-abcdef01234-abcdef", + }' + values: '0x30' # 0 0 0 0 .. 0 + + - series: 'polkadot_block_height{ + status="best", job="polkadot", + pod="polkadot-abcdef01234-abcdef", + instance="polkadot-abcdef01234-abcdef", + }' + values: '1+1x3 4+0x13' # 1 2 3 4 4 4 4 4 4 4 4 4 ... + + - series: 'polkadot_block_height{ + status="finalized", + job="polkadot", + pod="polkadot-abcdef01234-abcdef", + instance="polkadot-abcdef01234-abcdef", + }' + values: '1+1x3 4+0x13' # 1 2 3 4 4 4 4 4 4 4 4 4 ... + + - series: 'polkadot_cpu_usage_percentage{ + job="polkadot", + pod="polkadot-abcdef01234-abcdef", + instance="polkadot-abcdef01234-abcdef", + }' + values: '0+20x5 100+0x5' # 0 20 40 60 80 100 100 100 100 100 100 + + alert_rule_test: + + ###################################################################### + # Resource usage + ###################################################################### + + - eval_time: 9m + alertname: HighCPUUsage + exp_alerts: + - eval_time: 10m + alertname: HighCPUUsage + exp_alerts: + - exp_labels: + severity: warning + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + exp_annotations: + message: "The node polkadot-abcdef01234-abcdef has a CPU + usage higher than 100% for more than 5 minutes" + + ###################################################################### + # Block production + ###################################################################### + + - eval_time: 6m + alertname: LowNumberOfNewBlocks + exp_alerts: + - eval_time: 7m + alertname: LowNumberOfNewBlocks + exp_alerts: + - exp_labels: + severity: warning + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + status: best + exp_annotations: + message: "Less than one new block per minute on instance + polkadot-abcdef01234-abcdef." + + - eval_time: 14m + alertname: LowNumberOfNewBlocks + exp_alerts: + - exp_labels: + severity: warning + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + status: best + exp_annotations: + message: "Less than one new block per minute on instance + polkadot-abcdef01234-abcdef." + - exp_labels: + severity: critical + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + status: best + exp_annotations: + message: "Less than one new block per minute on instance + polkadot-abcdef01234-abcdef." + + ###################################################################### + # Block finalization + ###################################################################### + + - eval_time: 6m + alertname: BlockFinalizationSlow + exp_alerts: + - eval_time: 7m + alertname: BlockFinalizationSlow + exp_alerts: + - exp_labels: + severity: warning + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + status: finalized + exp_annotations: + message: "Finalized block on instance + polkadot-abcdef01234-abcdef increases by less than 1 per + minute." + + - eval_time: 14m + alertname: BlockFinalizationSlow + exp_alerts: + - exp_labels: + severity: warning + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + status: finalized + exp_annotations: + message: "Finalized block on instance + polkadot-abcdef01234-abcdef increases by less than 1 per + minute." + - exp_labels: + severity: critical + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + status: finalized + exp_annotations: + message: "Finalized block on instance + polkadot-abcdef01234-abcdef increases by less than 1 per + minute." + + ###################################################################### + # Transaction queue + ###################################################################### + + - eval_time: 10m + alertname: TransactionQueueSize + exp_alerts: + - eval_time: 11m + alertname: TransactionQueueSize + exp_alerts: + - exp_labels: + severity: warning + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + exp_annotations: + message: "The node polkadot-abcdef01234-abcdef has more + than 10 transactions in the queue for more than 10 + minutes" + + - eval_time: 31m + alertname: TransactionQueueSize + exp_alerts: + - exp_labels: + severity: warning + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + exp_annotations: + message: "The node polkadot-abcdef01234-abcdef has more + than 10 transactions in the queue for more than 10 + minutes" + - exp_labels: + severity: critical + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + exp_annotations: + message: "The node polkadot-abcdef01234-abcdef has more + than 10 transactions in the queue for more than 30 + minutes" + + ###################################################################### + # Networking + ###################################################################### + + - eval_time: 3m # Values: 3 2 2 + alertname: LowNumberOfPeers + exp_alerts: + - eval_time: 4m # Values: 2 2 2 + alertname: LowNumberOfPeers + exp_alerts: + - exp_labels: + severity: warning + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + exp_annotations: + message: "The node polkadot-abcdef01234-abcdef has less + than 3 peers for more than 3 minutes" + + - eval_time: 16m # Values: 3 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 + alertname: LowNumberOfPeers + exp_alerts: + - exp_labels: + severity: warning + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + exp_annotations: + message: "The node polkadot-abcdef01234-abcdef has less + than 3 peers for more than 3 minutes" + - exp_labels: + severity: critical + pod: polkadot-abcdef01234-abcdef + instance: polkadot-abcdef01234-abcdef + job: polkadot + exp_annotations: + message: "The node polkadot-abcdef01234-abcdef has less + than 3 peers for more than 15 minutes" diff --git a/.maintain/monitoring/alerting-rules/alerting-rules.yaml b/.maintain/monitoring/alerting-rules/alerting-rules.yaml index cb5b3c271dd14..06d204f7afa41 100644 --- a/.maintain/monitoring/alerting-rules/alerting-rules.yaml +++ b/.maintain/monitoring/alerting-rules/alerting-rules.yaml @@ -12,7 +12,8 @@ groups: labels: severity: warning annotations: - message: 'The node {{ $labels.instance }} has a CPU usage higher than 100% for more than 5 minutes' + message: 'The node {{ $labels.instance }} has a CPU usage higher than 100% + for more than 5 minutes' ############################################################################## # Block production @@ -20,14 +21,16 @@ groups: - alert: LowNumberOfNewBlocks annotations: - message: 'Less than one new block per minute on instance {{ $labels.instance }}.' + message: 'Less than one new block per minute on instance {{ + $labels.instance }}.' expr: increase(polkadot_block_height{status="best"}[1m]) < 1 for: 3m labels: severity: warning - alert: LowNumberOfNewBlocks annotations: - message: 'Less than one new block per minute on instance {{ $labels.instance }}.' + message: 'Less than one new block per minute on instance {{ + $labels.instance }}.' expr: increase(polkadot_block_height{status="best"}[1m]) < 1 for: 10m labels: @@ -43,43 +46,51 @@ groups: labels: severity: warning annotations: - message: 'Finalized block on instance {{ $labels.instance }} increases by less than 1 per minute.' + message: 'Finalized block on instance {{ $labels.instance }} increases by + less than 1 per minute.' - alert: BlockFinalizationSlow expr: increase(polkadot_block_height{status="finalized"}[1m]) < 1 for: 10m labels: severity: critical annotations: - message: 'Finalized block on instance {{ $labels.instance }} increases by less than 1 per minute.' + message: 'Finalized block on instance {{ $labels.instance }} increases by + less than 1 per minute.' - alert: BlockFinalizationLaggingBehind # Under the assumption of an average block production of 6 seconds, # "best" and "finalized" being more than 10 blocks apart would imply # more than a 1 minute delay between block production and finalization. - expr: (polkadot_block_height_number{status="best"} - ignoring(status) polkadot_block_height_number{status="finalized"}) > 10 + expr: '(polkadot_block_height_number{status="best"} - ignoring(status) + polkadot_block_height_number{status="finalized"}) > 10' for: 8m labels: severity: critical annotations: - message: "Block finalization on instance {{ $labels.instance }} is behind block production by {{ $value }} for more than 8m" + message: "Block finalization on instance {{ $labels.instance }} is behind + block production by {{ $value }} for more than 8m" ############################################################################## # Transaction queue ############################################################################## - alert: TransactionQueueSize - expr: polkadot_sub_txpool_validations_scheduled - polkadot_sub_txpool_validations_finished > 10 + expr: 'polkadot_sub_txpool_validations_scheduled - + polkadot_sub_txpool_validations_finished > 10' for: 10m labels: severity: warning annotations: - message: 'The node {{ $labels.instance }} has more than 10 transactions in the queue for more than 10 minutes' + message: 'The node {{ $labels.instance }} has more than 10 transactions in + the queue for more than 10 minutes' - alert: TransactionQueueSize - expr: polkadot_sub_txpool_validations_scheduled - polkadot_sub_txpool_validations_finished > 10 + expr: 'polkadot_sub_txpool_validations_scheduled - + polkadot_sub_txpool_validations_finished > 10' for: 30m labels: severity: critical annotations: - message: 'The node {{ $labels.instance }} has more than 10 transactions in the queue for more than 30 minutes' + message: 'The node {{ $labels.instance }} has more than 10 transactions in + the queue for more than 30 minutes' ############################################################################## # Networking @@ -91,23 +102,28 @@ groups: labels: severity: warning annotations: - message: 'The node {{ $labels.instance }} has less than 3 peers for more than 3 minutes' + message: 'The node {{ $labels.instance }} has less than 3 peers for more + than 3 minutes' - alert: LowNumberOfPeers expr: polkadot_sub_libp2p_peers_count < 3 for: 15m labels: severity: critical annotations: - message: 'The node {{ $labels.instance }} has less than 3 peers for more than 15 minutes' + message: 'The node {{ $labels.instance }} has less than 3 peers for more + than 15 minutes' ############################################################################## # Others ############################################################################## - alert: AuthorityDiscoveryHighDiscoveryFailure - expr: polkadot_authority_discovery_handle_value_found_event_failure / ignoring(name) polkadot_authority_discovery_dht_event_received{name="value_found"} > 0.5 + expr: 'polkadot_authority_discovery_handle_value_found_event_failure / + ignoring(name) + polkadot_authority_discovery_dht_event_received{name="value_found"} > 0.5' for: 2h labels: severity: warning annotations: - message: "Authority discovery on node {{ $labels.instance }} fails to process more than 50 % of the values found on the DHT." + message: "Authority discovery on node {{ $labels.instance }} fails to + process more than 50 % of the values found on the DHT." diff --git a/.maintain/sentry-node/docker-compose.yml b/.maintain/sentry-node/docker-compose.yml index 376538dde5786..235f2c49630a1 100644 --- a/.maintain/sentry-node/docker-compose.yml +++ b/.maintain/sentry-node/docker-compose.yml @@ -47,9 +47,9 @@ services: - "--validator" - "--alice" - "--sentry-nodes" - - "/dns4/sentry-a/tcp/30333/p2p/QmV7EhW6J6KgmNdr558RH1mPx2xGGznW7At4BhXzntRFsi" + - "/dns/sentry-a/tcp/30333/p2p/QmV7EhW6J6KgmNdr558RH1mPx2xGGznW7At4BhXzntRFsi" - "--reserved-nodes" - - "/dns4/sentry-a/tcp/30333/p2p/QmV7EhW6J6KgmNdr558RH1mPx2xGGznW7At4BhXzntRFsi" + - "/dns/sentry-a/tcp/30333/p2p/QmV7EhW6J6KgmNdr558RH1mPx2xGGznW7At4BhXzntRFsi" # Not only bind to localhost. - "--unsafe-ws-external" - "--unsafe-rpc-external" @@ -83,11 +83,11 @@ services: - "--port" - "30333" - "--sentry" - - "/dns4/validator-a/tcp/30333/p2p/QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR" + - "/dns/validator-a/tcp/30333/p2p/QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR" - "--reserved-nodes" - - "/dns4/validator-a/tcp/30333/p2p/QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR" + - "/dns/validator-a/tcp/30333/p2p/QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR" - "--bootnodes" - - "/dns4/validator-b/tcp/30333/p2p/QmSVnNf9HwVMT1Y4cK1P6aoJcEZjmoTXpjKBmAABLMnZEk" + - "/dns/validator-b/tcp/30333/p2p/QmSVnNf9HwVMT1Y4cK1P6aoJcEZjmoTXpjKBmAABLMnZEk" - "--no-telemetry" - "--rpc-cors" - "all" @@ -118,9 +118,9 @@ services: - "--validator" - "--bob" - "--bootnodes" - - "/dns4/validator-a/tcp/30333/p2p/QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR" + - "/dns/validator-a/tcp/30333/p2p/QmRpheLN4JWdAnY7HGJfWFNbfkQCb6tFf4vvA6hgjMZKrR" - "--bootnodes" - - "/dns4/sentry-a/tcp/30333/p2p/QmV7EhW6J6KgmNdr558RH1mPx2xGGznW7At4BhXzntRFsi" + - "/dns/sentry-a/tcp/30333/p2p/QmV7EhW6J6KgmNdr558RH1mPx2xGGznW7At4BhXzntRFsi" - "--no-telemetry" - "--rpc-cors" - "all" diff --git a/Cargo.lock b/Cargo.lock index 97ef32787a098..4abcbe7f92bde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,11 +10,40 @@ dependencies = [ "regex", ] +[[package]] +name = "addr2line" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602d785912f476e480434627e8732e6766b760c045bbf897d9dfaa9f4fbd399c" +dependencies = [ + "gimli 0.21.0", +] + [[package]] name = "adler32" -version = "1.0.4" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d" + +[[package]] +name = "aead" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf01b9b56e767bb57b94ebf91a58b338002963785cdd7013e21c0d4679471e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" +checksum = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" +dependencies = [ + "aes-soft", + "aesni", + "block-cipher-trait", +] [[package]] name = "aes-ctr" @@ -28,6 +57,20 @@ dependencies = [ "stream-cipher", ] +[[package]] +name = "aes-gcm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "834a6bda386024dbb7c8fc51322856c10ffe69559f972261c868485f5759c638" +dependencies = [ + "aead", + "aes", + "block-cipher-trait", + "ghash", + "subtle 2.2.3", + "zeroize", +] + [[package]] name = "aes-soft" version = "0.3.3" @@ -61,9 +104,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.10" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" dependencies = [ "memchr", ] @@ -76,7 +119,7 @@ checksum = "4f823d037a7ec6ea2197046bafd4ae150e6bc36f9ca347404f46a46823fa84f2" dependencies = [ "approx", "num-complex", - "num-traits 0.2.11", + "num-traits 0.2.12", ] [[package]] @@ -99,9 +142,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.28" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" [[package]] name = "approx" @@ -109,20 +152,20 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" dependencies = [ - "num-traits 0.2.11", + "num-traits 0.2.12", ] [[package]] name = "arbitrary" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5eb01a9ab8a3369f2f7632b9461c34f5920bd454774bab5b9fc6744f21d6143" +checksum = "7cb544f1057eaaff4b34f8c4dcf56fc3cd04debd291998405d135017a7c3c0f4" [[package]] name = "arc-swap" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62" +checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" [[package]] name = "arrayref" @@ -160,8 +203,8 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d0864d84b8e07b145449be9a8537db86bf9de5ce03b913214694643b4743502" dependencies = [ - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -225,7 +268,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95fd83426b89b034bf4e9ceb9c533c2f2386b813fd3dcae0a425ec6f1837d78a" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "rustls", "webpki", "webpki-roots 0.19.0", @@ -256,26 +299,18 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.46" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" +checksum = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c" dependencies = [ - "backtrace-sys", + "addr2line", "cfg-if", "libc", + "miniz_oxide", + "object 0.20.0", "rustc-demangle", ] -[[package]] -name = "backtrace-sys" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fbebbe1c9d1f383a9cc7e8ccdb471b91c8d024ee9c2ca5b5346121fe8b4399" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "base58" version = "0.1.0" @@ -290,15 +325,15 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "base64" -version = "0.12.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3" +checksum = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67" [[package]] name = "bincode" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" +checksum = "1596f02a1f089e295623489ec1f9bb3c1e4795b3b9a0d97931c340a517634381" dependencies = [ "byteorder", "serde", @@ -306,22 +341,22 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb26d6a69a335b8cb0e7c7e9775cd5666611dc50a37177c3f2cedcfc040e8c8" +checksum = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5" dependencies = [ "bitflags", "cexpr", "cfg-if", "clang-sys", "clap", - "env_logger 0.7.1", + "env_logger", "lazy_static", "lazycell", "log", "peeking_take_while", "proc-macro2", - "quote 1.0.6", + "quote 1.0.7", "regex", "rustc-hash", "shlex", @@ -446,9 +481,9 @@ checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" [[package]] name = "bstr" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41" +checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" dependencies = [ "lazy_static", "memchr", @@ -467,9 +502,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.2.1" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" [[package]] name = "byte-slice-cast" @@ -502,9 +537,12 @@ dependencies = [ [[package]] name = "bytes" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" +checksum = "118cf036fbb97d0816e3c34b2d7a1e8cfc60f68fcf63d550ddbe9bd5f59c213b" +dependencies = [ + "loom", +] [[package]] name = "c_linked_list" @@ -535,9 +573,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.52" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" +checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" dependencies = [ "jobserver", ] @@ -558,12 +596,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] -name = "chacha20-poly1305-aead" -version = "0.1.2" +name = "chacha20" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77d2058ba29594f69c75e8a9018e0485e3914ca5084e3613cd64529042f5423b" +checksum = "f6a7ae4c498f8447d86baef0fa0831909333f558866fabcb21600625ac5a31c7" dependencies = [ - "constant_time_eq", + "stream-cipher", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48901293601228db2131606f741db33561f7576b5d19c99cd66222380a7dc863" +dependencies = [ + "aead", + "chacha20", + "poly1305", + "stream-cipher", + "zeroize", ] [[package]] @@ -587,7 +639,7 @@ checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" dependencies = [ "js-sys", "num-integer", - "num-traits 0.2.11", + "num-traits 0.2.12", "time", "wasm-bindgen", ] @@ -605,9 +657,9 @@ dependencies = [ [[package]] name = "clap" -version = "2.33.0" +version = "2.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" dependencies = [ "ansi_term 0.11.0", "atty", @@ -620,9 +672,9 @@ dependencies = [ [[package]] name = "clear_on_drop" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" +checksum = "c9cc5db465b294c3fa986d5bbb0f3017cd850bff6dd6c52f9ccff8b4d21b7b08" dependencies = [ "cc", ] @@ -638,9 +690,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.42" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" +checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb" dependencies = [ "cc", ] @@ -727,7 +779,7 @@ dependencies = [ "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-entity", - "gimli", + "gimli 0.20.0", "log", "regalloc", "serde", @@ -822,7 +874,7 @@ dependencies = [ "itertools 0.8.2", "lazy_static", "libc", - "num-traits 0.2.11", + "num-traits 0.2.12", "rand_core 0.3.1", "rand_os", "rand_xoshiro", @@ -848,7 +900,7 @@ dependencies = [ "csv", "itertools 0.9.0", "lazy_static", - "num-traits 0.2.11", + "num-traits 0.2.12", "oorandom", "plotters", "rayon", @@ -919,12 +971,13 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" dependencies = [ "cfg-if", "crossbeam-utils", + "maybe-uninit", ] [[package]] @@ -987,12 +1040,12 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf6b25ee9ac1995c54d7adb2eff8cfffb7260bc774fb63c601ec65467f43cd9d" +checksum = "39858aa5bac06462d4dd4b9164848eb81ffc4aa5c479746393598fd193afa227" dependencies = [ - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -1007,32 +1060,32 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" +checksum = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5" dependencies = [ "byteorder", "digest", "rand_core 0.5.1", - "subtle 2.2.2", + "subtle 2.2.3", "zeroize", ] [[package]] name = "data-encoding" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c0346158a19b3627234e15596f5e465c360fcdb97d817bcb255e0510f5a788" +checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" [[package]] name = "derive_more" -version = "0.99.5" +version = "0.99.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7" +checksum = "bc655351f820d774679da6cdc23355a93de496867d8203496675162e17b1d671" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -1062,11 +1115,10 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" dependencies = [ - "cfg-if", "libc", "redox_users", "winapi 0.3.8", @@ -1133,21 +1185,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", -] - -[[package]] -name = "env_logger" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -1171,9 +1210,9 @@ checksum = "516aa8d7a71cb00a1c4146f0798549b93d083d4f189b3ced8f3de6b8f11ee6c4" [[package]] name = "erased-serde" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d88b6d1705e16a4d62e05ea61cc0496c2bd190f4fa8e5c1f11ce747be6bcf3d1" +checksum = "6ca8b296792113e1500fd935ae487be6e00ce318952a6880555554824d6ebf38" dependencies = [ "serde", ] @@ -1251,7 +1290,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", ] [[package]] @@ -1286,8 +1325,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", "synstructure", ] @@ -1314,11 +1353,11 @@ dependencies = [ [[package]] name = "file-per-thread-logger" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505b75b31ef7285168dd237c4a7db3c1f3e0927e7d314e670bc98e854272fe9" +checksum = "8b3937f028664bd0e13df401ba49a4567ccda587420365823242977f06609ed1" dependencies = [ - "env_logger 0.6.2", + "env_logger", "log", ] @@ -1329,10 +1368,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8feb87a63249689640ac9c011742c33139204e3c134293d3054022276869133b" dependencies = [ "either", - "futures 0.3.4", + "futures 0.3.5", "futures-timer 2.0.2", "log", - "num-traits 0.2.11", + "num-traits 0.2.12", "parity-scale-codec", "parking_lot 0.9.0", "rand 0.6.5", @@ -1371,9 +1410,9 @@ dependencies = [ [[package]] name = "fnv" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" @@ -1477,8 +1516,8 @@ version = "2.0.0-rc3" dependencies = [ "frame-support-procedural-tools", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -1488,8 +1527,8 @@ dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -1497,8 +1536,8 @@ name = "frame-support-procedural-tools-derive" version = "2.0.0-rc3" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -1515,6 +1554,7 @@ dependencies = [ "sp-io", "sp-runtime", "sp-state-machine", + "sp-std", "trybuild", ] @@ -1617,9 +1657,9 @@ checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" [[package]] name = "futures" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" +checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" dependencies = [ "futures-channel", "futures-core", @@ -1632,9 +1672,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" dependencies = [ "futures-core", "futures-sink", @@ -1651,9 +1691,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" [[package]] name = "futures-core-preview" @@ -1678,7 +1718,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdcef58a173af8148b182684c9f2d5250875adbcaff7b5794073894f9d8634a9" dependencies = [ "futures 0.1.29", - "futures 0.3.4", + "futures 0.3.5", "lazy_static", "log", "parking_lot 0.9.0", @@ -1689,9 +1729,9 @@ dependencies = [ [[package]] name = "futures-executor" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" +checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" dependencies = [ "futures-core", "futures-task", @@ -1701,33 +1741,36 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" [[package]] name = "futures-macro" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" +checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" dependencies = [ "proc-macro-hack", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] name = "futures-sink" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" [[package]] name = "futures-task" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +dependencies = [ + "once_cell", +] [[package]] name = "futures-timer" @@ -1747,9 +1790,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" dependencies = [ "futures 0.1.29", "futures-channel", @@ -1759,6 +1802,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", + "pin-project", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -1783,8 +1827,20 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0a73299e4718f5452e45980fc1d6957a070abe308d3700b63b8673f47e1c2b3" dependencies = [ - "bytes 0.5.4", - "futures 0.3.4", + "bytes 0.5.5", + "futures 0.3.5", + "memchr", + "pin-project", +] + +[[package]] +name = "futures_codec" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce54d63f8b0c75023ed920d46fd71d0cbbb830b0ee012726b5b4f506fb6dea5b" +dependencies = [ + "bytes 0.5.5", + "futures 0.3.5", "memchr", "pin-project", ] @@ -1795,6 +1851,19 @@ version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" +[[package]] +name = "generator" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add72f17bb81521258fcc8a7a3245b1e184e916bfbe34f0ea89558f440df5c68" +dependencies = [ + "cc", + "libc", + "log", + "rustc_version", + "winapi 0.3.8", +] + [[package]] name = "generic-array" version = "0.12.3" @@ -1838,6 +1907,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f0930ed19a7184089ea46d2fedead2f6dc2b674c5db4276b7da336c7cd83252" +dependencies = [ + "polyval", +] + [[package]] name = "gimli" version = "0.20.0" @@ -1852,6 +1930,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" + [[package]] name = "glob" version = "0.2.11" @@ -1925,7 +2009,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "fnv", "futures-core", "futures-sink", @@ -1934,7 +2018,7 @@ dependencies = [ "indexmap", "log", "slab", - "tokio 0.2.20", + "tokio 0.2.21", "tokio-util", ] @@ -1974,9 +2058,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" +checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909" dependencies = [ "libc", ] @@ -1999,9 +2083,9 @@ dependencies = [ [[package]] name = "hex-literal-impl" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d" +checksum = "853f769599eb31de176303197b7ba4973299c38c7a7604a6bc88c3eef05b9b46" dependencies = [ "proc-macro-hack", ] @@ -2055,7 +2139,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "fnv", "itoa", ] @@ -2078,7 +2162,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "http 0.2.1", ] @@ -2129,11 +2213,11 @@ dependencies = [ [[package]] name = "hyper" -version = "0.13.5" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96816e1d921eca64d208a85aab4f7798455a8e34229ee5a88c935bdee1b78b14" +checksum = "a6e7655b9594024ad0ee439f3b5a7299369dc2a3f459b47c696f9ff676f9aa1f" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "futures-channel", "futures-core", "futures-util", @@ -2143,10 +2227,10 @@ dependencies = [ "httparse", "itoa", "log", - "net2", "pin-project", + "socket2", "time", - "tokio 0.2.20", + "tokio 0.2.21", "tower-service", "want 0.3.0", ] @@ -2157,14 +2241,14 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac965ea399ec3a25ac7d13b8affd4b8f39325cca00858ddf5eb29b79e6b14b08" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "ct-logs", "futures-util", - "hyper 0.13.5", + "hyper 0.13.6", "log", "rustls", "rustls-native-certs", - "tokio 0.2.20", + "tokio 0.2.21", "tokio-rustls", "webpki", ] @@ -2234,15 +2318,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] name = "indexmap" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" +checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" dependencies = [ "autocfg 1.0.0", ] @@ -2259,7 +2343,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64fa110ec7b8f493f416eed552740d10e7030ad5f63b2308f82c9608ec2df275" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "futures-timer 2.0.2", ] @@ -2304,9 +2388,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "jobserver" @@ -2373,8 +2457,8 @@ checksum = "0fadf6945e227246825a583514534d864554e9f23d80b3c77d034b10983db5ef" dependencies = [ "proc-macro-crate", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -2392,6 +2476,20 @@ dependencies = [ "unicase", ] +[[package]] +name = "jsonrpc-ipc-server" +version = "14.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dedccd693325d833963b549e959137f30a7a0ea650cde92feda81dc0c1393cb5" +dependencies = [ + "jsonrpc-core", + "jsonrpc-server-utils", + "log", + "parity-tokio-ipc", + "parking_lot 0.10.2", + "tokio-service", +] + [[package]] name = "jsonrpc-pubsub" version = "14.2.0" @@ -2464,9 +2562,9 @@ dependencies = [ [[package]] name = "kv-log-macro" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" +checksum = "4ff57d6d215f7ca7eb35a9a64d656ba4d9d2bef114d741dc08048e75e2f5d418" dependencies = [ "log", ] @@ -2516,7 +2614,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c7f36acb1841d4c701d30ae1f2cfd242e805991443f75f6935479ed3de64903" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "js-sys", "kvdb", "kvdb-memorydb", @@ -2547,22 +2645,28 @@ checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" [[package]] name = "libc" -version = "0.2.69" +version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" [[package]] name = "libflate" -version = "0.1.27" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9135df43b1f5d0e333385cb6e7897ecd1a43d7d11b91ac003f4d2c2d2401fdd" +checksum = "784f4ec5908a9d7f4e53658906386667e8b02e9389a47cfebf45d324ba9e8d25" dependencies = [ "adler32", "crc32fast", + "libflate_lz77", "rle-decode-fast", - "take_mut", ] +[[package]] +name = "libflate_lz77" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b" + [[package]] name = "libloading" version = "0.5.2" @@ -2585,8 +2689,8 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "057eba5432d3e740e313c6e13c9153d0cb76b4f71bfc2e5242ae5bdb7d41af67" dependencies = [ - "bytes 0.5.4", - "futures 0.3.4", + "bytes 0.5.5", + "futures 0.3.5", "lazy_static", "libp2p-core", "libp2p-core-derive", @@ -2604,7 +2708,7 @@ dependencies = [ "libp2p-websocket", "libp2p-yamux", "multihash", - "parity-multiaddr 0.9.0", + "parity-multiaddr 0.9.1", "parking_lot 0.10.2", "pin-project", "smallvec 1.4.0", @@ -2613,23 +2717,23 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.19.0" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80a6000296bdbff540b6c00ef82108ef23aa68d195b9333823ea491562c338d7" +checksum = "3a0387b930c3d4c2533dc4893c1e0394185ddcc019846121b1b27491e45a2c08" dependencies = [ "asn1_der", "bs58", "ed25519-dalek", "either", "fnv", - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", "lazy_static", "libsecp256k1", "log", "multihash", "multistream-select", - "parity-multiaddr 0.9.0", + "parity-multiaddr 0.9.1", "parking_lot 0.10.2", "pin-project", "prost", @@ -2640,7 +2744,7 @@ dependencies = [ "sha2", "smallvec 1.4.0", "thiserror", - "unsigned-varint", + "unsigned-varint 0.4.0", "void", "zeroize", ] @@ -2651,8 +2755,8 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f09548626b737ed64080fde595e06ce1117795b8b9fc4d2629fa36561c583171" dependencies = [ - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -2661,18 +2765,18 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cc186d9a941fd0207cf8f08ef225a735e2d7296258f570155e525f6ee732f87" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "libp2p-core", "log", ] [[package]] name = "libp2p-identify" -version = "0.19.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6438ed8ca240c7635c9caa3be6c5258bc0058553ae97ba81737f04e5d33804f5" +checksum = "62f76075b170d908bae616f550ade410d9d27c013fa69042551dbfc757c7c094" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "libp2p-core", "libp2p-swarm", "log", @@ -2689,11 +2793,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d6c1d5100973527ae70d82687465b17049c1b717a7964de38b8e65000878ff" dependencies = [ "arrayvec 0.5.1", - "bytes 0.5.4", + "bytes 0.5.5", "either", "fnv", - "futures 0.3.4", - "futures_codec", + "futures 0.3.5", + "futures_codec 0.3.4", "libp2p-core", "libp2p-swarm", "log", @@ -2704,22 +2808,22 @@ dependencies = [ "sha2", "smallvec 1.4.0", "uint", - "unsigned-varint", + "unsigned-varint 0.3.3", "void", "wasm-timer", ] [[package]] name = "libp2p-mdns" -version = "0.19.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b00163d13f705aae67c427bea0575f8aaf63da6524f9bd4a5a093b8bda0b38" +checksum = "7f55b2d4b80986e5bf158270ab23268ec0e7f644ece5436fbaabc5155472f357" dependencies = [ "async-std", "data-encoding", "dns-parser", "either", - "futures 0.3.4", + "futures 0.3.5", "lazy_static", "libp2p-core", "libp2p-swarm", @@ -2733,28 +2837,28 @@ dependencies = [ [[package]] name = "libp2p-mplex" -version = "0.19.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ce63313ad4bce2d76e54c292a1293ea47a0ebbe16708f1513fa62184992f53" +checksum = "be7d913a4cd57de2013257ec73f07d77bfce390b370023e2d59083e5ca079864" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "fnv", - "futures 0.3.4", - "futures_codec", + "futures 0.3.5", + "futures_codec 0.4.1", "libp2p-core", "log", "parking_lot 0.10.2", - "unsigned-varint", + "unsigned-varint 0.4.0", ] [[package]] name = "libp2p-noise" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84fd504e27b0eadd451e06b67694ef714bd8374044e7db339bb0cdb83755ddf4" +checksum = "a03db664653369f46ee03fcec483a378c20195089bb43a26cb9fb0058009ac88" dependencies = [ "curve25519-dalek", - "futures 0.3.4", + "futures 0.3.5", "lazy_static", "libp2p-core", "log", @@ -2770,11 +2874,11 @@ dependencies = [ [[package]] name = "libp2p-ping" -version = "0.19.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c189cf1dfe4b3f01e2c0fe5e97a6f5df8aeb6f3569e26981015eb7c08015ce5f" +checksum = "ffb3c4f9273313357d4977799aec69f581cfe9568854919c5b8066018ccf59f5" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "libp2p-core", "libp2p-swarm", "log", @@ -2785,13 +2889,13 @@ dependencies = [ [[package]] name = "libp2p-secio" -version = "0.19.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b73f0cc119c83a5b619d6d11074a319fdb4aa4daf8088ade00d511418566e28" +checksum = "c99b3c33e96bb402486d5b4f7cbeab14e66e6a2ed010abbb5bb032a05460bfda" dependencies = [ "aes-ctr", "ctr", - "futures 0.3.4", + "futures 0.3.5", "hmac", "js-sys", "lazy_static", @@ -2815,11 +2919,11 @@ dependencies = [ [[package]] name = "libp2p-swarm" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a8101a0e0d5f04562137a476bf5f5423cd5bdab2f7e43a75909668e63cb102" +checksum = "ce53ff4d127cf8b39adf84dbd381ca32d49bd85788cee08e6669da2495993930" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "libp2p-core", "log", "rand 0.7.3", @@ -2830,12 +2934,12 @@ dependencies = [ [[package]] name = "libp2p-tcp" -version = "0.19.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "309f95fce9bec755eff5406f8b822fd3969990830c2b54f752e1fc181d5ace3e" +checksum = "9481500c5774c62e8c413e9535b3f33a0e3dbacf2da63b8d3056c686a9df4146" dependencies = [ "async-std", - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", "get_if_addrs", "ipnet", @@ -2850,7 +2954,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f59fdbb5706f2723ca108c088b1c7a37f735a8c328021f0508007162627e9885" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "js-sys", "libp2p-core", "parity-send-wrapper", @@ -2865,9 +2969,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "085fbe4c05c4116c2164ab4d5a521eb6e00516c444f61b3ee9f68c7b1e53580b" dependencies = [ "async-tls", - "bytes 0.5.4", + "bytes 0.5.5", "either", - "futures 0.3.4", + "futures 0.3.5", "libp2p-core", "log", "quicksink", @@ -2881,11 +2985,11 @@ dependencies = [ [[package]] name = "libp2p-yamux" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b305d3a8981e68f11c0e17f2d11d5c52fae95e0d7c283f9e462b5b2dab413b2" +checksum = "8da33e7b5f49c75c6a8afb0b8d1e229f5fa48be9f39bd14cdbc21459a02ac6fc" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "libp2p-core", "parking_lot 0.10.2", "thiserror", @@ -2916,7 +3020,7 @@ dependencies = [ "hmac-drbg", "rand 0.7.3", "sha2", - "subtle 2.2.2", + "subtle 2.2.3", "typenum", ] @@ -2960,9 +3064,18 @@ dependencies = [ [[package]] name = "lite-json" -version = "0.1.0" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c73e713a23ac6e12074c9e96ef2dfb770921e0cb9244c093bd38424209e0e523" +dependencies = [ + "lite-parser", +] + +[[package]] +name = "lite-parser" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa835713bb12ba5204013497da16caf2dd2eee25ca829d0efaa054fb38c4ddd" +checksum = "0c50092e40e0ccd1bf2015a10333fde0502ff95b832b0895dc1ca0d7ac6c52f6" dependencies = [ "paste", ] @@ -2985,6 +3098,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "loom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ecc775857611e1df29abba5c41355cdf540e7e9d4acfdf0f355eefee82330b7" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls 0.1.2", +] + [[package]] name = "lru" version = "0.4.3" @@ -2996,9 +3120,9 @@ dependencies = [ [[package]] name = "mach" -version = "0.2.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" dependencies = [ "libc", ] @@ -3051,9 +3175,9 @@ dependencies = [ [[package]] name = "memory-db" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be512cb2ccb4ecbdca937fdd4a62ea5f09f8e7195466a85e4632b3d5bcce82e6" +checksum = "fb2999ff7a65d5a1d72172f6d51fa2ea03024b51aee709ba5ff81c3c629a2410" dependencies = [ "ahash", "hash-db", @@ -3081,9 +3205,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" dependencies = [ "adler32", ] @@ -3101,7 +3225,7 @@ dependencies = [ "kernel32-sys", "libc", "log", - "miow", + "miow 0.2.1", "net2", "slab", "winapi 0.2.8", @@ -3119,6 +3243,18 @@ dependencies = [ "slab", ] +[[package]] +name = "mio-named-pipes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" +dependencies = [ + "log", + "mio", + "miow 0.3.5", + "winapi 0.3.8", +] + [[package]] name = "mio-uds" version = "0.6.8" @@ -3142,6 +3278,16 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "miow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" +dependencies = [ + "socket2", + "winapi 0.3.8", +] + [[package]] name = "more-asserts" version = "0.2.1" @@ -3150,9 +3296,9 @@ checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238" [[package]] name = "multihash" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae32179a9904ccc6e063de8beee7f5dd55fae85ecb851ca923d55722bc28cf5d" +checksum = "f75db05d738947aa5389863aadafbcf2e509d7ba099dc2ddcdf4fc66bf7a9e03" dependencies = [ "blake2b_simd", "blake2s_simd", @@ -3160,7 +3306,7 @@ dependencies = [ "sha-1", "sha2", "sha3", - "unsigned-varint", + "unsigned-varint 0.3.3", ] [[package]] @@ -3171,16 +3317,16 @@ checksum = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce" [[package]] name = "multistream-select" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74cdcf7cfb3402881e15a1f95116cb033d69b33c83d481e1234777f5ef0c3d2c" +checksum = "c9157e87afbc2ef0d84cc0345423d715f445edde00141c93721c162de35a05e5" dependencies = [ - "bytes 0.5.4", - "futures 0.3.4", + "bytes 0.5.5", + "futures 0.3.5", "log", "pin-project", "smallvec 1.4.0", - "unsigned-varint", + "unsigned-varint 0.4.0", ] [[package]] @@ -3195,7 +3341,7 @@ dependencies = [ "matrixmultiply", "num-complex", "num-rational", - "num-traits 0.2.11", + "num-traits 0.2.12", "rand 0.6.5", "typenum", ] @@ -3230,7 +3376,7 @@ dependencies = [ "byteorder", "enum-primitive-derive", "libc", - "num-traits 0.2.11", + "num-traits 0.2.12", "thiserror", ] @@ -3281,7 +3427,7 @@ dependencies = [ name = "node-browser-testing" version = "2.0.0-rc3" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", "jsonrpc-core", "libp2p", @@ -3302,7 +3448,7 @@ dependencies = [ "frame-benchmarking-cli", "frame-support", "frame-system", - "futures 0.3.4", + "futures 0.3.5", "hex-literal", "jsonrpc-core", "log", @@ -3449,6 +3595,7 @@ dependencies = [ "sc-keystore", "sc-rpc-api", "sp-api", + "sp-block-builder", "sp-blockchain", "sp-consensus", "sp-consensus-babe", @@ -3461,7 +3608,7 @@ dependencies = [ name = "node-rpc-client" version = "2.0.0-rc3" dependencies = [ - "env_logger 0.7.1", + "env_logger", "futures 0.1.29", "hyper 0.12.35", "jsonrpc-core-client", @@ -3542,7 +3689,7 @@ dependencies = [ name = "node-template" version = "2.0.0-rc3" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "log", "node-template-runtime", "parking_lot 0.10.2", @@ -3607,7 +3754,7 @@ dependencies = [ "frame-support", "frame-system", "fs_extra", - "futures 0.3.4", + "futures 0.3.5", "log", "node-executor", "node-primitives", @@ -3659,9 +3806,9 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "nom" -version = "5.1.1" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ "memchr", "version_check", @@ -3669,9 +3816,9 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26e041cd983acbc087e30fcba770380cfa352d0e392e175b2344ebaf7ea0602" +checksum = "7a31937dea023539c72ddae0e3571deadc1414b300483fa7aaec176168cfa9d2" dependencies = [ "winapi 0.3.8", ] @@ -3684,7 +3831,7 @@ checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ "autocfg 1.0.0", "num-integer", - "num-traits 0.2.11", + "num-traits 0.2.12", ] [[package]] @@ -3694,17 +3841,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" dependencies = [ "autocfg 1.0.0", - "num-traits 0.2.11", + "num-traits 0.2.12", ] [[package]] name = "num-integer" -version = "0.1.42" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" dependencies = [ "autocfg 1.0.0", - "num-traits 0.2.11", + "num-traits 0.2.12", ] [[package]] @@ -3716,7 +3863,7 @@ dependencies = [ "autocfg 1.0.0", "num-bigint", "num-integer", - "num-traits 0.2.11", + "num-traits 0.2.12", ] [[package]] @@ -3725,14 +3872,14 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" dependencies = [ - "num-traits 0.2.11", + "num-traits 0.2.12", ] [[package]] name = "num-traits" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" dependencies = [ "autocfg 1.0.0", "libm", @@ -3757,20 +3904,26 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "object" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" + [[package]] name = "once_cell" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" dependencies = [ - "parking_lot 0.9.0", + "parking_lot 0.10.2", ] [[package]] name = "oorandom" -version = "11.1.1" +version = "11.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94af325bc33c7f60191be4e2c984d48aaa21e2854f473b85398344b60c9b6358" +checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c" [[package]] name = "opaque-debug" @@ -3816,6 +3969,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-atomic-swap" +version = "2.0.0-rc3" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-aura" version = "2.0.0-rc3" @@ -3956,6 +4124,7 @@ dependencies = [ "pallet-transaction-payment", "parity-scale-codec", "parity-wasm 0.41.0", + "pretty_assertions", "pwasm-utils", "serde", "sp-core", @@ -4447,7 +4616,7 @@ dependencies = [ name = "pallet-staking" version = "2.0.0-rc3" dependencies = [ - "env_logger 0.7.1", + "env_logger", "frame-benchmarking", "frame-support", "frame-system", @@ -4500,9 +4669,9 @@ version = "2.0.0-rc3" dependencies = [ "proc-macro-crate", "proc-macro2", - "quote 1.0.6", + "quote 1.0.7", "sp-runtime", - "syn 1.0.19", + "syn 1.0.33", ] [[package]] @@ -4558,6 +4727,7 @@ dependencies = [ "pallet-balances", "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", + "serde", "smallvec 1.4.0", "sp-core", "sp-io", @@ -4675,15 +4845,15 @@ dependencies = [ "percent-encoding 2.1.0", "serde", "static_assertions", - "unsigned-varint", + "unsigned-varint 0.3.3", "url 2.1.1", ] [[package]] name = "parity-multiaddr" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ca96399f4a01aa89c59220c4f52ac371940eb4e53e3ce990da796f364bdf69" +checksum = "cc20af3143a62c16e7c9e92ea5c6ae49f7d271d97d4d8fe73afc28f0514a3d0f" dependencies = [ "arrayref", "bs58", @@ -4693,7 +4863,7 @@ dependencies = [ "percent-encoding 2.1.0", "serde", "static_assertions", - "unsigned-varint", + "unsigned-varint 0.4.0", "url 2.1.1", ] @@ -4704,19 +4874,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1cd2ba02391b81367bec529fb209019d718684fdc8ad6a712c2b536e46f775" dependencies = [ "blake2", - "bytes 0.5.4", + "bytes 0.5.5", "rand 0.7.3", "sha-1", "sha2", "sha3", - "unsigned-varint", + "unsigned-varint 0.3.3", ] [[package]] name = "parity-scale-codec" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c8f7f4244ddb5c37c103641027a76c530e65e8e4b8240b29f81ea40508b17" +checksum = "a74f02beb35d47e0706155c9eac554b50c671e0d868fe8296bcdf44a9a4847bf" dependencies = [ "arrayvec 0.5.1", "bitvec", @@ -4733,8 +4903,8 @@ checksum = "5a0ec292e92e8ec7c58e576adacc1e3f399c597c8f263c42f18420abe58e7245" dependencies = [ "proc-macro-crate", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -4743,6 +4913,25 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" +[[package]] +name = "parity-tokio-ipc" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e57fea504fea33f9fbb5f49f378359030e7e026a6ab849bb9e8f0787376f1bf" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.29", + "libc", + "log", + "mio-named-pipes", + "miow 0.3.5", + "rand 0.7.3", + "tokio 0.1.22", + "tokio-named-pipes", + "tokio-uds", + "winapi 0.3.8", +] + [[package]] name = "parity-util-mem" version = "0.6.1" @@ -4765,7 +4954,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ "proc-macro2", - "syn 1.0.19", + "syn 1.0.33", "synstructure", ] @@ -4836,9 +5025,9 @@ dependencies = [ [[package]] name = "paste" -version = "0.1.12" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a229b1c58c692edcaa5b9b0948084f130f55d2dcc15b02fcc5340b2b4521476" +checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" dependencies = [ "paste-impl", "proc-macro-hack", @@ -4846,14 +5035,11 @@ dependencies = [ [[package]] name = "paste-impl" -version = "0.1.12" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e0bf239e447e67ff6d16a8bb5e4d4bd2343acf5066061c0e8e06ac5ba8ca68c" +checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" dependencies = [ "proc-macro-hack", - "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", ] [[package]] @@ -4892,9 +5078,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "petgraph" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c127eea4a29ec6c85d153c59dc1213f33ec74cead30fe4730aecc88cc1fd92" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" dependencies = [ "fixedbitset", "indexmap", @@ -4902,29 +5088,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.13" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c3bfbfb5bb42f99498c7234bbd768c220eb0cea6818259d0d18a1aa3d2595d" +checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.13" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccbf6449dcfb18562c015526b085b8df1aa3cdab180af8ec2ebd300a3bd28f63" +checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] name = "pin-project-lite" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f" +checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" [[package]] name = "pin-utils" @@ -4952,21 +5138,40 @@ checksum = "feb3b2b1033b8a60b4da6ee470325f887758c95d5320f52f9ce0df055a55940e" [[package]] name = "plotters" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9b1d9ca091d370ea3a78d5619145d1b59426ab0c9eedbad2514a4cee08bf389" +checksum = "0d1685fbe7beba33de0330629da9d955ac75bd54f33d7b79f9a895590124f6bb" dependencies = [ "js-sys", - "num-traits 0.2.11", + "num-traits 0.2.12", "wasm-bindgen", "web-sys", ] +[[package]] +name = "poly1305" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5829f50f48e9ddb79f3f7c3097029d0caee30f8286accb241416df603b080b8" +dependencies = [ + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ec3341498978de3bfd12d1b22f1af1de22818f5473a11e8a6ef997989e3a212" +dependencies = [ + "cfg-if", + "universal-hash", +] + [[package]] name = "ppv-lite86" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "predicates" @@ -5036,8 +5241,8 @@ checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" dependencies = [ "proc-macro-error-attr", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", "version_check", ] @@ -5048,38 +5253,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", "syn-mid", "version_check", ] [[package]] name = "proc-macro-hack" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" +checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" [[package]] name = "proc-macro-nested" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" +checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" [[package]] name = "proc-macro2" -version = "1.0.12" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" dependencies = [ "unicode-xid 0.2.0", ] [[package]] name = "procfs" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe50036aa1b71e553a4a0c48ab7baabf8aa8c7a5a61aae06bf38c2eab7430475" +checksum = "c434e93ef69c216e68e4f417c927b4f31502c3560b72cfdb6827e2321c5c6b3e" dependencies = [ "bitflags", "byteorder", @@ -5110,7 +5315,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce49aefe0a6144a45de32927c77bd2859a5f7677b55f220ae5b744e87389c212" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "prost-derive", ] @@ -5120,7 +5325,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b10678c913ecbd69350e8535c3aef91a8676c0773fc1d7b95cdd196d7f2f26" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "heck", "itertools 0.8.2", "log", @@ -5141,8 +5346,8 @@ dependencies = [ "anyhow", "itertools 0.8.2", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -5151,15 +5356,15 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1834f67c0697c001304b75be76f67add9c89742eda3a085ad8ee0bb38c3417aa" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "prost", ] [[package]] name = "protobuf" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e86d370532557ae7573551a1ec8235a0f8d6cb276c7c9e6aa490b511c447485" +checksum = "3e2ccb6b8f7e175f2d2401e7a5988b0630000164d221262c4fe50ae729513202" [[package]] name = "pwasm-utils" @@ -5184,7 +5389,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" dependencies = [ - "env_logger 0.7.1", + "env_logger", "log", "rand 0.7.3", "rand_core 0.5.1", @@ -5209,9 +5414,9 @@ checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" [[package]] name = "quote" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ "proc-macro2", ] @@ -5445,10 +5650,11 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" +checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080" dependencies = [ + "autocfg 1.0.0", "crossbeam-deque", "either", "rayon-core", @@ -5456,9 +5662,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" +checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280" dependencies = [ "crossbeam-deque", "crossbeam-queue", @@ -5495,22 +5701,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a214c7875e1b63fc1618db7c80efc0954f6156c9ff07699fd9039e255accdd1" +checksum = "745c1787167ddae5569661d5ffb8b25ae5fedbf46717eaa92d652221cec72623" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602eb59cda66fcb9aec25841fb76bc01d2b34282dcdd705028da297db6f3eec8" +checksum = "7d21b475ab879ef0e315ad99067fa25778c3b0377f57f1b00207448dac1a3144" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -5526,9 +5732,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.3.7" +version = "1.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" dependencies = [ "aho-corasick", "memchr", @@ -5547,15 +5753,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.17" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" [[package]] name = "region" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "448e868c6e4cfddfa49b6a72c95906c04e8547465e9536575b95c70a4044f856" +checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" dependencies = [ "bitflags", "libc", @@ -5565,18 +5771,39 @@ dependencies = [ [[package]] name = "remove_dir_all" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "rental" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8545debe98b2b139fb04cad8618b530e9b07c152d99a5de83c860b877d67847f" +dependencies = [ + "rental-impl", + "stable_deref_trait", +] + +[[package]] +name = "rental-impl" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "475e68978dc5b743f2f40d8e0a8fdc83f1c5e78cbf4b8fa5e74e73beebc340de" +dependencies = [ + "proc-macro2", + "quote 1.0.7", + "syn 1.0.33", +] + [[package]] name = "ring" -version = "0.16.13" +version = "0.16.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703516ae74571f24b465b4a1431e81e2ad51336cb0ded733a55a1aa3eccac196" +checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4" dependencies = [ "cc", "libc", @@ -5688,13 +5915,13 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" +checksum = "b9bdc5e856e51e685846fb6c13a1f5e5432946c2c90501bdc76a1319f19e29da" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -5703,16 +5930,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4da5fcb054c46f5a5dff833b129285a93d3f0179531735e6c866e8cc307d2020" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "pin-project", "static_assertions", ] [[package]] name = "ryu" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "safe-mix" @@ -5736,10 +5963,10 @@ dependencies = [ name = "sc-authority-discovery" version = "0.8.0-rc3" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "derive_more", - "env_logger 0.7.1", - "futures 0.3.4", + "env_logger", + "futures 0.3.5", "futures-timer 3.0.2", "libp2p", "log", @@ -5766,7 +5993,7 @@ dependencies = [ name = "sc-basic-authorship" version = "0.8.0-rc3" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", "log", "parity-scale-codec", @@ -5826,8 +6053,8 @@ version = "2.0.0-rc3" dependencies = [ "proc-macro-crate", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -5838,10 +6065,9 @@ dependencies = [ "atty", "chrono", "derive_more", - "directories", - "env_logger 0.7.1", + "env_logger", "fdlimit", - "futures 0.3.4", + "futures 0.3.5", "lazy_static", "log", "names", @@ -5868,7 +6094,7 @@ dependencies = [ "substrate-prometheus-endpoint", "tempfile", "time", - "tokio 0.2.20", + "tokio 0.2.21", ] [[package]] @@ -5877,7 +6103,7 @@ version = "2.0.0-rc3" dependencies = [ "derive_more", "fnv", - "futures 0.3.4", + "futures 0.3.5", "hash-db", "hex-literal", "kvdb", @@ -5914,7 +6140,7 @@ name = "sc-client-db" version = "0.8.0-rc3" dependencies = [ "blake2-rfc", - "env_logger 0.7.1", + "env_logger", "hash-db", "kvdb", "kvdb-memorydb", @@ -5957,8 +6183,8 @@ name = "sc-consensus-aura" version = "0.8.0-rc3" dependencies = [ "derive_more", - "env_logger 0.7.1", - "futures 0.3.4", + "env_logger", + "futures 0.3.5", "futures-timer 3.0.2", "log", "parity-scale-codec", @@ -5995,19 +6221,20 @@ name = "sc-consensus-babe" version = "0.8.0-rc3" dependencies = [ "derive_more", - "env_logger 0.7.1", + "env_logger", "fork-tree", - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", "log", "merlin", "num-bigint", "num-rational", - "num-traits 0.2.11", + "num-traits 0.2.12", "parity-scale-codec", "parking_lot 0.10.2", "pdqselect", "rand 0.7.3", + "rand_chacha 0.2.2", "sc-block-builder", "sc-client-api", "sc-consensus-epochs", @@ -6045,7 +6272,7 @@ name = "sc-consensus-babe-rpc" version = "0.8.0-rc3" dependencies = [ "derive_more", - "futures 0.3.4", + "futures 0.3.5", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -6086,8 +6313,8 @@ version = "0.8.0-rc3" dependencies = [ "assert_matches", "derive_more", - "env_logger 0.7.1", - "futures 0.3.4", + "env_logger", + "futures 0.3.5", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -6107,7 +6334,7 @@ dependencies = [ "substrate-test-runtime-client", "substrate-test-runtime-transaction-pool", "tempfile", - "tokio 0.2.20", + "tokio 0.2.21", ] [[package]] @@ -6115,7 +6342,7 @@ name = "sc-consensus-pow" version = "0.8.0-rc3" dependencies = [ "derive_more", - "futures 0.3.4", + "futures 0.3.5", "log", "parity-scale-codec", "sc-client-api", @@ -6135,7 +6362,7 @@ dependencies = [ name = "sc-consensus-slots" version = "0.8.0-rc3" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", "log", "parity-scale-codec", @@ -6183,6 +6410,7 @@ dependencies = [ "sc-executor-wasmi", "sc-executor-wasmtime", "sc-runtime-test", + "sc-tracing", "sp-api", "sp-core", "sp-externalities", @@ -6192,11 +6420,13 @@ dependencies = [ "sp-runtime-interface", "sp-serializer", "sp-state-machine", + "sp-tracing", "sp-trie", "sp-version", "sp-wasm-interface", "substrate-test-runtime", "test-case", + "tracing", "wabt", "wasmi", ] @@ -6242,7 +6472,7 @@ dependencies = [ "parity-scale-codec", "parity-wasm 0.41.0", "sc-executor-common", - "scoped-tls", + "scoped-tls 1.0.0", "sp-allocator", "sp-core", "sp-runtime-interface", @@ -6258,10 +6488,10 @@ version = "0.8.0-rc3" dependencies = [ "assert_matches", "derive_more", - "env_logger 0.7.1", + "env_logger", "finality-grandpa", "fork-tree", - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", "log", "parity-scale-codec", @@ -6278,6 +6508,7 @@ dependencies = [ "sc-telemetry", "serde_json", "sp-api", + "sp-application-crypto", "sp-arithmetic", "sp-blockchain", "sp-consensus", @@ -6293,7 +6524,7 @@ dependencies = [ "substrate-prometheus-endpoint", "substrate-test-runtime-client", "tempfile", - "tokio 0.2.20", + "tokio 0.2.21", ] [[package]] @@ -6302,7 +6533,7 @@ version = "0.8.0-rc3" dependencies = [ "derive_more", "finality-grandpa", - "futures 0.3.4", + "futures 0.3.5", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -6318,14 +6549,16 @@ name = "sc-informant" version = "0.8.0-rc3" dependencies = [ "ansi_term 0.12.1", - "futures 0.3.4", + "futures 0.3.5", "log", "parity-util-mem", + "parking_lot 0.10.2", "sc-client-api", "sc-network", - "sc-service", "sp-blockchain", "sp-runtime", + "sp-transaction-pool", + "sp-utils", "wasm-timer", ] @@ -6335,15 +6568,34 @@ version = "2.0.0-rc3" dependencies = [ "derive_more", "hex", + "merlin", "parking_lot 0.10.2", "rand 0.7.3", "serde_json", "sp-application-crypto", "sp-core", - "subtle 2.2.2", + "subtle 2.2.3", "tempfile", ] +[[package]] +name = "sc-light" +version = "2.0.0-rc3" +dependencies = [ + "hash-db", + "lazy_static", + "parity-scale-codec", + "parking_lot 0.10.2", + "sc-client-api", + "sc-executor", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-externalities", + "sp-runtime", + "sp-state-machine", +] + [[package]] name = "sc-network" version = "0.8.0-rc3" @@ -6352,16 +6604,16 @@ dependencies = [ "async-std", "bitflags", "bs58", - "bytes 0.5.4", + "bytes 0.5.5", "derive_more", "either", - "env_logger 0.7.1", + "env_logger", "erased-serde", "fnv", "fork-tree", - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", - "futures_codec", + "futures_codec 0.3.4", "hex", "ip_network", "libp2p", @@ -6398,7 +6650,7 @@ dependencies = [ "substrate-test-runtime-client", "tempfile", "thiserror", - "unsigned-varint", + "unsigned-varint 0.3.3", "void", "wasm-timer", "zeroize", @@ -6409,7 +6661,7 @@ name = "sc-network-gossip" version = "0.8.0-rc3" dependencies = [ "async-std", - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", "libp2p", "log", @@ -6426,8 +6678,8 @@ dependencies = [ name = "sc-network-test" version = "0.8.0-rc3" dependencies = [ - "env_logger 0.7.1", - "futures 0.3.4", + "env_logger", + "futures 0.3.5", "futures-timer 3.0.2", "libp2p", "log", @@ -6452,14 +6704,14 @@ dependencies = [ name = "sc-offchain" version = "2.0.0-rc3" dependencies = [ - "bytes 0.5.4", - "env_logger 0.7.1", - "fdlimit", + "bytes 0.5.5", + "env_logger", "fnv", - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", - "hyper 0.13.5", + "hyper 0.13.6", "hyper-rustls", + "lazy_static", "log", "num_cpus", "parity-scale-codec", @@ -6478,14 +6730,14 @@ dependencies = [ "sp-utils", "substrate-test-runtime-client", "threadpool", - "tokio 0.2.20", + "tokio 0.2.21", ] [[package]] name = "sc-peerset" version = "2.0.0-rc3" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "libp2p", "log", "rand 0.7.3", @@ -6508,7 +6760,7 @@ version = "2.0.0-rc3" dependencies = [ "assert_matches", "futures 0.1.29", - "futures 0.3.4", + "futures 0.3.5", "hash-db", "jsonrpc-core", "jsonrpc-pubsub", @@ -6546,7 +6798,7 @@ name = "sc-rpc-api" version = "0.8.0-rc3" dependencies = [ "derive_more", - "futures 0.3.4", + "futures 0.3.5", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", @@ -6570,6 +6822,7 @@ version = "2.0.0-rc3" dependencies = [ "jsonrpc-core", "jsonrpc-http-server", + "jsonrpc-ipc-server", "jsonrpc-pubsub", "jsonrpc-ws-server", "log", @@ -6596,9 +6849,10 @@ name = "sc-service" version = "0.8.0-rc3" dependencies = [ "derive_more", + "directories", "exit-future", "futures 0.1.29", - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", "hash-db", "jsonrpc-pubsub", @@ -6618,7 +6872,9 @@ dependencies = [ "sc-client-db", "sc-executor", "sc-finality-grandpa", + "sc-informant", "sc-keystore", + "sc-light", "sc-network", "sc-offchain", "sc-rpc", @@ -6649,6 +6905,7 @@ dependencies = [ "substrate-prometheus-endpoint", "substrate-test-runtime-client", "sysinfo", + "tempfile", "tracing", "wasm-timer", ] @@ -6657,10 +6914,10 @@ dependencies = [ name = "sc-service-test" version = "2.0.0-rc3" dependencies = [ - "env_logger 0.7.1", + "env_logger", "fdlimit", "futures 0.1.29", - "futures 0.3.4", + "futures 0.3.5", "hex-literal", "log", "parity-scale-codec", @@ -6669,6 +6926,7 @@ dependencies = [ "sc-client-api", "sc-client-db", "sc-executor", + "sc-light", "sc-network", "sc-service", "sp-api", @@ -6692,7 +6950,7 @@ dependencies = [ name = "sc-state-db" version = "0.8.0-rc3" dependencies = [ - "env_logger 0.7.1", + "env_logger", "log", "parity-scale-codec", "parity-util-mem", @@ -6706,8 +6964,8 @@ dependencies = [ name = "sc-telemetry" version = "2.0.0-rc3" dependencies = [ - "bytes 0.5.4", - "futures 0.3.4", + "bytes 0.5.5", + "futures 0.3.5", "futures-timer 3.0.2", "libp2p", "log", @@ -6730,10 +6988,12 @@ dependencies = [ "erased-serde", "log", "parking_lot 0.10.2", + "rustc-hash", "sc-telemetry", "serde", "serde_json", "slog", + "sp-tracing", "tracing", "tracing-core", ] @@ -6745,7 +7005,7 @@ dependencies = [ "assert_matches", "criterion 0.3.2", "derive_more", - "futures 0.3.4", + "futures 0.3.5", "linked-hash-map", "log", "parity-scale-codec", @@ -6767,7 +7027,7 @@ version = "2.0.0-rc3" dependencies = [ "assert_matches", "derive_more", - "futures 0.3.4", + "futures 0.3.5", "futures-diagnose", "hex", "intervalier", @@ -6795,9 +7055,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" dependencies = [ "lazy_static", "winapi 0.3.8", @@ -6817,10 +7077,16 @@ dependencies = [ "rand 0.7.3", "rand_core 0.5.1", "sha2", - "subtle 2.2.2", + "subtle 2.2.3", "zeroize", ] +[[package]] +name = "scoped-tls" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" + [[package]] name = "scoped-tls" version = "1.0.0" @@ -6844,13 +7110,13 @@ dependencies = [ [[package]] name = "scroll_derive" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" +checksum = "e367622f934864ffa1c704ba2b82280aab856e3d8213c84c5720257eb34b15b9" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -6865,9 +7131,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f331b9025654145cd425b9ded0caf8f5ae0df80d418b326e2dc1c3dc5eb0620" +checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" dependencies = [ "bitflags", "core-foundation", @@ -6931,29 +7197,29 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" [[package]] name = "serde" -version = "1.0.110" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" +checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.110" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" +checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] name = "serde_json" -version = "1.0.52" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd" +checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226" dependencies = [ "itoa", "ryu", @@ -6980,9 +7246,9 @@ checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" [[package]] name = "sha2" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" dependencies = [ "block-buffer", "digest", @@ -7065,8 +7331,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a945ec7f7ce853e89ffa36be1e27dce9a43e82ff9093bf3461c30d5da74ed11b" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -7086,19 +7352,19 @@ checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" [[package]] name = "snow" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb767eee7d257ba202f0b9b08673bc13b22281632ef45267b19f13100accd2f" +checksum = "ce0f91be479494dd92e69d9971bd23ed27037dd1c94fcf558f6c6e74e6afa654" dependencies = [ - "arrayref", - "blake2-rfc", - "chacha20-poly1305-aead", + "aes-gcm", + "blake2", + "chacha20poly1305", "rand 0.7.3", "rand_core 0.5.1", "ring", "rustc_version", "sha2", - "subtle 2.2.2", + "subtle 2.2.3", "x25519-dalek", ] @@ -7121,9 +7387,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c9dab3f95c9ebdf3a88268c19af668f637a3c5039c2c56ff2d40b1b2d64a25b" dependencies = [ "base64 0.11.0", - "bytes 0.5.4", + "bytes 0.5.5", "flate2", - "futures 0.3.4", + "futures 0.3.5", "http 0.2.1", "httparse", "log", @@ -7167,8 +7433,8 @@ dependencies = [ "blake2-rfc", "proc-macro-crate", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -7218,7 +7484,7 @@ version = "2.0.0-rc3" dependencies = [ "criterion 0.3.2", "integer-sqrt", - "num-traits 0.2.11", + "num-traits 0.2.12", "parity-scale-codec", "primitive-types", "rand 0.7.3", @@ -7234,7 +7500,7 @@ version = "2.0.0-rc3" dependencies = [ "honggfuzz", "num-bigint", - "num-traits 0.2.11", + "num-traits 0.2.12", "primitive-types", "sp-arithmetic", ] @@ -7299,7 +7565,7 @@ name = "sp-consensus" version = "0.8.0-rc3" dependencies = [ "derive_more", - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", "libp2p", "log", @@ -7315,6 +7581,7 @@ dependencies = [ "sp-utils", "sp-version", "substrate-prometheus-endpoint", + "wasm-timer", ] [[package]] @@ -7340,6 +7607,7 @@ dependencies = [ "sp-application-crypto", "sp-consensus", "sp-consensus-vrf", + "sp-core", "sp-inherents", "sp-runtime", "sp-std", @@ -7378,7 +7646,7 @@ dependencies = [ "criterion 0.2.11", "derive_more", "ed25519-dalek", - "futures 0.3.4", + "futures 0.3.5", "hash-db", "hash256-std-hasher", "hex", @@ -7388,13 +7656,14 @@ dependencies = [ "libsecp256k1", "log", "merlin", - "num-traits 0.2.11", + "num-traits 0.2.12", "parity-scale-codec", "parity-util-mem", "parking_lot 0.10.2", "pretty_assertions", "primitive-types", "rand 0.7.3", + "rand_chacha 0.2.2", "regex", "schnorrkel", "serde", @@ -7427,8 +7696,8 @@ name = "sp-debug-derive" version = "2.0.0-rc3" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -7480,7 +7749,7 @@ dependencies = [ name = "sp-io" version = "2.0.0-rc3" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "hash-db", "libsecp256k1", "log", @@ -7491,6 +7760,7 @@ dependencies = [ "sp-runtime-interface", "sp-state-machine", "sp-std", + "sp-tracing", "sp-trie", "sp-wasm-interface", ] @@ -7513,7 +7783,6 @@ dependencies = [ "rand 0.7.3", "serde", "sp-arithmetic", - "sp-npos-elections", "sp-npos-elections-compact", "sp-runtime", "sp-std", @@ -7526,8 +7795,8 @@ version = "2.0.0-rc3" dependencies = [ "proc-macro-crate", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -7572,6 +7841,7 @@ dependencies = [ name = "sp-runtime" version = "2.0.0-rc3" dependencies = [ + "either", "hash256-std-hasher", "impl-trait-for-tuples", "log", @@ -7617,8 +7887,8 @@ dependencies = [ "Inflector", "proc-macro-crate", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -7707,11 +7977,14 @@ version = "0.8.0-rc3" dependencies = [ "hash-db", "hex-literal", + "itertools 0.9.0", "log", - "num-traits 0.2.11", + "num-traits 0.2.12", "parity-scale-codec", "parking_lot 0.10.2", + "pretty_assertions", "rand 0.7.3", + "smallvec 1.4.0", "sp-core", "sp-externalities", "sp-panic-handler", @@ -7765,6 +8038,8 @@ dependencies = [ name = "sp-tracing" version = "2.0.0-rc3" dependencies = [ + "log", + "rental", "tracing", ] @@ -7773,7 +8048,7 @@ name = "sp-transaction-pool" version = "2.0.0-rc3" dependencies = [ "derive_more", - "futures 0.3.4", + "futures 0.3.5", "log", "parity-scale-codec", "serde", @@ -7805,8 +8080,9 @@ dependencies = [ name = "sp-utils" version = "2.0.0-rc3" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "futures-core", + "futures-timer 3.0.2", "lazy_static", "prometheus", ] @@ -7894,9 +8170,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" +checksum = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c" dependencies = [ "clap", "lazy_static", @@ -7905,15 +8181,15 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" +checksum = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118" dependencies = [ "heck", "proc-macro-error", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -7933,8 +8209,8 @@ checksum = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" dependencies = [ "heck", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -7988,7 +8264,7 @@ dependencies = [ "console_error_panic_hook", "console_log", "futures 0.1.29", - "futures 0.3.4", + "futures 0.3.5", "futures-timer 3.0.2", "js-sys", "kvdb-web", @@ -8018,32 +8294,34 @@ version = "2.0.0-rc3" dependencies = [ "frame-support", "frame-system", - "futures 0.3.4", + "futures 0.3.5", "jsonrpc-client-transports", "jsonrpc-core", "parity-scale-codec", "sc-rpc-api", "serde", "sp-storage", - "tokio 0.2.20", + "tokio 0.2.21", ] [[package]] name = "substrate-frame-rpc-system" version = "2.0.0-rc3" dependencies = [ - "env_logger 0.7.1", + "env_logger", "frame-system-rpc-runtime-api", - "futures 0.3.4", + "futures 0.3.5", "jsonrpc-core", "jsonrpc-core-client", "jsonrpc-derive", "log", "parity-scale-codec", "sc-client-api", + "sc-rpc-api", "sc-transaction-pool", "serde", "sp-api", + "sp-block-builder", "sp-blockchain", "sp-core", "sp-runtime", @@ -8058,23 +8336,24 @@ dependencies = [ "async-std", "derive_more", "futures-util", - "hyper 0.13.5", + "hyper 0.13.6", "log", "prometheus", - "tokio 0.2.20", + "tokio 0.2.21", ] [[package]] name = "substrate-test-client" version = "2.0.0-rc3" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "hash-db", "parity-scale-codec", "sc-client-api", "sc-client-db", "sc-consensus", "sc-executor", + "sc-light", "sc-service", "sp-blockchain", "sp-consensus", @@ -8131,11 +8410,12 @@ dependencies = [ name = "substrate-test-runtime-client" version = "2.0.0-rc3" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "parity-scale-codec", "sc-block-builder", "sc-client-api", "sc-consensus", + "sc-light", "sc-service", "sp-api", "sp-blockchain", @@ -8151,7 +8431,7 @@ name = "substrate-test-runtime-transaction-pool" version = "2.0.0-rc3" dependencies = [ "derive_more", - "futures 0.3.4", + "futures 0.3.5", "parity-scale-codec", "parking_lot 0.10.2", "sc-transaction-graph", @@ -8220,7 +8500,7 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli", + "gimli 0.20.0", "log", "more-asserts", "region", @@ -8242,10 +8522,10 @@ checksum = "c77f0ce539b5a09a54dc80a1cf0c7cd7e694df11029354fe50a2d5fe889bdb97" dependencies = [ "anyhow", "cfg-if", - "gimli", + "gimli 0.20.0", "lazy_static", "libc", - "object", + "object 0.18.0", "scroll", "serde", "substrate-wasmtime-runtime", @@ -8281,9 +8561,9 @@ checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" [[package]] name = "subtle" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" +checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" [[package]] name = "syn" @@ -8298,12 +8578,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.19" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7" +checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd" dependencies = [ "proc-macro2", - "quote 1.0.6", + "quote 1.0.7", "unicode-xid 0.2.0", ] @@ -8314,8 +8594,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -8329,13 +8609,13 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", "unicode-xid 0.2.0", ] @@ -8397,8 +8677,8 @@ checksum = "a605baa797821796a751f4a959e1206079b24a4b7e1ed302b7d785d81a9276c9" dependencies = [ "lazy_static", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", "version_check", ] @@ -8413,22 +8693,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.16" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d12a1dae4add0f0d568eebc7bf142f145ba1aa2544cafb195c76f0f409091b60" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.16" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f34e0c1caaa462fd840ec6b768946ea1e7842620d94fe29d5b847138f521269" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -8442,9 +8722,9 @@ dependencies = [ [[package]] name = "threadpool" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dae184447c15d5a6916d973c642aec485105a13cd238192a6927ae3e077d66" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" dependencies = [ "num_cpus", ] @@ -8495,14 +8775,20 @@ dependencies = [ [[package]] name = "tinytemplate" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e4bc5ac99433e0dcb8b9f309dd271a165ae37dde129b9e0ce1bfdd8bfe4891" +checksum = "6d3dc76004a03cec1c5932bca4cdc2e39aaa798e3f82363dd94f9adf6098c12f" dependencies = [ "serde", "serde_json", ] +[[package]] +name = "tinyvec" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed" + [[package]] name = "tokio" version = "0.1.22" @@ -8529,11 +8815,11 @@ dependencies = [ [[package]] name = "tokio" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c1d570eb1a36f0345a5ce9c6c6e665b70b73d11236912c0b477616aeec47b1" +checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "fnv", "futures-core", "iovec", @@ -8632,8 +8918,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", +] + +[[package]] +name = "tokio-named-pipes" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d282d483052288b2308ba5ee795f5673b159c9bdf63c385a05609da782a5eae" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.29", + "mio", + "mio-named-pipes", + "tokio 0.1.22", ] [[package]] @@ -8657,16 +8956,25 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4adb8b3e5f86b707f1b54e7c15b6de52617a823608ccda98a15d3a24222f265a" +checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4" dependencies = [ "futures-core", "rustls", - "tokio 0.2.20", + "tokio 0.2.21", "webpki", ] +[[package]] +name = "tokio-service" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" +dependencies = [ + "futures 0.1.29", +] + [[package]] name = "tokio-sync" version = "0.1.8" @@ -8770,12 +9078,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "futures-core", "futures-sink", "log", "pin-project-lite", - "tokio 0.2.20", + "tokio 0.2.21", ] [[package]] @@ -8795,9 +9103,9 @@ checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" [[package]] name = "tracing" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1721cc8cf7d770cc4257872507180f35a4797272f5962f24c806af9e7faf52ab" +checksum = "a7c6b59d116d218cb2d990eb06b77b64043e0268ef7323aae63d8b30ae462923" dependencies = [ "cfg-if", "tracing-attributes", @@ -8806,12 +9114,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbad39da2f9af1cae3016339ad7f2c7a9e870f12e8fd04c4fd7ef35b30c0d2b" +checksum = "99bbad0de3fd923c9c3232ead88510b783e5a4d16a6154adffa3d53308de984c" dependencies = [ - "quote 1.0.6", - "syn 1.0.19", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.33", ] [[package]] @@ -8831,9 +9140,9 @@ checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" [[package]] name = "trie-bench" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48b309cdda1abbdada28424bdc46f8b85362b3e66d6786d91223e83874429c7" +checksum = "ed8419971832eb3333dc26066e50943a20e0934efeb451b3df5ee94f7f7323ab" dependencies = [ "criterion 0.2.11", "hash-db", @@ -8847,9 +9156,9 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc309f34008563989045a4c4dbcc5770467f3a3785ee80a9b5cc0d83362475f" +checksum = "cb230c24c741993b04cfccbabb45acff6f6480c5f00d3ed8794ea43db3a9d727" dependencies = [ "hash-db", "hashbrown", @@ -8885,9 +9194,9 @@ checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" [[package]] name = "trybuild" -version = "1.0.26" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5696e4fd793743fbcc29943fe965ea3993b6c3d2a6a3a35c6680d926fd3a49" +checksum = "bbe777c4e2060f44d83892be1189f96200be8ed3d99569d5c2d5ee26e62c0ea9" dependencies = [ "glob 0.3.0", "lazy_static", @@ -8955,11 +9264,11 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" dependencies = [ - "smallvec 1.4.0", + "tinyvec", ] [[package]] @@ -8986,16 +9295,36 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +[[package]] +name = "universal-hash" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0c900f2f9b4116803415878ff48b63da9edb268668e08cf9292d7503114a01" +dependencies = [ + "generic-array", + "subtle 2.2.3", +] + [[package]] name = "unsigned-varint" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f67332660eb59a6f1eb24ff1220c9e8d01738a8503c6002e30bcfe4bd9f2b4a9" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.5", "futures-io", "futures-util", - "futures_codec", + "futures_codec 0.3.4", +] + +[[package]] +name = "unsigned-varint" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669d776983b692a906c881fcd0cfb34271a48e197e4d6cb8df32b05bfc3d3fa5" +dependencies = [ + "bytes 0.5.5", + "futures_codec 0.4.1", ] [[package]] @@ -9028,9 +9357,9 @@ dependencies = [ [[package]] name = "vcpkg" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" [[package]] name = "vec_map" @@ -9040,9 +9369,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "void" @@ -9064,9 +9393,9 @@ dependencies = [ [[package]] name = "wabt-sys" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7043ebb3e5d96fad7a8d3ca22ee9880748ff8c3e18092cfb2a49d3b8f9084" +checksum = "01c695f98f7eb81fd4e2f6b65301ccc916a950dc2265eeefc4d376b34ce666df" dependencies = [ "cc", "cmake", @@ -9142,8 +9471,8 @@ dependencies = [ "lazy_static", "log", "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", "wasm-bindgen-shared", ] @@ -9165,7 +9494,7 @@ version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776" dependencies = [ - "quote 1.0.6", + "quote 1.0.7", "wasm-bindgen-macro-support", ] @@ -9176,8 +9505,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9196,7 +9525,7 @@ checksum = "fd8e9dad8040e378f0696b017570c6bc929aac373180e06b3d67ac5059c52da3" dependencies = [ "console_error_panic_hook", "js-sys", - "scoped-tls", + "scoped-tls 1.0.0", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -9209,7 +9538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c358c8d2507c1bae25efa069e62ea907aa28700b25c8c33dafb0b15ba4603627" dependencies = [ "proc-macro2", - "quote 1.0.6", + "quote 1.0.7", ] [[package]] @@ -9229,7 +9558,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "324c5e65a08699c9c4334ba136597ab22b85dccd4b65dd1e36ccf8f723a95b54" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "js-sys", "parking_lot 0.9.0", "pin-utils", @@ -9249,7 +9578,7 @@ dependencies = [ "libc", "memory_units", "num-rational", - "num-traits 0.2.11", + "num-traits 0.2.12", "parity-wasm 0.41.0", "wasmi-validation", ] @@ -9283,7 +9612,7 @@ checksum = "d39ba645aee700b29ff0093028b4123556dd142a74973f04ed6225eedb40e77d" dependencies = [ "anyhow", "faerie", - "gimli", + "gimli 0.20.0", "more-asserts", "target-lexicon", "thiserror", @@ -9298,7 +9627,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed54fd9d64dfeeee7c285fd126174a6b5e6d4efc7e5a1566fdb635e60ff6a74e" dependencies = [ "anyhow", - "base64 0.12.0", + "base64 0.12.2", "bincode", "cranelift-codegen", "cranelift-entity", @@ -9322,18 +9651,18 @@ dependencies = [ [[package]] name = "wast" -version = "15.0.0" +version = "20.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a10df5277f68adee65bba117b40235f07a4cb3d59e5ec9aa86dbee180fb1bc04" +checksum = "5fb95ce157a8c779ec301ef3e4c0a7caeb6f9f902f813f1f5f7e464367048924" dependencies = [ "leb128", ] [[package]] name = "wat" -version = "1.0.16" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "526d28df6c047d9f9a92d4925b98afd8d8d95b1b3aa4f13eb1306f17d1da56c4" +checksum = "9afa9815ddfb592fb97bb2b817a514c6995e5c171b05037b91d0d831f3a2a9c0" dependencies = [ "wast", ] @@ -9350,9 +9679,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.21.2" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f50e1972865d6b1adb54167d1c8ed48606004c2c9d0ea5f1eeb34d95e863ef" +checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae" dependencies = [ "ring", "untrusted", @@ -9469,11 +9798,11 @@ dependencies = [ [[package]] name = "yamux" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84300bb493cc878f3638b981c62b4632ec1a5c52daaa3036651e8c106d3b55ea" +checksum = "cd37e58a1256a0b328ce9c67d8b62ecdd02f4803ba443df478835cb1a41a637c" dependencies = [ - "futures 0.3.4", + "futures 0.3.5", "log", "nohash-hasher", "parking_lot 0.10.2", @@ -9497,25 +9826,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" dependencies = [ "proc-macro2", - "quote 1.0.6", - "syn 1.0.19", + "quote 1.0.7", + "syn 1.0.33", "synstructure", ] [[package]] name = "zstd" -version = "0.5.1+zstd.1.4.4" +version = "0.5.3+zstd.1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5d978b793ae64375b80baf652919b148f6a496ac8802922d9999f5a553194f" +checksum = "01b32eaf771efa709e8308605bbf9319bf485dc1503179ec0469b611937c0cd8" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "2.0.3+zstd.1.4.4" +version = "2.0.5+zstd.1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee25eac9753cfedd48133fa1736cbd23b774e253d89badbeac7d12b23848d3f" +checksum = "1cfb642e0d27f64729a639c52db457e0ae906e7bc6f5fe8f5c453230400f1055" dependencies = [ "libc", "zstd-sys", @@ -9523,11 +9852,12 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "1.4.15+zstd.1.4.4" +version = "1.4.17+zstd.1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89719b034dc22d240d5b407fb0a3fe6d29952c181cff9a9f95c0bd40b4f8f7d8" +checksum = "b89249644df056b522696b1bb9e7c18c87e8ffa3e2f0dc3b0155875d6498f01b" dependencies = [ "cc", "glob 0.3.0", + "itertools 0.9.0", "libc", ] diff --git a/Cargo.toml b/Cargo.toml index 782cdcd23a4e6..d1c7339b993fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ members = [ "client/executor/runtime-test", "client/finality-grandpa", "client/informant", + "client/light", "client/tracing", "client/keystore", "client/network", @@ -59,6 +60,7 @@ members = [ "utils/wasm-builder-runner", "frame/assets", "frame/aura", + "frame/atomic-swap", "frame/authority-discovery", "frame/authorship", "frame/babe", diff --git a/README.md b/README.md index 2b62cbaa717af..5764722373d43 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,9 @@ The security policy and procedures can be found in [`docs/SECURITY.md`](docs/SEC ## License -Substrate Client (`/client/*` / `sc-*`) is licensed under [GPL v3.0 with a classpath linking exception](LICENSE-GPL3), primitives (`sp-*`), FRAME (`frame-*`) and pallets (`pallets-*`), binaries (`/bin`) and all other utilities are licensed under [Apache 2.0](LICENSE-APACHE2). +- Substrate Primitives (`sp-*`), Frame (`frame-*`) and the pallets (`pallets-*`), binaries (`/bin`) and all other utilities are licensed under [Apache 2.0](LICENSE-APACHE2). +- Substrate Client (`/client/*` / `sc-*`) is licensed under [GPL v3.0 with a classpath linking exception](LICENSE-GPL3). + +The reason for the split-licensing is to ensure that for the vast majority of teams using Substrate to create feature-chains, then all changes can be made entirely in Apache2-licensed code, allowing teams full freedom over what and how they release and giving licensing clarity to commercial teams. + +In the interests of the community, we require any deeper improvements made to Substrate's core logic (e.g. Substrate's internal consensus, crypto or database code) to be contributed back so everyone can benefit. diff --git a/bin/node-template/node/Cargo.toml b/bin/node-template/node/Cargo.toml index 88cdc6d608ec1..52fc1b4f8dacc 100644 --- a/bin/node-template/node/Cargo.toml +++ b/bin/node-template/node/Cargo.toml @@ -21,10 +21,10 @@ log = "0.4.8" structopt = "0.3.8" parking_lot = "0.10.0" -sc-cli = { version = "0.8.0-rc3", path = "../../../client/cli" } +sc-cli = { version = "0.8.0-rc3", path = "../../../client/cli", features = ["wasmtime"] } sp-core = { version = "2.0.0-rc3", path = "../../../primitives/core" } -sc-executor = { version = "0.8.0-rc3", path = "../../../client/executor" } -sc-service = { version = "0.8.0-rc3", path = "../../../client/service" } +sc-executor = { version = "0.8.0-rc3", path = "../../../client/executor", features = ["wasmtime"] } +sc-service = { version = "0.8.0-rc3", path = "../../../client/service", features = ["wasmtime"] } sp-inherents = { version = "2.0.0-rc3", path = "../../../primitives/inherents" } sc-transaction-pool = { version = "2.0.0-rc3", path = "../../../client/transaction-pool" } sp-transaction-pool = { version = "2.0.0-rc3", path = "../../../primitives/transaction-pool" } diff --git a/bin/node-template/node/src/lib.rs b/bin/node-template/node/src/lib.rs new file mode 100644 index 0000000000000..38e43372ca3ff --- /dev/null +++ b/bin/node-template/node/src/lib.rs @@ -0,0 +1,2 @@ +pub mod chain_spec; +pub mod service; diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index 17606e29239bf..e330c17b244b0 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -111,7 +111,7 @@ pub fn new_full(config: Configuration) -> Result>; Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, provider)) as _) })? - .build()?; + .build_full()?; if role.is_authority() { let proposer = sc_basic_authorship::ProposerFactory::new( @@ -142,13 +142,13 @@ pub fn new_full(config: Configuration) -> Result Result Result>; Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, provider)) as _) })? - .build() + .build_light() } diff --git a/bin/node-template/pallets/template/Cargo.toml b/bin/node-template/pallets/template/Cargo.toml index 8c4f98d85b739..714c9d93a90a1 100644 --- a/bin/node-template/pallets/template/Cargo.toml +++ b/bin/node-template/pallets/template/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME pallet template" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } [dependencies.frame-support] default-features = false @@ -23,6 +23,7 @@ path = "../../../../frame/support" default-features = false version = "2.0.0-rc3" path = "../../../../frame/system" + [dev-dependencies.sp-core] default-features = false version = "2.0.0-rc3" diff --git a/bin/node-template/pallets/template/src/mock.rs b/bin/node-template/pallets/template/src/mock.rs index 0da96c10dcc5a..8904679a29d45 100644 --- a/bin/node-template/pallets/template/src/mock.rs +++ b/bin/node-template/pallets/template/src/mock.rs @@ -24,6 +24,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); } impl system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Call = (); type Index = u64; @@ -45,7 +46,8 @@ impl system::Trait for Test { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } impl Trait for Test { diff --git a/bin/node-template/runtime/Cargo.toml b/bin/node-template/runtime/Cargo.toml index 9042edc8fa57f..16bb0fe0cbdb0 100644 --- a/bin/node-template/runtime/Cargo.toml +++ b/bin/node-template/runtime/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } aura = { version = "2.0.0-rc3", default-features = false, package = "pallet-aura", path = "../../../frame/aura" } balances = { version = "2.0.0-rc3", default-features = false, package = "pallet-balances", path = "../../../frame/balances" } diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index 2fe50ee25e44f..0ca0fcdbd6a62 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -135,6 +135,8 @@ parameter_types! { } impl system::Trait for Runtime { + /// The basic call filter to use in dispatchable. + type BaseCallFilter = (); /// The identifier used to distinguish between accounts. type AccountId = AccountId; /// The aggregated dispatch type that is available for extrinsics. @@ -182,7 +184,8 @@ impl system::Trait for Runtime { /// This type is being generated by `construct_runtime!`. type ModuleToIndex = ModuleToIndex; /// What to do if a new account is created. - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); /// What to do if an account is fully reaped from the system. type OnKilledAccount = (); /// The data to be stored in an account. diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index 74edf2f257bfa..4e2c0151b7ea5 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -34,7 +34,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] # third-party dependencies -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } serde = { version = "1.0.102", features = ["derive"] } futures = { version = "0.3.1", features = ["compat"] } hex-literal = "0.2.1" diff --git a/bin/node/cli/res/flaming-fir.json b/bin/node/cli/res/flaming-fir.json index 7cc2c11c327e1..5f2eb265880ef 100644 --- a/bin/node/cli/res/flaming-fir.json +++ b/bin/node/cli/res/flaming-fir.json @@ -14,7 +14,7 @@ ], "telemetryEndpoints": [ [ - "/dns4/telemetry.polkadot.io/tcp/443/x-parity-wss/%2Fsubmit%2F", + "/dns/telemetry.polkadot.io/tcp/443/x-parity-wss/%2Fsubmit%2F", 0 ] ], diff --git a/bin/node/cli/src/cli.rs b/bin/node/cli/src/cli.rs index 0156faf47ee4a..29e916fe0180e 100644 --- a/bin/node/cli/src/cli.rs +++ b/bin/node/cli/src/cli.rs @@ -20,7 +20,7 @@ use sc_cli::RunCmd; use structopt::StructOpt; /// An overarching CLI command definition. -#[derive(Clone, Debug, StructOpt)] +#[derive(Debug, StructOpt)] pub struct Cli { /// Possible subcommand with parameters. #[structopt(subcommand)] @@ -31,7 +31,7 @@ pub struct Cli { } /// Possible subcommands of the main binary. -#[derive(Clone, Debug, StructOpt)] +#[derive(Debug, StructOpt)] pub enum Subcommand { /// A set of base subcommands handled by `sc_cli`. #[structopt(flatten)] diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index af4cc5e1279f3..afc9e23d68705 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -21,7 +21,6 @@ //! Service implementation. Specialized wrapper over substrate service. use std::sync::Arc; - use sc_consensus_babe; use grandpa::{ self, FinalityProofProvider as GrandpaFinalityProofProvider, StorageAndProofProvider, @@ -157,6 +156,7 @@ macro_rules! new_full { use futures::prelude::*; use sc_network::Event; use sc_client_api::ExecutorProvider; + use sp_core::traits::BareCryptoStorePtr; let ( role, @@ -179,7 +179,7 @@ macro_rules! new_full { let provider = client as Arc>; Ok(Arc::new(grandpa::FinalityProofProvider::new(backend, provider)) as _) })? - .build()?; + .build_full()?; let (block_import, grandpa_link, babe_link) = import_setup.take() .expect("Link Half and Block Import are present for Full Services or setup failed before. qed"); @@ -217,7 +217,7 @@ macro_rules! new_full { }; let babe = sc_consensus_babe::start_babe(babe_config)?; - service.spawn_essential_task("babe-proposer", babe); + service.spawn_essential_task_handle().spawn_blocking("babe-proposer", babe); } // Spawn authority discovery module. @@ -250,13 +250,13 @@ macro_rules! new_full { service.prometheus_registry(), ); - service.spawn_task("authority-discovery", authority_discovery); + service.spawn_task_handle().spawn("authority-discovery", authority_discovery); } // if the node isn't actively participating in consensus then it doesn't // need a keystore, regardless of which protocol we use below. let keystore = if role.is_authority() { - Some(service.keystore()) + Some(service.keystore() as BareCryptoStorePtr) } else { None }; @@ -292,7 +292,7 @@ macro_rules! new_full { // the GRANDPA voter task is considered infallible, i.e. // if it fails we take down the service with it. - service.spawn_essential_task( + service.spawn_essential_task_handle().spawn_blocking( "grandpa-voter", grandpa::run_grandpa_voter(grandpa_config)? ); @@ -405,7 +405,7 @@ pub fn new_light(config: Configuration) Ok(node_rpc::create_light(light_deps)) })? - .build()?; + .build_light()?; Ok(service) } diff --git a/bin/node/cli/tests/temp_base_path_works.rs b/bin/node/cli/tests/temp_base_path_works.rs new file mode 100644 index 0000000000000..9351568d87955 --- /dev/null +++ b/bin/node/cli/tests/temp_base_path_works.rs @@ -0,0 +1,72 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +#![cfg(unix)] + +use assert_cmd::cargo::cargo_bin; +use nix::sys::signal::{kill, Signal::SIGINT}; +use nix::unistd::Pid; +use regex::Regex; +use std::convert::TryInto; +use std::io::Read; +use std::path::PathBuf; +use std::process::{Command, Stdio}; +use std::thread; +use std::time::Duration; + +pub mod common; + +#[test] +fn temp_base_path_works() { + let mut cmd = Command::new(cargo_bin("substrate")); + + let mut cmd = cmd + .args(&["--dev", "--tmp"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap(); + + // Let it produce some blocks. + thread::sleep(Duration::from_secs(30)); + assert!( + cmd.try_wait().unwrap().is_none(), + "the process should still be running" + ); + + // Stop the process + kill(Pid::from_raw(cmd.id().try_into().unwrap()), SIGINT).unwrap(); + assert!(common::wait_for(&mut cmd, 40) + .map(|x| x.success()) + .unwrap_or_default()); + + // Ensure the database has been deleted + let mut stderr = String::new(); + cmd.stderr.unwrap().read_to_string(&mut stderr).unwrap(); + let re = Regex::new(r"Database: .+ at (\S+)").unwrap(); + let db_path = PathBuf::from( + re.captures(stderr.as_str()) + .unwrap() + .get(1) + .unwrap() + .as_str() + .to_string(), + ); + + assert!(!db_path.exists()); +} diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 64799129fc9e4..2c5a5db281ed3 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } node-primitives = { version = "2.0.0-rc3", path = "../primitives" } node-runtime = { version = "2.0.0-rc3", path = "../runtime" } sc-executor = { version = "0.8.0-rc3", path = "../../../client/executor" } diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index 2bb444b47c9be..e4de98d90e94d 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -19,14 +19,11 @@ use codec::{Encode, Decode, Joiner}; use frame_support::{ StorageValue, StorageMap, traits::Currency, - weights::{ - GetDispatchInfo, DispatchInfo, DispatchClass, constants::ExtrinsicBaseWeight, - WeightToFeePolynomial, - }, + weights::{GetDispatchInfo, DispatchInfo, DispatchClass}, }; use sp_core::{NeverNativeValue, traits::Externalities, storage::well_known_keys}; use sp_runtime::{ - ApplyExtrinsicResult, FixedI128, FixedPointNumber, + ApplyExtrinsicResult, traits::Hash as HashT, transaction_validity::InvalidTransaction, }; @@ -35,7 +32,7 @@ use frame_system::{self, EventRecord, Phase}; use node_runtime::{ Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, - System, TransactionPayment, Event, TransactionByteFee, + System, TransactionPayment, Event, constants::currency::*, }; use node_primitives::{Balance, Hash}; @@ -52,16 +49,17 @@ use self::common::{*, sign}; /// test code paths that differ between native and wasm versions. pub const BLOATY_CODE: &[u8] = node_runtime::WASM_BINARY_BLOATY; -/// Default transfer fee -fn transfer_fee(extrinsic: &E, fee_multiplier: FixedI128) -> Balance { - let length_fee = TransactionByteFee::get() * (extrinsic.encode().len() as Balance); - - let base_weight = ExtrinsicBaseWeight::get(); - let base_fee = ::WeightToFee::calc(&base_weight); - let weight = default_transfer_call().get_dispatch_info().weight; - let weight_fee = ::WeightToFee::calc(&weight); - - base_fee + fee_multiplier.saturating_mul_acc_int(length_fee + weight_fee) +/// Default transfer fee. This will use the same logic that is implemented in transaction-payment module. +/// +/// Note that reads the multiplier from storage directly, hence to get the fee of `extrinsic` +/// at block `n`, it must be called prior to executing block `n` to do the calculation with the +/// correct multiplier. +fn transfer_fee(extrinsic: &E) -> Balance { + TransactionPayment::compute_fee( + extrinsic.encode().len() as u32, + &default_transfer_call().get_dispatch_info(), + 0, + ) } fn xt() -> UncheckedExtrinsic { @@ -242,7 +240,7 @@ fn successful_execution_with_native_equivalent_code_gives_ok() { ).0; assert!(r.is_ok()); - let fm = t.execute_with(TransactionPayment::next_fee_multiplier); + let fees = t.execute_with(|| transfer_fee(&xt())); let r = executor_call:: _>( &mut t, @@ -254,7 +252,6 @@ fn successful_execution_with_native_equivalent_code_gives_ok() { assert!(r.is_ok()); t.execute_with(|| { - let fees = transfer_fee(&xt(), fm); assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); @@ -286,7 +283,7 @@ fn successful_execution_with_foreign_code_gives_ok() { ).0; assert!(r.is_ok()); - let fm = t.execute_with(TransactionPayment::next_fee_multiplier); + let fees = t.execute_with(|| transfer_fee(&xt())); let r = executor_call:: _>( &mut t, @@ -298,7 +295,6 @@ fn successful_execution_with_foreign_code_gives_ok() { assert!(r.is_ok()); t.execute_with(|| { - let fees = transfer_fee(&xt(), fm); assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); @@ -311,7 +307,7 @@ fn full_native_block_import_works() { let (block1, block2) = blocks(); let mut alice_last_known_balance: Balance = Default::default(); - let mut fm = t.execute_with(TransactionPayment::next_fee_multiplier); + let mut fees = t.execute_with(|| transfer_fee(&xt())); executor_call:: _>( &mut t, @@ -322,7 +318,6 @@ fn full_native_block_import_works() { ).0.unwrap(); t.execute_with(|| { - let fees = transfer_fee(&xt(), fm); assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); alice_last_known_balance = Balances::total_balance(&alice()); @@ -361,7 +356,7 @@ fn full_native_block_import_works() { assert_eq!(System::events(), events); }); - fm = t.execute_with(TransactionPayment::next_fee_multiplier); + fees = t.execute_with(|| transfer_fee(&xt())); executor_call:: _>( &mut t, @@ -372,7 +367,6 @@ fn full_native_block_import_works() { ).0.unwrap(); t.execute_with(|| { - let fees = transfer_fee(&xt(), fm); assert_eq!( Balances::total_balance(&alice()), alice_last_known_balance - 10 * DOLLARS - fees, @@ -450,7 +444,7 @@ fn full_wasm_block_import_works() { let (block1, block2) = blocks(); let mut alice_last_known_balance: Balance = Default::default(); - let mut fm = t.execute_with(TransactionPayment::next_fee_multiplier); + let mut fees = t.execute_with(|| transfer_fee(&xt())); executor_call:: _>( &mut t, @@ -461,12 +455,12 @@ fn full_wasm_block_import_works() { ).0.unwrap(); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); alice_last_known_balance = Balances::total_balance(&alice()); }); - fm = t.execute_with(TransactionPayment::next_fee_multiplier); + fees = t.execute_with(|| transfer_fee(&xt())); executor_call:: _>( &mut t, @@ -479,11 +473,11 @@ fn full_wasm_block_import_works() { t.execute_with(|| { assert_eq!( Balances::total_balance(&alice()), - alice_last_known_balance - 10 * DOLLARS - transfer_fee(&xt(), fm), + alice_last_known_balance - 10 * DOLLARS - fees, ); assert_eq!( Balances::total_balance(&bob()), - 179 * DOLLARS - 1 * transfer_fee(&xt(), fm), + 179 * DOLLARS - 1 * fees, ); }); } @@ -755,7 +749,7 @@ fn successful_execution_gives_ok() { assert_eq!(Balances::total_balance(&alice()), 111 * DOLLARS); }); - let fm = t.execute_with(TransactionPayment::next_fee_multiplier); + let fees = t.execute_with(|| transfer_fee(&xt())); let r = executor_call:: _>( &mut t, @@ -770,7 +764,6 @@ fn successful_execution_gives_ok() { .expect("Extrinsic failed"); t.execute_with(|| { - let fees = transfer_fee(&xt(), fm); assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); diff --git a/bin/node/executor/tests/fees.rs b/bin/node/executor/tests/fees.rs index 280408357ed2e..8f828263c5bdb 100644 --- a/bin/node/executor/tests/fees.rs +++ b/bin/node/executor/tests/fees.rs @@ -22,9 +22,9 @@ use frame_support::{ weights::{GetDispatchInfo, constants::ExtrinsicBaseWeight, IdentityFee, WeightToFeePolynomial}, }; use sp_core::NeverNativeValue; -use sp_runtime::{FixedPointNumber, FixedI128, Perbill}; +use sp_runtime::{Perbill, FixedPointNumber}; use node_runtime::{ - CheckedExtrinsic, Call, Runtime, Balances, TransactionPayment, + CheckedExtrinsic, Call, Runtime, Balances, TransactionPayment, Multiplier, TransactionByteFee, constants::currency::*, }; @@ -38,8 +38,8 @@ use self::common::{*, sign}; fn fee_multiplier_increases_and_decreases_on_big_weight() { let mut t = new_test_ext(COMPACT_CODE, false); - // initial fee multiplier must be zero - let mut prev_multiplier = FixedI128::from_inner(0); + // initial fee multiplier must be one. + let mut prev_multiplier = Multiplier::one(); t.execute_with(|| { assert_eq!(TransactionPayment::next_fee_multiplier(), prev_multiplier); @@ -59,7 +59,7 @@ fn fee_multiplier_increases_and_decreases_on_big_weight() { }, CheckedExtrinsic { signed: Some((charlie(), signed_extra(0, 0))), - function: Call::System(frame_system::Call::fill_block(Perbill::from_percent(90))), + function: Call::System(frame_system::Call::fill_block(Perbill::from_percent(60))), } ] ); @@ -122,7 +122,7 @@ fn fee_multiplier_increases_and_decreases_on_big_weight() { } #[test] -fn transaction_fee_is_correct_ultimate() { +fn transaction_fee_is_correct() { // This uses the exact values of substrate-node. // // weight of transfer call as of now: 1_000_000 diff --git a/bin/node/inspect/Cargo.toml b/bin/node/inspect/Cargo.toml index 1c2f316b40479..91202191f18df 100644 --- a/bin/node/inspect/Cargo.toml +++ b/bin/node/inspect/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } derive_more = "0.99" log = "0.4.8" sc-cli = { version = "0.8.0-rc3", path = "../../../client/cli" } diff --git a/bin/node/inspect/src/cli.rs b/bin/node/inspect/src/cli.rs index 4475d31755fdc..d66644bab52fa 100644 --- a/bin/node/inspect/src/cli.rs +++ b/bin/node/inspect/src/cli.rs @@ -23,7 +23,7 @@ use sc_cli::{ImportParams, SharedParams}; use structopt::StructOpt; /// The `inspect` command used to print decoded chain data. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct InspectCmd { #[allow(missing_docs)] #[structopt(flatten)] @@ -39,7 +39,7 @@ pub struct InspectCmd { } /// A possible inspect sub-commands. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub enum InspectSubCmd { /// Decode block with native version of runtime and print out the details. Block { diff --git a/bin/node/primitives/Cargo.toml b/bin/node/primitives/Cargo.toml index ec8d58fe27c91..75a8cbb33262e 100644 --- a/bin/node/primitives/Cargo.toml +++ b/bin/node/primitives/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } frame-system = { version = "2.0.0-rc3", default-features = false, path = "../../../frame/system" } sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path = "../../../primitives/application-crypto" } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../../primitives/core" } diff --git a/bin/node/rpc/Cargo.toml b/bin/node/rpc/Cargo.toml index 0c6c913b137ad..2bac8b67409d2 100644 --- a/bin/node/rpc/Cargo.toml +++ b/bin/node/rpc/Cargo.toml @@ -31,3 +31,4 @@ sp-blockchain = { version = "2.0.0-rc3", path = "../../../primitives/blockchain" sc-finality-grandpa = { version = "0.8.0-rc3", path = "../../../client/finality-grandpa" } sc-finality-grandpa-rpc = { version = "0.8.0-rc3", path = "../../../client/finality-grandpa/rpc" } sc-rpc-api = { version = "0.8.0-rc3", path = "../../../client/rpc-api" } +sp-block-builder = { version = "2.0.0-rc3", path = "../../../primitives/block-builder" } diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index 259a792441d40..9b6b5991748f9 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -30,7 +30,7 @@ #![warn(missing_docs)] -use std::{sync::Arc, fmt}; +use std::sync::Arc; use node_primitives::{Block, BlockNumber, AccountId, Index, Balance, Hash}; use node_runtime::UncheckedExtrinsic; @@ -46,6 +46,7 @@ use sc_consensus_babe_rpc::BabeRpcHandler; use sc_finality_grandpa::{SharedVoterState, SharedAuthoritySet}; use sc_finality_grandpa_rpc::GrandpaRpcHandler; use sc_rpc_api::DenyUnsafe; +use sp_block_builder::BlockBuilder; /// Light client extra dependencies. pub struct LightDeps { @@ -104,7 +105,7 @@ pub fn create_full( C::Api: pallet_contracts_rpc::ContractsRuntimeApi, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, C::Api: BabeApi, - ::Error: fmt::Debug, + C::Api: BlockBuilder, P: TransactionPool + 'static, M: jsonrpc_core::Metadata + Default, SC: SelectChain +'static, @@ -133,7 +134,7 @@ pub fn create_full( } = grandpa; io.extend_with( - SystemApi::to_delegate(FullSystem::new(client.clone(), pool)) + SystemApi::to_delegate(FullSystem::new(client.clone(), pool, deny_unsafe)) ); // Making synchronous calls in light client freezes the browser currently, // more context: https://github.com/paritytech/substrate/pull/3480 @@ -185,7 +186,7 @@ pub fn create_light( } = deps; let mut io = jsonrpc_core::IoHandler::default(); io.extend_with( - SystemApi::::to_delegate(LightSystem::new(client, remote_blockchain, fetcher, pool)) + SystemApi::::to_delegate(LightSystem::new(client, remote_blockchain, fetcher, pool)) ); io diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index ebe3196dd744e..b26b53cd6c589 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # third-party dependencies -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } integer-sqrt = { version = "0.1.2" } serde = { version = "1.0.102", optional = true } static_assertions = "1.1.0" @@ -111,6 +111,7 @@ std = [ "pallet-membership/std", "pallet-multisig/std", "pallet-identity/std", + "pallet-scheduler/std", "node-primitives/std", "sp-offchain/std", "pallet-offences/std", diff --git a/bin/node/runtime/src/impls.rs b/bin/node/runtime/src/impls.rs index c8f42f3f26649..039093ddee697 100644 --- a/bin/node/runtime/src/impls.rs +++ b/bin/node/runtime/src/impls.rs @@ -18,11 +18,9 @@ //! Some configurable implementations as associated type for the substrate runtime. use node_primitives::Balance; -use sp_runtime::traits::{Convert, Saturating}; -use sp_runtime::{FixedPointNumber, Perquintill}; -use frame_support::traits::{OnUnbalanced, Currency, Get}; -use pallet_transaction_payment::Multiplier; -use crate::{Balances, System, Authorship, MaximumBlockWeight, NegativeImbalance}; +use sp_runtime::traits::Convert; +use frame_support::traits::{OnUnbalanced, Currency}; +use crate::{Balances, Authorship, NegativeImbalance}; pub struct Author; impl OnUnbalanced for Author { @@ -47,89 +45,63 @@ impl Convert for CurrencyToVoteHandler { fn convert(x: u128) -> Balance { x * Self::factor() } } -/// Update the given multiplier based on the following formula -/// -/// diff = (previous_block_weight - target_weight)/max_weight -/// v = 0.00004 -/// next_weight = weight * (1 + (v * diff) + (v * diff)^2 / 2) -/// -/// Where `target_weight` must be given as the `Get` implementation of the `T` generic type. -/// https://research.web3.foundation/en/latest/polkadot/Token%20Economics/#relay-chain-transaction-fees -pub struct TargetedFeeAdjustment(sp_std::marker::PhantomData); - -impl> Convert for TargetedFeeAdjustment { - fn convert(multiplier: Multiplier) -> Multiplier { - let max_weight = MaximumBlockWeight::get(); - let block_weight = System::block_weight().total().min(max_weight); - let target_weight = (T::get() * max_weight) as u128; - let block_weight = block_weight as u128; - - // determines if the first_term is positive - let positive = block_weight >= target_weight; - let diff_abs = block_weight.max(target_weight) - block_weight.min(target_weight); - // safe, diff_abs cannot exceed u64. - let diff = Multiplier::saturating_from_rational(diff_abs, max_weight.max(1)); - let diff_squared = diff.saturating_mul(diff); - - // 0.00004 = 4/100_000 = 40_000/10^9 - let v = Multiplier::saturating_from_rational(4, 100_000); - // 0.00004^2 = 16/10^10 Taking the future /2 into account... 8/10^10 - let v_squared_2 = Multiplier::saturating_from_rational(8, 10_000_000_000u64); - - let first_term = v.saturating_mul(diff); - let second_term = v_squared_2.saturating_mul(diff_squared); - - if positive { - // Note: this is merely bounded by how big the multiplier and the inner value can go, - // not by any economical reasoning. - let excess = first_term.saturating_add(second_term); - multiplier.saturating_add(excess) - } else { - // Defensive-only: first_term > second_term. Safe subtraction. - let negative = first_term.saturating_sub(second_term); - multiplier.saturating_sub(negative) - // despite the fact that apply_to saturates weight (final fee cannot go below 0) - // it is crucially important to stop here and don't further reduce the weight fee - // multiplier. While at -1, it means that the network is so un-congested that all - // transactions have no weight fee. We stop here and only increase if the network - // became more busy. - .max(Multiplier::saturating_from_integer(-1)) - } - } -} - #[cfg(test)] -mod tests { +mod multiplier_tests { use super::*; - use sp_runtime::assert_eq_error_rate; - use crate::{MaximumBlockWeight, AvailableBlockRatio, Runtime}; - use crate::{constants::currency::*, TransactionPayment, TargetBlockFullness}; + use sp_runtime::{assert_eq_error_rate, FixedPointNumber}; + use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment}; + + use crate::{ + constants::{currency::*, time::*}, + TransactionPayment, MaximumBlockWeight, AvailableBlockRatio, Runtime, TargetBlockFullness, + AdjustmentVariable, System, MinimumMultiplier, + }; use frame_support::weights::{Weight, WeightToFeePolynomial}; fn max() -> Weight { - MaximumBlockWeight::get() + AvailableBlockRatio::get() * MaximumBlockWeight::get() + } + + fn min_multiplier() -> Multiplier { + MinimumMultiplier::get() } fn target() -> Weight { TargetBlockFullness::get() * max() } - // poc reference implementation. - fn fee_multiplier_update(block_weight: Weight, previous: Multiplier) -> Multiplier { + // update based on runtime impl. + fn runtime_multiplier_update(fm: Multiplier) -> Multiplier { + TargetedFeeAdjustment::< + Runtime, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + >::convert(fm) + } + + // update based on reference impl. + fn truth_value_update(block_weight: Weight, previous: Multiplier) -> Multiplier { + let accuracy = Multiplier::accuracy() as f64; + let previous_float = previous.into_inner() as f64 / accuracy; + // bump if it is zero. + let previous_float = previous_float.max(min_multiplier().into_inner() as f64 / accuracy); + // maximum tx weight let m = max() as f64; // block weight always truncated to max weight let block_weight = (block_weight as f64).min(m); - let v: f64 = 0.00004; + let v: f64 = AdjustmentVariable::get().to_fraction(); // Ideal saturation in terms of weight let ss = target() as f64; // Current saturation in terms of weight let s = block_weight; - let fm = v * (s/m - ss/m) + v.powi(2) * (s/m - ss/m).powi(2) / 2.0; - let addition_fm = Multiplier::from_inner((fm * Multiplier::accuracy() as f64).round() as i128); - previous.saturating_add(addition_fm) + let t1 = v * (s/m - ss/m); + let t2 = v.powi(2) * (s/m - ss/m).powi(2) / 2.0; + let next_float = previous_float * (1.0 + t1 + t2); + Multiplier::from_fraction(next_float) } fn run_with_system_weight(w: Weight, assertions: F) where F: Fn() -> () { @@ -142,11 +114,12 @@ mod tests { } #[test] - fn fee_multiplier_update_poc_works() { - let fm = Multiplier::saturating_from_rational(0, 1); + fn truth_value_update_poc_works() { + let fm = Multiplier::saturating_from_rational(1, 2); let test_set = vec![ (0, fm.clone()), (100, fm.clone()), + (1000, fm.clone()), (target(), fm.clone()), (max() / 2, fm.clone()), (max(), fm.clone()), @@ -154,37 +127,71 @@ mod tests { test_set.into_iter().for_each(|(w, fm)| { run_with_system_weight(w, || { assert_eq_error_rate!( - fee_multiplier_update(w, fm), - TargetedFeeAdjustment::::convert(fm), - // Error is only 1 in 10^18 - Multiplier::from_inner(1), + truth_value_update(w, fm), + runtime_multiplier_update(fm), + // Error is only 1 in 100^18 + Multiplier::from_inner(100), ); }) }) } #[test] - fn empty_chain_simulation() { - // just a few txs per_block. - let block_weight = 0; - run_with_system_weight(block_weight, || { - let mut fm = Multiplier::default(); + fn multiplier_can_grow_from_zero() { + // if the min is too small, then this will not change, and we are doomed forever. + // the weight is 1/100th bigger than target. + run_with_system_weight(target() * 101 / 100, || { + let next = runtime_multiplier_update(min_multiplier()); + assert!(next > min_multiplier(), "{:?} !>= {:?}", next, min_multiplier()); + }) + } + + #[test] + fn multiplier_cannot_go_below_limit() { + // will not go any further below even if block is empty. + run_with_system_weight(0, || { + let next = runtime_multiplier_update(min_multiplier()); + assert_eq!(next, min_multiplier()); + }) + } + + #[test] + fn time_to_reach_zero() { + // blocks per 24h in substrate-node: 28,800 (k) + // s* = 0.1875 + // The bound from the research in an empty chain is: + // v <~ (p / k(0 - s*)) + // p > v * k * -0.1875 + // to get p == -1 we'd need + // -1 > 0.00001 * k * -0.1875 + // 1 < 0.00001 * k * 0.1875 + // 10^9 / 1875 < k + // k > 533_333 ~ 18,5 days. + run_with_system_weight(0, || { + // start from 1, the default. + let mut fm = Multiplier::one(); let mut iterations: u64 = 0; loop { - let next = TargetedFeeAdjustment::::convert(fm); + let next = runtime_multiplier_update(fm); fm = next; - if fm == Multiplier::saturating_from_integer(-1) { break; } + if fm == min_multiplier() { break; } iterations += 1; } - println!("iteration {}, new fm = {:?}. Weight fee is now zero", iterations, fm); - assert!(iterations > 50_000, "This assertion is just a warning; Don't panic. \ - Current substrate/polkadot node are configured with a _slow adjusting fee_ \ - mechanism. Hence, it is really unlikely that fees collapse to zero even on an \ - empty chain in less than at least of couple of thousands of empty blocks. But this \ - simulation indicates that fees collapsed to zero after {} almost-empty blocks. \ - Check it", - iterations, - ); + assert!(iterations > 533_333); + }) + } + + #[test] + fn min_change_per_day() { + run_with_system_weight(max(), || { + let mut fm = Multiplier::one(); + // See the example in the doc of `TargetedFeeAdjustment`. are at least 0.234, hence + // `fm > 1.234`. + for _ in 0..DAYS { + let next = runtime_multiplier_update(fm); + fm = next; + } + assert!(fm > Multiplier::saturating_from_rational(1234, 1000)); }) } @@ -196,17 +203,17 @@ mod tests { // almost full. The entire quota of normal transactions is taken. let block_weight = AvailableBlockRatio::get() * max() - 100; - // Default substrate minimum. - let tx_weight = 10_000; + // Default substrate weight. + let tx_weight = frame_support::weights::constants::ExtrinsicBaseWeight::get(); run_with_system_weight(block_weight, || { // initial value configured on module - let mut fm = Multiplier::default(); + let mut fm = Multiplier::one(); assert_eq!(fm, TransactionPayment::next_fee_multiplier()); let mut iterations: u64 = 0; loop { - let next = TargetedFeeAdjustment::::convert(fm); + let next = runtime_multiplier_update(fm); // if no change, panic. This should never happen in this case. if fm == next { panic!("The fee should ever increase"); } fm = next; @@ -230,95 +237,86 @@ mod tests { #[test] fn stateless_weight_mul() { - // This test will show that heavy blocks have a weight multiplier greater than 0 - // and light blocks will have a weight multiplier less than 0. + let fm = Multiplier::saturating_from_rational(1, 2); run_with_system_weight(target() / 4, || { - // `fee_multiplier_update` is enough as it is the absolute truth value. - let next = TargetedFeeAdjustment::::convert(Multiplier::default()); - assert_eq!( + let next = runtime_multiplier_update(fm); + assert_eq_error_rate!( next, - fee_multiplier_update(target() / 4 ,Multiplier::default()) + truth_value_update(target() / 4 , fm), + Multiplier::from_inner(100), ); - // Light block. Fee is reduced a little. - assert!(next < Multiplier::zero()) + // Light block. Multiplier is reduced a little. + assert!(next < fm); }); + run_with_system_weight(target() / 2, || { - let next = TargetedFeeAdjustment::::convert(Multiplier::default()); - assert_eq!( + let next = runtime_multiplier_update(fm); + assert_eq_error_rate!( next, - fee_multiplier_update(target() / 2 ,Multiplier::default()) + truth_value_update(target() / 2 , fm), + Multiplier::from_inner(100), ); - - // Light block. Fee is reduced a little. - assert!(next < Multiplier::zero()) + // Light block. Multiplier is reduced a little. + assert!(next < fm); }); run_with_system_weight(target(), || { - // ideal. Original fee. No changes. - let next = TargetedFeeAdjustment::::convert(Multiplier::default()); - assert_eq!(next, Multiplier::zero()) + let next = runtime_multiplier_update(fm); + assert_eq_error_rate!( + next, + truth_value_update(target(), fm), + Multiplier::from_inner(100), + ); + // ideal. No changes. + assert_eq!(next, fm) }); run_with_system_weight(target() * 2, || { // More than ideal. Fee is increased. - let next = TargetedFeeAdjustment::::convert(Multiplier::default()); - assert_eq!( + let next = runtime_multiplier_update(fm); + assert_eq_error_rate!( next, - fee_multiplier_update(target() * 2 ,Multiplier::default()) + truth_value_update(target() * 2 , fm), + Multiplier::from_inner(100), ); // Heavy block. Fee is increased a little. - assert!(next > Multiplier::zero()) + assert!(next > fm); }); } #[test] - fn stateful_weight_mul_grow_to_infinity() { + fn weight_mul_grow_on_big_block() { run_with_system_weight(target() * 2, || { - let mut original = Multiplier::default(); + let mut original = Multiplier::zero(); let mut next = Multiplier::default(); (0..1_000).for_each(|_| { - next = TargetedFeeAdjustment::::convert(original); - assert_eq!( + next = runtime_multiplier_update(original); + assert_eq_error_rate!( next, - fee_multiplier_update(target() * 2, original), + truth_value_update(target() * 2, original), + Multiplier::from_inner(100), ); // must always increase - assert!(next > original); + assert!(next > original, "{:?} !>= {:?}", next, original); original = next; }); }); } #[test] - fn stateful_weight_mil_collapse_to_minus_one() { - run_with_system_weight(0, || { - let mut original = Multiplier::default(); // 0 + fn weight_mul_decrease_on_small_block() { + run_with_system_weight(target() / 2, || { + let mut original = Multiplier::saturating_from_rational(1, 2); let mut next; - // decreases - next = TargetedFeeAdjustment::::convert(original); - assert_eq!( - next, - fee_multiplier_update(0, original), - ); - assert!(next < original); - original = next; - - // keeps decreasing - next = TargetedFeeAdjustment::::convert(original); - assert_eq!( - next, - fee_multiplier_update(0, original), - ); - assert!(next < original); - - // ... stops going down at -1 - assert_eq!( - TargetedFeeAdjustment::::convert(Multiplier::saturating_from_integer(-1)), - Multiplier::saturating_from_integer(-1) - ); + for _ in 0..100 { + // decreases + next = runtime_multiplier_update(original); + assert!(next < original, "{:?} !<= {:?}", next, original); + original = next; + } }) } @@ -347,8 +345,8 @@ mod tests { Weight::max_value(), ].into_iter().for_each(|i| { run_with_system_weight(i, || { - let next = TargetedFeeAdjustment::::convert(Multiplier::default()); - let truth = fee_multiplier_update(i, Multiplier::default()); + let next = runtime_multiplier_update(Multiplier::one()); + let truth = truth_value_update(i, Multiplier::one()); assert_eq_error_rate!(truth, next, Multiplier::from_inner(50_000_000)); }); }); @@ -359,7 +357,7 @@ mod tests { .into_iter() .for_each(|i| { run_with_system_weight(i, || { - let fm = TargetedFeeAdjustment::::convert(max_fm); + let fm = runtime_multiplier_update(max_fm); // won't grow. The convert saturates everything. assert_eq!(fm, max_fm); }) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index bcb6b6ed70ae2..5f70d04deca80 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -32,7 +32,8 @@ use frame_support::{ }, traits::{Currency, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness, LockIdentifier}, }; -use frame_support::traits::{Filter, InstanceFilter}; +use frame_system::{EnsureRoot, EnsureOneOf}; +use frame_support::traits::InstanceFilter; use codec::{Encode, Decode}; use sp_core::{ crypto::KeyTypeId, @@ -43,8 +44,8 @@ pub use node_primitives::{AccountId, Signature}; use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Index, Moment}; use sp_api::impl_runtime_apis; use sp_runtime::{ - Permill, Perbill, Perquintill, Percent, PerThing, ApplyExtrinsicResult, - impl_opaque_keys, generic, create_runtime_str, ModuleId, + Permill, Perbill, Perquintill, Percent, ApplyExtrinsicResult, + impl_opaque_keys, generic, create_runtime_str, ModuleId, FixedPointNumber, }; use sp_runtime::curve::PiecewiseLinear; use sp_runtime::transaction_validity::{TransactionValidity, TransactionSource, TransactionPriority}; @@ -60,6 +61,7 @@ use pallet_grandpa::fg_primitives; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo; +pub use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment}; use pallet_contracts_rpc_runtime_api::ContractExecResult; use pallet_session::{historical as pallet_session_historical}; use sp_inherents::{InherentData, CheckInherentsResult}; @@ -76,7 +78,7 @@ pub use pallet_staking::StakerStatus; /// Implementations of some helper traits passed into runtime modules as associated types. pub mod impls; -use impls::{CurrencyToVoteHandler, Author, TargetedFeeAdjustment}; +use impls::{CurrencyToVoteHandler, Author}; /// Constant values used within the runtime. pub mod constants; @@ -95,8 +97,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 251, - impl_version: 2, + spec_version: 254, + impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, }; @@ -112,15 +114,6 @@ pub fn native_version() -> NativeVersion { type NegativeImbalance = >::NegativeImbalance; -pub struct BaseFilter; -impl Filter for BaseFilter { - fn filter(_call: &Call) -> bool { - true - } -} -pub struct IsCallable; -frame_support::impl_filter_stack!(IsCallable, BaseFilter, Call, is_callable); - pub struct DealWithFees; impl OnUnbalanced for DealWithFees { fn on_unbalanceds(mut fees_then_tips: impl Iterator) { @@ -154,6 +147,7 @@ parameter_types! { const_assert!(AvailableBlockRatio::get().deconstruct() >= AVERAGE_ON_INITIALIZE_WEIGHT.deconstruct()); impl frame_system::Trait for Runtime { + type BaseCallFilter = (); type Origin = Origin; type Call = Call; type Index = Index; @@ -183,7 +177,6 @@ impl frame_system::Trait for Runtime { impl pallet_utility::Trait for Runtime { type Event = Event; type Call = Call; - type IsCallable = IsCallable; } parameter_types! { @@ -201,7 +194,6 @@ impl pallet_multisig::Trait for Runtime { type DepositBase = DepositBase; type DepositFactor = DepositFactor; type MaxSignatories = MaxSignatories; - type IsCallable = IsCallable; } parameter_types! { @@ -251,7 +243,6 @@ impl pallet_proxy::Trait for Runtime { type Event = Event; type Call = Call; type Currency = Balances; - type IsCallable = IsCallable; type ProxyType = ProxyType; type ProxyDepositBase = ProxyDepositBase; type ProxyDepositFactor = ProxyDepositFactor; @@ -306,23 +297,17 @@ impl pallet_balances::Trait for Runtime { parameter_types! { pub const TransactionByteFee: Balance = 10 * MILLICENTS; pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(1, 100_000); + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000_000u128); } -// for a sane configuration, this should always be less than `AvailableBlockRatio`. -const_assert!( - TargetBlockFullness::get().deconstruct() < - (AvailableBlockRatio::get().deconstruct() as ::Inner) - * (::ACCURACY / ::ACCURACY as ::Inner) -); - impl pallet_transaction_payment::Trait for Runtime { type Currency = Balances; type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; - // In the Substrate node, a weight of 10_000_000 (smallest non-zero weight) - // is mapped to 10_000_000 units of fees, hence: type WeightToFee = IdentityFee; - type FeeMultiplierUpdate = TargetedFeeAdjustment; + type FeeMultiplierUpdate = + TargetedFeeAdjustment; } parameter_types! { @@ -411,7 +396,11 @@ impl pallet_staking::Trait for Runtime { type BondingDuration = BondingDuration; type SlashDeferDuration = SlashDeferDuration; /// A super-majority of the council can cancel the slash. - type SlashCancelOrigin = pallet_collective::EnsureProportionAtLeast<_3, _4, AccountId, CouncilCollective>; + type SlashCancelOrigin = EnsureOneOf< + AccountId, + EnsureRoot, + pallet_collective::EnsureProportionAtLeast<_3, _4, AccountId, CouncilCollective> + >; type SessionInterface = Self; type RewardCurve = RewardCurve; type NextNewSession = Session; @@ -529,13 +518,18 @@ impl pallet_collective::Trait for Runtime { type MaxProposals = TechnicalMaxProposals; } +type EnsureRootOrHalfCouncil = EnsureOneOf< + AccountId, + EnsureRoot, + pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective> +>; impl pallet_membership::Trait for Runtime { type Event = Event; - type AddOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; - type RemoveOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; - type SwapOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; - type ResetOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; - type PrimeOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; + type AddOrigin = EnsureRootOrHalfCouncil; + type RemoveOrigin = EnsureRootOrHalfCouncil; + type SwapOrigin = EnsureRootOrHalfCouncil; + type ResetOrigin = EnsureRootOrHalfCouncil; + type PrimeOrigin = EnsureRootOrHalfCouncil; type MembershipInitialized = TechnicalCommittee; type MembershipChanged = TechnicalCommittee; } @@ -555,8 +549,16 @@ parameter_types! { impl pallet_treasury::Trait for Runtime { type ModuleId = TreasuryModuleId; type Currency = Balances; - type ApproveOrigin = pallet_collective::EnsureMembers<_4, AccountId, CouncilCollective>; - type RejectOrigin = pallet_collective::EnsureMembers<_2, AccountId, CouncilCollective>; + type ApproveOrigin = EnsureOneOf< + AccountId, + EnsureRoot, + pallet_collective::EnsureMembers<_4, AccountId, CouncilCollective> + >; + type RejectOrigin = EnsureOneOf< + AccountId, + EnsureRoot, + pallet_collective::EnsureMembers<_2, AccountId, CouncilCollective> + >; type Tippers = Elections; type TipCountdown = TipCountdown; type TipFindersFee = TipFindersFee; @@ -735,8 +737,8 @@ impl pallet_identity::Trait for Runtime { type MaxAdditionalFields = MaxAdditionalFields; type MaxRegistrars = MaxRegistrars; type Slashed = Treasury; - type ForceOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; - type RegistrarOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; + type ForceOrigin = EnsureRootOrHalfCouncil; + type RegistrarOrigin = EnsureRootOrHalfCouncil; } parameter_types! { diff --git a/bin/node/testing/Cargo.toml b/bin/node/testing/Cargo.toml index f423c58fe6d94..6bf4abc03d5aa 100644 --- a/bin/node/testing/Cargo.toml +++ b/bin/node/testing/Cargo.toml @@ -17,7 +17,7 @@ pallet-balances = { version = "2.0.0-rc3", path = "../../../frame/balances" } sc-service = { version = "0.8.0-rc3", features = ["test-helpers", "db"], path = "../../../client/service" } sc-client-db = { version = "0.8.0-rc3", path = "../../../client/db/", features = ["kvdb-rocksdb", "parity-db"] } sc-client-api = { version = "2.0.0-rc3", path = "../../../client/api/" } -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } pallet-contracts = { version = "2.0.0-rc3", path = "../../../frame/contracts" } pallet-grandpa = { version = "2.0.0-rc3", path = "../../../frame/grandpa" } pallet-indices = { version = "2.0.0-rc3", path = "../../../frame/indices" } diff --git a/bin/utils/subkey/Cargo.toml b/bin/utils/subkey/Cargo.toml index 064470ea7cb4e..fa570f5759fa5 100644 --- a/bin/utils/subkey/Cargo.toml +++ b/bin/utils/subkey/Cargo.toml @@ -22,7 +22,7 @@ tiny-bip39 = "0.7" substrate-bip39 = "0.4.1" hex = "0.4.0" hex-literal = "0.2.1" -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } frame-system = { version = "2.0.0-rc3", path = "../../../frame/system" } pallet-balances = { version = "2.0.0-rc3", path = "../../../frame/balances" } pallet-transaction-payment = { version = "2.0.0-rc3", path = "../../../frame/transaction-payment" } diff --git a/client/api/Cargo.toml b/client/api/Cargo.toml index 7730168136bd9..606c1c4813115 100644 --- a/client/api/Cargo.toml +++ b/client/api/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/sc-client-api" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-consensus = { version = "0.8.0-rc3", path = "../../primitives/consensus/common" } derive_more = { version = "0.99.2" } sc-executor = { version = "0.8.0-rc3", path = "../executor" } diff --git a/client/authority-discovery/Cargo.toml b/client/authority-discovery/Cargo.toml index 8833306f06010..114092ab31a08 100644 --- a/client/authority-discovery/Cargo.toml +++ b/client/authority-discovery/Cargo.toml @@ -17,7 +17,7 @@ prost-build = "0.6.1" [dependencies] bytes = "0.5.0" -codec = { package = "parity-scale-codec", default-features = false, version = "1.3.0" } +codec = { package = "parity-scale-codec", default-features = false, version = "1.3.1" } derive_more = "0.99.2" futures = "0.3.4" futures-timer = "3.0.1" diff --git a/client/authority-discovery/src/lib.rs b/client/authority-discovery/src/lib.rs index bc76c14331403..ba1c9f0fa8dce 100644 --- a/client/authority-discovery/src/lib.rs +++ b/client/authority-discovery/src/lib.rs @@ -58,19 +58,27 @@ use futures::task::{Context, Poll}; use futures::{Future, FutureExt, ready, Stream, StreamExt}; use futures_timer::Delay; +use addr_cache::AddrCache; use codec::Decode; use error::{Error, Result}; +use libp2p::core::multiaddr; use log::{debug, error, log_enabled}; use prometheus_endpoint::{Counter, CounterVec, Gauge, Opts, U64, register}; use prost::Message; use sc_client_api::blockchain::HeaderBackend; -use sc_network::{Multiaddr, config::MultiaddrWithPeerId, DhtEvent, ExHashT, NetworkStateInfo}; +use sc_network::{ + config::MultiaddrWithPeerId, + DhtEvent, + ExHashT, + Multiaddr, + NetworkStateInfo, + PeerId, +}; use sp_authority_discovery::{AuthorityDiscoveryApi, AuthorityId, AuthoritySignature, AuthorityPair}; use sp_core::crypto::{key_types, Pair}; use sp_core::traits::BareCryptoStorePtr; use sp_runtime::{traits::Block as BlockT, generic::BlockId}; use sp_api::ProvideRuntimeApi; -use addr_cache::AddrCache; #[cfg(test)] mod tests; @@ -233,7 +241,7 @@ where .collect(), None => self.network.external_addresses() .into_iter() - .map(|a| a.with(libp2p::core::multiaddr::Protocol::P2p( + .map(|a| a.with(multiaddr::Protocol::P2p( self.network.local_peer_id().into(), ))) .map(|a| a.to_vec()) @@ -294,13 +302,26 @@ where .authorities(&id) .map_err(Error::CallingRuntime)?; + let local_keys = match &self.role { + Role::Authority(key_store) => { + key_store.read() + .sr25519_public_keys(key_types::AUTHORITY_DISCOVERY) + .into_iter() + .collect::>() + }, + Role::Sentry => HashSet::new(), + }; + for authority_id in authorities.iter() { - if let Some(metrics) = &self.metrics { - metrics.request.inc(); - } + // Make sure we don't look up our own keys. + if !local_keys.contains(authority_id.as_ref()) { + if let Some(metrics) = &self.metrics { + metrics.request.inc(); + } - self.network - .get_value(&hash_authority_id(authority_id.as_ref())); + self.network + .get_value(&hash_authority_id(authority_id.as_ref())); + } } Ok(()) @@ -410,6 +431,8 @@ where .get(&remote_key) .ok_or(Error::MatchingHashedAuthorityIdWithAuthorityId)?; + let local_peer_id = self.network.local_peer_id(); + let remote_addresses: Vec = values.into_iter() .map(|(_k, v)| { let schema::SignedAuthorityAddresses { signature, addresses } = @@ -434,7 +457,27 @@ where Ok(addresses) }) .collect::>>>()? - .into_iter().flatten().collect(); + .into_iter() + .flatten() + // Ignore own addresses. + .filter(|addr| !addr.iter().any(|protocol| { + // Parse to PeerId first as Multihashes of old and new PeerId + // representation don't equal. + // + // See https://github.com/libp2p/rust-libp2p/issues/555 for + // details. + if let multiaddr::Protocol::P2p(hash) = protocol { + let peer_id = match PeerId::from_multihash(hash) { + Ok(peer_id) => peer_id, + Err(_) => return true, // Discard address. + }; + + return peer_id == local_peer_id; + } + + false // Multiaddr does not contain a PeerId. + })) + .collect(); if !remote_addresses.is_empty() { self.addr_cache.insert(authority_id.clone(), remote_addresses); diff --git a/client/authority-discovery/src/tests.rs b/client/authority-discovery/src/tests.rs index 12edcf5fc9008..09a65fd138c11 100644 --- a/client/authority-discovery/src/tests.rs +++ b/client/authority-discovery/src/tests.rs @@ -24,10 +24,10 @@ use futures::future::{poll_fn, FutureExt}; use futures::sink::SinkExt; use futures::task::LocalSpawn; use futures::poll; -use libp2p::{kad, PeerId}; +use libp2p::{kad, core::multiaddr, PeerId}; use sp_api::{ProvideRuntimeApi, ApiRef}; -use sp_core::testing::KeyStore; +use sp_core::{crypto::Public, testing::KeyStore}; use sp_runtime::traits::{Zero, Block as BlockT, NumberFor}; use substrate_test_runtime_client::runtime::Block; @@ -210,7 +210,7 @@ impl NetworkStateInfo for TestNetwork { } fn external_addresses(&self) -> Vec { - vec!["/ip6/2001:db8::".parse().unwrap()] + vec!["/ip6/2001:db8::/tcp/30333".parse().unwrap()] } } @@ -281,7 +281,7 @@ fn publish_discover_cycle() { let peer_id = network.local_peer_id(); let address = network.external_addresses().pop().unwrap(); - address.with(libp2p::core::multiaddr::Protocol::P2p( + address.with(multiaddr::Protocol::P2p( peer_id.into(), )) }; @@ -461,3 +461,102 @@ fn dont_stop_polling_when_error_is_returned() { } ); } + +/// In the scenario of a validator publishing the address of its sentry node to +/// the DHT, said sentry node should not add its own Multiaddr to the +/// peerset "authority" priority group. +#[test] +fn never_add_own_address_to_priority_group() { + let validator_key_store = KeyStore::new(); + let validator_public = validator_key_store + .write() + .sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None) + .unwrap(); + + let sentry_network: Arc = Arc::new(Default::default()); + + let sentry_multiaddr = { + let peer_id = sentry_network.local_peer_id(); + let address: Multiaddr = "/ip6/2001:db8:0:0:0:0:0:2/tcp/30333".parse().unwrap(); + + address.with(multiaddr::Protocol::P2p( + peer_id.into(), + )) + }; + + // Address of some other sentry node of `validator`. + let random_multiaddr = { + let peer_id = PeerId::random(); + let address: Multiaddr = "/ip6/2001:db8:0:0:0:0:0:1/tcp/30333".parse().unwrap(); + + address.with(multiaddr::Protocol::P2p( + peer_id.into(), + )) + }; + + let dht_event = { + let addresses = vec![ + sentry_multiaddr.to_vec(), + random_multiaddr.to_vec(), + ]; + + let mut serialized_addresses = vec![]; + schema::AuthorityAddresses { addresses } + .encode(&mut serialized_addresses) + .map_err(Error::EncodingProto) + .unwrap(); + + let signature = validator_key_store.read() + .sign_with( + key_types::AUTHORITY_DISCOVERY, + &validator_public.clone().into(), + serialized_addresses.as_slice(), + ) + .map_err(|_| Error::Signing) + .unwrap(); + + let mut signed_addresses = vec![]; + schema::SignedAuthorityAddresses { + addresses: serialized_addresses.clone(), + signature, + } + .encode(&mut signed_addresses) + .map_err(Error::EncodingProto) + .unwrap(); + + let key = hash_authority_id(&validator_public.to_raw_vec()); + let value = signed_addresses; + (key, value) + }; + + let (_dht_event_tx, dht_event_rx) = channel(1); + let sentry_test_api = Arc::new(TestApi { + // Make sure the sentry node identifies its validator as an authority. + authorities: vec![validator_public.into()], + }); + + let mut sentry_authority_discovery = AuthorityDiscovery::new( + sentry_test_api, + sentry_network.clone(), + vec![], + dht_event_rx.boxed(), + Role::Sentry, + None, + ); + + sentry_authority_discovery.handle_dht_value_found_event(vec![dht_event]).unwrap(); + + assert_eq!( + sentry_network.set_priority_group_call.lock().unwrap().len(), 1, + "Expect authority discovery to set the priority set.", + ); + + assert_eq!( + sentry_network.set_priority_group_call.lock().unwrap()[0], + ( + "authorities".to_string(), + HashSet::from_iter(vec![random_multiaddr.clone()].into_iter(),) + ), + "Expect authority discovery to only add `random_multiaddr`." + ); +} diff --git a/client/basic-authorship/Cargo.toml b/client/basic-authorship/Cargo.toml index 964d1c3798fbd..6e3ec49ea70e6 100644 --- a/client/basic-authorship/Cargo.toml +++ b/client/basic-authorship/Cargo.toml @@ -12,7 +12,7 @@ description = "Basic implementation of block-authoring logic." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } futures = "0.3.4" futures-timer = "3.0.1" log = "0.4.8" diff --git a/client/basic-authorship/src/basic_authorship.rs b/client/basic-authorship/src/basic_authorship.rs index f9321f5b9d143..383d0ea6fcad4 100644 --- a/client/basic-authorship/src/basic_authorship.rs +++ b/client/basic-authorship/src/basic_authorship.rs @@ -234,7 +234,8 @@ impl Proposer Either::Left((iterator, _)) => iterator, Either::Right(_) => { log::warn!( - "Timeout fired waiting for transaction pool to be ready. Proceeding to block production anyway.", + "Timeout fired waiting for transaction pool at block #{}. Proceeding with production.", + self.parent_number, ); self.transaction_pool.ready() } diff --git a/client/block-builder/Cargo.toml b/client/block-builder/Cargo.toml index ff142887ff9ef..ce94526e0cc55 100644 --- a/client/block-builder/Cargo.toml +++ b/client/block-builder/Cargo.toml @@ -21,7 +21,7 @@ sp-blockchain = { version = "2.0.0-rc3", path = "../../primitives/blockchain" } sp-core = { version = "2.0.0-rc3", path = "../../primitives/core" } sp-block-builder = { version = "2.0.0-rc3", path = "../../primitives/block-builder" } sc-client-api = { version = "2.0.0-rc3", path = "../api" } -codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } [dev-dependencies] substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } diff --git a/client/chain-spec/src/lib.rs b/client/chain-spec/src/lib.rs index 6fb26942612d3..66bce2b1363c2 100644 --- a/client/chain-spec/src/lib.rs +++ b/client/chain-spec/src/lib.rs @@ -158,3 +158,9 @@ pub trait ChainSpec: BuildStorage + Send { /// This will be used as storage at genesis. fn set_storage(&mut self, storage: Storage); } + +impl std::fmt::Debug for dyn ChainSpec { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "ChainSpec(name = {:?}, id = {:?})", self.name(), self.id()) + } +} diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index 4bdacfcbd2b50..7ffc27749b136 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -20,7 +20,6 @@ regex = "1.3.1" time = "0.1.42" ansi_term = "0.12.1" lazy_static = "1.4.0" -directories = "2.0.2" tokio = { version = "0.2.9", features = [ "signal", "rt-core", "rt-threaded" ] } futures = "0.3.4" fdlimit = "0.1.4" diff --git a/client/cli/src/arg_enums.rs b/client/cli/src/arg_enums.rs index 4dfd384d9513c..db13fff76148e 100644 --- a/client/cli/src/arg_enums.rs +++ b/client/cli/src/arg_enums.rs @@ -178,6 +178,8 @@ arg_enum! { pub const DEFAULT_EXECUTION_SYNCING: ExecutionStrategy = ExecutionStrategy::NativeElseWasm; /// Default value for the `--execution-import-block` parameter. pub const DEFAULT_EXECUTION_IMPORT_BLOCK: ExecutionStrategy = ExecutionStrategy::NativeElseWasm; +/// Default value for the `--execution-import-block` parameter when the node is a validator. +pub const DEFAULT_EXECUTION_IMPORT_BLOCK_VALIDATOR: ExecutionStrategy = ExecutionStrategy::Wasm; /// Default value for the `--execution-block-construction` parameter. pub const DEFAULT_EXECUTION_BLOCK_CONSTRUCTION: ExecutionStrategy = ExecutionStrategy::Wasm; /// Default value for the `--execution-offchain-worker` parameter. diff --git a/client/cli/src/commands/build_spec_cmd.rs b/client/cli/src/commands/build_spec_cmd.rs index d2e2ef3a5467c..23626359ff131 100644 --- a/client/cli/src/commands/build_spec_cmd.rs +++ b/client/cli/src/commands/build_spec_cmd.rs @@ -27,7 +27,7 @@ use structopt::StructOpt; use std::io::Write; /// The `build-spec` command used to build a specification. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct BuildSpecCmd { /// Force raw genesis storage output. #[structopt(long = "raw")] diff --git a/client/cli/src/commands/check_block_cmd.rs b/client/cli/src/commands/check_block_cmd.rs index d1241f010d597..c000ea7fb11ee 100644 --- a/client/cli/src/commands/check_block_cmd.rs +++ b/client/cli/src/commands/check_block_cmd.rs @@ -25,7 +25,7 @@ use std::{fmt::Debug, str::FromStr}; use structopt::StructOpt; /// The `check-block` command used to validate blocks. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct CheckBlockCmd { /// Block hash or number #[structopt(value_name = "HASH or NUMBER")] diff --git a/client/cli/src/commands/export_blocks_cmd.rs b/client/cli/src/commands/export_blocks_cmd.rs index 2fdc408250bce..7c523c0555d55 100644 --- a/client/cli/src/commands/export_blocks_cmd.rs +++ b/client/cli/src/commands/export_blocks_cmd.rs @@ -31,7 +31,7 @@ use std::path::PathBuf; use structopt::StructOpt; /// The `export-blocks` command used to export blocks. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct ExportBlocksCmd { /// Output file name or stdout if unspecified. #[structopt(parse(from_os_str))] diff --git a/client/cli/src/commands/export_state_cmd.rs b/client/cli/src/commands/export_state_cmd.rs index 33111e7737b1c..23a43a178abe5 100644 --- a/client/cli/src/commands/export_state_cmd.rs +++ b/client/cli/src/commands/export_state_cmd.rs @@ -27,7 +27,7 @@ use structopt::StructOpt; /// The `export-state` command used to export the state of a given block into /// a chain spec. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct ExportStateCmd { /// Block hash or number. #[structopt(value_name = "HASH or NUMBER")] @@ -59,7 +59,7 @@ impl ExportStateCmd { { info!("Exporting raw state..."); let mut input_spec = config.chain_spec.cloned_box(); - let block_id = self.input.clone().map(|b| b.parse()).transpose()?; + let block_id = self.input.as_ref().map(|b| b.parse()).transpose()?; let raw_state = builder(config)?.export_raw_state(block_id)?; input_spec.set_storage(raw_state); diff --git a/client/cli/src/commands/import_blocks_cmd.rs b/client/cli/src/commands/import_blocks_cmd.rs index a74f4d524c95b..8e178c4b97964 100644 --- a/client/cli/src/commands/import_blocks_cmd.rs +++ b/client/cli/src/commands/import_blocks_cmd.rs @@ -29,7 +29,7 @@ use std::path::PathBuf; use structopt::StructOpt; /// The `import-blocks` command used to import blocks. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct ImportBlocksCmd { /// Input file or stdin if unspecified. #[structopt(parse(from_os_str))] diff --git a/client/cli/src/commands/mod.rs b/client/cli/src/commands/mod.rs index 62757890ef01d..04cce66bef80d 100644 --- a/client/cli/src/commands/mod.rs +++ b/client/cli/src/commands/mod.rs @@ -27,11 +27,11 @@ mod run_cmd; pub use self::build_spec_cmd::BuildSpecCmd; pub use self::check_block_cmd::CheckBlockCmd; pub use self::export_blocks_cmd::ExportBlocksCmd; +pub use self::export_state_cmd::ExportStateCmd; pub use self::import_blocks_cmd::ImportBlocksCmd; pub use self::purge_chain_cmd::PurgeChainCmd; pub use self::revert_cmd::RevertCmd; pub use self::run_cmd::RunCmd; -pub use self::export_state_cmd::ExportStateCmd; use std::fmt::Debug; use structopt::StructOpt; @@ -40,7 +40,7 @@ use structopt::StructOpt; /// The core commands are split into multiple subcommands and `Run` is the default subcommand. From /// the CLI user perspective, it is not visible that `Run` is a subcommand. So, all parameters of /// `Run` are exported as main executable parameters. -#[derive(Debug, Clone, StructOpt)] +#[derive(Debug, StructOpt)] pub enum Subcommand { /// Build a spec.json file, outputs to stdout. BuildSpec(BuildSpecCmd), @@ -162,7 +162,7 @@ macro_rules! substrate_cli_subcommands { } } - fn base_path(&self) -> $crate::Result<::std::option::Option<::std::path::PathBuf>> { + fn base_path(&self) -> $crate::Result<::std::option::Option> { match self { $($enum::$variant(cmd) => cmd.base_path()),* } @@ -278,10 +278,16 @@ macro_rules! substrate_cli_subcommands { } } - fn execution_strategies(&self, is_dev: bool) + fn execution_strategies(&self, is_dev: bool, is_validator: bool) -> $crate::Result<::sc_client_api::execution_extensions::ExecutionStrategies> { match self { - $($enum::$variant(cmd) => cmd.execution_strategies(is_dev)),* + $($enum::$variant(cmd) => cmd.execution_strategies(is_dev, is_validator)),* + } + } + + fn rpc_ipc(&self) -> $crate::Result<::std::option::Option<::std::string::String>> { + match self { + $($enum::$variant(cmd) => cmd.rpc_ipc()),* } } @@ -409,4 +415,3 @@ macro_rules! substrate_cli_subcommands { substrate_cli_subcommands!( Subcommand => BuildSpec, ExportBlocks, ImportBlocks, CheckBlock, Revert, PurgeChain, ExportState ); - diff --git a/client/cli/src/commands/purge_chain_cmd.rs b/client/cli/src/commands/purge_chain_cmd.rs index 9d364a45f7d09..053f427309828 100644 --- a/client/cli/src/commands/purge_chain_cmd.rs +++ b/client/cli/src/commands/purge_chain_cmd.rs @@ -26,7 +26,7 @@ use std::io::{self, Write}; use structopt::StructOpt; /// The `purge-chain` command used to remove the whole chain. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct PurgeChainCmd { /// Skip interactive prompt by answering yes automatically. #[structopt(short = "y")] diff --git a/client/cli/src/commands/revert_cmd.rs b/client/cli/src/commands/revert_cmd.rs index 6117eaf4880bf..1b5489df708a7 100644 --- a/client/cli/src/commands/revert_cmd.rs +++ b/client/cli/src/commands/revert_cmd.rs @@ -25,7 +25,7 @@ use std::fmt::Debug; use structopt::StructOpt; /// The `revert` command used revert the chain to a previous state. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct RevertCmd { /// Number of blocks to revert. #[structopt(default_value = "256")] diff --git a/client/cli/src/commands/run_cmd.rs b/client/cli/src/commands/run_cmd.rs index 23a410d679b8f..16bae1ea963b0 100644 --- a/client/cli/src/commands/run_cmd.rs +++ b/client/cli/src/commands/run_cmd.rs @@ -21,13 +21,13 @@ use crate::error::{Error, Result}; use crate::params::ImportParams; use crate::params::KeystoreParams; use crate::params::NetworkParams; +use crate::params::OffchainWorkerParams; use crate::params::SharedParams; use crate::params::TransactionPoolParams; -use crate::params::OffchainWorkerParams; use crate::CliConfiguration; use regex::Regex; use sc_service::{ - config::{MultiaddrWithPeerId, PrometheusConfig, TransactionPoolOptions}, + config::{BasePath, MultiaddrWithPeerId, PrometheusConfig, TransactionPoolOptions}, ChainSpec, Role, }; use sc_telemetry::TelemetryEndpoints; @@ -35,7 +35,7 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use structopt::StructOpt; /// The `run` command used to run a node. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct RunCmd { /// Enable validator mode. /// @@ -122,6 +122,10 @@ pub struct RunCmd { #[structopt(long = "prometheus-external")] pub prometheus_external: bool, + /// Specify IPC RPC server path + #[structopt(long = "ipc-path", value_name = "PATH")] + pub ipc_path: Option, + /// Specify HTTP RPC server TCP port. #[structopt(long = "rpc-port", value_name = "PORT")] pub rpc_port: Option, @@ -250,6 +254,16 @@ pub struct RunCmd { conflicts_with_all = &[ "sentry", "public-addr" ] )] pub sentry_nodes: Vec, + + /// Run a temporary node. + /// + /// A temporary directory will be created to store the configuration and will be deleted + /// at the end of the process. + /// + /// Note: the directory is random per process execution. This directory is used as base path + /// which includes: database, node key and keystore. + #[structopt(long, conflicts_with = "base-path")] + pub tmp: bool, } impl RunCmd { @@ -424,6 +438,10 @@ impl CliConfiguration for RunCmd { Ok(Some(SocketAddr::new(interface, self.rpc_port.unwrap_or(9933)))) } + fn rpc_ipc(&self) -> Result> { + Ok(self.ipc_path.clone()) + } + fn rpc_ws(&self) -> Result> { let interface = rpc_interface( self.ws_external, @@ -446,6 +464,14 @@ impl CliConfiguration for RunCmd { fn max_runtime_instances(&self) -> Result> { Ok(self.max_runtime_instances.map(|x| x.min(256))) } + + fn base_path(&self) -> Result> { + Ok(if self.tmp { + Some(BasePath::new_temp_dir()?) + } else { + self.shared_params().base_path() + }) + } } /// Check whether a node name is considered as valid. diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index a1ee1b0cc1da9..598acd0ab913e 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -27,16 +27,13 @@ use crate::{ use names::{Generator, Name}; use sc_client_api::execution_extensions::ExecutionStrategies; use sc_service::config::{ - Configuration, DatabaseConfig, ExtTransport, KeystoreConfig, NetworkConfiguration, + BasePath, Configuration, DatabaseConfig, ExtTransport, KeystoreConfig, NetworkConfiguration, NodeKeyConfig, OffchainWorkerConfig, PrometheusConfig, PruningMode, Role, RpcMethods, - TaskType, TelemetryEndpoints, TransactionPoolOptions, WasmExecutionMethod, + TaskExecutor, TelemetryEndpoints, TransactionPoolOptions, WasmExecutionMethod, }; use sc_service::{ChainSpec, TracingReceiver}; -use std::future::Future; use std::net::SocketAddr; use std::path::PathBuf; -use std::pin::Pin; -use std::sync::Arc; /// The maximum number of characters for a node name. pub(crate) const NODE_NAME_MAX_LENGTH: usize = 32; @@ -87,7 +84,7 @@ pub trait CliConfiguration: Sized { /// Get the base path of the configuration (if any) /// /// By default this is retrieved from `SharedParams`. - fn base_path(&self) -> Result> { + fn base_path(&self) -> Result> { Ok(self.shared_params().base_path()) } @@ -246,9 +243,14 @@ pub trait CliConfiguration: Sized { /// /// By default this is retrieved from `ImportParams` if it is available. Otherwise its /// `ExecutionStrategies::default()`. - fn execution_strategies(&self, is_dev: bool) -> Result { - Ok(self.import_params() - .map(|x| x.execution_strategies(is_dev)) + fn execution_strategies( + &self, + is_dev: bool, + is_validator: bool, + ) -> Result { + Ok(self + .import_params() + .map(|x| x.execution_strategies(is_dev, is_validator)) .unwrap_or(Default::default())) } @@ -259,6 +261,13 @@ pub trait CliConfiguration: Sized { Ok(Default::default()) } + /// Get the RPC IPC path (`None` if disabled). + /// + /// By default this is `None`. + fn rpc_ipc(&self) -> Result> { + Ok(Default::default()) + } + /// Get the RPC websocket address (`None` if disabled). /// /// By default this is `None`. @@ -397,19 +406,17 @@ pub trait CliConfiguration: Sized { fn create_configuration( &self, cli: &C, - task_executor: Arc + Send>>, TaskType) + Send + Sync>, + task_executor: TaskExecutor, ) -> Result { let is_dev = self.is_dev()?; let chain_id = self.chain_id(is_dev)?; let chain_spec = cli.load_spec(chain_id.as_str())?; - let config_dir = self + let base_path = self .base_path()? - .unwrap_or_else(|| { - directories::ProjectDirs::from("", "", C::executable_name()) - .expect("app directories exist on all supported platforms; qed") - .data_local_dir() - .into() - }) + .unwrap_or_else(|| BasePath::from_project("", "", C::executable_name())); + let config_dir = base_path + .path() + .to_path_buf() .join("chains") .join(chain_spec.id()); let net_config_dir = config_dir.join(DEFAULT_NETWORK_CONFIG_PATH); @@ -419,6 +426,7 @@ pub trait CliConfiguration: Sized { let node_key = self.node_key(&net_config_dir)?; let role = self.role(is_dev)?; let max_runtime_instances = self.max_runtime_instances()?.unwrap_or(8); + let is_validator = role.is_network_authority(); let unsafe_pruning = self .import_params() @@ -444,9 +452,10 @@ pub trait CliConfiguration: Sized { state_cache_child_ratio: self.state_cache_child_ratio()?, pruning: self.pruning(unsafe_pruning, &role)?, wasm_method: self.wasm_method()?, - execution_strategies: self.execution_strategies(is_dev)?, + execution_strategies: self.execution_strategies(is_dev, is_validator)?, rpc_http: self.rpc_http()?, rpc_ws: self.rpc_ws()?, + rpc_ipc: self.rpc_ipc()?, rpc_methods: self.rpc_methods()?, rpc_ws_max_connections: self.rpc_ws_max_connections()?, rpc_cors: self.rpc_cors(is_dev)?, @@ -464,6 +473,8 @@ pub trait CliConfiguration: Sized { max_runtime_instances, announce_block: self.announce_block()?, role, + base_path: Some(base_path), + informant_output_format: Default::default(), }) } @@ -507,5 +518,5 @@ pub fn generate_node_name() -> String { if count < NODE_NAME_MAX_LENGTH { return node_name; } - }; + } } diff --git a/client/cli/src/lib.rs b/client/cli/src/lib.rs index 1acd5ee60474a..9623b08bfbb7f 100644 --- a/client/cli/src/lib.rs +++ b/client/cli/src/lib.rs @@ -37,11 +37,8 @@ use log::info; pub use params::*; use regex::Regex; pub use runner::*; -use sc_service::{ChainSpec, Configuration, TaskType}; -use std::future::Future; +use sc_service::{ChainSpec, Configuration, TaskExecutor}; use std::io::Write; -use std::pin::Pin; -use std::sync::Arc; pub use structopt; use structopt::{ clap::{self, AppSettings}, @@ -199,7 +196,7 @@ pub trait SubstrateCli: Sized { fn create_configuration( &self, command: &T, - task_executor: Arc + Send>>, TaskType) + Send + Sync>, + task_executor: TaskExecutor, ) -> error::Result { command.create_configuration(self, task_executor) } diff --git a/client/cli/src/params/database_params.rs b/client/cli/src/params/database_params.rs index 3ff8eb01d0643..24b23f6076a02 100644 --- a/client/cli/src/params/database_params.rs +++ b/client/cli/src/params/database_params.rs @@ -20,7 +20,7 @@ use crate::arg_enums::Database; use structopt::StructOpt; /// Parameters for block import. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct DatabaseParams { /// Select database backend to use. #[structopt( diff --git a/client/cli/src/params/import_params.rs b/client/cli/src/params/import_params.rs index fb683df6d3be9..c2fb34f90e6fc 100644 --- a/client/cli/src/params/import_params.rs +++ b/client/cli/src/params/import_params.rs @@ -17,8 +17,8 @@ // along with this program. If not, see . use crate::arg_enums::{ - ExecutionStrategy, TracingReceiver, WasmExecutionMethod, - DEFAULT_EXECUTION_BLOCK_CONSTRUCTION, DEFAULT_EXECUTION_IMPORT_BLOCK, + ExecutionStrategy, TracingReceiver, WasmExecutionMethod, DEFAULT_EXECUTION_BLOCK_CONSTRUCTION, + DEFAULT_EXECUTION_IMPORT_BLOCK, DEFAULT_EXECUTION_IMPORT_BLOCK_VALIDATOR, DEFAULT_EXECUTION_OFFCHAIN_WORKER, DEFAULT_EXECUTION_OTHER, DEFAULT_EXECUTION_SYNCING, }; use crate::params::DatabaseParams; @@ -27,7 +27,7 @@ use sc_client_api::execution_extensions::ExecutionStrategies; use structopt::StructOpt; /// Parameters for block import. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct ImportParams { #[allow(missing_docs)] #[structopt(flatten)] @@ -104,22 +104,27 @@ impl ImportParams { } /// Get execution strategies for the parameters - pub fn execution_strategies( - &self, - is_dev: bool, - ) -> ExecutionStrategies { + pub fn execution_strategies(&self, is_dev: bool, is_validator: bool) -> ExecutionStrategies { let exec = &self.execution_strategies; - let exec_all_or = |strat: ExecutionStrategy, default: ExecutionStrategy| { - exec.execution.unwrap_or(if strat == default && is_dev { + let exec_all_or = |strat: Option, default: ExecutionStrategy| { + let default = if is_dev { ExecutionStrategy::Native } else { - strat - }).into() + default + }; + + exec.execution.unwrap_or(strat.unwrap_or(default)).into() + }; + + let default_execution_import_block = if is_validator { + DEFAULT_EXECUTION_IMPORT_BLOCK_VALIDATOR + } else { + DEFAULT_EXECUTION_IMPORT_BLOCK }; ExecutionStrategies { syncing: exec_all_or(exec.execution_syncing, DEFAULT_EXECUTION_SYNCING), - importing: exec_all_or(exec.execution_import_block, DEFAULT_EXECUTION_IMPORT_BLOCK), + importing: exec_all_or(exec.execution_import_block, default_execution_import_block), block_construction: exec_all_or(exec.execution_block_construction, DEFAULT_EXECUTION_BLOCK_CONSTRUCTION), offchain_worker: @@ -130,27 +135,27 @@ impl ImportParams { } /// Execution strategies parameters. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct ExecutionStrategiesParams { - /// The means of execution used when calling into the runtime while syncing blocks. + /// The means of execution used when calling into the runtime for importing blocks as + /// part of an initial sync. #[structopt( long = "execution-syncing", value_name = "STRATEGY", possible_values = &ExecutionStrategy::variants(), case_insensitive = true, - default_value = DEFAULT_EXECUTION_SYNCING.as_str(), )] - pub execution_syncing: ExecutionStrategy, + pub execution_syncing: Option, - /// The means of execution used when calling into the runtime while importing blocks. + /// The means of execution used when calling into the runtime for general block import + /// (including locally authored blocks). #[structopt( long = "execution-import-block", value_name = "STRATEGY", possible_values = &ExecutionStrategy::variants(), case_insensitive = true, - default_value = DEFAULT_EXECUTION_IMPORT_BLOCK.as_str(), )] - pub execution_import_block: ExecutionStrategy, + pub execution_import_block: Option, /// The means of execution used when calling into the runtime while constructing blocks. #[structopt( @@ -158,9 +163,8 @@ pub struct ExecutionStrategiesParams { value_name = "STRATEGY", possible_values = &ExecutionStrategy::variants(), case_insensitive = true, - default_value = DEFAULT_EXECUTION_BLOCK_CONSTRUCTION.as_str(), )] - pub execution_block_construction: ExecutionStrategy, + pub execution_block_construction: Option, /// The means of execution used when calling into the runtime while using an off-chain worker. #[structopt( @@ -168,9 +172,8 @@ pub struct ExecutionStrategiesParams { value_name = "STRATEGY", possible_values = &ExecutionStrategy::variants(), case_insensitive = true, - default_value = DEFAULT_EXECUTION_OFFCHAIN_WORKER.as_str(), )] - pub execution_offchain_worker: ExecutionStrategy, + pub execution_offchain_worker: Option, /// The means of execution used when calling into the runtime while not syncing, importing or constructing blocks. #[structopt( @@ -178,9 +181,8 @@ pub struct ExecutionStrategiesParams { value_name = "STRATEGY", possible_values = &ExecutionStrategy::variants(), case_insensitive = true, - default_value = DEFAULT_EXECUTION_OTHER.as_str(), )] - pub execution_other: ExecutionStrategy, + pub execution_other: Option, /// The execution strategy that should be used by all execution contexts. #[structopt( diff --git a/client/cli/src/params/keystore_params.rs b/client/cli/src/params/keystore_params.rs index 2fd610377d793..840cc51dff33f 100644 --- a/client/cli/src/params/keystore_params.rs +++ b/client/cli/src/params/keystore_params.rs @@ -26,7 +26,7 @@ use structopt::StructOpt; const DEFAULT_KEYSTORE_CONFIG_PATH: &'static str = "keystore"; /// Parameters of the keystore -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct KeystoreParams { /// Specify custom keystore path. #[structopt(long = "keystore-path", value_name = "PATH", parse(from_os_str))] diff --git a/client/cli/src/params/mod.rs b/client/cli/src/params/mod.rs index 3a66e5f05588a..f648337ed0af6 100644 --- a/client/cli/src/params/mod.rs +++ b/client/cli/src/params/mod.rs @@ -39,7 +39,7 @@ pub use crate::params::shared_params::*; pub use crate::params::transaction_pool_params::*; /// Wrapper type of `String` that holds an unsigned integer of arbitrary size, formatted as a decimal. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct BlockNumber(String); impl FromStr for BlockNumber { @@ -72,7 +72,7 @@ impl BlockNumber { } /// Wrapper type that is either a `Hash` or the number of a `Block`. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct BlockNumberOrHash(String); impl FromStr for BlockNumberOrHash { diff --git a/client/cli/src/params/network_params.rs b/client/cli/src/params/network_params.rs index c1639ad2b4370..253585544d260 100644 --- a/client/cli/src/params/network_params.rs +++ b/client/cli/src/params/network_params.rs @@ -26,7 +26,7 @@ use std::path::PathBuf; use structopt::StructOpt; /// Parameters used to create the network configuration. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct NetworkParams { /// Specify a list of bootnodes. #[structopt(long = "bootnodes", value_name = "ADDR")] @@ -102,11 +102,6 @@ pub struct NetworkParams { /// By default this option is true for `--dev` and false otherwise. #[structopt(long)] pub discover_local: bool, - - /// Use the legacy "pre-mainnet-launch" networking protocol. Enable if things seem broken. - /// This option will be removed in the future. - #[structopt(long)] - pub legacy_network_protocol: bool, } impl NetworkParams { @@ -165,7 +160,6 @@ impl NetworkParams { }, max_parallel_downloads: self.max_parallel_downloads, allow_non_globals_in_dht: self.discover_local || is_dev, - use_new_block_requests_protocol: !self.legacy_network_protocol, } } } diff --git a/client/cli/src/params/node_key_params.rs b/client/cli/src/params/node_key_params.rs index 7d19971ad6470..689cc6c681c83 100644 --- a/client/cli/src/params/node_key_params.rs +++ b/client/cli/src/params/node_key_params.rs @@ -31,7 +31,7 @@ const NODE_KEY_ED25519_FILE: &str = "secret_ed25519"; /// Parameters used to create the `NodeKeyConfig`, which determines the keypair /// used for libp2p networking. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct NodeKeyParams { /// The secret key to use for libp2p networking. /// diff --git a/client/cli/src/params/offchain_worker_params.rs b/client/cli/src/params/offchain_worker_params.rs index ca99ab506e484..f8d48edc4729d 100644 --- a/client/cli/src/params/offchain_worker_params.rs +++ b/client/cli/src/params/offchain_worker_params.rs @@ -32,7 +32,7 @@ use crate::OffchainWorkerEnabled; /// Offchain worker related parameters. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct OffchainWorkerParams { /// Should execute offchain workers on every block. /// diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index 36179359063e7..7db808e6d8f2f 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -21,7 +21,7 @@ use sc_service::{PruningMode, Role}; use structopt::StructOpt; /// Parameters to define the pruning mode -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct PruningParams { /// Specify the state pruning mode, a number of blocks to keep or 'archive'. /// diff --git a/client/cli/src/params/shared_params.rs b/client/cli/src/params/shared_params.rs index e9440f38a1fad..ad9ab04070563 100644 --- a/client/cli/src/params/shared_params.rs +++ b/client/cli/src/params/shared_params.rs @@ -16,11 +16,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use sc_service::config::BasePath; use std::path::PathBuf; use structopt::StructOpt; /// Shared parameters used by all `CoreParams`. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct SharedParams { /// Specify the chain specification (one of dev, local, or staging). #[structopt(long, value_name = "CHAIN_SPEC")] @@ -31,12 +32,7 @@ pub struct SharedParams { pub dev: bool, /// Specify custom base path. - #[structopt( - long, - short = "d", - value_name = "PATH", - parse(from_os_str) - )] + #[structopt(long, short = "d", value_name = "PATH", parse(from_os_str))] pub base_path: Option, /// Sets a custom logging filter. Syntax is =, e.g. -lsync=debug. @@ -49,8 +45,8 @@ pub struct SharedParams { impl SharedParams { /// Specify custom base path. - pub fn base_path(&self) -> Option { - self.base_path.clone() + pub fn base_path(&self) -> Option { + self.base_path.clone().map(Into::into) } /// Specify the development chain. diff --git a/client/cli/src/params/transaction_pool_params.rs b/client/cli/src/params/transaction_pool_params.rs index 2283c0f39f9c7..3ad278426922e 100644 --- a/client/cli/src/params/transaction_pool_params.rs +++ b/client/cli/src/params/transaction_pool_params.rs @@ -20,7 +20,7 @@ use sc_service::config::TransactionPoolOptions; use structopt::StructOpt; /// Parameters used to create the pool configuration. -#[derive(Debug, StructOpt, Clone)] +#[derive(Debug, StructOpt)] pub struct TransactionPoolParams { /// Maximum number of transactions in the transaction pool. #[structopt(long = "pool-limit", value_name = "COUNT", default_value = "8192")] diff --git a/client/cli/src/runner.rs b/client/cli/src/runner.rs index 6c220b5261aec..51ea2d21862ef 100644 --- a/client/cli/src/runner.rs +++ b/client/cli/src/runner.rs @@ -29,7 +29,7 @@ use sc_service::{AbstractService, Configuration, Role, ServiceBuilderCommand, Ta use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use sp_utils::metrics::{TOKIO_THREADS_ALIVE, TOKIO_THREADS_TOTAL}; use sp_version::RuntimeVersion; -use std::{fmt::Debug, marker::PhantomData, str::FromStr, sync::Arc}; +use std::{fmt::Debug, marker::PhantomData, str::FromStr}; #[cfg(target_family = "unix")] async fn main(func: F) -> std::result::Result<(), Box> @@ -119,23 +119,21 @@ impl Runner { let tokio_runtime = build_runtime()?; let runtime_handle = tokio_runtime.handle().clone(); - let task_executor = Arc::new( - move |fut, task_type| { - match task_type { - TaskType::Async => { runtime_handle.spawn(fut); } - TaskType::Blocking => { - runtime_handle.spawn( async move { - // `spawn_blocking` is looking for the current runtime, and as such has to be called - // from within `spawn`. - tokio::task::spawn_blocking(move || futures::executor::block_on(fut)) - }); - } + let task_executor = move |fut, task_type| { + match task_type { + TaskType::Async => { runtime_handle.spawn(fut); } + TaskType::Blocking => { + runtime_handle.spawn(async move { + // `spawn_blocking` is looking for the current runtime, and as such has to + // be called from within `spawn`. + tokio::task::spawn_blocking(move || futures::executor::block_on(fut)) + }); } } - ); + }; Ok(Runner { - config: command.create_configuration(cli, task_executor)?, + config: command.create_configuration(cli, task_executor.into())?, tokio_runtime, phantom: PhantomData, }) @@ -266,14 +264,15 @@ impl Runner { { let service = service_builder(self.config)?; - let informant_future = sc_informant::build(&service, sc_informant::OutputFormat::Coloured); - let _informant_handle = self.tokio_runtime.spawn(informant_future); - // we eagerly drop the service so that the internal exit future is fired, // but we need to keep holding a reference to the global telemetry guard // and drop the runtime first. let _telemetry = service.telemetry(); + // we hold a reference to the base path so if the base path is a temporary directory it will + // not be deleted before the tokio runtime finish to clean up + let _base_path = service.base_path(); + { let f = service.fuse(); self.tokio_runtime diff --git a/client/consensus/aura/Cargo.toml b/client/consensus/aura/Cargo.toml index 1cb1c4657b4d9..04bdc19fe47f2 100644 --- a/client/consensus/aura/Cargo.toml +++ b/client/consensus/aura/Cargo.toml @@ -17,7 +17,7 @@ sp-consensus-aura = { version = "0.8.0-rc3", path = "../../../primitives/consens sp-block-builder = { version = "2.0.0-rc3", path = "../../../primitives/block-builder" } sc-block-builder = { version = "0.8.0-rc3", path = "../../../client/block-builder" } sc-client-api = { version = "2.0.0-rc3", path = "../../api" } -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } sp-consensus = { version = "0.8.0-rc3", path = "../../../primitives/consensus/common" } derive_more = "0.99.2" futures = "0.3.4" diff --git a/client/consensus/aura/src/lib.rs b/client/consensus/aura/src/lib.rs index 818bb563484be..8b30720d0b130 100644 --- a/client/consensus/aura/src/lib.rs +++ b/client/consensus/aura/src/lib.rs @@ -833,7 +833,7 @@ pub fn import_queue( P: Pair + Send + Sync + 'static, P::Public: Clone + Eq + Send + Sync + Hash + Debug + Encode + Decode, P::Signature: Encode + Decode, - S: sp_core::traits::SpawnBlocking, + S: sp_core::traits::SpawnNamed, { register_aura_inherent_data_provider(&inherent_data_providers, slot_duration.get())?; initialize_authorities_cache(&*client)?; diff --git a/client/consensus/babe/Cargo.toml b/client/consensus/babe/Cargo.toml index 86bc5b19f13d8..4f8f4db2645a3 100644 --- a/client/consensus/babe/Cargo.toml +++ b/client/consensus/babe/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/sc-consensus-babe" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } sp-consensus-babe = { version = "0.8.0-rc3", path = "../../../primitives/consensus/babe" } sp-core = { version = "2.0.0-rc3", path = "../../../primitives/core" } sp-application-crypto = { version = "2.0.0-rc3", path = "../../../primitives/application-crypto" } @@ -58,6 +58,7 @@ sc-service = { version = "0.8.0-rc3", default-features = false, path = "../../se substrate-test-runtime-client = { version = "2.0.0-rc3", path = "../../../test-utils/runtime/client" } sc-block-builder = { version = "0.8.0-rc3", path = "../../block-builder" } env_logger = "0.7.0" +rand_chacha = "0.2.2" tempfile = "3.1.0" [features] diff --git a/client/consensus/babe/rpc/Cargo.toml b/client/consensus/babe/rpc/Cargo.toml index 79cff3eb3875d..401434cadbd9a 100644 --- a/client/consensus/babe/rpc/Cargo.toml +++ b/client/consensus/babe/rpc/Cargo.toml @@ -27,12 +27,12 @@ derive_more = "0.99.2" sp-api = { version = "2.0.0-rc3", path = "../../../../primitives/api" } sp-consensus = { version = "0.8.0-rc3", path = "../../../../primitives/consensus/common" } sp-core = { version = "2.0.0-rc3", path = "../../../../primitives/core" } +sp-application-crypto = { version = "2.0.0-rc3", path = "../../../../primitives/application-crypto" } sc-keystore = { version = "2.0.0-rc3", path = "../../../keystore" } [dev-dependencies] sc-consensus = { version = "0.8.0-rc3", path = "../../../consensus/common" } serde_json = "1.0.50" -sp-application-crypto = { version = "2.0.0-rc3", path = "../../../../primitives/application-crypto" } sp-keyring = { version = "2.0.0-rc3", path = "../../../../primitives/keyring" } substrate-test-runtime-client = { version = "2.0.0-rc3", path = "../../../../test-utils/runtime/client" } tempfile = "3.1.0" diff --git a/client/consensus/babe/rpc/src/lib.rs b/client/consensus/babe/rpc/src/lib.rs index 8e1282a8d79a7..652f4f00baac2 100644 --- a/client/consensus/babe/rpc/src/lib.rs +++ b/client/consensus/babe/rpc/src/lib.rs @@ -32,13 +32,18 @@ use sp_consensus_babe::{ digests::PreDigest, }; use serde::{Deserialize, Serialize}; +use sp_core::{ + crypto::Public, + traits::BareCryptoStore, +}; +use sp_application_crypto::AppKey; use sc_keystore::KeyStorePtr; use sc_rpc_api::DenyUnsafe; use sp_api::{ProvideRuntimeApi, BlockId}; use sp_runtime::traits::{Block as BlockT, Header as _}; use sp_consensus::{SelectChain, Error as ConsensusError}; use sp_blockchain::{HeaderBackend, HeaderMetadata, Error as BlockChainError}; -use std::{collections::HashMap, fmt, sync::Arc}; +use std::{collections::HashMap, sync::Arc}; type FutureResult = Box + Send>; @@ -93,7 +98,6 @@ impl BabeApi for BabeRpcHandler B: BlockT, C: ProvideRuntimeApi + HeaderBackend + HeaderMetadata + 'static, C::Api: BabeRuntimeApi, - ::Error: fmt::Debug, SC: SelectChain + Clone + 'static, { fn epoch_authorship(&self) -> FutureResult> { @@ -126,22 +130,23 @@ impl BabeApi for BabeRpcHandler let mut claims: HashMap = HashMap::new(); - let key_pairs = { - let keystore = keystore.read(); + let keys = { + let ks = keystore.read(); epoch.authorities.iter() .enumerate() - .flat_map(|(i, a)| { - keystore - .key_pair::(&a.0) - .ok() - .map(|kp| (kp, i)) + .filter_map(|(i, a)| { + if ks.has_keys(&[(a.0.to_raw_vec(), AuthorityId::ID)]) { + Some((a.0.clone(), i)) + } else { + None + } }) .collect::>() }; for slot_number in epoch_start..epoch_end { if let Some((claim, key)) = - authorship::claim_slot_using_key_pairs(slot_number, &epoch, &key_pairs) + authorship::claim_slot_using_keys(slot_number, &epoch, &keystore, &keys) { match claim { PreDigest::Primary { .. } => { diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index 1a6852c0c186d..682e04e380d7c 100644 --- a/client/consensus/babe/src/authorship.rs +++ b/client/consensus/babe/src/authorship.rs @@ -16,18 +16,24 @@ //! BABE authority selection and slot claiming. +use sp_application_crypto::AppKey; use sp_consensus_babe::{ - make_transcript, AuthorityId, BabeAuthorityWeight, BABE_VRF_PREFIX, - SlotNumber, AuthorityPair, + BABE_VRF_PREFIX, + AuthorityId, BabeAuthorityWeight, + SlotNumber, + make_transcript, + make_transcript_data, }; use sp_consensus_babe::digests::{ PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest, }; use sp_consensus_vrf::schnorrkel::{VRFOutput, VRFProof}; -use sp_core::{U256, blake2_256}; +use sp_core::{U256, blake2_256, crypto::Public, traits::BareCryptoStore}; use codec::Encode; -use schnorrkel::vrf::VRFInOut; -use sp_core::Pair; +use schnorrkel::{ + keys::PublicKey, + vrf::VRFInOut, +}; use sc_keystore::KeyStorePtr; use super::Epoch; @@ -124,7 +130,8 @@ pub(super) fn secondary_slot_author( fn claim_secondary_slot( slot_number: SlotNumber, epoch: &Epoch, - key_pairs: &[(AuthorityPair, usize)], + keys: &[(AuthorityId, usize)], + keystore: &KeyStorePtr, author_secondary_vrf: bool, ) -> Option<(PreDigest, AuthorityId)> { let Epoch { authorities, randomness, epoch_index, .. } = epoch; @@ -139,31 +146,41 @@ fn claim_secondary_slot( *randomness, )?; - for (pair, authority_index) in key_pairs { - if pair.public() == *expected_author { + for (authority_id, authority_index) in keys { + if authority_id == expected_author { let pre_digest = if author_secondary_vrf { - let transcript = super::authorship::make_transcript( + let transcript_data = super::authorship::make_transcript_data( randomness, slot_number, *epoch_index, ); - - let s = get_keypair(&pair).vrf_sign(transcript); - - PreDigest::SecondaryVRF(SecondaryVRFPreDigest { + let result = keystore.read().sr25519_vrf_sign( + AuthorityId::ID, + authority_id.as_ref(), + transcript_data, + ); + if let Ok(signature) = result { + Some(PreDigest::SecondaryVRF(SecondaryVRFPreDigest { + slot_number, + vrf_output: VRFOutput(signature.output), + vrf_proof: VRFProof(signature.proof), + authority_index: *authority_index as u32, + })) + } else { + None + } + } else if keystore.read().has_keys(&[(authority_id.to_raw_vec(), AuthorityId::ID)]) { + Some(PreDigest::SecondaryPlain(SecondaryPlainPreDigest { slot_number, - vrf_output: VRFOutput(s.0.to_output()), - vrf_proof: VRFProof(s.1), authority_index: *authority_index as u32, - }) + })) } else { - PreDigest::SecondaryPlain(SecondaryPlainPreDigest { - slot_number, - authority_index: *authority_index as u32, - }) + None }; - return Some((pre_digest, pair.public())); + if let Some(pre_digest) = pre_digest { + return Some((pre_digest, authority_id.clone())); + } } } @@ -179,26 +196,22 @@ pub fn claim_slot( epoch: &Epoch, keystore: &KeyStorePtr, ) -> Option<(PreDigest, AuthorityId)> { - let key_pairs = { - let keystore = keystore.read(); - epoch.authorities.iter() - .enumerate() - .flat_map(|(i, a)| { - keystore.key_pair::(&a.0).ok().map(|kp| (kp, i)) - }) - .collect::>() - }; - claim_slot_using_key_pairs(slot_number, epoch, &key_pairs) + let authorities = epoch.authorities.iter() + .enumerate() + .map(|(index, a)| (a.0.clone(), index)) + .collect::>(); + claim_slot_using_keys(slot_number, epoch, keystore, &authorities) } /// Like `claim_slot`, but allows passing an explicit set of key pairs. Useful if we intend /// to make repeated calls for different slots using the same key pairs. -pub fn claim_slot_using_key_pairs( +pub fn claim_slot_using_keys( slot_number: SlotNumber, epoch: &Epoch, - key_pairs: &[(AuthorityPair, usize)], + keystore: &KeyStorePtr, + keys: &[(AuthorityId, usize)], ) -> Option<(PreDigest, AuthorityId)> { - claim_primary_slot(slot_number, epoch, epoch.config.c, &key_pairs) + claim_primary_slot(slot_number, epoch, epoch.config.c, keystore, &keys) .or_else(|| { if epoch.config.allowed_slots.is_secondary_plain_slots_allowed() || epoch.config.allowed_slots.is_secondary_vrf_slots_allowed() @@ -206,7 +219,8 @@ pub fn claim_slot_using_key_pairs( claim_secondary_slot( slot_number, &epoch, - &key_pairs, + keys, + keystore, epoch.config.allowed_slots.is_secondary_vrf_slots_allowed(), ) } else { @@ -215,11 +229,6 @@ pub fn claim_slot_using_key_pairs( }) } -fn get_keypair(q: &AuthorityPair) -> &schnorrkel::Keypair { - use sp_core::crypto::IsWrappedBy; - sp_core::sr25519::Pair::from_ref(q).as_ref() -} - /// Claim a primary slot if it is our turn. Returns `None` if it is not our turn. /// This hashes the slot number, epoch, genesis hash, and chain randomness into /// the VRF. If the VRF produces a value less than `threshold`, it is our turn, @@ -228,35 +237,89 @@ fn claim_primary_slot( slot_number: SlotNumber, epoch: &Epoch, c: (u64, u64), - key_pairs: &[(AuthorityPair, usize)], + keystore: &KeyStorePtr, + keys: &[(AuthorityId, usize)], ) -> Option<(PreDigest, AuthorityId)> { let Epoch { authorities, randomness, epoch_index, .. } = epoch; - for (pair, authority_index) in key_pairs { - let transcript = super::authorship::make_transcript(randomness, slot_number, *epoch_index); - + for (authority_id, authority_index) in keys { + let transcript = super::authorship::make_transcript( + randomness, + slot_number, + *epoch_index + ); + let transcript_data = super::authorship::make_transcript_data( + randomness, + slot_number, + *epoch_index + ); // Compute the threshold we will use. // // We already checked that authorities contains `key.public()`, so it can't // be empty. Therefore, this division in `calculate_threshold` is safe. let threshold = super::authorship::calculate_primary_threshold(c, authorities, *authority_index); - let pre_digest = get_keypair(pair) - .vrf_sign_after_check(transcript, |inout| super::authorship::check_primary_threshold(inout, threshold)) - .map(|s| { - PreDigest::Primary(PrimaryPreDigest { + let result = keystore.read().sr25519_vrf_sign( + AuthorityId::ID, + authority_id.as_ref(), + transcript_data, + ); + if let Ok(signature) = result { + let public = PublicKey::from_bytes(&authority_id.to_raw_vec()).ok()?; + let inout = match signature.output.attach_input_hash(&public, transcript) { + Ok(inout) => inout, + Err(_) => continue, + }; + if super::authorship::check_primary_threshold(&inout, threshold) { + let pre_digest = PreDigest::Primary(PrimaryPreDigest { slot_number, - vrf_output: VRFOutput(s.0.to_output()), - vrf_proof: VRFProof(s.1), + vrf_output: VRFOutput(signature.output), + vrf_proof: VRFProof(signature.proof), authority_index: *authority_index as u32, - }) - }); + }); - // early exit on first successful claim - if let Some(pre_digest) = pre_digest { - return Some((pre_digest, pair.public())); + return Some((pre_digest, authority_id.clone())); + } } } None } + +#[cfg(test)] +mod tests { + use super::*; + use sp_core::{sr25519::Pair, crypto::Pair as _}; + use sp_consensus_babe::{AuthorityId, BabeEpochConfiguration, AllowedSlots}; + + #[test] + fn claim_secondary_plain_slot_works() { + let keystore = sc_keystore::Store::new_in_memory(); + let valid_public_key = dbg!(keystore.write().sr25519_generate_new( + AuthorityId::ID, + Some(sp_core::crypto::DEV_PHRASE), + ).unwrap()); + + let authorities = vec![ + (AuthorityId::from(Pair::generate().0.public()), 5), + (AuthorityId::from(Pair::generate().0.public()), 7), + ]; + + let mut epoch = Epoch { + epoch_index: 10, + start_slot: 0, + duration: 20, + authorities: authorities.clone(), + randomness: Default::default(), + config: BabeEpochConfiguration { + c: (3, 10), + allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots, + }, + }; + + assert!(claim_slot(10, &epoch, &keystore).is_none()); + + epoch.authorities.push((valid_public_key.clone().into(), 10)); + assert_eq!(claim_slot(10, &epoch, &keystore).unwrap().1, valid_public_key.into()); + } +} diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 3d14f0a7bf08d..961b0382c5858 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -1291,7 +1291,7 @@ pub fn import_queue( finality_proof_import: Option>, client: Arc, inherent_data_providers: InherentDataProviders, - spawner: &impl sp_core::traits::SpawnBlocking, + spawner: &impl sp_core::traits::SpawnNamed, registry: Option<&Registry>, ) -> ClientResult>> where Inner: BlockImport> diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index ada1332295d46..1caed18c1781e 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -21,8 +21,14 @@ #![allow(deprecated)] use super::*; use authorship::claim_slot; -use sp_core::crypto::Pair; -use sp_consensus_babe::{AuthorityPair, SlotNumber, AllowedSlots}; +use sp_core::{crypto::Pair, vrf::make_transcript as transcript_from_data}; +use sp_consensus_babe::{ + AuthorityPair, + SlotNumber, + AllowedSlots, + make_transcript, + make_transcript_data, +}; use sc_block_builder::{BlockBuilder, BlockBuilderProvider}; use sp_consensus::{ NoNetwork as DummyOracle, Proposal, RecordProof, @@ -35,6 +41,11 @@ use sp_runtime::{generic::DigestItem, traits::{Block as BlockT, DigestFor}}; use sc_client_api::{BlockchainEvents, backend::TransactionFor}; use log::debug; use std::{time::Duration, cell::RefCell, task::Poll}; +use rand::RngCore; +use rand_chacha::{ + rand_core::SeedableRng, + ChaChaRng, +}; type Item = DigestItem; @@ -796,3 +807,36 @@ fn verify_slots_are_strictly_increasing() { &mut block_import, ); } + +#[test] +fn babe_transcript_generation_match() { + let _ = env_logger::try_init(); + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore = sc_keystore::Store::open(keystore_path.path(), None).expect("Creates keystore"); + let pair = keystore.write().insert_ephemeral_from_seed::("//Alice") + .expect("Generates authority pair"); + + let epoch = Epoch { + start_slot: 0, + authorities: vec![(pair.public(), 1)], + randomness: [0; 32], + epoch_index: 1, + duration: 100, + config: BabeEpochConfiguration { + c: (3, 10), + allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots, + }, + }; + + let orig_transcript = make_transcript(&epoch.randomness.clone(), 1, epoch.epoch_index); + let new_transcript = make_transcript_data(&epoch.randomness, 1, epoch.epoch_index); + + let test = |t: merlin::Transcript| -> [u8; 16] { + let mut b = [0u8; 16]; + t.build_rng() + .finalize(&mut ChaChaRng::from_seed([0u8;32])) + .fill_bytes(&mut b); + b + }; + debug_assert!(test(orig_transcript) == test(transcript_from_data(new_transcript))); +} diff --git a/client/consensus/epochs/Cargo.toml b/client/consensus/epochs/Cargo.toml index c1c47a1a68068..3911a59b72774 100644 --- a/client/consensus/epochs/Cargo.toml +++ b/client/consensus/epochs/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } parking_lot = "0.10.0" fork-tree = { version = "2.0.0-rc3", path = "../../../utils/fork-tree" } sp-runtime = { path = "../../../primitives/runtime" , version = "2.0.0-rc3"} diff --git a/client/consensus/manual-seal/src/lib.rs b/client/consensus/manual-seal/src/lib.rs index 233a774a54b11..53cc57ba6e8f2 100644 --- a/client/consensus/manual-seal/src/lib.rs +++ b/client/consensus/manual-seal/src/lib.rs @@ -70,7 +70,7 @@ impl Verifier for ManualSealVerifier { /// Instantiate the import queue for the manual seal consensus engine. pub fn import_queue( block_import: BoxBlockImport, - spawner: &impl sp_core::traits::SpawnBlocking, + spawner: &impl sp_core::traits::SpawnNamed, registry: Option<&Registry>, ) -> BasicQueue where diff --git a/client/consensus/pow/Cargo.toml b/client/consensus/pow/Cargo.toml index b48f54a3257f7..cd8d4cab422d4 100644 --- a/client/consensus/pow/Cargo.toml +++ b/client/consensus/pow/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } sp-core = { version = "2.0.0-rc3", path = "../../../primitives/core" } sp-blockchain = { version = "2.0.0-rc3", path = "../../../primitives/blockchain" } sp-runtime = { version = "2.0.0-rc3", path = "../../../primitives/runtime" } diff --git a/client/consensus/pow/src/lib.rs b/client/consensus/pow/src/lib.rs index 24a8b63281208..8c15528795c55 100644 --- a/client/consensus/pow/src/lib.rs +++ b/client/consensus/pow/src/lib.rs @@ -466,7 +466,7 @@ pub fn import_queue( finality_proof_import: Option>, algorithm: Algorithm, inherent_data_providers: InherentDataProviders, - spawner: &impl sp_core::traits::SpawnBlocking, + spawner: &impl sp_core::traits::SpawnNamed, registry: Option<&Registry>, ) -> Result< PowImportQueue, diff --git a/client/consensus/slots/Cargo.toml b/client/consensus/slots/Cargo.toml index dae0a924b79c3..25a137d214678 100644 --- a/client/consensus/slots/Cargo.toml +++ b/client/consensus/slots/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } sc-client-api = { version = "2.0.0-rc3", path = "../../api" } sp-core = { version = "2.0.0-rc3", path = "../../../primitives/core" } sp-application-crypto = { version = "2.0.0-rc3", path = "../../../primitives/application-crypto" } diff --git a/client/consensus/slots/src/lib.rs b/client/consensus/slots/src/lib.rs index fe1c6bab7b579..950f83fbced18 100644 --- a/client/consensus/slots/src/lib.rs +++ b/client/consensus/slots/src/lib.rs @@ -455,7 +455,7 @@ impl SlotDuration { CB: FnOnce(ApiRef, &BlockId) -> sp_blockchain::Result, T: SlotData + Encode + Decode + Debug, { - match client.get_aux(T::SLOT_KEY)? { + let slot_duration = match client.get_aux(T::SLOT_KEY)? { Some(v) => ::decode(&mut &v[..]) .map(SlotDuration) .map_err(|_| { @@ -479,7 +479,15 @@ impl SlotDuration { Ok(SlotDuration(genesis_slot_duration)) } + }?; + + if slot_duration.slot_duration() == 0 { + return Err(sp_blockchain::Error::Msg( + "Invalid value for slot_duration: the value must be greater than 0.".into(), + )) } + + Ok(slot_duration) } /// Returns slot data value. diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index d9fcdf7f6a2cf..22ca6e64aa568 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -20,7 +20,7 @@ kvdb-memorydb = "0.6.0" linked-hash-map = "0.5.2" hash-db = "0.15.2" parity-util-mem = { version = "0.6.1", default-features = false, features = ["std"] } -codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } blake2-rfc = "0.2.18" sc-client-api = { version = "2.0.0-rc3", path = "../api" } diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index f75693ec9f00e..b4f4892a04970 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -271,7 +271,7 @@ pub struct DatabaseSettings { } /// Where to find the database.. -#[derive(Clone)] +#[derive(Debug, Clone)] pub enum DatabaseSettingsSrc { /// Load a RocksDB database from a given path. Recommended for most uses. RocksDb { @@ -544,7 +544,12 @@ pub struct BlockImportOperation { impl BlockImportOperation { fn apply_offchain(&mut self, transaction: &mut Transaction) { - for (key, value_operation) in self.offchain_storage_updates.drain() { + for ((prefix, key), value_operation) in self.offchain_storage_updates.drain() { + let key: Vec = prefix + .into_iter() + .chain(sp_core::sp_std::iter::once(b'/')) + .chain(key.into_iter()) + .collect(); match value_operation { OffchainOverlayedChange::SetValue(val) => transaction.set_from_vec(columns::OFFCHAIN, &key, val), OffchainOverlayedChange::Remove => transaction.remove(columns::OFFCHAIN, &key), diff --git a/client/db/src/parity_db.rs b/client/db/src/parity_db.rs index ea25aaa9f89e3..ad1c6c7656aaa 100644 --- a/client/db/src/parity_db.rs +++ b/client/db/src/parity_db.rs @@ -27,7 +27,7 @@ fn handle_err(result: parity_db::Result) -> T { match result { Ok(r) => r, Err(e) => { - panic!("Critical database eror: {:?}", e); + panic!("Critical database error: {:?}", e); } } } diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml index 9eee3de1e26a2..f1499693f39cc 100644 --- a/client/executor/Cargo.toml +++ b/client/executor/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] derive_more = "0.99.2" -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } sp-io = { version = "2.0.0-rc3", path = "../../primitives/io" } sp-core = { version = "2.0.0-rc3", path = "../../primitives/core" } sp-trie = { version = "2.0.0-rc3", path = "../../primitives/trie" } @@ -44,6 +44,9 @@ substrate-test-runtime = { version = "2.0.0-rc3", path = "../../test-utils/runti sp-state-machine = { version = "0.8.0-rc3", path = "../../primitives/state-machine" } test-case = "0.3.3" sp-runtime = { version = "2.0.0-rc3", path = "../../primitives/runtime" } +sp-tracing = { version = "2.0.0-rc3", path = "../../primitives/tracing" } +sc-tracing = { version = "2.0.0-rc3", path = "../tracing" } +tracing = "0.1.14" [features] default = [ "std" ] diff --git a/client/executor/common/Cargo.toml b/client/executor/common/Cargo.toml index e9d2586e365b8..a6ff79a06777e 100644 --- a/client/executor/common/Cargo.toml +++ b/client/executor/common/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] log = "0.4.8" derive_more = "0.99.2" parity-wasm = "0.41.0" -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } wasmi = "0.6.2" sp-core = { version = "2.0.0-rc3", path = "../../../primitives/core" } sp-allocator = { version = "2.0.0-rc3", path = "../../../primitives/allocator" } diff --git a/client/executor/runtime-test/src/lib.rs b/client/executor/runtime-test/src/lib.rs index dc6bab759ef00..4962c558eaa2d 100644 --- a/client/executor/runtime-test/src/lib.rs +++ b/client/executor/runtime-test/src/lib.rs @@ -10,7 +10,7 @@ use sp_std::{vec::Vec, vec}; #[cfg(not(feature = "std"))] use sp_io::{ storage, hashing::{blake2_128, blake2_256, sha2_256, twox_128, twox_256}, - crypto::{ed25519_verify, sr25519_verify}, + crypto::{ed25519_verify, sr25519_verify}, wasm_tracing, }; #[cfg(not(feature = "std"))] use sp_runtime::{print, traits::{BlakeTwo256, Hash}}; @@ -246,6 +246,14 @@ sp_core::wasm_export_functions! { sp_allocator::FreeingBumpHeapAllocator::new(0); } + fn test_enter_span() -> u64 { + wasm_tracing::enter_span("integration_test_span_target", "integration_test_span_name") + } + + fn test_exit_span(span_id: u64) { + wasm_tracing::exit_span(span_id) + } + fn returns_mutable_static() -> u64 { unsafe { MUTABLE_STATIC += 1; diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index 80b123ed4b556..f07e98178b5d9 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -658,3 +658,102 @@ fn parallel_execution(wasm_method: WasmExecutionMethod) { t.join().unwrap(); } } + +#[test_case(WasmExecutionMethod::Interpreted)] +fn wasm_tracing_should_work(wasm_method: WasmExecutionMethod) { + + use std::sync::{Arc, Mutex}; + + use sc_tracing::SpanDatum; + + impl sc_tracing::TraceHandler for TestTraceHandler { + fn process_span(&self, sd: SpanDatum) { + self.0.lock().unwrap().push(sd); + } + } + + struct TestTraceHandler(Arc>>); + + let traces = Arc::new(Mutex::new(Vec::new())); + let handler = TestTraceHandler(traces.clone()); + + // Create subscriber with wasm_tracing disabled + let test_subscriber = sc_tracing::ProfilingSubscriber::new_with_handler( + Box::new(handler), "integration_test_span_target"); + + let _guard = tracing::subscriber::set_default(test_subscriber); + + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + + // Test tracing disabled + assert!(!sp_tracing::wasm_tracing_enabled()); + + let span_id = call_in_wasm( + "test_enter_span", + &[], + wasm_method, + &mut ext, + ).unwrap(); + + assert_eq!( + 0u64.encode(), + span_id + ); + // Repeat to check span id always 0 when deactivated + let span_id = call_in_wasm( + "test_enter_span", + &[], + wasm_method, + &mut ext, + ).unwrap(); + + assert_eq!( + 0u64.encode(), + span_id + ); + + call_in_wasm( + "test_exit_span", + &span_id.encode(), + wasm_method, + &mut ext, + ).unwrap(); + // Check span has not been recorded + let len = traces.lock().unwrap().len(); + assert_eq!(len, 0); + + // Test tracing enabled + sp_tracing::set_wasm_tracing(true); + + let span_id = call_in_wasm( + "test_enter_span", + &[], + wasm_method, + &mut ext, + ).unwrap(); + + let span_id = u64::decode(&mut &span_id[..]).unwrap(); + + assert!( + span_id > 0 + ); + + call_in_wasm( + "test_exit_span", + &span_id.encode(), + wasm_method, + &mut ext, + ).unwrap(); + + // Check there is only the single trace + let len = traces.lock().unwrap().len(); + assert_eq!(len, 1); + + let span_datum = traces.lock().unwrap().pop().unwrap(); + let values = span_datum.values.into_inner(); + assert_eq!(span_datum.target, "integration_test_span_target"); + assert_eq!(span_datum.name, "integration_test_span_name"); + assert_eq!(values.get("wasm").unwrap(), "true"); + assert_eq!(values.get("is_valid_trace").unwrap(), "true"); +} diff --git a/client/executor/wasmi/Cargo.toml b/client/executor/wasmi/Cargo.toml index 94f28f744bbd5..f3c2ee2c67521 100644 --- a/client/executor/wasmi/Cargo.toml +++ b/client/executor/wasmi/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] log = "0.4.8" wasmi = "0.6.2" -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } sc-executor-common = { version = "0.8.0-rc3", path = "../common" } sp-wasm-interface = { version = "2.0.0-rc3", path = "../../../primitives/wasm-interface" } sp-runtime-interface = { version = "2.0.0-rc3", path = "../../../primitives/runtime-interface" } diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index 730bc74932267..6d008bcee6b5c 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] log = "0.4.8" scoped-tls = "1.0" parity-wasm = "0.41.0" -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } sc-executor-common = { version = "0.8.0-rc3", path = "../common" } sp-wasm-interface = { version = "2.0.0-rc3", path = "../../../primitives/wasm-interface" } sp-runtime-interface = { version = "2.0.0-rc3", path = "../../../primitives/runtime-interface" } diff --git a/client/finality-grandpa/Cargo.toml b/client/finality-grandpa/Cargo.toml index 2abe609ea546b..29b9cdaebadae 100644 --- a/client/finality-grandpa/Cargo.toml +++ b/client/finality-grandpa/Cargo.toml @@ -22,7 +22,8 @@ log = "0.4.8" parking_lot = "0.10.0" rand = "0.7.2" assert_matches = "1.3.0" -parity-scale-codec = { version = "1.3.0", features = ["derive"] } +parity-scale-codec = { version = "1.3.1", features = ["derive"] } +sp-application-crypto = { version = "2.0.0-rc3", path = "../../primitives/application-crypto" } sp-arithmetic = { version = "2.0.0-rc3", path = "../../primitives/arithmetic" } sp-runtime = { version = "2.0.0-rc3", path = "../../primitives/runtime" } sp-utils = { version = "2.0.0-rc3", path = "../../primitives/utils" } diff --git a/client/finality-grandpa/src/communication/mod.rs b/client/finality-grandpa/src/communication/mod.rs index abd1c27983a6d..e331d8b089fc7 100644 --- a/client/finality-grandpa/src/communication/mod.rs +++ b/client/finality-grandpa/src/communication/mod.rs @@ -35,12 +35,12 @@ use parking_lot::Mutex; use prometheus_endpoint::Registry; use std::{pin::Pin, sync::Arc, task::{Context, Poll}}; +use sp_core::traits::BareCryptoStorePtr; use finality_grandpa::Message::{Prevote, Precommit, PrimaryPropose}; use finality_grandpa::{voter, voter_set::VoterSet}; use sc_network::{NetworkService, ReputationChange}; use sc_network_gossip::{GossipEngine, Network as GossipNetwork}; use parity_scale_codec::{Encode, Decode}; -use sp_core::Pair; use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, NumberFor}; use sc_telemetry::{telemetry, CONSENSUS_DEBUG, CONSENSUS_INFO}; @@ -58,7 +58,7 @@ use gossip::{ VoteMessage, }; use sp_finality_grandpa::{ - AuthorityPair, AuthorityId, AuthoritySignature, SetId as SetIdNumber, RoundNumber, + AuthorityId, AuthoritySignature, SetId as SetIdNumber, RoundNumber, }; use sp_utils::mpsc::TracingUnboundedReceiver; @@ -272,10 +272,11 @@ impl> NetworkBridge { /// network all within the current set. pub(crate) fn round_communication( &self, + keystore: Option, round: Round, set_id: SetId, voters: Arc>, - local_key: Option, + local_key: Option, has_voted: HasVoted, ) -> ( impl Stream> + Unpin, @@ -287,10 +288,9 @@ impl> NetworkBridge { &*voters, ); - let locals = local_key.and_then(|pair| { - let id = pair.public(); + let local_id = local_key.and_then(|id| { if voters.contains(&id) { - Some((pair, id)) + Some(id) } else { None } @@ -350,10 +350,11 @@ impl> NetworkBridge { let (tx, out_rx) = mpsc::channel(0); let outgoing = OutgoingMessages:: { + keystore: keystore.clone(), round: round.0, set_id: set_id.0, network: self.gossip_engine.clone(), - locals, + local_id, sender: tx, has_voted, }; @@ -628,10 +629,11 @@ pub struct SetId(pub SetIdNumber); pub(crate) struct OutgoingMessages { round: RoundNumber, set_id: SetIdNumber, - locals: Option<(AuthorityPair, AuthorityId)>, + local_id: Option, sender: mpsc::Sender>, network: Arc>>, has_voted: HasVoted, + keystore: Option, } impl Unpin for OutgoingMessages {} @@ -665,14 +667,26 @@ impl Sink> for OutgoingMessages } // when locals exist, sign messages on import - if let Some((ref pair, _)) = self.locals { + if let Some(ref public) = self.local_id { + let keystore = match &self.keystore { + Some(keystore) => keystore.clone(), + None => { + return Err(Error::Signing("Cannot sign without a keystore".to_string())) + } + }; + let target_hash = *(msg.target().0); let signed = sp_finality_grandpa::sign_message( + keystore, msg, - pair, + public.clone(), self.round, self.set_id, - ); + ).ok_or( + Error::Signing(format!( + "Failed to sign GRANDPA vote for round {} targetting {:?}", self.round, target_hash + )) + )?; let message = GossipMessage::Vote(VoteMessage:: { message: signed.clone(), diff --git a/client/finality-grandpa/src/environment.rs b/client/finality-grandpa/src/environment.rs index afcc3891ac39f..6db854bacc13b 100644 --- a/client/finality-grandpa/src/environment.rs +++ b/client/finality-grandpa/src/environment.rs @@ -35,7 +35,6 @@ use finality_grandpa::{ voter, voter_set::VoterSet, }; use sp_blockchain::{HeaderBackend, HeaderMetadata, Error as ClientError}; -use sp_core::Pair; use sp_runtime::generic::BlockId; use sp_runtime::traits::{ Block as BlockT, Header as HeaderT, NumberFor, One, Zero, @@ -704,11 +703,11 @@ where let prevote_timer = Delay::new(self.config.gossip_duration * 2); let precommit_timer = Delay::new(self.config.gossip_duration * 4); - let local_key = crate::is_voter(&self.voters, &self.config.keystore); + let local_key = crate::is_voter(&self.voters, self.config.keystore.as_ref()); let has_voted = match self.voter_set_state.has_voted(round) { HasVoted::Yes(id, vote) => { - if local_key.as_ref().map(|k| k.public() == id).unwrap_or(false) { + if local_key.as_ref().map(|k| k == &id).unwrap_or(false) { HasVoted::Yes(id, vote) } else { HasVoted::No @@ -718,6 +717,7 @@ where }; let (incoming, outgoing) = self.network.round_communication( + self.config.keystore.clone(), crate::communication::Round(round), crate::communication::SetId(self.set_id), self.voters.clone(), @@ -740,7 +740,7 @@ where let outgoing = Box::pin(outgoing.sink_err_into()); voter::RoundData { - voter_id: local_key.map(|pair| pair.public()), + voter_id: local_key, prevote_timer: Box::pin(prevote_timer.map(Ok)), precommit_timer: Box::pin(precommit_timer.map(Ok)), incoming, @@ -749,10 +749,10 @@ where } fn proposed(&self, round: RoundNumber, propose: PrimaryPropose) -> Result<(), Self::Error> { - let local_id = crate::is_voter(&self.voters, &self.config.keystore); + let local_id = crate::is_voter(&self.voters, self.config.keystore.as_ref()); let local_id = match local_id { - Some(id) => id.public(), + Some(id) => id, None => return Ok(()), }; @@ -788,10 +788,10 @@ where } fn prevoted(&self, round: RoundNumber, prevote: Prevote) -> Result<(), Self::Error> { - let local_id = crate::is_voter(&self.voters, &self.config.keystore); + let local_id = crate::is_voter(&self.voters, self.config.keystore.as_ref()); let local_id = match local_id { - Some(id) => id.public(), + Some(id) => id, None => return Ok(()), }; @@ -829,10 +829,10 @@ where } fn precommitted(&self, round: RoundNumber, precommit: Precommit) -> Result<(), Self::Error> { - let local_id = crate::is_voter(&self.voters, &self.config.keystore); + let local_id = crate::is_voter(&self.voters, self.config.keystore.as_ref()); let local_id = match local_id { - Some(id) => id.public(), + Some(id) => id, None => return Ok(()), }; diff --git a/client/finality-grandpa/src/import.rs b/client/finality-grandpa/src/import.rs index 7e3799b1e25e1..b37ab7907a62e 100644 --- a/client/finality-grandpa/src/import.rs +++ b/client/finality-grandpa/src/import.rs @@ -669,6 +669,7 @@ where Error::Blockchain(error) => ConsensusError::ClientImport(error), Error::Client(error) => ConsensusError::ClientImport(error.to_string()), Error::Safety(error) => ConsensusError::ClientImport(error), + Error::Signing(error) => ConsensusError::ClientImport(error), Error::Timer(error) => ConsensusError::ClientImport(error.to_string()), }); }, diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index 7b20d082a01ab..481544b5c640d 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -56,8 +56,10 @@ #![warn(missing_docs)] -use futures::prelude::*; -use futures::StreamExt; +use futures::{ + prelude::*, + StreamExt, +}; use log::{debug, info}; use sc_client_api::{ backend::{AuxStore, Backend}, @@ -70,10 +72,13 @@ use sp_api::ProvideRuntimeApi; use sp_blockchain::{HeaderBackend, Error as ClientError, HeaderMetadata}; use sp_runtime::generic::BlockId; use sp_runtime::traits::{NumberFor, Block as BlockT, DigestFor, Zero}; -use sc_keystore::KeyStorePtr; use sp_inherents::InherentDataProviders; use sp_consensus::{SelectChain, BlockImport}; -use sp_core::Pair; +use sp_core::{ + crypto::Public, + traits::BareCryptoStorePtr, +}; +use sp_application_crypto::AppKey; use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver}; use sc_telemetry::{telemetry, CONSENSUS_INFO, CONSENSUS_DEBUG}; use parking_lot::RwLock; @@ -131,10 +136,10 @@ use aux_schema::PersistentData; use environment::{Environment, VoterSetState}; use until_imported::UntilGlobalMessageBlocksImported; use communication::{NetworkBridge, Network as NetworkT}; -use sp_finality_grandpa::{AuthorityList, AuthorityPair, AuthoritySignature, SetId}; +use sp_finality_grandpa::{AuthorityList, AuthoritySignature, SetId}; // Re-export these two because it's just so damn convenient. -pub use sp_finality_grandpa::{AuthorityId, GrandpaApi, ScheduledChange}; +pub use sp_finality_grandpa::{AuthorityId, AuthorityPair, GrandpaApi, ScheduledChange}; use std::marker::PhantomData; #[cfg(test)] @@ -264,7 +269,7 @@ pub struct Config { /// Some local identifier of the voter. pub name: Option, /// The keystore that manages the keys of this node. - pub keystore: Option, + pub keystore: Option, } impl Config { @@ -284,6 +289,8 @@ pub enum Error { Blockchain(String), /// Could not complete a round on disk. Client(ClientError), + /// Could not sign outgoing message + Signing(String), /// An invariant has been violated (e.g. not finalizing pending change blocks in-order) Safety(String), /// A timer failed to fire. @@ -586,7 +593,7 @@ fn global_communication( voters: &Arc>, client: Arc, network: &NetworkBridge, - keystore: &Option, + keystore: &Option, metrics: Option, ) -> ( impl Stream< @@ -602,7 +609,7 @@ fn global_communication( N: NetworkT, NumberFor: BlockNumberOps, { - let is_voter = is_voter(voters, keystore).is_some(); + let is_voter = is_voter(voters, keystore.as_ref()).is_some(); // verification stream let (global_in, global_out) = network.global_communication( @@ -729,7 +736,7 @@ pub fn run_grandpa_voter( .for_each(move |_| { let curr = authorities.current_authorities(); let mut auths = curr.iter().map(|(p, _)| p); - let maybe_authority_id = authority_id(&mut auths, &conf.keystore) + let maybe_authority_id = authority_id(&mut auths, conf.keystore.as_ref()) .unwrap_or_default(); telemetry!(CONSENSUS_INFO; "afg.authority_set"; @@ -865,8 +872,7 @@ where fn rebuild_voter(&mut self) { debug!(target: "afg", "{}: Starting new voter with set ID {}", self.env.config.name(), self.env.set_id); - let authority_id = is_voter(&self.env.voters, &self.env.config.keystore) - .map(|ap| ap.public()) + let authority_id = is_voter(&self.env.voters, self.env.config.keystore.as_ref()) .unwrap_or_default(); telemetry!(CONSENSUS_DEBUG; "afg.starting_new_voter"; @@ -1089,12 +1095,16 @@ pub fn setup_disabled_grandpa( /// Returns the key pair of the node that is being used in the current voter set or `None`. fn is_voter( voters: &Arc>, - keystore: &Option, -) -> Option { + keystore: Option<&BareCryptoStorePtr>, +) -> Option { match keystore { Some(keystore) => voters .iter() - .find_map(|(p, _)| keystore.read().key_pair::(&p).ok()), + .find(|(p, _)| { + keystore.read() + .has_keys(&[(p.to_raw_vec(), AuthorityId::ID)]) + }) + .map(|(p, _)| p.clone()), None => None, } } @@ -1102,19 +1112,16 @@ fn is_voter( /// Returns the authority id of this node, if available. fn authority_id<'a, I>( authorities: &mut I, - keystore: &Option, + keystore: Option<&BareCryptoStorePtr>, ) -> Option where I: Iterator, { match keystore { Some(keystore) => { authorities - .find_map(|p| { - keystore.read().key_pair::(&p) - .ok() - .map(|ap| ap.public()) - }) - } + .find(|p| keystore.read().has_keys(&[(p.to_raw_vec(), AuthorityId::ID)])) + .cloned() + }, None => None, } } diff --git a/client/finality-grandpa/src/observer.rs b/client/finality-grandpa/src/observer.rs index cd678b3bb4542..f7179d70e7a34 100644 --- a/client/finality-grandpa/src/observer.rs +++ b/client/finality-grandpa/src/observer.rs @@ -26,7 +26,7 @@ use finality_grandpa::{ BlockNumberOps, Error as GrandpaError, voter, voter_set::VoterSet }; use log::{debug, info, warn}; - +use sp_core::traits::BareCryptoStorePtr; use sp_consensus::SelectChain; use sc_client_api::backend::Backend; use sp_utils::mpsc::TracingUnboundedReceiver; @@ -211,7 +211,7 @@ struct ObserverWork> { client: Arc, network: NetworkBridge, persistent_data: PersistentData, - keystore: Option, + keystore: Option, voter_commands_rx: TracingUnboundedReceiver>>, _phantom: PhantomData, } @@ -228,7 +228,7 @@ where client: Arc, network: NetworkBridge, persistent_data: PersistentData, - keystore: Option, + keystore: Option, voter_commands_rx: TracingUnboundedReceiver>>, ) -> Self { @@ -239,7 +239,7 @@ where client, network, persistent_data, - keystore, + keystore: keystore.clone(), voter_commands_rx, _phantom: PhantomData, }; diff --git a/client/finality-grandpa/src/tests.rs b/client/finality-grandpa/src/tests.rs index 25e6253652001..ffd8f1c8c642d 100644 --- a/client/finality-grandpa/src/tests.rs +++ b/client/finality-grandpa/src/tests.rs @@ -282,7 +282,7 @@ fn make_ids(keys: &[Ed25519Keyring]) -> AuthorityList { keys.iter().map(|key| key.clone().public().into()).map(|id| (id, 1)).collect() } -fn create_keystore(authority: Ed25519Keyring) -> (KeyStorePtr, tempfile::TempDir) { +fn create_keystore(authority: Ed25519Keyring) -> (BareCryptoStorePtr, tempfile::TempDir) { let keystore_path = tempfile::tempdir().expect("Creates keystore path"); let keystore = sc_keystore::Store::open(keystore_path.path(), None).expect("Creates keystore"); keystore.write().insert_ephemeral_from_seed::(&authority.to_seed()) @@ -1050,7 +1050,7 @@ fn voter_persists_its_votes() { voter_rx: TracingUnboundedReceiver<()>, net: Arc>, client: PeersClient, - keystore: KeyStorePtr, + keystore: BareCryptoStorePtr, } impl Future for ResettableVoter { @@ -1136,7 +1136,7 @@ fn voter_persists_its_votes() { let config = Config { gossip_duration: TEST_GOSSIP_DURATION, justification_period: 32, - keystore: Some(keystore), + keystore: Some(keystore.clone()), name: Some(format!("peer#{}", 1)), is_authority: true, observer_enabled: true, @@ -1160,10 +1160,11 @@ fn voter_persists_its_votes() { ); let (round_rx, round_tx) = network.round_communication( + Some(keystore), communication::Round(1), communication::SetId(0), Arc::new(VoterSet::new(voters).unwrap()), - Some(peers[1].pair().into()), + Some(peers[1].public().into()), HasVoted::No, ); diff --git a/client/informant/Cargo.toml b/client/informant/Cargo.toml index 0ef6f30805561..671535933b86d 100644 --- a/client/informant/Cargo.toml +++ b/client/informant/Cargo.toml @@ -19,6 +19,8 @@ parity-util-mem = { version = "0.6.1", default-features = false, features = ["pr wasm-timer = "0.2" sc-client-api = { version = "2.0.0-rc3", path = "../api" } sc-network = { version = "0.8.0-rc3", path = "../network" } -sc-service = { version = "0.8.0-rc3", default-features = false, path = "../service" } sp-blockchain = { version = "2.0.0-rc3", path = "../../primitives/blockchain" } sp-runtime = { version = "2.0.0-rc3", path = "../../primitives/runtime" } +sp-utils = { version = "2.0.0-rc2", path = "../../primitives/utils" } +sp-transaction-pool = { version = "2.0.0-rc2", path = "../../primitives/transaction-pool" } +parking_lot = "0.10.2" diff --git a/client/informant/src/display.rs b/client/informant/src/display.rs index 42f498998362e..4491eb61d69ca 100644 --- a/client/informant/src/display.rs +++ b/client/informant/src/display.rs @@ -14,15 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +use crate::OutputFormat; use ansi_term::Colour; -use sc_client_api::ClientInfo; use log::info; -use sc_network::SyncState; -use sp_runtime::traits::{Block as BlockT, CheckedDiv, NumberFor, Zero, Saturating}; -use sc_service::NetworkStatus; -use std::{convert::{TryFrom, TryInto}, fmt}; +use sc_client_api::ClientInfo; +use sc_network::{NetworkStatus, SyncState}; +use sp_runtime::traits::{Block as BlockT, CheckedDiv, NumberFor, Saturating, Zero}; +use std::{ + convert::{TryFrom, TryInto}, + fmt, +}; use wasm_timer::Instant; -use crate::OutputFormat; /// State of the informant display system. /// @@ -67,16 +69,22 @@ impl InformantDisplay { self.last_update = Instant::now(); self.last_number = Some(best_number); - let (status, target) = match (net_status.sync_state, net_status.best_seen_block) { - (SyncState::Idle, _) => ("💤 Idle".into(), "".into()), - (SyncState::Downloading, None) => (format!("⚙️ Preparing{}", speed), "".into()), - (SyncState::Downloading, Some(n)) => (format!("⚙️ Syncing{}", speed), format!(", target=#{}", n)), + let (level, status, target) = match (net_status.sync_state, net_status.best_seen_block) { + (SyncState::Idle, _) => ("💤", "Idle".into(), "".into()), + (SyncState::Downloading, None) => ("⚙️ ", format!("Preparing{}", speed), "".into()), + (SyncState::Downloading, Some(n)) => ( + "⚙️ ", + format!("Syncing{}", speed), + format!(", target=#{}", n), + ), }; - if self.format == OutputFormat::Coloured { + if self.format.enable_color { info!( target: "substrate", - "{}{} ({} peers), best: #{} ({}), finalized #{} ({}), {} {}", + "{} {}{}{} ({} peers), best: #{} ({}), finalized #{} ({}), {} {}", + level, + self.format.prefix, Colour::White.bold().paint(&status), target, Colour::White.bold().paint(format!("{}", num_connected_peers)), @@ -86,11 +94,13 @@ impl InformantDisplay { info.chain.finalized_hash, Colour::Green.paint(format!("⬇ {}", TransferRateFormat(net_status.average_download_per_sec))), Colour::Red.paint(format!("⬆ {}", TransferRateFormat(net_status.average_upload_per_sec))), - ); + ) } else { info!( target: "substrate", - "{}{} ({} peers), best: #{} ({}), finalized #{} ({}), ⬇ {} ⬆ {}", + "{} {}{}{} ({} peers), best: #{} ({}), finalized #{} ({}), ⬇ {} ⬆ {}", + level, + self.format.prefix, status, target, num_connected_peers, @@ -100,7 +110,7 @@ impl InformantDisplay { info.chain.finalized_hash, TransferRateFormat(net_status.average_download_per_sec), TransferRateFormat(net_status.average_upload_per_sec), - ); + ) } } } diff --git a/client/informant/src/lib.rs b/client/informant/src/lib.rs index 6eea9c1d0434c..d56afcf335917 100644 --- a/client/informant/src/lib.rs +++ b/client/informant/src/lib.rs @@ -19,33 +19,86 @@ //! Console informant. Prints sync progress and block events. Runs on the calling thread. use ansi_term::Colour; -use sc_client_api::{BlockchainEvents, UsageProvider}; use futures::prelude::*; -use log::{info, warn, trace}; -use sp_runtime::traits::Header; -use sc_service::AbstractService; -use std::time::Duration; +use log::{info, trace, warn}; +use parity_util_mem::MallocSizeOf; +use sc_client_api::{BlockchainEvents, UsageProvider}; +use sc_network::{network_state::NetworkState, NetworkStatus}; +use sp_blockchain::HeaderMetadata; +use sp_runtime::traits::{Block as BlockT, Header}; +use sp_transaction_pool::TransactionPool; +use sp_utils::{status_sinks, mpsc::tracing_unbounded}; +use std::{fmt::Display, sync::Arc, time::Duration, collections::VecDeque}; +use parking_lot::Mutex; mod display; /// The format to print telemetry output in. -#[derive(PartialEq)] -pub enum OutputFormat { - Coloured, - Plain, +#[derive(Clone, Debug)] +pub struct OutputFormat { + /// Enable color output in logs. True by default. + pub enable_color: bool, + /// Defines the informant's prefix for the logs. An empty string by default. + /// + /// By default substrate will show logs without a prefix. Example: + /// + /// ```text + /// 2020-05-28 15:11:06 ✨ Imported #2 (0xc21c…2ca8) + /// 2020-05-28 15:11:07 💤 Idle (0 peers), best: #2 (0xc21c…2ca8), finalized #0 (0x7299…e6df), ⬇ 0 ⬆ 0 + /// ``` + /// + /// But you can define a prefix by setting this string. This will output: + /// + /// ```text + /// 2020-05-28 15:11:06 ✨ [Prefix] Imported #2 (0xc21c…2ca8) + /// 2020-05-28 15:11:07 💤 [Prefix] Idle (0 peers), best: #2 (0xc21c…2ca8), finalized #0 (0x7299…e6df), ⬇ 0 ⬆ 0 + /// ``` + pub prefix: String, } -/// Creates an informant in the form of a `Future` that must be polled regularly. -pub fn build(service: &impl AbstractService, format: OutputFormat) -> impl futures::Future { - let client = service.client(); - let pool = service.transaction_pool(); - - let mut display = display::InformantDisplay::new(format); +impl Default for OutputFormat { + fn default() -> Self { + Self { + enable_color: true, + prefix: String::new(), + } + } +} - let display_notifications = service - .network_status(Duration::from_millis(5000)) +/// Marker trait for a type that implements `TransactionPool` and `MallocSizeOf` on `not(target_os = "unknown")`. +#[cfg(target_os = "unknown")] +pub trait TransactionPoolAndMaybeMallogSizeOf: TransactionPool {} + +/// Marker trait for a type that implements `TransactionPool` and `MallocSizeOf` on `not(target_os = "unknown")`. +#[cfg(not(target_os = "unknown"))] +pub trait TransactionPoolAndMaybeMallogSizeOf: TransactionPool + MallocSizeOf {} + +#[cfg(target_os = "unknown")] +impl TransactionPoolAndMaybeMallogSizeOf for T {} + +#[cfg(not(target_os = "unknown"))] +impl TransactionPoolAndMaybeMallogSizeOf for T {} + +/// Builds the informant and returns a `Future` that drives the informant. +pub fn build( + client: Arc, + network_status_sinks: Arc, NetworkState)>>>, + pool: Arc, + format: OutputFormat, +) -> impl futures::Future +where + C: UsageProvider + HeaderMetadata + BlockchainEvents, + >::Error: Display, +{ + let mut display = display::InformantDisplay::new(format.clone()); + + let client_1 = client.clone(); + let (network_status_sink, network_status_stream) = tracing_unbounded("mpsc_network_status"); + network_status_sinks.lock().push(Duration::from_millis(5000), network_status_sink); + + let display_notifications = network_status_stream .for_each(move |(net_status, _)| { - let info = client.usage_info(); + let info = client_1.usage_info(); if let Some(ref usage) = info.usage { trace!(target: "usage", "Usage statistics: {}", usage); } else { @@ -64,13 +117,30 @@ pub fn build(service: &impl AbstractService, format: OutputFormat) -> impl futur future::ready(()) }); - let client = service.client(); + future::join( + display_notifications, + display_block_import(client, format.prefix), + ).map(|_| ()) +} + +fn display_block_import( + client: Arc, + prefix: String, +) -> impl Future +where + C: UsageProvider + HeaderMetadata + BlockchainEvents, + >::Error: Display, +{ let mut last_best = { let info = client.usage_info(); Some((info.chain.best_number, info.chain.best_hash)) }; - let display_block_import = client.import_notification_stream().for_each(move |n| { + // Hashes of the last blocks we have seen at import. + let mut last_blocks = VecDeque::new(); + let max_blocks_to_track = 100; + + client.import_notification_stream().for_each(move |n| { // detect and log reorganizations. if let Some((ref last_num, ref last_hash)) = last_best { if n.header.parent_hash() != last_hash && n.is_new_best { @@ -82,7 +152,8 @@ pub fn build(service: &impl AbstractService, format: OutputFormat) -> impl futur match maybe_ancestor { Ok(ref ancestor) if ancestor.hash != *last_hash => info!( - "♻️ Reorg on #{},{} to #{},{}, common ancestor #{},{}", + "♻️ {}Reorg on #{},{} to #{},{}, common ancestor #{},{}", + prefix, Colour::Red.bold().paint(format!("{}", last_num)), last_hash, Colour::Green.bold().paint(format!("{}", n.header.number())), n.hash, Colour::White.bold().paint(format!("{}", ancestor.number)), ancestor.hash, @@ -97,12 +168,25 @@ pub fn build(service: &impl AbstractService, format: OutputFormat) -> impl futur last_best = Some((n.header.number().clone(), n.hash.clone())); } - info!(target: "substrate", "✨ Imported #{} ({})", Colour::White.bold().paint(format!("{}", n.header.number())), n.hash); - future::ready(()) - }); - future::join( - display_notifications, - display_block_import - ).map(|_| ()) + // If we already printed a message for a given block recently, + // we should not print it again. + if !last_blocks.contains(&n.hash) { + last_blocks.push_back(n.hash.clone()); + + if last_blocks.len() > max_blocks_to_track { + last_blocks.pop_front(); + } + + info!( + target: "substrate", + "✨ {}Imported #{} ({})", + prefix, + Colour::White.bold().paint(format!("{}", n.header.number())), + n.hash, + ); + } + + future::ready(()) + }) } diff --git a/client/keystore/Cargo.toml b/client/keystore/Cargo.toml index 7ceffc9061a10..47308dd692c06 100644 --- a/client/keystore/Cargo.toml +++ b/client/keystore/Cargo.toml @@ -18,10 +18,11 @@ derive_more = "0.99.2" sp-core = { version = "2.0.0-rc3", path = "../../primitives/core" } sp-application-crypto = { version = "2.0.0-rc3", path = "../../primitives/application-crypto" } hex = "0.4.0" +merlin = { version = "2.0", default-features = false } +parking_lot = "0.10.0" rand = "0.7.2" serde_json = "1.0.41" subtle = "2.1.1" -parking_lot = "0.10.0" [dev-dependencies] tempfile = "3.1.0" diff --git a/client/keystore/src/lib.rs b/client/keystore/src/lib.rs index 6510bb82327b1..aed60ab0cf823 100644 --- a/client/keystore/src/lib.rs +++ b/client/keystore/src/lib.rs @@ -20,7 +20,9 @@ use std::{collections::{HashMap, HashSet}, path::PathBuf, fs::{self, File}, io::{self, Write}, sync::Arc}; use sp_core::{ crypto::{IsWrappedBy, CryptoTypePublicPair, KeyTypeId, Pair as PairT, Protected, Public}, - traits::{BareCryptoStore, BareCryptoStoreError as TraitError}, + traits::{BareCryptoStore, Error as TraitError}, + sr25519::{Public as Sr25519Public, Pair as Sr25519Pair}, + vrf::{VRFTranscriptData, VRFSignature, make_transcript}, Encode, }; use sp_application_crypto::{AppKey, AppPublic, AppPair, ed25519, sr25519, ecdsa}; @@ -270,7 +272,7 @@ impl Store { fn raw_public_keys(&self, id: KeyTypeId) -> Result>> { let mut public_keys: Vec> = self.additional.keys() .into_iter() - .filter_map(|k| if k.0 == id { Some(k.1.clone()) } else { None }) + .filter_map(|k| if k.0 == id { Some(k.1.clone()) } else { None }) .collect(); if let Some(path) = &self.path { @@ -363,7 +365,7 @@ impl BareCryptoStore for Store { .map(|k| sr25519::Public::from_slice(k.as_slice())) .collect() }) - .unwrap_or_default() + .unwrap_or_default() } fn sr25519_generate_new( @@ -438,6 +440,23 @@ impl BareCryptoStore for Store { fn has_keys(&self, public_keys: &[(Vec, KeyTypeId)]) -> bool { public_keys.iter().all(|(p, t)| self.key_phrase_by_type(&p, *t).is_ok()) } + + fn sr25519_vrf_sign( + &self, + key_type: KeyTypeId, + public: &Sr25519Public, + transcript_data: VRFTranscriptData, + ) -> std::result::Result { + let transcript = make_transcript(transcript_data); + let pair = self.key_pair_by_type::(public, key_type) + .map_err(|e| TraitError::PairNotFound(e.to_string()))?; + + let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); + Ok(VRFSignature { + output: inout.to_output(), + proof, + }) + } } #[cfg(test)] diff --git a/client/light/Cargo.toml b/client/light/Cargo.toml new file mode 100644 index 0000000000000..490da15364378 --- /dev/null +++ b/client/light/Cargo.toml @@ -0,0 +1,27 @@ +[package] +description = "components for a light client" +name = "sc-light" +version = "2.0.0-rc3" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +authors = ["Parity Technologies "] +edition = "2018" +homepage = "https://substrate.dev" +repository = "https://github.com/paritytech/substrate/" +documentation = "https://docs.rs/sc-light" + +[dependencies] +parking_lot = "0.10.0" +lazy_static = "1.4.0" +hash-db = "0.15.2" +sp-runtime = { version = "2.0.0-rc2", path = "../../primitives/runtime" } +sp-externalities = { version = "0.8.0-rc2", path = "../../primitives/externalities" } +sp-blockchain = { version = "2.0.0-rc2", path = "../../primitives/blockchain" } +sp-core = { version = "2.0.0-rc2", path = "../../primitives/core" } +sp-state-machine = { version = "0.8.0-rc2", path = "../../primitives/state-machine" } +sc-client-api = { version = "2.0.0-rc2", path = "../api" } +sp-api = { version = "2.0.0-rc2", path = "../../primitives/api" } +codec = { package = "parity-scale-codec", version = "1.3.1" } +sc-executor = { version = "0.8.0-rc2", path = "../executor" } + +[features] +default = [] diff --git a/client/service/src/client/light/backend.rs b/client/light/src/backend.rs similarity index 100% rename from client/service/src/client/light/backend.rs rename to client/light/src/backend.rs diff --git a/client/service/src/client/light/blockchain.rs b/client/light/src/blockchain.rs similarity index 97% rename from client/service/src/client/light/blockchain.rs rename to client/light/src/blockchain.rs index c7b20de594d67..9d557db887d29 100644 --- a/client/service/src/client/light/blockchain.rs +++ b/client/light/src/blockchain.rs @@ -25,8 +25,7 @@ use sp_runtime::{Justification, generic::BlockId}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}; use sp_blockchain::{ - HeaderMetadata, CachedHeaderMetadata, - Error as ClientError, Result as ClientResult, + HeaderMetadata, CachedHeaderMetadata, Error as ClientError, Result as ClientResult, }; pub use sc_client_api::{ backend::{ @@ -42,7 +41,7 @@ pub use sc_client_api::{ }, cht, }; -use super::fetcher::RemoteHeaderRequest; +use crate::fetcher::RemoteHeaderRequest; /// Light client blockchain. pub struct Blockchain { diff --git a/client/service/src/client/light/call_executor.rs b/client/light/src/call_executor.rs similarity index 100% rename from client/service/src/client/light/call_executor.rs rename to client/light/src/call_executor.rs diff --git a/client/service/src/client/light/fetcher.rs b/client/light/src/fetcher.rs similarity index 99% rename from client/service/src/client/light/fetcher.rs rename to client/light/src/fetcher.rs index 542255496739a..88d20cafc903f 100644 --- a/client/service/src/client/light/fetcher.rs +++ b/client/light/src/fetcher.rs @@ -46,8 +46,8 @@ pub use sc_client_api::{ }, cht, }; -use super::blockchain::{Blockchain}; -use super::call_executor::check_execution_proof; +use crate::blockchain::Blockchain; +use crate::call_executor::check_execution_proof; /// Remote data checker. pub struct LightDataChecker> { diff --git a/client/light/src/lib.rs b/client/light/src/lib.rs new file mode 100644 index 0000000000000..deea642bd39d0 --- /dev/null +++ b/client/light/src/lib.rs @@ -0,0 +1,57 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Light client components. + +use sp_runtime::traits::{Block as BlockT, HashFor}; +use sc_client_api::CloneableSpawn; +use std::sync::Arc; +use sp_core::traits::CodeExecutor; + +pub mod backend; +pub mod blockchain; +pub mod call_executor; +pub mod fetcher; + +pub use {backend::*, blockchain::*, call_executor::*, fetcher::*}; + +/// Create an instance of fetch data checker. +pub fn new_fetch_checker>( + blockchain: Arc>, + executor: E, + spawn_handle: Box, +) -> LightDataChecker, B, S> + where + E: CodeExecutor, +{ + LightDataChecker::new(blockchain, executor, spawn_handle) +} + +/// Create an instance of light client blockchain backend. +pub fn new_light_blockchain>(storage: S) -> Arc> { + Arc::new(Blockchain::new(storage)) +} + +/// Create an instance of light client backend. +pub fn new_light_backend(blockchain: Arc>) -> Arc>> + where + B: BlockT, + S: BlockchainStorage, +{ + Arc::new(Backend::new(blockchain)) +} diff --git a/client/network/Cargo.toml b/client/network/Cargo.toml index 94a7b2b57dca3..8467aa1154323 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -21,7 +21,7 @@ prost-build = "0.6.1" bitflags = "1.2.0" bs58 = "0.3.1" bytes = "0.5.0" -codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } derive_more = "0.99.2" either = "1.5.3" erased-serde = "0.3.9" diff --git a/client/network/src/block_requests.rs b/client/network/src/block_requests.rs index ae5a3a0b4e894..6d698a7300126 100644 --- a/client/network/src/block_requests.rs +++ b/client/network/src/block_requests.rs @@ -277,21 +277,13 @@ where return SendRequestOutcome::NotConnected; }; - let protobuf_rq = schema::v1::BlockRequest { - fields: u32::from_be_bytes([req.fields.bits(), 0, 0, 0]), - from_block: match req.from { - message::FromBlock::Hash(h) => - Some(schema::v1::block_request::FromBlock::Hash(h.encode())), - message::FromBlock::Number(n) => - Some(schema::v1::block_request::FromBlock::Number(n.encode())), - }, - to_block: req.to.map(|h| h.encode()).unwrap_or_default(), - direction: match req.direction { - message::Direction::Ascending => schema::v1::Direction::Ascending as i32, - message::Direction::Descending => schema::v1::Direction::Descending as i32, - }, - max_blocks: req.max.unwrap_or(0), - }; + let protobuf_rq = build_protobuf_block_request( + req.fields, + req.from.clone(), + req.to.clone(), + req.direction, + req.max, + ); let mut buf = Vec::with_capacity(protobuf_rq.encoded_len()); if let Err(err) = protobuf_rq.encode(&mut buf) { @@ -386,7 +378,7 @@ where return Err(io::Error::new(io::ErrorKind::Other, msg).into()) }; - let attributes = BlockAttributes::decode(&mut request.fields.to_be_bytes().as_ref())?; + let attributes = BlockAttributes::from_be_u32(request.fields)?; let get_header = attributes.contains(BlockAttributes::HEADER); let get_body = attributes.contains(BlockAttributes::BODY); let get_justification = attributes.contains(BlockAttributes::JUSTIFICATION); @@ -826,3 +818,28 @@ where }.boxed() } } + +/// Build protobuf block request message. +pub(crate) fn build_protobuf_block_request( + attributes: BlockAttributes, + from_block: message::FromBlock, + to_block: Option, + direction: message::Direction, + max_blocks: Option, +) -> schema::v1::BlockRequest { + schema::v1::BlockRequest { + fields: attributes.to_be_u32(), + from_block: match from_block { + message::FromBlock::Hash(h) => + Some(schema::v1::block_request::FromBlock::Hash(h.encode())), + message::FromBlock::Number(n) => + Some(schema::v1::block_request::FromBlock::Number(n.encode())), + }, + to_block: to_block.map(|h| h.encode()).unwrap_or_default(), + direction: match direction { + message::Direction::Ascending => schema::v1::Direction::Ascending as i32, + message::Direction::Descending => schema::v1::Direction::Descending as i32, + }, + max_blocks: max_blocks.unwrap_or(0), + } +} diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 6c9bd3adb9fd7..94b2993b4e6bd 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -425,9 +425,6 @@ pub struct NetworkConfiguration { pub max_parallel_downloads: u32, /// Should we insert non-global addresses into the DHT? pub allow_non_globals_in_dht: bool, - /// If true, uses the `//block-requests/` experimental protocol rather than - /// the legacy substream. This option is meant to be hard-wired to `true` in the future. - pub use_new_block_requests_protocol: bool, } impl NetworkConfiguration { @@ -459,7 +456,6 @@ impl NetworkConfiguration { }, max_parallel_downloads: 5, allow_non_globals_in_dht: false, - use_new_block_requests_protocol: true, } } } diff --git a/client/network/src/discovery.rs b/client/network/src/discovery.rs index f5c293b25128a..73a5916947a39 100644 --- a/client/network/src/discovery.rs +++ b/client/network/src/discovery.rs @@ -324,7 +324,8 @@ impl DiscoveryBehaviour { let ip = match addr.iter().next() { Some(Protocol::Ip4(ip)) => IpNetwork::from(ip), Some(Protocol::Ip6(ip)) => IpNetwork::from(ip), - Some(Protocol::Dns4(_)) | Some(Protocol::Dns6(_)) => return true, + Some(Protocol::Dns(_)) | Some(Protocol::Dns4(_)) | Some(Protocol::Dns6(_)) + => return true, _ => return false }; ip.is_global() diff --git a/client/network/src/error.rs b/client/network/src/error.rs index fed7a331da93c..b87e495983eaf 100644 --- a/client/network/src/error.rs +++ b/client/network/src/error.rs @@ -18,6 +18,7 @@ //! Substrate network possible errors. +use crate::config::TransportConfig; use libp2p::{PeerId, Multiaddr}; use std::fmt; @@ -48,7 +49,18 @@ pub enum Error { second_id: PeerId, }, /// Prometheus metrics error. - Prometheus(prometheus_endpoint::PrometheusError) + Prometheus(prometheus_endpoint::PrometheusError), + /// The network addresses are invalid because they don't match the transport. + #[display( + fmt = "The following addresses are invalid because they don't match the transport: {:?}", + addresses, + )] + AddressesForAnotherTransport { + /// Transport used. + transport: TransportConfig, + /// The invalid addresses. + addresses: Vec, + }, } // Make `Debug` use the `Display` implementation. @@ -65,6 +77,7 @@ impl std::error::Error for Error { Error::Client(ref err) => Some(err), Error::DuplicateBootnode { .. } => None, Error::Prometheus(ref err) => Some(err), + Error::AddressesForAnotherTransport { .. } => None, } } } diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index 0105f32ac37a9..6106616d99d10 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -77,7 +77,7 @@ //! - WebSockets for addresses of the form `/ip4/1.2.3.4/tcp/5/ws`. A TCP/IP connection is open and //! the WebSockets protocol is negotiated on top. Communications then happen inside WebSockets data //! frames. Encryption and multiplexing are additionally negotiated again inside this channel. -//! - DNS for addresses of the form `/dns4/example.com/tcp/5` or `/dns4/example.com/tcp/5/ws`. A +//! - DNS for addresses of the form `/dns/example.com/tcp/5` or `/dns/example.com/tcp/5/ws`. A //! node's address can contain a domain name. //! - (All of the above using IPv6 instead of IPv4.) //! @@ -269,6 +269,7 @@ pub use libp2p::{Multiaddr, PeerId}; pub use libp2p::multiaddr; pub use sc_peerset::ReputationChange; +use sp_runtime::traits::{Block as BlockT, NumberFor}; /// The maximum allowed number of established connections per peer. /// @@ -293,3 +294,22 @@ pub trait NetworkStateInfo { /// Returns the local Peer ID. fn local_peer_id(&self) -> PeerId; } + +/// Overview status of the network. +#[derive(Clone)] +pub struct NetworkStatus { + /// Current global sync state. + pub sync_state: SyncState, + /// Target sync block number. + pub best_seen_block: Option>, + /// Number of peers participating in syncing. + pub num_sync_peers: u32, + /// Total number of connected peers + pub num_connected_peers: usize, + /// Total number of active peers. + pub num_active_peers: usize, + /// Downloaded bytes per second averaged over the past few seconds. + pub average_download_per_sec: u64, + /// Uploaded bytes per second averaged over the past few seconds. + pub average_upload_per_sec: u64, +} diff --git a/client/network/src/light_client_handler.rs b/client/network/src/light_client_handler.rs index 236ae817474ab..ab6bea8761b95 100644 --- a/client/network/src/light_client_handler.rs +++ b/client/network/src/light_client_handler.rs @@ -27,9 +27,10 @@ use bytes::Bytes; use codec::{self, Encode, Decode}; use crate::{ + block_requests::build_protobuf_block_request, chain::Client, config::ProtocolId, - protocol::message::BlockAttributes, + protocol::message::{BlockAttributes, Direction, FromBlock}, schema, }; use futures::{channel::oneshot, future::BoxFuture, prelude::*, stream::FuturesUnordered}; @@ -1062,13 +1063,13 @@ fn retries(request: &Request) -> usize { fn serialize_request(request: &Request) -> Result, prost::EncodeError> { let request = match request { Request::Body { request, .. } => { - let rq = schema::v1::BlockRequest { - fields: u32::from(BlockAttributes::BODY.bits()), - from_block: Some(schema::v1::block_request::FromBlock::Hash(request.header.hash().encode())), - to_block: Vec::new(), - direction: schema::v1::Direction::Ascending as i32, - max_blocks: 1, - }; + let rq = build_protobuf_block_request::<_, NumberFor>( + BlockAttributes::BODY, + FromBlock::Hash(request.header.hash()), + None, + Direction::Ascending, + Some(1), + ); let mut buf = Vec::with_capacity(rq.encoded_len()); rq.encode(&mut buf)?; return Ok(buf); @@ -2036,4 +2037,22 @@ mod tests { assert_eq!(vec![(100, 2)], task::block_on(chan.1).unwrap().unwrap()); // ^--- from `DummyFetchChecker::check_changes_proof` } + + #[test] + fn body_request_fields_encoded_properly() { + let (sender, _) = oneshot::channel(); + let serialized_request = serialize_request::(&Request::Body { + request: RemoteBodyRequest { + header: dummy_header(), + retry_count: None, + }, + sender, + }).unwrap(); + let deserialized_request = schema::v1::BlockRequest::decode(&serialized_request[..]).unwrap(); + assert!( + BlockAttributes::from_be_u32(deserialized_request.fields) + .unwrap() + .contains(BlockAttributes::BODY) + ); + } } diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index 0450818e7a398..90076552a75a5 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -48,7 +48,7 @@ use sp_runtime::traits::{ use sp_arithmetic::traits::SaturatedConversion; use message::{BlockAnnounce, Message}; use message::generic::{Message as GenericMessage, ConsensusMessage, Roles}; -use prometheus_endpoint::{Registry, Gauge, GaugeVec, HistogramVec, PrometheusError, Opts, register, U64}; +use prometheus_endpoint::{Registry, Gauge, Counter, GaugeVec, HistogramVec, PrometheusError, Opts, register, U64}; use sync::{ChainSync, SyncState}; use std::borrow::Cow; use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; @@ -72,13 +72,15 @@ pub use generic_proto::LegacyConnectionKillError; const REQUEST_TIMEOUT_SEC: u64 = 40; /// Interval at which we perform time based maintenance const TICK_TIMEOUT: time::Duration = time::Duration::from_millis(1100); -/// Interval at which we propagate extrinsics; +/// Interval at which we propagate transactions; const PROPAGATE_TIMEOUT: time::Duration = time::Duration::from_millis(2900); /// Maximim number of known block hashes to keep for a peer. const MAX_KNOWN_BLOCKS: usize = 1024; // ~32kb per peer + LruHashSet overhead -/// Maximim number of known extrinsic hashes to keep for a peer. -const MAX_KNOWN_EXTRINSICS: usize = 4096; // ~128kb per peer + overhead +/// Maximim number of known transaction hashes to keep for a peer. +/// +/// This should be approx. 2 blocks full of transactions for the network to function properly. +const MAX_KNOWN_TRANSACTIONS: usize = 10240; // ~300kb per peer + overhead. /// Maximim number of transaction validation request we keep at any moment. const MAX_PENDING_TRANSACTIONS: usize = 8192; @@ -102,29 +104,27 @@ mod rep { pub const CLOGGED_PEER: Rep = Rep::new(-(1 << 12), "Clogged message queue"); /// Reputation change when a peer doesn't respond in time to our messages. pub const TIMEOUT: Rep = Rep::new(-(1 << 10), "Request timeout"); - /// Reputation change when a peer sends us a status message while we already received one. - pub const UNEXPECTED_STATUS: Rep = Rep::new(-(1 << 20), "Unexpected status message"); /// Reputation change when we are a light client and a peer is behind us. pub const PEER_BEHIND_US_LIGHT: Rep = Rep::new(-(1 << 8), "Useless for a light peer"); - /// Reputation change when a peer sends us any extrinsic. + /// Reputation change when a peer sends us any transaction. /// - /// This forces node to verify it, thus the negative value here. Once extrinsic is verified, - /// reputation change should be refunded with `ANY_EXTRINSIC_REFUND` - pub const ANY_EXTRINSIC: Rep = Rep::new(-(1 << 4), "Any extrinsic"); - /// Reputation change when a peer sends us any extrinsic that is not invalid. - pub const ANY_EXTRINSIC_REFUND: Rep = Rep::new(1 << 4, "Any extrinsic (refund)"); - /// Reputation change when a peer sends us an extrinsic that we didn't know about. - pub const GOOD_EXTRINSIC: Rep = Rep::new(1 << 7, "Good extrinsic"); - /// Reputation change when a peer sends us a bad extrinsic. - pub const BAD_EXTRINSIC: Rep = Rep::new(-(1 << 12), "Bad extrinsic"); + /// This forces node to verify it, thus the negative value here. Once transaction is verified, + /// reputation change should be refunded with `ANY_TRANSACTION_REFUND` + pub const ANY_TRANSACTION: Rep = Rep::new(-(1 << 4), "Any transaction"); + /// Reputation change when a peer sends us any transaction that is not invalid. + pub const ANY_TRANSACTION_REFUND: Rep = Rep::new(1 << 4, "Any transaction (refund)"); + /// Reputation change when a peer sends us an transaction that we didn't know about. + pub const GOOD_TRANSACTION: Rep = Rep::new(1 << 7, "Good transaction"); + /// Reputation change when a peer sends us a bad transaction. + pub const BAD_TRANSACTION: Rep = Rep::new(-(1 << 12), "Bad transaction"); /// We sent an RPC query to the given node, but it failed. pub const RPC_FAILED: Rep = Rep::new(-(1 << 12), "Remote call failed"); /// We received a message that failed to decode. pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message"); /// We received an unexpected response. pub const UNEXPECTED_RESPONSE: Rep = Rep::new_fatal("Unexpected response packet"); - /// We received an unexpected extrinsic packet. - pub const UNEXPECTED_EXTRINSICS: Rep = Rep::new_fatal("Unexpected extrinsics packet"); + /// We received an unexpected transaction packet. + pub const UNEXPECTED_TRANSACTIONS: Rep = Rep::new_fatal("Unexpected transactions packet"); /// We received an unexpected light node request. pub const UNEXPECTED_REQUEST: Rep = Rep::new_fatal("Unexpected block request packet"); /// Peer has different genesis. @@ -145,6 +145,7 @@ struct Metrics { fork_targets: Gauge, finality_proofs: GaugeVec, justifications: GaugeVec, + propagated_transactions: Counter, } impl Metrics { @@ -190,6 +191,10 @@ impl Metrics { )?; register(g, r)? }, + propagated_transactions: register(Counter::new( + "sync_propagated_transactions", + "Number of transactions propagated to at least one peer", + )?, r)?, }) } } @@ -216,11 +221,11 @@ impl Future for PendingTransaction { pub struct Protocol { /// Interval at which we call `tick`. tick_timeout: Pin + Send>>, - /// Interval at which we call `propagate_extrinsics`. + /// Interval at which we call `propagate_transactions`. propagate_timeout: Pin + Send>>, /// Pending list of messages to return from `poll` as a priority. pending_messages: VecDeque>, - /// Pending extrinsic verification tasks. + /// Pending transactions verification tasks. pending_transactions: FuturesUnordered, config: ProtocolConfig, genesis_hash: B::Hash, @@ -250,9 +255,6 @@ pub struct Protocol { metrics: Option, /// The `PeerId`'s of all boot nodes. boot_node_ids: Arc>, - /// If true, we send back requests as `CustomMessageOutcome` events. If false, we directly - /// dispatch requests using the legacy substream. - use_new_block_requests_protocol: bool, } #[derive(Default)] @@ -278,7 +280,7 @@ struct Peer { /// Requests we are no longer interested in. obsolete_requests: HashMap, /// Holds a set of transactions known to this peer. - known_extrinsics: LruHashSet, + known_transactions: LruHashSet, /// Holds a set of blocks known to this peer. known_blocks: LruHashSet, /// Request counter, @@ -374,7 +376,6 @@ impl Protocol { block_announce_validator: Box + Send>, metrics_registry: Option<&Registry>, boot_node_ids: Arc>, - use_new_block_requests_protocol: bool, queue_size_report: Option, ) -> error::Result<(Protocol, sc_peerset::PeersetHandle)> { let info = chain.info(); @@ -458,7 +459,6 @@ impl Protocol { None }, boot_node_ids, - use_new_block_requests_protocol, }; Ok((protocol, peerset_handle)) @@ -549,6 +549,11 @@ impl Protocol { self.sync.update_chain_info(&info.best_hash, info.best_number); } + /// Inform sync about an own imported block. + pub fn own_block_imported(&mut self, hash: B::Hash, number: NumberFor) { + self.sync.update_chain_info(&hash, number); + } + fn update_peer_info(&mut self, who: &PeerId) { if let Some(info) = self.sync.peer_info(who) { if let Some(ref mut peer) = self.context_data.peers.get_mut(who) { @@ -596,7 +601,7 @@ impl Protocol { return outcome; }, GenericMessage::Transactions(m) => - self.on_extrinsics(who, m), + self.on_transactions(who, m), GenericMessage::RemoteCallRequest(request) => self.on_remote_call_request(who, request), GenericMessage::RemoteCallResponse(_) => warn!(target: "sub-libp2p", "Received unexpected RemoteCallResponse"), @@ -655,16 +660,6 @@ impl Protocol { CustomMessageOutcome::None } - fn send_request(&mut self, who: &PeerId, message: Message) { - send_request::( - &mut self.behaviour, - &mut self.context_data.stats, - &mut self.context_data.peers, - who, - message, - ); - } - fn send_message( &mut self, who: &PeerId, @@ -725,8 +720,8 @@ impl Protocol { // Print some diagnostics. if let Some(peer) = self.context_data.peers.get(&who) { debug!(target: "sync", "Clogged peer {} (protocol_version: {:?}; roles: {:?}; \ - known_extrinsics: {:?}; known_blocks: {:?}; best_hash: {:?}; best_number: {:?})", - who, peer.info.protocol_version, peer.info.roles, peer.known_extrinsics, peer.known_blocks, + known_transactions: {:?}; known_blocks: {:?}; best_hash: {:?}; best_number: {:?})", + who, peer.info.protocol_version, peer.info.roles, peer.known_transactions, peer.known_blocks, peer.info.best_hash, peer.info.best_number); } else { debug!(target: "sync", "Peer clogged before being properly connected"); @@ -896,15 +891,10 @@ impl Protocol { Ok(sync::OnBlockData::Import(origin, blocks)) => CustomMessageOutcome::BlockImport(origin, blocks), Ok(sync::OnBlockData::Request(peer, mut req)) => { - if self.use_new_block_requests_protocol { - self.update_peer_request(&peer, &mut req); - CustomMessageOutcome::BlockRequest { - target: peer, - request: req, - } - } else { - self.send_request(&peer, GenericMessage::BlockRequest(req)); - CustomMessageOutcome::None + self.update_peer_request(&peer, &mut req); + CustomMessageOutcome::BlockRequest { + target: peer, + request: req, } } Err(sync::BadPeer(id, repu)) => { @@ -977,12 +967,7 @@ impl Protocol { trace!(target: "sync", "New peer {} {:?}", who, status); let _protocol_version = { if self.context_data.peers.contains_key(&who) { - log!( - target: "sync", - if self.important_peers.contains(&who) { Level::Warn } else { Level::Debug }, - "Unexpected status packet from {}", who - ); - self.peerset_handle.report_peer(who, rep::UNEXPECTED_STATUS); + debug!(target: "sync", "Ignoring duplicate status packet from {}", who); return CustomMessageOutcome::None; } if status.genesis_hash != self.genesis_hash { @@ -1063,7 +1048,7 @@ impl Protocol { let peer = Peer { info, block_request: None, - known_extrinsics: LruHashSet::new(NonZeroUsize::new(MAX_KNOWN_EXTRINSICS) + known_transactions: LruHashSet::new(NonZeroUsize::new(MAX_KNOWN_TRANSACTIONS) .expect("Constant is nonzero")), known_blocks: LruHashSet::new(NonZeroUsize::new(MAX_KNOWN_BLOCKS) .expect("Constant is nonzero")), @@ -1082,15 +1067,11 @@ impl Protocol { match self.sync.new_peer(who.clone(), info.best_hash, info.best_number) { Ok(None) => (), Ok(Some(mut req)) => { - if self.use_new_block_requests_protocol { - self.update_peer_request(&who, &mut req); - self.pending_messages.push_back(CustomMessageOutcome::BlockRequest { - target: who.clone(), - request: req, - }); - } else { - self.send_request(&who, GenericMessage::BlockRequest(req)) - } + self.update_peer_request(&who, &mut req); + self.pending_messages.push_back(CustomMessageOutcome::BlockRequest { + target: who.clone(), + request: req, + }); }, Err(sync::BadPeer(id, repu)) => { self.behaviour.disconnect_peer(&id); @@ -1156,28 +1137,29 @@ impl Protocol { .map(|(peer_id, peer)| (peer_id, peer.info.roles)) } - /// Called when peer sends us new extrinsics - fn on_extrinsics( + /// Called when peer sends us new transactions + fn on_transactions( &mut self, who: PeerId, - extrinsics: message::Transactions + transactions: message::Transactions ) { - // sending extrinsic to light node is considered a bad behavior + // sending transaction to light node is considered a bad behavior if !self.config.roles.is_full() { - trace!(target: "sync", "Peer {} is trying to send extrinsic to the light node", who); + trace!(target: "sync", "Peer {} is trying to send transactions to the light node", who); self.behaviour.disconnect_peer(&who); - self.peerset_handle.report_peer(who, rep::UNEXPECTED_EXTRINSICS); + self.peerset_handle.report_peer(who, rep::UNEXPECTED_TRANSACTIONS); return; } - // Accept extrinsics only when fully synced + // Accept transactions only when fully synced if self.sync.status().state != SyncState::Idle { - trace!(target: "sync", "{} Ignoring extrinsics while syncing", who); + trace!(target: "sync", "{} Ignoring transactions while syncing", who); return; } - trace!(target: "sync", "Received {} extrinsics from {}", extrinsics.len(), who); + + trace!(target: "sync", "Received {} transactions from {}", transactions.len(), who); if let Some(ref mut peer) = self.context_data.peers.get_mut(&who) { - for t in extrinsics { + for t in transactions { if self.pending_transactions.len() > MAX_PENDING_TRANSACTIONS { debug!( target: "sync", @@ -1188,9 +1170,9 @@ impl Protocol { } let hash = self.transaction_pool.hash_of(&t); - peer.known_extrinsics.insert(hash); + peer.known_transactions.insert(hash); - self.peerset_handle.report_peer(who.clone(), rep::ANY_EXTRINSIC); + self.peerset_handle.report_peer(who.clone(), rep::ANY_TRANSACTION); self.pending_transactions.push(PendingTransaction { peer_id: who.clone(), @@ -1200,45 +1182,45 @@ impl Protocol { } } - fn on_handle_extrinsic_import(&mut self, who: PeerId, import: TransactionImport) { + fn on_handle_transaction_import(&mut self, who: PeerId, import: TransactionImport) { match import { - TransactionImport::KnownGood => self.peerset_handle.report_peer(who, rep::ANY_EXTRINSIC_REFUND), - TransactionImport::NewGood => self.peerset_handle.report_peer(who, rep::GOOD_EXTRINSIC), - TransactionImport::Bad => self.peerset_handle.report_peer(who, rep::BAD_EXTRINSIC), + TransactionImport::KnownGood => self.peerset_handle.report_peer(who, rep::ANY_TRANSACTION_REFUND), + TransactionImport::NewGood => self.peerset_handle.report_peer(who, rep::GOOD_TRANSACTION), + TransactionImport::Bad => self.peerset_handle.report_peer(who, rep::BAD_TRANSACTION), TransactionImport::None => {}, } } - /// Propagate one extrinsic. - pub fn propagate_extrinsic( + /// Propagate one transaction. + pub fn propagate_transaction( &mut self, hash: &H, ) { - debug!(target: "sync", "Propagating extrinsic [{:?}]", hash); + debug!(target: "sync", "Propagating transaction [{:?}]", hash); // Accept transactions only when fully synced if self.sync.status().state != SyncState::Idle { return; } - if let Some(extrinsic) = self.transaction_pool.transaction(hash) { - let propagated_to = self.do_propagate_extrinsics(&[(hash.clone(), extrinsic)]); + if let Some(transaction) = self.transaction_pool.transaction(hash) { + let propagated_to = self.do_propagate_transactions(&[(hash.clone(), transaction)]); self.transaction_pool.on_broadcasted(propagated_to); } } - fn do_propagate_extrinsics( + fn do_propagate_transactions( &mut self, - extrinsics: &[(H, B::Extrinsic)], + transactions: &[(H, B::Extrinsic)], ) -> HashMap> { let mut propagated_to = HashMap::new(); for (who, peer) in self.context_data.peers.iter_mut() { - // never send extrinsics to the light node + // never send transactions to the light node if !peer.info.roles.is_full() { continue; } - let (hashes, to_send): (Vec<_>, Vec<_>) = extrinsics + let (hashes, to_send): (Vec<_>, Vec<_>) = transactions .iter() - .filter(|&(ref hash, _)| peer.known_extrinsics.insert(hash.clone())) + .filter(|&(ref hash, _)| peer.known_transactions.insert(hash.clone())) .cloned() .unzip(); @@ -1261,18 +1243,24 @@ impl Protocol { } } + if propagated_to.len() > 0 { + if let Some(ref metrics) = self.metrics { + metrics.propagated_transactions.inc(); + } + } + propagated_to } - /// Call when we must propagate ready extrinsics to peers. - pub fn propagate_extrinsics(&mut self) { - debug!(target: "sync", "Propagating extrinsics"); + /// Call when we must propagate ready transactions to peers. + pub fn propagate_transactions(&mut self) { + debug!(target: "sync", "Propagating transactions"); // Accept transactions only when fully synced if self.sync.status().state != SyncState::Idle { return; } - let extrinsics = self.transaction_pool.transactions(); - let propagated_to = self.do_propagate_extrinsics(&extrinsics); + let transactions = self.transaction_pool.transactions(); + let propagated_to = self.do_propagate_transactions(&transactions); self.transaction_pool.on_broadcasted(propagated_to); } @@ -1420,15 +1408,10 @@ impl Protocol { CustomMessageOutcome::BlockImport(origin, blocks) }, Ok(sync::OnBlockData::Request(peer, mut req)) => { - if self.use_new_block_requests_protocol { - self.update_peer_request(&peer, &mut req); - CustomMessageOutcome::BlockRequest { - target: peer, - request: req, - } - } else { - self.send_request(&peer, GenericMessage::BlockRequest(req)); - CustomMessageOutcome::None + self.update_peer_request(&peer, &mut req); + CustomMessageOutcome::BlockRequest { + target: peer, + request: req, } } Err(sync::BadPeer(id, repu)) => { @@ -1528,22 +1511,11 @@ impl Protocol { for result in results { match result { Ok((id, mut req)) => { - if self.use_new_block_requests_protocol { - update_peer_request(&mut self.context_data.peers, &id, &mut req); - self.pending_messages.push_back(CustomMessageOutcome::BlockRequest { - target: id, - request: req, - }); - } else { - let msg = GenericMessage::BlockRequest(req); - send_request( - &mut self.behaviour, - &mut self.context_data.stats, - &mut self.context_data.peers, - &id, - msg - ) - } + update_peer_request(&mut self.context_data.peers, &id, &mut req); + self.pending_messages.push_back(CustomMessageOutcome::BlockRequest { + target: id, + request: req, + }); } Err(sync::BadPeer(id, repu)) => { self.behaviour.disconnect_peer(&id); @@ -1922,27 +1894,6 @@ pub enum CustomMessageOutcome { None, } -fn send_request( - behaviour: &mut GenericProto, - stats: &mut HashMap<&'static str, PacketStats>, - peers: &mut HashMap>, - who: &PeerId, - mut message: Message, -) { - if let GenericMessage::BlockRequest(ref mut r) = message { - if let Some(ref mut peer) = peers.get_mut(who) { - r.id = peer.next_request_id; - peer.next_request_id += 1; - if let Some((timestamp, request)) = peer.block_request.take() { - trace!(target: "sync", "Request {} for {} is now obsolete.", request.id, who); - peer.obsolete_requests.insert(request.id, timestamp); - } - peer.block_request = Some((Instant::now(), r.clone())); - } - } - send_message::(behaviour, stats, who, None, message) -} - fn update_peer_request( peers: &mut HashMap>, who: &PeerId, @@ -2033,65 +1984,35 @@ impl NetworkBehaviour for Protocol { } while let Poll::Ready(Some(())) = self.propagate_timeout.poll_next_unpin(cx) { - self.propagate_extrinsics(); + self.propagate_transactions(); } for (id, mut r) in self.sync.block_requests() { - if self.use_new_block_requests_protocol { - update_peer_request(&mut self.context_data.peers, &id, &mut r); - let event = CustomMessageOutcome::BlockRequest { - target: id.clone(), - request: r, - }; - self.pending_messages.push_back(event); - } else { - send_request( - &mut self.behaviour, - &mut self.context_data.stats, - &mut self.context_data.peers, - &id, - GenericMessage::BlockRequest(r), - ) - } + update_peer_request(&mut self.context_data.peers, &id, &mut r); + let event = CustomMessageOutcome::BlockRequest { + target: id.clone(), + request: r, + }; + self.pending_messages.push_back(event); } for (id, mut r) in self.sync.justification_requests() { - if self.use_new_block_requests_protocol { - update_peer_request(&mut self.context_data.peers, &id, &mut r); - let event = CustomMessageOutcome::BlockRequest { - target: id, - request: r, - }; - self.pending_messages.push_back(event); - } else { - send_request( - &mut self.behaviour, - &mut self.context_data.stats, - &mut self.context_data.peers, - &id, - GenericMessage::BlockRequest(r), - ) - } + update_peer_request(&mut self.context_data.peers, &id, &mut r); + let event = CustomMessageOutcome::BlockRequest { + target: id, + request: r, + }; + self.pending_messages.push_back(event); } for (id, r) in self.sync.finality_proof_requests() { - if self.use_new_block_requests_protocol { - let event = CustomMessageOutcome::FinalityProofRequest { - target: id, - block_hash: r.block, - request: r.request, - }; - self.pending_messages.push_back(event); - } else { - send_request( - &mut self.behaviour, - &mut self.context_data.stats, - &mut self.context_data.peers, - &id, - GenericMessage::FinalityProofRequest(r), - ) - } + let event = CustomMessageOutcome::FinalityProofRequest { + target: id, + block_hash: r.block, + request: r.request, + }; + self.pending_messages.push_back(event); } if let Poll::Ready(Some((peer_id, result))) = self.pending_transactions.poll_next_unpin(cx) { - self.on_handle_extrinsic_import(peer_id, result); + self.on_handle_transaction_import(peer_id, result); } if let Some(message) = self.pending_messages.pop_front() { return Poll::Ready(NetworkBehaviourAction::GenerateEvent(message)); @@ -2130,7 +2051,7 @@ impl NetworkBehaviour for Protocol { } Some(Fallback::Transactions) => { if let Ok(m) = message::Transactions::decode(&mut message.as_ref()) { - self.on_extrinsics(peer_id, m); + self.on_transactions(peer_id, m); } else { warn!(target: "sub-libp2p", "Failed to decode transactions list"); } @@ -2242,7 +2163,6 @@ mod tests { Box::new(DefaultBlockAnnounceValidator::new(client.clone())), None, Default::default(), - true, None, ).unwrap(); diff --git a/client/network/src/protocol/generic_proto/behaviour.rs b/client/network/src/protocol/generic_proto/behaviour.rs index cf6188726daae..be2451c3f4a03 100644 --- a/client/network/src/protocol/generic_proto/behaviour.rs +++ b/client/network/src/protocol/generic_proto/behaviour.rs @@ -1086,7 +1086,9 @@ impl NetworkBehaviour for GenericProto { // In the incoming state, we don't report "Dropped". Instead we will just ignore the // corresponding Accept/Reject. Some(PeerState::Incoming { }) => { - if let Some(state) = self.incoming.iter_mut().find(|i| i.peer_id == *peer_id) { + if let Some(state) = self.incoming.iter_mut() + .find(|i| i.alive && i.peer_id == *peer_id) + { debug!(target: "sub-libp2p", "Libp2p => Disconnected({}): Was in incoming mode with id {:?}.", peer_id, state.incoming_id); diff --git a/client/network/src/protocol/message.rs b/client/network/src/protocol/message.rs index bb2253b733864..a7fbb92387cf6 100644 --- a/client/network/src/protocol/message.rs +++ b/client/network/src/protocol/message.rs @@ -87,6 +87,20 @@ bitflags! { } } +impl BlockAttributes { + /// Encodes attributes as big endian u32, compatible with SCALE-encoding (i.e the + /// significant byte has zero index). + pub fn to_be_u32(&self) -> u32 { + u32::from_be_bytes([self.bits(), 0, 0, 0]) + } + + /// Decodes attributes, encoded with the `encode_to_be_u32()` call. + pub fn from_be_u32(encoded: u32) -> Result { + BlockAttributes::from_bits(encoded.to_be_bytes()[0]) + .ok_or_else(|| Error::from("Invalid BlockAttributes")) + } +} + impl Encode for BlockAttributes { fn encode_to(&self, dest: &mut T) { dest.push_byte(self.bits()) diff --git a/client/network/src/protocol/sync.rs b/client/network/src/protocol/sync.rs index 781d410fff993..c3e87ca19a341 100644 --- a/client/network/src/protocol/sync.rs +++ b/client/network/src/protocol/sync.rs @@ -651,7 +651,6 @@ impl ChainSync { let blocks = &mut self.blocks; let attrs = &self.required_block_attributes; let fork_targets = &mut self.fork_targets; - let mut have_requests = false; let last_finalized = self.client.info().finalized_number; let best_queued = self.best_queued_number; let client = &self.client; @@ -681,7 +680,6 @@ impl ChainSync { peer.common_number, req, ); - have_requests = true; Some((id, req)) } else if let Some((hash, req)) = fork_sync_request( id, @@ -697,7 +695,6 @@ impl ChainSync { ) { trace!(target: "sync", "Downloading fork {:?} from {}", hash, id); peer.state = PeerSyncState::DownloadingStale(hash); - have_requests = true; Some((id, req)) } else { None diff --git a/client/network/src/service.rs b/client/network/src/service.rs index fd58aa631d6b2..2ef6b7bc21457 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -107,6 +107,24 @@ impl NetworkWorker { /// for the network processing to advance. From it, you can extract a `NetworkService` using /// `worker.service()`. The `NetworkService` can be shared through the codebase. pub fn new(params: Params) -> Result, Error> { + // Ensure the listen addresses are consistent with the transport. + ensure_addresses_consistent_with_transport( + params.network_config.listen_addresses.iter(), + ¶ms.network_config.transport, + )?; + ensure_addresses_consistent_with_transport( + params.network_config.boot_nodes.iter().map(|x| &x.multiaddr), + ¶ms.network_config.transport, + )?; + ensure_addresses_consistent_with_transport( + params.network_config.reserved_nodes.iter().map(|x| &x.multiaddr), + ¶ms.network_config.transport, + )?; + ensure_addresses_consistent_with_transport( + params.network_config.public_addresses.iter(), + ¶ms.network_config.transport, + )?; + let (to_worker, from_worker) = tracing_unbounded("mpsc_network_worker"); if let Some(path) = params.network_config.net_config_path { @@ -224,7 +242,6 @@ impl NetworkWorker { params.block_announce_validator, params.metrics_registry.as_ref(), boot_node_ids.clone(), - params.network_config.use_new_block_requests_protocol, metrics.as_ref().map(|m| m.notifications_queues_size.clone()), )?; @@ -298,8 +315,8 @@ impl NetworkWorker { }; let mut builder = SwarmBuilder::new(transport, behaviour, local_peer_id.clone()) .peer_connection_limit(crate::MAX_CONNECTIONS_PER_PEER) - .notify_handler_buffer_size(NonZeroUsize::new(16).expect("16 != 0; qed")) - .connection_event_buffer_size(128); + .notify_handler_buffer_size(NonZeroUsize::new(32).expect("32 != 0; qed")) + .connection_event_buffer_size(1024); if let Some(spawner) = params.executor { struct SpawnImpl(F); impl + Send>>)> Executor for SpawnImpl { @@ -588,15 +605,15 @@ impl NetworkService { /// All transactions will be fetched from the `TransactionPool` that was passed at /// initialization as part of the configuration and propagated to peers. pub fn trigger_repropagate(&self) { - let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::PropagateExtrinsics); + let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::PropagateTransactions); } /// You must call when new transaction is imported by the transaction pool. /// /// This transaction will be fetched from the `TransactionPool` that was passed at /// initialization as part of the configuration and propagated to peers. - pub fn propagate_extrinsic(&self, hash: H) { - let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::PropagateExtrinsic(hash)); + pub fn propagate_transaction(&self, hash: H) { + let _ = self.to_worker.unbounded_send(ServiceToWorkerMsg::PropagateTransaction(hash)); } /// Make sure an important block is propagated to peers. @@ -672,8 +689,15 @@ impl NetworkService { /// Adds a `PeerId` and its address as reserved. The string should encode the address /// and peer ID of the remote node. + /// + /// Returns an `Err` if the given string is not a valid multiaddress + /// or contains an invalid peer ID (which includes the local peer ID). pub fn add_reserved_peer(&self, peer: String) -> Result<(), String> { let (peer_id, addr) = parse_str_addr(&peer).map_err(|e| format!("{:?}", e))?; + // Make sure the local peer ID is never added to the PSM. + if peer_id == self.local_peer_id { + return Err("Local peer ID cannot be added as a reserved peer.".to_string()) + } self.peerset.add_reserved_peer(peer_id.clone()); let _ = self .to_worker @@ -694,12 +718,26 @@ impl NetworkService { } /// Modify a peerset priority group. + /// + /// Returns an `Err` if one of the given addresses contains an invalid + /// peer ID (which includes the local peer ID). pub fn set_priority_group(&self, group_id: String, peers: HashSet) -> Result<(), String> { - let peers = peers.into_iter().map(|p| { - parse_addr(p).map_err(|e| format!("{:?}", e)) - }).collect::, String>>()?; + let peers = peers.into_iter() + .map(|p| match parse_addr(p) { + Err(e) => Err(format!("{:?}", e)), + Ok((peer, addr)) => + // Make sure the local peer ID is never added to the PSM + // or added as a "known address", even if given. + if peer == self.local_peer_id { + Err("Local peer ID in priority group.".to_string()) + } else { + Ok((peer, addr)) + } + }) + .collect::, String>>()?; let peer_ids = peers.iter().map(|(peer_id, _addr)| peer_id.clone()).collect(); + self.peerset.set_priority_group(group_id, peer_ids); for (peer_id, addr) in peers.into_iter() { @@ -726,6 +764,12 @@ impl NetworkService { .unbounded_send(ServiceToWorkerMsg::UpdateChain); } + /// Inform the network service about an own imported block. + pub fn own_block_imported(&self, hash: B::Hash, number: NumberFor) { + let _ = self + .to_worker + .unbounded_send(ServiceToWorkerMsg::OwnBlockImported(hash, number)); + } } impl sp_consensus::SyncOracle @@ -772,8 +816,8 @@ impl NetworkStateInfo for NetworkService /// /// Each entry corresponds to a method of `NetworkService`. enum ServiceToWorkerMsg { - PropagateExtrinsic(H), - PropagateExtrinsics, + PropagateTransaction(H), + PropagateTransactions, RequestJustification(B::Hash, NumberFor), AnnounceBlock(B::Hash, Vec), GetValue(record::Key), @@ -792,6 +836,7 @@ enum ServiceToWorkerMsg { }, DisconnectPeer(PeerId), UpdateChain, + OwnBlockImported(B::Hash, NumberFor), } /// Main network worker. Must be polled in order for the network to advance. @@ -827,6 +872,8 @@ struct Metrics { // This list is ordered alphabetically connections_closed_total: CounterVec, connections_opened_total: CounterVec, + distinct_peers_connections_closed_total: Counter, + distinct_peers_connections_opened_total: Counter, import_queue_blocks_submitted: Counter, import_queue_finality_proofs_submitted: Counter, import_queue_justifications_submitted: Counter, @@ -862,17 +909,25 @@ impl Metrics { connections_closed_total: register(CounterVec::new( Opts::new( "sub_libp2p_connections_closed_total", - "Total number of connections closed, by reason and direction" + "Total number of connections closed, by direction and reason" ), &["direction", "reason"] )?, registry)?, connections_opened_total: register(CounterVec::new( Opts::new( "sub_libp2p_connections_opened_total", - "Total number of connections opened" + "Total number of connections opened by direction" ), &["direction"] )?, registry)?, + distinct_peers_connections_closed_total: register(Counter::new( + "sub_libp2p_distinct_peers_connections_closed_total", + "Total number of connections closed with distinct peers" + )?, registry)?, + distinct_peers_connections_opened_total: register(Counter::new( + "sub_libp2p_distinct_peers_connections_opened_total", + "Total number of connections opened with distinct peers" + )?, registry)?, import_queue_blocks_submitted: register(Counter::new( "import_queue_blocks_submitted", "Number of blocks submitted to the import queue.", @@ -1092,10 +1147,10 @@ impl Future for NetworkWorker { this.network_service.user_protocol_mut().announce_block(hash, data), ServiceToWorkerMsg::RequestJustification(hash, number) => this.network_service.user_protocol_mut().request_justification(&hash, number), - ServiceToWorkerMsg::PropagateExtrinsic(hash) => - this.network_service.user_protocol_mut().propagate_extrinsic(&hash), - ServiceToWorkerMsg::PropagateExtrinsics => - this.network_service.user_protocol_mut().propagate_extrinsics(), + ServiceToWorkerMsg::PropagateTransaction(hash) => + this.network_service.user_protocol_mut().propagate_transaction(&hash), + ServiceToWorkerMsg::PropagateTransactions => + this.network_service.user_protocol_mut().propagate_transactions(), ServiceToWorkerMsg::GetValue(key) => this.network_service.get_value(&key), ServiceToWorkerMsg::PutValue(key, value) => @@ -1122,6 +1177,8 @@ impl Future for NetworkWorker { this.network_service.user_protocol_mut().disconnect_peer(&who), ServiceToWorkerMsg::UpdateChain => this.network_service.user_protocol_mut().update_chain(), + ServiceToWorkerMsg::OwnBlockImported(hash, number) => + this.network_service.user_protocol_mut().own_block_imported(hash, number), } } @@ -1185,40 +1242,44 @@ impl Future for NetworkWorker { } this.event_streams.send(ev); }, - Poll::Ready(SwarmEvent::ConnectionEstablished { peer_id, endpoint, .. }) => { + Poll::Ready(SwarmEvent::ConnectionEstablished { peer_id, endpoint, num_established }) => { trace!(target: "sub-libp2p", "Libp2p => Connected({:?})", peer_id); + if let Some(metrics) = this.metrics.as_ref() { - match endpoint { - ConnectedPoint::Dialer { .. } => - metrics.connections_opened_total.with_label_values(&["out"]).inc(), - ConnectedPoint::Listener { .. } => - metrics.connections_opened_total.with_label_values(&["in"]).inc(), + let direction = match endpoint { + ConnectedPoint::Dialer { .. } => "out", + ConnectedPoint::Listener { .. } => "in", + }; + metrics.connections_opened_total.with_label_values(&[direction]).inc(); + + if num_established.get() == 1 { + metrics.distinct_peers_connections_opened_total.inc(); } } }, - Poll::Ready(SwarmEvent::ConnectionClosed { peer_id, cause, endpoint, .. }) => { + Poll::Ready(SwarmEvent::ConnectionClosed { peer_id, cause, endpoint, num_established }) => { trace!(target: "sub-libp2p", "Libp2p => Disconnected({:?}, {:?})", peer_id, cause); if let Some(metrics) = this.metrics.as_ref() { - let dir = match endpoint { + let direction = match endpoint { ConnectedPoint::Dialer { .. } => "out", ConnectedPoint::Listener { .. } => "in", }; - - match cause { - ConnectionError::IO(_) => - metrics.connections_closed_total.with_label_values(&[dir, "transport-error"]).inc(), + let reason = match cause { + ConnectionError::IO(_) => "transport-error", ConnectionError::Handler(NodeHandlerWrapperError::Handler(EitherError::A(EitherError::A( EitherError::A(EitherError::A(EitherError::B( - EitherError::A(PingFailure::Timeout)))))))) => - metrics.connections_closed_total.with_label_values(&[dir, "ping-timeout"]).inc(), + EitherError::A(PingFailure::Timeout)))))))) => "ping-timeout", ConnectionError::Handler(NodeHandlerWrapperError::Handler(EitherError::A(EitherError::A( EitherError::A(EitherError::A(EitherError::A( - EitherError::B(LegacyConnectionKillError)))))))) => - metrics.connections_closed_total.with_label_values(&[dir, "force-closed"]).inc(), - ConnectionError::Handler(NodeHandlerWrapperError::Handler(_)) => - metrics.connections_closed_total.with_label_values(&[dir, "protocol-error"]).inc(), - ConnectionError::Handler(NodeHandlerWrapperError::KeepAliveTimeout) => - metrics.connections_closed_total.with_label_values(&[dir, "keep-alive-timeout"]).inc(), + EitherError::B(LegacyConnectionKillError)))))))) => "force-closed", + ConnectionError::Handler(NodeHandlerWrapperError::Handler(_)) => "protocol-error", + ConnectionError::Handler(NodeHandlerWrapperError::KeepAliveTimeout) => "keep-alive-timeout", + }; + metrics.connections_closed_total.with_label_values(&[direction, reason]).inc(); + + // `num_established` represents the number of *remaining* connections. + if num_established == 0 { + metrics.distinct_peers_connections_closed_total.inc(); } } }, @@ -1426,3 +1487,40 @@ impl<'a, B: BlockT, H: ExHashT> Link for NetworkLink<'a, B, H> { } } } + +fn ensure_addresses_consistent_with_transport<'a>( + addresses: impl Iterator, + transport: &TransportConfig, +) -> Result<(), Error> { + if matches!(transport, TransportConfig::MemoryOnly) { + let addresses: Vec<_> = addresses + .filter(|x| x.iter() + .any(|y| !matches!(y, libp2p::core::multiaddr::Protocol::Memory(_))) + ) + .cloned() + .collect(); + + if !addresses.is_empty() { + return Err(Error::AddressesForAnotherTransport { + transport: transport.clone(), + addresses, + }); + } + } else { + let addresses: Vec<_> = addresses + .filter(|x| x.iter() + .any(|y| matches!(y, libp2p::core::multiaddr::Protocol::Memory(_))) + ) + .cloned() + .collect(); + + if !addresses.is_empty() { + return Err(Error::AddressesForAnotherTransport { + transport: transport.clone(), + addresses, + }); + } + } + + Ok(()) +} diff --git a/client/network/src/service/out_events.rs b/client/network/src/service/out_events.rs index 4a631601a669e..1b86a5fa4317d 100644 --- a/client/network/src/service/out_events.rs +++ b/client/network/src/service/out_events.rs @@ -35,7 +35,7 @@ use crate::Event; use super::maybe_utf8_bytes_to_string; -use futures::{prelude::*, channel::mpsc, ready}; +use futures::{prelude::*, channel::mpsc, ready, stream::FusedStream}; use parking_lot::Mutex; use prometheus_endpoint::{register, CounterVec, GaugeVec, Opts, PrometheusError, Registry, U64}; use std::{ @@ -119,8 +119,10 @@ impl fmt::Debug for Receiver { impl Drop for Receiver { fn drop(&mut self) { - // Empty the list to properly decrease the metrics. - while let Some(Some(_)) = self.next().now_or_never() {} + if !self.inner.is_terminated() { + // Empty the list to properly decrease the metrics. + while let Some(Some(_)) = self.next().now_or_never() {} + } } } diff --git a/client/network/src/service/tests.rs b/client/network/src/service/tests.rs index 8eaa98449213c..c027c3be7378c 100644 --- a/client/network/src/service/tests.rs +++ b/client/network/src/service/tests.rs @@ -18,6 +18,7 @@ use crate::{config, Event, NetworkService, NetworkWorker}; +use libp2p::PeerId; use futures::prelude::*; use sp_runtime::traits::{Block as BlockT, Header as _}; use std::{sync::Arc, time::Duration}; @@ -138,6 +139,7 @@ fn build_nodes_one_proto() let (node2, events_stream2) = build_test_full_node(config::NetworkConfiguration { notifications_protocols: vec![(ENGINE_ID, From::from(&b"/foo"[..]))], + listen_addresses: vec![], reserved_nodes: vec![config::MultiaddrWithPeerId { multiaddr: listen_addr, peer_id: node1.local_peer_id().clone(), @@ -272,3 +274,189 @@ fn notifications_state_consistent() { } }); } + +#[test] +fn lots_of_incoming_peers_works() { + let listen_addr = config::build_multiaddr![Memory(rand::random::())]; + + let (main_node, _) = build_test_full_node(config::NetworkConfiguration { + notifications_protocols: vec![(ENGINE_ID, From::from(&b"/foo"[..]))], + listen_addresses: vec![listen_addr.clone()], + in_peers: u32::max_value(), + transport: config::TransportConfig::MemoryOnly, + .. config::NetworkConfiguration::new_local() + }); + + let main_node_peer_id = main_node.local_peer_id().clone(); + + // We spawn background tasks and push them in this `Vec`. They will all be waited upon before + // this test ends. + let mut background_tasks_to_wait = Vec::new(); + + for _ in 0..32 { + let main_node_peer_id = main_node_peer_id.clone(); + + let (_dialing_node, event_stream) = build_test_full_node(config::NetworkConfiguration { + notifications_protocols: vec![(ENGINE_ID, From::from(&b"/foo"[..]))], + listen_addresses: vec![], + reserved_nodes: vec![config::MultiaddrWithPeerId { + multiaddr: listen_addr.clone(), + peer_id: main_node_peer_id.clone(), + }], + transport: config::TransportConfig::MemoryOnly, + .. config::NetworkConfiguration::new_local() + }); + + background_tasks_to_wait.push(async_std::task::spawn(async move { + // Create a dummy timer that will "never" fire, and that will be overwritten when we + // actually need the timer. Using an Option would be technically cleaner, but it would + // make the code below way more complicated. + let mut timer = futures_timer::Delay::new(Duration::from_secs(3600 * 24 * 7)).fuse(); + + let mut event_stream = event_stream.fuse(); + loop { + futures::select! { + _ = timer => { + // Test succeeds when timer fires. + return; + } + ev = event_stream.next() => { + match ev.unwrap() { + Event::NotificationStreamOpened { remote, .. } => { + assert_eq!(remote, main_node_peer_id); + // Test succeeds after 5 seconds. This timer is here in order to + // detect a potential problem after opening. + timer = futures_timer::Delay::new(Duration::from_secs(5)).fuse(); + } + Event::NotificationStreamClosed { .. } => { + // Test failed. + panic!(); + } + _ => {} + } + } + } + } + })); + } + + futures::executor::block_on(async move { + future::join_all(background_tasks_to_wait).await + }); +} + +#[test] +#[should_panic(expected = "don't match the transport")] +fn ensure_listen_addresses_consistent_with_transport_memory() { + let listen_addr = config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)]; + + let _ = build_test_full_node(config::NetworkConfiguration { + listen_addresses: vec![listen_addr.clone()], + transport: config::TransportConfig::MemoryOnly, + .. config::NetworkConfiguration::new("test-node", "test-client", Default::default(), None) + }); +} + +#[test] +#[should_panic(expected = "don't match the transport")] +fn ensure_listen_addresses_consistent_with_transport_not_memory() { + let listen_addr = config::build_multiaddr![Memory(rand::random::())]; + + let _ = build_test_full_node(config::NetworkConfiguration { + listen_addresses: vec![listen_addr.clone()], + .. config::NetworkConfiguration::new("test-node", "test-client", Default::default(), None) + }); +} + +#[test] +#[should_panic(expected = "don't match the transport")] +fn ensure_boot_node_addresses_consistent_with_transport_memory() { + let listen_addr = config::build_multiaddr![Memory(rand::random::())]; + let boot_node = config::MultiaddrWithPeerId { + multiaddr: config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)], + peer_id: PeerId::random(), + }; + + let _ = build_test_full_node(config::NetworkConfiguration { + listen_addresses: vec![listen_addr.clone()], + transport: config::TransportConfig::MemoryOnly, + boot_nodes: vec![boot_node], + .. config::NetworkConfiguration::new("test-node", "test-client", Default::default(), None) + }); +} + +#[test] +#[should_panic(expected = "don't match the transport")] +fn ensure_boot_node_addresses_consistent_with_transport_not_memory() { + let listen_addr = config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)]; + let boot_node = config::MultiaddrWithPeerId { + multiaddr: config::build_multiaddr![Memory(rand::random::())], + peer_id: PeerId::random(), + }; + + let _ = build_test_full_node(config::NetworkConfiguration { + listen_addresses: vec![listen_addr.clone()], + boot_nodes: vec![boot_node], + .. config::NetworkConfiguration::new("test-node", "test-client", Default::default(), None) + }); +} + +#[test] +#[should_panic(expected = "don't match the transport")] +fn ensure_reserved_node_addresses_consistent_with_transport_memory() { + let listen_addr = config::build_multiaddr![Memory(rand::random::())]; + let reserved_node = config::MultiaddrWithPeerId { + multiaddr: config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)], + peer_id: PeerId::random(), + }; + + let _ = build_test_full_node(config::NetworkConfiguration { + listen_addresses: vec![listen_addr.clone()], + transport: config::TransportConfig::MemoryOnly, + reserved_nodes: vec![reserved_node], + .. config::NetworkConfiguration::new("test-node", "test-client", Default::default(), None) + }); +} + +#[test] +#[should_panic(expected = "don't match the transport")] +fn ensure_reserved_node_addresses_consistent_with_transport_not_memory() { + let listen_addr = config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)]; + let reserved_node = config::MultiaddrWithPeerId { + multiaddr: config::build_multiaddr![Memory(rand::random::())], + peer_id: PeerId::random(), + }; + + let _ = build_test_full_node(config::NetworkConfiguration { + listen_addresses: vec![listen_addr.clone()], + reserved_nodes: vec![reserved_node], + .. config::NetworkConfiguration::new("test-node", "test-client", Default::default(), None) + }); +} + +#[test] +#[should_panic(expected = "don't match the transport")] +fn ensure_public_addresses_consistent_with_transport_memory() { + let listen_addr = config::build_multiaddr![Memory(rand::random::())]; + let public_address = config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)]; + + let _ = build_test_full_node(config::NetworkConfiguration { + listen_addresses: vec![listen_addr.clone()], + transport: config::TransportConfig::MemoryOnly, + public_addresses: vec![public_address], + .. config::NetworkConfiguration::new("test-node", "test-client", Default::default(), None) + }); +} + +#[test] +#[should_panic(expected = "don't match the transport")] +fn ensure_public_addresses_consistent_with_transport_not_memory() { + let listen_addr = config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)]; + let public_address = config::build_multiaddr![Memory(rand::random::())]; + + let _ = build_test_full_node(config::NetworkConfiguration { + listen_addresses: vec![listen_addr.clone()], + public_addresses: vec![public_address], + .. config::NetworkConfiguration::new("test-node", "test-client", Default::default(), None) + }); +} diff --git a/client/offchain/Cargo.toml b/client/offchain/Cargo.toml index 7f1d23f5582f8..819d6ac3a52c1 100644 --- a/client/offchain/Cargo.toml +++ b/client/offchain/Cargo.toml @@ -22,7 +22,7 @@ log = "0.4.8" threadpool = "1.7" num_cpus = "1.10" sp-offchain = { version = "2.0.0-rc3", path = "../../primitives/offchain" } -codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } parking_lot = "0.10.0" sp-core = { version = "2.0.0-rc3", path = "../../primitives/core" } rand = "0.7.2" @@ -37,12 +37,12 @@ hyper-rustls = "0.20" [dev-dependencies] env_logger = "0.7.0" -fdlimit = "0.1.4" sc-client-db = { version = "0.8.0-rc3", default-features = true, path = "../db/" } sc-transaction-pool = { version = "2.0.0-rc3", path = "../../client/transaction-pool" } sp-transaction-pool = { version = "2.0.0-rc3", path = "../../primitives/transaction-pool" } substrate-test-runtime-client = { version = "2.0.0-rc3", path = "../../test-utils/runtime/client" } tokio = "0.2" +lazy_static = "1.4.0" [features] default = [] diff --git a/client/offchain/src/api.rs b/client/offchain/src/api.rs index a7f4ecbc5825e..0aa5d4ad788ab 100644 --- a/client/offchain/src/api.rs +++ b/client/offchain/src/api.rs @@ -31,6 +31,7 @@ use sp_core::offchain::{ OpaqueNetworkState, OpaquePeerId, OpaqueMultiaddr, StorageKind, }; pub use sp_offchain::STORAGE_PREFIX; +pub use http::SharedClient; #[cfg(not(target_os = "unknown"))] mod http; @@ -260,8 +261,9 @@ impl AsyncApi { db: S, network_state: Arc, is_validator: bool, - ) -> (Api, AsyncApi) { - let (http_api, http_worker) = http::http(); + shared_client: SharedClient, + ) -> (Api, Self) { + let (http_api, http_worker) = http::http(shared_client); let api = Api { db, @@ -270,7 +272,7 @@ impl AsyncApi { http: http_api, }; - let async_api = AsyncApi { + let async_api = Self { http: Some(http_worker), }; @@ -308,11 +310,14 @@ mod tests { let _ = env_logger::try_init(); let db = LocalStorage::new_test(); let mock = Arc::new(MockNetworkStateInfo()); + let shared_client = SharedClient::new(); + AsyncApi::new( db, mock, false, + shared_client, ) } diff --git a/client/offchain/src/api/http.rs b/client/offchain/src/api/http.rs index 91a673872fc7c..1f542b7c11e19 100644 --- a/client/offchain/src/api/http.rs +++ b/client/offchain/src/api/http.rs @@ -33,9 +33,22 @@ use log::error; use sp_core::offchain::{HttpRequestId, Timestamp, HttpRequestStatus, HttpError}; use std::{convert::TryFrom, fmt, io::Read as _, pin::Pin, task::{Context, Poll}}; use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender, TracingUnboundedReceiver}; +use std::sync::Arc; +use hyper::{Client as HyperClient, Body, client}; +use hyper_rustls::HttpsConnector; + +/// Wrapper struct used for keeping the hyper_rustls client running. +#[derive(Clone)] +pub struct SharedClient(Arc, Body>>); + +impl SharedClient { + pub fn new() -> Self { + Self(Arc::new(HyperClient::builder().build(HttpsConnector::new()))) + } +} /// Creates a pair of [`HttpApi`] and [`HttpWorker`]. -pub fn http() -> (HttpApi, HttpWorker) { +pub fn http(shared_client: SharedClient) -> (HttpApi, HttpWorker) { let (to_worker, from_api) = tracing_unbounded("mpsc_ocw_to_worker"); let (to_api, from_worker) = tracing_unbounded("mpsc_ocw_to_api"); @@ -51,7 +64,7 @@ pub fn http() -> (HttpApi, HttpWorker) { let engine = HttpWorker { to_api, from_api, - http_client: hyper::Client::builder().build(hyper_rustls::HttpsConnector::new()), + http_client: shared_client.0, requests: Vec::new(), }; @@ -551,7 +564,7 @@ pub struct HttpWorker { /// Used to receive messages from the `HttpApi`. from_api: TracingUnboundedReceiver, /// The engine that runs HTTP requests. - http_client: hyper::Client, hyper::Body>, + http_client: Arc, Body>>, /// HTTP requests that are being worked on by the engine. requests: Vec<(HttpRequestId, HttpWorkerRequest)>, } @@ -685,21 +698,23 @@ impl fmt::Debug for HttpWorkerRequest { mod tests { use core::convert::Infallible; use crate::api::timestamp; - use super::http; + use super::{http, SharedClient}; use sp_core::offchain::{HttpError, HttpRequestId, HttpRequestStatus, Duration}; use futures::future; + use lazy_static::lazy_static; + + // Using lazy_static to avoid spawning lots of different SharedClients, + // as spawning a SharedClient is CPU-intensive and opens lots of fds. + lazy_static! { + static ref SHARED_CLIENT: SharedClient = SharedClient::new(); + } // Returns an `HttpApi` whose worker is ran in the background, and a `SocketAddr` to an HTTP // server that runs in the background as well. macro_rules! build_api_server { () => {{ - // We spawn quite a bit of HTTP servers here due to how async API - // works for offchain workers, so be sure to raise the FD limit - // (particularly useful for macOS where the default soft limit may - // not be enough). - fdlimit::raise_fd_limit(); - - let (api, worker) = http(); + let hyper_client = SHARED_CLIENT.clone(); + let (api, worker) = http(hyper_client.clone()); let (addr_tx, addr_rx) = std::sync::mpsc::channel(); std::thread::spawn(move || { diff --git a/client/offchain/src/api/http_dummy.rs b/client/offchain/src/api/http_dummy.rs index 5ff77a1068312..1c83325c93b20 100644 --- a/client/offchain/src/api/http_dummy.rs +++ b/client/offchain/src/api/http_dummy.rs @@ -19,8 +19,18 @@ use sp_core::offchain::{HttpRequestId, Timestamp, HttpRequestStatus, HttpError}; use std::{future::Future, pin::Pin, task::Context, task::Poll}; +/// Wrapper struct (wrapping nothing in case of http_dummy) used for keeping the hyper_rustls client running. +#[derive(Clone)] +pub struct SharedClient; + +impl SharedClient { + pub fn new() -> Self { + Self + } +} + /// Creates a pair of [`HttpApi`] and [`HttpWorker`]. -pub fn http() -> (HttpApi, HttpWorker) { +pub fn http(_: SharedClient) -> (HttpApi, HttpWorker) { (HttpApi, HttpWorker) } diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index d6e62501b6bd8..7c90065746aa3 100644 --- a/client/offchain/src/lib.rs +++ b/client/offchain/src/lib.rs @@ -19,7 +19,7 @@ //! The offchain workers is a special function of the runtime that //! gets executed after block is imported. During execution //! it's able to asynchronously submit extrinsics that will either -//! be propagated to other nodes added to the next block +//! be propagated to other nodes or added to the next block //! produced by the node as unsigned transactions. //! //! Offchain workers can be used for computation-heavy tasks @@ -41,10 +41,12 @@ use sp_api::{ApiExt, ProvideRuntimeApi}; use futures::future::Future; use log::{debug, warn}; use sc_network::NetworkStateInfo; -use sp_core::{offchain::{self, OffchainStorage}, ExecutionContext}; +use sp_core::{offchain::{self, OffchainStorage}, ExecutionContext, traits::SpawnNamed}; use sp_runtime::{generic::BlockId, traits::{self, Header}}; +use futures::{prelude::*, future::ready}; mod api; +use api::SharedClient; pub use sp_offchain::{OffchainWorkerApi, STORAGE_PREFIX}; @@ -54,16 +56,19 @@ pub struct OffchainWorkers { db: Storage, _block: PhantomData, thread_pool: Mutex, + shared_client: SharedClient, } impl OffchainWorkers { /// Creates new `OffchainWorkers`. pub fn new(client: Arc, db: Storage) -> Self { + let shared_client = SharedClient::new(); Self { client, db, _block: PhantomData, thread_pool: Mutex::new(ThreadPool::new(num_cpus::get())), + shared_client, } } } @@ -119,6 +124,7 @@ impl OffchainWorkers< self.db.clone(), network_state.clone(), is_validator, + self.shared_client.clone(), ); debug!("Spawning offchain workers at {:?}", at); let header = header.clone(); @@ -161,6 +167,43 @@ impl OffchainWorkers< } } +/// Inform the offchain worker about new imported blocks +pub async fn notification_future( + is_validator: bool, + client: Arc, + offchain: Arc>, + spawner: Spawner, + network_state_info: Arc, +) + where + Block: traits::Block, + Client: ProvideRuntimeApi + sc_client_api::BlockchainEvents + Send + Sync + 'static, + Client::Api: OffchainWorkerApi, + Storage: OffchainStorage + 'static, + Spawner: SpawnNamed +{ + client.import_notification_stream().for_each(move |n| { + if n.is_new_best { + spawner.spawn( + "offchain-on-block", + offchain.on_block_imported( + &n.header, + network_state_info.clone(), + is_validator, + ).boxed(), + ); + } else { + log::debug!( + target: "sc_offchain", + "Skipping offchain workers for non-canon block: {:?}", + n.header, + ) + } + + ready(()) + }).await; +} + #[cfg(test)] mod tests { use super::*; diff --git a/client/rpc-api/Cargo.toml b/client/rpc-api/Cargo.toml index c7aad9a1b3ff6..3e3195b9146d8 100644 --- a/client/rpc-api/Cargo.toml +++ b/client/rpc-api/Cargo.toml @@ -12,7 +12,7 @@ description = "Substrate RPC interfaces." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } derive_more = "0.99.2" futures = { version = "0.3.1", features = ["compat"] } jsonrpc-core = "14.2.0" diff --git a/client/rpc-api/src/author/error.rs b/client/rpc-api/src/author/error.rs index e6ee36cdce19a..69c036be95fe0 100644 --- a/client/rpc-api/src/author/error.rs +++ b/client/rpc-api/src/author/error.rs @@ -20,6 +20,7 @@ use crate::errors; use jsonrpc_core as rpc; +use sp_runtime::transaction_validity::InvalidTransaction; /// Author RPC Result type. pub type Result = std::result::Result; @@ -114,10 +115,18 @@ impl From for rpc::Error { message: format!("Verification Error: {}", e).into(), data: Some(format!("{:?}", e).into()), }, - Error::Pool(PoolError::InvalidTransaction(e)) => rpc::Error { + Error::Pool(PoolError::InvalidTransaction(InvalidTransaction::Custom(e))) => rpc::Error { code: rpc::ErrorCode::ServerError(POOL_INVALID_TX), message: "Invalid Transaction".into(), - data: serde_json::to_value(e).ok(), + data: Some(format!("Custom error: {}", e).into()), + }, + Error::Pool(PoolError::InvalidTransaction(e)) => { + let msg: &str = e.into(); + rpc::Error { + code: rpc::ErrorCode::ServerError(POOL_INVALID_TX), + message: "Invalid Transaction".into(), + data: Some(msg.into()), + } }, Error::Pool(PoolError::UnknownTransaction(e)) => rpc::Error { code: rpc::ErrorCode::ServerError(POOL_UNKNOWN_VALIDITY), diff --git a/client/rpc-api/src/chain/mod.rs b/client/rpc-api/src/chain/mod.rs index a7b26f3024248..753ac5617a29d 100644 --- a/client/rpc-api/src/chain/mod.rs +++ b/client/rpc-api/src/chain/mod.rs @@ -49,7 +49,7 @@ pub trait ChainApi { #[rpc(name = "chain_getBlockHash", alias("chain_getHead"))] fn block_hash( &self, - hash: Option>>, + hash: Option>, ) -> Result>>; /// Get hash of the last finalized block in the canon chain. diff --git a/client/rpc-servers/Cargo.toml b/client/rpc-servers/Cargo.toml index 9ea70f1794591..b1ec04f5e4aa4 100644 --- a/client/rpc-servers/Cargo.toml +++ b/client/rpc-servers/Cargo.toml @@ -22,3 +22,4 @@ sp-runtime = { version = "2.0.0-rc3", path = "../../primitives/runtime" } [target.'cfg(not(target_os = "unknown"))'.dependencies] http = { package = "jsonrpc-http-server", version = "14.2.0" } ws = { package = "jsonrpc-ws-server", version = "14.2.0" } +ipc = { version = "14.2.0", package = "jsonrpc-ipc-server" } diff --git a/client/rpc-servers/src/lib.rs b/client/rpc-servers/src/lib.rs index 6fe4586b6ee5e..1e476262acea8 100644 --- a/client/rpc-servers/src/lib.rs +++ b/client/rpc-servers/src/lib.rs @@ -62,6 +62,8 @@ pub fn rpc_handler( mod inner { use super::*; + /// Type alias for ipc server + pub type IpcServer = ipc::Server; /// Type alias for http server pub type HttpServer = http::Server; /// Type alias for ws server @@ -89,6 +91,23 @@ mod inner { .start_http(addr) } + /// Start IPC server listening on given path. + /// + /// **Note**: Only available if `not(target_os = "unknown")`. + pub fn start_ipc( + addr: &str, + io: RpcHandler, + ) -> io::Result { + let builder = ipc::ServerBuilder::new(io); + #[cfg(target_os = "unix")] + builder.set_security_attributes({ + let security_attributes = ipc::SecurityAttributes::empty(); + security_attributes.set_mode(0o600)?; + security_attributes + }); + builder.start(addr) + } + /// Start WS server listening on given address. /// /// **Note**: Only available if `not(target_os = "unknown")`. diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index f3557ca6b2b5f..9cda4451c11f7 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] sc-rpc-api = { version = "0.8.0-rc3", path = "../rpc-api" } sc-client-api = { version = "2.0.0-rc3", path = "../api" } sp-api = { version = "2.0.0-rc3", path = "../../primitives/api" } -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } futures = { version = "0.3.1", features = ["compat"] } jsonrpc-pubsub = "14.2.0" log = "0.4.8" diff --git a/client/rpc/src/chain/mod.rs b/client/rpc/src/chain/mod.rs index 7b13e7a6005ff..8b6bf19d23565 100644 --- a/client/rpc/src/chain/mod.rs +++ b/client/rpc/src/chain/mod.rs @@ -75,17 +75,27 @@ trait ChainBackend: Send + Sync + 'static /// Get hash of the n-th block in the canon chain. /// /// By default returns latest block hash. - fn block_hash( - &self, - number: Option>>, - ) -> Result> { - Ok(match number { - None => Some(self.client().info().best_hash), - Some(num_or_hex) => self.client() - .header(BlockId::number(num_or_hex.to_number()?)) - .map_err(client_err)? - .map(|h| h.hash()), - }) + fn block_hash(&self, number: Option) -> Result> { + match number { + None => Ok(Some(self.client().info().best_hash)), + Some(num_or_hex) => { + use std::convert::TryInto; + + // FIXME <2329>: Database seems to limit the block number to u32 for no reason + let block_num: u32 = num_or_hex.try_into().map_err(|_| { + Error::from(format!( + "`{:?}` > u32::max_value(), the max block number is u32.", + num_or_hex + )) + })?; + let block_num = >::from(block_num); + Ok(self + .client() + .header(BlockId::number(block_num)) + .map_err(client_err)? + .map(|h| h.hash())) + } + } } /// Get hash of the last finalized block in the canon chain. @@ -233,7 +243,7 @@ impl ChainApi, Block::Hash, Block::Header, Signe fn block_hash( &self, - number: Option>>> + number: Option>, ) -> Result>> { match number { None => self.backend.block_hash(None).map(ListOrValue::Value), diff --git a/client/rpc/src/chain/tests.rs b/client/rpc/src/chain/tests.rs index 68d46135e36b1..b36fc4eab1d86 100644 --- a/client/rpc/src/chain/tests.rs +++ b/client/rpc/src/chain/tests.rs @@ -149,7 +149,7 @@ fn should_return_block_hash() { ); assert_matches!( - api.block_hash(Some(vec![0u64.into(), 1.into(), 2.into()].into())), + api.block_hash(Some(vec![0u64.into(), 1u64.into(), 2u64.into()].into())), Ok(ListOrValue::List(list)) if list == &[client.genesis_hash().into(), block.hash().into(), None] ); } diff --git a/client/service/Cargo.toml b/client/service/Cargo.toml index bd830ec8dd7fc..1740e6fad48df 100644 --- a/client/service/Cargo.toml +++ b/client/service/Cargo.toml @@ -55,10 +55,11 @@ sp-application-crypto = { version = "2.0.0-rc3", path = "../../primitives/applic sp-consensus = { version = "0.8.0-rc3", path = "../../primitives/consensus/common" } sc-network = { version = "0.8.0-rc3", path = "../network" } sc-chain-spec = { version = "2.0.0-rc3", path = "../chain-spec" } +sc-light = { version = "2.0.0-rc3", path = "../light" } sc-client-api = { version = "2.0.0-rc3", path = "../api" } sp-api = { version = "2.0.0-rc3", path = "../../primitives/api" } sc-client-db = { version = "0.8.0-rc3", default-features = false, path = "../db" } -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } sc-executor = { version = "0.8.0-rc3", path = "../executor" } sc-transaction-pool = { version = "2.0.0-rc3", path = "../transaction-pool" } sp-transaction-pool = { version = "2.0.0-rc3", path = "../../primitives/transaction-pool" } @@ -66,7 +67,7 @@ sc-rpc-server = { version = "2.0.0-rc3", path = "../rpc-servers" } sc-rpc = { version = "2.0.0-rc3", path = "../rpc" } sc-block-builder = { version = "0.8.0-rc3", path = "../block-builder" } sp-block-builder = { version = "2.0.0-rc3", path = "../../primitives/block-builder" } - +sc-informant = { version = "0.8.0-rc2", path = "../informant" } sc-telemetry = { version = "2.0.0-rc3", path = "../telemetry" } sc-offchain = { version = "2.0.0-rc3", path = "../offchain" } parity-multiaddr = { package = "parity-multiaddr", version = "0.7.3" } @@ -82,6 +83,9 @@ netstat2 = "0.8.1" [target.'cfg(target_os = "linux")'.dependencies] procfs = '0.7.8' +[target.'cfg(not(target_os = "unknown"))'.dependencies] +tempfile = "3.1.0" +directories = "2.0.2" [dev-dependencies] substrate-test-runtime-client = { version = "2.0.0-rc3", path = "../../test-utils/runtime/client" } diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 16500baae1e7b..eebc825b212d9 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -19,15 +19,16 @@ use crate::{ Service, NetworkStatus, NetworkState, error::Error, DEFAULT_PROTOCOL_ID, MallocSizeOfWasm, start_rpc_servers, build_network_future, TransactionPoolAdapter, TaskManager, SpawnTaskHandle, - status_sinks, metrics::MetricsService, client::{Client, ClientConfig}, + status_sinks, metrics::MetricsService, + client::{light, Client, ClientConfig}, config::{Configuration, KeystoreConfig, PrometheusConfig, OffchainWorkerConfig}, }; use sc_client_api::{ - BlockchainEvents, backend::RemoteBackend, light::RemoteBlockchain, - execution_extensions::ExtensionsFactory, ExecutorProvider, CallExecutor, ForkBlocks, BadBlocks, - CloneableSpawn, UsageProvider, + self, light::RemoteBlockchain, execution_extensions::ExtensionsFactory, + ExecutorProvider, CallExecutor, ForkBlocks, BadBlocks, CloneableSpawn, UsageProvider, + backend::RemoteBackend, }; -use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; +use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender, TracingUnboundedReceiver}; use sc_chain_spec::get_extension; use sp_consensus::{ block_validation::{BlockAnnounceValidator, DefaultBlockAnnounceValidator}, @@ -41,7 +42,7 @@ use jsonrpc_pubsub::manager::SubscriptionManager; use sc_keystore::Store as Keystore; use log::{info, warn, error}; use sc_network::config::{Role, FinalityProofProvider, OnDemand, BoxFinalityProofRequestBuilder}; -use sc_network::{NetworkService, NetworkStateInfo}; +use sc_network::NetworkService; use parking_lot::{Mutex, RwLock}; use sp_runtime::generic::BlockId; use sp_runtime::traits::{ @@ -56,7 +57,7 @@ use std::{ }; use wasm_timer::SystemTime; use sc_telemetry::{telemetry, SUBSTRATE_INFO}; -use sp_transaction_pool::MaintainedTransactionPool; +use sp_transaction_pool::{LocalTransactionPool, MaintainedTransactionPool}; use prometheus_endpoint::Registry; use sc_client_db::{Backend, DatabaseSettings}; use sp_core::traits::CodeExecutor; @@ -178,19 +179,19 @@ pub type TLightClient = Client< >; /// Light client backend type. -pub type TLightBackend = crate::client::light::backend::Backend< +pub type TLightBackend = sc_light::Backend< sc_client_db::light::LightStorage, HashFor, >; /// Light call executor type. -pub type TLightCallExecutor = crate::client::light::call_executor::GenesisCallExecutor< - crate::client::light::backend::Backend< +pub type TLightCallExecutor = sc_light::GenesisCallExecutor< + sc_light::Backend< sc_client_db::light::LightStorage, HashFor >, crate::client::LocalCallExecutor< - crate::client::light::backend::Backend< + sc_light::Backend< sc_client_db::light::LightStorage, HashFor >, @@ -413,18 +414,18 @@ impl ServiceBuilder<(), (), (), (), (), (), (), (), (), (), ()> { }; sc_client_db::light::LightStorage::new(db_settings)? }; - let light_blockchain = crate::client::light::new_light_blockchain(db_storage); + let light_blockchain = sc_light::new_light_blockchain(db_storage); let fetch_checker = Arc::new( - crate::client::light::new_fetch_checker::<_, TBl, _>( + sc_light::new_fetch_checker::<_, TBl, _>( light_blockchain.clone(), executor.clone(), Box::new(task_manager.spawn_handle()), ), ); let fetcher = Arc::new(sc_network::config::OnDemand::new(fetch_checker)); - let backend = crate::client::light::new_light_backend(light_blockchain); + let backend = sc_light::new_light_backend(light_blockchain); let remote_blockchain = backend.remote_blockchain(); - let client = Arc::new(crate::client::light::new_light( + let client = Arc::new(light::new_light( backend.clone(), config.chain_spec.as_storage_builder(), executor, @@ -915,8 +916,7 @@ ServiceBuilder< Ok(self) } - /// Builds the service. - pub fn build(self) -> Result Result, TSc, @@ -958,11 +958,8 @@ ServiceBuilder< // A side-channel for essential tasks to communicate shutdown. let (essential_failed_tx, essential_failed_rx) = tracing_unbounded("mpsc_essential_tasks"); - let import_queue = Box::new(import_queue); let chain_info = client.chain_info(); - let chain_spec = &config.chain_spec; - let version = config.impl_version; info!("📦 Highest known block at #{}", chain_info.best_number); telemetry!( SUBSTRATE_INFO; @@ -971,59 +968,26 @@ ServiceBuilder< "best" => ?chain_info.best_hash ); - // make transaction pool available for off-chain runtime calls. - client.execution_extensions() - .register_transaction_pool(Arc::downgrade(&transaction_pool) as _); - - let transaction_pool_adapter = Arc::new(TransactionPoolAdapter { - imports_external_transactions: !matches!(config.role, Role::Light), - pool: transaction_pool.clone(), - client: client.clone(), - }); - - let protocol_id = { - let protocol_id_full = match chain_spec.protocol_id() { - Some(pid) => pid, - None => { - warn!("Using default protocol ID {:?} because none is configured in the \ - chain specs", DEFAULT_PROTOCOL_ID - ); - DEFAULT_PROTOCOL_ID - } - }.as_bytes(); - sc_network::config::ProtocolId::from(protocol_id_full) - }; - - let block_announce_validator = if let Some(f) = block_announce_validator_builder { - f(client.clone()) - } else { - Box::new(DefaultBlockAnnounceValidator::new(client.clone())) - }; + let spawn_handle = task_manager.spawn_handle(); + let (system_rpc_tx, system_rpc_rx) = tracing_unbounded("mpsc_system_rpc"); - let network_params = sc_network::config::Params { - role: config.role.clone(), - executor: { - let spawn_handle = task_manager.spawn_handle(); - Some(Box::new(move |fut| { - spawn_handle.spawn("libp2p-node", fut); - })) - }, - network_config: config.network.clone(), - chain: client.clone(), - finality_proof_provider, - finality_proof_request_builder, - on_demand: on_demand.clone(), - transaction_pool: transaction_pool_adapter.clone() as _, - import_queue, - protocol_id, - block_announce_validator, - metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()) - }; + let (network, network_status_sinks, network_future) = build_network( + &config, client.clone(), transaction_pool.clone(), Clone::clone(&spawn_handle), on_demand.clone(), + block_announce_validator_builder, finality_proof_request_builder, finality_proof_provider, + system_rpc_rx, import_queue + )?; - let has_bootnodes = !network_params.network_config.boot_nodes.is_empty(); - let network_mut = sc_network::NetworkWorker::new(network_params)?; - let network = network_mut.service().clone(); - let network_status_sinks = Arc::new(Mutex::new(status_sinks::StatusSinks::new())); + // The network worker is responsible for gathering all network messages and processing + // them. This is quite a heavy task, and at the time of the writing of this comment it + // frequently happens that this future takes several seconds or in some situations + // even more than a minute until it has processed its entire queue. This is clearly an + // issue, and ideally we would like to fix the network future to take as little time as + // possible, but we also take the extra harm-prevention measure to execute the networking + // future using `spawn_blocking`. + spawn_handle.spawn_blocking( + "network-worker", + network_future + ); let offchain_storage = backend.offchain_storage(); let offchain_workers = match (config.offchain_worker.clone(), offchain_storage.clone()) { @@ -1037,114 +1001,39 @@ ServiceBuilder< _ => None, }; - let spawn_handle = task_manager.spawn_handle(); - // Inform the tx pool about imported and finalized blocks. - { - let txpool = Arc::downgrade(&transaction_pool); - - let mut import_stream = client.import_notification_stream().map(Into::into).fuse(); - let mut finality_stream = client.finality_notification_stream() - .map(Into::into) - .fuse(); - - let events = async move { - loop { - let evt = futures::select! { - evt = import_stream.next() => evt, - evt = finality_stream.next() => evt, - complete => return, - }; - - let txpool = txpool.upgrade(); - if let Some((txpool, evt)) = txpool.and_then(|tp| evt.map(|evt| (tp, evt))) { - txpool.maintain(evt).await; - } - } - }; - - spawn_handle.spawn( - "txpool-notifications", - events, - ); - } + spawn_handle.spawn( + "txpool-notifications", + sc_transaction_pool::notification_future(client.clone(), transaction_pool.clone()), + ); // Inform the offchain worker about new imported blocks - { - let offchain = offchain_workers.as_ref().map(Arc::downgrade); - let notifications_spawn_handle = task_manager.spawn_handle(); - let network_state_info: Arc = network.clone(); - let is_validator = config.role.is_authority(); - - let events = client.import_notification_stream().for_each(move |n| { - let offchain = offchain.as_ref().and_then(|o| o.upgrade()); - match offchain { - Some(offchain) if n.is_new_best => { - notifications_spawn_handle.spawn( - "offchain-on-block", - offchain.on_block_imported( - &n.header, - network_state_info.clone(), - is_validator, - ), - ); - }, - Some(_) => log::debug!( - target: "sc_offchain", - "Skipping offchain workers for non-canon block: {:?}", - n.header, - ), - _ => {}, - } - - ready(()) - }); - + if let Some(offchain) = offchain_workers.clone() { spawn_handle.spawn( "offchain-notifications", - events, + sc_offchain::notification_future( + config.role.is_authority(), + client.clone(), + offchain, + task_manager.spawn_handle(), + network.clone() + ) ); } - { - // extrinsic notifications - let network = Arc::downgrade(&network); - let transaction_pool_ = transaction_pool.clone(); - let events = transaction_pool.import_notification_stream() - .for_each(move |hash| { - if let Some(network) = network.upgrade() { - network.propagate_extrinsic(hash); - } - let status = transaction_pool_.status(); - telemetry!(SUBSTRATE_INFO; "txpool.import"; - "ready" => status.ready, - "future" => status.future - ); - ready(()) - }); - - spawn_handle.spawn( - "on-transaction-imported", - events, - ); - } + spawn_handle.spawn( + "on-transaction-imported", + transaction_notifications(transaction_pool.clone(), network.clone()), + ); // Prometheus metrics. - let mut metrics_service = if let Some(PrometheusConfig { port, registry }) = config.prometheus_config.clone() { + let metrics_service = if let Some(PrometheusConfig { port, registry }) = config.prometheus_config.clone() { // Set static metrics. - - - let role_bits = match config.role { - Role::Full => 1u64, - Role::Light => 2u64, - Role::Sentry { .. } => 3u64, - Role::Authority { .. } => 4u64, - }; let metrics = MetricsService::with_prometheus( ®istry, &config.network.node_name, &config.impl_version, - role_bits, + &config.role, )?; spawn_handle.spawn( "prometheus-endpoint", @@ -1157,171 +1046,33 @@ ServiceBuilder< }; // Periodically notify the telemetry. - let transaction_pool_ = transaction_pool.clone(); - let client_ = client.clone(); - let (state_tx, state_rx) = tracing_unbounded::<(NetworkStatus<_>, NetworkState)>("mpsc_netstat1"); - network_status_sinks.lock().push(std::time::Duration::from_millis(5000), state_tx); - let tel_task = state_rx.for_each(move |(net_status, _)| { - let info = client_.usage_info(); - metrics_service.tick( - &info, - &transaction_pool_.status(), - &net_status, - ); - ready(()) - }); - - spawn_handle.spawn( - "telemetry-periodic-send", - tel_task, - ); + spawn_handle.spawn("telemetry-periodic-send", telemetry_periodic_send( + client.clone(), transaction_pool.clone(), metrics_service, network_status_sinks.clone() + )); // Periodically send the network state to the telemetry. - let (netstat_tx, netstat_rx) = tracing_unbounded::<(NetworkStatus<_>, NetworkState)>("mpsc_netstat2"); - network_status_sinks.lock().push(std::time::Duration::from_secs(30), netstat_tx); - let tel_task_2 = netstat_rx.for_each(move |(_, network_state)| { - telemetry!( - SUBSTRATE_INFO; - "system.network_state"; - "state" => network_state, - ); - ready(()) - }); spawn_handle.spawn( "telemetry-periodic-network-state", - tel_task_2, + telemetry_periodic_network_state(network_status_sinks.clone()), ); // RPC - let (system_rpc_tx, system_rpc_rx) = tracing_unbounded("mpsc_system_rpc"); - let gen_handler = |deny_unsafe: sc_rpc::DenyUnsafe| { - use sc_rpc::{chain, state, author, system, offchain}; - - let system_info = sc_rpc::system::SystemInfo { - chain_name: chain_spec.name().into(), - impl_name: config.impl_name.into(), - impl_version: config.impl_version.into(), - properties: chain_spec.properties().clone(), - chain_type: chain_spec.chain_type().clone(), - }; - - let subscriptions = SubscriptionManager::new(Arc::new(task_manager.spawn_handle())); - - let (chain, state, child_state) = if let (Some(remote_backend), Some(on_demand)) = - (remote_backend.as_ref(), on_demand.as_ref()) { - // Light clients - let chain = sc_rpc::chain::new_light( - client.clone(), - subscriptions.clone(), - remote_backend.clone(), - on_demand.clone() - ); - let (state, child_state) = sc_rpc::state::new_light( - client.clone(), - subscriptions.clone(), - remote_backend.clone(), - on_demand.clone() - ); - (chain, state, child_state) - - } else { - // Full nodes - let chain = sc_rpc::chain::new_full(client.clone(), subscriptions.clone()); - let (state, child_state) = sc_rpc::state::new_full(client.clone(), subscriptions.clone()); - (chain, state, child_state) - }; - - let author = sc_rpc::author::Author::new( - client.clone(), - transaction_pool.clone(), - subscriptions, - keystore.clone(), - deny_unsafe, - ); - let system = system::System::new(system_info, system_rpc_tx.clone(), deny_unsafe); - - let maybe_offchain_rpc = offchain_storage.clone() - .map(|storage| { - let offchain = sc_rpc::offchain::Offchain::new(storage, deny_unsafe); - // FIXME: Use plain Option (don't collect into HashMap) when we upgrade to jsonrpc 14.1 - // https://github.com/paritytech/jsonrpc/commit/20485387ed06a48f1a70bf4d609a7cde6cf0accf - let delegate = offchain::OffchainApi::to_delegate(offchain); - delegate.into_iter().collect::>() - }).unwrap_or_default(); - - sc_rpc_server::rpc_handler(( - state::StateApi::to_delegate(state), - state::ChildStateApi::to_delegate(child_state), - chain::ChainApi::to_delegate(chain), - maybe_offchain_rpc, - author::AuthorApi::to_delegate(author), - system::SystemApi::to_delegate(system), - rpc_extensions_builder.build(deny_unsafe), - )) - }; + let gen_handler = |deny_unsafe: sc_rpc::DenyUnsafe| gen_handler( + deny_unsafe, &config, &task_manager, client.clone(), transaction_pool.clone(), + keystore.clone(), on_demand.clone(), remote_backend.clone(), &*rpc_extensions_builder, + offchain_storage.clone(), system_rpc_tx.clone() + ); let rpc = start_rpc_servers(&config, gen_handler)?; // This is used internally, so don't restrict access to unsafe RPC let rpc_handlers = gen_handler(sc_rpc::DenyUnsafe::No); - // The network worker is responsible for gathering all network messages and processing - // them. This is quite a heavy task, and at the time of the writing of this comment it - // frequently happens that this future takes several seconds or in some situations - // even more than a minute until it has processed its entire queue. This is clearly an - // issue, and ideally we would like to fix the network future to take as little time as - // possible, but we also take the extra harm-prevention measure to execute the networking - // future using `spawn_blocking`. - spawn_handle.spawn_blocking( - "network-worker", - build_network_future( - config.role.clone(), - network_mut, - client.clone(), - network_status_sinks.clone(), - system_rpc_rx, - has_bootnodes, - config.announce_block, - ), - ); - let telemetry_connection_sinks: Arc>>> = Default::default(); // Telemetry let telemetry = config.telemetry_endpoints.clone().map(|endpoints| { - let is_authority = config.role.is_authority(); - let network_id = network.local_peer_id().to_base58(); - let name = config.network.node_name.clone(); - let impl_name = config.impl_name.to_owned(); - let version = version.clone(); - let chain_name = config.chain_spec.name().to_owned(); - let telemetry_connection_sinks_ = telemetry_connection_sinks.clone(); - let telemetry = sc_telemetry::init_telemetry(sc_telemetry::TelemetryConfig { - endpoints, - wasm_external_transport: config.telemetry_external_transport.take(), - }); - let startup_time = SystemTime::UNIX_EPOCH.elapsed() - .map(|dur| dur.as_millis()) - .unwrap_or(0); - let future = telemetry.clone() - .for_each(move |event| { - // Safe-guard in case we add more events in the future. - let sc_telemetry::TelemetryEvent::Connected = event; - - telemetry!(SUBSTRATE_INFO; "system.connected"; - "name" => name.clone(), - "implementation" => impl_name.clone(), - "version" => version.clone(), - "config" => "", - "chain" => chain_name.clone(), - "authority" => is_authority, - "startup_time" => startup_time, - "network_id" => network_id.clone() - ); - - telemetry_connection_sinks_.lock().retain(|sink| { - sink.unbounded_send(()).is_ok() - }); - ready(()) - }); + let (telemetry, future) = build_telemetry( + &mut config, endpoints, telemetry_connection_sinks.clone(), network.clone() + ); spawn_handle.spawn( "telemetry-worker", @@ -1342,6 +1093,14 @@ ServiceBuilder< } } + // Spawn informant task + spawn_handle.spawn("informant", sc_informant::build( + client.clone(), + network_status_sinks.clone(), + transaction_pool.clone(), + config.informant_output_format, + )); + Ok(Service { client, task_manager, @@ -1359,6 +1118,378 @@ ServiceBuilder< keystore, marker: PhantomData::, prometheus_registry: config.prometheus_config.map(|config| config.registry), + _base_path: config.base_path.map(Arc::new), }) } + + /// Builds the light service. + pub fn build_light(self) -> Result, + TSc, + NetworkStatus, + NetworkService::Hash>, + TExPool, + sc_offchain::OffchainWorkers< + Client, + TBackend::OffchainStorage, + TBl + >, + >, Error> + where TExec: CallExecutor, + { + self.build_common() + } +} + +impl +ServiceBuilder< + TBl, + TRtApi, + Client, + Arc>, + TSc, + TImpQu, + BoxFinalityProofRequestBuilder, + Arc>, + TExPool, + TRpc, + TBackend, +> where + Client: ProvideRuntimeApi, + as ProvideRuntimeApi>::Api: + sp_api::Metadata + + sc_offchain::OffchainWorkerApi + + sp_transaction_pool::runtime_api::TaggedTransactionQueue + + sp_session::SessionKeys + + sp_api::ApiErrorExt + + sp_api::ApiExt, + TBl: BlockT, + TRtApi: 'static + Send + Sync, + TBackend: 'static + sc_client_api::backend::Backend + Send, + TExec: 'static + CallExecutor + Send + Sync + Clone, + TSc: Clone, + TImpQu: 'static + ImportQueue, + TExPool: MaintainedTransactionPool::Hash> + + LocalTransactionPool::Hash> + + MallocSizeOfWasm + + 'static, + TRpc: sc_rpc::RpcExtension, +{ + + /// Builds the full service. + pub fn build_full(self) -> Result, + TSc, + NetworkStatus, + NetworkService::Hash>, + TExPool, + sc_offchain::OffchainWorkers< + Client, + TBackend::OffchainStorage, + TBl + >, + >, Error> + where TExec: CallExecutor, + { + // make transaction pool available for off-chain runtime calls. + self.client.execution_extensions() + .register_transaction_pool(Arc::downgrade(&self.transaction_pool) as _); + + self.build_common() + } +} + +async fn transaction_notifications( + transaction_pool: Arc, + network: Arc::Hash>> +) + where + TBl: BlockT, + TExPool: MaintainedTransactionPool::Hash>, +{ + // transaction notifications + transaction_pool.import_notification_stream() + .for_each(move |hash| { + network.propagate_transaction(hash); + let status = transaction_pool.status(); + telemetry!(SUBSTRATE_INFO; "txpool.import"; + "ready" => status.ready, + "future" => status.future + ); + ready(()) + }) + .await; +} + +// Periodically notify the telemetry. +async fn telemetry_periodic_send( + client: Arc>, + transaction_pool: Arc, + mut metrics_service: MetricsService, + network_status_sinks: Arc, NetworkState)>>> +) + where + TBl: BlockT, + TExec: CallExecutor, + Client: ProvideRuntimeApi, + TExPool: MaintainedTransactionPool::Hash>, + TBackend: sc_client_api::backend::Backend, +{ + let (state_tx, state_rx) = tracing_unbounded::<(NetworkStatus<_>, NetworkState)>("mpsc_netstat1"); + network_status_sinks.lock().push(std::time::Duration::from_millis(5000), state_tx); + state_rx.for_each(move |(net_status, _)| { + let info = client.usage_info(); + metrics_service.tick( + &info, + &transaction_pool.status(), + &net_status, + ); + ready(()) + }).await; +} + +async fn telemetry_periodic_network_state( + network_status_sinks: Arc, NetworkState)>>> +) { + // Periodically send the network state to the telemetry. + let (netstat_tx, netstat_rx) = tracing_unbounded::<(NetworkStatus<_>, NetworkState)>("mpsc_netstat2"); + network_status_sinks.lock().push(std::time::Duration::from_secs(30), netstat_tx); + netstat_rx.for_each(move |(_, network_state)| { + telemetry!( + SUBSTRATE_INFO; + "system.network_state"; + "state" => network_state, + ); + ready(()) + }).await; +} + +fn build_telemetry( + config: &mut Configuration, + endpoints: sc_telemetry::TelemetryEndpoints, + telemetry_connection_sinks: Arc>>>, + network: Arc::Hash>> +) -> (sc_telemetry::Telemetry, Pin + Send>>) { + let is_authority = config.role.is_authority(); + let network_id = network.local_peer_id().to_base58(); + let name = config.network.node_name.clone(); + let impl_name = config.impl_name.to_owned(); + let version = config.impl_version; + let chain_name = config.chain_spec.name().to_owned(); + let telemetry = sc_telemetry::init_telemetry(sc_telemetry::TelemetryConfig { + endpoints, + wasm_external_transport: config.telemetry_external_transport.take(), + }); + let startup_time = SystemTime::UNIX_EPOCH.elapsed() + .map(|dur| dur.as_millis()) + .unwrap_or(0); + let future = telemetry.clone() + .for_each(move |event| { + // Safe-guard in case we add more events in the future. + let sc_telemetry::TelemetryEvent::Connected = event; + + telemetry!(SUBSTRATE_INFO; "system.connected"; + "name" => name.clone(), + "implementation" => impl_name.clone(), + "version" => version, + "config" => "", + "chain" => chain_name.clone(), + "authority" => is_authority, + "startup_time" => startup_time, + "network_id" => network_id.clone() + ); + + telemetry_connection_sinks.lock().retain(|sink| { + sink.unbounded_send(()).is_ok() + }); + ready(()) + }) + .boxed(); + + (telemetry, future) +} + +fn gen_handler( + deny_unsafe: sc_rpc::DenyUnsafe, + config: &Configuration, + task_manager: &TaskManager, + client: Arc>, + transaction_pool: Arc, + keystore: Arc>, + on_demand: Option>>, + remote_backend: Option>>, + rpc_extensions_builder: &(dyn RpcExtensionBuilder + Send), + offchain_storage: Option<>::OffchainStorage>, + system_rpc_tx: TracingUnboundedSender> +) -> jsonrpc_pubsub::PubSubHandler + where + TBl: BlockT, + TExec: CallExecutor + Send + Sync + 'static, + TRtApi: Send + Sync + 'static, + Client: ProvideRuntimeApi, + TExPool: MaintainedTransactionPool::Hash> + 'static, + TBackend: sc_client_api::backend::Backend + 'static, + TRpc: sc_rpc::RpcExtension, + as ProvideRuntimeApi>::Api: + sp_session::SessionKeys + + sp_api::Metadata, +{ + use sc_rpc::{chain, state, author, system, offchain}; + + let system_info = sc_rpc::system::SystemInfo { + chain_name: config.chain_spec.name().into(), + impl_name: config.impl_name.into(), + impl_version: config.impl_version.into(), + properties: config.chain_spec.properties(), + chain_type: config.chain_spec.chain_type(), + }; + + let subscriptions = SubscriptionManager::new(Arc::new(task_manager.spawn_handle())); + + let (chain, state, child_state) = if let (Some(remote_backend), Some(on_demand)) = + (remote_backend, on_demand) { + // Light clients + let chain = sc_rpc::chain::new_light( + client.clone(), + subscriptions.clone(), + remote_backend.clone(), + on_demand.clone() + ); + let (state, child_state) = sc_rpc::state::new_light( + client.clone(), + subscriptions.clone(), + remote_backend.clone(), + on_demand.clone() + ); + (chain, state, child_state) + + } else { + // Full nodes + let chain = sc_rpc::chain::new_full(client.clone(), subscriptions.clone()); + let (state, child_state) = sc_rpc::state::new_full(client.clone(), subscriptions.clone()); + (chain, state, child_state) + }; + + let author = sc_rpc::author::Author::new( + client.clone(), + transaction_pool.clone(), + subscriptions, + keystore.clone(), + deny_unsafe, + ); + let system = system::System::new(system_info, system_rpc_tx.clone(), deny_unsafe); + + let maybe_offchain_rpc = offchain_storage.clone() + .map(|storage| { + let offchain = sc_rpc::offchain::Offchain::new(storage, deny_unsafe); + // FIXME: Use plain Option (don't collect into HashMap) when we upgrade to jsonrpc 14.1 + // https://github.com/paritytech/jsonrpc/commit/20485387ed06a48f1a70bf4d609a7cde6cf0accf + let delegate = offchain::OffchainApi::to_delegate(offchain); + delegate.into_iter().collect::>() + }).unwrap_or_default(); + + sc_rpc_server::rpc_handler(( + state::StateApi::to_delegate(state), + state::ChildStateApi::to_delegate(child_state), + chain::ChainApi::to_delegate(chain), + maybe_offchain_rpc, + author::AuthorApi::to_delegate(author), + system::SystemApi::to_delegate(system), + rpc_extensions_builder.build(deny_unsafe), + )) +} + +fn build_network( + config: &Configuration, + client: Arc>, + transaction_pool: Arc, + spawn_handle: SpawnTaskHandle, + on_demand: Option>>, + block_announce_validator_builder: Option>) -> + Box + Send> + Send + >>, + finality_proof_request_builder: Option>, + finality_proof_provider: Option>>, + system_rpc_rx: TracingUnboundedReceiver>, + import_queue: TImpQu +) -> Result< + ( + Arc::Hash>>, + Arc, NetworkState)>>>, + Pin + Send>> + ), + Error +> + where + TBl: BlockT, + TExec: CallExecutor + Send + Sync + 'static, + TRtApi: Send + Sync + 'static, + Client: ProvideRuntimeApi, + TExPool: MaintainedTransactionPool::Hash> + 'static, + TBackend: sc_client_api::backend::Backend + 'static, + TImpQu: ImportQueue + 'static, +{ + let transaction_pool_adapter = Arc::new(TransactionPoolAdapter { + imports_external_transactions: !matches!(config.role, Role::Light), + pool: transaction_pool.clone(), + client: client.clone(), + }); + + let protocol_id = { + let protocol_id_full = match config.chain_spec.protocol_id() { + Some(pid) => pid, + None => { + warn!("Using default protocol ID {:?} because none is configured in the \ + chain specs", DEFAULT_PROTOCOL_ID + ); + DEFAULT_PROTOCOL_ID + } + }.as_bytes(); + sc_network::config::ProtocolId::from(protocol_id_full) + }; + + let block_announce_validator = if let Some(f) = block_announce_validator_builder { + f(client.clone()) + } else { + Box::new(DefaultBlockAnnounceValidator::new(client.clone())) + }; + + let network_params = sc_network::config::Params { + role: config.role.clone(), + executor: { + Some(Box::new(move |fut| { + spawn_handle.spawn("libp2p-node", fut); + })) + }, + network_config: config.network.clone(), + chain: client.clone(), + finality_proof_provider, + finality_proof_request_builder, + on_demand: on_demand.clone(), + transaction_pool: transaction_pool_adapter.clone() as _, + import_queue: Box::new(import_queue), + protocol_id, + block_announce_validator, + metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()) + }; + + let has_bootnodes = !network_params.network_config.boot_nodes.is_empty(); + let network_mut = sc_network::NetworkWorker::new(network_params)?; + let network = network_mut.service().clone(); + let network_status_sinks = Arc::new(Mutex::new(status_sinks::StatusSinks::new())); + + let future = build_network_future( + config.role.clone(), + network_mut, + client.clone(), + network_status_sinks.clone(), + system_rpc_rx, + has_bootnodes, + config.announce_block, + ).boxed(); + + Ok((network, network_status_sinks, future)) } diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index a3d2489fd0850..922f34b656813 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -28,8 +28,9 @@ use parking_lot::{Mutex, RwLock}; use codec::{Encode, Decode}; use hash_db::Prefix; use sp_core::{ - ChangesTrieConfiguration, convert_hash, NativeOrEncoded, - storage::{StorageKey, PrefixedStorageKey, StorageData, well_known_keys, ChildInfo}, + convert_hash, + storage::{well_known_keys, ChildInfo, PrefixedStorageKey, StorageData, StorageKey}, + ChangesTrieConfiguration, ExecutionContext, NativeOrEncoded, }; use sc_telemetry::{telemetry, SUBSTRATE_INFO}; use sp_runtime::{ @@ -84,10 +85,9 @@ use sp_utils::mpsc::{TracingUnboundedSender, tracing_unbounded}; use sp_blockchain::Error; use prometheus_endpoint::Registry; use super::{ - genesis, - light::{call_executor::prove_execution, fetcher::ChangesProof}, - block_rules::{BlockRules, LookupResult as BlockLookupResult}, + genesis, block_rules::{BlockRules, LookupResult as BlockLookupResult}, }; +use sc_light::{call_executor::prove_execution, fetcher::ChangesProof}; use rand::Rng; #[cfg(feature="test-helpers")] @@ -753,11 +753,6 @@ impl Client where ) = storage_changes.into_inner(); if self.config.offchain_indexing_api { - // if let Some(mut offchain_storage) = self.backend.offchain_storage() { - // offchain_sc.iter().for_each(|(k,v)| { - // offchain_storage.set(b"block-import-info", k,v) - // }); - // } operation.op.update_offchain_storage(offchain_sc)?; } @@ -864,9 +859,15 @@ impl Client where // block. (true, ref mut storage_changes @ None, Some(ref body)) => { let runtime_api = self.runtime_api(); + let execution_context = if import_block.origin == BlockOrigin::NetworkInitialSync { + ExecutionContext::Syncing + } else { + ExecutionContext::Importing + }; - runtime_api.execute_block( + runtime_api.execute_block_with_context( &at, + execution_context, Block::new(import_block.header.clone(), body.clone()), )?; diff --git a/client/service/src/client/light/mod.rs b/client/service/src/client/light.rs similarity index 63% rename from client/service/src/client/light/mod.rs rename to client/service/src/client/light.rs index a3456f96a3786..8b9b65fc2fadd 100644 --- a/client/service/src/client/light/mod.rs +++ b/client/service/src/client/light.rs @@ -16,12 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Light client components. - -pub mod backend; -pub mod blockchain; -pub mod call_executor; -pub mod fetcher; +//! Light client utilities. use std::sync::Arc; @@ -37,24 +32,8 @@ use super::client::{Client,ClientConfig}; use sc_client_api::{ light::Storage as BlockchainStorage, CloneableSpawn, }; -use self::backend::Backend; -use self::blockchain::Blockchain; -use self::call_executor::GenesisCallExecutor; -use self::fetcher::LightDataChecker; - -/// Create an instance of light client blockchain backend. -pub fn new_light_blockchain>(storage: S) -> Arc> { - Arc::new(Blockchain::new(storage)) -} +use sc_light::{Backend, GenesisCallExecutor}; -/// Create an instance of light client backend. -pub fn new_light_backend(blockchain: Arc>) -> Arc>> - where - B: BlockT, - S: BlockchainStorage, -{ - Arc::new(Backend::new(blockchain)) -} /// Create an instance of light client. pub fn new_light( @@ -79,7 +58,12 @@ pub fn new_light( S: BlockchainStorage + 'static, E: CodeExecutor + RuntimeInfo + Clone + 'static, { - let local_executor = LocalCallExecutor::new(backend.clone(), code_executor, spawn_handle.clone(), ClientConfig::default()); + let local_executor = LocalCallExecutor::new( + backend.clone(), + code_executor, + spawn_handle.clone(), + ClientConfig::default() + ); let executor = GenesisCallExecutor::new(backend.clone(), local_executor); Client::new( backend, @@ -92,15 +76,3 @@ pub fn new_light( ClientConfig::default(), ) } - -/// Create an instance of fetch data checker. -pub fn new_fetch_checker>( - blockchain: Arc>, - executor: E, - spawn_handle: Box, -) -> LightDataChecker, B, S> - where - E: CodeExecutor, -{ - LightDataChecker::new(blockchain, executor, spawn_handle) -} diff --git a/client/service/src/config.rs b/client/service/src/config.rs index cc9c742ed68db..fb4dbc666a9d6 100644 --- a/client/service/src/config.rs +++ b/client/service/src/config.rs @@ -24,14 +24,17 @@ pub use sc_network::config::{ExtTransport, MultiaddrWithPeerId, NetworkConfigura pub use sc_executor::WasmExecutionMethod; use sc_client_api::execution_extensions::ExecutionStrategies; -use std::{future::Future, path::{PathBuf, Path}, pin::Pin, net::SocketAddr, sync::Arc}; +use std::{io, future::Future, path::{PathBuf, Path}, pin::Pin, net::SocketAddr, sync::Arc}; pub use sc_transaction_pool::txpool::Options as TransactionPoolOptions; use sc_chain_spec::ChainSpec; use sp_core::crypto::Protected; pub use sc_telemetry::TelemetryEndpoints; use prometheus_endpoint::Registry; +#[cfg(not(target_os = "unknown"))] +use tempfile::TempDir; /// Service configuration. +#[derive(Debug)] pub struct Configuration { /// Implementation name pub impl_name: &'static str, @@ -40,7 +43,7 @@ pub struct Configuration { /// Node role. pub role: Role, /// How to spawn background tasks. Mandatory, otherwise creating a `Service` will error. - pub task_executor: Arc + Send>>, TaskType) + Send + Sync>, + pub task_executor: TaskExecutor, /// Extrinsic pool configuration. pub transaction_pool: TransactionPoolOptions, /// Network configuration. @@ -65,6 +68,8 @@ pub struct Configuration { pub rpc_http: Option, /// RPC over Websockets binding address. `None` if disabled. pub rpc_ws: Option, + /// RPC over IPC binding path. `None` if disabled. + pub rpc_ipc: Option, /// Maximum number of connections for WebSockets RPC server. `None` if default. pub rpc_ws_max_connections: Option, /// CORS settings for HTTP & WS servers. `None` if all origins are allowed. @@ -102,6 +107,10 @@ pub struct Configuration { pub max_runtime_instances: usize, /// Announce block automatically after they have been imported pub announce_block: bool, + /// Base path of the configuration + pub base_path: Option, + /// Configuration of the output format that the informant uses. + pub informant_output_format: sc_informant::OutputFormat, } /// Type for tasks spawned by the executor. @@ -114,7 +123,7 @@ pub enum TaskType { } /// Configuration of the client keystore. -#[derive(Clone)] +#[derive(Debug, Clone)] pub enum KeystoreConfig { /// Keystore at a path on-disk. Recommended for native nodes. Path { @@ -137,7 +146,7 @@ impl KeystoreConfig { } } /// Configuration of the database of the client. -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub struct OffchainWorkerConfig { /// If this is allowed. pub enabled: bool, @@ -146,7 +155,7 @@ pub struct OffchainWorkerConfig { } /// Configuration of the Prometheus endpoint. -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct PrometheusConfig { /// Port to use. pub port: SocketAddr, @@ -191,3 +200,126 @@ impl Default for RpcMethods { RpcMethods::Auto } } + +/// The base path that is used for everything that needs to be write on disk to run a node. +#[derive(Debug)] +pub enum BasePath { + /// A temporary directory is used as base path and will be deleted when dropped. + #[cfg(not(target_os = "unknown"))] + Temporary(TempDir), + /// A path on the disk. + Permanenent(PathBuf), +} + +impl BasePath { + /// Create a `BasePath` instance using a temporary directory prefixed with "substrate" and use + /// it as base path. + /// + /// Note: the temporary directory will be created automatically and deleted when the `BasePath` + /// instance is dropped. + #[cfg(not(target_os = "unknown"))] + pub fn new_temp_dir() -> io::Result { + Ok(BasePath::Temporary( + tempfile::Builder::new().prefix("substrate").tempdir()?, + )) + } + + /// Create a `BasePath` instance based on an existing path on disk. + /// + /// Note: this function will not ensure that the directory exist nor create the directory. It + /// will also not delete the directory when the instance is dropped. + pub fn new>(path: P) -> BasePath { + BasePath::Permanenent(path.as_ref().to_path_buf()) + } + + /// Create a base path from values describing the project. + #[cfg(not(target_os = "unknown"))] + pub fn from_project(qualifier: &str, organization: &str, application: &str) -> BasePath { + BasePath::new( + directories::ProjectDirs::from(qualifier, organization, application) + .expect("app directories exist on all supported platforms; qed") + .data_local_dir(), + ) + } + + /// Retrieve the base path. + pub fn path(&self) -> &Path { + match self { + #[cfg(not(target_os = "unknown"))] + BasePath::Temporary(temp_dir) => temp_dir.path(), + BasePath::Permanenent(path) => path.as_path(), + } + } +} + +impl std::convert::From for BasePath { + fn from(path: PathBuf) -> Self { + BasePath::new(path) + } +} + +type TaskExecutorInner = Arc + Send>>, TaskType) + Send + Sync>; + +/// Callable object that execute tasks. +/// +/// This struct can be created easily using `Into`. +/// +/// # Examples +/// +/// ## Using tokio +/// +/// ``` +/// # use sc_service::TaskExecutor; +/// # mod tokio { pub mod runtime { +/// # #[derive(Clone)] +/// # pub struct Runtime; +/// # impl Runtime { +/// # pub fn new() -> Result { Ok(Runtime) } +/// # pub fn handle(&self) -> &Self { &self } +/// # pub fn spawn(&self, _: std::pin::Pin + Send>>) {} +/// # } +/// # } } +/// use tokio::runtime::Runtime; +/// +/// let runtime = Runtime::new().unwrap(); +/// let handle = runtime.handle().clone(); +/// let task_executor: TaskExecutor = (move |future, _task_type| { +/// handle.spawn(future); +/// }).into(); +/// ``` +/// +/// ## Using async-std +/// +/// ``` +/// # use sc_service::TaskExecutor; +/// # mod async_std { pub mod task { +/// # pub fn spawn(_: std::pin::Pin + Send>>) {} +/// # } } +/// let task_executor: TaskExecutor = (|future, _task_type| { +/// async_std::task::spawn(future); +/// }).into(); +/// ``` +#[derive(Clone)] +pub struct TaskExecutor(TaskExecutorInner); + +impl std::fmt::Debug for TaskExecutor { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "TaskExecutor") + } +} + +impl std::convert::From for TaskExecutor +where + F: Fn(Pin + Send>>, TaskType) + Send + Sync + 'static, +{ + fn from(x: F) -> Self { + Self(Arc::new(x)) + } +} + +impl TaskExecutor { + /// Spawns a new asynchronous task. + pub fn spawn(&self, future: Pin + Send>>, task_type: TaskType) { + self.0(future, task_type) + } +} diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index 4f2be23f877ba..036c95777323e 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -33,7 +33,6 @@ mod builder; pub mod client; #[cfg(not(feature = "test-helpers"))] mod client; -mod status_sinks; mod task_manager; use std::{io, pin::Pin}; @@ -52,13 +51,13 @@ use futures::{ sink::SinkExt, task::{Spawn, FutureObj, SpawnError}, }; -use sc_network::{NetworkService, network_state::NetworkState, PeerId}; +use sc_network::{NetworkService, NetworkStatus, network_state::NetworkState, PeerId}; use log::{log, warn, debug, error, Level}; use codec::{Encode, Decode}; use sp_runtime::generic::BlockId; -use sp_runtime::traits::{NumberFor, Block as BlockT}; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use parity_util_mem::MallocSizeOf; -use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; +use sp_utils::{status_sinks, mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}}; pub use self::error::Error; pub use self::builder::{ @@ -66,7 +65,9 @@ pub use self::builder::{ ServiceBuilder, ServiceBuilderCommand, TFullClient, TLightClient, TFullBackend, TLightBackend, TFullCallExecutor, TLightCallExecutor, RpcExtensionBuilder, }; -pub use config::{Configuration, DatabaseConfig, PruningMode, Role, RpcMethods, TaskType}; +pub use config::{ + BasePath, Configuration, DatabaseConfig, PruningMode, Role, RpcMethods, TaskExecutor, TaskType, +}; pub use sc_chain_spec::{ ChainSpec, GenericChainSpec, Properties, RuntimeGenesis, Extension as ChainSpecExtension, NoExtension, ChainType, @@ -83,7 +84,7 @@ pub use sc_network::config::{ TransactionImportFuture, }; pub use sc_tracing::TracingReceiver; -pub use task_manager::SpawnTaskHandle; +pub use task_manager::{SpawnEssentialTaskHandle, SpawnTaskHandle}; use task_manager::TaskManager; use sp_blockchain::{HeaderBackend, HeaderMetadata}; use sp_api::{ApiExt, ConstructRuntimeApi, ApiErrorExt}; @@ -110,14 +111,14 @@ pub struct Service { task_manager: TaskManager, select_chain: Option, network: Arc, - /// Sinks to propagate network status updates. - /// For each element, every time the `Interval` fires we push an element on the sender. + // Sinks to propagate network status updates. + // For each element, every time the `Interval` fires we push an element on the sender. network_status_sinks: Arc>>, transaction_pool: Arc, - /// Send a signal when a spawned essential task has concluded. The next time - /// the service future is polled it should complete with an error. + // Send a signal when a spawned essential task has concluded. The next time + // the service future is polled it should complete with an error. essential_failed_tx: TracingUnboundedSender<()>, - /// A receiver for spawned essential-tasks concluding. + // A receiver for spawned essential-tasks concluding. essential_failed_rx: TracingUnboundedReceiver<()>, rpc_handlers: sc_rpc_server::RpcHandler, _rpc: Box, @@ -127,6 +128,9 @@ pub struct Service { keystore: sc_keystore::KeyStorePtr, marker: PhantomData, prometheus_registry: Option, + // The base path is kept here because it can be a temporary directory which will be deleted + // when dropped + _base_path: Option>, } impl Unpin for Service {} @@ -164,13 +168,19 @@ pub trait AbstractService: Future> + Send + Unpin + S /// The task name is a `&'static str` as opposed to a `String`. The reason for that is that /// in order to avoid memory consumption issues with the Prometheus metrics, the set of /// possible task names has to be bounded. + #[deprecated(note = "Use `spawn_task_handle().spawn() instead.")] fn spawn_task(&self, name: &'static str, task: impl Future + Send + 'static); /// Spawns a task in the background that runs the future passed as /// parameter. The given task is considered essential, i.e. if it errors we /// trigger a service exit. + #[deprecated(note = "Use `spawn_essential_task_handle().spawn() instead.")] fn spawn_essential_task(&self, name: &'static str, task: impl Future + Send + 'static); + /// Returns a handle for spawning essential tasks. Any task spawned through this handle is + /// considered essential, i.e. if it errors we trigger a service exit. + fn spawn_essential_task_handle(&self) -> SpawnEssentialTaskHandle; + /// Returns a handle for spawning tasks. fn spawn_task_handle(&self) -> SpawnTaskHandle; @@ -210,6 +220,9 @@ pub trait AbstractService: Future> + Send + Unpin + S /// Get the prometheus metrics registry, if available. fn prometheus_registry(&self) -> Option; + + /// Get a clone of the base_path + fn base_path(&self) -> Option>; } impl AbstractService for @@ -244,7 +257,7 @@ where } fn telemetry(&self) -> Option { - self._telemetry.as_ref().map(|t| t.clone()) + self._telemetry.clone() } fn keystore(&self) -> sc_keystore::KeyStorePtr { @@ -264,13 +277,20 @@ where let _ = essential_failed.send(()); }); - let _ = self.spawn_task(name, essential_task); + let _ = self.spawn_task_handle().spawn(name, essential_task); } fn spawn_task_handle(&self) -> SpawnTaskHandle { self.task_manager.spawn_handle() } + fn spawn_essential_task_handle(&self) -> SpawnEssentialTaskHandle { + SpawnEssentialTaskHandle::new( + self.essential_failed_tx.clone(), + self.task_manager.spawn_handle(), + ) + } + fn rpc_query(&self, mem: &RpcSession, request: &str) -> Pin> + Send>> { Box::pin( self.rpc_handlers.handle_request(request, mem.metadata.clone()) @@ -310,6 +330,10 @@ where fn prometheus_registry(&self) -> Option { self.prometheus_registry.clone() } + + fn base_path(&self) -> Option> { + self._base_path.clone() + } } impl Future for @@ -373,6 +397,13 @@ fn build_network_future< if announce_imported_blocks { network.service().announce_block(notification.hash, Vec::new()); } + + if let sp_consensus::BlockOrigin::Own = notification.origin { + network.service().own_block_imported( + notification.hash, + notification.header.number().clone(), + ); + } } // We poll `finality_notification_stream`, but we only take the last event. @@ -487,25 +518,6 @@ fn build_network_future< }) } -/// Overview status of the network. -#[derive(Clone)] -pub struct NetworkStatus { - /// Current global sync state. - pub sync_state: sc_network::SyncState, - /// Target sync block number. - pub best_seen_block: Option>, - /// Number of peers participating in syncing. - pub num_sync_peers: u32, - /// Total number of connected peers - pub num_connected_peers: usize, - /// Total number of active peers. - pub num_active_peers: usize, - /// Downloaded bytes per second averaged over the past few seconds. - pub average_download_per_sec: u64, - /// Uploaded bytes per second averaged over the past few seconds. - pub average_upload_per_sec: u64, -} - #[cfg(not(target_os = "unknown"))] // Wrapper for HTTP and WS servers that makes sure they are properly shut down. mod waiting { @@ -519,6 +531,16 @@ mod waiting { } } + pub struct IpcServer(pub Option); + impl Drop for IpcServer { + fn drop(&mut self) { + if let Some(server) = self.0.take() { + server.close_handle().close(); + let _ = server.wait(); + } + } + } + pub struct WsServer(pub Option); impl Drop for WsServer { fn drop(&mut self) { @@ -564,6 +586,7 @@ fn start_rpc_servers sc_rpc_server::RpcHandler. -use std::convert::TryFrom; +use std::{convert::TryFrom, time::SystemTime}; use crate::NetworkStatus; use prometheus_endpoint::{register, Gauge, U64, F64, Registry, PrometheusError, Opts, GaugeVec}; @@ -25,6 +25,7 @@ use sp_runtime::traits::{NumberFor, Block, SaturatedConversion, UniqueSaturatedI use sp_transaction_pool::PoolStatus; use sp_utils::metrics::register_globals; use sc_client_api::ClientInfo; +use sc_network::config::Role; use sysinfo::{self, ProcessExt, SystemExt}; @@ -79,6 +80,13 @@ impl PrometheusMetrics { register_globals(registry)?; + let start_time_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or_default(); + register(Gauge::::new( + "process_start_time_seconds", + "Number of seconds between the UNIX epoch and the moment the process started", + )?, registry)?.set(start_time_since_epoch.as_secs()); + Ok(Self { // system #[cfg(all(any(unix, windows), not(target_os = "android")))] @@ -253,10 +261,17 @@ impl MetricsService { impl MetricsService { - pub fn with_prometheus(registry: &Registry, name: &str, version: &str, roles: u64) + pub fn with_prometheus(registry: &Registry, name: &str, version: &str, role: &Role) -> Result { - PrometheusMetrics::setup(registry, name, version, roles).map(|p| { + let role_bits = match role { + Role::Full => 1u64, + Role::Light => 2u64, + Role::Sentry { .. } => 3u64, + Role::Authority { .. } => 4u64, + }; + + PrometheusMetrics::setup(registry, name, version, role_bits).map(|p| { Self::inner_new(Some(p)) }) } diff --git a/client/service/src/task_manager.rs b/client/service/src/task_manager.rs index 553ca9c326d8b..544d76fc472cc 100644 --- a/client/service/src/task_manager.rs +++ b/client/service/src/task_manager.rs @@ -13,7 +13,7 @@ //! Substrate service tasks management module. -use std::{panic, pin::Pin, result::Result, sync::Arc}; +use std::{panic, result::Result}; use exit_future::Signal; use log::debug; use futures::{ @@ -28,18 +28,16 @@ use prometheus_endpoint::{ CounterVec, HistogramOpts, HistogramVec, Opts, Registry, U64 }; use sc_client_api::CloneableSpawn; -use crate::config::TaskType; +use sp_utils::mpsc::TracingUnboundedSender; +use crate::config::{TaskExecutor, TaskType}; mod prometheus_future; -/// Type alias for service task executor (usually runtime). -pub type ServiceTaskExecutor = Arc + Send>>, TaskType) + Send + Sync>; - /// An handle for spawning tasks in the service. #[derive(Clone)] pub struct SpawnTaskHandle { on_exit: exit_future::Exit, - executor: ServiceTaskExecutor, + executor: TaskExecutor, metrics: Option, } @@ -112,7 +110,7 @@ impl SpawnTaskHandle { } }; - (self.executor)(Box::pin(future), task_type); + self.executor.spawn(Box::pin(future), task_type); } } @@ -124,10 +122,14 @@ impl Spawn for SpawnTaskHandle { } } -impl sp_core::traits::SpawnBlocking for SpawnTaskHandle { +impl sp_core::traits::SpawnNamed for SpawnTaskHandle { fn spawn_blocking(&self, name: &'static str, future: BoxFuture<'static, ()>) { self.spawn_blocking(name, future); } + + fn spawn(&self, name: &'static str, future: BoxFuture<'static, ()>) { + self.spawn(name, future); + } } impl sc_client_api::CloneableSpawn for SpawnTaskHandle { @@ -145,6 +147,64 @@ impl futures01::future::Executor for SpawnTaskHandle { } } +/// A wrapper over `SpawnTaskHandle` that will notify a receiver whenever any +/// task spawned through it fails. The service should be on the receiver side +/// and will shut itself down whenever it receives any message, i.e. an +/// essential task has failed. +pub struct SpawnEssentialTaskHandle { + essential_failed_tx: TracingUnboundedSender<()>, + inner: SpawnTaskHandle, +} + +impl SpawnEssentialTaskHandle { + /// Creates a new `SpawnEssentialTaskHandle`. + pub fn new( + essential_failed_tx: TracingUnboundedSender<()>, + spawn_task_handle: SpawnTaskHandle, + ) -> SpawnEssentialTaskHandle { + SpawnEssentialTaskHandle { + essential_failed_tx, + inner: spawn_task_handle, + } + } + + /// Spawns the given task with the given name. + /// + /// See also [`SpawnTaskHandle::spawn`]. + pub fn spawn(&self, name: &'static str, task: impl Future + Send + 'static) { + self.spawn_inner(name, task, TaskType::Async) + } + + /// Spawns the blocking task with the given name. + /// + /// See also [`SpawnTaskHandle::spawn_blocking`]. + pub fn spawn_blocking( + &self, + name: &'static str, + task: impl Future + Send + 'static, + ) { + self.spawn_inner(name, task, TaskType::Blocking) + } + + fn spawn_inner( + &self, + name: &'static str, + task: impl Future + Send + 'static, + task_type: TaskType, + ) { + use futures::sink::SinkExt; + let mut essential_failed = self.essential_failed_tx.clone(); + let essential_task = std::panic::AssertUnwindSafe(task) + .catch_unwind() + .map(move |_| { + log::error!("Essential task `{}` failed. Shutting down service.", name); + let _ = essential_failed.send(()); + }); + + let _ = self.inner.spawn_inner(name, essential_task, task_type); + } +} + /// Helper struct to manage background/async tasks in Service. pub struct TaskManager { /// A future that resolves when the service has exited, this is useful to @@ -153,7 +213,7 @@ pub struct TaskManager { /// A signal that makes the exit future above resolve, fired on service drop. signal: Option, /// How to spawn background tasks. - executor: ServiceTaskExecutor, + executor: TaskExecutor, /// Prometheus metric where to report the polling times. metrics: Option, } @@ -162,7 +222,7 @@ impl TaskManager { /// If a Prometheus registry is passed, it will be used to report statistics about the /// service tasks. pub(super) fn new( - executor: ServiceTaskExecutor, + executor: TaskExecutor, prometheus_registry: Option<&Registry> ) -> Result { let (signal, on_exit) = exit_future::signal(); diff --git a/client/service/test/Cargo.toml b/client/service/test/Cargo.toml index a887c24a87959..7d61e86708a8c 100644 --- a/client/service/test/Cargo.toml +++ b/client/service/test/Cargo.toml @@ -20,6 +20,7 @@ log = "0.4.8" env_logger = "0.7.0" fdlimit = "0.1.4" parking_lot = "0.10.0" +sc-light = { version = "2.0.0-rc3", path = "../../light" } sp-blockchain = { version = "2.0.0-rc3", path = "../../../primitives/blockchain" } sp-api = { version = "2.0.0-rc3", path = "../../../primitives/api" } sp-state-machine = { version = "0.8.0-rc3", path = "../../../primitives/state-machine" } @@ -40,4 +41,4 @@ sc-client-api = { version = "2.0.0-rc3", path = "../../api" } sc-block-builder = { version = "0.8.0-rc3", path = "../../block-builder" } sc-executor = { version = "0.8.0-rc3", path = "../../executor" } sp-panic-handler = { version = "2.0.0-rc3", path = "../../../primitives/panic-handler" } -parity-scale-codec = "1.3.0" +parity-scale-codec = "1.3.1" diff --git a/client/service/test/src/client/light.rs b/client/service/test/src/client/light.rs index ec319e4832fdb..994d846c6a088 100644 --- a/client/service/test/src/client/light.rs +++ b/client/service/test/src/client/light.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use sc_service::client::light::{ +use sc_light::{ call_executor::{ GenesisCallExecutor, check_execution_proof, diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 206153082505c..4ff89f5319ff4 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -34,11 +34,11 @@ use sc_service::{ GenericChainSpec, ChainSpecExtension, Configuration, - config::{DatabaseConfig, KeystoreConfig}, + config::{BasePath, DatabaseConfig, KeystoreConfig}, RuntimeGenesis, Role, Error, - TaskType, + TaskExecutor, }; use sp_blockchain::HeaderBackend; use sc_network::{multiaddr, Multiaddr}; @@ -142,7 +142,7 @@ fn node_config, role: Role, - task_executor: Arc + Send>>, TaskType) + Send + Sync>, + task_executor: TaskExecutor, key_seed: Option, base_port: u16, root: &TempDir, @@ -194,6 +194,7 @@ fn node_config TestNet where authorities: impl Iterator Result<(F, U), Error>)> ) { let executor = self.runtime.executor(); + let task_executor: TaskExecutor = { + let executor = executor.clone(); + (move |fut: Pin + Send>>, _| { + executor.spawn(fut.unit_error().compat()); + }).into() + }; for (key, authority) in authorities { - let task_executor = { - let executor = executor.clone(); - Arc::new(move |fut: Pin + Send>>, _| executor.spawn(fut.unit_error().compat())) - }; let node_config = node_config( self.nodes, &self.chain_spec, Role::Authority { sentry_nodes: Vec::new() }, - task_executor, + task_executor.clone(), Some(key), self.base_port, &temp, @@ -280,11 +285,15 @@ impl TestNet where } for full in full { - let task_executor = { - let executor = executor.clone(); - Arc::new(move |fut: Pin + Send>>, _| executor.spawn(fut.unit_error().compat())) - }; - let node_config = node_config(self.nodes, &self.chain_spec, Role::Full, task_executor, None, self.base_port, &temp); + let node_config = node_config( + self.nodes, + &self.chain_spec, + Role::Full, + task_executor.clone(), + None, + self.base_port, + &temp, + ); let addr = node_config.network.listen_addresses.iter().next().unwrap().clone(); let (service, user_data) = full(node_config).expect("Error creating test node service"); let service = SyncService::from(service); @@ -296,11 +305,15 @@ impl TestNet where } for light in light { - let task_executor = { - let executor = executor.clone(); - Arc::new(move |fut: Pin + Send>>, _| executor.spawn(fut.unit_error().compat())) - }; - let node_config = node_config(self.nodes, &self.chain_spec, Role::Light, task_executor, None, self.base_port, &temp); + let node_config = node_config( + self.nodes, + &self.chain_spec, + Role::Light, + task_executor.clone(), + None, + self.base_port, + &temp, + ); let addr = node_config.network.listen_addresses.iter().next().unwrap().clone(); let service = SyncService::from(light(node_config).expect("Error creating test node service")); diff --git a/client/state-db/Cargo.toml b/client/state-db/Cargo.toml index 5b30a2230ac1b..ee9bbf7273e63 100644 --- a/client/state-db/Cargo.toml +++ b/client/state-db/Cargo.toml @@ -16,7 +16,7 @@ parking_lot = "0.10.0" log = "0.4.8" sc-client-api = { version = "2.0.0-rc3", path = "../api" } sp-core = { version = "2.0.0-rc3", path = "../../primitives/core" } -codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } parity-util-mem = { version = "0.6.1", default-features = false, features = ["primitive-types"] } parity-util-mem-derive = "0.1.0" diff --git a/client/tracing/Cargo.toml b/client/tracing/Cargo.toml index bc402442b93af..c4345648eff30 100644 --- a/client/tracing/Cargo.toml +++ b/client/tracing/Cargo.toml @@ -15,10 +15,12 @@ targets = ["x86_64-unknown-linux-gnu"] erased-serde = "0.3.9" log = { version = "0.4.8" } parking_lot = "0.10.0" +rustc-hash = "1.1.0" serde = "1.0.101" serde_json = "1.0.41" slog = { version = "2.5.2", features = ["nested-values"] } tracing-core = "0.1.7" +sp-tracing = { version = "2.0.0-rc2", path = "../../primitives/tracing" } sc-telemetry = { version = "2.0.0-rc3", path = "../telemetry" } diff --git a/client/tracing/src/lib.rs b/client/tracing/src/lib.rs index d450700ed3c49..c62b8d5b1e9c9 100644 --- a/client/tracing/src/lib.rs +++ b/client/tracing/src/lib.rs @@ -24,7 +24,7 @@ //! //! Currently we provide `Log` (default), `Telemetry` variants for `Receiver` -use std::collections::HashMap; +use rustc_hash::FxHashMap; use std::fmt; use std::sync::atomic::{AtomicU64, Ordering}; use std::time::{Duration, Instant}; @@ -38,10 +38,14 @@ use tracing_core::{ Level, metadata::Metadata, span::{Attributes, Id, Record}, - subscriber::Subscriber + subscriber::Subscriber, }; use sc_telemetry::{telemetry, SUBSTRATE_INFO}; +use sp_tracing::proxy::{WASM_NAME_KEY, WASM_TARGET_KEY, WASM_TRACE_IDENTIFIER}; + +const ZERO_DURATION: Duration = Duration::from_nanos(0); +const PROXY_TARGET: &'static str = "sp_tracing::proxy"; /// Used to configure how to receive the metrics #[derive(Debug, Clone)] @@ -58,36 +62,55 @@ impl Default for TracingReceiver { } } +/// A handler for tracing `SpanDatum` +pub trait TraceHandler: Send + Sync { + /// Process a `SpanDatum` + fn process_span(&self, span: SpanDatum); +} + +/// Represents a single instance of a tracing span #[derive(Debug)] -struct SpanDatum { - id: u64, - name: &'static str, - target: &'static str, - level: Level, - line: u32, - start_time: Instant, - overall_time: Duration, - values: Visitor, +pub struct SpanDatum { + pub id: u64, + pub name: String, + pub target: String, + pub level: Level, + pub line: u32, + pub start_time: Instant, + pub overall_time: Duration, + pub values: Visitor, } +/// Holds associated values for a tracing span #[derive(Clone, Debug)] -struct Visitor(Vec<(String, String)>); +pub struct Visitor(FxHashMap); + +impl Visitor { + /// Consume the Visitor, returning the inner FxHashMap + pub fn into_inner(self) -> FxHashMap { + self.0 + } +} impl Visit for Visitor { fn record_i64(&mut self, field: &Field, value: i64) { - self.record_debug(field, &value) + self.0.insert(field.name().to_string(), value.to_string()); } fn record_u64(&mut self, field: &Field, value: u64) { - self.record_debug(field, &value) + self.0.insert(field.name().to_string(), value.to_string()); } fn record_bool(&mut self, field: &Field, value: bool) { - self.record_debug(field, &value) + self.0.insert(field.name().to_string(), value.to_string()); + } + + fn record_str(&mut self, field: &Field, value: &str) { + self.0.insert(field.name().to_string(), value.to_owned()); } fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) { - self.0.push((field.name().to_string(), format!("{:?}",value))); + self.0.insert(field.name().to_string(), format!("{:?}", value)); } } @@ -105,7 +128,7 @@ impl Serialize for Visitor { impl fmt::Display for Visitor { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let values = self.0.iter().map(|(k,v)| format!("{}={}",k,v)).collect::>().join(", "); + let values = self.0.iter().map(|(k, v)| format!("{}={}", k, v)).collect::>().join(", "); write!(f, "{}", values) } } @@ -135,23 +158,50 @@ impl Value for Visitor { pub struct ProfilingSubscriber { next_id: AtomicU64, targets: Vec<(String, Level)>, - receiver: TracingReceiver, - span_data: Mutex>, + trace_handler: Box, + span_data: Mutex>, } impl ProfilingSubscriber { - /// Takes a `Receiver` and a comma separated list of targets, - /// either with a level: "pallet=trace" - /// or without: "pallet". - pub fn new(receiver: TracingReceiver, targets: &str) -> Self { + /// Takes a `TracingReceiver` and a comma separated list of targets, + /// either with a level: "pallet=trace,frame=debug" + /// or without: "pallet,frame" in which case the level defaults to `trace`. + /// wasm_tracing indicates whether to enable wasm traces + pub fn new(receiver: TracingReceiver, targets: &str) -> ProfilingSubscriber { + match receiver { + TracingReceiver::Log => Self::new_with_handler(Box::new(LogTraceHandler), targets), + TracingReceiver::Telemetry => Self::new_with_handler( + Box::new(TelemetryTraceHandler), + targets, + ), + } + } + + /// Allows use of a custom TraceHandler to create a new instance of ProfilingSubscriber. + /// Takes a comma separated list of targets, + /// either with a level, eg: "pallet=trace" + /// or without: "pallet" in which case the level defaults to `trace`. + /// wasm_tracing indicates whether to enable wasm traces + pub fn new_with_handler(trace_handler: Box, targets: &str) + -> ProfilingSubscriber + { let targets: Vec<_> = targets.split(',').map(|s| parse_target(s)).collect(); ProfilingSubscriber { next_id: AtomicU64::new(1), targets, - receiver, - span_data: Mutex::new(HashMap::new()), + trace_handler, + span_data: Mutex::new(FxHashMap::default()), } } + + fn check_target(&self, target: &str, level: &Level) -> bool { + for t in &self.targets { + if target.starts_with(t.0.as_str()) && level <= &t.1 { + return true; + } + } + false + } } // Default to TRACE if no level given or unable to parse Level @@ -173,36 +223,45 @@ fn parse_target(s: &str) -> (String, Level) { impl Subscriber for ProfilingSubscriber { fn enabled(&self, metadata: &Metadata<'_>) -> bool { - for t in &self.targets { - if metadata.target().starts_with(t.0.as_str()) && metadata.level() <= &t.1 { - log::debug!("Enabled target: {}, level: {}", metadata.target(), metadata.level()); - return true; - } else { - log::debug!("Disabled target: {}, level: {}", metadata.target(), metadata.level()); - } + if metadata.target() == PROXY_TARGET || self.check_target(metadata.target(), metadata.level()) { + log::debug!(target: "tracing", "Enabled target: {}, level: {}", metadata.target(), metadata.level()); + true + } else { + log::debug!(target: "tracing", "Disabled target: {}, level: {}", metadata.target(), metadata.level()); + false } - false } fn new_span(&self, attrs: &Attributes<'_>) -> Id { let id = self.next_id.fetch_add(1, Ordering::Relaxed); - let mut values = Visitor(Vec::new()); + let mut values = Visitor(FxHashMap::default()); attrs.record(&mut values); + // If this is a wasm trace, check if target/level is enabled + if let Some(wasm_target) = values.0.get(WASM_TARGET_KEY) { + if !self.check_target(wasm_target, attrs.metadata().level()) { + return Id::from_u64(id); + } + } let span_datum = SpanDatum { id, - name: attrs.metadata().name(), - target: attrs.metadata().target(), + name: attrs.metadata().name().to_owned(), + target: attrs.metadata().target().to_owned(), level: attrs.metadata().level().clone(), line: attrs.metadata().line().unwrap_or(0), start_time: Instant::now(), - overall_time: Duration::from_nanos(0), + overall_time: ZERO_DURATION, values, }; self.span_data.lock().insert(id, span_datum); Id::from_u64(id) } - fn record(&self, _span: &Id, _values: &Record<'_>) {} + fn record(&self, span: &Id, values: &Record<'_>) { + let mut span_data = self.span_data.lock(); + if let Some(s) = span_data.get_mut(&span.into_u64()) { + values.record(&mut s.values); + } + } fn record_follows_from(&self, _span: &Id, _follows: &Id) {} @@ -213,65 +272,89 @@ impl Subscriber for ProfilingSubscriber { let start_time = Instant::now(); if let Some(mut s) = span_data.get_mut(&span.into_u64()) { s.start_time = start_time; - } else { - log::warn!("Tried to enter span {:?} that has already been closed!", span); } } fn exit(&self, span: &Id) { - let mut span_data = self.span_data.lock(); let end_time = Instant::now(); + let mut span_data = self.span_data.lock(); if let Some(mut s) = span_data.get_mut(&span.into_u64()) { s.overall_time = end_time - s.start_time + s.overall_time; } } fn try_close(&self, span: Id) -> bool { - let mut span_data = self.span_data.lock(); - if let Some(data) = span_data.remove(&span.into_u64()) { - self.send_span(data); + let span_datum = { + let mut span_data = self.span_data.lock(); + span_data.remove(&span.into_u64()) + }; + if let Some(mut span_datum) = span_datum { + if span_datum.name == WASM_TRACE_IDENTIFIER { + span_datum.values.0.insert("wasm".to_owned(), "true".to_owned()); + if let Some(n) = span_datum.values.0.remove(WASM_NAME_KEY) { + span_datum.name = n; + } + if let Some(t) = span_datum.values.0.remove(WASM_TARGET_KEY) { + span_datum.target = t; + } + } + if self.check_target(&span_datum.target, &span_datum.level) { + self.trace_handler.process_span(span_datum); + } }; true } } -impl ProfilingSubscriber { - fn send_span(&self, span_datum: SpanDatum) { - match self.receiver { - TracingReceiver::Log => print_log(span_datum), - TracingReceiver::Telemetry => send_telemetry(span_datum), - } +/// TraceHandler for sending span data to the logger +pub struct LogTraceHandler; + +fn log_level(level: Level) -> log::Level { + match level { + Level::TRACE => log::Level::Trace, + Level::DEBUG => log::Level::Debug, + Level::INFO => log::Level::Info, + Level::WARN => log::Level::Warn, + Level::ERROR => log::Level::Error, } } -fn print_log(span_datum: SpanDatum) { - if span_datum.values.0.is_empty() { - log::info!("TRACING: {} {}: {}, line: {}, time: {}", - span_datum.level, - span_datum.target, - span_datum.name, - span_datum.line, - span_datum.overall_time.as_nanos(), - ); - } else { - log::info!("TRACING: {} {}: {}, line: {}, time: {}, {}", - span_datum.level, - span_datum.target, - span_datum.name, - span_datum.line, - span_datum.overall_time.as_nanos(), - span_datum.values - ); +impl TraceHandler for LogTraceHandler { + fn process_span(&self, span_datum: SpanDatum) { + if span_datum.values.0.is_empty() { + log::log!( + log_level(span_datum.level), + "{}: {}, time: {}", + span_datum.target, + span_datum.name, + span_datum.overall_time.as_nanos(), + ); + } else { + log::log!( + log_level(span_datum.level), + "{}: {}, time: {}, {}", + span_datum.target, + span_datum.name, + span_datum.overall_time.as_nanos(), + span_datum.values, + ); + } } } -fn send_telemetry(span_datum: SpanDatum) { - telemetry!(SUBSTRATE_INFO; "tracing.profiling"; - "name" => span_datum.name, - "target" => span_datum.target, - "line" => span_datum.line, - "time" => span_datum.overall_time.as_nanos(), - "values" => span_datum.values - ); +/// TraceHandler for sending span data to telemetry, +/// Please see telemetry documentation for details on how to specify endpoints and +/// set the required telemetry level to activate tracing messages +pub struct TelemetryTraceHandler; + +impl TraceHandler for TelemetryTraceHandler { + fn process_span(&self, span_datum: SpanDatum) { + telemetry!(SUBSTRATE_INFO; "tracing.profiling"; + "name" => span_datum.name, + "target" => span_datum.target, + "line" => span_datum.line, + "time" => span_datum.overall_time.as_nanos(), + "values" => span_datum.values + ); + } } - diff --git a/client/transaction-pool/Cargo.toml b/client/transaction-pool/Cargo.toml index dce8ce48d25cc..e837f40a34c7b 100644 --- a/client/transaction-pool/Cargo.toml +++ b/client/transaction-pool/Cargo.toml @@ -12,7 +12,7 @@ description = "Substrate transaction pool implementation." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } derive_more = "0.99.2" futures = { version = "0.3.1", features = ["compat"] } futures-diagnose = "1.0" diff --git a/client/transaction-pool/graph/Cargo.toml b/client/transaction-pool/graph/Cargo.toml index e174b3198880b..cb16af0f53d81 100644 --- a/client/transaction-pool/graph/Cargo.toml +++ b/client/transaction-pool/graph/Cargo.toml @@ -28,7 +28,7 @@ linked-hash-map = "0.5.2" [dev-dependencies] assert_matches = "1.3.0" -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } substrate-test-runtime = { version = "2.0.0-rc3", path = "../../../test-utils/runtime" } criterion = "0.3" diff --git a/client/transaction-pool/graph/src/lib.rs b/client/transaction-pool/graph/src/lib.rs index b4646c6055bf6..bf220ce22973a 100644 --- a/client/transaction-pool/graph/src/lib.rs +++ b/client/transaction-pool/graph/src/lib.rs @@ -32,6 +32,7 @@ mod pool; mod ready; mod rotator; mod validated_pool; +mod tracked_map; pub mod base_pool; pub mod watcher; diff --git a/client/transaction-pool/graph/src/ready.rs b/client/transaction-pool/graph/src/ready.rs index b5807ffce4480..47289f26f02a9 100644 --- a/client/transaction-pool/graph/src/ready.rs +++ b/client/transaction-pool/graph/src/ready.rs @@ -25,15 +25,17 @@ use std::{ use serde::Serialize; use log::trace; -use parking_lot::RwLock; use sp_runtime::traits::Member; use sp_runtime::transaction_validity::{ TransactionTag as Tag, }; use sp_transaction_pool::error; -use crate::future::WaitingTransaction; -use crate::base_pool::Transaction; +use crate::{ + base_pool::Transaction, + future::WaitingTransaction, + tracked_map::{self, ReadOnlyTrackedMap, TrackedMap}, +}; /// An in-pool transaction reference. /// @@ -113,11 +115,17 @@ pub struct ReadyTransactions { /// tags that are provided by Ready transactions provided_tags: HashMap, /// Transactions that are ready (i.e. don't have any requirements external to the pool) - ready: Arc>>>, + ready: TrackedMap>, /// Best transactions that are ready to be included to the block without any other previous transaction. best: BTreeSet>, } +impl tracked_map::Size for ReadyTx { + fn size(&self) -> usize { + self.transaction.transaction.bytes + } +} + impl Default for ReadyTransactions { fn default() -> Self { ReadyTransactions { @@ -468,18 +476,18 @@ impl ReadyTransactions { /// Returns number of transactions in this queue. pub fn len(&self) -> usize { - self.ready.read().len() + self.ready.len() } /// Returns sum of encoding lengths of all transactions in this queue. pub fn bytes(&self) -> usize { - self.ready.read().values().fold(0, |acc, tx| acc + tx.transaction.transaction.bytes) + self.ready.bytes() } } /// Iterator of ready transactions ordered by priority. pub struct BestIterator { - all: Arc>>>, + all: ReadOnlyTrackedMap>, awaiting: HashMap)>, best: BTreeSet>, } diff --git a/client/transaction-pool/graph/src/tracked_map.rs b/client/transaction-pool/graph/src/tracked_map.rs new file mode 100644 index 0000000000000..c799eb0b96ea1 --- /dev/null +++ b/client/transaction-pool/graph/src/tracked_map.rs @@ -0,0 +1,189 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use std::{ + collections::HashMap, + sync::{Arc, atomic::{AtomicIsize, Ordering as AtomicOrdering}}, +}; +use parking_lot::{RwLock, RwLockWriteGuard, RwLockReadGuard}; + +/// Something that can report it's size. +pub trait Size { + fn size(&self) -> usize; +} + +/// Map with size tracking. +/// +/// Size reported might be slightly off and only approximately true. +#[derive(Debug, parity_util_mem::MallocSizeOf)] +pub struct TrackedMap { + index: Arc>>, + bytes: AtomicIsize, + length: AtomicIsize, +} + +impl Default for TrackedMap { + fn default() -> Self { + Self { + index: Arc::new(HashMap::default().into()), + bytes: 0.into(), + length: 0.into(), + } + } +} + +impl TrackedMap { + /// Current tracked length of the content. + pub fn len(&self) -> usize { + std::cmp::max(self.length.load(AtomicOrdering::Relaxed), 0) as usize + } + + /// Current sum of content length. + pub fn bytes(&self) -> usize { + std::cmp::max(self.bytes.load(AtomicOrdering::Relaxed), 0) as usize + } + + /// Read-only clone of the interior. + pub fn clone(&self) -> ReadOnlyTrackedMap { + ReadOnlyTrackedMap(self.index.clone()) + } + + /// Lock map for read. + pub fn read<'a>(&'a self) -> TrackedMapReadAccess<'a, K, V> { + TrackedMapReadAccess { + inner_guard: self.index.read(), + } + } + + /// Lock map for write. + pub fn write<'a>(&'a self) -> TrackedMapWriteAccess<'a, K, V> { + TrackedMapWriteAccess { + inner_guard: self.index.write(), + bytes: &self.bytes, + length: &self.length, + } + } +} + +/// Read-only access to map. +/// +/// The only thing can be done is .read(). +pub struct ReadOnlyTrackedMap(Arc>>); + +impl ReadOnlyTrackedMap +where + K: Eq + std::hash::Hash +{ + /// Lock map for read. + pub fn read<'a>(&'a self) -> TrackedMapReadAccess<'a, K, V> { + TrackedMapReadAccess { + inner_guard: self.0.read(), + } + } +} + +pub struct TrackedMapReadAccess<'a, K, V> { + inner_guard: RwLockReadGuard<'a, HashMap>, +} + +impl<'a, K, V> TrackedMapReadAccess<'a, K, V> +where + K: Eq + std::hash::Hash +{ + /// Returns true if map contains key. + pub fn contains_key(&self, key: &K) -> bool { + self.inner_guard.contains_key(key) + } + + /// Returns reference to the contained value by key, if exists. + pub fn get(&self, key: &K) -> Option<&V> { + self.inner_guard.get(key) + } + + /// Returns iterator over all values. + pub fn values(&self) -> std::collections::hash_map::Values { + self.inner_guard.values() + } +} + +pub struct TrackedMapWriteAccess<'a, K, V> { + bytes: &'a AtomicIsize, + length: &'a AtomicIsize, + inner_guard: RwLockWriteGuard<'a, HashMap>, +} + +impl<'a, K, V> TrackedMapWriteAccess<'a, K, V> +where + K: Eq + std::hash::Hash, V: Size +{ + /// Insert value and return previous (if any). + pub fn insert(&mut self, key: K, val: V) -> Option { + let new_bytes = val.size(); + self.bytes.fetch_add(new_bytes as isize, AtomicOrdering::Relaxed); + self.length.fetch_add(1, AtomicOrdering::Relaxed); + self.inner_guard.insert(key, val).and_then(|old_val| { + self.bytes.fetch_sub(old_val.size() as isize, AtomicOrdering::Relaxed); + self.length.fetch_sub(1, AtomicOrdering::Relaxed); + Some(old_val) + }) + } + + /// Remove value by key. + pub fn remove(&mut self, key: &K) -> Option { + let val = self.inner_guard.remove(key); + if let Some(size) = val.as_ref().map(Size::size) { + self.bytes.fetch_sub(size as isize, AtomicOrdering::Relaxed); + self.length.fetch_sub(1, AtomicOrdering::Relaxed); + } + val + } + + /// Returns mutable reference to the contained value by key, if exists. + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { + self.inner_guard.get_mut(key) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + impl Size for i32 { + fn size(&self) -> usize { *self as usize / 10 } + } + + #[test] + fn basic() { + let map = TrackedMap::default(); + map.write().insert(5, 10); + map.write().insert(6, 20); + + assert_eq!(map.bytes(), 3); + assert_eq!(map.len(), 2); + + map.write().insert(6, 30); + + assert_eq!(map.bytes(), 4); + assert_eq!(map.len(), 2); + + map.write().remove(&6); + assert_eq!(map.bytes(), 1); + assert_eq!(map.len(), 1); + } +} \ No newline at end of file diff --git a/client/transaction-pool/src/api.rs b/client/transaction-pool/src/api.rs index 79315c2724b5b..10ac4aa46960d 100644 --- a/client/transaction-pool/src/api.rs +++ b/client/transaction-pool/src/api.rs @@ -64,7 +64,7 @@ where Client: ProvideRuntimeApi + BlockBackend + BlockIdTo, Client: Send + Sync + 'static, Client::Api: TaggedTransactionQueue, - sp_api::ApiErrorFor: Send, + sp_api::ApiErrorFor: Send + std::fmt::Display, { type Block = Block; type Error = error::Error; @@ -87,29 +87,15 @@ where let client = self.client.clone(); let at = at.clone(); - self.pool.spawn_ok(futures_diagnose::diagnose("validate-transaction", async move { - sp_tracing::enter_span!("validate_transaction"); - let runtime_api = client.runtime_api(); - let has_v2 = sp_tracing::tracing_span! { "check_version"; - runtime_api - .has_api_with::, _>( - &at, |v| v >= 2, - ) - .unwrap_or_default() - }; - - sp_tracing::enter_span!("runtime::validate_transaction"); - let res = if has_v2 { - runtime_api.validate_transaction(&at, source, uxt) - } else { - #[allow(deprecated)] // old validate_transaction - runtime_api.validate_transaction_before_version_2(&at, uxt) - }; - let res = res.map_err(|e| Error::RuntimeApi(format!("{:?}", e))); - if let Err(e) = tx.send(res) { - log::warn!("Unable to send a validate transaction result: {:?}", e); - } - })); + self.pool.spawn_ok(futures_diagnose::diagnose( + "validate-transaction", + async move { + let res = validate_transaction_blocking(&*client, &at, source, uxt); + if let Err(e) = tx.send(res) { + log::warn!("Unable to send a validate transaction result: {:?}", e); + } + }, + )); Box::pin(async move { match rx.await { @@ -143,6 +129,62 @@ where } } +/// Helper function to validate a transaction using a full chain API. +/// This method will call into the runtime to perform the validation. +fn validate_transaction_blocking( + client: &Client, + at: &BlockId, + source: TransactionSource, + uxt: sc_transaction_graph::ExtrinsicFor>, +) -> error::Result +where + Block: BlockT, + Client: ProvideRuntimeApi + BlockBackend + BlockIdTo, + Client: Send + Sync + 'static, + Client::Api: TaggedTransactionQueue, + sp_api::ApiErrorFor: Send + std::fmt::Display, +{ + sp_tracing::enter_span!("validate_transaction"); + let runtime_api = client.runtime_api(); + let has_v2 = sp_tracing::tracing_span! { "check_version"; + runtime_api + .has_api_with::, _>(&at, |v| v >= 2) + .unwrap_or_default() + }; + + sp_tracing::enter_span!("runtime::validate_transaction"); + let res = if has_v2 { + runtime_api.validate_transaction(&at, source, uxt) + } else { + #[allow(deprecated)] // old validate_transaction + runtime_api.validate_transaction_before_version_2(&at, uxt) + }; + + res.map_err(|e| Error::RuntimeApi(e.to_string())) +} + +impl FullChainApi +where + Block: BlockT, + Client: ProvideRuntimeApi + BlockBackend + BlockIdTo, + Client: Send + Sync + 'static, + Client::Api: TaggedTransactionQueue, + sp_api::ApiErrorFor: Send + std::fmt::Display, +{ + /// Validates a transaction by calling into the runtime, same as + /// `validate_transaction` but blocks the current thread when performing + /// validation. Only implemented for `FullChainApi` since we can call into + /// the runtime locally. + pub fn validate_transaction_blocking( + &self, + at: &BlockId, + source: TransactionSource, + uxt: sc_transaction_graph::ExtrinsicFor, + ) -> error::Result { + validate_transaction_blocking(&*self.client, at, source, uxt) + } +} + /// The transaction pool logic for light client. pub struct LightChainApi { client: Arc, diff --git a/client/transaction-pool/src/lib.rs b/client/transaction-pool/src/lib.rs index ad238e6241183..ea8b4bf9dec81 100644 --- a/client/transaction-pool/src/lib.rs +++ b/client/transaction-pool/src/lib.rs @@ -34,8 +34,8 @@ pub mod testing; pub use sc_transaction_graph as txpool; pub use crate::api::{FullChainApi, LightChainApi}; -use std::{collections::HashMap, sync::Arc, pin::Pin}; -use futures::{prelude::*, future::{ready, self}, channel::oneshot}; +use std::{collections::{HashMap, HashSet}, sync::Arc, pin::Pin}; +use futures::{prelude::*, future::{self, ready}, channel::oneshot}; use parking_lot::Mutex; use sp_runtime::{ @@ -47,7 +47,7 @@ use sp_transaction_pool::{ TransactionStatusStreamFor, MaintainedTransactionPool, PoolFuture, ChainEvent, TransactionSource, }; -use sc_transaction_graph::ChainApi; +use sc_transaction_graph::{ChainApi, ExtrinsicHash}; use wasm_timer::Instant; use prometheus_endpoint::Registry as PrometheusRegistry; @@ -331,6 +331,7 @@ impl TransactionPool for BasicPool fn ready_at(&self, at: NumberFor) -> PolledIterator { if self.ready_poll.lock().updated_at() >= at { + log::trace!(target: "txpool", "Transaction pool already processed block #{}", at); let iterator: ReadyIteratorFor = Box::new(self.pool.validated_pool().ready()); return Box::pin(futures::future::ready(iterator)); } @@ -351,6 +352,59 @@ impl TransactionPool for BasicPool } } +impl sp_transaction_pool::LocalTransactionPool + for BasicPool, Block> +where + Block: BlockT, + Client: sp_api::ProvideRuntimeApi + + sc_client_api::BlockBackend + + sp_runtime::traits::BlockIdTo, + Client: Send + Sync + 'static, + Client::Api: sp_transaction_pool::runtime_api::TaggedTransactionQueue, + sp_api::ApiErrorFor: Send + std::fmt::Display, +{ + type Block = Block; + type Hash = sc_transaction_graph::ExtrinsicHash>; + type Error = as ChainApi>::Error; + + fn submit_local( + &self, + at: &BlockId, + xt: sp_transaction_pool::LocalTransactionFor, + ) -> Result { + use sc_transaction_graph::ValidatedTransaction; + use sp_runtime::traits::SaturatedConversion; + use sp_runtime::transaction_validity::TransactionValidityError; + + let validity = self + .api + .validate_transaction_blocking(at, TransactionSource::Local, xt.clone())? + .map_err(|e| { + Self::Error::Pool(match e { + TransactionValidityError::Invalid(i) => i.into(), + TransactionValidityError::Unknown(u) => u.into(), + }) + })?; + + let (hash, bytes) = self.pool.validated_pool().api().hash_and_length(&xt); + let block_number = self + .api + .block_id_to_number(at)? + .ok_or_else(|| error::Error::BlockIdConversion(format!("{:?}", at)))?; + + let validated = ValidatedTransaction::valid_at( + block_number.saturated_into::(), + hash.clone(), + TransactionSource::Local, + xt, + bytes, + validity, + ); + + self.pool.validated_pool().submit(vec![validated]).remove(0) + } +} + #[cfg_attr(test, derive(Debug))] enum RevalidationStatus { /// The revalidation has never been completed. @@ -440,12 +494,7 @@ async fn prune_known_txs_for_block>( block_id: BlockId, api: &Api, pool: &sc_transaction_graph::Pool, -) { - // We don't query block if we won't prune anything - if pool.validated_pool().status().is_empty() { - return; - } - +) -> Vec> { let hashes = api.block_body(&block_id).await .unwrap_or_else(|e| { log::warn!("Prune known transactions: error request {:?}!", e); @@ -456,9 +505,13 @@ async fn prune_known_txs_for_block>( .map(|tx| pool.hash_of(&tx)) .collect::>(); + log::trace!(target: "txpool", "Pruning transactions: {:?}", hashes); + if let Err(e) = pool.prune_known(&block_id, &hashes) { log::error!("Cannot prune known in the pool {:?}!", e); } + + hashes } impl MaintainedTransactionPool for BasicPool @@ -493,11 +546,24 @@ impl MaintainedTransactionPool for BasicPool let revalidation_strategy = self.revalidation_strategy.clone(); let revalidation_queue = self.revalidation_queue.clone(); let ready_poll = self.ready_poll.clone(); + let metrics = self.metrics.clone(); async move { + // We keep track of everything we prune so that later we won't add + // tranactions with those hashes from the retracted blocks. + let mut pruned_log = HashSet::>::new(); + // If there is a tree route, we use this to prune known tx based on the enacted - // blocks. + // blocks. Before pruning enacted transactions, we inform the listeners about + // retracted blocks and their transactions. This order is important, because + // if we enact and retract the same transaction at the same time, we want to + // send first the retract and than the prune event. if let Some(ref tree_route) = tree_route { + for retracted in tree_route.retracted() { + // notify txs awaiting finality that it has been retracted + pool.validated_pool().on_block_retracted(retracted.hash.clone()); + } + future::join_all( tree_route .enacted() @@ -509,23 +575,26 @@ impl MaintainedTransactionPool for BasicPool &*pool, ), ), - ).await; + ).await.into_iter().for_each(|enacted_log|{ + pruned_log.extend(enacted_log); + }) } // If this is a new best block, we need to prune its transactions from the pool. if is_new_best { - prune_known_txs_for_block(id.clone(), &*api, &*pool).await; + pruned_log.extend(prune_known_txs_for_block(id.clone(), &*api, &*pool).await); } + metrics.report( + |metrics| metrics.block_transactions_pruned.inc_by(pruned_log.len() as u64) + ); + if let (true, Some(tree_route)) = (next_action.resubmit, tree_route) { let mut resubmit_transactions = Vec::new(); for retracted in tree_route.retracted() { let hash = retracted.hash.clone(); - // notify txs awaiting finality that it has been retracted - pool.validated_pool().on_block_retracted(hash.clone()); - let block_transactions = api.block_body(&BlockId::hash(hash)) .await .unwrap_or_else(|e| { @@ -536,7 +605,31 @@ impl MaintainedTransactionPool for BasicPool .into_iter() .filter(|tx| tx.is_signed().unwrap_or(true)); - resubmit_transactions.extend(block_transactions); + let mut resubmitted_to_report = 0; + + resubmit_transactions.extend( + block_transactions.into_iter().filter(|tx| { + let tx_hash = pool.hash_of(&tx); + let contains = pruned_log.contains(&tx_hash); + + // need to count all transactions, not just filtered, here + resubmitted_to_report += 1; + + if !contains { + log::debug!( + target: "txpool", + "[{:?}]: Resubmitting from retracted block {:?}", + tx_hash, + hash, + ); + } + !contains + }) + ); + + metrics.report( + |metrics| metrics.block_transactions_resubmitted.inc_by(resubmitted_to_report) + ); } if let Err(e) = pool.submit_at( @@ -590,3 +683,23 @@ impl MaintainedTransactionPool for BasicPool } } } + +/// Inform the transaction pool about imported and finalized blocks. +pub async fn notification_future( + client: Arc, + txpool: Arc +) + where + Block: BlockT, + Client: sc_client_api::BlockchainEvents, + Pool: MaintainedTransactionPool, +{ + let import_stream = client.import_notification_stream().map(Into::into).fuse(); + let finality_stream = client.finality_notification_stream() + .map(Into::into) + .fuse(); + + futures::stream::select(import_stream, finality_stream) + .for_each(|evt| txpool.maintain(evt)) + .await +} diff --git a/client/transaction-pool/src/metrics.rs b/client/transaction-pool/src/metrics.rs index e377b2fe8294c..d5a10dfd6f4bd 100644 --- a/client/transaction-pool/src/metrics.rs +++ b/client/transaction-pool/src/metrics.rs @@ -48,6 +48,8 @@ pub struct Metrics { pub validations_scheduled: Counter, pub validations_finished: Counter, pub validations_invalid: Counter, + pub block_transactions_pruned: Counter, + pub block_transactions_resubmitted: Counter, } impl Metrics { @@ -74,6 +76,20 @@ impl Metrics { )?, registry, )?, + block_transactions_pruned: register( + Counter::new( + "sub_txpool_block_transactions_pruned", + "Total number of transactions that was requested to be pruned by block events", + )?, + registry, + )?, + block_transactions_resubmitted: register( + Counter::new( + "sub_txpool_block_transactions_resubmitted", + "Total number of transactions that was requested to be resubmitted by block events", + )?, + registry, + )?, }) } } diff --git a/client/transaction-pool/src/revalidation.rs b/client/transaction-pool/src/revalidation.rs index 05a2076c6659e..cb49560662c85 100644 --- a/client/transaction-pool/src/revalidation.rs +++ b/client/transaction-pool/src/revalidation.rs @@ -34,7 +34,7 @@ const BACKGROUND_REVALIDATION_INTERVAL: Duration = Duration::from_millis(200); #[cfg(test)] pub const BACKGROUND_REVALIDATION_INTERVAL: Duration = Duration::from_millis(1); -const BACKGROUND_REVALIDATION_BATCH_SIZE: usize = 20; +const MIN_BACKGROUND_REVALIDATION_BATCH_SIZE: usize = 20; /// Payload from queue to worker. struct WorkerPayload { @@ -68,13 +68,17 @@ async fn batch_revalidate( let mut invalid_hashes = Vec::new(); let mut revalidated = HashMap::new(); - for ext_hash in batch { - let ext = match pool.validated_pool().ready_by_hash(&ext_hash) { - Some(ext) => ext, - None => continue, - }; - - match api.validate_transaction(&BlockId::Number(at), ext.source, ext.data.clone()).await { + let validation_results = futures::future::join_all( + batch.into_iter().filter_map(|ext_hash| { + pool.validated_pool().ready_by_hash(&ext_hash).map(|ext| { + api.validate_transaction(&BlockId::Number(at), ext.source, ext.data.clone()) + .map(move |validation_result| (validation_result, ext_hash, ext)) + }) + }) + ).await; + + for (validation_result, ext_hash, ext) in validation_results { + match validation_result { Ok(Err(TransactionValidityError::Invalid(err))) => { log::debug!(target: "txpool", "[{:?}]: Revalidation: invalid {:?}", ext_hash, err); invalid_hashes.push(ext_hash); @@ -131,7 +135,7 @@ impl RevalidationWorker { fn prepare_batch(&mut self) -> Vec> { let mut queued_exts = Vec::new(); - let mut left = BACKGROUND_REVALIDATION_BATCH_SIZE; + let mut left = std::cmp::max(MIN_BACKGROUND_REVALIDATION_BATCH_SIZE, self.members.len() / 4); // Take maximum of count transaction by order // which they got into the pool diff --git a/client/transaction-pool/src/testing/pool.rs b/client/transaction-pool/src/testing/pool.rs index 0f0c0004893e9..61aba5efe3bfa 100644 --- a/client/transaction-pool/src/testing/pool.rs +++ b/client/transaction-pool/src/testing/pool.rs @@ -281,6 +281,25 @@ fn should_resubmit_from_retracted_during_maintenance() { assert_eq!(pool.status().ready, 1); } + +#[test] +fn should_not_resubmit_from_retracted_during_maintenance_if_tx_is_also_in_enacted() { + let xt = uxt(Alice, 209); + + let (pool, _guard, _notifier) = maintained_pool(); + + block_on(pool.submit_one(&BlockId::number(0), SOURCE, xt.clone())).expect("1. Imported"); + assert_eq!(pool.status().ready, 1); + + let header = pool.api.push_block(1, vec![xt.clone()]); + let fork_header = pool.api.push_block(1, vec![xt]); + + let event = block_event_with_retracted(header, fork_header.hash(), &*pool.api); + + block_on(pool.maintain(event)); + assert_eq!(pool.status().ready, 0); +} + #[test] fn should_not_retain_invalid_hashes_from_retracted() { let xt = uxt(Alice, 209); @@ -659,6 +678,66 @@ fn fork_aware_finalization() { } } +/// Tests that when pruning and retracing a tx by the same event, we generate +/// the correct events in the correct order. +#[test] +fn prune_and_retract_tx_at_same_time() { + let api = TestApi::empty(); + // starting block A1 (last finalized.) + api.push_block(1, vec![]); + + let (pool, _background, _) = BasicPool::new_test(api.into()); + + let from_alice = uxt(Alice, 1); + pool.api.increment_nonce(Alice.into()); + + let watcher = block_on( + pool.submit_and_watch(&BlockId::number(1), SOURCE, from_alice.clone()) + ).expect("1. Imported"); + + // Block B1 + let b1 = { + let header = pool.api.push_block(2, vec![from_alice.clone()]); + assert_eq!(pool.status().ready, 1); + + let event = ChainEvent::NewBlock { + hash: header.hash(), + is_new_best: true, + header: header.clone(), + tree_route: None, + }; + block_on(pool.maintain(event)); + assert_eq!(pool.status().ready, 0); + header.hash() + }; + + // Block B2 + let b2 = { + let header = pool.api.push_block(2, vec![from_alice.clone()]); + assert_eq!(pool.status().ready, 0); + + let event = block_event_with_retracted(header.clone(), b1, &*pool.api); + block_on(pool.maintain(event)); + assert_eq!(pool.status().ready, 0); + + let event = ChainEvent::Finalized { hash: header.hash() }; + block_on(pool.maintain(event)); + + header.hash() + }; + + { + let mut stream = futures::executor::block_on_stream(watcher); + assert_eq!(stream.next(), Some(TransactionStatus::Ready)); + assert_eq!(stream.next(), Some(TransactionStatus::InBlock(b1.clone()))); + assert_eq!(stream.next(), Some(TransactionStatus::Retracted(b1))); + assert_eq!(stream.next(), Some(TransactionStatus::InBlock(b2.clone()))); + assert_eq!(stream.next(), Some(TransactionStatus::Finalized(b2))); + assert_eq!(stream.next(), None); + } +} + + /// This test ensures that transactions from a fork are re-submitted if /// the forked block is not part of the retracted blocks. This happens as the /// retracted block list only contains the route from the old best to the new diff --git a/docs/CONTRIBUTING.adoc b/docs/CONTRIBUTING.adoc index b573aef50d874..ec747d6693bc5 100644 --- a/docs/CONTRIBUTING.adoc +++ b/docs/CONTRIBUTING.adoc @@ -19,17 +19,30 @@ There are a few basic ground-rules for contributors (including the maintainer(s) == Merge Process -Merging pull requests once CI is successful: +*In General* -. A PR needs to be reviewed and approved by project maintainers unless: - - it does not alter any logic (e.g. comments, dependencies, docs), then it may be tagged https://github.com/paritytech/substrate/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+label%3AA2-insubstantial[`insubstantial`] and merged by its author once CI is complete. - - it is an urgent fix with no large change to logic, then it may be merged after a non-author contributor has approved the review once CI is complete. +A PR needs to be reviewed and approved by project maintainers unless: +- it does not alter any logic (e.g. comments, dependencies, docs), then it may be tagged https://github.com/paritytech/substrate/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aopen+label%3AA2-insubstantial[`insubstantial`] and merged by its author once CI is complete. +- it is an urgent fix with no large change to logic, then it may be merged after a non-author contributor has approved the review once CI is complete. + +*Labels TLDR:* + +- `A-*` Pull request status. ONE REQUIRED. +- `B-*` Changelog and/or Runtime-upgrade post composition markers. ONE REQUIRED. (used by automation) +- `C-*` Release notes release-priority markers. EXACTLY ONE REQUIRED. (used by automation) +- `D-*` More general tags on the PR denoting various implications and requirements. + +*Process:* + +. Please tag each PR with exactly one `A`, `B` and `C` label at the minimum. . Once a PR is ready for review please add the https://github.com/paritytech/substrate/pulls?q=is%3Apr+is%3Aopen+label%3AA0-pleasereview[`A0-pleasereview`] label. Generally PRs should sit with this label for 48 hours in order to garner feedback. It may be merged before if all relevant parties had a look at it. -. If the first review is not an approval, swap `A0-pleasereview` to any label `[A3, A7]` to indicate that the PR has received some feedback, but needs further work. For example. https://github.com/paritytech/substrate/labels/A3-inprogress[`A3-inprogress`] is a general indicator that the PR is work in progress and https://github.com/paritytech/substrate/labels/A4-gotissues[`A4-gotissues`] means that it has significant problems that need fixing. Once the work is done, change the label back to `A0-pleasereview`. You might end up swapping a few times back and forth to climb up the A label group. Once a PR is https://github.com/paritytech/substrate/labels/A8-mergeoncegreen[`A8-mergeoncegreen`], it is ready to merge. +. If the first review is not an approval, swap `A0-pleasereview` to any label `[A3, A7]` to indicate that the PR has received some feedback, but needs further work. For example. https://github.com/paritytech/substrate/labels/A3-inprogress[`A3-inprogress`] is a general indicator that the PR is work in progress and https://github.com/paritytech/substrate/labels/A4-gotissues[`A4-gotissues`] means that it has significant problems that need fixing. Once the work is done, change the label back to `A0-pleasereview`. You might end up swapping a few times back and forth to climb up the A label group. Once a PR is https://github.com/paritytech/substrate/labels/A8-mergeoncegreen[`A8-mergeoncegreen`], it is ready to merge. . PRs must be tagged with respect to _release notes_ with https://github.com/paritytech/substrate/labels/B0-silent[`B0-silent`] and `B1-..`. The former indicates that no changes should be mentioned in any release notes. The latter indicates that the changes should be reported in the corresponding release note -. PRs that break the external API must be tagged with https://github.com/paritytech/substrate/labels/B2-breaksapi[`B2-breaksapi`], when it changes the FRAME or consensus of running system with https://github.com/paritytech/substrate/labels/B3-breaksconsensus[`B3-breaksconsensus`] -. No PR should be merged until all reviews' comments are addressed. +. PRs that break the external API must be tagged with https://github.com/paritytech/substrate/labels/D2-breaksapi[`D2-breaksapi`], when it changes the FRAME or consensus of running system with https://github.com/paritytech/substrate/labels/B3-breaksconsensus[`B3-breaksconsensus`]. +. PRs should be labeled with their release importance via the `C1-C9`. +. PRs should be categorized into projects. +. No PR should be merged until all reviews' comments are addressed and CI is successful. *Reviewing pull requests*: @@ -49,19 +62,19 @@ When reviewing a pull request, the end-goal is to suggest useful changes to the === Updating Polkadot as well -**All pull requests will be checked agains either Polkadot master, or your provided Polkadot companion PR**. That is, If your PR changes the external APIs or interfaces used by Polkadot. If you tagged the PR with `breaksapi` or `breaksconsensus` this is most certainly the case, in all other cases check for it by running step 1 below. +**All pull requests will be checked against either Polkadot master, or your provided Polkadot companion PR**. That is, If your PR changes the external APIs or interfaces used by Polkadot. If you tagged the PR with `breaksapi` or `breaksconsensus` this is most certainly the case, in all other cases check for it by running step 1 below. To create a Polkadot companion PR: . Pull latest Polkadot master (or clone it, if you haven't yet). . Override your local cargo config to point to your local substrate (pointing to your WIP branch): place `paths = ["path/to/substrate"]` in `~/.cargo/config`. . Make the changes required and build polkadot locally. -. Submit all this as a PR against the Polkadot Repo. Link to your Polkadot PR in the _description_ of your Substrate PR as "polkadot companion: [URL]" OR use the same name for your Polkdadot branch as the Substrate branch. +. Submit all this as a PR against the Polkadot Repo. Link to your Polkadot PR in the _description_ of your Substrate PR as "polkadot companion: [URL]" OR use the same name for your Polkdadot branch as the Substrate branch. . Now you should see that the `check_polkadot` CI job will build your Substrate PR agains the mentioned Polkadot branch in your PR description. . Wait for reviews on both -. Once both PRs have been green lit, they can both be merged 🍻. +. Once both PRs have been green lit, they can both be merged 🍻. -If your PR is reviewed well, but a Polkadot PR is missing, signal it with https://github.com/paritytech/substrate/labels/A7-needspolkadotpr[`A7-needspolkadotpr`] to prevent it from getting automatically merged. +If your PR is reviewed well, but a Polkadot PR is missing, signal it with https://github.com/paritytech/substrate/labels/A7-needspolkadotpr[`A7-needspolkadotpr`] to prevent it from getting automatically merged. As there might be multiple pending PRs that might conflict with one another, a) you should not merge the substrate PR until the Polkadot PR has also been reviewed and b) both should be merged pretty quickly after another to not block others. @@ -69,6 +82,14 @@ As there might be multiple pending PRs that might conflict with one another, a) We use https://github.com/paritytech/substrate/labels[labels] to manage PRs and issues and communicate state of a PR. Please familiarize yourself with them. Furthermore we are organizing issues in https://github.com/paritytech/substrate/milestones[milestones]. Best way to get started is to a pick a ticket from the current milestone tagged https://github.com/paritytech/substrate/issues?q=is%3Aissue+is%3Aopen+label%3AQ2-easy[`easy`] or https://github.com/paritytech/substrate/issues?q=is%3Aissue+is%3Aopen+label%3AQ3-medium[`medium`] and get going or https://github.com/paritytech/substrate/issues?q=is%3Aissue+is%3Aopen+label%3AX1-mentor[`mentor`] and get in contact with the mentor offering their support on that larger task. +== Issues +Please label issues with the following labels: + +. `I-*` Issue severity and type. EXACTLY ONE REQUIRED. +. `P-*` Issue priority. AT MOST ONE ALLOWED. +. `Q-*` Issue difficulty. AT MOST ONE ALLOWED. +. `Z-*` More general tags on the issue, denoting context and resolution. + == Releases Declaring formal releases remains the prerogative of the project maintainer(s). diff --git a/docs/PULL_REQUEST_TEMPLATE.md b/docs/PULL_REQUEST_TEMPLATE.md index fa2c15e6c02fe..8ca6ba9b01fe2 100644 --- a/docs/PULL_REQUEST_TEMPLATE.md +++ b/docs/PULL_REQUEST_TEMPLATE.md @@ -6,17 +6,24 @@ Before you submitting, please check that: - What does it do? - What important points reviewers should know? - Is there something left for follow-up PRs? -- [ ] You labeled the PR with appropriate labels if you have permissions to do so. +- [ ] You labeled the PR appropriately if you have permissions to do so: + - [ ] `A*` for PR status (**one required**) + - [ ] `B*` for changelog (**one required**) + - [ ] `C*` for release notes (**exactly one required**) + - [ ] `D*` for various implications/requirements + - [ ] Github's project assignment - [ ] You mentioned a related issue if this PR related to it, e.g. `Fixes #228` or `Related #1337`. - [ ] You asked any particular reviewers to review. If you aren't sure, start with GH suggestions. -- [ ] Your PR adheres [the style guide](https://wiki.parity.io/Substrate-Style-Guide) - - In particular, mind the maximal line length. +- [ ] Your PR adheres to [the style guide](https://wiki.parity.io/Substrate-Style-Guide) + - In particular, mind the maximal line length of 100 (120 in exceptional circumstances). - There is no commented code checked in unless necessary. - Any panickers have a proof or removed. - [ ] You bumped the runtime version if there are breaking changes in the **runtime**. - [ ] You updated any rustdocs which may have changed - [ ] Has the PR altered the external API or interfaces used by Polkadot? Do you have the corresponding Polkadot PR ready? +Refer to [the contributing guide](https://github.com/paritytech/substrate/blob/master/docs/CONTRIBUTING.adoc) for details. + After you've read this notice feel free to remove it. Thank you! diff --git a/frame/assets/Cargo.toml b/frame/assets/Cargo.toml index 0039e8898c0e7..33882671a450e 100644 --- a/frame/assets/Cargo.toml +++ b/frame/assets/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } # Needed for various traits. In our case, `OnFinalize`. sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } # Needed for type-safe access to storage DB. diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index f6a042a320a20..257dea0e963de 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -304,6 +304,7 @@ mod tests { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type Call = (); @@ -325,7 +326,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } impl Trait for Test { diff --git a/frame/atomic-swap/Cargo.toml b/frame/atomic-swap/Cargo.toml new file mode 100644 index 0000000000000..ce32d8b783cbc --- /dev/null +++ b/frame/atomic-swap/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "pallet-atomic-swap" +version = "2.0.0-rc3" +authors = ["Parity Technologies "] +edition = "2018" +license = "Apache-2.0" +homepage = "https://substrate.dev" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME atomic swap pallet" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +serde = { version = "1.0.101", optional = true } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } +frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } +frame-system = { version = "2.0.0-rc3", default-features = false, path = "../system" } +sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } +sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } + +[dev-dependencies] +pallet-balances = { version = "2.0.0-rc3", path = "../balances" } + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + "frame-support/std", + "frame-system/std", + "sp-runtime/std", + "sp-std/std", + "sp-io/std", + "sp-core/std", +] diff --git a/frame/atomic-swap/src/lib.rs b/frame/atomic-swap/src/lib.rs new file mode 100644 index 0000000000000..56aa67310fb48 --- /dev/null +++ b/frame/atomic-swap/src/lib.rs @@ -0,0 +1,327 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Atomic Swap +//! +//! A module for atomically sending funds. +//! +//! - [`atomic_swap::Trait`](./trait.Trait.html) +//! - [`Call`](./enum.Call.html) +//! - [`Module`](./struct.Module.html) +//! +//! ## Overview +//! +//! A module for atomically sending funds from an origin to a target. A proof +//! is used to allow the target to approve (claim) the swap. If the swap is not +//! claimed within a specified duration of time, the sender may cancel it. +//! +//! ## Interface +//! +//! ### Dispatchable Functions +//! +//! * `create_swap` - called by a sender to register a new atomic swap +//! * `claim_swap` - called by the target to approve a swap +//! * `cancel_swap` - may be called by a sender after a specified duration + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +mod tests; + +use sp_std::{prelude::*, marker::PhantomData, ops::{Deref, DerefMut}}; +use sp_io::hashing::blake2_256; +use frame_support::{ + Parameter, decl_module, decl_storage, decl_event, decl_error, ensure, + traits::{Get, Currency, ReservableCurrency, BalanceStatus}, + weights::Weight, + dispatch::DispatchResult, +}; +use frame_system::{self as system, ensure_signed}; +use codec::{Encode, Decode}; +use sp_runtime::RuntimeDebug; + +/// Pending atomic swap operation. +#[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode)] +pub struct PendingSwap { + /// Source of the swap. + pub source: T::AccountId, + /// Action of this swap. + pub action: T::SwapAction, + /// End block of the lock. + pub end_block: T::BlockNumber, +} + +/// Hashed proof type. +pub type HashedProof = [u8; 32]; + +/// Definition of a pending atomic swap action. It contains the following three phrases: +/// +/// - **Reserve**: reserve the resources needed for a swap. This is to make sure that **Claim** +/// succeeds with best efforts. +/// - **Claim**: claim any resources reserved in the first phrase. +/// - **Cancel**: cancel any resources reserved in the first phrase. +pub trait SwapAction { + /// Reserve the resources needed for the swap, from the given `source`. The reservation is + /// allowed to fail. If that is the case, the the full swap creation operation is cancelled. + fn reserve(&self, source: &T::AccountId) -> DispatchResult; + /// Claim the reserved resources, with `source` and `target`. Returns whether the claim + /// succeeds. + fn claim(&self, source: &T::AccountId, target: &T::AccountId) -> bool; + /// Weight for executing the operation. + fn weight(&self) -> Weight; + /// Cancel the resources reserved in `source`. + fn cancel(&self, source: &T::AccountId); +} + +/// A swap action that only allows transferring balances. +#[derive(Clone, RuntimeDebug, Eq, PartialEq, Encode, Decode)] +pub struct BalanceSwapAction> { + value: ::AccountId>>::Balance, + _marker: PhantomData, +} + +impl BalanceSwapAction where + C: ReservableCurrency, +{ + /// Create a new swap action value of balance. + pub fn new(value: ::AccountId>>::Balance) -> Self { + Self { value, _marker: PhantomData } + } +} + +impl Deref for BalanceSwapAction where + C: ReservableCurrency, +{ + type Target = ::AccountId>>::Balance; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl DerefMut for BalanceSwapAction where + C: ReservableCurrency, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.value + } +} + +impl SwapAction for BalanceSwapAction where + C: ReservableCurrency, +{ + fn reserve(&self, source: &T::AccountId) -> DispatchResult { + C::reserve(&source, self.value) + } + + fn claim(&self, source: &T::AccountId, target: &T::AccountId) -> bool { + C::repatriate_reserved(source, target, self.value, BalanceStatus::Free).is_ok() + } + + fn weight(&self) -> Weight { + T::DbWeight::get().reads_writes(1, 1) + } + + fn cancel(&self, source: &T::AccountId) { + C::unreserve(source, self.value); + } +} + +/// Atomic swap's pallet configuration trait. +pub trait Trait: frame_system::Trait { + /// The overarching event type. + type Event: From> + Into<::Event>; + /// Swap action. + type SwapAction: SwapAction + Parameter; + /// Limit of proof size. + /// + /// Atomic swap is only atomic if once the proof is revealed, both parties can submit the proofs + /// on-chain. If A is the one that generates the proof, then it requires that either: + /// - A's blockchain has the same proof length limit as B's blockchain. + /// - Or A's blockchain has shorter proof length limit as B's blockchain. + /// + /// If B sees A is on a blockchain with larger proof length limit, then it should kindly refuse + /// to accept the atomic swap request if A generates the proof, and asks that B generates the + /// proof instead. + type ProofLimit: Get; +} + +decl_storage! { + trait Store for Module as AtomicSwap { + pub PendingSwaps: double_map + hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) HashedProof + => Option>; + } +} + +decl_error! { + pub enum Error for Module { + /// Swap already exists. + AlreadyExist, + /// Swap proof is invalid. + InvalidProof, + /// Proof is too large. + ProofTooLarge, + /// Source does not match. + SourceMismatch, + /// Swap has already been claimed. + AlreadyClaimed, + /// Swap does not exist. + NotExist, + /// Claim action mismatch. + ClaimActionMismatch, + /// Duration has not yet passed for the swap to be cancelled. + DurationNotPassed, + } +} + +decl_event!( + /// Event of atomic swap pallet. + pub enum Event where + AccountId = ::AccountId, + PendingSwap = PendingSwap, + { + /// Swap created. + NewSwap(AccountId, HashedProof, PendingSwap), + /// Swap claimed. The last parameter indicates whether the execution succeeds. + SwapClaimed(AccountId, HashedProof, bool), + /// Swap cancelled. + SwapCancelled(AccountId, HashedProof), + } +); + +decl_module! { + /// Module definition of atomic swap pallet. + pub struct Module for enum Call where origin: T::Origin { + type Error = Error; + + fn deposit_event() = default; + + /// Register a new atomic swap, declaring an intention to send funds from origin to target + /// on the current blockchain. The target can claim the fund using the revealed proof. If + /// the fund is not claimed after `duration` blocks, then the sender can cancel the swap. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// - `target`: Receiver of the atomic swap. + /// - `hashed_proof`: The blake2_256 hash of the secret proof. + /// - `balance`: Funds to be sent from origin. + /// - `duration`: Locked duration of the atomic swap. For safety reasons, it is recommended + /// that the revealer uses a shorter duration than the counterparty, to prevent the + /// situation where the revealer reveals the proof too late around the end block. + #[weight = T::DbWeight::get().reads_writes(1, 1).saturating_add(40_000_000)] + fn create_swap( + origin, + target: T::AccountId, + hashed_proof: HashedProof, + action: T::SwapAction, + duration: T::BlockNumber, + ) { + let source = ensure_signed(origin)?; + ensure!( + !PendingSwaps::::contains_key(&target, hashed_proof), + Error::::AlreadyExist + ); + + action.reserve(&source)?; + + let swap = PendingSwap { + source, + action, + end_block: frame_system::Module::::block_number() + duration, + }; + PendingSwaps::::insert(target.clone(), hashed_proof.clone(), swap.clone()); + + Self::deposit_event( + RawEvent::NewSwap(target, hashed_proof, swap) + ); + } + + /// Claim an atomic swap. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// - `proof`: Revealed proof of the claim. + /// - `action`: Action defined in the swap, it must match the entry in blockchain. Otherwise + /// the operation fails. This is used for weight calculation. + #[weight = T::DbWeight::get().reads_writes(1, 1) + .saturating_add(40_000_000) + .saturating_add((proof.len() as Weight).saturating_mul(100)) + .saturating_add(action.weight()) + ] + fn claim_swap( + origin, + proof: Vec, + action: T::SwapAction, + ) -> DispatchResult { + ensure!( + proof.len() <= T::ProofLimit::get() as usize, + Error::::ProofTooLarge, + ); + + let target = ensure_signed(origin)?; + let hashed_proof = blake2_256(&proof); + + let swap = PendingSwaps::::get(&target, hashed_proof) + .ok_or(Error::::InvalidProof)?; + ensure!(swap.action == action, Error::::ClaimActionMismatch); + + let succeeded = swap.action.claim(&swap.source, &target); + + PendingSwaps::::remove(target.clone(), hashed_proof.clone()); + + Self::deposit_event( + RawEvent::SwapClaimed(target, hashed_proof, succeeded) + ); + + Ok(()) + } + + /// Cancel an atomic swap. Only possible after the originally set duration has passed. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// - `target`: Target of the original atomic swap. + /// - `hashed_proof`: Hashed proof of the original atomic swap. + #[weight = T::DbWeight::get().reads_writes(1, 1).saturating_add(40_000_000)] + fn cancel_swap( + origin, + target: T::AccountId, + hashed_proof: HashedProof, + ) { + let source = ensure_signed(origin)?; + + let swap = PendingSwaps::::get(&target, hashed_proof) + .ok_or(Error::::NotExist)?; + ensure!( + swap.source == source, + Error::::SourceMismatch, + ); + ensure!( + frame_system::Module::::block_number() >= swap.end_block, + Error::::DurationNotPassed, + ); + + swap.action.cancel(&swap.source); + PendingSwaps::::remove(&target, hashed_proof.clone()); + + Self::deposit_event( + RawEvent::SwapCancelled(target, hashed_proof) + ); + } + } +} diff --git a/frame/atomic-swap/src/tests.rs b/frame/atomic-swap/src/tests.rs new file mode 100644 index 0000000000000..d04ffab205283 --- /dev/null +++ b/frame/atomic-swap/src/tests.rs @@ -0,0 +1,157 @@ +#![cfg(test)] + +use super::*; + +use frame_support::{ + impl_outer_origin, parameter_types, weights::Weight, +}; +use sp_core::H256; +// The testing primitives are very useful for avoiding having to work with signatures +// or public keys. `u64` is used as the `AccountId` and no `Signature`s are required. +use sp_runtime::{ + Perbill, + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, +}; + +impl_outer_origin! { + pub enum Origin for Test where system = frame_system {} +} + +// For testing the pallet, we construct most of a mock runtime. This means +// first constructing a configuration type (`Test`) which `impl`s each of the +// configuration traits of pallets we want to use. +#[derive(Clone, Eq, Debug, PartialEq)] +pub struct Test; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} +impl frame_system::Trait for Test { + type BaseCallFilter = (); + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Call = (); + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type DbWeight = (); + type BlockExecutionWeight = (); + type ExtrinsicBaseWeight = (); + type MaximumExtrinsicWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); +} +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} +impl pallet_balances::Trait for Test { + type Balance = u64; + type DustRemoval = (); + type Event = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; +} +parameter_types! { + pub const ProofLimit: u32 = 1024; + pub const ExpireDuration: u64 = 100; +} +impl Trait for Test { + type Event = (); + type SwapAction = BalanceSwapAction; + type ProofLimit = ProofLimit; +} +type System = frame_system::Module; +type Balances = pallet_balances::Module; +type AtomicSwap = Module; + +const A: u64 = 1; +const B: u64 = 2; + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let genesis = pallet_balances::GenesisConfig:: { + balances: vec![ + (A, 100), + (B, 200), + ], + }; + genesis.assimilate_storage(&mut t).unwrap(); + t.into() +} + +#[test] +fn two_party_successful_swap() { + let mut chain1 = new_test_ext(); + let mut chain2 = new_test_ext(); + + // A generates a random proof. Keep it secret. + let proof: [u8; 2] = [4, 2]; + // The hashed proof is the blake2_256 hash of the proof. This is public. + let hashed_proof = blake2_256(&proof); + + // A creates the swap on chain1. + chain1.execute_with(|| { + AtomicSwap::create_swap( + Origin::signed(A), + B, + hashed_proof.clone(), + BalanceSwapAction::new(50), + 1000, + ).unwrap(); + + assert_eq!(Balances::free_balance(A), 100 - 50); + assert_eq!(Balances::free_balance(B), 200); + }); + + // B creates the swap on chain2. + chain2.execute_with(|| { + AtomicSwap::create_swap( + Origin::signed(B), + A, + hashed_proof.clone(), + BalanceSwapAction::new(75), + 1000, + ).unwrap(); + + assert_eq!(Balances::free_balance(A), 100); + assert_eq!(Balances::free_balance(B), 200 - 75); + }); + + // A reveals the proof and claims the swap on chain2. + chain2.execute_with(|| { + AtomicSwap::claim_swap( + Origin::signed(A), + proof.to_vec(), + BalanceSwapAction::new(75), + ).unwrap(); + + assert_eq!(Balances::free_balance(A), 100 + 75); + assert_eq!(Balances::free_balance(B), 200 - 75); + }); + + // B use the revealed proof to claim the swap on chain1. + chain1.execute_with(|| { + AtomicSwap::claim_swap( + Origin::signed(B), + proof.to_vec(), + BalanceSwapAction::new(50), + ).unwrap(); + + assert_eq!(Balances::free_balance(A), 100 - 50); + assert_eq!(Balances::free_balance(B), 200 + 50); + }); +} diff --git a/frame/aura/Cargo.toml b/frame/aura/Cargo.toml index f28171ae9130b..5a60d23270447 100644 --- a/frame/aura/Cargo.toml +++ b/frame/aura/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/application-crypto" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-inherents = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/inherents" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } serde = { version = "1.0.101", optional = true } diff --git a/frame/aura/src/mock.rs b/frame/aura/src/mock.rs index 09c8e596d1aee..6b8271e617aee 100644 --- a/frame/aura/src/mock.rs +++ b/frame/aura/src/mock.rs @@ -46,6 +46,7 @@ parameter_types! { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -67,7 +68,8 @@ impl frame_system::Trait for Test { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/authority-discovery/Cargo.toml b/frame/authority-discovery/Cargo.toml index 5cd93d3c31000..e3c7a256a9db6 100644 --- a/frame/authority-discovery/Cargo.toml +++ b/frame/authority-discovery/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-authority-discovery = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/authority-discovery" } sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/application-crypto" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } serde = { version = "1.0.101", optional = true } pallet-session = { version = "2.0.0-rc3", features = ["historical" ], path = "../session", default-features = false } diff --git a/frame/authority-discovery/src/lib.rs b/frame/authority-discovery/src/lib.rs index 80dbaeb8699ed..7409635031bf3 100644 --- a/frame/authority-discovery/src/lib.rs +++ b/frame/authority-discovery/src/lib.rs @@ -143,6 +143,7 @@ mod tests { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = BlockNumber; @@ -164,7 +165,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/authorship/Cargo.toml b/frame/authorship/Cargo.toml index 6f7ae89762162..9cc25b075d731 100644 --- a/frame/authorship/Cargo.toml +++ b/frame/authorship/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-inherents = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/inherents" } sp-authorship = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/authorship" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } diff --git a/frame/authorship/src/lib.rs b/frame/authorship/src/lib.rs index 2661d5e3cf2ab..26d61bf8e9ef8 100644 --- a/frame/authorship/src/lib.rs +++ b/frame/authorship/src/lib.rs @@ -418,6 +418,7 @@ mod tests { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -439,7 +440,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/babe/Cargo.toml b/frame/babe/Cargo.toml index dcc6b2937655f..5e9dcf7fb5751 100644 --- a/frame/babe/Cargo.toml +++ b/frame/babe/Cargo.toml @@ -12,7 +12,7 @@ description = "Consensus extension module for BABE consensus. Collects on-chain targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true } sp-inherents = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/inherents" } sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/application-crypto" } diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 61f7565736641..2e108bf54783d 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -57,6 +57,7 @@ parameter_types! { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -78,7 +79,8 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/balances/Cargo.toml b/frame/balances/Cargo.toml index 268f48666c0a3..68814fa50afd4 100644 --- a/frame/balances/Cargo.toml +++ b/frame/balances/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 7d48fe9d29c5f..30a321c0f425b 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -232,6 +232,13 @@ decl_event!( BalanceSet(AccountId, Balance, Balance), /// Some amount was deposited (e.g. for transaction fees). Deposit(AccountId, Balance), + /// Some balance was reserved (moved from free to reserved). + Reserved(AccountId, Balance), + /// Some balance was unreserved (moved from reserved to free). + Unreserved(AccountId, Balance), + /// Some balance was moved from the reserve of the first account to the second account. + /// Final argument indicates the destination balance type. + ReserveRepatriated(AccountId, AccountId, Balance, Status), } ); @@ -873,6 +880,7 @@ impl, I: Instance> PartialEq for ElevatedTrait { } impl, I: Instance> Eq for ElevatedTrait {} impl, I: Instance> frame_system::Trait for ElevatedTrait { + type BaseCallFilter = T::BaseCallFilter; type Origin = T::Origin; type Call = T::Call; type Index = T::Index; @@ -886,14 +894,15 @@ impl, I: Instance> frame_system::Trait for ElevatedTrait { type BlockHashCount = T::BlockHashCount; type MaximumBlockWeight = T::MaximumBlockWeight; type DbWeight = T::DbWeight; - type BlockExecutionWeight = (); - type ExtrinsicBaseWeight = (); + type BlockExecutionWeight = T::BlockExecutionWeight; + type ExtrinsicBaseWeight = T::ExtrinsicBaseWeight; type MaximumExtrinsicWeight = T::MaximumBlockWeight; type MaximumBlockLength = T::MaximumBlockLength; type AvailableBlockRatio = T::AvailableBlockRatio; type Version = T::Version; type ModuleToIndex = T::ModuleToIndex; - type MigrateAccount = (); type OnNewAccount = T::OnNewAccount; + type MigrateAccount = (); + type OnNewAccount = T::OnNewAccount; type OnKilledAccount = T::OnKilledAccount; type AccountData = T::AccountData; } @@ -1182,8 +1191,11 @@ impl, I: Instance> ReservableCurrency for Module Self::try_mutate_account(who, |account, _| -> DispatchResult { account.free = account.free.checked_sub(&value).ok_or(Error::::InsufficientBalance)?; account.reserved = account.reserved.checked_add(&value).ok_or(Error::::Overflow)?; - Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve.into(), account.free) - }) + Self::ensure_can_withdraw(&who, value.clone(), WithdrawReason::Reserve.into(), account.free) + })?; + + Self::deposit_event(RawEvent::Reserved(who.clone(), value)); + Ok(()) } /// Unreserve some funds, returning any amount that was unable to be unreserved. @@ -1192,14 +1204,17 @@ impl, I: Instance> ReservableCurrency for Module fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { if value.is_zero() { return Zero::zero() } - Self::mutate_account(who, |account| { + let actual = Self::mutate_account(who, |account| { let actual = cmp::min(account.reserved, value); account.reserved -= actual; // defensive only: this can never fail since total issuance which is at least free+reserved // fits into the same data type. account.free = account.free.saturating_add(actual); - value - actual - }) + actual + }); + + Self::deposit_event(RawEvent::Unreserved(who.clone(), actual.clone())); + value - actual } /// Slash from reserved balance, returning the negative imbalance created, @@ -1240,7 +1255,7 @@ impl, I: Instance> ReservableCurrency for Module }; } - Self::try_mutate_account(beneficiary, |to_account, is_new| -> Result { + let actual = Self::try_mutate_account(beneficiary, |to_account, is_new|-> Result { ensure!(!is_new, Error::::DeadAccount); Self::try_mutate_account(slashed, |from_account, _| -> Result { let actual = cmp::min(from_account.reserved, value); @@ -1249,9 +1264,12 @@ impl, I: Instance> ReservableCurrency for Module Status::Reserved => to_account.reserved = to_account.reserved.checked_add(&actual).ok_or(Error::::Overflow)?, } from_account.reserved -= actual; - Ok(value - actual) + Ok(actual) }) - }) + })?; + + Self::deposit_event(RawEvent::ReserveRepatriated(slashed.clone(), beneficiary.clone(), actual, status)); + Ok(value - actual) } } diff --git a/frame/balances/src/tests.rs b/frame/balances/src/tests.rs index c49a04ae56572..210c75631da63 100644 --- a/frame/balances/src/tests.rs +++ b/frame/balances/src/tests.rs @@ -26,6 +26,7 @@ impl sp_runtime::traits::Dispatchable for CallWithDispatchInfo { type Trait = (); type Info = frame_support::weights::DispatchInfo; type PostInfo = frame_support::weights::PostDispatchInfo; + fn dispatch(self, _origin: Self::Origin) -> sp_runtime::DispatchResultWithInfo { panic!("Do not use dummy implementation for dispatch."); @@ -37,7 +38,7 @@ macro_rules! decl_tests { ($test:ty, $ext_builder:ty, $existential_deposit:expr) => { use crate::*; - use sp_runtime::{FixedPointNumber, FixedI128, traits::{SignedExtension, BadOrigin}}; + use sp_runtime::{FixedPointNumber, traits::{SignedExtension, BadOrigin}}; use frame_support::{ assert_noop, assert_ok, assert_err, traits::{ @@ -45,7 +46,7 @@ macro_rules! decl_tests { Currency, ReservableCurrency, ExistenceRequirement::AllowDeath, StoredMap } }; - use pallet_transaction_payment::ChargeTransactionPayment; + use pallet_transaction_payment::{ChargeTransactionPayment, Multiplier}; use frame_system::RawOrigin; const ID_1: LockIdentifier = *b"1 "; @@ -69,6 +70,10 @@ macro_rules! decl_tests { evt } + fn last_event() -> Event { + system::Module::::events().pop().expect("Event expected").event + } + #[test] fn basic_locking_should_work() { <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { @@ -162,7 +167,7 @@ macro_rules! decl_tests { .monied(true) .build() .execute_with(|| { - pallet_transaction_payment::NextFeeMultiplier::put(FixedI128::saturating_from_integer(1)); + pallet_transaction_payment::NextFeeMultiplier::put(Multiplier::saturating_from_integer(1)); Balances::set_lock(ID_1, &1, 10, WithdrawReason::Reserve.into()); assert_noop!( >::transfer(&1, &2, 1, AllowDeath), @@ -170,7 +175,7 @@ macro_rules! decl_tests { ); assert_noop!( >::reserve(&1, 1), - Error::<$test, _>::LiquidityRestrictions + Error::<$test, _>::LiquidityRestrictions, ); assert!( as SignedExtension>::pre_dispatch( ChargeTransactionPayment::from(1), @@ -485,6 +490,10 @@ macro_rules! decl_tests { let _ = Balances::deposit_creating(&2, 1); assert_ok!(Balances::reserve(&1, 110)); assert_ok!(Balances::repatriate_reserved(&1, &2, 41, Status::Free), 0); + assert_eq!( + last_event(), + Event::balances(RawEvent::ReserveRepatriated(1, 2, 41, Status::Free)), + ); assert_eq!(Balances::reserved_balance(1), 69); assert_eq!(Balances::free_balance(1), 0); assert_eq!(Balances::reserved_balance(2), 0); @@ -683,6 +692,40 @@ macro_rules! decl_tests { }); } + #[test] + fn emit_events_with_reserve_and_unreserve() { + <$ext_builder>::default() + .build() + .execute_with(|| { + let _ = Balances::deposit_creating(&1, 100); + + System::set_block_number(2); + let _ = Balances::reserve(&1, 10); + + assert_eq!( + last_event(), + Event::balances(RawEvent::Reserved(1, 10)), + ); + + System::set_block_number(3); + let _ = Balances::unreserve(&1, 5); + + assert_eq!( + last_event(), + Event::balances(RawEvent::Unreserved(1, 5)), + ); + + System::set_block_number(4); + let _ = Balances::unreserve(&1, 6); + + // should only unreserve 5 + assert_eq!( + last_event(), + Event::balances(RawEvent::Unreserved(1, 5)), + ); + }); + } + #[test] fn emit_events_with_existential_deposit() { <$ext_builder>::default() diff --git a/frame/balances/src/tests_composite.rs b/frame/balances/src/tests_composite.rs index cdfa0a3b60a95..c7bfa806e9663 100644 --- a/frame/balances/src/tests_composite.rs +++ b/frame/balances/src/tests_composite.rs @@ -67,6 +67,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -88,7 +89,8 @@ impl frame_system::Trait for Test { type Version = (); type ModuleToIndex = (); type AccountData = super::AccountData; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } parameter_types! { diff --git a/frame/balances/src/tests_local.rs b/frame/balances/src/tests_local.rs index 3a296233e5252..9abc66fcffe77 100644 --- a/frame/balances/src/tests_local.rs +++ b/frame/balances/src/tests_local.rs @@ -67,6 +67,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -88,7 +89,8 @@ impl frame_system::Trait for Test { type Version = (); type ModuleToIndex = (); type AccountData = super::AccountData; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = Module; } parameter_types! { diff --git a/frame/benchmarking/Cargo.toml b/frame/benchmarking/Cargo.toml index 3b383d2a965ef..5c6306ebbbf56 100644 --- a/frame/benchmarking/Cargo.toml +++ b/frame/benchmarking/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] linregress = "0.1" paste = "0.1" -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } sp-api = { version = "2.0.0-rc3", path = "../../primitives/api", default-features = false } sp-runtime-interface = { version = "2.0.0-rc3", path = "../../primitives/runtime-interface", default-features = false } sp-runtime = { version = "2.0.0-rc3", path = "../../primitives/runtime", default-features = false } diff --git a/frame/benchmarking/src/lib.rs b/frame/benchmarking/src/lib.rs index ae9ef90764cd2..47e83cffbcd3e 100644 --- a/frame/benchmarking/src/lib.rs +++ b/frame/benchmarking/src/lib.rs @@ -29,7 +29,8 @@ pub use utils::*; pub use analysis::Analysis; #[doc(hidden)] pub use sp_io::storage::root as storage_root; -pub use sp_runtime::traits::{Dispatchable, Zero}; +pub use sp_runtime::traits::Zero; +pub use frame_support; pub use paste; /// Construct pallet benchmarks for weighing dispatchables. @@ -242,7 +243,9 @@ macro_rules! benchmarks_iter { { $( $common )* } ( $( $names )* ) $name { $( $code )* }: { - as $crate::Dispatchable>::dispatch(Call::::$dispatch($($arg),*), $origin.into())?; + < + Call as $crate::frame_support::traits::UnfilteredDispatchable + >::dispatch_bypass_filter(Call::::$dispatch($($arg),*), $origin.into())?; } verify $postcode $( $rest )* @@ -262,7 +265,9 @@ macro_rules! benchmarks_iter { { $( $common )* } ( $( $names )* ) $name { $( $code )* }: { - as $crate::Dispatchable>::dispatch(Call::::$dispatch($($arg),*), $origin.into())?; + < + Call as $crate::frame_support::traits::UnfilteredDispatchable + >::dispatch_bypass_filter(Call::::$dispatch($($arg),*), $origin.into())?; } verify $postcode $( $rest )* diff --git a/frame/benchmarking/src/tests.rs b/frame/benchmarking/src/tests.rs index f262b6ba0459e..318cda275680a 100644 --- a/frame/benchmarking/src/tests.rs +++ b/frame/benchmarking/src/tests.rs @@ -67,6 +67,7 @@ pub trait Trait { pub struct Test; impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -88,7 +89,8 @@ impl frame_system::Trait for Test { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/collective/Cargo.toml b/frame/collective/Cargo.toml index 071fc83b7258a..5517f3b03fb1e 100644 --- a/frame/collective/Cargo.toml +++ b/frame/collective/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index 92636a1058207..410e458e1d569 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -165,11 +165,11 @@ decl_event! { Approved(Hash), /// A motion was not approved by the required threshold. Disapproved(Hash), - /// A motion was executed; `bool` is true if returned without error. + /// A motion was executed; result will be `Ok` if it returned without error. Executed(Hash, DispatchResult), - /// A single member did some action; `bool` is true if returned without error. + /// A single member did some action; result will be `Ok` if it returned without error. MemberExecuted(Hash, DispatchResult), - /// A proposal was closed after its duration was up. + /// A proposal was closed because its threshold was reached or after its duration was up. Closed(Hash, MemberCount, MemberCount), } } @@ -188,7 +188,7 @@ decl_error! { DuplicateVote, /// Members are already initialized! AlreadyInitialized, - /// The close call is made too early, before the end of the voting. + /// The close call was made too early, before the end of the voting. TooEarly, /// There can only be a maximum of `MaxProposals` active proposals. TooManyProposals, @@ -1031,10 +1031,11 @@ mod tests { pub const MaxProposals: u32 = 100; } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; - type Call = (); + type Call = Call; type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; @@ -1052,7 +1053,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } impl Trait for Test { @@ -1183,7 +1185,7 @@ mod tests { let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let proposal_weight = proposal.get_dispatch_info().weight; let hash = BlakeTwo256::hash_of(&proposal); - assert_ok!(Collective::set_members(Origin::ROOT, vec![1, 2, 3], Some(3), MAX_MEMBERS)); + assert_ok!(Collective::set_members(Origin::root(), vec![1, 2, 3], Some(3), MAX_MEMBERS)); assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()), proposal_len)); assert_ok!(Collective::vote(Origin::signed(2), hash.clone(), 0, true)); @@ -1208,7 +1210,7 @@ mod tests { let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); let proposal_weight = proposal.get_dispatch_info().weight; let hash = BlakeTwo256::hash_of(&proposal); - assert_ok!(Collective::set_members(Origin::ROOT, vec![1, 2, 3], Some(1), MAX_MEMBERS)); + assert_ok!(Collective::set_members(Origin::root(), vec![1, 2, 3], Some(1), MAX_MEMBERS)); assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()), proposal_len)); assert_ok!(Collective::vote(Origin::signed(2), hash.clone(), 0, true)); @@ -1276,7 +1278,7 @@ mod tests { Collective::voting(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![1, 2], nays: vec![], end }) ); - assert_ok!(Collective::set_members(Origin::ROOT, vec![2, 3, 4], None, MAX_MEMBERS)); + assert_ok!(Collective::set_members(Origin::root(), vec![2, 3, 4], None, MAX_MEMBERS)); assert_eq!( Collective::voting(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![2], nays: vec![], end }) @@ -1291,7 +1293,7 @@ mod tests { Collective::voting(&hash), Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![3], end }) ); - assert_ok!(Collective::set_members(Origin::ROOT, vec![2, 4], None, MAX_MEMBERS)); + assert_ok!(Collective::set_members(Origin::root(), vec![2, 4], None, MAX_MEMBERS)); assert_eq!( Collective::voting(&hash), Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![], end }) @@ -1634,7 +1636,7 @@ mod tests { // Proposal would normally succeed assert_ok!(Collective::vote(Origin::signed(2), hash.clone(), 0, true)); // But Root can disapprove and remove it anyway - assert_ok!(Collective::disapprove_proposal(Origin::ROOT, hash.clone())); + assert_ok!(Collective::disapprove_proposal(Origin::root(), hash.clone())); let record = |event| EventRecord { phase: Phase::Initialization, event, topics: vec![] }; assert_eq!(System::events(), vec![ record(Event::collective_Instance1(RawEvent::Proposed(1, 0, hash.clone(), 2))), diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index 6c41875c635f0..57c278a3fb2a9 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } pwasm-utils = { version = "0.12.0", default-features = false } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } parity-wasm = { version = "0.41.0", default-features = false } wasmi-validation = { version = "0.3.0", default-features = false } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } @@ -31,6 +31,7 @@ pallet-transaction-payment = { version = "2.0.0-rc3", default-features = false, wabt = "0.9.2" assert_matches = "1.3.0" hex-literal = "0.2.1" +pretty_assertions = "0.6.1" pallet-balances = { version = "2.0.0-rc3", path = "../balances" } pallet-timestamp = { version = "2.0.0-rc3", path = "../timestamp" } pallet-randomness-collective-flip = { version = "2.0.0-rc3", path = "../randomness-collective-flip" } diff --git a/frame/contracts/common/Cargo.toml b/frame/contracts/common/Cargo.toml index 4a0581524e8ca..520b7239336b3 100644 --- a/frame/contracts/common/Cargo.toml +++ b/frame/contracts/common/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # This crate should not rely on any of the frame primitives. -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../../primitives/std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../../primitives/runtime" } diff --git a/frame/contracts/fixtures/restoration.wat b/frame/contracts/fixtures/restoration.wat index 4e11f97d5a2cc..225fdde817800 100644 --- a/frame/contracts/fixtures/restoration.wat +++ b/frame/contracts/fixtures/restoration.wat @@ -1,6 +1,10 @@ (module (import "env" "ext_set_storage" (func $ext_set_storage (param i32 i32 i32))) - (import "env" "ext_restore_to" (func $ext_restore_to (param i32 i32 i32 i32 i32 i32 i32 i32))) + (import "env" "ext_restore_to" + (func $ext_restore_to + (param i32 i32 i32 i32 i32 i32 i32 i32) + ) + ) (import "env" "memory" (memory 1 1)) (func (export "call") diff --git a/frame/contracts/rpc/Cargo.toml b/frame/contracts/rpc/Cargo.toml index ccfb2ad511091..75dc1bf3fb5bd 100644 --- a/frame/contracts/rpc/Cargo.toml +++ b/frame/contracts/rpc/Cargo.toml @@ -12,7 +12,7 @@ description = "Node-specific RPC methods for interaction with contracts." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } jsonrpc-core = "14.2.0" jsonrpc-core-client = "14.2.0" jsonrpc-derive = "14.2.1" diff --git a/frame/contracts/rpc/runtime-api/Cargo.toml b/frame/contracts/rpc/runtime-api/Cargo.toml index 60d79653428ea..359667731654a 100644 --- a/frame/contracts/rpc/runtime-api/Cargo.toml +++ b/frame/contracts/rpc/runtime-api/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-api = { version = "2.0.0-rc3", default-features = false, path = "../../../../primitives/api" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../../../primitives/std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../../../primitives/runtime" } pallet-contracts-primitives = { version = "2.0.0-rc3", default-features = false, path = "../../common" } diff --git a/frame/contracts/rpc/src/lib.rs b/frame/contracts/rpc/src/lib.rs index 89f43f42c3ad1..18496c13af9fa 100644 --- a/frame/contracts/rpc/src/lib.rs +++ b/frame/contracts/rpc/src/lib.rs @@ -32,6 +32,7 @@ use sp_runtime::{ generic::BlockId, traits::{Block as BlockT, Header as HeaderT}, }; +use std::convert::TryInto; pub use self::gen_client::Client as ContractsClient; pub use pallet_contracts_rpc_runtime_api::{ @@ -80,7 +81,7 @@ pub struct CallRequest { origin: AccountId, dest: AccountId, value: Balance, - gas_limit: number::NumberOrHex, + gas_limit: number::NumberOrHex, input_data: Bytes, } @@ -203,9 +204,11 @@ where gas_limit, input_data, } = call_request; - let gas_limit = gas_limit.to_number().map_err(|e| Error { + + // Make sure that gas_limit fits into 64 bits. + let gas_limit: u64 = gas_limit.try_into().map_err(|_| Error { code: ErrorCode::InvalidParams, - message: e, + message: format!("{:?} doesn't fit in 64 bit unsigned value", gas_limit), data: None, })?; @@ -282,15 +285,30 @@ fn runtime_error_into_rpc_err(err: impl std::fmt::Debug) -> Error { #[cfg(test)] mod tests { use super::*; + use sp_core::U256; + + #[test] + fn call_request_should_serialize_deserialize_properly() { + type Req = CallRequest; + let req: Req = serde_json::from_str(r#" + { + "origin": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "dest": "5DRakbLVnjVrW6niwLfHGW24EeCEvDAFGEXrtaYS5M4ynoom", + "value": 0, + "gasLimit": 1000000000000, + "inputData": "0x8c97db39" + } + "#).unwrap(); + assert_eq!(req.gas_limit.into_u256(), U256::from(0xe8d4a51000u64)); + } #[test] - fn should_serialize_deserialize_properly() { + fn result_should_serialize_deserialize_properly() { fn test(expected: &str) { let res: RpcContractExecResult = serde_json::from_str(expected).unwrap(); let actual = serde_json::to_string(&res).unwrap(); assert_eq!(actual, expected); } - test(r#"{"success":{"status":5,"data":"0x1234"}}"#); test(r#"{"error":null}"#); } diff --git a/frame/contracts/src/account_db.rs b/frame/contracts/src/account_db.rs deleted file mode 100644 index 5e1b0c34b5734..0000000000000 --- a/frame/contracts/src/account_db.rs +++ /dev/null @@ -1,450 +0,0 @@ -// Copyright 2018-2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate 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. - -// Substrate 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 Substrate. If not, see . - -//! Auxiliaries to help with managing partial changes to accounts state. - -use super::{ - AliveContractInfo, BalanceOf, CodeHash, ContractInfo, ContractInfoOf, Trait, TrieId, - TrieIdGenerator, -}; -use crate::exec::StorageKey; -use sp_std::cell::RefCell; -use sp_std::collections::btree_map::{BTreeMap, Entry}; -use sp_std::prelude::*; -use sp_io::hashing::blake2_256; -use sp_runtime::traits::{Bounded, Zero}; -use frame_support::traits::{Currency, Imbalance, SignedImbalance}; -use frame_support::{storage::child, StorageMap}; -use frame_system; - -// Note: we don't provide Option because we can't create -// the trie_id in the overlay, thus we provide an overlay on the fields -// specifically. -pub struct ChangeEntry { - /// If Some(_), then the account balance is modified to the value. If None and `reset` is false, - /// the balance unmodified. If None and `reset` is true, the balance is reset to 0. - balance: Option>, - /// If Some(_), then a contract is instantiated with the code hash. If None and `reset` is false, - /// then the contract code is unmodified. If None and `reset` is true, the contract is deleted. - code_hash: Option>, - /// If Some(_), then the rent allowance is set to the value. If None and `reset` is false, then - /// the rent allowance is unmodified. If None and `reset` is true, the contract is deleted. - rent_allowance: Option>, - storage: BTreeMap>>, - /// If true, indicates that the existing contract and all its storage entries should be removed - /// and replaced with the fields on this change entry. Otherwise, the fields on this change - /// entry are updates merged into the existing contract info and storage. - reset: bool, -} - -impl ChangeEntry { - fn balance(&self) -> Option> { - self.balance.or_else(|| { - if self.reset { - Some(>::zero()) - } else { - None - } - }) - } - - fn code_hash(&self) -> Option>> { - if self.reset { - Some(self.code_hash) - } else { - self.code_hash.map(Some) - } - } - - fn rent_allowance(&self) -> Option>> { - if self.reset { - Some(self.rent_allowance) - } else { - self.rent_allowance.map(Some) - } - } - - fn storage(&self, location: &StorageKey) -> Option>> { - let value = self.storage.get(location).cloned(); - if self.reset { - Some(value.unwrap_or(None)) - } else { - value - } - } -} - -// Cannot derive(Default) since it erroneously bounds T by Default. -impl Default for ChangeEntry { - fn default() -> Self { - ChangeEntry { - rent_allowance: Default::default(), - balance: Default::default(), - code_hash: Default::default(), - storage: Default::default(), - reset: false, - } - } -} - -pub type ChangeSet = BTreeMap<::AccountId, ChangeEntry>; - -pub trait AccountDb { - /// Account is used when overlayed otherwise trie_id must be provided. - /// This is for performance reason. - /// - /// Trie id is None iff account doesn't have an associated trie id in >. - /// Because DirectAccountDb bypass the lookup for this association. - fn get_storage( - &self, - account: &T::AccountId, - trie_id: Option<&TrieId>, - location: &StorageKey, - ) -> Option>; - /// If account has an alive contract then return the code hash associated. - fn get_code_hash(&self, account: &T::AccountId) -> Option>; - /// If account has an alive contract then return the rent allowance associated. - fn get_rent_allowance(&self, account: &T::AccountId) -> Option>; - /// Returns false iff account has no alive contract nor tombstone. - fn contract_exists(&self, account: &T::AccountId) -> bool; - fn get_balance(&self, account: &T::AccountId) -> BalanceOf; - - fn commit(&mut self, change_set: ChangeSet); -} - -pub struct DirectAccountDb; -impl AccountDb for DirectAccountDb { - fn get_storage( - &self, - _account: &T::AccountId, - trie_id: Option<&TrieId>, - location: &StorageKey, - ) -> Option> { - trie_id - .and_then(|id| child::get_raw(&crate::child_trie_info(&id[..]), &blake2_256(location))) - } - fn get_code_hash(&self, account: &T::AccountId) -> Option> { - >::get(account).and_then(|i| i.as_alive().map(|i| i.code_hash)) - } - fn get_rent_allowance(&self, account: &T::AccountId) -> Option> { - >::get(account).and_then(|i| i.as_alive().map(|i| i.rent_allowance)) - } - fn contract_exists(&self, account: &T::AccountId) -> bool { - >::contains_key(account) - } - fn get_balance(&self, account: &T::AccountId) -> BalanceOf { - T::Currency::free_balance(account) - } - fn commit(&mut self, s: ChangeSet) { - let mut total_imbalance = SignedImbalance::zero(); - for (address, changed) in s.into_iter() { - if let Some(balance) = changed.balance() { - let imbalance = T::Currency::make_free_balance_be(&address, balance); - total_imbalance = total_imbalance.merge(imbalance); - } - - if changed.code_hash().is_some() - || changed.rent_allowance().is_some() - || !changed.storage.is_empty() - || changed.reset - { - let old_info = match >::get(&address) { - Some(ContractInfo::Alive(alive)) => Some(alive), - None => None, - // Cannot commit changes to tombstone contract - Some(ContractInfo::Tombstone(_)) => continue, - }; - - let mut new_info = match (changed.reset, old_info.clone(), changed.code_hash) { - // Existing contract is being modified. - (false, Some(info), _) => info, - // Existing contract is being removed. - (true, Some(info), None) => { - child::kill_storage(&info.child_trie_info()); - >::remove(&address); - continue; - } - // Existing contract is being replaced by a new one. - (true, Some(info), Some(code_hash)) => { - child::kill_storage(&info.child_trie_info()); - AliveContractInfo:: { - code_hash, - storage_size: 0, - empty_pair_count: 0, - total_pair_count: 0, - trie_id: ::TrieIdGenerator::trie_id(&address), - deduct_block: >::block_number(), - rent_allowance: >::max_value(), - last_write: None, - } - } - // New contract is being instantiated. - (_, None, Some(code_hash)) => AliveContractInfo:: { - code_hash, - storage_size: 0, - empty_pair_count: 0, - total_pair_count: 0, - trie_id: ::TrieIdGenerator::trie_id(&address), - deduct_block: >::block_number(), - rent_allowance: >::max_value(), - last_write: None, - }, - // There is no existing at the address nor a new one to be instantiated. - (_, None, None) => continue, - }; - - if let Some(rent_allowance) = changed.rent_allowance { - new_info.rent_allowance = rent_allowance; - } - - if let Some(code_hash) = changed.code_hash { - new_info.code_hash = code_hash; - } - - if !changed.storage.is_empty() { - new_info.last_write = Some(>::block_number()); - } - - // NB: this call allocates internally. To keep allocations to the minimum we cache - // the child trie info here. - let child_trie_info = new_info.child_trie_info(); - - // Here we iterate over all storage key-value pairs that were changed throughout the - // execution of a contract and apply them to the substrate storage. - for (key, opt_new_value) in changed.storage.into_iter() { - let hashed_key = blake2_256(&key); - - // In order to correctly update the book keeping we need to fetch the previous - // value of the key-value pair. - // - // It might be a bit more clean if we had an API that supported getting the size - // of the value without going through the loading of it. But at the moment of - // writing, there is no such API. - // - // That's not a show stopper in any case, since the performance cost is - // dominated by the trie traversal anyway. - let opt_prev_value = child::get_raw(&child_trie_info, &hashed_key); - - // Update the total number of KV pairs and the number of empty pairs. - match (&opt_prev_value, &opt_new_value) { - (Some(prev_value), None) => { - new_info.total_pair_count -= 1; - if prev_value.is_empty() { - new_info.empty_pair_count -= 1; - } - }, - (None, Some(new_value)) => { - new_info.total_pair_count += 1; - if new_value.is_empty() { - new_info.empty_pair_count += 1; - } - }, - (Some(prev_value), Some(new_value)) => { - if prev_value.is_empty() { - new_info.empty_pair_count -= 1; - } - if new_value.is_empty() { - new_info.empty_pair_count += 1; - } - } - (None, None) => {} - } - - // Update the total storage size. - let prev_value_len = opt_prev_value - .as_ref() - .map(|old_value| old_value.len() as u32) - .unwrap_or(0); - let new_value_len = opt_new_value - .as_ref() - .map(|new_value| new_value.len() as u32) - .unwrap_or(0); - new_info.storage_size = new_info - .storage_size - .saturating_add(new_value_len) - .saturating_sub(prev_value_len); - - // Finally, perform the change on the storage. - match opt_new_value { - Some(new_value) => child::put_raw(&child_trie_info, &hashed_key, &new_value[..]), - None => child::kill(&child_trie_info, &hashed_key), - } - } - - if old_info - .map(|old_info| old_info != new_info) - .unwrap_or(true) - { - >::insert(&address, ContractInfo::Alive(new_info)); - } - } - } - - match total_imbalance { - // If we've detected a positive imbalance as a result of our contract-level machinations - // then it's indicative of a buggy contracts system. - // Panicking is far from ideal as it opens up a DoS attack on block validators, however - // it's a less bad option than allowing arbitrary value to be created. - SignedImbalance::Positive(ref p) if !p.peek().is_zero() => { - panic!("contract subsystem resulting in positive imbalance!") - } - _ => {} - } - } -} - -pub struct OverlayAccountDb<'a, T: Trait + 'a> { - local: RefCell>, - underlying: &'a dyn AccountDb, -} -impl<'a, T: Trait> OverlayAccountDb<'a, T> { - pub fn new(underlying: &'a dyn AccountDb) -> OverlayAccountDb<'a, T> { - OverlayAccountDb { - local: RefCell::new(ChangeSet::new()), - underlying, - } - } - - pub fn into_change_set(self) -> ChangeSet { - self.local.into_inner() - } - - pub fn set_storage( - &mut self, - account: &T::AccountId, - location: StorageKey, - value: Option>, - ) { - self.local - .borrow_mut() - .entry(account.clone()) - .or_insert(Default::default()) - .storage - .insert(location, value); - } - - /// Return an error if contract already exists (either if it is alive or tombstone) - pub fn instantiate_contract( - &mut self, - account: &T::AccountId, - code_hash: CodeHash, - ) -> Result<(), &'static str> { - if self.contract_exists(account) { - return Err("Alive contract or tombstone already exists"); - } - - let mut local = self.local.borrow_mut(); - let contract = local.entry(account.clone()).or_default(); - - contract.code_hash = Some(code_hash); - contract.rent_allowance = Some(>::max_value()); - - Ok(()) - } - - /// Mark a contract as deleted. - pub fn destroy_contract(&mut self, account: &T::AccountId) { - let mut local = self.local.borrow_mut(); - local.insert( - account.clone(), - ChangeEntry { - reset: true, - ..Default::default() - }, - ); - } - - /// Assume contract exists - pub fn set_rent_allowance(&mut self, account: &T::AccountId, rent_allowance: BalanceOf) { - self.local - .borrow_mut() - .entry(account.clone()) - .or_insert(Default::default()) - .rent_allowance = Some(rent_allowance); - } - pub fn set_balance(&mut self, account: &T::AccountId, balance: BalanceOf) { - self.local - .borrow_mut() - .entry(account.clone()) - .or_insert(Default::default()) - .balance = Some(balance); - } -} - -impl<'a, T: Trait> AccountDb for OverlayAccountDb<'a, T> { - fn get_storage( - &self, - account: &T::AccountId, - trie_id: Option<&TrieId>, - location: &StorageKey, - ) -> Option> { - self.local - .borrow() - .get(account) - .and_then(|changes| changes.storage(location)) - .unwrap_or_else(|| self.underlying.get_storage(account, trie_id, location)) - } - fn get_code_hash(&self, account: &T::AccountId) -> Option> { - self.local - .borrow() - .get(account) - .and_then(|changes| changes.code_hash()) - .unwrap_or_else(|| self.underlying.get_code_hash(account)) - } - fn get_rent_allowance(&self, account: &T::AccountId) -> Option> { - self.local - .borrow() - .get(account) - .and_then(|changes| changes.rent_allowance()) - .unwrap_or_else(|| self.underlying.get_rent_allowance(account)) - } - fn contract_exists(&self, account: &T::AccountId) -> bool { - self.local - .borrow() - .get(account) - .and_then(|changes| changes.code_hash().map(|code_hash| code_hash.is_some())) - .unwrap_or_else(|| self.underlying.contract_exists(account)) - } - fn get_balance(&self, account: &T::AccountId) -> BalanceOf { - self.local - .borrow() - .get(account) - .and_then(|changes| changes.balance()) - .unwrap_or_else(|| self.underlying.get_balance(account)) - } - fn commit(&mut self, s: ChangeSet) { - let mut local = self.local.borrow_mut(); - - for (address, changed) in s.into_iter() { - match local.entry(address) { - Entry::Occupied(e) => { - let mut value = e.into_mut(); - if changed.reset { - *value = changed; - } else { - value.balance = changed.balance.or(value.balance); - value.code_hash = changed.code_hash.or(value.code_hash); - value.rent_allowance = changed.rent_allowance.or(value.rent_allowance); - value.storage.extend(changed.storage.into_iter()); - } - } - Entry::Vacant(e) => { - e.insert(changed); - } - } - } - } -} diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 9cc1c50260db9..ff0d4d9dc0de1 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -15,16 +15,16 @@ // along with Substrate. If not, see . use super::{CodeHash, Config, ContractAddressFor, Event, RawEvent, Trait, - TrieId, BalanceOf, ContractInfo}; -use crate::account_db::{AccountDb, DirectAccountDb, OverlayAccountDb}; + TrieId, BalanceOf, ContractInfo, TrieIdGenerator}; use crate::gas::{Gas, GasMeter, Token}; use crate::rent; +use crate::storage; use sp_std::prelude::*; -use sp_runtime::traits::{Bounded, CheckedAdd, CheckedSub, Zero}; +use sp_runtime::traits::{Bounded, Zero}; use frame_support::{ storage::unhashed, dispatch::DispatchError, - traits::{WithdrawReason, Currency, Time, Randomness}, + traits::{ExistenceRequirement, Currency, Time, Randomness}, }; pub type AccountIdOf = ::AccountId; @@ -105,8 +105,8 @@ pub trait Ext { fn get_storage(&self, key: &StorageKey) -> Option>; /// Sets the storage entry by the given key to the specified value. If `value` is `None` then - /// the storage entry is deleted. Returns an Err if the value size is too large. - fn set_storage(&mut self, key: StorageKey, value: Option>) -> Result<(), &'static str>; + /// the storage entry is deleted. + fn set_storage(&mut self, key: StorageKey, value: Option>); /// Instantiate a contract from the given code. /// @@ -129,6 +129,12 @@ pub trait Ext { ) -> Result<(), DispatchError>; /// Transfer all funds to `beneficiary` and delete the contract. + /// + /// Since this function removes the self contract eagerly, if succeeded, no further actions should + /// be performed on this `Ext` instance. + /// + /// This function will fail if the same contract is present on the contract + /// call stack. fn terminate( &mut self, beneficiary: &AccountIdOf, @@ -147,14 +153,20 @@ pub trait Ext { /// Notes a call dispatch. fn note_dispatch_call(&mut self, call: CallOf); - /// Notes a restoration request. - fn note_restore_to( + /// Restores the given destination contract sacrificing the current one. + /// + /// Since this function removes the self contract eagerly, if succeeded, no further actions should + /// be performed on this `Ext` instance. + /// + /// This function will fail if the same contract is present + /// on the contract call stack. + fn restore_to( &mut self, dest: AccountIdOf, code_hash: CodeHash, rent_allowance: BalanceOf, delta: Vec, - ); + ) -> Result<(), &'static str>; /// Returns a reference to the account id of the caller. fn caller(&self) -> &AccountIdOf; @@ -264,38 +276,18 @@ impl Token for ExecFeeToken { #[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq, Clone))] #[derive(sp_runtime::RuntimeDebug)] pub enum DeferredAction { - DepositEvent { - /// A list of topics this event will be deposited with. - topics: Vec, - /// The event to deposit. - event: Event, - }, DispatchRuntimeCall { /// The account id of the contract who dispatched this call. origin: T::AccountId, /// The call to dispatch. call: ::Call, }, - RestoreTo { - /// The account id of the contract which is removed during the restoration and transfers - /// its storage to the restored contract. - donor: T::AccountId, - /// The account id of the restored contract. - dest: T::AccountId, - /// The code hash of the restored contract. - code_hash: CodeHash, - /// The initial rent allowance to set. - rent_allowance: BalanceOf, - /// The keys to delete upon restoration. - delta: Vec, - }, } pub struct ExecutionContext<'a, T: Trait + 'a, V, L> { pub caller: Option<&'a ExecutionContext<'a, T, V, L>>, pub self_account: T::AccountId, pub self_trie_id: Option, - pub overlay: OverlayAccountDb<'a, T>, pub depth: usize, pub deferred: Vec>, pub config: &'a Config, @@ -320,7 +312,6 @@ where caller: None, self_trie_id: None, self_account: origin, - overlay: OverlayAccountDb::::new(&DirectAccountDb), depth: 0, deferred: Vec::new(), config: &cfg, @@ -338,7 +329,6 @@ where caller: Some(self), self_trie_id: trie_id, self_account: dest, - overlay: OverlayAccountDb::new(&self.overlay), depth: self.depth + 1, deferred: Vec::new(), config: self.config, @@ -349,23 +339,6 @@ where } } - /// Transfer balance to `dest` without calling any contract code. - pub fn transfer( - &mut self, - dest: T::AccountId, - value: BalanceOf, - gas_meter: &mut GasMeter - ) -> Result<(), DispatchError> { - transfer( - gas_meter, - TransferCause::Call, - &self.self_account.clone(), - &dest, - value, - self, - ) - } - /// Make a call to the specified address, optionally transferring some funds. pub fn call( &mut self, @@ -424,8 +397,8 @@ where // If code_hash is not none, then the destination account is a live contract, otherwise // it is a regular account since tombstone accounts have already been rejected. - match nested.overlay.get_code_hash(&dest) { - Some(dest_code_hash) => { + match storage::code_hash::(&dest) { + Ok(dest_code_hash) => { let executable = try_or_exec_error!( nested.loader.load_main(&dest_code_hash), input_data @@ -437,10 +410,9 @@ where input_data, gas_meter, )?; - Ok(output) } - None => Ok(ExecReturnValue { status: STATUS_SUCCESS, data: Vec::new() }), + Err(storage::ContractAbsentError) => Ok(ExecReturnValue { status: STATUS_SUCCESS, data: Vec::new() }), } }) } @@ -477,11 +449,20 @@ where ); // TrieId has not been generated yet and storage is empty since contract is new. - let dest_trie_id = None; + // + // Generate it now. + let dest_trie_id = ::TrieIdGenerator::trie_id(&dest); - let output = self.with_nested_context(dest.clone(), dest_trie_id, |nested| { + let output = self.with_nested_context(dest.clone(), Some(dest_trie_id), |nested| { try_or_exec_error!( - nested.overlay.instantiate_contract(&dest, code_hash.clone()), + storage::place_contract::( + &dest, + nested + .self_trie_id + .clone() + .expect("the nested context always has to have self_trie_id"), + code_hash.clone() + ), input_data ); @@ -512,7 +493,7 @@ where )?; // Error out if insufficient remaining balance. - if nested.overlay.get_balance(&dest) < nested.config.existential_deposit { + if T::Currency::free_balance(&dest) < nested.config.existential_deposit { return Err(ExecError { reason: "insufficient remaining balance".into(), buffer: output.data, @@ -520,10 +501,7 @@ where } // Deposit an instantiation event. - nested.deferred.push(DeferredAction::DepositEvent { - event: RawEvent::Instantiated(caller.clone(), dest.clone()), - topics: Vec::new(), - }); + deposit_event::(vec![], RawEvent::Instantiated(caller.clone(), dest.clone())); Ok(output) })?; @@ -531,32 +509,6 @@ where Ok((dest, output)) } - pub fn terminate( - &mut self, - beneficiary: &T::AccountId, - gas_meter: &mut GasMeter, - ) -> Result<(), DispatchError> { - let self_id = self.self_account.clone(); - let value = self.overlay.get_balance(&self_id); - if let Some(caller) = self.caller { - if caller.is_live(&self_id) { - return Err(DispatchError::Other( - "Cannot terminate a contract that is present on the call stack", - )); - } - } - transfer( - gas_meter, - TransferCause::Terminate, - &self_id, - beneficiary, - value, - self, - )?; - self.overlay.destroy_contract(&self_id); - Ok(()) - } - fn new_call_context<'b>( &'b mut self, caller: T::AccountId, @@ -573,21 +525,26 @@ where } } + /// Execute the given closure within a nested execution context. fn with_nested_context(&mut self, dest: T::AccountId, trie_id: Option, func: F) -> ExecResult where F: FnOnce(&mut ExecutionContext) -> ExecResult { - let (output, change_set, deferred) = { + use frame_support::storage::TransactionOutcome::*; + let (output, deferred) = { let mut nested = self.nested(dest, trie_id); - let output = func(&mut nested)?; - (output, nested.overlay.into_change_set(), nested.deferred) + let output = frame_support::storage::with_transaction(|| { + let output = func(&mut nested); + match output { + Ok(ref rv) if rv.is_success() => Commit(output), + _ => Rollback(output), + } + })?; + (output, nested.deferred) }; - if output.is_success() { - self.overlay.commit(change_set); self.deferred.extend(deferred); } - Ok(output) } @@ -676,48 +633,27 @@ fn transfer<'a, T: Trait, V: Vm, L: Loader>( Err("not enough gas to pay transfer fee")? } - // We allow balance to go below the existential deposit here: - let from_balance = ctx.overlay.get_balance(transactor); - let new_from_balance = match from_balance.checked_sub(&value) { - Some(b) => b, - None => Err("balance too low to send value")?, - }; - let to_balance = ctx.overlay.get_balance(dest); - if to_balance.is_zero() && value < ctx.config.existential_deposit { - Err("value too low to create account")? - } - // Only ext_terminate is allowed to bring the sender below the existential deposit - let required_balance = match cause { - Terminate => 0.into(), - _ => ctx.config.existential_deposit - }; - - T::Currency::ensure_can_withdraw( - transactor, - value, - WithdrawReason::Transfer.into(), - new_from_balance.checked_sub(&required_balance) - .ok_or("brings sender below existential deposit")?, - )?; - - let new_to_balance = match to_balance.checked_add(&value) { - Some(b) => b, - None => Err("destination balance too high to receive value")?, + let existence_requirement = match cause { + Terminate => ExistenceRequirement::AllowDeath, + _ => ExistenceRequirement::KeepAlive, }; - - if transactor != dest { - ctx.overlay.set_balance(transactor, new_from_balance); - ctx.overlay.set_balance(dest, new_to_balance); - ctx.deferred.push(DeferredAction::DepositEvent { - event: RawEvent::Transfer(transactor.clone(), dest.clone(), value), - topics: Vec::new(), - }); - } + T::Currency::transfer(transactor, dest, value, existence_requirement)?; Ok(()) } +/// A context that is active within a call. +/// +/// This context has some invariants that must be held at all times. Specifically: +///`ctx` always points to a context of an alive contract. That implies that it has an existent +/// `self_trie_id`. +/// +/// Be advised that there are brief time spans where these invariants could be invalidated. +/// For example, when a contract requests self-termination the contract is removed eagerly. That +/// implies that the control won't be returned to the contract anymore, but there is still some code +/// on the path of the return from that call context. Therefore, care must be taken in these +/// situations. struct CallContext<'a, 'b: 'a, T: Trait + 'b, V: Vm + 'b, L: Loader> { ctx: &'a mut ExecutionContext<'b, T, V, L>, caller: T::AccountId, @@ -735,20 +671,32 @@ where type T = T; fn get_storage(&self, key: &StorageKey) -> Option> { - self.ctx.overlay.get_storage(&self.ctx.self_account, self.ctx.self_trie_id.as_ref(), key) + let trie_id = self.ctx.self_trie_id.as_ref().expect( + "`ctx.self_trie_id` points to an alive contract within the `CallContext`;\ + it cannot be `None`;\ + expect can't fail;\ + qed", + ); + storage::read_contract_storage(trie_id, key) } - fn set_storage(&mut self, key: StorageKey, value: Option>) -> Result<(), &'static str> { - if let Some(ref value) = value { - if self.max_value_size() < value.len() as u32 { - return Err("value size exceeds maximum"); - } + fn set_storage(&mut self, key: StorageKey, value: Option>) { + let trie_id = self.ctx.self_trie_id.as_ref().expect( + "`ctx.self_trie_id` points to an alive contract within the `CallContext`;\ + it cannot be `None`;\ + expect can't fail;\ + qed", + ); + if let Err(storage::ContractAbsentError) = + storage::write_contract_storage::(&self.ctx.self_account, trie_id, &key, value) + { + panic!( + "the contract must be in the alive state within the `CallContext`;\ + the contract cannot be absent in storage; + write_contract_storage cannot return `None`; + qed" + ); } - - self.ctx - .overlay - .set_storage(&self.ctx.self_account, key, value); - Ok(()) } fn instantiate( @@ -767,7 +715,14 @@ where value: BalanceOf, gas_meter: &mut GasMeter, ) -> Result<(), DispatchError> { - self.ctx.transfer(to.clone(), value, gas_meter) + transfer( + gas_meter, + TransferCause::Call, + &self.ctx.self_account.clone(), + &to, + value, + self.ctx, + ) } fn terminate( @@ -775,7 +730,30 @@ where beneficiary: &AccountIdOf, gas_meter: &mut GasMeter, ) -> Result<(), DispatchError> { - self.ctx.terminate(beneficiary, gas_meter) + let self_id = self.ctx.self_account.clone(); + let value = T::Currency::free_balance(&self_id); + if let Some(caller_ctx) = self.ctx.caller { + if caller_ctx.is_live(&self_id) { + return Err(DispatchError::Other( + "Cannot terminate a contract that is present on the call stack", + )); + } + } + transfer( + gas_meter, + TransferCause::Terminate, + &self_id, + beneficiary, + value, + self.ctx, + )?; + let self_trie_id = self.ctx.self_trie_id.as_ref().expect( + "this function is only invoked by in the context of a contract;\ + a contract has a trie id;\ + this can't be None; qed", + ); + storage::destroy_contract::(&self_id, self_trie_id); + Ok(()) } fn call( @@ -795,20 +773,40 @@ where }); } - fn note_restore_to( + fn restore_to( &mut self, dest: AccountIdOf, code_hash: CodeHash, rent_allowance: BalanceOf, delta: Vec, - ) { - self.ctx.deferred.push(DeferredAction::RestoreTo { - donor: self.ctx.self_account.clone(), - dest, - code_hash, + ) -> Result<(), &'static str> { + if let Some(caller_ctx) = self.ctx.caller { + if caller_ctx.is_live(&self.ctx.self_account) { + return Err( + "Cannot perform restoration of a contract that is present on the call stack", + ); + } + } + + let result = crate::rent::restore_to::( + self.ctx.self_account.clone(), + dest.clone(), + code_hash.clone(), rent_allowance, delta, - }); + ); + if let Ok(_) = result { + deposit_event::( + vec![], + RawEvent::Restored( + self.ctx.self_account.clone(), + dest, + code_hash, + rent_allowance, + ), + ); + } + result } fn address(&self) -> &T::AccountId { @@ -820,7 +818,7 @@ where } fn balance(&self) -> BalanceOf { - self.ctx.overlay.get_balance(&self.ctx.self_account) + T::Currency::free_balance(&self.ctx.self_account) } fn value_transferred(&self) -> BalanceOf { @@ -844,18 +842,25 @@ where } fn deposit_event(&mut self, topics: Vec, data: Vec) { - self.ctx.deferred.push(DeferredAction::DepositEvent { + deposit_event::( topics, - event: RawEvent::ContractExecution(self.ctx.self_account.clone(), data), - }); + RawEvent::ContractExecution(self.ctx.self_account.clone(), data) + ); } fn set_rent_allowance(&mut self, rent_allowance: BalanceOf) { - self.ctx.overlay.set_rent_allowance(&self.ctx.self_account, rent_allowance) + if let Err(storage::ContractAbsentError) = + storage::set_rent_allowance::(&self.ctx.self_account, rent_allowance) + { + panic!( + "`self_account` points to an alive contract within the `CallContext`; + set_rent_allowance cannot return `Err`; qed" + ); + } } fn rent_allowance(&self) -> BalanceOf { - self.ctx.overlay.get_rent_allowance(&self.ctx.self_account) + storage::rent_allowance::(&self.ctx.self_account) .unwrap_or(>::max_value()) // Must never be triggered actually } @@ -877,30 +882,37 @@ where } } +fn deposit_event( + topics: Vec, + event: Event, +) { + >::deposit_event_indexed( + &*topics, + ::Event::from(event).into(), + ) +} + /// These tests exercise the executive layer. /// /// In these tests the VM/loader are mocked. Instead of dealing with wasm bytecode they use simple closures. /// This allows you to tackle executive logic more thoroughly without writing a /// wasm VM code. -/// -/// Because it's the executive layer: -/// -/// - no gas meter setup and teardown logic. All balances are *AFTER* gas purchase. -/// - executive layer doesn't alter any storage! #[cfg(test)] mod tests { use super::{ - BalanceOf, ExecFeeToken, ExecutionContext, Ext, Loader, TransferFeeKind, TransferFeeToken, - Vm, ExecResult, RawEvent, DeferredAction, + BalanceOf, Event, ExecFeeToken, ExecResult, ExecutionContext, Ext, Loader, + RawEvent, TransferFeeKind, TransferFeeToken, Vm, }; use crate::{ - account_db::AccountDb, gas::GasMeter, tests::{ExtBuilder, Test}, + gas::GasMeter, tests::{ExtBuilder, Test, MetaEvent}, exec::{ExecReturnValue, ExecError, STATUS_SUCCESS}, CodeHash, Config, gas::Gas, + storage, }; - use std::{cell::RefCell, rc::Rc, collections::HashMap, marker::PhantomData}; - use assert_matches::assert_matches; + use crate::tests::test_utils::{place_contract, set_balance, get_balance}; use sp_runtime::DispatchError; + use assert_matches::assert_matches; + use std::{cell::RefCell, collections::HashMap, marker::PhantomData, rc::Rc}; const ALICE: u64 = 1; const BOB: u64 = 2; @@ -908,19 +920,14 @@ mod tests { const GAS_LIMIT: Gas = 10_000_000_000; - impl<'a, T, V, L> ExecutionContext<'a, T, V, L> - where T: crate::Trait - { - fn events(&self) -> Vec> { - self.deferred - .iter() - .filter(|action| match *action { - DeferredAction::DepositEvent { .. } => true, - _ => false, - }) - .cloned() - .collect() - } + fn events() -> Vec> { + >::events() + .into_iter() + .filter_map(|meta| match meta.event { + MetaEvent::contracts(contract_event) => Some(contract_event), + _ => None, + }) + .collect() } struct MockCtx<'a> { @@ -1029,7 +1036,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.instantiate_contract(&BOB, exec_ch).unwrap(); + place_contract(&BOB, exec_ch); assert_matches!( ctx.call(BOB, value, &mut gas_meter, data), @@ -1051,8 +1058,8 @@ mod tests { let loader = MockLoader::empty(); let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.set_balance(&origin, 100); - ctx.overlay.set_balance(&dest, 0); + set_balance(&origin, 100); + set_balance(&dest, 0); let mut gas_meter = GasMeter::::new(GAS_LIMIT); @@ -1072,7 +1079,7 @@ mod tests { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.set_balance(&origin, 100); + set_balance(&origin, 100); let mut gas_meter = GasMeter::::new(GAS_LIMIT); @@ -1097,8 +1104,8 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.set_balance(&origin, 100); - ctx.overlay.set_balance(&dest, 0); + set_balance(&origin, 100); + set_balance(&dest, 0); let output = ctx.call( dest, @@ -1108,15 +1115,15 @@ mod tests { ).unwrap(); assert!(output.is_success()); - assert_eq!(ctx.overlay.get_balance(&origin), 45); - assert_eq!(ctx.overlay.get_balance(&dest), 55); + assert_eq!(get_balance(&origin), 45); + assert_eq!(get_balance(&dest), 55); }); } #[test] fn changes_are_reverted_on_failing_call() { - // This test verifies that a contract is able to transfer - // some funds to another account. + // This test verifies that changes are reverted on a call which fails (or equally, returns + // a non-zero status code). let origin = ALICE; let dest = BOB; @@ -1129,9 +1136,9 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.instantiate_contract(&BOB, return_ch).unwrap(); - ctx.overlay.set_balance(&origin, 100); - ctx.overlay.set_balance(&dest, 0); + place_contract(&BOB, return_ch); + set_balance(&origin, 100); + set_balance(&dest, 0); let output = ctx.call( dest, @@ -1141,8 +1148,8 @@ mod tests { ).unwrap(); assert!(!output.is_success()); - assert_eq!(ctx.overlay.get_balance(&origin), 100); - assert_eq!(ctx.overlay.get_balance(&dest), 0); + assert_eq!(get_balance(&origin), 100); + assert_eq!(get_balance(&dest), 0); }); } @@ -1159,8 +1166,8 @@ mod tests { let loader = MockLoader::empty(); let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.set_balance(&origin, 100); - ctx.overlay.set_balance(&dest, 0); + set_balance(&origin, 100); + set_balance(&dest, 0); let mut gas_meter = GasMeter::::new(GAS_LIMIT); @@ -1184,8 +1191,8 @@ mod tests { let loader = MockLoader::empty(); let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.set_balance(&origin, 100); - ctx.overlay.set_balance(&dest, 15); + set_balance(&origin, 100); + set_balance(&dest, 15); let mut gas_meter = GasMeter::::new(GAS_LIMIT); @@ -1212,8 +1219,8 @@ mod tests { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.set_balance(&origin, 100); - ctx.overlay.set_balance(&dest, 15); + set_balance(&origin, 100); + set_balance(&dest, 15); let mut gas_meter = GasMeter::::new(GAS_LIMIT); @@ -1244,7 +1251,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.set_balance(&origin, 0); + set_balance(&origin, 0); let result = ctx.call( dest, @@ -1256,12 +1263,12 @@ mod tests { assert_matches!( result, Err(ExecError { - reason: DispatchError::Other("balance too low to send value"), + reason: DispatchError::Module { message: Some("InsufficientBalance"), .. }, buffer: _, }) ); - assert_eq!(ctx.overlay.get_balance(&origin), 0); - assert_eq!(ctx.overlay.get_balance(&dest), 0); + assert_eq!(get_balance(&origin), 0); + assert_eq!(get_balance(&dest), 0); }); } @@ -1281,7 +1288,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.instantiate_contract(&BOB, return_ch).unwrap(); + place_contract(&BOB, return_ch); let result = ctx.call( dest, @@ -1312,7 +1319,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.instantiate_contract(&BOB, return_ch).unwrap(); + place_contract(&BOB, return_ch); let result = ctx.call( dest, @@ -1340,7 +1347,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.instantiate_contract(&BOB, input_data_ch).unwrap(); + place_contract(&BOB, input_data_ch); let result = ctx.call( BOB, @@ -1366,7 +1373,7 @@ mod tests { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.set_balance(&ALICE, 100); + set_balance(&ALICE, 100); let result = ctx.instantiate( 1, @@ -1414,8 +1421,8 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.set_balance(&BOB, 1); - ctx.overlay.instantiate_contract(&BOB, recurse_ch).unwrap(); + set_balance(&BOB, 1); + place_contract(&BOB, recurse_ch); let result = ctx.call( BOB, @@ -1460,8 +1467,8 @@ mod tests { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(origin, &cfg, &vm, &loader); - ctx.overlay.instantiate_contract(&dest, bob_ch).unwrap(); - ctx.overlay.instantiate_contract(&CHARLIE, charlie_ch).unwrap(); + place_contract(&dest, bob_ch); + place_contract(&CHARLIE, charlie_ch); let result = ctx.call( dest, @@ -1501,8 +1508,8 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.instantiate_contract(&BOB, bob_ch).unwrap(); - ctx.overlay.instantiate_contract(&CHARLIE, charlie_ch).unwrap(); + place_contract(&BOB, bob_ch); + place_contract(&CHARLIE, charlie_ch); let result = ctx.call( BOB, @@ -1550,7 +1557,7 @@ mod tests { ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.set_balance(&ALICE, 1000); + set_balance(&ALICE, 1000); let instantiated_contract_address = assert_matches!( ctx.instantiate( @@ -1564,16 +1571,9 @@ mod tests { // Check that the newly created account has the expected code hash and // there are instantiation event. - assert_eq!(ctx.overlay.get_code_hash(&instantiated_contract_address).unwrap(), dummy_ch); - assert_eq!(&ctx.events(), &[ - DeferredAction::DepositEvent { - event: RawEvent::Transfer(ALICE, instantiated_contract_address, 100), - topics: Vec::new(), - }, - DeferredAction::DepositEvent { - event: RawEvent::Instantiated(ALICE, instantiated_contract_address), - topics: Vec::new(), - } + assert_eq!(storage::code_hash::(&instantiated_contract_address).unwrap(), dummy_ch); + assert_eq!(&events(), &[ + RawEvent::Instantiated(ALICE, instantiated_contract_address) ]); }); } @@ -1590,7 +1590,7 @@ mod tests { ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.set_balance(&ALICE, 1000); + set_balance(&ALICE, 1000); let instantiated_contract_address = assert_matches!( ctx.instantiate( @@ -1603,8 +1603,8 @@ mod tests { ); // Check that the account has not been created. - assert!(ctx.overlay.get_code_hash(&instantiated_contract_address).is_none()); - assert!(ctx.events().is_empty()); + assert!(storage::code_hash::(&instantiated_contract_address).is_err()); + assert!(events().is_empty()); }); } @@ -1635,9 +1635,9 @@ mod tests { ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.set_balance(&ALICE, 1000); - ctx.overlay.set_balance(&BOB, 100); - ctx.overlay.instantiate_contract(&BOB, instantiator_ch).unwrap(); + set_balance(&ALICE, 1000); + set_balance(&BOB, 100); + place_contract(&BOB, instantiator_ch); assert_matches!( ctx.call(BOB, 20, &mut GasMeter::::new(GAS_LIMIT), vec![]), @@ -1648,20 +1648,9 @@ mod tests { // Check that the newly created account has the expected code hash and // there are instantiation event. - assert_eq!(ctx.overlay.get_code_hash(&instantiated_contract_address).unwrap(), dummy_ch); - assert_eq!(&ctx.events(), &[ - DeferredAction::DepositEvent { - event: RawEvent::Transfer(ALICE, BOB, 20), - topics: Vec::new(), - }, - DeferredAction::DepositEvent { - event: RawEvent::Transfer(BOB, instantiated_contract_address, 15), - topics: Vec::new(), - }, - DeferredAction::DepositEvent { - event: RawEvent::Instantiated(BOB, instantiated_contract_address), - topics: Vec::new(), - }, + assert_eq!(storage::code_hash::(&instantiated_contract_address).unwrap(), dummy_ch); + assert_eq!(&events(), &[ + RawEvent::Instantiated(BOB, instantiated_contract_address) ]); }); } @@ -1695,9 +1684,9 @@ mod tests { ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.set_balance(&ALICE, 1000); - ctx.overlay.set_balance(&BOB, 100); - ctx.overlay.instantiate_contract(&BOB, instantiator_ch).unwrap(); + set_balance(&ALICE, 1000); + set_balance(&BOB, 100); + place_contract(&BOB, instantiator_ch); assert_matches!( ctx.call(BOB, 20, &mut GasMeter::::new(GAS_LIMIT), vec![]), @@ -1706,12 +1695,7 @@ mod tests { // The contract wasn't instantiated so we don't expect to see an instantiation // event here. - assert_eq!(&ctx.events(), &[ - DeferredAction::DepositEvent { - event: RawEvent::Transfer(ALICE, BOB, 20), - topics: Vec::new(), - }, - ]); + assert_eq!(&events(), &[]); }); } @@ -1732,7 +1716,7 @@ mod tests { .execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - ctx.overlay.set_balance(&ALICE, 1000); + set_balance(&ALICE, 1000); assert_matches!( ctx.instantiate( @@ -1748,7 +1732,7 @@ mod tests { ); assert_eq!( - &ctx.events(), + &events(), &[] ); }); @@ -1768,8 +1752,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { let cfg = Config::preload(); let mut ctx = ExecutionContext::top_level(ALICE, &cfg, &vm, &loader); - - ctx.overlay.set_balance(&ALICE, 100); + set_balance(&ALICE, 100); let result = ctx.instantiate( 1, diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 345c3db7927b4..0db602eb3f8e1 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -81,8 +81,7 @@ #[macro_use] mod gas; - -mod account_db; +mod storage; mod exec; mod wasm; mod rent; @@ -92,7 +91,6 @@ mod tests; mod migration; use crate::exec::ExecutionContext; -use crate::account_db::{AccountDb, DirectAccountDb}; use crate::wasm::{WasmLoader, WasmVm}; pub use crate::gas::{Gas, GasMeter}; @@ -103,7 +101,6 @@ use serde::{Serialize, Deserialize}; use sp_core::crypto::UncheckedFrom; use sp_std::{prelude::*, marker::PhantomData, fmt::Debug}; use codec::{Codec, Encode, Decode}; -use sp_io::hashing::blake2_256; use sp_runtime::{ traits::{ Hash, StaticLookup, Zero, MaybeSerializeDeserialize, Member, @@ -115,10 +112,10 @@ use frame_support::dispatch::{ }; use frame_support::{ Parameter, decl_module, decl_event, decl_storage, decl_error, - parameter_types, IsSubType, storage::child::{self, ChildInfo}, + parameter_types, IsSubType, storage::child::ChildInfo, }; use frame_support::traits::{OnUnbalanced, Currency, Get, Time, Randomness}; -use frame_support::weights::{FunctionOf, DispatchClass, Weight, GetDispatchInfo, Pays}; +use frame_support::weights::{GetDispatchInfo, Weight}; use frame_system::{self as system, ensure_signed, RawOrigin, ensure_root}; use pallet_contracts_primitives::{RentProjection, ContractAccessError}; @@ -130,11 +127,6 @@ pub trait ContractAddressFor { fn contract_address_for(code_hash: &CodeHash, data: &[u8], origin: &AccountId) -> AccountId; } -/// A function that returns the fee for dispatching a `Call`. -pub trait ComputeDispatchFee { - fn compute_dispatch_fee(call: &Call) -> Balance; -} - /// Information for managing an account and its sub trie abstraction. /// This is the required info to cache for an account #[derive(Encode, Decode, RuntimeDebug)] @@ -256,6 +248,12 @@ where } } +impl From> for ContractInfo { + fn from(alive_info: AliveContractInfo) -> Self { + Self::Alive(alive_info) + } +} + /// Get a trie id (trie id must be unique and collision resistant depending upon its context). /// Note that it is different than encode because trie id should be collision resistant /// (being a proper unique identifier). @@ -482,11 +480,7 @@ decl_module! { /// Stores the given binary Wasm code into the chain's storage and returns its `codehash`. /// You can instantiate contracts only with stored code. - #[weight = FunctionOf( - |args: (&Vec,)| Module::::calc_code_put_costs(args.0), - DispatchClass::Normal, - Pays::Yes - )] + #[weight = Module::::calc_code_put_costs(&code)] pub fn put_code( origin, code: Vec @@ -507,11 +501,7 @@ decl_module! { /// * If the account is a regular account, any value will be transferred. /// * If no account exists and the call value is not less than `existential_deposit`, /// a regular account will be created and any value will be transferred. - #[weight = FunctionOf( - |args: (&::Source, &BalanceOf, &Weight, &Vec)| *args.2, - DispatchClass::Normal, - Pays::Yes - )] + #[weight = *gas_limit] pub fn call( origin, dest: ::Source, @@ -539,11 +529,7 @@ decl_module! { /// after the execution is saved as the `code` of the account. That code will be invoked /// upon any call received by this account. /// - The contract is initialized. - #[weight = FunctionOf( - |args: (&BalanceOf, &Weight, &CodeHash, &Vec)| *args.1, - DispatchClass::Normal, - Pays::Yes - )] + #[weight = *gas_limit] pub fn instantiate( origin, #[compact] endowment: BalanceOf, @@ -629,12 +615,7 @@ impl Module { .get_alive() .ok_or(ContractAccessError::IsTombstone)?; - let maybe_value = AccountDb::::get_storage( - &DirectAccountDb, - &address, - Some(&contract_info.trie_id), - &key, - ); + let maybe_value = storage::read_contract_storage(&contract_info.trie_id, &key); Ok(maybe_value) } @@ -653,7 +634,7 @@ impl Module { fn execute_wasm( origin: T::AccountId, gas_meter: &mut GasMeter, - func: impl FnOnce(&mut ExecutionContext, &mut GasMeter) -> ExecResult + func: impl FnOnce(&mut ExecutionContext, &mut GasMeter) -> ExecResult, ) -> ExecResult { let cfg = Config::preload(); let vm = WasmVm::new(&cfg.schedule); @@ -662,22 +643,10 @@ impl Module { let result = func(&mut ctx, gas_meter); - if result.as_ref().map(|output| output.is_success()).unwrap_or(false) { - // Commit all changes that made it thus far into the persistent storage. - DirectAccountDb.commit(ctx.overlay.into_change_set()); - } - // Execute deferred actions. ctx.deferred.into_iter().for_each(|deferred| { use self::exec::DeferredAction::*; match deferred { - DepositEvent { - topics, - event, - } => >::deposit_event_indexed( - &*topics, - ::Event::from(event).into(), - ), DispatchRuntimeCall { origin: who, call, @@ -691,112 +660,11 @@ impl Module { gas_meter.refund(post_info.calc_unspent(&info)); Self::deposit_event(RawEvent::Dispatched(who, result.is_ok())); } - RestoreTo { - donor, - dest, - code_hash, - rent_allowance, - delta, - } => { - let result = Self::restore_to( - donor.clone(), dest.clone(), code_hash.clone(), rent_allowance.clone(), delta - ); - Self::deposit_event( - RawEvent::Restored(donor, dest, code_hash, rent_allowance, result.is_ok()) - ); - } } }); result } - - fn restore_to( - origin: T::AccountId, - dest: T::AccountId, - code_hash: CodeHash, - rent_allowance: BalanceOf, - delta: Vec, - ) -> DispatchResult { - let mut origin_contract = >::get(&origin) - .and_then(|c| c.get_alive()) - .ok_or(Error::::InvalidSourceContract)?; - - let current_block = >::block_number(); - - if origin_contract.last_write == Some(current_block) { - Err(Error::::InvalidContractOrigin)? - } - - let dest_tombstone = >::get(&dest) - .and_then(|c| c.get_tombstone()) - .ok_or(Error::::InvalidDestinationContract)?; - - let last_write = if !delta.is_empty() { - Some(current_block) - } else { - origin_contract.last_write - }; - - let key_values_taken = delta.iter() - .filter_map(|key| { - child::get_raw( - &origin_contract.child_trie_info(), - &blake2_256(key), - ).map(|value| { - child::kill( - &origin_contract.child_trie_info(), - &blake2_256(key), - ); - - (key, value) - }) - }) - .collect::>(); - - let tombstone = >::new( - // This operation is cheap enough because last_write (delta not included) - // is not this block as it has been checked earlier. - &child::root( - &origin_contract.child_trie_info(), - )[..], - code_hash, - ); - - if tombstone != dest_tombstone { - for (key, value) in key_values_taken { - child::put_raw( - &origin_contract.child_trie_info(), - &blake2_256(key), - &value, - ); - } - - return Err(Error::::InvalidTombstone.into()); - } - - origin_contract.storage_size -= key_values_taken.iter() - .map(|(_, value)| value.len() as u32) - .sum::(); - - >::remove(&origin); - >::insert(&dest, ContractInfo::Alive(RawAliveContractInfo { - trie_id: origin_contract.trie_id, - storage_size: origin_contract.storage_size, - empty_pair_count: origin_contract.empty_pair_count, - total_pair_count: origin_contract.total_pair_count, - code_hash, - rent_allowance, - deduct_block: current_block, - last_write, - })); - - let origin_free_balance = T::Currency::free_balance(&origin); - T::Currency::make_free_balance_be(&origin, >::zero()); - T::Currency::deposit_creating(&dest, origin_free_balance); - - Ok(()) - } } decl_event! { @@ -806,9 +674,6 @@ decl_event! { ::AccountId, ::Hash { - /// Transfer happened `from` to `to` with given `value` as part of a `call` or `instantiate`. - Transfer(AccountId, AccountId, Balance), - /// Contract deployed by address at the specified address. Instantiated(AccountId, AccountId), @@ -820,7 +685,7 @@ decl_event! { /// - `tombstone`: `bool`: True if the evicted contract left behind a tombstone. Evicted(AccountId, bool), - /// Restoration for a contract has been initiated. + /// Restoration for a contract has been successful. /// /// # Params /// @@ -828,8 +693,7 @@ decl_event! { /// - `dest`: `AccountId`: Account ID of the restored contract /// - `code_hash`: `Hash`: Code hash of the restored contract /// - `rent_allowance: `Balance`: Rent allowance of the restored contract - /// - `success`: `bool`: True if the restoration was successful - Restored(AccountId, AccountId, Hash, Balance, bool), + Restored(AccountId, AccountId, Hash, Balance), /// Code with the specified hash has been stored. CodeStored(Hash), diff --git a/frame/contracts/src/rent.rs b/frame/contracts/src/rent.rs index 1d8f47462731b..6afd85aa8eb42 100644 --- a/frame/contracts/src/rent.rs +++ b/frame/contracts/src/rent.rs @@ -18,8 +18,10 @@ use crate::{ AliveContractInfo, BalanceOf, ContractInfo, ContractInfoOf, Module, RawEvent, - TombstoneContractInfo, Trait, + TombstoneContractInfo, Trait, CodeHash, }; +use sp_std::prelude::*; +use sp_io::hashing::blake2_256; use frame_support::storage::child; use frame_support::traits::{Currency, ExistenceRequirement, Get, OnUnbalanced, WithdrawReason}; use frame_support::StorageMap; @@ -396,3 +398,90 @@ pub fn compute_rent_projection( current_block_number + blocks_left, )) } + +/// Restores the destination account using the origin as prototype. +/// +/// The restoration will be performed iff: +/// - origin exists and is alive, +/// - the origin's storage is not written in the current block +/// - the restored account has tombstone +/// - the tombstone matches the hash of the origin storage root, and code hash. +/// +/// Upon succesful restoration, `origin` will be destroyed, all its funds are transferred to +/// the restored account. The restored account will inherit the last write block and its last +/// deduct block will be set to the current block. +pub fn restore_to( + origin: T::AccountId, + dest: T::AccountId, + code_hash: CodeHash, + rent_allowance: BalanceOf, + delta: Vec, +) -> Result<(), &'static str> { + let mut origin_contract = >::get(&origin) + .and_then(|c| c.get_alive()) + .ok_or("Cannot restore from inexisting or tombstone contract")?; + + let child_trie_info = origin_contract.child_trie_info(); + + let current_block = >::block_number(); + + if origin_contract.last_write == Some(current_block) { + return Err("Origin TrieId written in the current block"); + } + + let dest_tombstone = >::get(&dest) + .and_then(|c| c.get_tombstone()) + .ok_or("Cannot restore to inexisting or alive contract")?; + + let last_write = if !delta.is_empty() { + Some(current_block) + } else { + origin_contract.last_write + }; + + let key_values_taken = delta.iter() + .filter_map(|key| { + child::get_raw(&child_trie_info, &blake2_256(key)).map(|value| { + child::kill(&child_trie_info, &blake2_256(key)); + (key, value) + }) + }) + .collect::>(); + + let tombstone = >::new( + // This operation is cheap enough because last_write (delta not included) + // is not this block as it has been checked earlier. + &child::root(&child_trie_info)[..], + code_hash, + ); + + if tombstone != dest_tombstone { + for (key, value) in key_values_taken { + child::put_raw(&child_trie_info, &blake2_256(key), &value); + } + + return Err("Tombstones don't match"); + } + + origin_contract.storage_size -= key_values_taken.iter() + .map(|(_, value)| value.len() as u32) + .sum::(); + + >::remove(&origin); + >::insert(&dest, ContractInfo::Alive(AliveContractInfo:: { + trie_id: origin_contract.trie_id, + storage_size: origin_contract.storage_size, + empty_pair_count: origin_contract.empty_pair_count, + total_pair_count: origin_contract.total_pair_count, + code_hash, + rent_allowance, + deduct_block: current_block, + last_write, + })); + + let origin_free_balance = T::Currency::free_balance(&origin); + T::Currency::make_free_balance_be(&origin, >::zero()); + T::Currency::deposit_creating(&dest, origin_free_balance); + + Ok(()) +} diff --git a/frame/contracts/src/storage.rs b/frame/contracts/src/storage.rs new file mode 100644 index 0000000000000..4c5ad892a967b --- /dev/null +++ b/frame/contracts/src/storage.rs @@ -0,0 +1,195 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! This module contains routines for accessing and altering a contract related state. + +use crate::{ + exec::{AccountIdOf, StorageKey}, + AliveContractInfo, BalanceOf, CodeHash, ContractInfo, ContractInfoOf, Trait, TrieId, +}; +use sp_std::prelude::*; +use sp_io::hashing::blake2_256; +use sp_runtime::traits::Bounded; +use frame_support::{storage::child, StorageMap}; + +/// An error that means that the account requested either doesn't exist or represents a tombstone +/// account. +#[cfg_attr(test, derive(PartialEq, Eq, Debug))] +pub struct ContractAbsentError; + +/// Reads a storage kv pair of a contract. +/// +/// The read is performed from the `trie_id` only. The `address` is not necessary. If the contract +/// doesn't store under the given `key` `None` is returned. +pub fn read_contract_storage(trie_id: &TrieId, key: &StorageKey) -> Option> { + child::get_raw(&crate::child_trie_info(&trie_id), &blake2_256(key)) +} + +/// Update a storage entry into a contract's kv storage. +/// +/// If the `opt_new_value` is `None` then the kv pair is removed. +/// +/// This function also updates the bookkeeping info such as: number of total non-empty pairs a +/// contract owns, the last block the storage was written to, etc. That's why, in contrast to +/// `read_contract_storage`, this function also requires the `account` ID. +/// +/// If the contract specified by the id `account` doesn't exist `Err` is returned.` +pub fn write_contract_storage( + account: &AccountIdOf, + trie_id: &TrieId, + key: &StorageKey, + opt_new_value: Option>, +) -> Result<(), ContractAbsentError> { + let mut new_info = match >::get(account) { + Some(ContractInfo::Alive(alive)) => alive, + None | Some(ContractInfo::Tombstone(_)) => return Err(ContractAbsentError), + }; + + let hashed_key = blake2_256(key); + let child_trie_info = &crate::child_trie_info(&trie_id); + + // In order to correctly update the book keeping we need to fetch the previous + // value of the key-value pair. + // + // It might be a bit more clean if we had an API that supported getting the size + // of the value without going through the loading of it. But at the moment of + // writing, there is no such API. + // + // That's not a show stopper in any case, since the performance cost is + // dominated by the trie traversal anyway. + let opt_prev_value = child::get_raw(&child_trie_info, &hashed_key); + + // Update the total number of KV pairs and the number of empty pairs. + match (&opt_prev_value, &opt_new_value) { + (Some(prev_value), None) => { + new_info.total_pair_count -= 1; + if prev_value.is_empty() { + new_info.empty_pair_count -= 1; + } + }, + (None, Some(new_value)) => { + new_info.total_pair_count += 1; + if new_value.is_empty() { + new_info.empty_pair_count += 1; + } + }, + (Some(prev_value), Some(new_value)) => { + if prev_value.is_empty() { + new_info.empty_pair_count -= 1; + } + if new_value.is_empty() { + new_info.empty_pair_count += 1; + } + } + (None, None) => {} + } + + // Update the total storage size. + let prev_value_len = opt_prev_value + .as_ref() + .map(|old_value| old_value.len() as u32) + .unwrap_or(0); + let new_value_len = opt_new_value + .as_ref() + .map(|new_value| new_value.len() as u32) + .unwrap_or(0); + new_info.storage_size = new_info + .storage_size + .saturating_add(new_value_len) + .saturating_sub(prev_value_len); + + new_info.last_write = Some(>::block_number()); + >::insert(&account, ContractInfo::Alive(new_info)); + + // Finally, perform the change on the storage. + match opt_new_value { + Some(new_value) => child::put_raw(&child_trie_info, &hashed_key, &new_value[..]), + None => child::kill(&child_trie_info, &hashed_key), + } + + Ok(()) +} + +/// Returns the rent allowance set for the contract give by the account id. +pub fn rent_allowance( + account: &AccountIdOf, +) -> Result, ContractAbsentError> { + >::get(account) + .and_then(|i| i.as_alive().map(|i| i.rent_allowance)) + .ok_or(ContractAbsentError) +} + +/// Set the rent allowance for the contract given by the account id. +/// +/// Returns `Err` if the contract doesn't exist or is a tombstone. +pub fn set_rent_allowance( + account: &AccountIdOf, + rent_allowance: BalanceOf, +) -> Result<(), ContractAbsentError> { + >::mutate(account, |maybe_contract_info| match maybe_contract_info { + Some(ContractInfo::Alive(ref mut alive_info)) => { + alive_info.rent_allowance = rent_allowance; + Ok(()) + } + _ => Err(ContractAbsentError), + }) +} + +/// Returns the code hash of the contract specified by `account` ID. +pub fn code_hash(account: &AccountIdOf) -> Result, ContractAbsentError> { + >::get(account) + .and_then(|i| i.as_alive().map(|i| i.code_hash)) + .ok_or(ContractAbsentError) +} + +/// Creates a new contract descriptor in the storage with the given code hash at the given address. +/// +/// Returns `Err` if there is already a contract (or a tombstone) exists at the given address. +pub fn place_contract( + account: &AccountIdOf, + trie_id: TrieId, + ch: CodeHash, +) -> Result<(), &'static str> { + >::mutate(account, |maybe_contract_info| { + if maybe_contract_info.is_some() { + return Err("Alive contract or tombstone already exists"); + } + + *maybe_contract_info = Some( + AliveContractInfo:: { + code_hash: ch, + storage_size: 0, + trie_id, + deduct_block: >::block_number(), + rent_allowance: >::max_value(), + empty_pair_count: 0, + total_pair_count: 0, + last_write: None, + } + .into(), + ); + + Ok(()) + }) +} + +/// Removes the contract and all the storage associated with it. +/// +/// This function doesn't affect the account. +pub fn destroy_contract(address: &AccountIdOf, trie_id: &TrieId) { + >::remove(address); + child::kill_storage(&crate::child_trie_info(&trie_id)); +} diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index ebc2649a46aa5..ee7f06a95013f 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -16,9 +16,7 @@ use crate::{ BalanceOf, ContractAddressFor, ContractInfo, ContractInfoOf, GenesisConfig, Module, - RawAliveContractInfo, RawEvent, Trait, TrieId, Schedule, TrieIdGenerator, - account_db::{AccountDb, DirectAccountDb, OverlayAccountDb}, - gas::Gas, + RawAliveContractInfo, RawEvent, Trait, TrieId, Schedule, TrieIdGenerator, gas::Gas, }; use assert_matches::assert_matches; use hex_literal::*; @@ -64,6 +62,34 @@ impl_outer_dispatch! { } } +pub mod test_utils { + use super::{Test, Balances}; + use crate::{ContractInfoOf, TrieIdGenerator, CodeHash}; + use crate::storage::{write_contract_storage, read_contract_storage}; + use crate::exec::StorageKey; + use frame_support::{StorageMap, traits::Currency}; + + pub fn set_storage(addr: &u64, key: &StorageKey, value: Option>) { + let contract_info = >::get(&addr).unwrap().get_alive().unwrap(); + write_contract_storage::(&1, &contract_info.trie_id, key, value).unwrap(); + } + pub fn get_storage(addr: &u64, key: &StorageKey) -> Option> { + let contract_info = >::get(&addr).unwrap().get_alive().unwrap(); + read_contract_storage(&contract_info.trie_id, key) + } + pub fn place_contract(address: &u64, code_hash: CodeHash) { + let trie_id = ::TrieIdGenerator::trie_id(address); + crate::storage::place_contract::(&address, trie_id, code_hash).unwrap() + } + pub fn set_balance(who: &u64, amount: u64) { + let imbalance = Balances::deposit_creating(who, amount); + drop(imbalance); + } + pub fn get_balance(who: &u64) -> u64 { + Balances::free_balance(who) + } +} + thread_local! { static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); } @@ -82,11 +108,12 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; type Hash = H256; - type Call = (); + type Call = Call; type Hashing = BlakeTwo256; type AccountId = u64; type Lookup = IdentityLookup; @@ -280,6 +307,8 @@ fn returns_base_call_cost() { #[test] fn account_removal_does_not_remove_storage() { + use self::test_utils::{set_storage, get_storage}; + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let trie_id1 = ::TrieIdGenerator::trie_id(&1); let trie_id2 = ::TrieIdGenerator::trie_id(&2); @@ -288,8 +317,7 @@ fn account_removal_does_not_remove_storage() { // Set up two accounts with free balance above the existential threshold. { - let _ = Balances::deposit_creating(&1, 110); - ContractInfoOf::::insert(1, &ContractInfo::Alive(RawAliveContractInfo { + let alice_contract_info = ContractInfo::Alive(RawAliveContractInfo { trie_id: trie_id1.clone(), storage_size: 0, empty_pair_count: 0, @@ -298,15 +326,13 @@ fn account_removal_does_not_remove_storage() { code_hash: H256::repeat_byte(1), rent_allowance: 40, last_write: None, - })); - - let mut overlay = OverlayAccountDb::::new(&DirectAccountDb); - overlay.set_storage(&1, key1.clone(), Some(b"1".to_vec())); - overlay.set_storage(&1, key2.clone(), Some(b"2".to_vec())); - DirectAccountDb.commit(overlay.into_change_set()); + }); + let _ = Balances::deposit_creating(&ALICE, 110); + ContractInfoOf::::insert(ALICE, &alice_contract_info); + set_storage(&ALICE, &key1, Some(b"1".to_vec())); + set_storage(&ALICE, &key2, Some(b"2".to_vec())); - let _ = Balances::deposit_creating(&2, 110); - ContractInfoOf::::insert(2, &ContractInfo::Alive(RawAliveContractInfo { + let bob_contract_info = ContractInfo::Alive(RawAliveContractInfo { trie_id: trie_id2.clone(), storage_size: 0, empty_pair_count: 0, @@ -315,40 +341,39 @@ fn account_removal_does_not_remove_storage() { code_hash: H256::repeat_byte(2), rent_allowance: 40, last_write: None, - })); - - let mut overlay = OverlayAccountDb::::new(&DirectAccountDb); - overlay.set_storage(&2, key1.clone(), Some(b"3".to_vec())); - overlay.set_storage(&2, key2.clone(), Some(b"4".to_vec())); - DirectAccountDb.commit(overlay.into_change_set()); + }); + let _ = Balances::deposit_creating(&BOB, 110); + ContractInfoOf::::insert(BOB, &bob_contract_info); + set_storage(&BOB, &key1, Some(b"3".to_vec())); + set_storage(&BOB, &key2, Some(b"4".to_vec())); } - // Transfer funds from account 1 of such amount that after this transfer - // the balance of account 1 will be below the existential threshold. + // Transfer funds from ALICE account of such amount that after this transfer + // the balance of the ALICE account will be below the existential threshold. // // This does not remove the contract storage as we are not notified about a // account removal. This cannot happen in reality because a contract can only // remove itself by `ext_terminate`. There is no external event that can remove // the account appart from that. - assert_ok!(Balances::transfer(Origin::signed(1), 2, 20)); + assert_ok!(Balances::transfer(Origin::signed(ALICE), BOB, 20)); // Verify that no entries are removed. { assert_eq!( - >::get_storage(&DirectAccountDb, &1, Some(&trie_id1), key1), + get_storage(&ALICE, key1), Some(b"1".to_vec()) ); assert_eq!( - >::get_storage(&DirectAccountDb, &1, Some(&trie_id1), key2), + get_storage(&ALICE, key2), Some(b"2".to_vec()) ); assert_eq!( - >::get_storage(&DirectAccountDb, &2, Some(&trie_id2), key1), + get_storage(&BOB, key1), Some(b"3".to_vec()) ); assert_eq!( - >::get_storage(&DirectAccountDb, &2, Some(&trie_id2), key2), + get_storage(&BOB, key2), Some(b"4".to_vec()) ); } @@ -376,7 +401,7 @@ fn instantiate_and_call_and_deposit_event() { vec![], ); - assert_eq!(System::events(), vec![ + pretty_assertions::assert_eq!(System::events(), vec![ EventRecord { phase: Phase::Initialization, event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), @@ -406,7 +431,9 @@ fn instantiate_and_call_and_deposit_event() { }, EventRecord { phase: Phase::Initialization, - event: MetaEvent::contracts(RawEvent::Transfer(ALICE, BOB, 100)), + event: MetaEvent::balances( + pallet_balances::RawEvent::Transfer(ALICE, BOB, 100) + ), topics: vec![], }, EventRecord { @@ -479,7 +506,7 @@ fn dispatch_call() { vec![], )); - assert_eq!(System::events(), vec![ + pretty_assertions::assert_eq!(System::events(), vec![ EventRecord { phase: Phase::Initialization, event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), @@ -509,7 +536,9 @@ fn dispatch_call() { }, EventRecord { phase: Phase::Initialization, - event: MetaEvent::contracts(RawEvent::Transfer(ALICE, BOB, 100)), + event: MetaEvent::balances( + pallet_balances::RawEvent::Transfer(ALICE, BOB, 100) + ), topics: vec![], }, EventRecord { @@ -606,7 +635,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { ), "contract trapped during execution" ); - assert_eq!(System::events(), vec![ + pretty_assertions::assert_eq!(System::events(), vec![ EventRecord { phase: Phase::Initialization, event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), @@ -636,7 +665,9 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { }, EventRecord { phase: Phase::Initialization, - event: MetaEvent::contracts(RawEvent::Transfer(ALICE, BOB, 100)), + event: MetaEvent::balances( + pallet_balances::RawEvent::Transfer(ALICE, BOB, 100) + ), topics: vec![], }, EventRecord { @@ -937,7 +968,7 @@ fn call_contract_removals() { #[test] fn inherent_claim_surcharge_contract_removals() { - removals(|| Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok()); + removals(|| Contracts::claim_surcharge(Origin::none(), BOB, Some(ALICE)).is_ok()); } #[test] @@ -948,10 +979,10 @@ fn signed_claim_surcharge_contract_removals() { #[test] fn claim_surcharge_malus() { // Test surcharge malus for inherent - claim_surcharge(4, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); - claim_surcharge(3, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); - claim_surcharge(2, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); - claim_surcharge(1, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), false); + claim_surcharge(4, || Contracts::claim_surcharge(Origin::none(), BOB, Some(ALICE)).is_ok(), true); + claim_surcharge(3, || Contracts::claim_surcharge(Origin::none(), BOB, Some(ALICE)).is_ok(), true); + claim_surcharge(2, || Contracts::claim_surcharge(Origin::none(), BOB, Some(ALICE)).is_ok(), true); + claim_surcharge(1, || Contracts::claim_surcharge(Origin::none(), BOB, Some(ALICE)).is_ok(), false); // Test surcharge malus for signed claim_surcharge(4, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), true); @@ -1323,9 +1354,6 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: // Advance 4 blocks, to the 5th. initialize_block(5); - // Preserve `BOB`'s code hash for later introspection. - let bob_code_hash = ContractInfoOf::::get(BOB).unwrap() - .get_alive().unwrap().code_hash; // Call `BOB`, which makes it pay rent. Since the rent allowance is set to 0 // we expect that it will get removed leaving tombstone. assert_err_ignore_postinfo!( @@ -1367,17 +1395,25 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: // Perform a call to `DJANGO`. This should either perform restoration successfully or // fail depending on the test parameters. - assert_ok!(Contracts::call( - Origin::signed(ALICE), - DJANGO, - 0, - GAS_LIMIT, - vec![], - )); + let perform_the_restoration = || { + Contracts::call( + Origin::signed(ALICE), + DJANGO, + 0, + GAS_LIMIT, + vec![], + ) + }; if test_different_storage || test_restore_to_with_dirty_storage { // Parametrization of the test imply restoration failure. Check that `DJANGO` aka // restoration contract is still in place and also that `BOB` doesn't exist. + + assert_err_ignore_postinfo!( + perform_the_restoration(), + "contract trapped during execution" + ); + assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); let django_contract = ContractInfoOf::::get(DJANGO).unwrap() .get_alive().unwrap(); @@ -1386,18 +1422,10 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: assert_eq!(django_contract.deduct_block, System::block_number()); match (test_different_storage, test_restore_to_with_dirty_storage) { (true, false) => { - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::Initialization, - event: MetaEvent::contracts( - RawEvent::Restored(DJANGO, BOB, bob_code_hash, 50, false) - ), - topics: vec![], - }, - ]); + assert_eq!(System::events(), vec![]); } (_, true) => { - assert_eq!(System::events(), vec![ + pretty_assertions::assert_eq!(System::events(), vec![ EventRecord { phase: Phase::Initialization, event: MetaEvent::contracts(RawEvent::Evicted(BOB, true)), @@ -1425,7 +1453,9 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: }, EventRecord { phase: Phase::Initialization, - event: MetaEvent::contracts(RawEvent::Transfer(CHARLIE, DJANGO, 30_000)), + event: MetaEvent::balances( + pallet_balances::RawEvent::Transfer(CHARLIE, DJANGO, 30_000) + ), topics: vec![], }, EventRecord { @@ -1433,22 +1463,13 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: event: MetaEvent::contracts(RawEvent::Instantiated(CHARLIE, DJANGO)), topics: vec![], }, - EventRecord { - phase: Phase::Initialization, - event: MetaEvent::contracts(RawEvent::Restored( - DJANGO, - BOB, - bob_code_hash, - 50, - false, - )), - topics: vec![], - }, ]); } _ => unreachable!(), } } else { + assert_ok!(perform_the_restoration()); + // Here we expect that the restoration is succeeded. Check that the restoration // contract `DJANGO` ceased to exist and that `BOB` returned back. println!("{:?}", ContractInfoOf::::get(BOB)); @@ -1468,7 +1489,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: EventRecord { phase: Phase::Initialization, event: MetaEvent::contracts( - RawEvent::Restored(DJANGO, BOB, bob_contract.code_hash, 50, true) + RawEvent::Restored(DJANGO, BOB, bob_contract.code_hash, 50) ), topics: vec![], }, diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index cb69cd689b265..890915a793d46 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -229,11 +229,8 @@ mod tests { fn get_storage(&self, key: &StorageKey) -> Option> { self.storage.get(key).cloned() } - fn set_storage(&mut self, key: StorageKey, value: Option>) - -> Result<(), &'static str> - { + fn set_storage(&mut self, key: StorageKey, value: Option>) { *self.storage.entry(key).or_insert(Vec::new()) = value.unwrap_or(Vec::new()); - Ok(()) } fn instantiate( &mut self, @@ -304,19 +301,20 @@ mod tests { fn note_dispatch_call(&mut self, call: Call) { self.dispatches.push(DispatchEntry(call)); } - fn note_restore_to( + fn restore_to( &mut self, dest: u64, code_hash: H256, rent_allowance: u64, delta: Vec, - ) { + ) -> Result<(), &'static str> { self.restores.push(RestoreEntry { dest, code_hash, rent_allowance, delta, }); + Ok(()) } fn caller(&self) -> &u64 { &42 @@ -386,9 +384,7 @@ mod tests { fn get_storage(&self, key: &[u8; 32]) -> Option> { (**self).get_storage(key) } - fn set_storage(&mut self, key: [u8; 32], value: Option>) - -> Result<(), &'static str> - { + fn set_storage(&mut self, key: [u8; 32], value: Option>) { (**self).set_storage(key, value) } fn instantiate( @@ -427,14 +423,14 @@ mod tests { fn note_dispatch_call(&mut self, call: Call) { (**self).note_dispatch_call(call) } - fn note_restore_to( + fn restore_to( &mut self, dest: u64, code_hash: H256, rent_allowance: u64, delta: Vec, - ) { - (**self).note_restore_to( + ) -> Result<(), &'static str> { + (**self).restore_to( dest, code_hash, rent_allowance, diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index f87f5d1ef53cc..b393898835ba3 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -51,6 +51,8 @@ enum SpecialTrap { /// Signals that a trap was generated in response to a succesful call to the /// `ext_terminate` host function. Termination, + /// Signals that a trap was generated because of a successful restoration. + Restoration, } /// Can only be used for one call. @@ -100,6 +102,12 @@ pub(crate) fn to_execution_result( data: Vec::new(), }) }, + Some(SpecialTrap::Restoration) => { + return Ok(ExecReturnValue { + status: STATUS_SUCCESS, + data: Vec::new(), + }) + } Some(SpecialTrap::OutOfGas) => { return Err(ExecError { reason: "ran out of gas during contract execution".into(), @@ -387,7 +395,7 @@ define_env!(Env, , let mut key: StorageKey = [0; 32]; read_sandbox_memory_into_buf(ctx, key_ptr, &mut key)?; let value = Some(read_sandbox_memory(ctx, value_ptr, value_len)?); - ctx.ext.set_storage(key, value).map_err(|_| sp_sandbox::HostError)?; + ctx.ext.set_storage(key, value); Ok(()) }, @@ -399,7 +407,7 @@ define_env!(Env, , ext_clear_storage(ctx, key_ptr: u32) => { let mut key: StorageKey = [0; 32]; read_sandbox_memory_into_buf(ctx, key_ptr, &mut key)?; - ctx.ext.set_storage(key, None).map_err(|_| sp_sandbox::HostError)?; + ctx.ext.set_storage(key, None); Ok(()) }, @@ -799,17 +807,18 @@ define_env!(Env, , Ok(()) }, - // Record a request to restore the caller contract to the specified contract. + // Try to restore the given destination contract sacrificing the caller. // - // At the finalization stage, i.e. when all changes from the extrinsic that invoked this - // contract are committed, this function will compute a tombstone hash from the caller's - // storage and the given code hash and if the hash matches the hash found in the tombstone at - // the specified address - kill the caller contract and restore the destination contract and set - // the specified `rent_allowance`. All caller's funds are transferred to the destination. + // This function will compute a tombstone hash from the caller's storage and the given code hash + // and if the hash matches the hash found in the tombstone at the specified address - kill + // the caller contract and restore the destination contract and set the specified `rent_allowance`. + // All caller's funds are transfered to the destination. // - // This function doesn't perform restoration right away but defers it to the end of the - // transaction. If there is no tombstone in the destination address or if the hashes don't match - // then restoration is cancelled and no changes are made. + // If there is no tombstone at the destination address, the hashes don't match or this contract + // instance is already present on the contract call stack, a trap is generated. + // + // Otherwise, the destination contract is restored. This function is diverging and stops execution + // even on success. // // `dest_ptr`, `dest_len` - the pointer and the length of a buffer that encodes `T::AccountId` // with the address of the to be restored contract. @@ -857,14 +866,15 @@ define_env!(Env, , delta }; - ctx.ext.note_restore_to( + if let Ok(()) = ctx.ext.restore_to( dest, code_hash, rent_allowance, delta, - ); - - Ok(()) + ) { + ctx.special_trap = Some(SpecialTrap::Restoration); + } + Err(sp_sandbox::HostError) }, // Returns the size of the scratch buffer. diff --git a/frame/democracy/Cargo.toml b/frame/democracy/Cargo.toml index 9af038aab38da..fea378caca0aa 100644 --- a/frame/democracy/Cargo.toml +++ b/frame/democracy/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/democracy/src/benchmarking.rs b/frame/democracy/src/benchmarking.rs index 3957b38f42926..d0bd73244825e 100644 --- a/frame/democracy/src/benchmarking.rs +++ b/frame/democracy/src/benchmarking.rs @@ -22,7 +22,7 @@ use super::*; use frame_benchmarking::{benchmarks, account}; use frame_support::{ IterableStorageMap, - traits::{Currency, Get, EnsureOrigin, OnInitialize}, + traits::{Currency, Get, EnsureOrigin, OnInitialize, UnfilteredDispatchable}, }; use frame_system::{RawOrigin, Module as System, self, EventRecord}; use sp_runtime::traits::{Bounded, One}; @@ -212,14 +212,14 @@ benchmarks! { for i in 0 .. r { let ref_idx = add_referendum::(i)?; let call = Call::::emergency_cancel(ref_idx); - call.dispatch(origin.clone())?; + call.dispatch_bypass_filter(origin.clone())?; } // Lets now measure one more let referendum_index = add_referendum::(r)?; let call = Call::::emergency_cancel(referendum_index); assert!(Democracy::::referendum_status(referendum_index).is_ok()); - }: { call.dispatch(origin)? } + }: { call.dispatch_bypass_filter(origin)? } verify { // Referendum has been canceled assert!(Democracy::::referendum_status(referendum_index).is_err()); @@ -239,7 +239,7 @@ benchmarks! { ); let call = Call::::external_propose(proposal_hash); - }: { call.dispatch(origin)? } + }: { call.dispatch_bypass_filter(origin)? } verify { // External proposal created ensure!(>::exists(), "External proposal didn't work"); @@ -251,7 +251,7 @@ benchmarks! { let origin = T::ExternalMajorityOrigin::successful_origin(); let proposal_hash = T::Hashing::hash_of(&p); let call = Call::::external_propose_majority(proposal_hash); - }: { call.dispatch(origin)? } + }: { call.dispatch_bypass_filter(origin)? } verify { // External proposal created ensure!(>::exists(), "External proposal didn't work"); @@ -263,7 +263,7 @@ benchmarks! { let origin = T::ExternalDefaultOrigin::successful_origin(); let proposal_hash = T::Hashing::hash_of(&p); let call = Call::::external_propose_default(proposal_hash); - }: { call.dispatch(origin)? } + }: { call.dispatch_bypass_filter(origin)? } verify { // External proposal created ensure!(>::exists(), "External proposal didn't work"); @@ -282,7 +282,7 @@ benchmarks! { let delay = 0; let call = Call::::fast_track(proposal_hash, voting_period.into(), delay.into()); - }: { call.dispatch(origin_fast_track)? } + }: { call.dispatch_bypass_filter(origin_fast_track)? } verify { assert_eq!(Democracy::::referendum_count(), 1, "referendum not created") } @@ -306,7 +306,7 @@ benchmarks! { let call = Call::::veto_external(proposal_hash); let origin = T::VetoOrigin::successful_origin(); ensure!(NextExternal::::get().is_some(), "no external proposal"); - }: { call.dispatch(origin)? } + }: { call.dispatch_bypass_filter(origin)? } verify { assert!(NextExternal::::get().is_none()); let (_, new_vetoers) = >::get(&proposal_hash).ok_or("no blacklist")?; @@ -347,7 +347,7 @@ benchmarks! { let origin = T::ExternalMajorityOrigin::successful_origin(); let proposal_hash = T::Hashing::hash_of(&r); let call = Call::::external_propose_majority(proposal_hash); - call.dispatch(origin)?; + call.dispatch_bypass_filter(origin)?; // External proposal created ensure!(>::exists(), "External proposal didn't work"); diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index ffb7131603549..d56af9c6ef069 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -160,7 +160,6 @@ use sp_runtime::{ use codec::{Encode, Decode, Input}; use frame_support::{ decl_module, decl_storage, decl_event, decl_error, ensure, Parameter, - storage::IterableStorageMap, weights::{Weight, DispatchClass}, traits::{ Currency, ReservableCurrency, LockableCurrency, WithdrawReason, LockIdentifier, Get, @@ -698,25 +697,6 @@ decl_module! { fn deposit_event() = default; - fn on_runtime_upgrade() -> Weight { - // TODO: determine correct weight - migration::migrate::(); - - if let None = StorageVersion::get() { - StorageVersion::put(Releases::V1); - - DepositOf::::translate::< - (BalanceOf, Vec), _ - >(|_, (balance, accounts)| { - Some((accounts, balance)) - }); - - T::MaximumBlockWeight::get() - } else { - T::DbWeight::get().reads(1) - } - } - /// Propose a sensitive action to be taken. /// /// The dispatch origin of this call must be _Signed_ and the sender must diff --git a/frame/democracy/src/tests.rs b/frame/democracy/src/tests.rs index d2b60ae2e2da5..881b0f44629ef 100644 --- a/frame/democracy/src/tests.rs +++ b/frame/democracy/src/tests.rs @@ -22,7 +22,8 @@ use std::cell::RefCell; use codec::Encode; use frame_support::{ impl_outer_origin, impl_outer_dispatch, assert_noop, assert_ok, parameter_types, - impl_outer_event, ord_parameter_types, traits::{Contains, OnInitialize}, weights::Weight, + impl_outer_event, ord_parameter_types, traits::{Contains, OnInitialize, Filter}, + weights::Weight, }; use sp_core::H256; use sp_runtime::{ @@ -41,7 +42,6 @@ mod preimage; mod public_proposals; mod scheduling; mod voting; -mod migration; mod decoders; const AYE: Vote = Vote { aye: true, conviction: Conviction::None }; @@ -74,6 +74,14 @@ impl_outer_event! { } } +// Test that a fitlered call can be dispatched. +pub struct BaseFilter; +impl Filter for BaseFilter { + fn filter(call: &Call) -> bool { + !matches!(call, &Call::Balances(pallet_balances::Call::set_balance(..))) + } +} + // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, Eq, PartialEq, Debug)] pub struct Test; @@ -84,6 +92,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = BaseFilter; type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -225,6 +234,14 @@ fn set_balance_proposal(value: u64) -> Vec { Call::Balances(pallet_balances::Call::set_balance(42, value, 0)).encode() } +#[test] +fn set_balance_proposal_is_correctly_filtered_out() { + for i in 0..10 { + let call = Call::decode(&mut &set_balance_proposal(i)[..]).unwrap(); + assert!(!::BaseCallFilter::filter(&call)); + } +} + fn set_balance_proposal_hash(value: u64) -> H256 { BlakeTwo256::hash(&set_balance_proposal(value)[..]) } diff --git a/frame/democracy/src/tests/cancellation.rs b/frame/democracy/src/tests/cancellation.rs index e75fd28109163..4221865a3e5b0 100644 --- a/frame/democracy/src/tests/cancellation.rs +++ b/frame/democracy/src/tests/cancellation.rs @@ -29,7 +29,7 @@ fn cancel_referendum_should_work() { 0 ); assert_ok!(Democracy::vote(Origin::signed(1), r, aye(1))); - assert_ok!(Democracy::cancel_referendum(Origin::ROOT, r.into())); + assert_ok!(Democracy::cancel_referendum(Origin::root(), r.into())); next_block(); next_block(); @@ -53,8 +53,8 @@ fn cancel_queued_should_work() { assert!(pallet_scheduler::Agenda::::get(6)[0].is_some()); - assert_noop!(Democracy::cancel_queued(Origin::ROOT, 1), Error::::ProposalMissing); - assert_ok!(Democracy::cancel_queued(Origin::ROOT, 0)); + assert_noop!(Democracy::cancel_queued(Origin::root(), 1), Error::::ProposalMissing); + assert_ok!(Democracy::cancel_queued(Origin::root(), 0)); assert!(pallet_scheduler::Agenda::::get(6)[0].is_none()); }); } diff --git a/frame/democracy/src/tests/migration.rs b/frame/democracy/src/tests/migration.rs deleted file mode 100644 index cab8f7f5c936c..0000000000000 --- a/frame/democracy/src/tests/migration.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate 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. - -// Substrate 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 Substrate. If not, see . - -//! The tests for migration. - -use super::*; -use frame_support::{storage::migration, Hashable, traits::OnRuntimeUpgrade}; -use substrate_test_utils::assert_eq_uvec; - -#[test] -fn migration() { - new_test_ext().execute_with(|| { - for i in 0..3 { - let k = i.twox_64_concat(); - let v: (BalanceOf, Vec) = (i * 1000, vec![i]); - migration::put_storage_value(b"Democracy", b"DepositOf", &k, v); - } - StorageVersion::kill(); - - Democracy::on_runtime_upgrade(); - - assert_eq!(StorageVersion::get(), Some(Releases::V1)); - assert_eq_uvec!( - DepositOf::::iter().collect::>(), - vec![ - (0, (vec![0u64], >::from(0u32))), - (1, (vec![1u64], >::from(1000u32))), - (2, (vec![2u64], >::from(2000u32))), - ] - ); - }) -} diff --git a/frame/elections-phragmen/Cargo.toml b/frame/elections-phragmen/Cargo.toml index a33ce0ed29198..08cdc5a98ea7b 100644 --- a/frame/elections-phragmen/Cargo.toml +++ b/frame/elections-phragmen/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME pallet based on seq-Phragmén election method." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } sp-npos-elections = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/npos-elections" } diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index a34ad694a1a80..a483273615506 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -1096,10 +1096,11 @@ mod tests { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; - type Call = (); + type Call = Call; type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; @@ -1117,7 +1118,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = pallet_balances::AccountData; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } @@ -1402,11 +1404,8 @@ mod tests { // historical note: helper function was created in a period of time in which the API of vote // call was changing. Currently it is a wrapper for the original call and does not do much. // Nonetheless, totally harmless. - if let Origin::system(frame_system::RawOrigin::Signed(_account)) = origin { - Elections::vote(origin, votes, stake) - } else { - panic!("vote origin must be signed"); - } + ensure_signed(origin.clone()).expect("vote origin must be signed"); + Elections::vote(origin, votes, stake) } fn votes_of(who: &u64) -> Vec { @@ -2384,7 +2383,7 @@ mod tests { assert_ok!(submit_candidacy(Origin::signed(3))); assert_ok!(vote(Origin::signed(3), vec![3], 30)); - assert_ok!(Elections::remove_member(Origin::ROOT, 4, false)); + assert_ok!(Elections::remove_member(Origin::root(), 4, false)); assert_eq!(balances(&4), (35, 2)); // slashed assert_eq!(Elections::election_rounds(), 2); // new election round @@ -2407,7 +2406,7 @@ mod tests { // no replacement yet. assert_err_with_weight!( - Elections::remove_member(Origin::ROOT, 4, true), + Elections::remove_member(Origin::root(), 4, true), Error::::InvalidReplacement, Some(6000000), ); @@ -2429,7 +2428,7 @@ mod tests { // there is a replacement! and this one needs a weight refund. assert_err_with_weight!( - Elections::remove_member(Origin::ROOT, 4, false), + Elections::remove_member(Origin::root(), 4, false), Error::::InvalidReplacement, Some(6000000) // only thing that matters for now is that it is NOT the full block. ); @@ -2588,7 +2587,7 @@ mod tests { Elections::end_block(System::block_number()); assert_eq!(Elections::members_ids(), vec![2, 4]); - assert_ok!(Elections::remove_member(Origin::ROOT, 2, true)); + assert_ok!(Elections::remove_member(Origin::root(), 2, true)); assert_eq!(Elections::members_ids(), vec![4, 5]); }); } diff --git a/frame/elections/Cargo.toml b/frame/elections/Cargo.toml index 60bb2dcb62e9c..d03ad4f056477 100644 --- a/frame/elections/Cargo.toml +++ b/frame/elections/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } diff --git a/frame/elections/src/mock.rs b/frame/elections/src/mock.rs index 51395d4cb467d..97f8559f95313 100644 --- a/frame/elections/src/mock.rs +++ b/frame/elections/src/mock.rs @@ -39,8 +39,9 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; - type Call = (); + type Call = Call; type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -60,7 +61,8 @@ impl frame_system::Trait for Test { type Version = (); type ModuleToIndex = (); type AccountData = pallet_balances::AccountData; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/elections/src/tests.rs b/frame/elections/src/tests.rs index 8a9f58b54a265..247b6272524b1 100644 --- a/frame/elections/src/tests.rs +++ b/frame/elections/src/tests.rs @@ -671,7 +671,7 @@ fn retracting_active_voter_should_slash_reporter() { assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(8); - assert_ok!(Elections::set_desired_seats(Origin::ROOT, 3)); + assert_ok!(Elections::set_desired_seats(Origin::root(), 3)); assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(10); @@ -1245,7 +1245,7 @@ fn election_second_tally_should_use_runners_up() { System::set_block_number(8); assert_ok!(Elections::set_approvals(Origin::signed(6), vec![false, false, true, false], 1, 0, 60)); - assert_ok!(Elections::set_desired_seats(Origin::ROOT, 3)); + assert_ok!(Elections::set_desired_seats(Origin::root(), 3)); assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(10); diff --git a/frame/evm/Cargo.toml b/frame/evm/Cargo.toml index c465090743f14..1a6d691cde0f9 100644 --- a/frame/evm/Cargo.toml +++ b/frame/evm/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } frame-system = { version = "2.0.0-rc3", default-features = false, path = "../system" } pallet-timestamp = { version = "2.0.0-rc3", default-features = false, path = "../timestamp" } diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index c5df79fdb8791..72392629d6efb 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -30,7 +30,7 @@ use codec::{Encode, Decode}; #[cfg(feature = "std")] use serde::{Serialize, Deserialize}; use frame_support::{ensure, decl_module, decl_storage, decl_event, decl_error}; -use frame_support::weights::{Weight, DispatchClass, FunctionOf, Pays}; +use frame_support::weights::Weight; use frame_support::traits::{Currency, WithdrawReason, ExistenceRequirement, Get}; use frame_system::{self as system, ensure_signed}; use sp_runtime::ModuleId; @@ -159,7 +159,8 @@ decl_storage! { trait Store for Module as EVM { Accounts get(fn accounts): map hasher(blake2_128_concat) H160 => Account; AccountCodes get(fn account_codes): map hasher(blake2_128_concat) H160 => Vec; - AccountStorages: double_map hasher(blake2_128_concat) H160, hasher(blake2_128_concat) H256 => H256; + AccountStorages get(fn account_storages): + double_map hasher(blake2_128_concat) H160, hasher(blake2_128_concat) H256 => H256; } add_extra_genesis { @@ -273,12 +274,7 @@ decl_module! { } /// Issue an EVM call operation. This is similar to a message call transaction in Ethereum. - #[weight = FunctionOf( - |(_, _, _, gas_limit, gas_price, _): (&H160, &Vec, &U256, &u32, &U256, &Option)| - (*gas_price).saturated_into::().saturating_mul(*gas_limit as Weight), - DispatchClass::Normal, - Pays::Yes, - )] + #[weight = (*gas_price).saturated_into::().saturating_mul(*gas_limit as Weight)] fn call( origin, target: H160, @@ -306,12 +302,7 @@ decl_module! { /// Issue an EVM create operation. This is similar to a contract creation transaction in /// Ethereum. - #[weight = FunctionOf( - |(_, _, gas_limit, gas_price, _): (&Vec, &U256, &u32, &U256, &Option)| - (*gas_price).saturated_into::().saturating_mul(*gas_limit as Weight), - DispatchClass::Normal, - Pays::Yes, - )] + #[weight = (*gas_price).saturated_into::().saturating_mul(*gas_limit as Weight)] fn create( origin, init: Vec, @@ -339,12 +330,7 @@ decl_module! { } /// Issue an EVM create2 operation. - #[weight = FunctionOf( - |(_, _, _, gas_limit, gas_price, _): (&Vec, &H256, &U256, &u32, &U256, &Option)| - (*gas_price).saturated_into::().saturating_mul(*gas_limit as Weight), - DispatchClass::Normal, - Pays::Yes, - )] + #[weight = (*gas_price).saturated_into::().saturating_mul(*gas_limit as Weight)] fn create2( origin, init: Vec, diff --git a/frame/example-offchain-worker/Cargo.toml b/frame/example-offchain-worker/Cargo.toml index d32f206de8621..f93ffcf9e4cb7 100644 --- a/frame/example-offchain-worker/Cargo.toml +++ b/frame/example-offchain-worker/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME example pallet for offchain worker" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } frame-system = { version = "2.0.0-rc3", default-features = false, path = "../system" } serde = { version = "1.0.101", optional = true } diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index 6984c0b56d300..ded36fe4b34d9 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -54,6 +54,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Call = (); type Index = u64; @@ -75,7 +76,8 @@ impl frame_system::Trait for Test { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/example/Cargo.toml b/frame/example/Cargo.toml index 89be881437a66..597f2266c3c06 100644 --- a/frame/example/Cargo.toml +++ b/frame/example/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } frame-system = { version = "2.0.0-rc3", default-features = false, path = "../system" } pallet-balances = { version = "2.0.0-rc3", default-features = false, path = "../balances" } diff --git a/frame/example/src/lib.rs b/frame/example/src/lib.rs index c0021fb1c50fe..fb97d03a33360 100644 --- a/frame/example/src/lib.rs +++ b/frame/example/src/lib.rs @@ -256,7 +256,7 @@ use sp_std::marker::PhantomData; use frame_support::{ - dispatch::DispatchResult, decl_module, decl_storage, decl_event, + dispatch::{DispatchResult, IsSubType}, decl_module, decl_storage, decl_event, weights::{DispatchClass, ClassifyDispatch, WeighData, Weight, PaysFee, Pays}, }; use sp_std::prelude::*; @@ -609,14 +609,13 @@ impl sp_std::fmt::Debug for WatchDummy { } } -impl SignedExtension for WatchDummy { +impl SignedExtension for WatchDummy +where + ::Call: IsSubType, T>, +{ const IDENTIFIER: &'static str = "WatchDummy"; type AccountId = T::AccountId; - // Note that this could also be assigned to the top-level call enum. It is passed into the - // Balances Pallet directly and since `Trait: pallet_balances::Trait`, you could also use `T::Call`. - // In that case, you would have had access to all call variants and could match on variants from - // other pallets. - type Call = Call; + type Call = ::Call; type AdditionalSigned = (); type Pre = (); @@ -635,8 +634,8 @@ impl SignedExtension for WatchDummy { } // check for `set_dummy` - match call { - Call::set_dummy(..) => { + match call.is_sub_type() { + Some(Call::set_dummy(..)) => { sp_runtime::print("set_dummy was received."); let mut valid_tx = ValidTransaction::default(); @@ -711,8 +710,8 @@ mod tests { use super::*; use frame_support::{ - assert_ok, impl_outer_origin, parameter_types, weights::{DispatchInfo, GetDispatchInfo}, - traits::{OnInitialize, OnFinalize} + assert_ok, impl_outer_origin, parameter_types, impl_outer_dispatch, + weights::{DispatchInfo, GetDispatchInfo}, traits::{OnInitialize, OnFinalize} }; use sp_core::H256; // The testing primitives are very useful for avoiding having to work with signatures @@ -727,6 +726,12 @@ mod tests { pub enum Origin for Test where system = frame_system {} } + impl_outer_dispatch! { + pub enum OuterCall for Test where origin: Origin { + self::Example, + } + } + // For testing the pallet, we construct most of a mock runtime. This means // first constructing a configuration type (`Test`) which `impl`s each of the // configuration traits of pallets we want to use. @@ -739,11 +744,12 @@ mod tests { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; type Hash = H256; - type Call = (); + type Call = OuterCall; type Hashing = BlakeTwo256; type AccountId = u64; type Lookup = IdentityLookup; @@ -760,7 +766,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = pallet_balances::AccountData; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } parameter_types! { @@ -827,7 +834,7 @@ mod tests { #[test] fn signed_ext_watch_dummy_works() { new_test_ext().execute_with(|| { - let call = >::set_dummy(10); + let call = >::set_dummy(10).into(); let info = DispatchInfo::default(); assert_eq!( diff --git a/frame/executive/Cargo.toml b/frame/executive/Cargo.toml index 303cf1c1f72cc..a922333eb9057 100644 --- a/frame/executive/Cargo.toml +++ b/frame/executive/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME executives engine" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } frame-system = { version = "2.0.0-rc3", default-features = false, path = "../system" } serde = { version = "1.0.101", optional = true } diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 4506f406a370c..eea6e3b85dfeb 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -545,6 +545,7 @@ mod tests { }; } impl frame_system::Trait for Runtime { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type Call = Call; @@ -709,7 +710,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("05a38fa4a48ca80ffa8482304be7749a484dc8c9c31462a570d0fbadde6a3633").into(), + state_root: hex!("e8ff7b3dd4375f6f3a76e24a1999e2a7be2d15b353e49ac94ace1eae3e80eb87").into(), extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(), digest: Digest { logs: vec![], }, }, diff --git a/frame/finality-tracker/Cargo.toml b/frame/finality-tracker/Cargo.toml index a8e11e83f9828..497f4fdec7883 100644 --- a/frame/finality-tracker/Cargo.toml +++ b/frame/finality-tracker/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", default-features = false, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } sp-inherents = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/inherents" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/finality-tracker/src/lib.rs b/frame/finality-tracker/src/lib.rs index d288dc9da3020..c452777459732 100644 --- a/frame/finality-tracker/src/lib.rs +++ b/frame/finality-tracker/src/lib.rs @@ -258,6 +258,7 @@ mod tests { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -279,7 +280,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } parameter_types! { @@ -343,10 +345,7 @@ mod tests { &Default::default(), Default::default(), ); - assert_ok!(FinalityTracker::dispatch( - Call::final_hint(i-1), - Origin::NONE, - )); + assert_ok!(FinalityTracker::final_hint(Origin::none(), i - 1)); FinalityTracker::on_finalize(i); let hdr = System::finalize(); parent_hash = hdr.hash(); diff --git a/frame/generic-asset/Cargo.toml b/frame/generic-asset/Cargo.toml index eb62b2986f260..cdac7a6d6d4ae 100644 --- a/frame/generic-asset/Cargo.toml +++ b/frame/generic-asset/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } diff --git a/frame/generic-asset/src/lib.rs b/frame/generic-asset/src/lib.rs index 28f603fb7c845..3f07a28b62be8 100644 --- a/frame/generic-asset/src/lib.rs +++ b/frame/generic-asset/src/lib.rs @@ -1120,6 +1120,7 @@ impl PartialEq for ElevatedTrait { } impl Eq for ElevatedTrait {} impl frame_system::Trait for ElevatedTrait { + type BaseCallFilter = T::BaseCallFilter; type Origin = T::Origin; type Call = T::Call; type Index = T::Index; @@ -1141,7 +1142,8 @@ impl frame_system::Trait for ElevatedTrait { type Version = T::Version; type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } impl Trait for ElevatedTrait { diff --git a/frame/generic-asset/src/mock.rs b/frame/generic-asset/src/mock.rs index 04d38ade524dc..bbdb4fa28dc5a 100644 --- a/frame/generic-asset/src/mock.rs +++ b/frame/generic-asset/src/mock.rs @@ -46,6 +46,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -67,7 +68,8 @@ impl frame_system::Trait for Test { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/generic-asset/src/tests.rs b/frame/generic-asset/src/tests.rs index d5c0a877dfe75..a094f69ba1fc5 100644 --- a/frame/generic-asset/src/tests.rs +++ b/frame/generic-asset/src/tests.rs @@ -598,7 +598,7 @@ fn create_reserved_should_create_a_default_account_with_the_balance_given() { let created_asset_id = 9; let created_account_id = 0; - assert_ok!(GenericAsset::create_reserved(Origin::ROOT, created_asset_id, options)); + assert_ok!(GenericAsset::create_reserved(Origin::root(), created_asset_id, options)); // Tests for side effects. assert_eq!(>::get(created_asset_id), expected_total_issuance); diff --git a/frame/grandpa/Cargo.toml b/frame/grandpa/Cargo.toml index 99a5dad149964..1ec939c9bd81c 100644 --- a/frame/grandpa/Cargo.toml +++ b/frame/grandpa/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/application-crypto" } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } sp-finality-grandpa = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/finality-grandpa" } diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index a0a9095bc3771..048148e3cfe6c 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -94,6 +94,7 @@ parameter_types! { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index e15021733ffa0..2337e00e8d203 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -25,7 +25,7 @@ use codec::{Decode, Encode}; use fg_primitives::ScheduledChange; use frame_support::{ assert_err, assert_ok, - traits::{Currency, OnFinalize}, + traits::{Currency, OnFinalize, UnfilteredDispatchable}, }; use frame_system::{EventRecord, Phase}; use sp_core::H256; @@ -376,7 +376,7 @@ fn report_equivocation_current_set_works() { // report the equivocation and the tx should be dispatched successfully let inner = report_equivocation(equivocation_proof, key_owner_proof).unwrap(); - assert_ok!(Grandpa::dispatch(inner, Origin::signed(1))); + assert_ok!(inner.dispatch_bypass_filter(Origin::signed(1))); start_era(2); @@ -457,7 +457,7 @@ fn report_equivocation_old_set_works() { // report the equivocation using the key ownership proof generated on // the old set, the tx should be dispatched successfully let inner = report_equivocation(equivocation_proof, key_owner_proof).unwrap(); - assert_ok!(Grandpa::dispatch(inner, Origin::signed(1))); + assert_ok!(inner.dispatch_bypass_filter(Origin::signed(1))); start_era(3); diff --git a/frame/identity/Cargo.toml b/frame/identity/Cargo.toml index 39eae1b68902f..0435d8c08664b 100644 --- a/frame/identity/Cargo.toml +++ b/frame/identity/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } enumflags2 = { version = "0.6.2" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } diff --git a/frame/identity/src/lib.rs b/frame/identity/src/lib.rs index 22459d368bddc..1539bfd061b60 100644 --- a/frame/identity/src/lib.rs +++ b/frame/identity/src/lib.rs @@ -75,11 +75,10 @@ use sp_runtime::traits::{StaticLookup, Zero, AppendZerosInput}; use frame_support::{ decl_module, decl_event, decl_storage, ensure, decl_error, dispatch::DispatchResultWithPostInfo, - traits::{Currency, ReservableCurrency, OnUnbalanced, Get, BalanceStatus, EnsureOrigin}, + traits::{Currency, ReservableCurrency, OnUnbalanced, Get, BalanceStatus, EnsureOrigin, MigrateAccount}, weights::Weight, }; -use frame_system::{self as system, ensure_signed, ensure_root}; -use frame_support::traits::MigrateAccount; +use frame_system::{self as system, ensure_signed}; #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; @@ -639,9 +638,7 @@ decl_module! { /// # #[weight = weight_for::add_registrar::(T::MaxRegistrars::get().into()) ] fn add_registrar(origin, account: T::AccountId) -> DispatchResultWithPostInfo { - T::RegistrarOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::RegistrarOrigin::ensure_origin(origin)?; let (i, registrar_count) = >::try_mutate( |registrars| -> Result<(RegistrarIndex, usize), DispatchError> { @@ -1112,9 +1109,7 @@ decl_module! { T::MaxAdditionalFields::get().into(), // X )] fn kill_identity(origin, target: ::Source) -> DispatchResultWithPostInfo { - T::ForceOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::ForceOrigin::ensure_origin(origin)?; // Figure out who we're meant to be clearing. let target = T::Lookup::lookup(target)?; @@ -1198,6 +1193,7 @@ mod tests { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -1219,7 +1215,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = pallet_balances::AccountData; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } parameter_types! { @@ -1455,7 +1452,7 @@ mod tests { new_test_ext().execute_with(|| { assert_ok!(Identity::set_identity(Origin::signed(10), ten())); assert_ok!(Identity::set_subs(Origin::signed(10), vec![(20, Data::Raw(vec![40; 1]))])); - assert_ok!(Identity::kill_identity(Origin::ROOT, 10)); + assert_ok!(Identity::kill_identity(Origin::signed(2), 10)); assert_eq!(Balances::free_balance(10), 80); assert!(Identity::super_of(20).is_none()); }); diff --git a/frame/im-online/Cargo.toml b/frame/im-online/Cargo.toml index 99979a47c0cfc..2f89ff2cb2d15 100644 --- a/frame/im-online/Cargo.toml +++ b/frame/im-online/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/application-crypto" } pallet-authorship = { version = "2.0.0-rc3", default-features = false, path = "../authorship" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } serde = { version = "1.0.101", optional = true } diff --git a/frame/im-online/src/benchmarking.rs b/frame/im-online/src/benchmarking.rs index 63457168b3638..92d9b9d5a5364 100644 --- a/frame/im-online/src/benchmarking.rs +++ b/frame/im-online/src/benchmarking.rs @@ -24,8 +24,9 @@ use super::*; use frame_system::RawOrigin; use frame_benchmarking::benchmarks; use sp_core::offchain::{OpaquePeerId, OpaqueMultiaddr}; -use sp_runtime::traits::{ValidateUnsigned, Zero, Dispatchable}; +use sp_runtime::traits::{ValidateUnsigned, Zero}; use sp_runtime::transaction_validity::TransactionSource; +use frame_support::traits::UnfilteredDispatchable; use crate::Module as ImOnline; @@ -85,7 +86,7 @@ benchmarks! { let call = Call::heartbeat(input_heartbeat, signature); }: { ImOnline::::validate_unsigned(TransactionSource::InBlock, &call)?; - call.dispatch(RawOrigin::None.into())?; + call.dispatch_bypass_filter(RawOrigin::None.into())?; } } diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 12a3a0f6aa7bc..cbbb3c07ce2a5 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -104,6 +104,7 @@ parameter_types! { } impl frame_system::Trait for Runtime { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -125,7 +126,8 @@ impl frame_system::Trait for Runtime { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index 7619781b68df0..835d8440e6d5b 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -135,7 +135,7 @@ fn heartbeat( e @ _ => <&'static str>::from(e), })?; ImOnline::heartbeat( - Origin::system(frame_system::RawOrigin::None), + Origin::none(), heartbeat, signature, ) diff --git a/frame/indices/Cargo.toml b/frame/indices/Cargo.toml index a52ad8e3311d7..2c856064e7a12 100644 --- a/frame/indices/Cargo.toml +++ b/frame/indices/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-keyring = { version = "2.0.0-rc3", optional = true, path = "../../primitives/keyring" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } diff --git a/frame/indices/src/benchmarking.rs b/frame/indices/src/benchmarking.rs index 843e4e2faa592..a6b543bb43f4a 100644 --- a/frame/indices/src/benchmarking.rs +++ b/frame/indices/src/benchmarking.rs @@ -83,11 +83,25 @@ benchmarks! { T::Currency::make_free_balance_be(&recipient, BalanceOf::::max_value()); // Claim the index Indices::::claim(RawOrigin::Signed(original).into(), account_index)?; - }: _(RawOrigin::Root, recipient.clone(), account_index) + }: _(RawOrigin::Root, recipient.clone(), account_index, false) verify { assert_eq!(Accounts::::get(account_index).unwrap().0, recipient); } + freeze { + // Index being claimed + let i in 0 .. 1000; + let account_index = T::AccountIndex::from(i); + // Setup accounts + let caller: T::AccountId = account("caller", 0, SEED); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + // Claim the index + Indices::::claim(RawOrigin::Signed(caller.clone()).into(), account_index)?; + }: _(RawOrigin::Signed(caller.clone()), account_index) + verify { + assert_eq!(Accounts::::get(account_index).unwrap().2, true); + } + // TODO in another PR: lookup and unlookup trait weights (not critical) } @@ -104,6 +118,7 @@ mod tests { assert_ok!(test_benchmark_transfer::()); assert_ok!(test_benchmark_free::()); assert_ok!(test_benchmark_force_transfer::()); + assert_ok!(test_benchmark_freeze::()); }); } } diff --git a/frame/indices/src/lib.rs b/frame/indices/src/lib.rs index ef4e0082f4fb6..e58112403f628 100644 --- a/frame/indices/src/lib.rs +++ b/frame/indices/src/lib.rs @@ -62,9 +62,9 @@ decl_storage! { pub Accounts build(|config: &GenesisConfig| config.indices.iter() .cloned() - .map(|(a, b)| (a, (b, Zero::zero()))) + .map(|(a, b)| (a, (b, Zero::zero(), false))) .collect::>() - ): map hasher(blake2_128_concat) T::AccountIndex => Option<(T::AccountId, BalanceOf)>; + ): map hasher(blake2_128_concat) T::AccountIndex => Option<(T::AccountId, BalanceOf, bool)>; } add_extra_genesis { config(indices): Vec<(T::AccountIndex, T::AccountId)>; @@ -80,6 +80,8 @@ decl_event!( IndexAssigned(AccountId, AccountIndex), /// A account index has been freed up (unassigned). IndexFreed(AccountIndex), + /// A account index has been frozen to its current account ID. + IndexFrozen(AccountIndex, AccountId), } ); @@ -93,6 +95,8 @@ decl_error! { InUse, /// The source and destination accounts are identical. NotTransfer, + /// The index is permanent and may not be freed/changed. + Permanent, } } @@ -125,7 +129,7 @@ decl_module! { Accounts::::try_mutate(index, |maybe_value| { ensure!(maybe_value.is_none(), Error::::InUse); - *maybe_value = Some((who.clone(), T::Deposit::get())); + *maybe_value = Some((who.clone(), T::Deposit::get(), false)); T::Currency::reserve(&who, T::Deposit::get()) })?; Self::deposit_event(RawEvent::IndexAssigned(who, index)); @@ -158,10 +162,11 @@ decl_module! { ensure!(who != new, Error::::NotTransfer); Accounts::::try_mutate(index, |maybe_value| -> DispatchResult { - let (account, amount) = maybe_value.take().ok_or(Error::::NotAssigned)?; + let (account, amount, perm) = maybe_value.take().ok_or(Error::::NotAssigned)?; + ensure!(!perm, Error::::Permanent); ensure!(&account == &who, Error::::NotOwner); let lost = T::Currency::repatriate_reserved(&who, &new, amount, Reserved)?; - *maybe_value = Some((new.clone(), amount.saturating_sub(lost))); + *maybe_value = Some((new.clone(), amount.saturating_sub(lost), false)); Ok(()) })?; Self::deposit_event(RawEvent::IndexAssigned(new, index)); @@ -191,7 +196,8 @@ decl_module! { let who = ensure_signed(origin)?; Accounts::::try_mutate(index, |maybe_value| -> DispatchResult { - let (account, amount) = maybe_value.take().ok_or(Error::::NotAssigned)?; + let (account, amount, perm) = maybe_value.take().ok_or(Error::::NotAssigned)?; + ensure!(!perm, Error::::Permanent); ensure!(&account == &who, Error::::NotOwner); T::Currency::unreserve(&who, amount); Ok(()) @@ -206,6 +212,7 @@ decl_module! { /// /// - `index`: the index to be (re-)assigned. /// - `new`: the new owner of the index. This function is a no-op if it is equal to sender. + /// - `freeze`: if set to `true`, will freeze the index so it cannot be transferred. /// /// Emits `IndexAssigned` if successful. /// @@ -221,17 +228,50 @@ decl_module! { /// - Writes: Indices Accounts, System Account (original owner) /// # #[weight = T::DbWeight::get().reads_writes(2, 2) + 25 * WEIGHT_PER_MICROS] - fn force_transfer(origin, new: T::AccountId, index: T::AccountIndex) { + fn force_transfer(origin, new: T::AccountId, index: T::AccountIndex, freeze: bool) { ensure_root(origin)?; Accounts::::mutate(index, |maybe_value| { - if let Some((account, amount)) = maybe_value.take() { + if let Some((account, amount, _)) = maybe_value.take() { T::Currency::unreserve(&account, amount); } - *maybe_value = Some((new.clone(), Zero::zero())); + *maybe_value = Some((new.clone(), Zero::zero(), freeze)); }); Self::deposit_event(RawEvent::IndexAssigned(new, index)); } + + /// Freeze an index so it will always point to the sender account. This consumes the deposit. + /// + /// The dispatch origin for this call must be _Signed_ and the signing account must have a + /// non-frozen account `index`. + /// + /// - `index`: the index to be frozen in place. + /// + /// Emits `IndexFrozen` if successful. + /// + /// # + /// - `O(1)`. + /// - One storage mutation (codec `O(1)`). + /// - Up to one slash operation. + /// - One event. + /// ------------------- + /// - Base Weight: 30.86 µs + /// - DB Weight: 1 Read/Write (Accounts) + /// # + #[weight = T::DbWeight::get().reads_writes(1, 1) + 30 * WEIGHT_PER_MICROS] + fn freeze(origin, index: T::AccountIndex) { + let who = ensure_signed(origin)?; + + Accounts::::try_mutate(index, |maybe_value| -> DispatchResult { + let (account, amount, perm) = maybe_value.take().ok_or(Error::::NotAssigned)?; + ensure!(!perm, Error::::Permanent); + ensure!(&account == &who, Error::::NotOwner); + T::Currency::slash_reserved(&who, amount); + *maybe_value = Some((account, Zero::zero(), true)); + Ok(()) + })?; + Self::deposit_event(RawEvent::IndexFrozen(index, who)); + } } } diff --git a/frame/indices/src/mock.rs b/frame/indices/src/mock.rs index 8a8fd98c2ab0f..665b54bab5622 100644 --- a/frame/indices/src/mock.rs +++ b/frame/indices/src/mock.rs @@ -50,6 +50,7 @@ parameter_types! { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Call = (); type Index = u64; @@ -71,7 +72,8 @@ impl frame_system::Trait for Test { type Version = (); type ModuleToIndex = (); type AccountData = pallet_balances::AccountData; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/indices/src/tests.rs b/frame/indices/src/tests.rs index 7f416afbd3392..e288871d55307 100644 --- a/frame/indices/src/tests.rs +++ b/frame/indices/src/tests.rs @@ -48,6 +48,20 @@ fn freeing_should_work() { }); } +#[test] +fn freezing_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_noop!(Indices::freeze(Some(1).into(), 1), Error::::NotAssigned); + assert_noop!(Indices::freeze(Some(2).into(), 0), Error::::NotOwner); + assert_ok!(Indices::freeze(Some(1).into(), 0)); + assert_noop!(Indices::freeze(Some(1).into(), 0), Error::::Permanent); + + assert_noop!(Indices::free(Some(1).into(), 0), Error::::Permanent); + assert_noop!(Indices::transfer(Some(1).into(), 2, 0), Error::::Permanent); + }); +} + #[test] fn indexing_lookup_should_work() { new_test_ext().execute_with(|| { @@ -87,7 +101,7 @@ fn transfer_index_on_accounts_should_work() { fn force_transfer_index_on_preowned_should_work() { new_test_ext().execute_with(|| { assert_ok!(Indices::claim(Some(1).into(), 0)); - assert_ok!(Indices::force_transfer(Origin::ROOT, 3, 0)); + assert_ok!(Indices::force_transfer(Origin::root(), 3, 0, false)); assert_eq!(Balances::reserved_balance(1), 0); assert_eq!(Balances::reserved_balance(3), 0); assert_eq!(Indices::lookup_index(0), Some(3)); @@ -97,7 +111,7 @@ fn force_transfer_index_on_preowned_should_work() { #[test] fn force_transfer_index_on_free_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Indices::force_transfer(Origin::ROOT, 3, 0)); + assert_ok!(Indices::force_transfer(Origin::root(), 3, 0, false)); assert_eq!(Balances::reserved_balance(3), 0); assert_eq!(Indices::lookup_index(0), Some(3)); }); diff --git a/frame/membership/Cargo.toml b/frame/membership/Cargo.toml index 6ea035c3f7e02..e0c94da3082ec 100644 --- a/frame/membership/Cargo.toml +++ b/frame/membership/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index 0cc3dc526cb9c..0d5555f0907ae 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -28,7 +28,7 @@ use frame_support::{ decl_module, decl_storage, decl_event, decl_error, traits::{ChangeMembers, InitializeMembers, EnsureOrigin}, }; -use frame_system::{self as system, ensure_root, ensure_signed}; +use frame_system::{self as system, ensure_signed}; pub trait Trait: frame_system::Trait { /// The overarching event type. @@ -120,9 +120,7 @@ decl_module! { /// May only be called from `AddOrigin` or root. #[weight = 50_000_000] pub fn add_member(origin, who: T::AccountId) { - T::AddOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::AddOrigin::ensure_origin(origin)?; let mut members = >::get(); let location = members.binary_search(&who).err().ok_or(Error::::AlreadyMember)?; @@ -139,9 +137,7 @@ decl_module! { /// May only be called from `RemoveOrigin` or root. #[weight = 50_000_000] pub fn remove_member(origin, who: T::AccountId) { - T::RemoveOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::RemoveOrigin::ensure_origin(origin)?; let mut members = >::get(); let location = members.binary_search(&who).ok().ok_or(Error::::NotMember)?; @@ -161,9 +157,7 @@ decl_module! { /// Prime membership is *not* passed from `remove` to `add`, if extant. #[weight = 50_000_000] pub fn swap_member(origin, remove: T::AccountId, add: T::AccountId) { - T::SwapOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::SwapOrigin::ensure_origin(origin)?; if remove == add { return Ok(()) } @@ -190,9 +184,7 @@ decl_module! { /// May only be called from `ResetOrigin` or root. #[weight = 50_000_000] pub fn reset_members(origin, members: Vec) { - T::ResetOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::ResetOrigin::ensure_origin(origin)?; let mut members = members; members.sort(); @@ -241,9 +233,7 @@ decl_module! { /// Set the prime member. Must be a current member. #[weight = 50_000_000] pub fn set_prime(origin, who: T::AccountId) { - T::PrimeOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::PrimeOrigin::ensure_origin(origin)?; Self::members().binary_search(&who).ok().ok_or(Error::::NotMember)?; Prime::::put(&who); T::MembershipChanged::set_prime(Some(who)); @@ -252,9 +242,7 @@ decl_module! { /// Remove the prime member if it exists. #[weight = 50_000_000] pub fn clear_prime(origin) { - T::PrimeOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::PrimeOrigin::ensure_origin(origin)?; Prime::::kill(); T::MembershipChanged::set_prime(None); } @@ -303,6 +291,7 @@ mod tests { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -324,7 +313,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } ord_parameter_types! { diff --git a/frame/metadata/Cargo.toml b/frame/metadata/Cargo.toml index 459f76b5e8b91..a8fb9eae5fb80 100644 --- a/frame/metadata/Cargo.toml +++ b/frame/metadata/Cargo.toml @@ -12,7 +12,7 @@ description = "Decodable variant of the RuntimeMetadata." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } diff --git a/frame/multisig/Cargo.toml b/frame/multisig/Cargo.toml index 00f3e51f3837f..44ea4dc3e9029 100644 --- a/frame/multisig/Cargo.toml +++ b/frame/multisig/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } frame-system = { version = "2.0.0-rc3", default-features = false, path = "../system" } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } diff --git a/frame/multisig/src/benchmarking.rs b/frame/multisig/src/benchmarking.rs index fa2ec52e6b2c5..9479c16cb2b07 100644 --- a/frame/multisig/src/benchmarking.rs +++ b/frame/multisig/src/benchmarking.rs @@ -22,14 +22,15 @@ use super::*; use frame_system::RawOrigin; use frame_benchmarking::{benchmarks, account}; -use sp_runtime::traits::Saturating; +use sp_runtime::traits::{Bounded, Saturating}; +use core::convert::TryInto; use crate::Module as Multisig; const SEED: u32 = 0; fn setup_multi(s: u32, z: u32) - -> Result<(Vec, Box<::Call>), &'static str> + -> Result<(Vec, Vec), &'static str> { let mut signatories: Vec = Vec::new(); for i in 0 .. s { @@ -41,36 +42,79 @@ fn setup_multi(s: u32, z: u32) signatories.push(signatory); } signatories.sort(); - let call: Box<::Call> = Box::new(frame_system::Call::remark(vec![0; z as usize]).into()); - return Ok((signatories, call)) + // Must first convert to outer call type. + let call: ::Call = frame_system::Call::::remark(vec![0; z as usize]).into(); + let call_data = call.encode(); + return Ok((signatories, call_data)) } benchmarks! { _ { } + as_multi_threshold_1 { + // Transaction Length + let z in 0 .. 10_000; + let max_signatories = T::MaxSignatories::get().into(); + let (mut signatories, _) = setup_multi::(max_signatories, z)?; + let call: ::Call = frame_system::Call::::remark(vec![0; z as usize]).into(); + let call_hash = call.using_encoded(blake2_256); + let multi_account_id = Multisig::::multi_account_id(&signatories, 1); + let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; + }: _(RawOrigin::Signed(caller.clone()), signatories, Box::new(call)) + verify { + // If the benchmark resolves, then the call was dispatched successfully. + } + as_multi_create { // Signatories, need at least 2 total people let s in 2 .. T::MaxSignatories::get() as u32; // Transaction Length let z in 0 .. 10_000; let (mut signatories, call) = setup_multi::(s, z)?; + let call_hash = blake2_256(&call); + let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - }: as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call) + }: as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, false, 0) + verify { + assert!(Multisigs::::contains_key(multi_account_id, call_hash)); + } - as_multi_approve { - // Signatories, need at least 2 people + as_multi_create_store { + // Signatories, need at least 2 total people let s in 2 .. T::MaxSignatories::get() as u32; // Transaction Length let z in 0 .. 10_000; let (mut signatories, call) = setup_multi::(s, z)?; + let call_hash = blake2_256(&call); + let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); + let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + }: as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, true, 0) + verify { + assert!(Multisigs::::contains_key(multi_account_id, call_hash)); + assert!(Calls::::contains_key(call_hash)); + } + + as_multi_approve { + // Signatories, need at least 3 people (so we don't complete the multisig) + let s in 3 .. T::MaxSignatories::get() as u32; + // Transaction Length + let z in 0 .. 10_000; + let (mut signatories, call) = setup_multi::(s, z)?; + let call_hash = blake2_256(&call); + let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let mut signatories2 = signatories.clone(); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; // before the call, get the timepoint let timepoint = Multisig::::timepoint(); - // Create the multi - Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone())?; + // Create the multi, storing for worst case + Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), true, 0)?; let caller2 = signatories2.remove(0); - }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call) + }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, false, 0) + verify { + let multisig = Multisigs::::get(multi_account_id, call_hash).ok_or("multisig not created")?; + assert_eq!(multisig.approvals.len(), 2); + } as_multi_complete { // Signatories, need at least 2 people @@ -78,21 +122,27 @@ benchmarks! { // Transaction Length let z in 0 .. 10_000; let (mut signatories, call) = setup_multi::(s, z)?; + let call_hash = blake2_256(&call); + let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let mut signatories2 = signatories.clone(); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; // before the call, get the timepoint let timepoint = Multisig::::timepoint(); - // Create the multi - Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone())?; + // Create the multi, storing it for worst case + Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), true, 0)?; // Everyone except the first person approves for i in 1 .. s - 1 { let mut signatories_loop = signatories2.clone(); let caller_loop = signatories_loop.remove(i as usize); let o = RawOrigin::Signed(caller_loop).into(); - Multisig::::as_multi(o, s as u16, signatories_loop, Some(timepoint), call.clone())?; + Multisig::::as_multi(o, s as u16, signatories_loop, Some(timepoint), call.clone(), false, 0)?; } let caller2 = signatories2.remove(0); - }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call) + assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); + }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, false, Weight::max_value()) + verify { + assert!(!Multisigs::::contains_key(&multi_account_id, call_hash)); + } approve_as_multi_create { // Signatories, need at least 2 people @@ -100,10 +150,14 @@ benchmarks! { // Transaction Length let z in 0 .. 10_000; let (mut signatories, call) = setup_multi::(s, z)?; + let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - let call_hash = call.using_encoded(blake2_256); + let call_hash = blake2_256(&call); // Create the multi - }: approve_as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call_hash) + }: approve_as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call_hash, 0) + verify { + assert!(Multisigs::::contains_key(multi_account_id, call_hash)); + } approve_as_multi_approve { // Signatories, need at least 2 people @@ -112,14 +166,63 @@ benchmarks! { let z in 0 .. 10_000; let (mut signatories, call) = setup_multi::(s, z)?; let mut signatories2 = signatories.clone(); + let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - let call_hash = call.using_encoded(blake2_256); + let call_hash = blake2_256(&call); // before the call, get the timepoint let timepoint = Multisig::::timepoint(); // Create the multi - Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone())?; + Multisig::::as_multi( + RawOrigin::Signed(caller.clone()).into(), + s as u16, + signatories, + None, + call.clone(), + false, + 0 + )?; let caller2 = signatories2.remove(0); - }: approve_as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call_hash) + }: approve_as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call_hash, 0) + verify { + let multisig = Multisigs::::get(multi_account_id, call_hash).ok_or("multisig not created")?; + assert_eq!(multisig.approvals.len(), 2); + } + + approve_as_multi_complete { + // Signatories, need at least 2 people + let s in 2 .. T::MaxSignatories::get() as u32; + // Transaction Length + let z in 0 .. 10_000; + let (mut signatories, call) = setup_multi::(s, z)?; + let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); + let mut signatories2 = signatories.clone(); + let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + let call_hash = blake2_256(&call); + // before the call, get the timepoint + let timepoint = Multisig::::timepoint(); + // Create the multi + Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), true, 0)?; + // Everyone except the first person approves + for i in 1 .. s - 1 { + let mut signatories_loop = signatories2.clone(); + let caller_loop = signatories_loop.remove(i as usize); + let o = RawOrigin::Signed(caller_loop).into(); + Multisig::::as_multi(o, s as u16, signatories_loop, Some(timepoint), call.clone(), false, 0)?; + } + let caller2 = signatories2.remove(0); + assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); + }: approve_as_multi( + RawOrigin::Signed(caller2), + s as u16, + signatories2, + Some(timepoint), + call_hash, + Weight::max_value() + ) + verify { + assert!(!Multisigs::::contains_key(multi_account_id, call_hash)); + } cancel_as_multi { // Signatories, need at least 2 people @@ -127,13 +230,40 @@ benchmarks! { // Transaction Length let z in 0 .. 10_000; let (mut signatories, call) = setup_multi::(s, z)?; + let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - let call_hash = call.using_encoded(blake2_256); + let call_hash = blake2_256(&call); let timepoint = Multisig::::timepoint(); // Create the multi let o = RawOrigin::Signed(caller.clone()).into(); - Multisig::::as_multi(o, s as u16, signatories.clone(), None, call.clone())?; + Multisig::::as_multi(o, s as u16, signatories.clone(), None, call.clone(), true, 0)?; + assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); }: _(RawOrigin::Signed(caller), s as u16, signatories, timepoint, call_hash) + verify { + assert!(!Multisigs::::contains_key(multi_account_id, call_hash)); + } + + cancel_as_multi_store { + // Signatories, need at least 2 people + let s in 2 .. T::MaxSignatories::get() as u32; + // Transaction Length + let z in 0 .. 10_000; + let (mut signatories, call) = setup_multi::(s, z)?; + let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); + let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + let call_hash = blake2_256(&call); + let timepoint = Multisig::::timepoint(); + // Create the multi + let o = RawOrigin::Signed(caller.clone()).into(); + Multisig::::as_multi(o, s as u16, signatories.clone(), None, call.clone(), true, 0)?; + assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); + assert!(Calls::::contains_key(call_hash)); + }: cancel_as_multi(RawOrigin::Signed(caller), s as u16, signatories, timepoint, call_hash) + verify { + assert!(!Multisigs::::contains_key(&multi_account_id, call_hash)); + assert!(!Calls::::contains_key(call_hash)); + } } #[cfg(test)] @@ -145,12 +275,16 @@ mod tests { #[test] fn test_benchmarks() { new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_as_multi_threshold_1::()); assert_ok!(test_benchmark_as_multi_create::()); + assert_ok!(test_benchmark_as_multi_create_store::()); assert_ok!(test_benchmark_as_multi_approve::()); assert_ok!(test_benchmark_as_multi_complete::()); assert_ok!(test_benchmark_approve_as_multi_create::()); assert_ok!(test_benchmark_approve_as_multi_approve::()); + assert_ok!(test_benchmark_approve_as_multi_complete::()); assert_ok!(test_benchmark_cancel_as_multi::()); + assert_ok!(test_benchmark_cancel_as_multi_store::()); }); } } diff --git a/frame/multisig/src/lib.rs b/frame/multisig/src/lib.rs index bde0a06de60fb..bcea34f9b36fa 100644 --- a/frame/multisig/src/lib.rs +++ b/frame/multisig/src/lib.rs @@ -50,12 +50,12 @@ use sp_std::prelude::*; use codec::{Encode, Decode}; use sp_io::hashing::blake2_256; use frame_support::{decl_module, decl_event, decl_error, decl_storage, Parameter, ensure, RuntimeDebug}; -use frame_support::{traits::{Get, ReservableCurrency, Currency, Filter, FilterStack, ClearFilterGuard}, - weights::{Weight, GetDispatchInfo, DispatchClass, Pays}, +use frame_support::{traits::{Get, ReservableCurrency, Currency}, + weights::{Weight, GetDispatchInfo, constants::{WEIGHT_PER_NANOS, WEIGHT_PER_MICROS}}, dispatch::{DispatchResultWithPostInfo, DispatchErrorWithPostInfo, PostDispatchInfo}, }; -use frame_system::{self as system, ensure_signed}; -use sp_runtime::{DispatchError, DispatchResult, traits::Dispatchable}; +use frame_system::{self as system, ensure_signed, RawOrigin}; +use sp_runtime::{DispatchError, DispatchResult, traits::{Dispatchable, Zero}}; mod tests; mod benchmarking; @@ -74,10 +74,12 @@ pub trait Trait: frame_system::Trait { /// The currency mechanism. type Currency: ReservableCurrency; - /// The base amount of currency needed to reserve for creating a multisig execution. + /// The base amount of currency needed to reserve for creating a multisig execution or to store + /// a dispatch call for later. /// /// This is held for an additional storage item whose value size is - /// `4 + sizeof((BlockNumber, Balance, AccountId))` bytes. + /// `4 + sizeof((BlockNumber, Balance, AccountId))` bytes and whose key size is + /// `32 + sizeof(AccountId)` bytes. type DepositBase: Get>; /// The amount of currency needed per unit threshold when creating a multisig execution. @@ -87,9 +89,6 @@ pub trait Trait: frame_system::Trait { /// The maximum amount of signatories allowed in the multisig. type MaxSignatories: Get; - - /// Is a given call compatible with the proxying subsystem? - type IsCallable: FilterStack<::Call>; } /// A global extrinsic index, formed as the extrinsic index within a block, together with that @@ -122,13 +121,15 @@ decl_storage! { pub Multisigs: double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) [u8; 32] => Option, T::AccountId>>; + + pub Calls: map hasher(identity) [u8; 32] => Option<(Vec, T::AccountId, BalanceOf)>; } } decl_error! { pub enum Error for Module { - /// Threshold is too low (zero). - ZeroThreshold, + /// Threshold must be 2 or greater. + MinimumThreshold, /// Call is already approved by this signatory. AlreadyApproved, /// Call doesn't need any (more) approvals. @@ -151,8 +152,10 @@ decl_error! { WrongTimepoint, /// A timepoint was given, yet no multisig operation is underway. UnexpectedTimepoint, - /// A call with a `false` `IsCallable` filter was attempted. - Uncallable, + /// The maximum weight information provided was too low. + WeightTooLow, + /// The data to be stored is already stored. + AlreadyStored, } } @@ -175,30 +178,56 @@ decl_event! { /// A multisig operation has been cancelled. First param is the account that is /// cancelling, third is the multisig account, fourth is hash of the call. MultisigCancelled(AccountId, Timepoint, AccountId, CallHash), - /// A call with a `false` IsCallable filter was attempted. - Uncallable(u32), } } mod weight_of { use super::*; + /// - Base Weight: 33.72 + 0.002 * Z µs + /// - DB Weight: None + /// - Plus Call Weight + pub fn as_multi_threshold_1( + call_len: usize, + call_weight: Weight, + ) -> Weight { + (34 * WEIGHT_PER_MICROS) + .saturating_add((2 * WEIGHT_PER_NANOS).saturating_mul(call_len as Weight)) + .saturating_add(call_weight) + } + /// - Base Weight: - /// - Create: 46.55 + 0.089 * S µs - /// - Approve: 34.03 + .112 * S µs - /// - Complete: 40.36 + .225 * S µs + /// - Create: 38.82 + 0.121 * S + .001 * Z µs + /// - Create w/ Store: 54.22 + 0.120 * S + .003 * Z µs + /// - Approve: 29.86 + 0.143 * S + .001 * Z µs + /// - Complete: 39.55 + 0.267 * S + .002 * Z µs /// - DB Weight: - /// - Reads: Multisig Storage, [Caller Account] - /// - Writes: Multisig Storage, [Caller Account] + /// - Reads: Multisig Storage, [Caller Account], Calls, Depositor Account + /// - Writes: Multisig Storage, [Caller Account], Calls, Depositor Account /// - Plus Call Weight - pub fn as_multi(other_sig_len: usize, call_weight: Weight) -> Weight { + pub fn as_multi( + sig_len: usize, + call_len: usize, + call_weight: Weight, + calls_write: bool, + refunded: bool, + ) -> Weight { call_weight - .saturating_add(45_000_000) - .saturating_add((other_sig_len as Weight).saturating_mul(250_000)) - .saturating_add(T::DbWeight::get().reads_writes(1, 1)) + .saturating_add(55 * WEIGHT_PER_MICROS) + .saturating_add((250 * WEIGHT_PER_NANOS).saturating_mul(sig_len as Weight)) + .saturating_add((3 * WEIGHT_PER_NANOS).saturating_mul(call_len as Weight)) + .saturating_add(T::DbWeight::get().reads_writes(1, 1)) // Multisig read/write + .saturating_add(T::DbWeight::get().reads(1)) // Calls read + .saturating_add(T::DbWeight::get().writes(calls_write.into())) // Calls write + .saturating_add(T::DbWeight::get().reads_writes(refunded.into(), refunded.into())) // Deposit refunded } } +enum CallOrHash { + Call(Vec, bool), + Hash([u8; 32]), +} + decl_module! { pub struct Module for enum Call where origin: T::Origin { type Error = Error; @@ -206,22 +235,70 @@ decl_module! { /// Deposit one of this module's events by using the default implementation. fn deposit_event() = default; - fn on_runtime_upgrade() -> Weight { - // Utility.Multisigs -> Multisig.Multisigs - use frame_support::migration::{StorageIterator, put_storage_value}; - for (key, value) in StorageIterator::< - Multisig, T::AccountId> - >::new(b"Utility", b"Multisigs").drain() { - put_storage_value(b"Multisig", b"Multisigs", &key, value); - } - 1_000_000_000 + /// Immediately dispatch a multi-signature call using a single approval from the caller. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// - `other_signatories`: The accounts (other than the sender) who are part of the + /// multi-signature, but do not participate in the approval process. + /// - `call`: The call to be executed. + /// + /// Result is equivalent to the dispatched result. + /// + /// # + /// O(Z + C) where Z is the length of the call and C its execution weight. + /// ------------------------------- + /// - Base Weight: 33.72 + 0.002 * Z µs + /// - DB Weight: None + /// - Plus Call Weight + /// # + #[weight = ( + weight_of::as_multi_threshold_1::( + call.using_encoded(|c| c.len()), + call.get_dispatch_info().weight + ), + call.get_dispatch_info().class, + )] + fn as_multi_threshold_1(origin, + other_signatories: Vec, + call: Box<::Call>, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + let max_sigs = T::MaxSignatories::get() as usize; + ensure!(!other_signatories.is_empty(), Error::::TooFewSignatories); + let other_signatories_len = other_signatories.len(); + ensure!(other_signatories_len < max_sigs, Error::::TooManySignatories); + let signatories = Self::ensure_sorted_and_insert(other_signatories, who.clone())?; + + let id = Self::multi_account_id(&signatories, 1); + + let call_len = call.using_encoded(|c| c.len()); + let result = call.dispatch(RawOrigin::Signed(id.clone()).into()); + + result.map(|post_dispatch_info| post_dispatch_info.actual_weight + .map(|actual_weight| weight_of::as_multi_threshold_1::( + call_len, + actual_weight, + )) + .into() + ).map_err(|err| match err.post_info.actual_weight { + Some(actual_weight) => { + let weight_used = weight_of::as_multi_threshold_1::( + call_len, + actual_weight, + ); + let post_info = Some(weight_used).into(); + let error = err.error.into(); + DispatchErrorWithPostInfo { post_info, error } + }, + None => err, + }) } /// Register approval for a dispatch to be made from a deterministic composite account if /// approved by a total of `threshold - 1` of `other_signatories`. /// - /// If there are enough, then dispatch the call. Calls must each fulfil the `IsCallable` - /// filter. + /// If there are enough, then dispatch the call. /// /// Payment: `DepositBase` will be reserved if this is the first approval, plus /// `threshold` times `DepositFactor`. It is returned once this dispatch happens or @@ -260,103 +337,32 @@ decl_module! { /// `DepositBase + threshold * DepositFactor`. /// ------------------------------- /// - Base Weight: - /// - Create: 46.55 + 0.089 * S µs - /// - Approve: 34.03 + .112 * S µs - /// - Complete: 40.36 + .225 * S µs + /// - Create: 41.89 + 0.118 * S + .002 * Z µs + /// - Create w/ Store: 53.57 + 0.119 * S + .003 * Z µs + /// - Approve: 31.39 + 0.136 * S + .002 * Z µs + /// - Complete: 39.94 + 0.26 * S + .002 * Z µs /// - DB Weight: - /// - Reads: Multisig Storage, [Caller Account] - /// - Writes: Multisig Storage, [Caller Account] + /// - Reads: Multisig Storage, [Caller Account], Calls (if `store_call`) + /// - Writes: Multisig Storage, [Caller Account], Calls (if `store_call`) /// - Plus Call Weight /// # - #[weight = ( - weight_of::as_multi::(other_signatories.len(), call.get_dispatch_info().weight), - call.get_dispatch_info().class, - Pays::Yes, + #[weight = weight_of::as_multi::( + other_signatories.len(), + call.len(), + *max_weight, + true, // assume worst case: calls write + true, // assume worst case: refunded )] fn as_multi(origin, threshold: u16, other_signatories: Vec, maybe_timepoint: Option>, - call: Box<::Call>, + call: Vec, + store_call: bool, + max_weight: Weight, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - // We're now executing as a freshly authenticated new account, so the previous call - // restrictions no longer apply. - let _guard = ClearFilterGuard::::Call>::new(); - ensure!(T::IsCallable::filter(call.as_ref()), Error::::Uncallable); - ensure!(threshold >= 1, Error::::ZeroThreshold); - let max_sigs = T::MaxSignatories::get() as usize; - ensure!(!other_signatories.is_empty(), Error::::TooFewSignatories); - let other_signatories_len = other_signatories.len(); - ensure!(other_signatories_len < max_sigs, Error::::TooManySignatories); - let signatories = Self::ensure_sorted_and_insert(other_signatories, who.clone())?; - - let id = Self::multi_account_id(&signatories, threshold); - let call_hash = call.using_encoded(blake2_256); - - if let Some(mut m) = >::get(&id, call_hash) { - let timepoint = maybe_timepoint.ok_or(Error::::NoTimepoint)?; - ensure!(m.when == timepoint, Error::::WrongTimepoint); - if let Err(pos) = m.approvals.binary_search(&who) { - // we know threshold is greater than zero from the above ensure. - if (m.approvals.len() as u16) < threshold - 1 { - m.approvals.insert(pos, who.clone()); - >::insert(&id, call_hash, m); - Self::deposit_event(RawEvent::MultisigApproval(who, timepoint, id, call_hash)); - // Call is not made, so the actual weight does not include call - return Ok(Some(weight_of::as_multi::(other_signatories_len, 0)).into()) - } - } else { - if (m.approvals.len() as u16) < threshold { - Err(Error::::AlreadyApproved)? - } - } - - let result = call.dispatch(frame_system::RawOrigin::Signed(id.clone()).into()); - let _ = T::Currency::unreserve(&m.depositor, m.deposit); - >::remove(&id, call_hash); - Self::deposit_event(RawEvent::MultisigExecuted( - who, timepoint, id, call_hash, result.map(|_| ()).map_err(|e| e.error) - )); - return Ok(None.into()) - } else { - ensure!(maybe_timepoint.is_none(), Error::::UnexpectedTimepoint); - if threshold > 1 { - let deposit = T::DepositBase::get() - + T::DepositFactor::get() * threshold.into(); - T::Currency::reserve(&who, deposit)?; - >::insert(&id, call_hash, Multisig { - when: Self::timepoint(), - deposit, - depositor: who.clone(), - approvals: vec![who.clone()], - }); - Self::deposit_event(RawEvent::NewMultisig(who, id, call_hash)); - // Call is not made, so we can return that weight - return Ok(Some(weight_of::as_multi::(other_signatories_len, 0)).into()) - } else { - let result = call.dispatch(frame_system::RawOrigin::Signed(id).into()); - match result { - Ok(post_dispatch_info) => { - match post_dispatch_info.actual_weight { - Some(actual_weight) => return Ok(Some(weight_of::as_multi::(other_signatories_len, actual_weight)).into()), - None => return Ok(None.into()), - } - }, - Err(err) => { - match err.post_info.actual_weight { - Some(actual_weight) => { - let weight_used = weight_of::as_multi::(other_signatories_len, actual_weight); - return Err(DispatchErrorWithPostInfo { post_info: Some(weight_used).into(), error: err.error.into() }) - }, - None => { - return Err(err) - } - } - } - } - } - } + Self::operate(who, threshold, other_signatories, maybe_timepoint, CallOrHash::Call(call, store_call), max_weight) } /// Register approval for a dispatch to be made from a deterministic composite account if @@ -398,57 +404,22 @@ decl_module! { /// - Read: Multisig Storage, [Caller Account] /// - Write: Multisig Storage, [Caller Account] /// # - #[weight = ( - T::DbWeight::get().reads_writes(1, 1) - .saturating_add(45_000_000) - .saturating_add((other_signatories.len() as Weight).saturating_mul(120_000)), - DispatchClass::Normal, - Pays::Yes, + #[weight = weight_of::as_multi::( + other_signatories.len(), + 0, // call_len is zero in this case + *max_weight, + true, // assume worst case: calls write + true, // assume worst case: refunded )] fn approve_as_multi(origin, threshold: u16, other_signatories: Vec, maybe_timepoint: Option>, call_hash: [u8; 32], - ) -> DispatchResult { + max_weight: Weight, + ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - ensure!(threshold >= 1, Error::::ZeroThreshold); - let max_sigs = T::MaxSignatories::get() as usize; - ensure!(!other_signatories.is_empty(), Error::::TooFewSignatories); - ensure!(other_signatories.len() < max_sigs, Error::::TooManySignatories); - let signatories = Self::ensure_sorted_and_insert(other_signatories, who.clone())?; - - let id = Self::multi_account_id(&signatories, threshold); - - if let Some(mut m) = >::get(&id, call_hash) { - let timepoint = maybe_timepoint.ok_or(Error::::NoTimepoint)?; - ensure!(m.when == timepoint, Error::::WrongTimepoint); - ensure!(m.approvals.len() < threshold as usize, Error::::NoApprovalsNeeded); - if let Err(pos) = m.approvals.binary_search(&who) { - m.approvals.insert(pos, who.clone()); - >::insert(&id, call_hash, m); - Self::deposit_event(RawEvent::MultisigApproval(who, timepoint, id, call_hash)); - } else { - Err(Error::::AlreadyApproved)? - } - } else { - if threshold > 1 { - ensure!(maybe_timepoint.is_none(), Error::::UnexpectedTimepoint); - let deposit = T::DepositBase::get() - + T::DepositFactor::get() * threshold.into(); - T::Currency::reserve(&who, deposit)?; - >::insert(&id, call_hash, Multisig { - when: Self::timepoint(), - deposit, - depositor: who.clone(), - approvals: vec![who.clone()], - }); - Self::deposit_event(RawEvent::NewMultisig(who, id, call_hash)); - } else { - Err(Error::::NoApprovalsNeeded)? - } - } - Ok(()) + Self::operate(who, threshold, other_signatories, maybe_timepoint, CallOrHash::Hash(call_hash), max_weight) } /// Cancel a pre-existing, on-going multisig transaction. Any deposit reserved previously @@ -473,18 +444,15 @@ decl_module! { /// - I/O: 1 read `O(S)`, one remove. /// - Storage: removes one item. /// ---------------------------------- - /// - Base Weight: 37.6 + 0.084 * S + /// - Base Weight: 36.07 + 0.124 * S /// - DB Weight: - /// - Read: Multisig Storage, [Caller Account] - /// - Write: Multisig Storage, [Caller Account] + /// - Read: Multisig Storage, [Caller Account], Refund Account, Calls + /// - Write: Multisig Storage, [Caller Account], Refund Account, Calls /// # - #[weight = ( - T::DbWeight::get().reads_writes(1, 1) - .saturating_add(40_000_000) - .saturating_add((other_signatories.len() as Weight).saturating_mul(100_000)), - DispatchClass::Normal, - Pays::Yes, - )] + #[weight = T::DbWeight::get().reads_writes(3, 3) + .saturating_add(36 * WEIGHT_PER_MICROS) + .saturating_add((other_signatories.len() as Weight).saturating_mul(100 * WEIGHT_PER_NANOS)) + ] fn cancel_as_multi(origin, threshold: u16, other_signatories: Vec, @@ -492,7 +460,7 @@ decl_module! { call_hash: [u8; 32], ) -> DispatchResult { let who = ensure_signed(origin)?; - ensure!(threshold >= 1, Error::::ZeroThreshold); + ensure!(threshold >= 2, Error::::MinimumThreshold); let max_sigs = T::MaxSignatories::get() as usize; ensure!(!other_signatories.is_empty(), Error::::TooFewSignatories); ensure!(other_signatories.len() < max_sigs, Error::::TooManySignatories); @@ -506,7 +474,8 @@ decl_module! { ensure!(m.depositor == who, Error::::NotOwner); let _ = T::Currency::unreserve(&m.depositor, m.deposit); - >::remove(&id, call_hash); + >::remove(&id, &call_hash); + Self::clear_call(&call_hash); Self::deposit_event(RawEvent::MultisigCancelled(who, timepoint, id, call_hash)); Ok(()) @@ -524,6 +493,172 @@ impl Module { T::AccountId::decode(&mut &entropy[..]).unwrap_or_default() } + fn operate( + who: T::AccountId, + threshold: u16, + other_signatories: Vec, + maybe_timepoint: Option>, + call_or_hash: CallOrHash, + max_weight: Weight, + ) -> DispatchResultWithPostInfo { + ensure!(threshold >= 2, Error::::MinimumThreshold); + let max_sigs = T::MaxSignatories::get() as usize; + ensure!(!other_signatories.is_empty(), Error::::TooFewSignatories); + let other_signatories_len = other_signatories.len(); + ensure!(other_signatories_len < max_sigs, Error::::TooManySignatories); + let signatories = Self::ensure_sorted_and_insert(other_signatories, who.clone())?; + + let id = Self::multi_account_id(&signatories, threshold); + + // Threshold > 1; this means it's a multi-step operation. We extract the `call_hash`. + let (call_hash, call_len, maybe_call, store) = match call_or_hash { + CallOrHash::Call(call, should_store) => { + let call_hash = blake2_256(&call); + let call_len = call.len(); + (call_hash, call_len, Some(call), should_store) + } + CallOrHash::Hash(h) => (h, 0, None, false), + }; + + // Branch on whether the operation has already started or not. + if let Some(mut m) = >::get(&id, call_hash) { + // Yes; ensure that the timepoint exists and agrees. + let timepoint = maybe_timepoint.ok_or(Error::::NoTimepoint)?; + ensure!(m.when == timepoint, Error::::WrongTimepoint); + + // Ensure that either we have not yet signed or that it is at threshold. + let mut approvals = m.approvals.len() as u16; + // We only bother with the approval if we're below threshold. + let maybe_pos = m.approvals.binary_search(&who).err().filter(|_| approvals < threshold); + // Bump approvals if not yet voted and the vote is needed. + if maybe_pos.is_some() { approvals += 1; } + + // We only bother fetching/decoding call if we know that we're ready to execute. + let maybe_approved_call = if approvals >= threshold { + Self::get_call(&call_hash, maybe_call.as_ref().map(|c| c.as_ref())) + } else { None }; + + if let Some(call) = maybe_approved_call { + // verify weight + ensure!(call.get_dispatch_info().weight <= max_weight, Error::::WeightTooLow); + + // Clean up storage before executing call to avoid an possibility of reentrancy + // attack. + >::remove(&id, call_hash); + Self::clear_call(&call_hash); + T::Currency::unreserve(&m.depositor, m.deposit); + + let result = call.dispatch(RawOrigin::Signed(id.clone()).into()); + Self::deposit_event(RawEvent::MultisigExecuted( + who, timepoint, id, call_hash, result.map(|_| ()).map_err(|e| e.error) + )); + Ok(get_result_weight(result).map(|actual_weight| weight_of::as_multi::( + other_signatories_len, + call_len, + actual_weight, + true, // Call is removed + true, // User is refunded + )).into()) + } else { + // We cannot dispatch the call now; either it isn't available, or it is, but we + // don't have threshold approvals even with our signature. + + // Store the call if desired. + let stored = if let Some(data) = maybe_call.filter(|_| store) { + Self::store_call_and_reserve(who.clone(), &call_hash, data, BalanceOf::::zero())?; + true + } else { + false + }; + + if let Some(pos) = maybe_pos { + // Record approval. + m.approvals.insert(pos, who.clone()); + >::insert(&id, call_hash, m); + Self::deposit_event(RawEvent::MultisigApproval(who, timepoint, id, call_hash)); + } else { + // If we already approved and didn't store the Call, then this was useless and + // we report an error. + ensure!(stored, Error::::AlreadyApproved); + } + + // Call is not made, so the actual weight does not include call + Ok(Some(weight_of::as_multi::( + other_signatories_len, + call_len, + 0, + stored, // Call stored? + false, // No refund + )).into()) + } + } else { + // Not yet started; there should be no timepoint given. + ensure!(maybe_timepoint.is_none(), Error::::UnexpectedTimepoint); + + // Just start the operation by recording it in storage. + let deposit = T::DepositBase::get() + T::DepositFactor::get() * threshold.into(); + + // Store the call if desired. + let stored = if let Some(data) = maybe_call.filter(|_| store) { + Self::store_call_and_reserve(who.clone(), &call_hash, data, deposit)?; + true + } else { + T::Currency::reserve(&who, deposit)?; + false + }; + + >::insert(&id, call_hash, Multisig { + when: Self::timepoint(), + deposit, + depositor: who.clone(), + approvals: vec![who.clone()], + }); + Self::deposit_event(RawEvent::NewMultisig(who, id, call_hash)); + // Call is not made, so we can return that weight + return Ok(Some(weight_of::as_multi::( + other_signatories_len, + call_len, + 0, + stored, // Call stored? + false, // No refund + )).into()) + } + } + + /// Place a call's encoded data in storage, reserving funds as appropriate. + /// + /// We store `data` here because storing `call` would result in needing another `.encode`. + /// + /// Returns a `bool` indicating whether the data did end up being stored. + fn store_call_and_reserve(who: T::AccountId, hash: &[u8; 32], data: Vec, other_deposit: BalanceOf) + -> DispatchResult + { + ensure!(!Calls::::contains_key(hash), Error::::AlreadyStored); + let deposit = other_deposit + T::DepositBase::get() + + T::DepositFactor::get() * BalanceOf::::from(((data.len() + 31) / 32) as u32); + T::Currency::reserve(&who, deposit)?; + Calls::::insert(&hash, (data, who, deposit)); + Ok(()) + } + + /// Attempt to decode and return the call, provided by the user or from storage. + fn get_call(hash: &[u8; 32], maybe_known: Option<&[u8]>) -> Option<::Call> { + maybe_known.map_or_else(|| { + Calls::::get(hash).and_then(|(data, ..)| { + Decode::decode(&mut &data[..]).ok() + }) + }, |data| { + Decode::decode(&mut &data[..]).ok() + }) + } + + /// Attempt to remove a call from storage, returning any deposit on it to the owner. + fn clear_call(hash: &[u8; 32]) { + if let Some((_, who, deposit)) = Calls::::take(hash) { + T::Currency::unreserve(&who, deposit); + } + } + /// The current `Timepoint`. pub fn timepoint() -> Timepoint { Timepoint { @@ -553,3 +688,13 @@ impl Module { Ok(signatories) } } + +/// Return the weight of a dispatch call result as an `Option`. +/// +/// Will return the weight regardless of what the state of the result is. +fn get_result_weight(result: DispatchResultWithPostInfo) -> Option { + match result { + Ok(post_info) => post_info.actual_weight, + Err(err) => err.post_info.actual_weight, + } +} diff --git a/frame/multisig/src/tests.rs b/frame/multisig/src/tests.rs index 43eb43b914b06..c0cd305fe5f9c 100644 --- a/frame/multisig/src/tests.rs +++ b/frame/multisig/src/tests.rs @@ -23,7 +23,7 @@ use super::*; use frame_support::{ assert_ok, assert_noop, impl_outer_origin, parameter_types, impl_outer_dispatch, - weights::Weight, impl_outer_event + weights::Weight, impl_outer_event, traits::Filter, }; use sp_core::H256; use sp_runtime::{Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header}; @@ -60,6 +60,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = TestBaseCallFilter; type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -100,8 +101,8 @@ parameter_types! { pub const DepositFactor: u64 = 1; pub const MaxSignatories: u16 = 3; } -pub struct TestIsCallable; -impl Filter for TestIsCallable { +pub struct TestBaseCallFilter; +impl Filter for TestBaseCallFilter { fn filter(c: &Call) -> bool { match *c { Call::Balances(_) => true, @@ -111,13 +112,6 @@ impl Filter for TestIsCallable { } } } -impl FilterStack for TestIsCallable { - type Stack = (); - fn push(_: impl Fn(&Call) -> bool + 'static) {} - fn pop() {} - fn take() -> Self::Stack { () } - fn restore(_: Self::Stack) {} -} impl Trait for Test { type Event = TestEvent; type Call = Call; @@ -125,7 +119,6 @@ impl Trait for Test { type DepositBase = DepositBase; type DepositFactor = DepositFactor; type MaxSignatories = MaxSignatories; - type IsCallable = TestIsCallable; } type System = frame_system::Module; type Balances = pallet_balances::Module; @@ -164,24 +157,79 @@ fn multisig_deposit_is_taken_and_returned() { assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); - let call = Box::new(Call::Balances(BalancesCall::transfer(6, 15))); - assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, call.clone())); + let call = Call::Balances(BalancesCall::transfer(6, 15)); + let call_weight = call.get_dispatch_info().weight; + let data = call.encode(); + assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, data.clone(), false, 0)); assert_eq!(Balances::free_balance(1), 2); assert_eq!(Balances::reserved_balance(1), 3); - assert_ok!(Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), call)); + assert_ok!(Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), data, false, call_weight)); + assert_eq!(Balances::free_balance(1), 5); + assert_eq!(Balances::reserved_balance(1), 0); + }); +} + +#[test] +fn multisig_deposit_is_taken_and_returned_with_call_storage() { + new_test_ext().execute_with(|| { + let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); + assert_ok!(Balances::transfer(Origin::signed(1), multi, 5)); + assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); + assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); + + let call = Call::Balances(BalancesCall::transfer(6, 15)); + let call_weight = call.get_dispatch_info().weight; + let data = call.encode(); + let hash = blake2_256(&data); + assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, data, true, 0)); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 5); + + assert_ok!(Multisig::approve_as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), hash, call_weight)); assert_eq!(Balances::free_balance(1), 5); assert_eq!(Balances::reserved_balance(1), 0); }); } +#[test] +fn multisig_deposit_is_taken_and_returned_with_alt_call_storage() { + new_test_ext().execute_with(|| { + let multi = Multisig::multi_account_id(&[1, 2, 3][..], 3); + assert_ok!(Balances::transfer(Origin::signed(1), multi, 5)); + assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); + assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); + + let call = Call::Balances(BalancesCall::transfer(6, 15)); + let call_weight = call.get_dispatch_info().weight; + let data = call.encode(); + let hash = blake2_256(&data); + + assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 3, vec![2, 3], None, hash.clone(), 0)); + assert_eq!(Balances::free_balance(1), 1); + assert_eq!(Balances::reserved_balance(1), 4); + + assert_ok!(Multisig::as_multi(Origin::signed(2), 3, vec![1, 3], Some(now()), data, true, 0)); + assert_eq!(Balances::free_balance(2), 3); + assert_eq!(Balances::reserved_balance(2), 2); + assert_eq!(Balances::free_balance(1), 1); + assert_eq!(Balances::reserved_balance(1), 4); + + assert_ok!(Multisig::approve_as_multi(Origin::signed(3), 3, vec![1, 2], Some(now()), hash, call_weight)); + assert_eq!(Balances::free_balance(1), 5); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::free_balance(2), 5); + assert_eq!(Balances::reserved_balance(2), 0); + }); +} + #[test] fn cancel_multisig_returns_deposit() { new_test_ext().execute_with(|| { - let call = Box::new(Call::Balances(BalancesCall::transfer(6, 15))); - let hash = call.using_encoded(blake2_256); - assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 3, vec![2, 3], None, hash.clone())); - assert_ok!(Multisig::approve_as_multi(Origin::signed(2), 3, vec![1, 3], Some(now()), hash.clone())); + let call = Call::Balances(BalancesCall::transfer(6, 15)).encode(); + let hash = blake2_256(&call); + assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 3, vec![2, 3], None, hash.clone(), 0)); + assert_ok!(Multisig::approve_as_multi(Origin::signed(2), 3, vec![1, 3], Some(now()), hash.clone(), 0)); assert_eq!(Balances::free_balance(1), 6); assert_eq!(Balances::reserved_balance(1), 4); assert_ok!( @@ -200,28 +248,48 @@ fn timepoint_checking_works() { assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); - let call = Box::new(Call::Balances(BalancesCall::transfer(6, 15))); - let hash = call.using_encoded(blake2_256); + let call = Call::Balances(BalancesCall::transfer(6, 15)).encode(); + let hash = blake2_256(&call); assert_noop!( - Multisig::approve_as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), hash.clone()), + Multisig::approve_as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), hash.clone(), 0), Error::::UnexpectedTimepoint, ); - assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 2, vec![2, 3], None, hash)); + assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 2, vec![2, 3], None, hash, 0)); assert_noop!( - Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], None, call.clone()), + Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], None, call.clone(), false, 0), Error::::NoTimepoint, ); let later = Timepoint { index: 1, .. now() }; assert_noop!( - Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], Some(later), call.clone()), + Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], Some(later), call.clone(), false, 0), Error::::WrongTimepoint, ); }); } +#[test] +fn multisig_2_of_3_works_with_call_storing() { + new_test_ext().execute_with(|| { + let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); + assert_ok!(Balances::transfer(Origin::signed(1), multi, 5)); + assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); + assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); + + let call = Call::Balances(BalancesCall::transfer(6, 15)); + let call_weight = call.get_dispatch_info().weight; + let data = call.encode(); + let hash = blake2_256(&data); + assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, data, true, 0)); + assert_eq!(Balances::free_balance(6), 0); + + assert_ok!(Multisig::approve_as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), hash, call_weight)); + assert_eq!(Balances::free_balance(6), 15); + }); +} + #[test] fn multisig_2_of_3_works() { new_test_ext().execute_with(|| { @@ -230,12 +298,14 @@ fn multisig_2_of_3_works() { assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); - let call = Box::new(Call::Balances(BalancesCall::transfer(6, 15))); - let hash = call.using_encoded(blake2_256); - assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 2, vec![2, 3], None, hash)); + let call = Call::Balances(BalancesCall::transfer(6, 15)); + let call_weight = call.get_dispatch_info().weight; + let data = call.encode(); + let hash = blake2_256(&data); + assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 2, vec![2, 3], None, hash, 0)); assert_eq!(Balances::free_balance(6), 0); - assert_ok!(Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), call)); + assert_ok!(Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), data, false, call_weight)); assert_eq!(Balances::free_balance(6), 15); }); } @@ -248,13 +318,15 @@ fn multisig_3_of_3_works() { assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); - let call = Box::new(Call::Balances(BalancesCall::transfer(6, 15))); - let hash = call.using_encoded(blake2_256); - assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 3, vec![2, 3], None, hash.clone())); - assert_ok!(Multisig::approve_as_multi(Origin::signed(2), 3, vec![1, 3], Some(now()), hash.clone())); + let call = Call::Balances(BalancesCall::transfer(6, 15)); + let call_weight = call.get_dispatch_info().weight; + let data = call.encode(); + let hash = blake2_256(&data); + assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 3, vec![2, 3], None, hash.clone(), 0)); + assert_ok!(Multisig::approve_as_multi(Origin::signed(2), 3, vec![1, 3], Some(now()), hash.clone(), 0)); assert_eq!(Balances::free_balance(6), 0); - assert_ok!(Multisig::as_multi(Origin::signed(3), 3, vec![1, 2], Some(now()), call)); + assert_ok!(Multisig::as_multi(Origin::signed(3), 3, vec![1, 2], Some(now()), data, false, call_weight)); assert_eq!(Balances::free_balance(6), 15); }); } @@ -262,10 +334,28 @@ fn multisig_3_of_3_works() { #[test] fn cancel_multisig_works() { new_test_ext().execute_with(|| { - let call = Box::new(Call::Balances(BalancesCall::transfer(6, 15))); - let hash = call.using_encoded(blake2_256); - assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 3, vec![2, 3], None, hash.clone())); - assert_ok!(Multisig::approve_as_multi(Origin::signed(2), 3, vec![1, 3], Some(now()), hash.clone())); + let call = Call::Balances(BalancesCall::transfer(6, 15)).encode(); + let hash = blake2_256(&call); + assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 3, vec![2, 3], None, hash.clone(), 0)); + assert_ok!(Multisig::approve_as_multi(Origin::signed(2), 3, vec![1, 3], Some(now()), hash.clone(), 0)); + assert_noop!( + Multisig::cancel_as_multi(Origin::signed(2), 3, vec![1, 3], now(), hash.clone()), + Error::::NotOwner, + ); + assert_ok!( + Multisig::cancel_as_multi(Origin::signed(1), 3, vec![2, 3], now(), hash.clone()), + ); + }); +} + +#[test] +fn cancel_multisig_with_call_storage_works() { + new_test_ext().execute_with(|| { + let call = Call::Balances(BalancesCall::transfer(6, 15)).encode(); + let hash = blake2_256(&call); + assert_ok!(Multisig::as_multi(Origin::signed(1), 3, vec![2, 3], None, call, true, 0)); + assert_eq!(Balances::free_balance(1), 4); + assert_ok!(Multisig::approve_as_multi(Origin::signed(2), 3, vec![1, 3], Some(now()), hash.clone(), 0)); assert_noop!( Multisig::cancel_as_multi(Origin::signed(2), 3, vec![1, 3], now(), hash.clone()), Error::::NotOwner, @@ -273,6 +363,22 @@ fn cancel_multisig_works() { assert_ok!( Multisig::cancel_as_multi(Origin::signed(1), 3, vec![2, 3], now(), hash.clone()), ); + assert_eq!(Balances::free_balance(1), 10); + }); +} + +#[test] +fn cancel_multisig_with_alt_call_storage_works() { + new_test_ext().execute_with(|| { + let call = Call::Balances(BalancesCall::transfer(6, 15)).encode(); + let hash = blake2_256(&call); + assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 3, vec![2, 3], None, hash.clone(), 0)); + assert_eq!(Balances::free_balance(1), 6); + assert_ok!(Multisig::as_multi(Origin::signed(2), 3, vec![1, 3], Some(now()), call, true, 0)); + assert_eq!(Balances::free_balance(2), 8); + assert_ok!(Multisig::cancel_as_multi(Origin::signed(1), 3, vec![2, 3], now(), hash)); + assert_eq!(Balances::free_balance(1), 10); + assert_eq!(Balances::free_balance(2), 10); }); } @@ -284,11 +390,13 @@ fn multisig_2_of_3_as_multi_works() { assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); - let call = Box::new(Call::Balances(BalancesCall::transfer(6, 15))); - assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, call.clone())); + let call = Call::Balances(BalancesCall::transfer(6, 15)); + let call_weight = call.get_dispatch_info().weight; + let data = call.encode(); + assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, data.clone(), false, 0)); assert_eq!(Balances::free_balance(6), 0); - assert_ok!(Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), call)); + assert_ok!(Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), data, false, call_weight)); assert_eq!(Balances::free_balance(6), 15); }); } @@ -301,13 +409,17 @@ fn multisig_2_of_3_as_multi_with_many_calls_works() { assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); - let call1 = Box::new(Call::Balances(BalancesCall::transfer(6, 10))); - let call2 = Box::new(Call::Balances(BalancesCall::transfer(7, 5))); + let call1 = Call::Balances(BalancesCall::transfer(6, 10)); + let call1_weight = call1.get_dispatch_info().weight; + let data1 = call1.encode(); + let call2 = Call::Balances(BalancesCall::transfer(7, 5)); + let call2_weight = call2.get_dispatch_info().weight; + let data2 = call2.encode(); - assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, call1.clone())); - assert_ok!(Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], None, call2.clone())); - assert_ok!(Multisig::as_multi(Origin::signed(3), 2, vec![1, 2], Some(now()), call2)); - assert_ok!(Multisig::as_multi(Origin::signed(3), 2, vec![1, 2], Some(now()), call1)); + assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, data1.clone(), false, 0)); + assert_ok!(Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], None, data2.clone(), false, 0)); + assert_ok!(Multisig::as_multi(Origin::signed(3), 2, vec![1, 2], Some(now()), data1, false, call1_weight)); + assert_ok!(Multisig::as_multi(Origin::signed(3), 2, vec![1, 2], Some(now()), data2, false, call2_weight)); assert_eq!(Balances::free_balance(6), 10); assert_eq!(Balances::free_balance(7), 5); @@ -322,26 +434,33 @@ fn multisig_2_of_3_cannot_reissue_same_call() { assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); - let call = Box::new(Call::Balances(BalancesCall::transfer(6, 10))); - assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, call.clone())); - assert_ok!(Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), call.clone())); + let call = Call::Balances(BalancesCall::transfer(6, 10)); + let call_weight = call.get_dispatch_info().weight; + let data = call.encode(); + let hash = blake2_256(&data); + assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, data.clone(), false, 0)); + assert_ok!(Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), data.clone(), false, call_weight)); assert_eq!(Balances::free_balance(multi), 5); - assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, call.clone())); - assert_ok!(Multisig::as_multi(Origin::signed(3), 2, vec![1, 2], Some(now()), call.clone())); + assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, data.clone(), false, 0)); + assert_ok!(Multisig::as_multi(Origin::signed(3), 2, vec![1, 2], Some(now()), data.clone(), false, call_weight)); let err = DispatchError::from(BalancesError::::InsufficientBalance).stripped(); - expect_event(RawEvent::MultisigExecuted(3, now(), multi, call.using_encoded(blake2_256), Err(err))); + expect_event(RawEvent::MultisigExecuted(3, now(), multi, hash, Err(err))); }); } #[test] -fn zero_threshold_fails() { +fn minimum_threshold_check_works() { new_test_ext().execute_with(|| { - let call = Box::new(Call::Balances(BalancesCall::transfer(6, 15))); + let call = Call::Balances(BalancesCall::transfer(6, 15)).encode(); + assert_noop!( + Multisig::as_multi(Origin::signed(1), 0, vec![2], None, call.clone(), false, 0), + Error::::MinimumThreshold, + ); assert_noop!( - Multisig::as_multi(Origin::signed(1), 0, vec![2], None, call), - Error::::ZeroThreshold, + Multisig::as_multi(Origin::signed(1), 1, vec![2], None, call.clone(), false, 0), + Error::::MinimumThreshold, ); }); } @@ -349,9 +468,9 @@ fn zero_threshold_fails() { #[test] fn too_many_signatories_fails() { new_test_ext().execute_with(|| { - let call = Box::new(Call::Balances(BalancesCall::transfer(6, 15))); + let call = Call::Balances(BalancesCall::transfer(6, 15)).encode(); assert_noop!( - Multisig::as_multi(Origin::signed(1), 2, vec![2, 3, 4], None, call.clone()), + Multisig::as_multi(Origin::signed(1), 2, vec![2, 3, 4], None, call.clone(), false, 0), Error::::TooManySignatories, ); }); @@ -360,17 +479,17 @@ fn too_many_signatories_fails() { #[test] fn duplicate_approvals_are_ignored() { new_test_ext().execute_with(|| { - let call = Box::new(Call::Balances(BalancesCall::transfer(6, 15))); - let hash = call.using_encoded(blake2_256); - assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 2, vec![2, 3], None, hash.clone())); + let call = Call::Balances(BalancesCall::transfer(6, 15)).encode(); + let hash = blake2_256(&call); + assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 2, vec![2, 3], None, hash.clone(), 0)); assert_noop!( - Multisig::approve_as_multi(Origin::signed(1), 2, vec![2, 3], Some(now()), hash.clone()), + Multisig::approve_as_multi(Origin::signed(1), 2, vec![2, 3], Some(now()), hash.clone(), 0), Error::::AlreadyApproved, ); - assert_ok!(Multisig::approve_as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), hash.clone())); + assert_ok!(Multisig::approve_as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), hash.clone(), 0)); assert_noop!( - Multisig::approve_as_multi(Origin::signed(3), 2, vec![1, 2], Some(now()), hash.clone()), - Error::::NoApprovalsNeeded, + Multisig::approve_as_multi(Origin::signed(3), 2, vec![1, 2], Some(now()), hash.clone(), 0), + Error::::AlreadyApproved, ); }); } @@ -383,17 +502,18 @@ fn multisig_1_of_3_works() { assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); - let call = Box::new(Call::Balances(BalancesCall::transfer(6, 15))); - let hash = call.using_encoded(blake2_256); + let call = Call::Balances(BalancesCall::transfer(6, 15)).encode(); + let hash = blake2_256(&call); assert_noop!( - Multisig::approve_as_multi(Origin::signed(1), 1, vec![2, 3], None, hash.clone()), - Error::::NoApprovalsNeeded, + Multisig::approve_as_multi(Origin::signed(1), 1, vec![2, 3], None, hash.clone(), 0), + Error::::MinimumThreshold, ); assert_noop!( - Multisig::as_multi(Origin::signed(4), 1, vec![2, 3], None, call.clone()), - BalancesError::::InsufficientBalance, + Multisig::as_multi(Origin::signed(1), 1, vec![2, 3], None, call.clone(), false, 0), + Error::::MinimumThreshold, ); - assert_ok!(Multisig::as_multi(Origin::signed(1), 1, vec![2, 3], None, call)); + let boxed_call = Box::new(Call::Balances(BalancesCall::transfer(6, 15))); + assert_ok!(Multisig::as_multi_threshold_1(Origin::signed(1), vec![2, 3], boxed_call)); assert_eq!(Balances::free_balance(6), 15); }); @@ -404,8 +524,52 @@ fn multisig_filters() { new_test_ext().execute_with(|| { let call = Box::new(Call::System(frame_system::Call::set_code(vec![]))); assert_noop!( - Multisig::as_multi(Origin::signed(1), 1, vec![], None, call.clone()), - Error::::Uncallable, + Multisig::as_multi_threshold_1(Origin::signed(1), vec![2], call.clone()), + DispatchError::BadOrigin, ); }); } + +#[test] +fn weight_check_works() { + new_test_ext().execute_with(|| { + let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); + assert_ok!(Balances::transfer(Origin::signed(1), multi, 5)); + assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); + assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); + + let call = Call::Balances(BalancesCall::transfer(6, 15)); + let data = call.encode(); + assert_ok!(Multisig::as_multi(Origin::signed(1), 2, vec![2, 3], None, data.clone(), false, 0)); + assert_eq!(Balances::free_balance(6), 0); + + assert_noop!( + Multisig::as_multi(Origin::signed(2), 2, vec![1, 3], Some(now()), data, false, 0), + Error::::WeightTooLow, + ); + }); +} + +#[test] +fn multisig_handles_no_preimage_after_all_approve() { + // This test checks the situation where everyone approves a multi-sig, but no-one provides the call data. + // In the end, any of the multisig callers can approve again with the call data and the call will go through. + new_test_ext().execute_with(|| { + let multi = Multisig::multi_account_id(&[1, 2, 3][..], 3); + assert_ok!(Balances::transfer(Origin::signed(1), multi, 5)); + assert_ok!(Balances::transfer(Origin::signed(2), multi, 5)); + assert_ok!(Balances::transfer(Origin::signed(3), multi, 5)); + + let call = Call::Balances(BalancesCall::transfer(6, 15)); + let call_weight = call.get_dispatch_info().weight; + let data = call.encode(); + let hash = blake2_256(&data); + assert_ok!(Multisig::approve_as_multi(Origin::signed(1), 3, vec![2, 3], None, hash.clone(), 0)); + assert_ok!(Multisig::approve_as_multi(Origin::signed(2), 3, vec![1, 3], Some(now()), hash.clone(), 0)); + assert_ok!(Multisig::approve_as_multi(Origin::signed(3), 3, vec![1, 2], Some(now()), hash.clone(), 0)); + assert_eq!(Balances::free_balance(6), 0); + + assert_ok!(Multisig::as_multi(Origin::signed(3), 3, vec![1, 2], Some(now()), data, false, call_weight)); + assert_eq!(Balances::free_balance(6), 15); + }); +} diff --git a/frame/nicks/Cargo.toml b/frame/nicks/Cargo.toml index 229c548eade13..544a0dc734ea7 100644 --- a/frame/nicks/Cargo.toml +++ b/frame/nicks/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/nicks/src/lib.rs b/frame/nicks/src/lib.rs index 304063d6cef24..1efd8c1756de3 100644 --- a/frame/nicks/src/lib.rs +++ b/frame/nicks/src/lib.rs @@ -47,7 +47,7 @@ use frame_support::{ decl_module, decl_event, decl_storage, ensure, decl_error, traits::{Currency, EnsureOrigin, ReservableCurrency, OnUnbalanced, Get}, }; -use frame_system::{self as system, ensure_signed, ensure_root}; +use frame_system::{self as system, ensure_signed}; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; @@ -197,9 +197,7 @@ decl_module! { /// # #[weight = 70_000_000] fn kill_name(origin, target: ::Source) { - T::ForceOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::ForceOrigin::ensure_origin(origin)?; // Figure out who we're meant to be clearing. let target = T::Lookup::lookup(target)?; @@ -225,9 +223,7 @@ decl_module! { /// # #[weight = 70_000_000] fn force_name(origin, target: ::Source, name: Vec) { - T::ForceOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::ForceOrigin::ensure_origin(origin)?; let target = T::Lookup::lookup(target)?; let deposit = >::get(&target).map(|x| x.1).unwrap_or_else(Zero::zero); @@ -270,6 +266,7 @@ mod tests { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -291,7 +288,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = pallet_balances::AccountData; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } parameter_types! { diff --git a/frame/offences/Cargo.toml b/frame/offences/Cargo.toml index fa36f42e4a222..0b8b74c4a95b3 100644 --- a/frame/offences/Cargo.toml +++ b/frame/offences/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] pallet-balances = { version = "2.0.0-rc3", default-features = false, path = "../balances" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } serde = { version = "1.0.101", optional = true } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/offences/benchmarking/Cargo.toml b/frame/offences/benchmarking/Cargo.toml index 366736ac4c173..ad8520484ef5a 100644 --- a/frame/offences/benchmarking/Cargo.toml +++ b/frame/offences/benchmarking/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME offences pallet benchmarking" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } frame-benchmarking = { version = "2.0.0-rc3", default-features = false, path = "../../benchmarking" } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../../support" } frame-system = { version = "2.0.0-rc3", default-features = false, path = "../../system" } @@ -28,7 +28,7 @@ sp-staking = { version = "2.0.0-rc3", default-features = false, path = "../../.. sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../../primitives/std" } [dev-dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } pallet-staking-reward-curve = { version = "2.0.0-rc3", path = "../../staking/reward-curve" } pallet-timestamp = { version = "2.0.0-rc3", path = "../../timestamp" } serde = { version = "1.0.101" } diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index 7c1b1d19e1626..786aff045ac32 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -42,6 +42,7 @@ parameter_types! { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = AccountIndex; type BlockNumber = BlockNumber; diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index 3aea787726b28..b385f6914320b 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -96,6 +96,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Runtime { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -117,7 +118,8 @@ impl frame_system::Trait for Runtime { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/proxy/Cargo.toml b/frame/proxy/Cargo.toml index beb924ab276bc..215f362cc8c08 100644 --- a/frame/proxy/Cargo.toml +++ b/frame/proxy/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } frame-system = { version = "2.0.0-rc3", default-features = false, path = "../system" } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } diff --git a/frame/proxy/src/lib.rs b/frame/proxy/src/lib.rs index 60305dfc74bed..bd56ad3f0f5fd 100644 --- a/frame/proxy/src/lib.rs +++ b/frame/proxy/src/lib.rs @@ -41,8 +41,8 @@ use sp_runtime::{DispatchResult, traits::{Dispatchable, Zero}}; use sp_runtime::traits::Member; use frame_support::{ decl_module, decl_event, decl_error, decl_storage, Parameter, ensure, traits::{ - Get, ReservableCurrency, Currency, Filter, FilterStack, FilterStackGuard, - ClearFilterGuard, InstanceFilter + Get, ReservableCurrency, Currency, InstanceFilter, + OriginTrait, IsType, }, weights::{GetDispatchInfo, constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS}}, dispatch::{PostDispatchInfo, IsSubType}, }; @@ -60,16 +60,16 @@ pub trait Trait: frame_system::Trait { /// The overarching call type. type Call: Parameter + Dispatchable - + GetDispatchInfo + From> + IsSubType, Self>; + + GetDispatchInfo + From> + IsSubType, Self> + + IsType<::Call>; /// The currency mechanism. type Currency: ReservableCurrency; - /// Is a given call compatible with the proxying subsystem? - type IsCallable: FilterStack<::Call>; - /// A kind of proxy; specified with the proxy and passed in to the `IsProxyable` fitler. /// The instance filter determines whether a given call may be proxied under this type. + /// + /// IMPORTANT: `Default` must be provided and MUST BE the the *most permissive* value. type ProxyType: Parameter + Member + Ord + PartialOrd + InstanceFilter<::Call> + Default; @@ -105,8 +105,6 @@ decl_error! { NotFound, /// Sender is not a proxy of the account to be proxied. NotProxy, - /// A call with a `false` `IsCallable` filter was attempted. - Uncallable, /// A call which is incompatible with the proxy type's filter was attempted. Unproxyable, /// Account is already a proxy. @@ -137,6 +135,15 @@ decl_module! { /// Deposit one of this module's events by using the default implementation. fn deposit_event() = default; + /// The base amount of currency needed to reserve for creating a proxy. + const ProxyDepositBase: BalanceOf = T::ProxyDepositBase::get(); + + /// The amount of currency needed per proxy added. + const ProxyDepositFactor: BalanceOf = T::ProxyDepositFactor::get(); + + /// The maximum amount of proxies allowed for a single account. + const MaxProxies: u16 = T::MaxProxies::get(); + /// Dispatch the given `call` from an account that the sender is authorised for through /// `add_proxy`. /// @@ -171,19 +178,19 @@ decl_module! { .find(|x| &x.0 == &who && force_proxy_type.as_ref().map_or(true, |y| &x.1 == y)) .ok_or(Error::::NotProxy)?; - // We're now executing as a freshly authenticated new account, so the previous call - // restrictions no longer apply. - let _clear_guard = ClearFilterGuard::::Call>::new(); - let _filter_guard = FilterStackGuard::::Call>::new( - move |c| match c.is_sub_type() { + // This is a freshly authenticated new account, the origin restrictions doesn't apply. + let mut origin: T::Origin = frame_system::RawOrigin::Signed(real).into(); + origin.add_filter(move |c: &::Call| { + let c = ::Call::from_ref(c); + match c.is_sub_type() { Some(Call::add_proxy(_, ref pt)) | Some(Call::remove_proxy(_, ref pt)) if !proxy_type.is_superset(&pt) => false, - _ => proxy_type.filter(&c) + Some(Call::remove_proxies(..)) | Some(Call::kill_anonymous(..)) + if proxy_type != T::ProxyType::default() => false, + _ => proxy_type.filter(c) } - ); - ensure!(T::IsCallable::filter(&call), Error::::Uncallable); - - let e = call.dispatch(frame_system::RawOrigin::Signed(real).into()); + }); + let e = call.dispatch(origin); Self::deposit_event(RawEvent::ProxyExecuted(e.map(|_| ()).map_err(|e| e.error))); } diff --git a/frame/proxy/src/tests.rs b/frame/proxy/src/tests.rs index c2ab06143bfe0..3d539d3cf07ae 100644 --- a/frame/proxy/src/tests.rs +++ b/frame/proxy/src/tests.rs @@ -23,7 +23,7 @@ use super::*; use frame_support::{ assert_ok, assert_noop, impl_outer_origin, parameter_types, impl_outer_dispatch, - impl_filter_stack, weights::Weight, impl_outer_event, RuntimeDebug, dispatch::DispatchError + weights::Weight, impl_outer_event, RuntimeDebug, dispatch::DispatchError, traits::Filter, }; use codec::{Encode, Decode}; use sp_core::H256; @@ -62,6 +62,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = BaseFilter; type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -100,15 +101,12 @@ impl pallet_balances::Trait for Test { impl pallet_utility::Trait for Test { type Event = TestEvent; type Call = Call; - type IsCallable = IsCallable; } parameter_types! { pub const ProxyDepositBase: u64 = 1; pub const ProxyDepositFactor: u64 = 1; pub const MaxProxies: u16 = 4; } -pub struct IsCallable; -impl_filter_stack!(crate::tests::IsCallable, crate::tests::BaseFilter, crate::tests::Call, is_callable); #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug)] pub enum ProxyType { Any, @@ -143,7 +141,6 @@ impl Trait for Test { type Event = TestEvent; type Call = Call; type Currency = Balances; - type IsCallable = IsCallable; type ProxyType = ProxyType; type ProxyDepositBase = ProxyDepositBase; type ProxyDepositFactor = ProxyDepositFactor; @@ -158,8 +155,8 @@ type Proxy = Module; use frame_system::Call as SystemCall; use pallet_balances::Call as BalancesCall; use pallet_balances::Error as BalancesError; +use pallet_balances::Event as BalancesEvent; use pallet_utility::Call as UtilityCall; -use pallet_utility::Error as UtilityError; use pallet_utility::Event as UtilityEvent; use super::Call as ProxyCall; @@ -202,7 +199,8 @@ fn filtering_works() { expect_event(RawEvent::ProxyExecuted(Ok(()))); assert_ok!(Proxy::proxy(Origin::signed(3), 1, None, call.clone())); expect_event(RawEvent::ProxyExecuted(Ok(()))); - assert_noop!(Proxy::proxy(Origin::signed(4), 1, None, call.clone()), Error::::Uncallable); + assert_ok!(Proxy::proxy(Origin::signed(4), 1, None, call.clone())); + expect_event(RawEvent::ProxyExecuted(Err(DispatchError::BadOrigin))); let sub_id = Utility::sub_account_id(1, 0); Balances::mutate_account(&sub_id, |a| a.free = 1000); @@ -211,32 +209,49 @@ fn filtering_works() { let call = Box::new(Call::Utility(UtilityCall::as_sub(0, inner.clone()))); assert_ok!(Proxy::proxy(Origin::signed(2), 1, None, call.clone())); expect_event(RawEvent::ProxyExecuted(Ok(()))); - assert_noop!(Proxy::proxy(Origin::signed(3), 1, None, call.clone()), Error::::Uncallable); + assert_ok!(Proxy::proxy(Origin::signed(3), 1, None, call.clone())); + expect_event(RawEvent::ProxyExecuted(Err(DispatchError::BadOrigin))); assert_ok!(Proxy::proxy(Origin::signed(4), 1, None, call.clone())); expect_event(RawEvent::ProxyExecuted(Ok(()))); let call = Box::new(Call::Utility(UtilityCall::as_limited_sub(0, inner.clone()))); assert_ok!(Proxy::proxy(Origin::signed(2), 1, None, call.clone())); expect_event(RawEvent::ProxyExecuted(Ok(()))); - assert_noop!(Proxy::proxy(Origin::signed(3), 1, None, call.clone()), Error::::Uncallable); + assert_ok!(Proxy::proxy(Origin::signed(3), 1, None, call.clone())); + expect_event(RawEvent::ProxyExecuted(Err(DispatchError::BadOrigin))); assert_ok!(Proxy::proxy(Origin::signed(4), 1, None, call.clone())); - let de = DispatchError::from(UtilityError::::Uncallable).stripped(); - expect_event(RawEvent::ProxyExecuted(Err(de))); + expect_event(RawEvent::ProxyExecuted(Err(DispatchError::BadOrigin))); let call = Box::new(Call::Utility(UtilityCall::batch(vec![*inner]))); assert_ok!(Proxy::proxy(Origin::signed(2), 1, None, call.clone())); expect_events(vec![UtilityEvent::BatchCompleted.into(), RawEvent::ProxyExecuted(Ok(())).into()]); - assert_noop!(Proxy::proxy(Origin::signed(3), 1, None, call.clone()), Error::::Uncallable); + assert_ok!(Proxy::proxy(Origin::signed(3), 1, None, call.clone())); + expect_event(RawEvent::ProxyExecuted(Err(DispatchError::BadOrigin))); assert_ok!(Proxy::proxy(Origin::signed(4), 1, None, call.clone())); - expect_events(vec![UtilityEvent::Uncallable(0).into(), RawEvent::ProxyExecuted(Ok(())).into()]); + expect_events(vec![ + UtilityEvent::BatchInterrupted(0, DispatchError::BadOrigin).into(), + RawEvent::ProxyExecuted(Ok(())).into(), + ]); let inner = Box::new(Call::Proxy(ProxyCall::add_proxy(5, ProxyType::Any))); let call = Box::new(Call::Utility(UtilityCall::batch(vec![*inner]))); assert_ok!(Proxy::proxy(Origin::signed(2), 1, None, call.clone())); expect_events(vec![UtilityEvent::BatchCompleted.into(), RawEvent::ProxyExecuted(Ok(())).into()]); - assert_noop!(Proxy::proxy(Origin::signed(3), 1, None, call.clone()), Error::::Uncallable); + assert_ok!(Proxy::proxy(Origin::signed(3), 1, None, call.clone())); + expect_event(RawEvent::ProxyExecuted(Err(DispatchError::BadOrigin))); assert_ok!(Proxy::proxy(Origin::signed(4), 1, None, call.clone())); - expect_events(vec![UtilityEvent::Uncallable(0).into(), RawEvent::ProxyExecuted(Ok(())).into()]); + expect_events(vec![ + UtilityEvent::BatchInterrupted(0, DispatchError::BadOrigin).into(), + RawEvent::ProxyExecuted(Ok(())).into(), + ]); + + let call = Box::new(Call::Proxy(ProxyCall::remove_proxies())); + assert_ok!(Proxy::proxy(Origin::signed(3), 1, None, call.clone())); + expect_event(RawEvent::ProxyExecuted(Err(DispatchError::BadOrigin))); + assert_ok!(Proxy::proxy(Origin::signed(4), 1, None, call.clone())); + expect_event(RawEvent::ProxyExecuted(Err(DispatchError::BadOrigin))); + assert_ok!(Proxy::proxy(Origin::signed(2), 1, None, call.clone())); + expect_events(vec![BalancesEvent::::Unreserved(1, 5).into(), RawEvent::ProxyExecuted(Ok(())).into()]); }); } @@ -294,10 +309,12 @@ fn proxying_works() { assert_eq!(Balances::free_balance(6), 1); let call = Box::new(Call::System(SystemCall::set_code(vec![]))); - assert_noop!(Proxy::proxy(Origin::signed(3), 1, None, call.clone()), Error::::Uncallable); + assert_ok!(Proxy::proxy(Origin::signed(3), 1, None, call.clone())); + expect_event(RawEvent::ProxyExecuted(Err(DispatchError::BadOrigin))); let call = Box::new(Call::Balances(BalancesCall::transfer_keep_alive(6, 1))); - assert_noop!(Proxy::proxy(Origin::signed(2), 1, None, call.clone()), Error::::Uncallable); + assert_ok!(Call::Proxy(super::Call::proxy(1, None, call.clone())).dispatch(Origin::signed(2))); + expect_event(RawEvent::ProxyExecuted(Err(DispatchError::BadOrigin))); assert_ok!(Proxy::proxy(Origin::signed(3), 1, None, call.clone())); expect_event(RawEvent::ProxyExecuted(Ok(()))); assert_eq!(Balances::free_balance(6), 2); diff --git a/frame/randomness-collective-flip/Cargo.toml b/frame/randomness-collective-flip/Cargo.toml index fb3775a625485..7e645394919b4 100644 --- a/frame/randomness-collective-flip/Cargo.toml +++ b/frame/randomness-collective-flip/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] safe-mix = { version = "1.0", default-features = false } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } frame-system = { version = "2.0.0-rc3", default-features = false, path = "../system" } diff --git a/frame/randomness-collective-flip/src/lib.rs b/frame/randomness-collective-flip/src/lib.rs index d3f960d436732..0f883476da25d 100644 --- a/frame/randomness-collective-flip/src/lib.rs +++ b/frame/randomness-collective-flip/src/lib.rs @@ -158,6 +158,7 @@ mod tests { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -179,7 +180,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/recovery/Cargo.toml b/frame/recovery/Cargo.toml index 88a738b058851..33f7b5e521c77 100644 --- a/frame/recovery/Cargo.toml +++ b/frame/recovery/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } enumflags2 = { version = "0.6.2" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } diff --git a/frame/recovery/src/lib.rs b/frame/recovery/src/lib.rs index 808e8c8d323bb..210b35ba7f440 100644 --- a/frame/recovery/src/lib.rs +++ b/frame/recovery/src/lib.rs @@ -160,7 +160,7 @@ use codec::{Encode, Decode}; use frame_support::{ decl_module, decl_event, decl_storage, decl_error, ensure, - Parameter, RuntimeDebug, weights::{GetDispatchInfo, FunctionOf, Pays}, + Parameter, RuntimeDebug, weights::GetDispatchInfo, traits::{Currency, ReservableCurrency, Get, BalanceStatus}, dispatch::PostDispatchInfo, }; @@ -328,6 +328,18 @@ decl_module! { pub struct Module for enum Call where origin: T::Origin { type Error = Error; + /// The base amount of currency needed to reserve for creating a recovery configuration. + const ConfigDepositBase: BalanceOf = T::ConfigDepositBase::get(); + + /// The amount of currency needed per additional user when creating a recovery configuration. + const FriendDepositFactor: BalanceOf = T::FriendDepositFactor::get(); + + /// The maximum amount of friends allowed in a recovery configuration. + const MaxFriends: u16 = T::MaxFriends::get(); + + /// The base amount of currency needed to reserve for starting a recovery. + const RecoveryDeposit: BalanceOf = T::RecoveryDeposit::get(); + /// Deposit one of this module's events by using the default implementation. fn deposit_event() = default; @@ -344,11 +356,7 @@ decl_module! { /// - The weight of the `call` + 10,000. /// - One storage lookup to check account is recovered by `who`. O(1) /// # - #[weight = FunctionOf( - |args: (&T::AccountId, &Box<::Call>)| args.1.get_dispatch_info().weight + 10_000, - |args: (&T::AccountId, &Box<::Call>)| args.1.get_dispatch_info().class, - Pays::Yes, - )] + #[weight = (call.get_dispatch_info().weight + 10_000, call.get_dispatch_info().class)] fn as_recovered(origin, account: T::AccountId, call: Box<::Call> diff --git a/frame/recovery/src/mock.rs b/frame/recovery/src/mock.rs index 2632511a8c341..ac5d1dcc53839 100644 --- a/frame/recovery/src/mock.rs +++ b/frame/recovery/src/mock.rs @@ -64,6 +64,7 @@ parameter_types! { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Call = Call; type Index = u64; @@ -85,7 +86,8 @@ impl frame_system::Trait for Test { type Version = (); type ModuleToIndex = (); type AccountData = pallet_balances::AccountData; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/recovery/src/tests.rs b/frame/recovery/src/tests.rs index 5192bdaca85e2..8e9484f0fb089 100644 --- a/frame/recovery/src/tests.rs +++ b/frame/recovery/src/tests.rs @@ -46,7 +46,7 @@ fn set_recovered_works() { // Not accessible by a normal user assert_noop!(Recovery::set_recovered(Origin::signed(1), 5, 1), BadOrigin); // Root can set a recovered account though - assert_ok!(Recovery::set_recovered(Origin::ROOT, 5, 1)); + assert_ok!(Recovery::set_recovered(Origin::root(), 5, 1)); // Account 1 should now be able to make a call through account 5 let call = Box::new(Call::Balances(BalancesCall::transfer(1, 100))); assert_ok!(Recovery::as_recovered(Origin::signed(1), 5, call)); diff --git a/frame/scheduler/src/lib.rs b/frame/scheduler/src/lib.rs index 2c7cc6a8aa3e6..f6012ee719b6f 100644 --- a/frame/scheduler/src/lib.rs +++ b/frame/scheduler/src/lib.rs @@ -16,31 +16,29 @@ // limitations under the License. //! # Scheduler +//! A module for scheduling dispatches. //! -//! \# Scheduler +//! - [`scheduler::Trait`](./trait.Trait.html) +//! - [`Call`](./enum.Call.html) +//! - [`Module`](./struct.Module.html) //! -//! - \[`scheduler::Trait`](./trait.Trait.html) -//! - \[`Call`](./enum.Call.html) -//! - \[`Module`](./struct.Module.html) +//! ## Overview //! -//! \## Overview +//! This module exposes capabilities for scheduling dispatches to occur at a +//! specified block number or at a specified period. These scheduled dispatches +//! may be named or anonymous and may be canceled. //! -//! // Short description of pallet's purpose. -//! // Links to Traits that should be implemented. -//! // What this pallet is for. -//! // What functionality the pallet provides. -//! // When to use the pallet (use case examples). -//! // How it is used. -//! // Inputs it uses and the source of each input. -//! // Outputs it produces. +//! ## Interface //! -//! \## Terminology +//! ### Dispatchable Functions //! -//! \## Goals -//! -//! \## Interface -//! -//! \### Dispatchable Functions +//! * `schedule` - schedule a dispatch, which may be periodic, to occur at a +//! specified block and with a specified priority. +//! * `cancel` - cancel a scheduled dispatch, specified by block number and +//! index. +//! * `schedule_named` - augments the `schedule` interface with an additional +//! `Vec` parameter that can be used for identification. +//! * `cancel_named` - the named complement to the cancel function. // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] @@ -121,6 +119,8 @@ decl_error! { FailedToSchedule, /// Failed to cancel a scheduled call FailedToCancel, + /// Given target block number is in the past. + TargetBlockNumberInPast, } } @@ -147,7 +147,7 @@ decl_module! { call: Box<::Call>, ) { ensure_root(origin)?; - let _ = Self::do_schedule(when, maybe_periodic, priority, *call); + Self::do_schedule(when, maybe_periodic, priority, *call)?; } /// Cancel an anonymously scheduled task. @@ -296,7 +296,11 @@ impl Module { maybe_periodic: Option>, priority: schedule::Priority, call: ::Call - ) -> TaskAddress { + ) -> Result, DispatchError> { + if when <= frame_system::Module::::block_number() { + return Err(Error::::TargetBlockNumberInPast.into()) + } + // sanitize maybe_periodic let maybe_periodic = maybe_periodic .filter(|p| p.1 > 1 && !p.0.is_zero()) @@ -306,7 +310,8 @@ impl Module { Agenda::::append(when, s); let index = Agenda::::decode_len(when).unwrap_or(1) as u32 - 1; Self::deposit_event(RawEvent::Scheduled(when, index)); - (when, index) + + Ok((when, index)) } fn do_cancel((when, index): TaskAddress) -> Result<(), DispatchError> { @@ -333,6 +338,10 @@ impl Module { return Err(Error::::FailedToSchedule)? } + if when <= frame_system::Module::::block_number() { + return Err(Error::::TargetBlockNumberInPast.into()) + } + // sanitize maybe_periodic let maybe_periodic = maybe_periodic .filter(|p| p.1 > 1 && !p.0.is_zero()) @@ -345,6 +354,7 @@ impl Module { let address = (when, index); Lookup::::insert(&id, &address); Self::deposit_event(RawEvent::Scheduled(when, index)); + Ok(address) } @@ -368,7 +378,7 @@ impl schedule::Anon::Call> for Module maybe_periodic: Option>, priority: schedule::Priority, call: ::Call - ) -> Self::Address { + ) -> Result { Self::do_schedule(when, maybe_periodic, priority, call) } @@ -401,8 +411,7 @@ mod tests { use frame_support::{ impl_outer_event, impl_outer_origin, impl_outer_dispatch, parameter_types, assert_ok, - traits::{OnInitialize, OnFinalize}, - weights::{DispatchClass, FunctionOf, Pays, constants::RocksDbWeight}, + assert_err, traits::{OnInitialize, OnFinalize, Filter}, weights::constants::RocksDbWeight, }; use sp_core::H256; // The testing primitives are very useful for avoiding having to work with signatures @@ -441,11 +450,7 @@ mod tests { pub struct Module for enum Call where origin: ::Origin { fn deposit_event() = default; - #[weight = FunctionOf( - |args: (&u32, &Weight)| *args.1, - |_: (&u32, &Weight)| DispatchClass::Normal, - Pays::Yes, - )] + #[weight = *weight] fn log(origin, i: u32, weight: Weight) { ensure_root(origin)?; Self::deposit_event(Event::Logged(i, weight)); @@ -475,6 +480,15 @@ mod tests { scheduler, } } + + // Scheduler must dispatch with root and no filter, this tests base filter is indeed not used. + pub struct BaseFilter; + impl Filter for BaseFilter { + fn filter(call: &Call) -> bool { + !matches!(call, Call::Logger(_)) + } + } + // For testing the pallet, we construct most of a mock runtime. This means // first constructing a configuration type (`Test`) which `impl`s each of the // configuration traits of pallets we want to use. @@ -487,8 +501,9 @@ mod tests { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl system::Trait for Test { + type BaseCallFilter = BaseFilter; type Origin = Origin; - type Call = (); + type Call = Call; type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -546,7 +561,9 @@ mod tests { #[test] fn basic_scheduling_works() { new_test_ext().execute_with(|| { - Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(42, 1000))); + let call = Call::Logger(logger::Call::log(42, 1000)); + assert!(!::BaseCallFilter::filter(&call)); + let _ = Scheduler::do_schedule(4, None, 127, call); run_to_block(3); assert!(logger::log().is_empty()); run_to_block(4); @@ -560,7 +577,7 @@ mod tests { fn periodic_scheduling_works() { new_test_ext().execute_with(|| { // at #4, every 3 blocks, 3 times. - Scheduler::do_schedule(4, Some((3, 3)), 127, Call::Logger(logger::Call::log(42, 1000))); + let _ = Scheduler::do_schedule(4, Some((3, 3)), 127, Call::Logger(logger::Call::log(42, 1000))); run_to_block(3); assert!(logger::log().is_empty()); run_to_block(4); @@ -583,7 +600,7 @@ mod tests { new_test_ext().execute_with(|| { // at #4. Scheduler::do_schedule_named(1u32.encode(), 4, None, 127, Call::Logger(logger::Call::log(69, 1000))).unwrap(); - let i = Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(42, 1000))); + let i = Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(42, 1000))).unwrap(); run_to_block(3); assert!(logger::log().is_empty()); assert_ok!(Scheduler::do_cancel_named(1u32.encode())); @@ -616,8 +633,8 @@ mod tests { #[test] fn scheduler_respects_weight_limits() { new_test_ext().execute_with(|| { - Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))); - Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); + let _ = Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))); + let _ = Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); // 69 and 42 do not fit together run_to_block(4); assert_eq!(logger::log(), vec![42u32]); @@ -629,8 +646,8 @@ mod tests { #[test] fn scheduler_respects_hard_deadlines_more() { new_test_ext().execute_with(|| { - Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))); - Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); + let _ = Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))); + let _ = Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); // With base weights, 69 and 42 should not fit together, but do because of hard deadlines run_to_block(4); assert_eq!(logger::log(), vec![42u32, 69u32]); @@ -640,8 +657,8 @@ mod tests { #[test] fn scheduler_respects_priority_ordering() { new_test_ext().execute_with(|| { - Scheduler::do_schedule(4, None, 1, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))); - Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); + let _ = Scheduler::do_schedule(4, None, 1, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 2))); + let _ = Scheduler::do_schedule(4, None, 0, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); run_to_block(4); assert_eq!(logger::log(), vec![69u32, 42u32]); }); @@ -650,9 +667,24 @@ mod tests { #[test] fn scheduler_respects_priority_ordering_with_soft_deadlines() { new_test_ext().execute_with(|| { - Scheduler::do_schedule(4, None, 255, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3))); - Scheduler::do_schedule(4, None, 127, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); - Scheduler::do_schedule(4, None, 126, Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2))); + let _ = Scheduler::do_schedule( + 4, + None, + 255, + Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3)), + ); + let _ = Scheduler::do_schedule( + 4, + None, + 127, + Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)), + ); + let _ = Scheduler::do_schedule( + 4, + None, + 126, + Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2)), + ); // 2600 does not fit with 69 or 42, but has higher priority, so will go through run_to_block(4); @@ -674,11 +706,27 @@ mod tests { // Named assert_ok!(Scheduler::do_schedule_named(1u32.encode(), 1, None, 255, Call::Logger(logger::Call::log(3, MaximumSchedulerWeight::get() / 3)))); // Anon Periodic - Scheduler::do_schedule(1, Some((1000, 3)), 128, Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3))); + let _ = Scheduler::do_schedule( + 1, + Some((1000, 3)), + 128, + Call::Logger(logger::Call::log(42, MaximumSchedulerWeight::get() / 3)), + ); // Anon - Scheduler::do_schedule(1, None, 127, Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2))); + let _ = Scheduler::do_schedule( + 1, + None, + 127, + Call::Logger(logger::Call::log(69, MaximumSchedulerWeight::get() / 2)), + ); // Named Periodic - assert_ok!(Scheduler::do_schedule_named(2u32.encode(), 1, Some((1000, 3)), 126, Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2)))); + assert_ok!(Scheduler::do_schedule_named( + 2u32.encode(), + 1, + Some((1000, 3)), + 126, + Call::Logger(logger::Call::log(2600, MaximumSchedulerWeight::get() / 2)), + )); // Will include the named periodic only let actual_weight = Scheduler::on_initialize(1); @@ -709,17 +757,42 @@ mod tests { new_test_ext().execute_with(|| { let call = Box::new(Call::Logger(logger::Call::log(69, 1000))); let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000))); - assert_ok!(Scheduler::schedule_named(Origin::ROOT, 1u32.encode(), 4, None, 127, call)); - assert_ok!(Scheduler::schedule(Origin::ROOT, 4, None, 127, call2)); + assert_ok!(Scheduler::schedule_named(Origin::root(), 1u32.encode(), 4, None, 127, call)); + assert_ok!(Scheduler::schedule(Origin::root(), 4, None, 127, call2)); run_to_block(3); // Scheduled calls are in the agenda. assert_eq!(Agenda::::get(4).len(), 2); assert!(logger::log().is_empty()); - assert_ok!(Scheduler::cancel_named(Origin::ROOT, 1u32.encode())); - assert_ok!(Scheduler::cancel(Origin::ROOT, 4, 1)); + assert_ok!(Scheduler::cancel_named(Origin::root(), 1u32.encode())); + assert_ok!(Scheduler::cancel(Origin::root(), 4, 1)); // Scheduled calls are made NONE, so should not effect state run_to_block(100); assert!(logger::log().is_empty()); }); } + + #[test] + fn fails_to_schedule_task_in_the_past() { + new_test_ext().execute_with(|| { + run_to_block(3); + + let call = Box::new(Call::Logger(logger::Call::log(69, 1000))); + let call2 = Box::new(Call::Logger(logger::Call::log(42, 1000))); + + assert_err!( + Scheduler::schedule_named(Origin::root(), 1u32.encode(), 2, None, 127, call), + Error::::TargetBlockNumberInPast, + ); + + assert_err!( + Scheduler::schedule(Origin::root(), 2, None, 127, call2.clone()), + Error::::TargetBlockNumberInPast, + ); + + assert_err!( + Scheduler::schedule(Origin::root(), 3, None, 127, call2), + Error::::TargetBlockNumberInPast, + ); + }); + } } diff --git a/frame/scored-pool/Cargo.toml b/frame/scored-pool/Cargo.toml index b5009079d28af..d1e0a5d62e3c1 100644 --- a/frame/scored-pool/Cargo.toml +++ b/frame/scored-pool/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME pallet for scored pools" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/scored-pool/src/lib.rs b/frame/scored-pool/src/lib.rs index ba56298493a99..5131a663e0e61 100644 --- a/frame/scored-pool/src/lib.rs +++ b/frame/scored-pool/src/lib.rs @@ -318,9 +318,7 @@ decl_module! { dest: ::Source, index: u32 ) { - T::KickOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::KickOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(dest)?; @@ -344,9 +342,7 @@ decl_module! { index: u32, score: T::Score ) { - T::ScoreOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::ScoreOrigin::ensure_origin(origin)?; let who = T::Lookup::lookup(dest)?; diff --git a/frame/scored-pool/src/mock.rs b/frame/scored-pool/src/mock.rs index 23107b5ae6b0f..6f41069c537fe 100644 --- a/frame/scored-pool/src/mock.rs +++ b/frame/scored-pool/src/mock.rs @@ -55,6 +55,7 @@ ord_parameter_types! { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -76,7 +77,8 @@ impl frame_system::Trait for Test { type Version = (); type ModuleToIndex = (); type AccountData = pallet_balances::AccountData; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/session/Cargo.toml b/frame/session/Cargo.toml index 6955940dc4d5a..38eef24bc602a 100644 --- a/frame/session/Cargo.toml +++ b/frame/session/Cargo.toml @@ -13,8 +13,10 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } +sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } sp-session = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/session" } sp-staking = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/staking" } @@ -25,8 +27,6 @@ sp-trie = { version = "2.0.0-rc3", optional = true, default-features = false, pa impl-trait-for-tuples = "0.1.3" [dev-dependencies] -sp-core = { version = "2.0.0-rc3", path = "../../primitives/core" } -sp-io ={ version = "2.0.0-rc3", path = "../../primitives/io" } sp-application-crypto = { version = "2.0.0-rc3", path = "../../primitives/application-crypto" } lazy_static = "1.4.0" @@ -37,7 +37,9 @@ std = [ "serde", "codec/std", "sp-std/std", + "sp-io/std", "frame-support/std", + "sp-core/std", "sp-runtime/std", "sp-session/std", "sp-staking/std", diff --git a/frame/session/benchmarking/Cargo.toml b/frame/session/benchmarking/Cargo.toml index da969932b171d..b2c70c28d170a 100644 --- a/frame/session/benchmarking/Cargo.toml +++ b/frame/session/benchmarking/Cargo.toml @@ -22,7 +22,7 @@ pallet-session = { version = "2.0.0-rc3", default-features = false, path = "../. [dev-dependencies] serde = { version = "1.0.101" } -codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } sp-core = { version = "2.0.0-rc3", path = "../../../primitives/core" } pallet-staking-reward-curve = { version = "2.0.0-rc3", path = "../../staking/reward-curve" } sp-io ={ version = "2.0.0-rc3", path = "../../../primitives/io" } diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 6d313ad567b94..265c3da9e7d9a 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -58,6 +58,7 @@ impl Convert for CurrencyToVoteHandler { pub struct Test; impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = AccountIndex; type BlockNumber = BlockNumber; diff --git a/frame/session/src/historical.rs b/frame/session/src/historical/mod.rs similarity index 97% rename from frame/session/src/historical.rs rename to frame/session/src/historical/mod.rs index 84a86781c942c..1b6ac0c2adb94 100644 --- a/frame/session/src/historical.rs +++ b/frame/session/src/historical/mod.rs @@ -37,6 +37,10 @@ use sp_trie::{MemoryDB, Trie, TrieMut, Recorder, EMPTY_PREFIX}; use sp_trie::trie_types::{TrieDBMut, TrieDB}; use super::{SessionIndex, Module as SessionModule}; +mod shared; +pub mod offchain; +pub mod onchain; + /// Trait necessary for the historical module. pub trait Trait: super::Trait { /// Full identification of the validator. @@ -139,6 +143,7 @@ impl crate::SessionManager for NoteHistoricalRoot { fn new_session(new_index: SessionIndex) -> Option> { + StoredRange::mutate(|range| { range.get_or_insert_with(|| (new_index, new_index)).1 = new_index + 1; }); @@ -166,10 +171,13 @@ impl crate::SessionManager for NoteHistoricalRoot>::start_session(start_index) } + fn end_session(end_index: SessionIndex) { + onchain::store_session_validator_set_to_offchain::(end_index); >::end_session(end_index) } } @@ -177,7 +185,7 @@ impl crate::SessionManager for NoteHistoricalRoot = (::ValidatorId, ::FullIdentification); -/// a trie instance for checking and generating proofs. +/// A trie instance for checking and generating proofs. pub struct ProvingTrie { db: MemoryDB, root: T::Hash, @@ -273,7 +281,6 @@ impl ProvingTrie { .ok()? .and_then(|raw| >::decode(&mut &*raw).ok()) } - } impl> frame_support::traits::KeyOwnerProofSystem<(KeyTypeId, D)> @@ -334,9 +341,9 @@ impl> frame_support::traits::KeyOwnerProofSystem<(KeyTy } #[cfg(test)] -mod tests { +pub(crate) mod tests { use super::*; - use sp_core::crypto::key_types::DUMMY; + use sp_runtime::key_types::DUMMY; use sp_runtime::testing::UintAuthorityId; use crate::mock::{ NEXT_VALIDATORS, force_new_session, @@ -346,7 +353,7 @@ mod tests { type Historical = Module; - fn new_test_ext() -> sp_io::TestExternalities { + pub(crate) fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); crate::GenesisConfig:: { keys: NEXT_VALIDATORS.with(|l| diff --git a/frame/session/src/historical/offchain.rs b/frame/session/src/historical/offchain.rs new file mode 100644 index 0000000000000..97655d1a18b32 --- /dev/null +++ b/frame/session/src/historical/offchain.rs @@ -0,0 +1,263 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Off-chain logic for creating a proof based data provided by on-chain logic. +//! +//! Validator-set extracting an iterator from an off-chain worker stored list containing +//! historical validator-sets. +//! Based on the logic of historical slashing, but the validation is done off-chain. +//! Use [`fn store_current_session_validator_set_to_offchain()`](super::onchain) to store the +//! required data to the offchain validator set. +//! This is used in conjunction with [`ProvingTrie`](super::ProvingTrie) and +//! the off-chain indexing API. + +use sp_runtime::{offchain::storage::StorageValueRef, KeyTypeId}; +use sp_session::MembershipProof; + +use super::super::{Module as SessionModule, SessionIndex}; +use super::{IdentificationTuple, ProvingTrie, Trait}; + +use super::shared; +use sp_std::prelude::*; + + +/// A set of validators, which was used for a fixed session index. +struct ValidatorSet { + validator_set: Vec>, +} + +impl ValidatorSet { + /// Load the set of validators for a particular session index from the off-chain storage. + /// + /// If none is found or decodable given `prefix` and `session`, it will return `None`. + /// Empty validator sets should only ever exist for genesis blocks. + pub fn load_from_offchain_db(session_index: SessionIndex) -> Option { + let derived_key = shared::derive_key(shared::PREFIX, session_index); + StorageValueRef::persistent(derived_key.as_ref()) + .get::>() + .flatten() + .map(|validator_set| Self { validator_set }) + } + + #[inline] + fn len(&self) -> usize { + self.validator_set.len() + } +} + +/// Implement conversion into iterator for usage +/// with [ProvingTrie](super::ProvingTrie::generate_for). +impl sp_std::iter::IntoIterator for ValidatorSet { + type Item = (T::ValidatorId, T::FullIdentification); + type IntoIter = sp_std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.validator_set.into_iter() + } +} + +/// Create a proof based on the data available in the off-chain database. +/// +/// Based on the yielded `MembershipProof` the implementer may decide what +/// to do, i.e. in case of a failed proof, enqueue a transaction back on +/// chain reflecting that, with all its consequences such as i.e. slashing. +pub fn prove_session_membership>( + session_index: SessionIndex, + session_key: (KeyTypeId, D), +) -> Option { + let validators = ValidatorSet::::load_from_offchain_db(session_index)?; + let count = validators.len() as u32; + let trie = ProvingTrie::::generate_for(validators.into_iter()).ok()?; + + let (id, data) = session_key; + trie.prove(id, data.as_ref()) + .map(|trie_nodes| MembershipProof { + session: session_index, + trie_nodes, + validator_count: count, + }) +} + + +/// Attempt to prune anything that is older than `first_to_keep` session index. +/// +/// Due to re-organisation it could be that the `first_to_keep` might be less +/// than the stored one, in which case the conservative choice is made to keep records +/// up to the one that is the lesser. +pub fn prune_older_than(first_to_keep: SessionIndex) { + let derived_key = shared::LAST_PRUNE.to_vec(); + let entry = StorageValueRef::persistent(derived_key.as_ref()); + match entry.mutate(|current: Option>| -> Result<_, ()> { + match current { + Some(Some(current)) if current < first_to_keep => Ok(first_to_keep), + // do not move the cursor, if the new one would be behind ours + Some(Some(current)) => Ok(current), + None => Ok(first_to_keep), + // if the storage contains undecodable data, overwrite with current anyways + // which might leak some entries being never purged, but that is acceptable + // in this context + Some(None) => Ok(first_to_keep), + } + }) { + Ok(Ok(new_value)) => { + // on a re-org this is not necessarily true, with the above they might be equal + if new_value < first_to_keep { + for session_index in new_value..first_to_keep { + let derived_key = shared::derive_key(shared::PREFIX, session_index); + let _ = StorageValueRef::persistent(derived_key.as_ref()).clear(); + } + } + } + Ok(Err(_)) => {} // failed to store the value calculated with the given closure + Err(_) => {} // failed to calculate the value to store with the given closure + } +} + +/// Keep the newest `n` items, and prune all items older than that. +pub fn keep_newest(n_to_keep: usize) { + let session_index = >::current_index(); + let n_to_keep = n_to_keep as SessionIndex; + if n_to_keep < session_index { + prune_older_than::(session_index - n_to_keep) + } +} + +#[cfg(test)] +mod tests { + use super::super::{onchain, Module}; + use super::*; + use crate::mock::{ + force_new_session, set_next_validators, Session, System, Test, NEXT_VALIDATORS, + }; + use codec::Encode; + use frame_support::traits::{KeyOwnerProofSystem, OnInitialize}; + use sp_core::crypto::key_types::DUMMY; + use sp_core::offchain::{ + testing::TestOffchainExt, + OffchainExt, + StorageKind, + }; + + use sp_runtime::testing::UintAuthorityId; + + type Historical = Module; + + pub fn new_test_ext() -> sp_io::TestExternalities { + let mut ext = frame_system::GenesisConfig::default() + .build_storage::() + .expect("Failed to create test externalities."); + + crate::GenesisConfig:: { + keys: NEXT_VALIDATORS.with(|l| { + l.borrow() + .iter() + .cloned() + .map(|i| (i, i, UintAuthorityId(i).into())) + .collect() + }), + } + .assimilate_storage(&mut ext) + .unwrap(); + + + let mut ext = sp_io::TestExternalities::new(ext); + + let (offchain, offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db()); + + const ITERATIONS: u32 = 5u32; + let mut seed = [0u8; 32]; + seed[0..4].copy_from_slice(&ITERATIONS.to_le_bytes()); + offchain_state.write().seed = seed; + + ext.register_extension(OffchainExt::new(offchain)); + ext + } + + #[test] + fn encode_decode_roundtrip() { + use codec::{Decode, Encode}; + use super::super::super::Trait as SessionTrait; + use super::super::Trait as HistoricalTrait; + + let sample = ( + 22u32 as ::ValidatorId, + 7_777_777 as ::FullIdentification); + + let encoded = sample.encode(); + let decoded = Decode::decode(&mut encoded.as_slice()).expect("Must decode"); + assert_eq!(sample, decoded); + } + + #[test] + fn onchain_to_offchain() { + let mut ext = new_test_ext(); + + const DATA: &[u8] = &[7,8,9,10,11]; + ext.execute_with(|| { + b"alphaomega"[..].using_encoded(|key| sp_io::offchain_index::set(key, DATA)); + }); + + ext.persist_offchain_overlay(); + + ext.execute_with(|| { + let data = + b"alphaomega"[..].using_encoded(|key| { + sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, key) + }); + assert_eq!(data, Some(DATA.to_vec())); + }); + } + + + #[test] + fn historical_proof_offchain() { + let mut ext = new_test_ext(); + let encoded_key_1 = UintAuthorityId(1).encode(); + + ext.execute_with(|| { + set_next_validators(vec![1, 2]); + force_new_session(); + + System::set_block_number(1); + Session::on_initialize(1); + + // "on-chain" + onchain::store_current_session_validator_set_to_offchain::(); + assert_eq!(>::current_index(), 1); + + set_next_validators(vec![7, 8]); + + force_new_session(); + }); + + ext.persist_offchain_overlay(); + + ext.execute_with(|| { + + + System::set_block_number(2); + Session::on_initialize(2); + assert_eq!(>::current_index(), 2); + + // "off-chain" + let proof = prove_session_membership::(1, (DUMMY, &encoded_key_1)); + assert!(proof.is_some()); + let proof = proof.expect("Must be Some(Proof)"); + + assert!(Historical::check_proof((DUMMY, &encoded_key_1[..]), proof.clone()).is_some()); + }); + } +} diff --git a/frame/session/src/historical/onchain.rs b/frame/session/src/historical/onchain.rs new file mode 100644 index 0000000000000..745603a49829b --- /dev/null +++ b/frame/session/src/historical/onchain.rs @@ -0,0 +1,62 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! On-chain logic to store a validator-set for deferred validation using an off-chain worker. + +use codec::Encode; +use sp_runtime::traits::Convert; + +use super::super::Trait as SessionTrait; +use super::super::{Module as SessionModule, SessionIndex}; +use super::Trait as HistoricalTrait; + +use super::shared; +use sp_std::prelude::*; + +/// Store the validator-set associated to the `session_index` to the off-chain database. +/// +/// Further processing is then done [`off-chain side`](super::offchain). +/// +/// **Must** be called from on-chain, i.e. a call that originates from +/// `on_initialize(..)` or `on_finalization(..)`. +/// **Must** be called during the session, which validator-set is to be stored for further +/// off-chain processing. Otherwise the `FullIdentification` might not be available. +pub fn store_session_validator_set_to_offchain( + session_index: SessionIndex, +) { + let encoded_validator_list = >::validators() + .into_iter() + .filter_map(|validator_id: ::ValidatorId| { + let full_identification = + <::FullIdentificationOf>::convert(validator_id.clone()); + full_identification.map(|full_identification| (validator_id, full_identification)) + }) + .collect::>(); + + encoded_validator_list.using_encoded(|encoded_validator_list| { + let derived_key = shared::derive_key(shared::PREFIX, session_index); + sp_io::offchain_index::set(derived_key.as_slice(), encoded_validator_list); + }); +} + +/// Store the validator set associated to the _current_ session index to the off-chain database. +/// +/// See [`fn store_session_validator_set_...(..)`](Self::store_session_validator_set_to_offchain) +/// for further information and restrictions. +pub fn store_current_session_validator_set_to_offchain() { + store_session_validator_set_to_offchain::(>::current_index()); +} diff --git a/frame/session/src/historical/shared.rs b/frame/session/src/historical/shared.rs new file mode 100644 index 0000000000000..fda0361b05959 --- /dev/null +++ b/frame/session/src/historical/shared.rs @@ -0,0 +1,39 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Shared logic between on-chain and off-chain components used for slashing using an off-chain +//! worker. + + +use super::SessionIndex; +use sp_std::prelude::*; +use codec::Encode; + +pub(super) const PREFIX: &[u8] = b"session_historical"; +pub(super) const LAST_PRUNE: &[u8] = b"session_historical_last_prune"; + +/// Derive the key used to store the list of validators +pub(super) fn derive_key>(prefix: P, session_index: SessionIndex) -> Vec { + let prefix: &[u8] = prefix.as_ref(); + session_index.using_encoded(|encoded_session_index| { + prefix.into_iter() + .chain(b"/".into_iter()) + .chain(encoded_session_index.into_iter()) + .copied() + .collect::>() + }) +} \ No newline at end of file diff --git a/frame/session/src/mock.rs b/frame/session/src/mock.rs index a7e43d6a11948..974ec40605d27 100644 --- a/frame/session/src/mock.rs +++ b/frame/session/src/mock.rs @@ -173,6 +173,7 @@ parameter_types! { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -194,7 +195,8 @@ impl frame_system::Trait for Test { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/society/Cargo.toml b/frame/society/Cargo.toml index c9e4f9cb4040e..eb28046d3fefc 100644 --- a/frame/society/Cargo.toml +++ b/frame/society/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } diff --git a/frame/society/src/mock.rs b/frame/society/src/mock.rs index 042c4d3794643..cd1c97547cf7b 100644 --- a/frame/society/src/mock.rs +++ b/frame/society/src/mock.rs @@ -65,6 +65,7 @@ ord_parameter_types! { } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -85,7 +86,8 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); type AccountData = pallet_balances::AccountData; } diff --git a/frame/society/src/tests.rs b/frame/society/src/tests.rs index 8f18ecba469b6..0374c7bcd7a60 100644 --- a/frame/society/src/tests.rs +++ b/frame/society/src/tests.rs @@ -813,7 +813,7 @@ fn max_limits_work() { // No candidates because full assert_eq!(Society::candidates().len(), 0); // Increase member limit - assert_ok!(Society::set_max_members(Origin::ROOT, 200)); + assert_ok!(Society::set_max_members(Origin::root(), 200)); // Rotate period run_to_block(16); // Candidates are back! diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 829f39b70b72a..45b2b42d9766b 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] static_assertions = "1.1.0" serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-npos-elections = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/npos-elections" } sp-io ={ version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } diff --git a/frame/staking/fuzzer/Cargo.toml b/frame/staking/fuzzer/Cargo.toml index 6362ebf414c82..97d79ecad51e3 100644 --- a/frame/staking/fuzzer/Cargo.toml +++ b/frame/staking/fuzzer/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] honggfuzz = "0.5" -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } pallet-staking = { version = "2.0.0-rc3", path = "..", features = ["runtime-benchmarks"] } pallet-staking-reward-curve = { version = "2.0.0-rc3", path = "../reward-curve" } pallet-session = { version = "2.0.0-rc3", path = "../../session" } diff --git a/frame/staking/fuzzer/src/mock.rs b/frame/staking/fuzzer/src/mock.rs index aa9bce0aa86b7..aa03e77097619 100644 --- a/frame/staking/fuzzer/src/mock.rs +++ b/frame/staking/fuzzer/src/mock.rs @@ -57,6 +57,7 @@ impl Convert for CurrencyToVoteHandler { pub struct Test; impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type DbWeight = (); type BlockExecutionWeight = (); diff --git a/frame/staking/fuzzer/src/submit_solution.rs b/frame/staking/fuzzer/src/submit_solution.rs index fafd686c9d802..7094c7ed888b2 100644 --- a/frame/staking/fuzzer/src/submit_solution.rs +++ b/frame/staking/fuzzer/src/submit_solution.rs @@ -23,9 +23,9 @@ use honggfuzz::fuzz; use mock::Test; use pallet_staking::testing_utils::*; -use frame_support::{assert_ok, storage::StorageValue}; +use frame_support::{assert_ok, storage::StorageValue, traits::UnfilteredDispatchable}; use frame_system::RawOrigin; -use sp_runtime::{traits::Dispatchable, DispatchError}; +use sp_runtime::DispatchError; use sp_core::offchain::{testing::TestOffchainExt, OffchainExt}; use pallet_staking::{EraElectionStatus, ElectionStatus, Module as Staking, Call as StakingCall}; @@ -159,7 +159,7 @@ fn main() { match mode { Mode::WeakerSubmission => { assert_eq!( - call.dispatch(origin.clone().into()).unwrap_err().error, + call.dispatch_bypass_filter(origin.clone().into()).unwrap_err().error, DispatchError::Module { index: 0, error: 16, @@ -170,7 +170,7 @@ fn main() { // NOTE: so exhaustive pattern doesn't work here.. maybe some rust issue? // or due to `#[repr(u32)]`? Mode::InitialSubmission | Mode::StrongerSubmission => { - assert_ok!(call.dispatch(origin.into())); + assert_ok!(call.dispatch_bypass_filter(origin.into())); } }; }) diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index 44fc90240380c..1dfa621033362 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; use crate::Module as Staking; use testing_utils::*; -use sp_runtime::{traits::{Dispatchable, One}}; +use sp_runtime::traits::One; use frame_system::RawOrigin; pub use frame_benchmarking::{benchmarks, account}; const SEED: u32 = 0; @@ -379,12 +379,12 @@ benchmarks! { let current_era = CurrentEra::get().unwrap(); let mut points_total = 0; let mut points_individual = Vec::new(); - let mut payout_calls = Vec::new(); + let mut payout_calls_arg = Vec::new(); for validator in new_validators.iter() { points_total += 10; points_individual.push((validator.clone(), 10)); - payout_calls.push(Call::::payout_stakers(validator.clone(), current_era)) + payout_calls_arg.push((validator.clone(), current_era)); } // Give Era Points @@ -401,8 +401,8 @@ benchmarks! { let caller: T::AccountId = account("caller", 0, SEED); }: { - for call in payout_calls { - call.dispatch(RawOrigin::Signed(caller.clone()).into())?; + for arg in payout_calls_arg { + >::payout_stakers(RawOrigin::Signed(caller.clone()).into(), arg.0, arg.1)?; } } diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index c8724205bcc0f..759e3b99dfa80 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -303,7 +303,7 @@ use frame_support::{ }; use pallet_session::historical; use sp_runtime::{ - Perbill, PerU16, PerThing, RuntimeDebug, DispatchError, + Percent, Perbill, PerU16, PerThing, RuntimeDebug, DispatchError, curve::PiecewiseLinear, traits::{ Convert, Zero, StaticLookup, CheckedSub, Saturating, SaturatedConversion, AtLeast32Bit, @@ -865,9 +865,10 @@ pub trait Trait: frame_system::Trait + SendTransactionTypes> { /// Number of eras that staked funds must remain bonded for. type BondingDuration: Get; - /// Number of eras that slashes are deferred by, after computation. This should be less than the - /// bonding duration. Set to 0 if slashes should be applied immediately, without opportunity for - /// intervention. + /// Number of eras that slashes are deferred by, after computation. + /// + /// This should be less than the bonding duration. Set to 0 if slashes + /// should be applied immediately, without opportunity for intervention. type SlashDeferDuration: Get; /// The origin which can cancel a deferred slash. Root can always do this. @@ -884,6 +885,7 @@ pub trait Trait: frame_system::Trait + SendTransactionTypes> { type NextNewSession: EstimateNextNewSession; /// The number of blocks before the end of the era from which election submissions are allowed. + /// /// Setting this to zero will disable the offchain compute and only on-chain seq-phragmen will /// be used. /// @@ -894,14 +896,15 @@ pub trait Trait: frame_system::Trait + SendTransactionTypes> { /// The overarching call type. type Call: Dispatchable + From> + IsSubType, Self> + Clone; - /// Maximum number of balancing iterations to run in the offchain submission. If set to 0, - /// balance_solution will not be executed at all. + /// Maximum number of balancing iterations to run in the offchain submission. + /// + /// If set to 0, balance_solution will not be executed at all. type MaxIterations: Get; /// The threshold of improvement that should be provided for a new solution to be accepted. type MinSolutionScoreBump: Get; - /// The maximum number of nominator rewarded for each validator. + /// The maximum number of nominators rewarded for each validator. /// /// For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can claim /// their reward. This used to limit the i/o cost for the nominator payout. @@ -1275,6 +1278,36 @@ decl_module! { /// Number of eras that staked funds must remain bonded for. const BondingDuration: EraIndex = T::BondingDuration::get(); + /// Number of eras that slashes are deferred by, after computation. + /// + /// This should be less than the bonding duration. + /// Set to 0 if slashes should be applied immediately, without opportunity for + /// intervention. + const SlashDeferDuration: EraIndex = T::SlashDeferDuration::get(); + + /// The number of blocks before the end of the era from which election submissions are allowed. + /// + /// Setting this to zero will disable the offchain compute and only on-chain seq-phragmen will + /// be used. + /// + /// This is bounded by being within the last session. Hence, setting it to a value more than the + /// length of a session will be pointless. + const ElectionLookahead: T::BlockNumber = T::ElectionLookahead::get(); + + /// Maximum number of balancing iterations to run in the offchain submission. + /// + /// If set to 0, balance_solution will not be executed at all. + const MaxIterations: u32 = T::MaxIterations::get(); + + /// The threshold of improvement that should be provided for a new solution to be accepted. + const MinSolutionScoreBump: Perbill = T::MinSolutionScoreBump::get(); + + /// The maximum number of nominators rewarded for each validator. + /// + /// For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can claim + /// their reward. This used to limit the i/o cost for the nominator payout. + const MaxNominatorRewardedPerValidator: u32 = T::MaxNominatorRewardedPerValidator::get(); + type Error = Error; fn deposit_event() = default; @@ -1778,6 +1811,34 @@ decl_module! { ValidatorCount::put(new); } + /// Increments the ideal number of validators. + /// + /// The dispatch origin must be Root. + /// + /// # + /// Base Weight: 1.717 µs + /// Read/Write: Validator Count + /// # + #[weight = 2 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(1, 1)] + fn increase_validator_count(origin, #[compact] additional: u32) { + ensure_root(origin)?; + ValidatorCount::mutate(|n| *n += additional); + } + + /// Scale up the ideal number of validators by a factor. + /// + /// The dispatch origin must be Root. + /// + /// # + /// Base Weight: 1.717 µs + /// Read/Write: Validator Count + /// # + #[weight = 2 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(1, 1)] + fn scale_validator_count(origin, factor: Percent) { + ensure_root(origin)?; + ValidatorCount::mutate(|n| *n += factor * *n); + } + /// Force there to be no new eras indefinitely. /// /// The dispatch origin must be Root. @@ -1890,9 +1951,7 @@ decl_module! { .saturating_add((35 * WEIGHT_PER_MICROS).saturating_mul(slash_indices.len() as Weight)) ] fn cancel_deferred_slash(origin, era: EraIndex, slash_indices: Vec) { - T::SlashCancelOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::SlashCancelOrigin::ensure_origin(origin)?; ensure!(!slash_indices.is_empty(), Error::::EmptyTargets); ensure!(is_sorted_and_unique(&slash_indices), Error::::NotSortedAndUnique); diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index f51b2f9841b4c..048e187bb6ade 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -200,6 +200,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = AccountIndex; type BlockNumber = BlockNumber; diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 8a7ae011c9134..eeac2c5c90e38 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -47,7 +47,7 @@ fn force_unstake_works() { // Force unstake needs correct number of slashing spans (for weight calculation) assert_noop!(Staking::force_unstake(Origin::signed(11), 11, 0), BadOrigin); // We now force them to unstake - assert_ok!(Staking::force_unstake(Origin::ROOT, 11, 2)); + assert_ok!(Staking::force_unstake(Origin::root(), 11, 2)); // No longer bonded. assert_eq!(Staking::bonded(&11), None); // Transfer works. @@ -337,7 +337,7 @@ fn staking_should_work() { claimed_rewards: vec![0], }) ); - // e.g. it cannot spend more than 500 that it has free from the total 2000 + // e.g. it cannot reserve more than 500 that it has free from the total 2000 assert_noop!( Balances::reserve(&3, 501), BalancesError::::LiquidityRestrictions @@ -783,10 +783,10 @@ fn cannot_reserve_staked_balance() { assert_eq!(Balances::free_balance(11), 1000); // Confirm account 11 (via controller 10) is totally staked assert_eq!(Staking::eras_stakers(Staking::active_era().unwrap().index, 11).own, 1000); - // Confirm account 11 cannot transfer as a result + // Confirm account 11 cannot reserve as a result assert_noop!( Balances::reserve(&11, 1), - BalancesError::::LiquidityRestrictions + BalancesError::::LiquidityRestrictions, ); // Give account 11 extra free balance @@ -1477,7 +1477,7 @@ fn on_free_balance_zero_stash_removes_validator() { assert_eq!(Balances::total_balance(&11), 0); // Reap the stash - assert_ok!(Staking::reap_stash(Origin::NONE, 11, 0)); + assert_ok!(Staking::reap_stash(Origin::none(), 11, 0)); // Check storage items do not exist assert!(!>::contains_key(&10)); @@ -1533,7 +1533,7 @@ fn on_free_balance_zero_stash_removes_nominator() { assert_eq!(Balances::total_balance(&11), 0); // Reap the stash - assert_ok!(Staking::reap_stash(Origin::NONE, 11, 0)); + assert_ok!(Staking::reap_stash(Origin::none(), 11, 0)); // Check storage items do not exist assert!(!>::contains_key(&10)); @@ -1928,7 +1928,7 @@ fn offence_forces_new_era() { #[test] fn offence_ensures_new_era_without_clobbering() { ExtBuilder::default().build_and_execute(|| { - assert_ok!(Staking::force_new_era_always(Origin::ROOT)); + assert_ok!(Staking::force_new_era_always(Origin::root())); assert_eq!(Staking::force_era(), Forcing::ForceAlways); on_offence_now( @@ -2302,8 +2302,8 @@ fn garbage_collection_after_slashing() { assert_eq!(slashing_spans.iter().count(), 2); // reap_stash respects num_slashing_spans so that weight is accurate - assert_noop!(Staking::reap_stash(Origin::NONE, 11, 0), Error::::IncorrectSlashingSpans); - assert_ok!(Staking::reap_stash(Origin::NONE, 11, 2)); + assert_noop!(Staking::reap_stash(Origin::none(), 11, 0), Error::::IncorrectSlashingSpans); + assert_ok!(Staking::reap_stash(Origin::none(), 11, 2)); assert!(::SlashingSpans::get(&11).is_none()); assert_eq!(::SpanSlash::get(&(11, 0)).amount_slashed(), &0); @@ -2591,11 +2591,11 @@ fn remove_deferred() { // fails if empty assert_noop!( - Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![]), + Staking::cancel_deferred_slash(Origin::root(), 1, vec![]), Error::::EmptyTargets ); - assert_ok!(Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![0])); + assert_ok!(Staking::cancel_deferred_slash(Origin::root(), 1, vec![0])); assert_eq!(Balances::free_balance(11), 1000); assert_eq!(Balances::free_balance(101), 2000); @@ -2692,21 +2692,21 @@ fn remove_multi_deferred() { // fails if list is not sorted assert_noop!( - Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![2, 0, 4]), + Staking::cancel_deferred_slash(Origin::root(), 1, vec![2, 0, 4]), Error::::NotSortedAndUnique ); // fails if list is not unique assert_noop!( - Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![0, 2, 2]), + Staking::cancel_deferred_slash(Origin::root(), 1, vec![0, 2, 2]), Error::::NotSortedAndUnique ); // fails if bad index assert_noop!( - Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![1, 2, 3, 4, 5]), + Staking::cancel_deferred_slash(Origin::root(), 1, vec![1, 2, 3, 4, 5]), Error::::InvalidSlashIndex ); - assert_ok!(Staking::cancel_deferred_slash(Origin::ROOT, 1, vec![0, 2, 4])); + assert_ok!(Staking::cancel_deferred_slash(Origin::root(), 1, vec![0, 2, 4])); let slashes = ::UnappliedSlashes::get(&1); assert_eq!(slashes.len(), 2); @@ -4243,16 +4243,16 @@ fn test_max_nominator_rewarded_per_validator_and_cant_steal_someone_else_reward( fn set_history_depth_works() { ExtBuilder::default().build_and_execute(|| { mock::start_era(10); - Staking::set_history_depth(Origin::ROOT, 20, 0).unwrap(); + Staking::set_history_depth(Origin::root(), 20, 0).unwrap(); assert!(::ErasTotalStake::contains_key(10 - 4)); assert!(::ErasTotalStake::contains_key(10 - 5)); - Staking::set_history_depth(Origin::ROOT, 4, 0).unwrap(); + Staking::set_history_depth(Origin::root(), 4, 0).unwrap(); assert!(::ErasTotalStake::contains_key(10 - 4)); assert!(!::ErasTotalStake::contains_key(10 - 5)); - Staking::set_history_depth(Origin::ROOT, 3, 0).unwrap(); + Staking::set_history_depth(Origin::root(), 3, 0).unwrap(); assert!(!::ErasTotalStake::contains_key(10 - 4)); assert!(!::ErasTotalStake::contains_key(10 - 5)); - Staking::set_history_depth(Origin::ROOT, 8, 0).unwrap(); + Staking::set_history_depth(Origin::root(), 8, 0).unwrap(); assert!(!::ErasTotalStake::contains_key(10 - 4)); assert!(!::ErasTotalStake::contains_key(10 - 5)); }); diff --git a/frame/sudo/Cargo.toml b/frame/sudo/Cargo.toml index 5aef45f8c2488..1bdd2aab69043 100644 --- a/frame/sudo/Cargo.toml +++ b/frame/sudo/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/sudo/src/lib.rs b/frame/sudo/src/lib.rs index 3d5d1b2582127..233e75e869006 100644 --- a/frame/sudo/src/lib.rs +++ b/frame/sudo/src/lib.rs @@ -88,12 +88,12 @@ #![cfg_attr(not(feature = "std"), no_std)] use sp_std::prelude::*; -use sp_runtime::{DispatchResult, traits::{StaticLookup, Dispatchable}}; +use sp_runtime::{DispatchResult, traits::StaticLookup}; use frame_support::{ Parameter, decl_module, decl_event, decl_storage, decl_error, ensure, }; -use frame_support::weights::{Weight, GetDispatchInfo, FunctionOf, Pays}; +use frame_support::{weights::{Weight, GetDispatchInfo}, traits::UnfilteredDispatchable}; use frame_system::{self as system, ensure_signed}; #[cfg(test)] @@ -106,7 +106,7 @@ pub trait Trait: frame_system::Trait { type Event: From> + Into<::Event>; /// A sudo-able call. - type Call: Parameter + Dispatchable + GetDispatchInfo; + type Call: Parameter + UnfilteredDispatchable + GetDispatchInfo; } decl_module! { @@ -126,17 +126,13 @@ decl_module! { /// - One DB write (event). /// - Weight of derivative `call` execution + 10,000. /// # - #[weight = FunctionOf( - |args: (&Box<::Call>,)| args.0.get_dispatch_info().weight + 10_000, - |args: (&Box<::Call>,)| args.0.get_dispatch_info().class, - Pays::Yes, - )] + #[weight = (call.get_dispatch_info().weight + 10_000, call.get_dispatch_info().class)] fn sudo(origin, call: Box<::Call>) { // This is a public call, so we ensure that the origin is some signed account. let sender = ensure_signed(origin)?; ensure!(sender == Self::key(), Error::::RequireSudo); - let res = call.dispatch(frame_system::RawOrigin::Root.into()); + let res = call.dispatch_bypass_filter(frame_system::RawOrigin::Root.into()); Self::deposit_event(RawEvent::Sudid(res.map(|_| ()).map_err(|e| e.error))); } @@ -150,17 +146,13 @@ decl_module! { /// - O(1). /// - The weight of this call is defined by the caller. /// # - #[weight = FunctionOf( - |(_, &weight): (&Box<::Call>,&Weight,)| weight, - |(call, _): (&Box<::Call>,&Weight,)| call.get_dispatch_info().class, - Pays::Yes, - )] + #[weight = (*_weight, call.get_dispatch_info().class)] fn sudo_unchecked_weight(origin, call: Box<::Call>, _weight: Weight) { // This is a public call, so we ensure that the origin is some signed account. let sender = ensure_signed(origin)?; ensure!(sender == Self::key(), Error::::RequireSudo); - let res = call.dispatch(frame_system::RawOrigin::Root.into()); + let res = call.dispatch_bypass_filter(frame_system::RawOrigin::Root.into()); Self::deposit_event(RawEvent::Sudid(res.map(|_| ()).map_err(|e| e.error))); } @@ -195,15 +187,7 @@ decl_module! { /// - One DB write (event). /// - Weight of derivative `call` execution + 10,000. /// # - #[weight = FunctionOf( - |args: (&::Source, &Box<::Call>,)| { - args.1.get_dispatch_info().weight + 10_000 - }, - |args: (&::Source, &Box<::Call>,)| { - args.1.get_dispatch_info().class - }, - Pays::Yes, - )] + #[weight = (call.get_dispatch_info().weight + 10_000, call.get_dispatch_info().class)] fn sudo_as(origin, who: ::Source, call: Box<::Call>) { // This is a public call, so we ensure that the origin is some signed account. let sender = ensure_signed(origin)?; @@ -211,7 +195,7 @@ decl_module! { let who = T::Lookup::lookup(who)?; - let res = match call.dispatch(frame_system::RawOrigin::Signed(who).into()) { + let res = match call.dispatch_bypass_filter(frame_system::RawOrigin::Signed(who).into()) { Ok(_) => true, Err(e) => { sp_runtime::print(e); diff --git a/frame/sudo/src/mock.rs b/frame/sudo/src/mock.rs index b4df4b0bea490..ab01e73e71110 100644 --- a/frame/sudo/src/mock.rs +++ b/frame/sudo/src/mock.rs @@ -20,14 +20,15 @@ use super::*; use frame_support::{ impl_outer_origin, impl_outer_dispatch, impl_outer_event, parameter_types, - weights::{Weight, DispatchClass} + weights::Weight, }; use sp_core::H256; // The testing primitives are very useful for avoiding having to work with signatures -// or public keys. +// or public keys. use sp_runtime::{Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header}; use sp_io; use crate as sudo; +use frame_support::traits::Filter; // Logger module to track execution. pub mod logger { @@ -56,25 +57,17 @@ pub mod logger { pub struct Module for enum Call where origin: ::Origin { fn deposit_event() = default; - #[weight = FunctionOf( - |args: (&i32, &Weight)| *args.1, - DispatchClass::Normal, - Pays::Yes, - )] + #[weight = *weight] fn privileged_i32_log(origin, i: i32, weight: Weight){ - // Ensure that the `origin` is `Root`. + // Ensure that the `origin` is `Root`. ensure_root(origin)?; ::append(i); Self::deposit_event(RawEvent::AppendI32(i, weight)); } - #[weight = FunctionOf( - |args: (&i32, &Weight)| *args.1, - DispatchClass::Normal, - Pays::Yes, - )] + #[weight = *weight] fn non_privileged_log(origin, i: i32, weight: Weight){ - // Ensure that the `origin` is some signed account. + // Ensure that the `origin` is some signed account. let sender = ensure_signed(origin)?; ::append(i); >::append(sender.clone()); @@ -120,7 +113,15 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } +pub struct BlockEverything; +impl Filter for BlockEverything { + fn filter(_: &Call) -> bool { + false + } +} + impl frame_system::Trait for Test { + type BaseCallFilter = BlockEverything; type Origin = Origin; type Call = Call; type Index = u64; @@ -128,7 +129,7 @@ impl frame_system::Trait for Test { type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; - type Lookup = IdentityLookup; + type Lookup = IdentityLookup; type Header = Header; type Event = TestEvent; type BlockHashCount = BlockHashCount; diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index dd9354d019629..e648eaf32db06 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] log = "0.4" serde = { version = "1.0.101", optional = true, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } frame-metadata = { version = "11.0.0-rc3", default-features = false, path = "../metadata" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } diff --git a/frame/support/procedural/src/construct_runtime/mod.rs b/frame/support/procedural/src/construct_runtime/mod.rs index d7529cd272d0e..cac75490621aa 100644 --- a/frame/support/procedural/src/construct_runtime/mod.rs +++ b/frame/support/procedural/src/construct_runtime/mod.rs @@ -89,6 +89,7 @@ fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result Result( .find(|decl| decl.name == SYSTEM_MODULE_NAME) .map(|decl| &decl.module) } + +fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 { + quote!( + #[cfg(test)] + mod __construct_runtime_integrity_test { + use super::*; + + #[test] + pub fn runtime_integrity_tests() { + ::integrity_test(); + } + } + ) +} diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index dd4b6d0b862f9..d9a3561802c8f 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -29,7 +29,7 @@ pub use crate::weights::{ PaysFee, PostDispatchInfo, WithPostDispatchInfo, }; pub use sp_runtime::{traits::Dispatchable, DispatchError}; -pub use crate::traits::{CallMetadata, GetCallMetadata, GetCallName}; +pub use crate::traits::{CallMetadata, GetCallMetadata, GetCallName, UnfilteredDispatchable}; /// The return typ of a `Dispatchable` in frame. When returned explicitly from /// a dispatchable function it allows overriding the default `PostDispatchInfo` @@ -47,10 +47,9 @@ pub type DispatchResult = Result<(), sp_runtime::DispatchError>; pub type DispatchErrorWithPostInfo = sp_runtime::DispatchErrorWithPostInfo; -/// Serializable version of Dispatchable. -/// This value can be used as a "function" in an extrinsic. +/// Serializable version of pallet dispatchable. pub trait Callable { - type Call: Dispatchable + Codec + Clone + PartialEq + Eq; + type Call: UnfilteredDispatchable + Codec + Clone + PartialEq + Eq; } // dirty hack to work around serde_derive issue @@ -270,8 +269,11 @@ impl Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {} /// * `fn on_finalize() -> frame_support::weights::Weight` /// /// * `offchain_worker`: Executes at the beginning of a block and produces extrinsics for a future block -/// upon completion. Using this function will implement the -/// [`OffchainWorker`](./traits/trait.OffchainWorker.html) trait. +/// upon completion. Using this function will implement the +/// [`OffchainWorker`](./traits/trait.OffchainWorker.html) trait. +/// * `integrity_test`: Executes in a test generated by `construct_runtime`, note it doesn't +/// execute in an externalities-provided environment. Implement +/// [`IntegrityTest`](./trait.IntegrityTest.html) trait. #[macro_export] macro_rules! decl_module { // Entry point #1. @@ -281,7 +283,7 @@ macro_rules! decl_module { $trait_instance:ident: $trait_name:ident $( , I: $instantiable:path $( = $module_default_instance:path )? )? > - for enum $call_type:ident where origin: $origin_type:ty $(, $where_ty:ty: $where_bound:path )* { + for enum $call_type:ident where origin: $origin_type:ty $(, $where_ty:ty: $where_bound:path )* $(,)? { $( $t:tt )* } ) => { @@ -299,6 +301,7 @@ macro_rules! decl_module { {} {} {} + {} [] $($t)* ); @@ -314,6 +317,7 @@ macro_rules! decl_module { origin: $origin_type:ty, system = $system:ident $(, $where_ty:ty: $where_bound:path )* + $(,)? { $($t:tt)* } @@ -332,6 +336,7 @@ macro_rules! decl_module { {} {} {} + {} [] $($t)* ); @@ -350,6 +355,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $vis:vis fn deposit_event() = default; @@ -367,6 +373,7 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $( $error_type )* } + { $( $integrity_test)* } [ $( $dispatchables )* ] $($rest)* ); @@ -383,6 +390,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $vis:vis fn deposit_event @@ -392,6 +400,29 @@ macro_rules! decl_module { "`deposit_event` function is reserved and must follow the syntax: `$vis:vis fn deposit_event() = default;`" ); }; + // Compile error on `deposit_event` being added a second time. + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident< + $trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)? + > + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )+ } + { $( $on_initialize:tt )* } + { $( $on_runtime_upgrade:tt )* } + { $( $on_finalize:tt )* } + { $( $offchain:tt )* } + { $( $constants:tt )* } + { $( $error_type:tt )* } + { $( $integrity_test:tt )* } + [ $( $dispatchables:tt )* ] + $(#[doc = $doc_attr:tt])* + $vis:vis fn deposit_event() = default; + $($rest:tt)* + ) => { + compile_error!("`deposit_event` can only be passed once as input."); + }; // Add on_finalize (@normalize $(#[$attr:meta])* @@ -405,6 +436,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_finalize( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* } @@ -424,6 +456,7 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $( $error_type )* } + { $( $integrity_test)* } [ $( $dispatchables )* ] $($rest)* ); @@ -441,6 +474,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* #[weight = $weight:expr] @@ -452,6 +486,30 @@ macro_rules! decl_module { `on_initialize` or `on_runtime_upgrade` instead" ); }; + // Compile error on `on_finalize` being added a second time. + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident< + $trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)? + > + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )* } + { $( $on_initialize:tt )* } + { $( $on_runtime_upgrade:tt )* } + { $( $on_finalize:tt )+ } + { $( $offchain:tt )* } + { $( $constants:tt )* } + { $( $error_type:tt )* } + { $( $integrity_test:tt )* } + [ $( $dispatchables:tt )* ] + $(#[doc = $doc_attr:tt])* + #[weight = $weight:expr] + fn on_finalize( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* } + $($rest:tt)* + ) => { + compile_error!("`on_finalize` can only be passed once as input."); + }; // compile_error on_runtime_upgrade, without a given weight removed syntax. (@normalize $(#[$attr:meta])* @@ -467,6 +525,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_runtime_upgrade( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* } @@ -491,6 +550,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* #[weight = $weight:expr] @@ -517,6 +577,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_runtime_upgrade( $( $param_name:ident : $param:ty ),* $(,)? ) -> $return:ty { $( $impl:tt )* } @@ -536,10 +597,98 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $( $error_type )* } + { $( $integrity_test)* } [ $( $dispatchables )* ] $($rest)* ); }; + // Compile error on `on_runtime_upgrade` being added a second time. + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident< + $trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)? + > + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )* } + { $( $on_initialize:tt )* } + { $( $on_runtime_upgrade:tt )+ } + { $( $on_finalize:tt )* } + { $( $offchain:tt )* } + { $( $constants:tt )* } + { $( $error_type:tt )* } + { $( $integrity_test:tt )* } + [ $( $dispatchables:tt )* ] + $(#[doc = $doc_attr:tt])* + fn on_runtime_upgrade( $( $param_name:ident : $param:ty ),* $(,)? ) -> $return:ty { $( $impl:tt )* } + $($rest:tt)* + ) => { + compile_error!("`on_runtime_upgrade` can only be passed once as input."); + }; + // Add integrity_test + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident< + $trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)? + > + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )* } + { $( $on_initialize:tt )* } + { $( $on_runtime_upgrade:tt )* } + { $( $on_finalize:tt )* } + { $( $offchain:tt )* } + { $( $constants:tt )* } + { $( $error_type:tt )* } + {} + [ $( $dispatchables:tt )* ] + $(#[doc = $doc_attr:tt])* + fn integrity_test() { $( $impl:tt )* } + $($rest:tt)* + ) => { + $crate::decl_module!(@normalize + $(#[$attr])* + pub struct $mod_type<$trait_instance: $trait_name$(, I: $instantiable $(= $module_default_instance)?)?> + for enum $call_type where origin: $origin_type, system = $system + { $( $other_where_bounds )* } + { $( $deposit_event )* } + { $( $on_initialize )* } + { $( $on_runtime_upgrade )* } + { $( $on_finalize )* } + { $( $offchain )* } + { $( $constants )* } + { $( $error_type )* } + { + $(#[doc = $doc_attr])* + fn integrity_test() { $( $impl)* } + } + [ $( $dispatchables )* ] + $($rest)* + ); + }; + // Compile error on `integrity_test` being added a second time. + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident< + $trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)? + > + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )* } + { $( $on_initialize:tt )* } + { $( $on_runtime_upgrade:tt )* } + { $( $on_finalize:tt )* } + { $( $offchain:tt )* } + { $( $constants:tt )* } + { $( $error_type:tt )* } + { $( $integrity_test:tt )+ } + [ $( $dispatchables:tt )* ] + $(#[doc = $doc_attr:tt])* + fn integrity_test() { $( $impl:tt )* } + $($rest:tt)* + ) => { + compile_error!("`integrity_test` can only be passed once as input."); + }; // compile_error on_initialize, without a given weight removed syntax. (@normalize $(#[$attr:meta])* @@ -555,6 +704,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_initialize( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* } @@ -579,6 +729,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* #[weight = $weight:expr] @@ -605,6 +756,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn on_initialize( $( $param_name:ident : $param:ty ),* $(,)? ) -> $return:ty { $( $impl:tt )* } @@ -624,10 +776,34 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $( $error_type )* } + { $( $integrity_test)* } [ $( $dispatchables )* ] $($rest)* ); }; + // Compile error on trying to add a second `on_initialize`. + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident< + $trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)? + > + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )* } + { $( $on_initialize:tt )+ } + { $( $on_runtime_upgrade:tt )* } + { $( $on_finalize:tt )* } + { $( $offchain:tt )* } + { $( $constants:tt )* } + { $( $error_type:tt )* } + { $( $integrity_test:tt )* } + [ $( $dispatchables:tt )* ] + $(#[doc = $doc_attr:tt])* + fn on_initialize( $( $param_name:ident : $param:ty ),* $(,)? ) -> $return:ty { $( $impl:tt )* } + $($rest:tt)* + ) => { + compile_error!("`on_initialize` can only be passed once as input."); + }; (@normalize $(#[$attr:meta])* pub struct $mod_type:ident< @@ -643,6 +819,7 @@ macro_rules! decl_module { { } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* fn offchain_worker( $( $param_name:ident : $param:ty ),* $(,)? ) { $( $impl:tt )* } @@ -662,11 +839,34 @@ macro_rules! decl_module { { fn offchain_worker( $( $param_name : $param ),* ) { $( $impl )* } } { $( $constants )* } { $( $error_type )* } + { $( $integrity_test)* } [ $( $dispatchables )* ] $($rest)* ); }; - + // Compile error on trying to add a second `offchain_worker`. + (@normalize + $(#[$attr:meta])* + pub struct $mod_type:ident< + $trait_instance:ident: $trait_name:ident$(, I: $instantiable:path $(= $module_default_instance:path)?)? + > + for enum $call_type:ident where origin: $origin_type:ty, system = $system:ident + { $( $other_where_bounds:tt )* } + { $( $deposit_event:tt )* } + { $( $on_initialize:tt )* } + { $( $on_runtime_upgrade:tt )* } + { $( $on_finalize:tt )* } + { $( $offchain:tt )+ } + { $( $constants:tt )* } + { $( $error_type:tt )* } + { $( $integrity_test:tt )* } + [ $( $dispatchables:tt )* ] + $(#[doc = $doc_attr:tt])* + fn offchain_worker( $( $param_name:ident : $param:ty ),* $(,)? ) -> $return:ty { $( $impl:tt )* } + $($rest:tt)* + ) => { + compile_error!("`offchain_worker` can only be passed once as input."); + }; // This puts a constant in the parsed constants list. (@normalize $(#[$attr:meta])* @@ -683,6 +883,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $( #[doc = $doc_attr:tt] )* const $name:ident: $ty:ty = $value:expr; @@ -707,6 +908,7 @@ macro_rules! decl_module { $name: $ty = $value; } { $( $error_type )* } + { $( $integrity_test)* } [ $( $dispatchables )* ] $($rest)* ); @@ -728,6 +930,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* type Error = $error_type:ty; @@ -747,6 +950,7 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $error_type } + { $( $integrity_test)* } [ $( $dispatchables )* ] $($rest)* ); @@ -767,6 +971,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { } + { $( $integrity_test:tt )* } [ $($t:tt)* ] $($rest:tt)* ) => { @@ -784,6 +989,7 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { &'static str } + { $( $integrity_test)* } [ $($t)* ] $($rest)* ); @@ -805,6 +1011,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $error_type:ty } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* #[weight = $weight:expr] @@ -827,6 +1034,7 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $error_type } + { $( $integrity_test)* } [ $( $dispatchables )* $(#[doc = $doc_attr])* @@ -855,6 +1063,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $fn_vis:vis fn $fn_name:ident( @@ -881,6 +1090,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? @@ -907,6 +1117,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? @@ -933,6 +1144,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] $(#[doc = $doc_attr:tt])* $(#[weight = $weight:expr])? @@ -960,6 +1172,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $( $error_type:tt )* } + { $( $integrity_test:tt )* } [ $( $dispatchables:tt )* ] ) => { $crate::decl_module!(@imp @@ -976,6 +1189,7 @@ macro_rules! decl_module { { $( $offchain )* } { $( $constants )* } { $( $error_type )* } + { $( $integrity_test)* } ); }; @@ -1005,6 +1219,7 @@ macro_rules! decl_module { impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> $module<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { + /// Deposits an event using `frame_system::Module::deposit_event`. $vis fn deposit_event( event: impl Into<< $trait_instance as $trait_name $(<$instance>)? >::Event> ) { @@ -1081,6 +1296,32 @@ macro_rules! decl_module { {} }; + (@impl_integrity_test + $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } + $(#[doc = $doc_attr:tt])* + fn integrity_test() { $( $impl:tt )* } + ) => { + impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> + $crate::traits::IntegrityTest + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* + { + $(#[doc = $doc_attr])* + fn integrity_test() { + $( $impl )* + } + } + }; + + (@impl_integrity_test + $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; + { $( $other_where_bounds:tt )* } + ) => { + impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> + $crate::traits::IntegrityTest + for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* + {} + }; (@impl_on_finalize $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; @@ -1340,6 +1581,7 @@ macro_rules! decl_module { { $( $offchain:tt )* } { $( $constants:tt )* } { $error_type:ty } + { $( $integrity_test:tt )* } ) => { $crate::__check_reserved_fn_name! { $( $fn_name )* } @@ -1366,7 +1608,6 @@ macro_rules! decl_module { $( $on_runtime_upgrade )* } - $crate::decl_module! { @impl_on_finalize $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?>; @@ -1388,6 +1629,13 @@ macro_rules! decl_module { $( $deposit_event )* } + $crate::decl_module! { + @impl_integrity_test + $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?>; + { $( $other_where_bounds )* } + $( $integrity_test )* + } + /// Can also be called using [`Call`]. /// /// [`Call`]: enum.Call.html @@ -1402,6 +1650,8 @@ macro_rules! decl_module { $error_type; $from; $(#[doc = $doc_attr])* + /// + /// NOTE: Calling this function will bypass origin filters. $fn_vis fn $fn_name ( $from $(, $param_name : $param )* ) $( -> $result )* { $( $impl )* } @@ -1546,14 +1796,11 @@ macro_rules! decl_module { } } - impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::dispatch::Dispatchable + impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $crate::traits::UnfilteredDispatchable for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { - type Trait = $trait_instance; type Origin = $origin_type; - type Info = $crate::weights::DispatchInfo; - type PostInfo = $crate::weights::PostDispatchInfo; - fn dispatch(self, _origin: Self::Origin) -> $crate::dispatch::DispatchResultWithPostInfo { + fn dispatch_bypass_filter(self, _origin: Self::Origin) -> $crate::dispatch::DispatchResultWithPostInfo { match self { $( $call_type::$fn_name( $( $param_name ),* ) => { @@ -1574,17 +1821,6 @@ macro_rules! decl_module { type Call = $call_type<$trait_instance $(, $instance)?>; } - impl<$trait_instance: $trait_name $(, $instance: $instantiable)?> $mod_type<$trait_instance $(, $instance)?> - where $( $other_where_bounds )* - { - #[doc(hidden)] - pub fn dispatch>( - d: D, - origin: D::Origin - ) -> $crate::dispatch::DispatchResultWithPostInfo { - d.dispatch(origin) - } - } $crate::__dispatch_impl_metadata! { $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?> { $( $other_where_bounds )* } @@ -1684,6 +1920,20 @@ macro_rules! impl_outer_dispatch { fn dispatch( self, origin: $origin, + ) -> $crate::dispatch::DispatchResultWithPostInfo { + if !::filter_call(&origin, &self) { + return $crate::sp_std::result::Result::Err($crate::dispatch::DispatchError::BadOrigin.into()) + } + + $crate::traits::UnfilteredDispatchable::dispatch_bypass_filter(self, origin) + } + } + + impl $crate::traits::UnfilteredDispatchable for $call_type { + type Origin = $origin; + fn dispatch_bypass_filter( + self, + origin: $origin, ) -> $crate::dispatch::DispatchResultWithPostInfo { $crate::impl_outer_dispatch! { @DISPATCH_MATCH @@ -1696,6 +1946,7 @@ macro_rules! impl_outer_dispatch { } } } + $( impl $crate::dispatch::IsSubType<$camelcase, $runtime> for $call_type { #[allow(unreachable_patterns)] @@ -1731,7 +1982,8 @@ macro_rules! impl_outer_dispatch { $origin { $( $generated )* - $call_type::$name(call) => call.dispatch($origin), + $call_type::$name(call) => + $crate::traits::UnfilteredDispatchable::dispatch_bypass_filter(call, $origin), } $index + 1; $( $rest ),* @@ -2011,6 +2263,9 @@ macro_rules! __check_reserved_fn_name { (offchain_worker $( $rest:ident )*) => { $crate::__check_reserved_fn_name!(@compile_error offchain_worker); }; + (integrity_test $( $rest:ident )*) => { + $crate::__check_reserved_fn_name!(@compile_error integrity_test); + }; ($t:ident $( $rest:ident )*) => { $crate::__check_reserved_fn_name!($( $rest )*); }; @@ -2046,25 +2301,39 @@ mod tests { use super::*; use crate::weights::{DispatchInfo, DispatchClass, Pays}; use crate::traits::{ - CallMetadata, GetCallMetadata, GetCallName, OnInitialize, OnFinalize, OnRuntimeUpgrade + CallMetadata, GetCallMetadata, GetCallName, OnInitialize, OnFinalize, OnRuntimeUpgrade, + IntegrityTest, }; pub trait Trait: system::Trait + Sized where Self::AccountId: From { - type Origin; type BlockNumber: Into; - type Call: From>; } pub mod system { - use super::*; - pub trait Trait { type AccountId; + type Call; + type BaseCallFilter; + type Origin: crate::traits::OriginTrait; } - pub fn ensure_root(_: R) -> DispatchResult { - Ok(()) + #[derive(Clone, PartialEq, Eq, Debug)] + pub enum RawOrigin { + Root, + Signed(AccountId), + None, } + + impl From> for RawOrigin { + fn from(s: Option) -> RawOrigin { + match s { + Some(who) => RawOrigin::Signed(who), + None => RawOrigin::None, + } + } + } + + pub type Origin = RawOrigin<::AccountId>; } decl_module! { @@ -2095,6 +2364,8 @@ mod tests { fn on_finalize(n: T::BlockNumber,) { if n.into() == 42 { panic!("on_finalize") } } fn on_runtime_upgrade() -> Weight { 10 } fn offchain_worker() {} + /// Some doc + fn integrity_test() { panic!("integrity_test") } } } @@ -2169,21 +2440,26 @@ mod tests { pub struct TraitImpl {} impl Trait for TraitImpl { - type Origin = u32; type BlockNumber = u32; - type Call = OuterCall; } type Test = Module; + impl_outer_origin!{ + pub enum OuterOrigin for TraitImpl where system = system {} + } + impl_outer_dispatch! { - pub enum OuterCall for TraitImpl where origin: u32 { + pub enum OuterCall for TraitImpl where origin: OuterOrigin { self::Test, } } impl system::Trait for TraitImpl { + type Origin = OuterOrigin; type AccountId = u32; + type Call = OuterCall; + type BaseCallFilter = (); } #[test] @@ -2281,4 +2557,10 @@ mod tests { let module_names = OuterCall::get_module_names(); assert_eq!(["Test"], module_names); } + + #[test] + #[should_panic(expected = "integrity_test")] + fn integrity_test_should_work() { + as IntegrityTest>::integrity_test(); + } } diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 471dd72a748df..196bddbdf5bce 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -44,21 +44,21 @@ pub use paste; #[doc(hidden)] pub use sp_state_machine::BasicExternalities; #[doc(hidden)] -pub use sp_io::storage::root as storage_root; +pub use sp_io::{storage::root as storage_root, self}; #[doc(hidden)] pub use sp_runtime::RuntimeDebug; #[macro_use] pub mod debug; #[macro_use] +mod origin; +#[macro_use] pub mod dispatch; pub mod storage; mod hash; #[macro_use] pub mod event; #[macro_use] -mod origin; -#[macro_use] pub mod metadata; #[macro_use] pub mod inherent; @@ -84,8 +84,24 @@ pub use sp_runtime::{self, ConsensusEngineId, print, traits::Printable}; #[derive(Debug)] pub enum Never {} -/// Macro for easily creating a new implementation of the `Get` trait. If `const` token is used, the -/// rhs of the expression must be `const`-only, and get is implemented as `const`: +/// Create new implementations of the [`Get`](crate::traits::Get) trait. +/// +/// The so-called parameter type can be created in three different ways: +/// +/// - Using `const` to create a parameter type that provides a `const` getter. +/// It is required that the `value` is const. +/// +/// - Declare the parameter type without `const` to have more freedom when creating the value. +/// +/// - Using `storage` to create a storage parameter type. This type is special as it tries to +/// load the value from the storage under a fixed key. If the value could not be found in the +/// storage, the given default value will be returned. It is required that the value implements +/// [`Encode`](codec::Encode) and [`Decode`](codec::Decode). The key for looking up the value +/// in the storage is built using the following formular: +/// +/// `twox_128(":" ++ NAME ++ ":")` where `NAME` is the name that is passed as type name. +/// +/// # Examples /// /// ``` /// # use frame_support::traits::Get; @@ -95,23 +111,27 @@ pub enum Never {} /// /// const FIXED_VALUE: u64 = 10; /// parameter_types! { -/// pub const Argument: u64 = 42 + FIXED_VALUE; -/// pub OtherArgument: u64 = non_const_expression(); +/// pub const Argument: u64 = 42 + FIXED_VALUE; +/// /// Visibility of the type is optional +/// OtherArgument: u64 = non_const_expression(); +/// pub storage StorageArgument: u64 = 5; /// } /// /// trait Config { -/// type Parameter: Get; -/// type OtherParameter: Get; +/// type Parameter: Get; +/// type OtherParameter: Get; +/// type StorageParameter: Get; /// } /// /// struct Runtime; /// impl Config for Runtime { -/// type Parameter = Argument; -/// type OtherParameter = OtherArgument; +/// type Parameter = Argument; +/// type OtherParameter = OtherArgument; +/// type StorageParameter = StorageArgument; /// } /// ``` /// -/// Invalid example: +/// # Invalid example: /// /// ```compile_fail /// # use frame_support::traits::Get; @@ -120,7 +140,7 @@ pub enum Never {} /// fn non_const_expression() -> u64 { 99 } /// /// parameter_types! { -/// pub const Argument: u64 = non_const_expression(); +/// pub const Argument: u64 = non_const_expression(); /// } /// ``` @@ -133,8 +153,8 @@ macro_rules! parameter_types { ) => ( $( #[ $attr ] )* $vis struct $name; - $crate::parameter_types!{IMPL_CONST $name , $type , $value} - $crate::parameter_types!{ $( $rest )* } + $crate::parameter_types!(IMPL_CONST $name , $type , $value); + $crate::parameter_types!( $( $rest )* ); ); ( $( #[ $attr:meta ] )* @@ -143,33 +163,79 @@ macro_rules! parameter_types { ) => ( $( #[ $attr ] )* $vis struct $name; - $crate::parameter_types!{IMPL $name , $type , $value} - $crate::parameter_types!{ $( $rest )* } + $crate::parameter_types!(IMPL $name, $type, $value); + $crate::parameter_types!( $( $rest )* ); + ); + ( + $( #[ $attr:meta ] )* + $vis:vis storage $name:ident: $type:ty = $value:expr; + $( $rest:tt )* + ) => ( + $( #[ $attr ] )* + $vis struct $name; + $crate::parameter_types!(IMPL_STORAGE $name, $type, $value); + $crate::parameter_types!( $( $rest )* ); ); () => (); - (IMPL_CONST $name:ident , $type:ty , $value:expr) => { + (IMPL_CONST $name:ident, $type:ty, $value:expr) => { impl $name { + /// Returns the value of this parameter type. pub const fn get() -> $type { $value } } + impl> $crate::traits::Get for $name { fn get() -> I { I::from($value) } } }; - (IMPL $name:ident , $type:ty , $value:expr) => { + (IMPL $name:ident, $type:ty, $value:expr) => { impl $name { + /// Returns the value of this parameter type. pub fn get() -> $type { $value } } + impl> $crate::traits::Get for $name { fn get() -> I { I::from($value) } } + }; + (IMPL_STORAGE $name:ident, $type:ty, $value:expr) => { + impl $name { + /// Returns the key for this parameter type. + pub fn key() -> [u8; 16] { + $crate::sp_io::hashing::twox_128( + concat!(":", stringify!($name), ":").as_bytes() + ) + } + + /// Set the value of this parameter type in the storage. + /// + /// This needs to be executed in an externalities provided + /// environment. + pub fn set(value: &$type) { + $crate::storage::unhashed::put(&Self::key(), value); + } + + /// Returns the value of this parameter type. + /// + /// This needs to be executed in an externalities provided + /// environment. + pub fn get() -> $type { + $crate::storage::unhashed::get(&Self::key()).unwrap_or_else(|| $value) + } + } + + impl> $crate::traits::Get for $name { + fn get() -> I { + I::from(Self::get()) + } + } } } @@ -316,6 +382,7 @@ mod tests { StorageEntryModifier, DefaultByteGetter, StorageHasher, }; use sp_std::marker::PhantomData; + use sp_io::TestExternalities; pub trait Trait { type BlockNumber: Codec + EncodeLike + Default; @@ -361,7 +428,7 @@ mod tests { type Origin = u32; } - fn new_test_ext() -> sp_io::TestExternalities { + fn new_test_ext() -> TestExternalities { GenesisConfig::default().build_storage().unwrap().into() } @@ -696,4 +763,20 @@ mod tests { let metadata = Module::::storage_metadata(); pretty_assertions::assert_eq!(EXPECTED_METADATA, metadata); } + + parameter_types! { + storage StorageParameter: u64 = 10; + } + + #[test] + fn check_storage_parameter_type_works() { + TestExternalities::default().execute_with(|| { + assert_eq!(sp_io::hashing::twox_128(b":StorageParameter:"), StorageParameter::key()); + + assert_eq!(10, StorageParameter::get()); + + StorageParameter::set(&300); + assert_eq!(300, StorageParameter::get()); + }) + } } diff --git a/frame/support/src/metadata.rs b/frame/support/src/metadata.rs index 7248d6bc4df5c..d6ec9a7373952 100644 --- a/frame/support/src/metadata.rs +++ b/frame/support/src/metadata.rs @@ -290,6 +290,7 @@ mod tests { use super::*; pub trait Trait: 'static { + type BaseCallFilter; const ASSOCIATED_CONST: u64 = 500; type Origin: Into, Self::Origin>> + From>; @@ -297,6 +298,7 @@ mod tests { type BlockNumber: From + Encode; type SomeValue: Get; type ModuleToIndex: crate::traits::ModuleToIndex; + type Call; } decl_module! { @@ -436,11 +438,13 @@ mod tests { } impl system::Trait for TestRuntime { + type BaseCallFilter = (); type Origin = Origin; type AccountId = u32; type BlockNumber = u32; type SomeValue = SystemValue; type ModuleToIndex = (); + type Call = Call; } impl_runtime_metadata!( diff --git a/frame/support/src/origin.rs b/frame/support/src/origin.rs index f96ec07af0a9c..77fe86cc5575b 100644 --- a/frame/support/src/origin.rs +++ b/frame/support/src/origin.rs @@ -45,19 +45,23 @@ macro_rules! impl_outer_origin { $( $rest_with_system:tt )* } ) => { - $crate::impl_outer_origin!( - $( #[$attr] )*; - $name; - $runtime; - $system; - Modules { $( $rest_with_system )* }; - ); + $crate::paste::item! { + $crate::impl_outer_origin!( + $( #[$attr] )*; + $name; + [< $name Caller >]; + $runtime; + $system; + Modules { $( $rest_with_system )* }; + ); + } }; // Generic + Instance ( $(#[$attr:meta])*; $name:ident; + $caller_name:ident; $runtime:ident; $system:ident; Modules { @@ -69,6 +73,7 @@ macro_rules! impl_outer_origin { $crate::impl_outer_origin!( $( #[$attr] )*; $name; + $caller_name; $runtime; $system; Modules { $( $( $rest_module )* )? }; @@ -80,6 +85,7 @@ macro_rules! impl_outer_origin { ( $(#[$attr:meta])*; $name:ident; + $caller_name:ident; $runtime:ident; $system:ident; Modules { @@ -91,6 +97,7 @@ macro_rules! impl_outer_origin { $crate::impl_outer_origin!( $( #[$attr] )*; $name; + $caller_name; $runtime; $system; Modules { $( $rest_module )* }; @@ -102,6 +109,7 @@ macro_rules! impl_outer_origin { ( $(#[$attr:meta])*; $name:ident; + $caller_name:ident; $runtime:ident; $system:ident; Modules { @@ -113,6 +121,7 @@ macro_rules! impl_outer_origin { $crate::impl_outer_origin!( $( #[$attr] )*; $name; + $caller_name; $runtime; $system; Modules { $( $( $rest_module )* )? }; @@ -124,6 +133,7 @@ macro_rules! impl_outer_origin { ( $(#[$attr:meta])*; $name:ident; + $caller_name:ident; $runtime:ident; $system:ident; Modules { @@ -135,6 +145,7 @@ macro_rules! impl_outer_origin { $crate::impl_outer_origin!( $( #[$attr] )*; $name; + $caller_name; $runtime; $system; Modules { $( $( $rest_module )* )? }; @@ -146,16 +157,78 @@ macro_rules! impl_outer_origin { ( $(#[$attr:meta])*; $name:ident; + $caller_name:ident; $runtime:ident; $system:ident; Modules { }; $( $module:ident $( < $generic:ident > )? $( { $generic_instance:ident } )? ,)* ) => { + // WARNING: All instance must hold the filter `frame_system::Trait::BaseCallFilter`, except + // when caller is system Root. One can use `OriginTrait::reset_filter` to do so. + #[derive(Clone)] + pub struct $name { + caller: $caller_name, + filter: $crate::sp_std::rc::Rc::Call) -> bool>>, + } + + #[cfg(not(feature = "std"))] + impl $crate::sp_std::fmt::Debug for $name { + fn fmt( + &self, + fmt: &mut $crate::sp_std::fmt::Formatter + ) -> $crate::sp_std::result::Result<(), $crate::sp_std::fmt::Error> { + fmt.write_str("") + } + } + + #[cfg(feature = "std")] + impl $crate::sp_std::fmt::Debug for $name { + fn fmt( + &self, + fmt: &mut $crate::sp_std::fmt::Formatter + ) -> $crate::sp_std::result::Result<(), $crate::sp_std::fmt::Error> { + fmt.debug_struct(stringify!($name)) + .field("caller", &self.caller) + .field("filter", &"[function ptr]") + .finish() + } + } + + impl $crate::traits::OriginTrait for $name { + type Call = <$runtime as $system::Trait>::Call; + type PalletsOrigin = $caller_name; + + fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static) { + let f = self.filter.clone(); + + self.filter = $crate::sp_std::rc::Rc::new(Box::new(move |call| { + f(call) && filter(call) + })); + } + + fn reset_filter(&mut self) { + let filter = < + <$runtime as $system::Trait>::BaseCallFilter + as $crate::traits::Filter<<$runtime as $system::Trait>::Call> + >::filter; + + self.filter = $crate::sp_std::rc::Rc::new(Box::new(filter)); + } + + fn set_caller_from(&mut self, other: impl Into) { + self.caller = other.into().caller + } + + fn filter_call(&self, call: &Self::Call) -> bool { + (self.filter)(call) + } + } + $crate::paste::item! { #[derive(Clone, PartialEq, Eq, $crate::RuntimeDebug)] $(#[$attr])* #[allow(non_camel_case_types)] - pub enum $name { + pub enum $caller_name { system($system::Origin<$runtime>), $( [< $module $( _ $generic_instance )? >] @@ -168,20 +241,42 @@ macro_rules! impl_outer_origin { #[allow(dead_code)] impl $name { - pub const NONE: Self = $name::system($system::RawOrigin::None); - pub const ROOT: Self = $name::system($system::RawOrigin::Root); + /// Create with system none origin and `frame-system::Trait::BaseCallFilter`. + pub fn none() -> Self { + $system::RawOrigin::None.into() + } + /// Create with system root origin and no filter. + pub fn root() -> Self { + $system::RawOrigin::Root.into() + } + /// Create with system signed origin and `frame-system::Trait::BaseCallFilter`. pub fn signed(by: <$runtime as $system::Trait>::AccountId) -> Self { - $name::system($system::RawOrigin::Signed(by)) + $system::RawOrigin::Signed(by).into() } } + impl From<$system::Origin<$runtime>> for $name { + /// Convert to runtime origin: + /// * root origin is built with no filter + /// * others use `frame-system::Trait::BaseCallFilter` fn from(x: $system::Origin<$runtime>) -> Self { - $name::system(x) + let mut o = $name { + caller: $caller_name::system(x), + filter: $crate::sp_std::rc::Rc::new(Box::new(|_| true)), + }; + + // Root has no filter + if !matches!(o.caller, $caller_name::system($system::Origin::<$runtime>::Root)) { + $crate::traits::OriginTrait::reset_filter(&mut o); + } + + o } } impl Into<$crate::sp_std::result::Result<$system::Origin<$runtime>, $name>> for $name { + /// NOTE: converting to pallet origin loses the origin filter information. fn into(self) -> $crate::sp_std::result::Result<$system::Origin<$runtime>, Self> { - if let $name::system(l) = self { + if let $caller_name::system(l) = self.caller { Ok(l) } else { Err(self) @@ -189,6 +284,8 @@ macro_rules! impl_outer_origin { } } impl From::AccountId>> for $name { + /// Convert to runtime origin with caller being system signed or none and use filter + /// `frame-system::Trait::BaseCallFilter`. fn from(x: Option<<$runtime as $system::Trait>::AccountId>) -> Self { <$system::Origin<$runtime>>::from(x).into() } @@ -196,8 +293,14 @@ macro_rules! impl_outer_origin { $( $crate::paste::item! { impl From<$module::Origin < $( $generic )? $(, $module::$generic_instance )? > > for $name { + /// Convert to runtime origin using `frame-system::Trait::BaseCallFilter`. fn from(x: $module::Origin < $( $generic )? $(, $module::$generic_instance )? >) -> Self { - $name::[< $module $( _ $generic_instance )? >](x) + let mut o = $name { + caller: $caller_name::[< $module $( _ $generic_instance )? >](x), + filter: $crate::sp_std::rc::Rc::new(Box::new(|_| true)), + }; + $crate::traits::OriginTrait::reset_filter(&mut o); + o } } impl Into< @@ -206,11 +309,12 @@ macro_rules! impl_outer_origin { $name, >> for $name { + /// NOTE: converting to pallet origin loses the origin filter information. fn into(self) -> $crate::sp_std::result::Result< $module::Origin < $( $generic )? $(, $module::$generic_instance )? >, Self, > { - if let $name::[< $module $( _ $generic_instance )? >](l) = self { + if let $caller_name::[< $module $( _ $generic_instance )? >](l) = self.caller { Ok(l) } else { Err(self) @@ -224,9 +328,12 @@ macro_rules! impl_outer_origin { #[cfg(test)] mod tests { + use crate::traits::{Filter, OriginTrait}; mod system { pub trait Trait { type AccountId; + type Call; + type BaseCallFilter; } #[derive(Clone, PartialEq, Eq, Debug)] @@ -263,8 +370,17 @@ mod tests { #[derive(Clone, PartialEq, Eq, Debug)] pub struct TestRuntime; + pub struct BaseCallFilter; + impl Filter for BaseCallFilter { + fn filter(c: &u32) -> bool { + *c % 2 == 0 + } + } + impl system::Trait for TestRuntime { type AccountId = u32; + type Call = u32; + type BaseCallFilter = BaseCallFilter; } impl_outer_origin!( @@ -298,4 +414,35 @@ mod tests { impl_outer_origin!( pub enum OriginEmpty for TestRuntime where system = system {} ); + + #[test] + fn test_default_filter() { + assert_eq!(OriginWithSystem::root().filter_call(&0), true); + assert_eq!(OriginWithSystem::root().filter_call(&1), true); + assert_eq!(OriginWithSystem::none().filter_call(&0), true); + assert_eq!(OriginWithSystem::none().filter_call(&1), false); + assert_eq!(OriginWithSystem::signed(0).filter_call(&0), true); + assert_eq!(OriginWithSystem::signed(0).filter_call(&1), false); + assert_eq!(OriginWithSystem::from(Some(0)).filter_call(&0), true); + assert_eq!(OriginWithSystem::from(Some(0)).filter_call(&1), false); + assert_eq!(OriginWithSystem::from(None).filter_call(&0), true); + assert_eq!(OriginWithSystem::from(None).filter_call(&1), false); + assert_eq!(OriginWithSystem::from(origin_without_generic::Origin).filter_call(&0), true); + assert_eq!(OriginWithSystem::from(origin_without_generic::Origin).filter_call(&1), false); + + let mut origin = OriginWithSystem::from(Some(0)); + + origin.add_filter(|c| *c % 2 == 1); + assert_eq!(origin.filter_call(&0), false); + assert_eq!(origin.filter_call(&1), false); + + origin.set_caller_from(OriginWithSystem::root()); + assert!(matches!(origin.caller, OriginWithSystemCaller::system(system::RawOrigin::Root))); + assert_eq!(origin.filter_call(&0), false); + assert_eq!(origin.filter_call(&1), false); + + origin.reset_filter(); + assert_eq!(origin.filter_call(&0), true); + assert_eq!(origin.filter_call(&1), false); + } } diff --git a/frame/support/src/storage/mod.rs b/frame/support/src/storage/mod.rs index 6d0ef91ce1e17..c2d7ceef0fee6 100644 --- a/frame/support/src/storage/mod.rs +++ b/frame/support/src/storage/mod.rs @@ -29,6 +29,33 @@ pub mod child; pub mod generator; pub mod migration; +/// Describes whether a storage transaction should be committed or rolled back. +pub enum TransactionOutcome { + /// Transaction should be committed. + Commit(T), + /// Transaction should be rolled back. + Rollback(T), +} + +/// Execute the supplied function in a new storage transaction. +/// +/// All changes to storage performed by the supplied function are discarded if the returned +/// outcome is `TransactionOutcome::Rollback`. +/// +/// Transactions can be nested to any depth. Commits happen to the parent transaction. +pub fn with_transaction(f: impl FnOnce() -> TransactionOutcome) -> R { + use sp_io::storage::{ + start_transaction, commit_transaction, rollback_transaction, + }; + use TransactionOutcome::*; + + start_transaction(); + match f() { + Commit(res) => { commit_transaction(); res }, + Rollback(res) => { rollback_transaction(); res }, + } +} + /// A trait for working with macro-generated storage values under the substrate storage API. /// /// Details on implementation can be found at diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 4ad54da27d65c..a8d4985806fc0 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -199,6 +199,17 @@ macro_rules! impl_filter_stack { } } +/// Type that provide some integrity tests. +/// +/// This implemented for modules by `decl_module`. +#[impl_for_tuples(30)] +pub trait IntegrityTest { + /// Run integrity test. + /// + /// The test is not executed in a externalities provided environment. + fn integrity_test() {} +} + #[cfg(test)] mod test_impl_filter_stack { use super::*; @@ -457,9 +468,11 @@ impl Len for T where ::IntoIter: Ex } } -/// A trait for querying a single fixed value from a type. +/// A trait for querying a single value from a type. +/// +/// It is not required that the value is constant. pub trait Get { - /// Return a constant value. + /// Return the current value. fn get() -> T; } @@ -1013,6 +1026,7 @@ pub trait Currency { } /// Status of funds. +#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug)] pub enum BalanceStatus { /// Funds are free, as corresponding to `free` item in Balances. Free, @@ -1472,12 +1486,12 @@ pub mod schedule { /// The highest priority. We invert the value so that normal sorting will place the highest /// priority at the beginning of the list. - pub const HIGHEST_PRORITY: Priority = 0; + pub const HIGHEST_PRIORITY: Priority = 0; /// Anything of this value or lower will definitely be scheduled on the block that they ask for, even /// if it breaches the `MaximumWeight` limitation. pub const HARD_DEADLINE: Priority = 63; /// The lowest priority. Most stuff should be around here. - pub const LOWEST_PRORITY: Priority = 255; + pub const LOWEST_PRIORITY: Priority = 255; /// A type that can be used as a scheduler. pub trait Anon { @@ -1494,7 +1508,7 @@ pub mod schedule { maybe_periodic: Option>, priority: Priority, call: Call - ) -> Self::Address; + ) -> Result; /// Cancel a scheduled task. If periodic, then it will cancel all further instances of that, /// also. @@ -1554,6 +1568,63 @@ pub trait EnsureOrigin { fn successful_origin() -> OuterOrigin; } +/// Type that can be dispatched with an origin but without checking the origin filter. +/// +/// Implemented for pallet dispatchable type by `decl_module` and for runtime dispatchable by +/// `construct_runtime` and `impl_outer_dispatch`. +pub trait UnfilteredDispatchable { + /// The origin type of the runtime, (i.e. `frame_system::Trait::Origin`). + type Origin; + + /// Dispatch this call but do not check the filter in origin. + fn dispatch_bypass_filter(self, origin: Self::Origin) -> crate::dispatch::DispatchResultWithPostInfo; +} + +/// Methods available on `frame_system::Trait::Origin`. +pub trait OriginTrait: Sized { + /// Runtime call type, as in `frame_system::Trait::Call` + type Call; + + /// The caller origin, overarching type of all pallets origins. + type PalletsOrigin; + + /// Add a filter to the origin. + fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static); + + /// Reset origin filters to default one, i.e `frame_system::Trait::BaseCallFilter`. + fn reset_filter(&mut self); + + /// Replace the caller with caller from the other origin + fn set_caller_from(&mut self, other: impl Into); + + /// Filter the call, if false then call is filtered out. + fn filter_call(&self, call: &Self::Call) -> bool; +} + +/// Trait to be used when types are exactly same. +/// +/// This allow to convert back and forth from type, a reference and a mutable reference. +pub trait IsType: Into + From { + /// Cast reference. + fn from_ref(t: &T) -> &Self; + + /// Cast reference. + fn into_ref(&self) -> &T; + + /// Cast mutable reference. + fn from_mut(t: &mut T) -> &mut Self; + + /// Cast mutable reference. + fn into_mut(&mut self) -> &mut T; +} + +impl IsType for T { + fn from_ref(t: &T) -> &Self { t } + fn into_ref(&self) -> &T { self } + fn from_mut(t: &mut T) -> &mut Self { t } + fn into_mut(&mut self) -> &mut T { self } +} + #[cfg(test)] mod tests { use super::*; diff --git a/frame/support/src/weights.rs b/frame/support/src/weights.rs index dd80f8d8a8e6c..810bd2fcb6416 100644 --- a/frame/support/src/weights.rs +++ b/frame/support/src/weights.rs @@ -425,9 +425,11 @@ impl PaysFee for (Weight, Pays) { /// with the same argument list as the dispatched, wrapped in a tuple. /// - `PF`: a `Pays` variant for whether this dispatch pays fee or not or a closure that /// returns a `Pays` variant with the same argument list as the dispatched, wrapped in a tuple. +#[deprecated = "Function arguments are available directly inside the annotation now."] pub struct FunctionOf(pub WD, pub CD, pub PF); // `WeighData` as a raw value +#[allow(deprecated)] impl WeighData for FunctionOf { fn weigh_data(&self, _: Args) -> Weight { self.0 @@ -435,6 +437,7 @@ impl WeighData for FunctionOf { } // `WeighData` as a closure +#[allow(deprecated)] impl WeighData for FunctionOf where WD : Fn(Args) -> Weight { @@ -444,6 +447,7 @@ impl WeighData for FunctionOf where } // `ClassifyDispatch` as a raw value +#[allow(deprecated)] impl ClassifyDispatch for FunctionOf { fn classify_dispatch(&self, _: Args) -> DispatchClass { self.1 @@ -451,6 +455,7 @@ impl ClassifyDispatch for FunctionOf } // `ClassifyDispatch` as a raw value +#[allow(deprecated)] impl ClassifyDispatch for FunctionOf where CD : Fn(Args) -> DispatchClass { @@ -460,6 +465,7 @@ impl ClassifyDispatch for FunctionOf where } // `PaysFee` as a raw value +#[allow(deprecated)] impl PaysFee for FunctionOf { fn pays_fee(&self, _: Args) -> Pays { self.2 @@ -467,6 +473,7 @@ impl PaysFee for FunctionOf { } // `PaysFee` as a closure +#[allow(deprecated)] impl PaysFee for FunctionOf where PF : Fn(Args) -> Pays { @@ -663,10 +670,10 @@ mod tests { fn f03(_origin) { unimplemented!(); } // weight = a x 10 + b - #[weight = FunctionOf(|args: (&u32, &u32)| (args.0 * 10 + args.1) as Weight, DispatchClass::Normal, Pays::Yes)] + #[weight = ((_a * 10 + _eb * 1) as Weight, DispatchClass::Normal, Pays::Yes)] fn f11(_origin, _a: u32, _eb: u32) { unimplemented!(); } - #[weight = FunctionOf(|_: (&u32, &u32)| 0, DispatchClass::Operational, Pays::Yes)] + #[weight = (0, DispatchClass::Operational, Pays::Yes)] fn f12(_origin, _a: u32, _eb: u32) { unimplemented!(); } #[weight = T::DbWeight::get().reads(3) + T::DbWeight::get().writes(2) + 10_000] diff --git a/frame/support/test/Cargo.toml b/frame/support/test/Cargo.toml index 65933929a5fa7..d6e7d7d633bbd 100644 --- a/frame/support/test/Cargo.toml +++ b/frame/support/test/Cargo.toml @@ -13,13 +13,14 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", default-features = false, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-io ={ version = "2.0.0-rc3", path = "../../../primitives/io", default-features = false } sp-state-machine = { version = "0.8.0-rc3", optional = true, path = "../../../primitives/state-machine" } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../" } sp-inherents = { version = "2.0.0-rc3", default-features = false, path = "../../../primitives/inherents" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../../primitives/runtime" } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../../primitives/core" } +sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../../primitives/std" } trybuild = "1.0.17" pretty_assertions = "0.6.1" rustversion = "1.0.0" @@ -33,6 +34,7 @@ std = [ "frame-support/std", "sp-inherents/std", "sp-core/std", + "sp-std/std", "sp-runtime/std", "sp-state-machine", ] diff --git a/frame/support/test/tests/decl_error.rs b/frame/support/test/tests/construct_runtime.rs similarity index 86% rename from frame/support/test/tests/decl_error.rs rename to frame/support/test/tests/construct_runtime.rs index 9536d4e8195d4..10fc3319fb080 100644 --- a/frame/support/test/tests/decl_error.rs +++ b/frame/support/test/tests/construct_runtime.rs @@ -15,16 +15,24 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! General tests for construct_runtime macro, test for: +//! * error declareed with decl_error works +//! * integrity test is generated + #![recursion_limit="128"] use sp_runtime::{generic, traits::{BlakeTwo256, Block as _, Verify}, DispatchError}; use sp_core::{H256, sr25519}; - +use sp_std::cell::RefCell; mod system; pub trait Currency {} +thread_local! { + pub static INTEGRITY_TEST_EXEC: RefCell = RefCell::new(0); +} + mod module1 { use super::*; @@ -65,6 +73,10 @@ mod module2 { pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { Err(Error::::Something.into()) } + + fn integrity_test() { + INTEGRITY_TEST_EXEC.with(|i| *i.borrow_mut() += 1); + } } } @@ -89,12 +101,14 @@ pub type BlockNumber = u64; pub type Index = u64; impl system::Trait for Runtime { + type BaseCallFilter = (); type Hash = H256; type Origin = Origin; type BlockNumber = BlockNumber; type AccountId = AccountId; type Event = Event; type ModuleToIndex = ModuleToIndex; + type Call = Call; } frame_support::construct_runtime!( @@ -137,3 +151,9 @@ fn check_module2_error_type() { Err(DispatchError::Module { index: 2, error: 0, message: Some("Something") }), ); } + +#[test] +fn integrity_test_works() { + __construct_runtime_integrity_test::runtime_integrity_tests(); + assert_eq!(INTEGRITY_TEST_EXEC.with(|i| *i.borrow()), 1); +} diff --git a/frame/support/test/tests/decl_module_ui.rs b/frame/support/test/tests/decl_module_ui.rs new file mode 100644 index 0000000000000..90d105e7cfae3 --- /dev/null +++ b/frame/support/test/tests/decl_module_ui.rs @@ -0,0 +1,26 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//#[rustversion::attr(not(stable), ignore)] +#[test] +fn decl_module_ui() { + // As trybuild is using `cargo check`, we don't need the real WASM binaries. + std::env::set_var("BUILD_DUMMY_WASM_BINARY", "1"); + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/decl_module_ui/*.rs"); +} diff --git a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs new file mode 100644 index 0000000000000..4dbae05f07ff7 --- /dev/null +++ b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs @@ -0,0 +1,7 @@ +frame_support::decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn integrity_test() {} + + fn integrity_test() {} + } +} diff --git a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.stderr b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.stderr new file mode 100644 index 0000000000000..d6498961d31c8 --- /dev/null +++ b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.stderr @@ -0,0 +1,25 @@ +error: `integrity_test` can only be passed once as input. + --> $DIR/reserved_keyword_two_times_integrity_test.rs:1:1 + | +1 | / frame_support::decl_module! { +2 | | pub struct Module for enum Call where origin: T::Origin { +3 | | fn integrity_test() {} +4 | | +5 | | fn integrity_test() {} +6 | | } +7 | | } + | |_^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0601]: `main` function not found in crate `$CRATE` + --> $DIR/reserved_keyword_two_times_integrity_test.rs:1:1 + | +1 | / frame_support::decl_module! { +2 | | pub struct Module for enum Call where origin: T::Origin { +3 | | fn integrity_test() {} +4 | | +5 | | fn integrity_test() {} +6 | | } +7 | | } + | |_^ consider adding a `main` function to `$DIR/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs` diff --git a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs new file mode 100644 index 0000000000000..4f05134997e81 --- /dev/null +++ b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs @@ -0,0 +1,11 @@ +frame_support::decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn on_initialize() -> Weight { + 0 + } + + fn on_initialize() -> Weight { + 0 + } + } +} diff --git a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.stderr b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.stderr new file mode 100644 index 0000000000000..8a9f025046b7e --- /dev/null +++ b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.stderr @@ -0,0 +1,25 @@ +error: `on_initialize` can only be passed once as input. + --> $DIR/reserved_keyword_two_times_on_initialize.rs:1:1 + | +1 | / frame_support::decl_module! { +2 | | pub struct Module for enum Call where origin: T::Origin { +3 | | fn on_initialize() -> Weight { +4 | | 0 +... | +10 | | } +11 | | } + | |_^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0601]: `main` function not found in crate `$CRATE` + --> $DIR/reserved_keyword_two_times_on_initialize.rs:1:1 + | +1 | / frame_support::decl_module! { +2 | | pub struct Module for enum Call where origin: T::Origin { +3 | | fn on_initialize() -> Weight { +4 | | 0 +... | +10 | | } +11 | | } + | |_^ consider adding a `main` function to `$DIR/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs` diff --git a/frame/support/test/tests/instance.rs b/frame/support/test/tests/instance.rs index 45e280902a268..920554346f73e 100644 --- a/frame/support/test/tests/instance.rs +++ b/frame/support/test/tests/instance.rs @@ -233,12 +233,14 @@ pub type BlockNumber = u64; pub type Index = u64; impl system::Trait for Runtime { + type BaseCallFilter= (); type Hash = H256; type Origin = Origin; type BlockNumber = BlockNumber; type AccountId = AccountId; type Event = Event; type ModuleToIndex = (); + type Call = Call; } frame_support::construct_runtime!( diff --git a/frame/support/test/tests/issue2219.rs b/frame/support/test/tests/issue2219.rs index cd357ba2667cb..7166f202c7325 100644 --- a/frame/support/test/tests/issue2219.rs +++ b/frame/support/test/tests/issue2219.rs @@ -158,12 +158,14 @@ pub type Block = generic::Block; pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; impl system::Trait for Runtime { + type BaseCallFilter = (); type Hash = H256; type Origin = Origin; type BlockNumber = BlockNumber; type AccountId = AccountId; type Event = Event; type ModuleToIndex = (); + type Call = Call; } impl module::Trait for Runtime {} diff --git a/frame/support/test/tests/storage_transaction.rs b/frame/support/test/tests/storage_transaction.rs new file mode 100644 index 0000000000000..bf6c70966b469 --- /dev/null +++ b/frame/support/test/tests/storage_transaction.rs @@ -0,0 +1,159 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use codec::{Encode, Decode, EncodeLike}; +use frame_support::{ + StorageMap, StorageValue, storage::{with_transaction, TransactionOutcome::*}, +}; +use sp_io::TestExternalities; + +pub trait Trait { + type Origin; + type BlockNumber: Encode + Decode + EncodeLike + Default + Clone; +} + +frame_support::decl_module! { + pub struct Module for enum Call where origin: T::Origin {} +} + +frame_support::decl_storage!{ + trait Store for Module as StorageTransactions { + pub Value: u32; + pub Map: map hasher(twox_64_concat) String => u32; + } +} + + +#[test] +fn storage_transaction_basic_commit() { + TestExternalities::default().execute_with(|| { + + assert_eq!(Value::get(), 0); + assert!(!Map::contains_key("val0")); + + with_transaction(|| { + Value::set(99); + Map::insert("val0", 99); + assert_eq!(Value::get(), 99); + assert_eq!(Map::get("val0"), 99); + Commit(()) + }); + + assert_eq!(Value::get(), 99); + assert_eq!(Map::get("val0"), 99); + }); +} + +#[test] +fn storage_transaction_basic_rollback() { + TestExternalities::default().execute_with(|| { + + assert_eq!(Value::get(), 0); + assert_eq!(Map::get("val0"), 0); + + with_transaction(|| { + Value::set(99); + Map::insert("val0", 99); + assert_eq!(Value::get(), 99); + assert_eq!(Map::get("val0"), 99); + Rollback(()) + }); + + assert_eq!(Value::get(), 0); + assert_eq!(Map::get("val0"), 0); + }); +} + +#[test] +fn storage_transaction_rollback_then_commit() { + TestExternalities::default().execute_with(|| { + Value::set(1); + Map::insert("val1", 1); + + with_transaction(|| { + Value::set(2); + Map::insert("val1", 2); + Map::insert("val2", 2); + + with_transaction(|| { + Value::set(3); + Map::insert("val1", 3); + Map::insert("val2", 3); + Map::insert("val3", 3); + + assert_eq!(Value::get(), 3); + assert_eq!(Map::get("val1"), 3); + assert_eq!(Map::get("val2"), 3); + assert_eq!(Map::get("val3"), 3); + + Rollback(()) + }); + + assert_eq!(Value::get(), 2); + assert_eq!(Map::get("val1"), 2); + assert_eq!(Map::get("val2"), 2); + assert_eq!(Map::get("val3"), 0); + + Commit(()) + }); + + assert_eq!(Value::get(), 2); + assert_eq!(Map::get("val1"), 2); + assert_eq!(Map::get("val2"), 2); + assert_eq!(Map::get("val3"), 0); + }); +} + +#[test] +fn storage_transaction_commit_then_rollback() { + TestExternalities::default().execute_with(|| { + Value::set(1); + Map::insert("val1", 1); + + with_transaction(|| { + Value::set(2); + Map::insert("val1", 2); + Map::insert("val2", 2); + + with_transaction(|| { + Value::set(3); + Map::insert("val1", 3); + Map::insert("val2", 3); + Map::insert("val3", 3); + + assert_eq!(Value::get(), 3); + assert_eq!(Map::get("val1"), 3); + assert_eq!(Map::get("val2"), 3); + assert_eq!(Map::get("val3"), 3); + + Commit(()) + }); + + assert_eq!(Value::get(), 3); + assert_eq!(Map::get("val1"), 3); + assert_eq!(Map::get("val2"), 3); + assert_eq!(Map::get("val3"), 3); + + Rollback(()) + }); + + assert_eq!(Value::get(), 1); + assert_eq!(Map::get("val1"), 1); + assert_eq!(Map::get("val2"), 0); + assert_eq!(Map::get("val3"), 0); + }); +} diff --git a/frame/support/test/tests/system.rs b/frame/support/test/tests/system.rs index 821224d0a29cf..0d6a22fd1a3e1 100644 --- a/frame/support/test/tests/system.rs +++ b/frame/support/test/tests/system.rs @@ -21,15 +21,17 @@ pub trait Trait: 'static + Eq + Clone { type Origin: Into, Self::Origin>> + From>; + type BaseCallFilter: frame_support::traits::Filter; type BlockNumber: Decode + Encode + EncodeLike + Clone + Default; type Hash; type AccountId: Encode + EncodeLike + Decode; + type Call; type Event: From>; type ModuleToIndex: frame_support::traits::ModuleToIndex; } frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin, {} } impl Module { diff --git a/frame/system/Cargo.toml b/frame/system/Cargo.toml index ca1b5d6a12dd3..af3288a907f50 100644 --- a/frame/system/Cargo.toml +++ b/frame/system/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0-rc3", path = "../../primitives/io", default-features = false } diff --git a/frame/system/benches/bench.rs b/frame/system/benches/bench.rs index 9a2364fcb535f..d8606c39be8bf 100644 --- a/frame/system/benches/bench.rs +++ b/frame/system/benches/bench.rs @@ -61,6 +61,7 @@ frame_support::parameter_types! { #[derive(Clone, Eq, PartialEq)] pub struct Runtime; impl system::Trait for Runtime { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -82,7 +83,8 @@ impl system::Trait for Runtime { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } diff --git a/frame/system/benchmarking/Cargo.toml b/frame/system/benchmarking/Cargo.toml index 71896f8a399a1..b1636c21e5fad 100644 --- a/frame/system/benchmarking/Cargo.toml +++ b/frame/system/benchmarking/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME System benchmarking" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../../primitives/std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../../primitives/runtime" } frame-benchmarking = { version = "2.0.0-rc3", default-features = false, path = "../../benchmarking" } diff --git a/frame/system/benchmarking/src/mock.rs b/frame/system/benchmarking/src/mock.rs index a4509f6d03933..56dc37d837628 100644 --- a/frame/system/benchmarking/src/mock.rs +++ b/frame/system/benchmarking/src/mock.rs @@ -51,6 +51,7 @@ impl Dispatchable for Call { pub struct Test; impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = AccountIndex; type BlockNumber = BlockNumber; diff --git a/frame/system/rpc/runtime-api/Cargo.toml b/frame/system/rpc/runtime-api/Cargo.toml index 4f599d6d47087..d919fd1b581d7 100644 --- a/frame/system/rpc/runtime-api/Cargo.toml +++ b/frame/system/rpc/runtime-api/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-api = { version = "2.0.0-rc3", default-features = false, path = "../../../../primitives/api" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } [features] default = ["std"] diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 481f3644276cb..6590f16675e1b 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -102,7 +102,7 @@ use sp_std::marker::PhantomData; use sp_std::fmt::Debug; use sp_version::RuntimeVersion; use sp_runtime::{ - RuntimeDebug, Perbill, DispatchError, DispatchResult, + RuntimeDebug, Perbill, DispatchError, DispatchResult, Either, generic::{self, Era}, transaction_validity::{ ValidTransaction, TransactionPriority, TransactionLongevity, TransactionValidityError, @@ -112,7 +112,7 @@ use sp_runtime::{ self, CheckEqual, AtLeast32Bit, Zero, SignedExtension, Lookup, LookupError, SimpleBitOps, Hash, Member, MaybeDisplay, BadOrigin, SaturatedConversion, MaybeSerialize, MaybeSerializeDeserialize, MaybeMallocSizeOf, StaticLookup, One, Bounded, - Dispatchable, DispatchInfoOf, PostDispatchInfoOf, + Dispatchable, DispatchInfoOf, PostDispatchInfoOf, Printable, }, offchain::storage_lock::BlockNumberProvider, }; @@ -123,11 +123,11 @@ use frame_support::{ storage, traits::{ Contains, Get, ModuleToIndex, OnNewAccount, OnKilledAccount, IsDeadAccount, Happened, - StoredMap, EnsureOrigin, MigrateAccount, + StoredMap, EnsureOrigin, OriginTrait, Filter, MigrateAccount, }, weights::{ Weight, RuntimeDbWeight, DispatchInfo, PostDispatchInfo, DispatchClass, - FunctionOf, Pays, extract_actual_weight, + extract_actual_weight, Pays, FunctionOf, }, dispatch::DispatchResultWithPostInfo, }; @@ -150,11 +150,16 @@ pub fn extrinsics_data_root(xts: Vec>) -> H::Output { } pub trait Trait: 'static + Eq + Clone { - /// The aggregated `Origin` type used by dispatchable calls. + /// The basic call filter to use in Origin. All origins are built with this filter as base, + /// except Root. + type BaseCallFilter: Filter; + + /// The `Origin` type used by dispatchable calls. type Origin: Into, Self::Origin>> + From> - + Clone; + + Clone + + OriginTrait; /// The aggregated `Call` type. type Call: Dispatchable + Debug; @@ -581,11 +586,7 @@ decl_module! { /// A dispatch that will fill the block weight up to the given ratio. // TODO: This should only be available for testing, rather than in general usage, but // that's not possible at present (since it's within the decl_module macro). - #[weight = FunctionOf( - |(ratio,): (&Perbill,)| *ratio * T::MaximumBlockWeight::get(), - DispatchClass::Operational, - Pays::Yes, - )] + #[weight = *_ratio * T::MaximumBlockWeight::get()] fn fill_block(origin, _ratio: Perbill) { ensure_root(origin)?; } @@ -684,13 +685,10 @@ decl_module! { /// - Base Weight: 0.568 * i µs /// - Writes: Number of items /// # - #[weight = FunctionOf( - |(items,): (&Vec,)| { - T::DbWeight::get().writes(items.len() as Weight) - .saturating_add((items.len() as Weight).saturating_mul(600_000)) - }, + #[weight = ( + T::DbWeight::get().writes(items.len() as Weight) + .saturating_add((items.len() as Weight).saturating_mul(600_000)), DispatchClass::Operational, - Pays::Yes, )] fn set_storage(origin, items: Vec) { ensure_root(origin)?; @@ -707,13 +705,10 @@ decl_module! { /// - Base Weight: .378 * i µs /// - Writes: Number of items /// # - #[weight = FunctionOf( - |(keys,): (&Vec,)| { - T::DbWeight::get().writes(keys.len() as Weight) - .saturating_add((keys.len() as Weight).saturating_mul(400_000)) - }, + #[weight = ( + T::DbWeight::get().writes(keys.len() as Weight) + .saturating_add((keys.len() as Weight).saturating_mul(400_000)), DispatchClass::Operational, - Pays::Yes, )] fn kill_storage(origin, keys: Vec) { ensure_root(origin)?; @@ -733,13 +728,10 @@ decl_module! { /// - Base Weight: 0.834 * P µs /// - Writes: Number of subkeys + 1 /// # - #[weight = FunctionOf( - |(_, &subkeys): (&Key, &u32)| { - T::DbWeight::get().writes(Weight::from(subkeys) + 1) - .saturating_add((Weight::from(subkeys) + 1).saturating_mul(850_000)) - }, + #[weight = ( + T::DbWeight::get().writes(Weight::from(*_subkeys) + 1) + .saturating_add((Weight::from(*_subkeys) + 1).saturating_mul(850_000)), DispatchClass::Operational, - Pays::Yes, )] fn kill_prefix(origin, prefix: Key, _subkeys: u32) { ensure_root(origin)?; @@ -877,6 +869,30 @@ impl EnsureOrigin for EnsureNever { } } +/// The "OR gate" implementation of `EnsureOrigin`. +/// +/// Origin check will pass if `L` or `R` origin check passes. `L` is tested first. +pub struct EnsureOneOf(sp_std::marker::PhantomData<(AccountId, L, R)>); +impl< + AccountId, + O: Into, O>> + From>, + L: EnsureOrigin, + R: EnsureOrigin, +> EnsureOrigin for EnsureOneOf { + type Success = Either; + fn try_origin(o: O) -> Result { + L::try_origin(o).map_or_else( + |o| R::try_origin(o).map(|o| Either::Right(o)), + |o| Ok(Either::Left(o)), + ) + } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> O { + L::successful_origin() + } +} + /// Ensure that the origin `o` represents a signed extrinsic (i.e. transaction). /// Returns `Ok` with the account that signed the extrinsic or an `Err` otherwise. pub fn ensure_signed(o: OuterOrigin) -> Result @@ -1409,8 +1425,10 @@ impl CheckWeight where info: &DispatchInfoOf, ) -> Result<(), TransactionValidityError> { match info.class { - // Mandatory and Operational transactions does not - DispatchClass::Mandatory | DispatchClass::Operational => Ok(()), + // Mandatory transactions are included in a block unconditionally, so + // we don't verify weight. + DispatchClass::Mandatory => Ok(()), + // Normal transactions must not exceed `MaximumExtrinsicWeight`. DispatchClass::Normal => { let maximum_weight = T::MaximumExtrinsicWeight::get(); let extrinsic_weight = info.weight.saturating_add(T::ExtrinsicBaseWeight::get()); @@ -1419,7 +1437,22 @@ impl CheckWeight where } else { Ok(()) } - } + }, + // For operational transactions we make sure it doesn't exceed + // the space alloted for `Operational` class. + DispatchClass::Operational => { + let maximum_weight = T::MaximumBlockWeight::get(); + let operational_limit = + Self::get_dispatch_limit_ratio(DispatchClass::Operational) * maximum_weight; + let operational_limit = + operational_limit.saturating_sub(T::BlockExecutionWeight::get()); + let extrinsic_weight = info.weight.saturating_add(T::ExtrinsicBaseWeight::get()); + if extrinsic_weight > operational_limit { + Err(InvalidTransaction::ExhaustsResources.into()) + } else { + Ok(()) + } + }, } } @@ -1498,9 +1531,11 @@ impl CheckWeight where fn get_priority(info: &DispatchInfoOf) -> TransactionPriority { match info.class { DispatchClass::Normal => info.weight.into(), - DispatchClass::Operational => Bounded::max_value(), + // Don't use up the whole priority space, to allow things like `tip` + // to be taken into account as well. + DispatchClass::Operational => TransactionPriority::max_value() / 2, // Mandatory extrinsics are only for inherents; never transactions. - DispatchClass::Mandatory => Bounded::min_value(), + DispatchClass::Mandatory => TransactionPriority::min_value(), } } @@ -1606,7 +1641,10 @@ impl SignedExtension for CheckWeight where // Since mandatory dispatched do not get validated for being overweight, we are sensitive // to them actually being useful. Block producers are thus not allowed to include mandatory // extrinsics that result in error. - if info.class == DispatchClass::Mandatory && result.is_err() { + if let (DispatchClass::Mandatory, Err(e)) = (info.class, result) { + "Bad mandantory".print(); + e.print(); + Err(InvalidTransaction::BadMandatory)? } @@ -1910,7 +1948,7 @@ pub(crate) mod tests { use sp_runtime::{traits::{BlakeTwo256, IdentityLookup, SignedExtension}, testing::Header, DispatchError}; use frame_support::{ impl_outer_origin, parameter_types, assert_ok, assert_noop, - weights::WithPostDispatchInfo, + weights::{WithPostDispatchInfo, Pays}, }; impl_outer_origin! { @@ -1956,7 +1994,7 @@ pub(crate) mod tests { pub struct Call; impl Dispatchable for Call { - type Origin = (); + type Origin = Origin; type Trait = (); type Info = DispatchInfo; type PostInfo = PostDispatchInfo; @@ -1967,6 +2005,7 @@ pub(crate) mod tests { } impl Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Call = Call; type Index = u64; @@ -1988,7 +2027,8 @@ pub(crate) mod tests { type Version = Version; type ModuleToIndex = (); type AccountData = u32; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = RecordKilled; } @@ -2016,7 +2056,7 @@ pub(crate) mod tests { fn origin_works() { let o = Origin::from(RawOrigin::::Signed(1u64)); let x: Result, Origin> = o.into(); - assert_eq!(x, Ok(RawOrigin::::Signed(1u64))); + assert_eq!(x.unwrap(), RawOrigin::::Signed(1u64)); } #[test] @@ -2462,6 +2502,42 @@ pub(crate) mod tests { }); } + #[test] + fn operational_extrinsic_limited_by_operational_space_limit() { + new_test_ext().execute_with(|| { + let operational_limit = CheckWeight::::get_dispatch_limit_ratio( + DispatchClass::Operational + ) * ::MaximumBlockWeight::get(); + let base_weight = ::ExtrinsicBaseWeight::get(); + let block_base = ::BlockExecutionWeight::get(); + + let weight = operational_limit - base_weight - block_base; + let okay = DispatchInfo { + weight, + class: DispatchClass::Operational, + ..Default::default() + }; + let max = DispatchInfo { + weight: weight + 1, + class: DispatchClass::Operational, + ..Default::default() + }; + let len = 0_usize; + + assert_eq!( + CheckWeight::::do_validate(&okay, len), + Ok(ValidTransaction { + priority: CheckWeight::::get_priority(&okay), + ..Default::default() + }) + ); + assert_noop!( + CheckWeight::::do_validate(&max, len), + InvalidTransaction::ExhaustsResources + ); + }); + } + #[test] fn register_extra_weight_unchecked_doesnt_care_about_limits() { new_test_ext().execute_with(|| { @@ -2489,6 +2565,8 @@ pub(crate) mod tests { assert_ok!(CheckWeight::::do_pre_dispatch(&rest_operational, len)); assert_eq!(::MaximumBlockWeight::get(), 1024); assert_eq!(System::block_weight().total(), ::MaximumBlockWeight::get()); + // Checking single extrinsic should not take current block weight into account. + assert_eq!(CheckWeight::::check_extrinsic_weight(&rest_operational), Ok(())); }); } @@ -2524,6 +2602,8 @@ pub(crate) mod tests { assert_ok!(CheckWeight::::do_pre_dispatch(&dispatch_operational, len)); // Not too much though assert_noop!(CheckWeight::::do_pre_dispatch(&dispatch_operational, len), InvalidTransaction::ExhaustsResources); + // Even with full block, validity of single transaction should be correct. + assert_eq!(CheckWeight::::check_extrinsic_weight(&dispatch_operational), Ok(())); }); } @@ -2569,7 +2649,7 @@ pub(crate) mod tests { .validate(&1, CALL, &op, len) .unwrap() .priority; - assert_eq!(priority, u64::max_value()); + assert_eq!(priority, u64::max_value() / 2); }) } @@ -2731,4 +2811,15 @@ pub(crate) mod tests { assert!(System::events().len() == 1); }); } + + #[test] + fn ensure_one_of_works() { + fn ensure_root_or_signed(o: RawOrigin) -> Result, Origin> { + EnsureOneOf::, EnsureSigned>::try_origin(o.into()) + } + + assert_eq!(ensure_root_or_signed(RawOrigin::Root).unwrap(), Either::Left(())); + assert_eq!(ensure_root_or_signed(RawOrigin::Signed(0)).unwrap(), Either::Right(0)); + assert!(ensure_root_or_signed(RawOrigin::None).is_err()) + } } diff --git a/frame/timestamp/Cargo.toml b/frame/timestamp/Cargo.toml index 804f17a23abcd..7d08164bdd758 100644 --- a/frame/timestamp/Cargo.toml +++ b/frame/timestamp/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io", optional = true } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/timestamp/src/lib.rs b/frame/timestamp/src/lib.rs index 94048cb148e6e..cbf587b1512da 100644 --- a/frame/timestamp/src/lib.rs +++ b/frame/timestamp/src/lib.rs @@ -314,6 +314,7 @@ mod tests { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -335,7 +336,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } parameter_types! { @@ -352,7 +354,7 @@ mod tests { fn timestamp_works() { new_test_ext().execute_with(|| { Timestamp::set_timestamp(42); - assert_ok!(Timestamp::dispatch(Call::set(69), Origin::NONE)); + assert_ok!(Timestamp::set(Origin::none(), 69)); assert_eq!(Timestamp::now(), 69); }); } @@ -362,8 +364,8 @@ mod tests { fn double_timestamp_should_fail() { new_test_ext().execute_with(|| { Timestamp::set_timestamp(42); - assert_ok!(Timestamp::dispatch(Call::set(69), Origin::NONE)); - let _ = Timestamp::dispatch(Call::set(70), Origin::NONE); + assert_ok!(Timestamp::set(Origin::none(), 69)); + let _ = Timestamp::set(Origin::none(), 70); }); } @@ -372,7 +374,7 @@ mod tests { fn block_period_minimum_enforced() { new_test_ext().execute_with(|| { Timestamp::set_timestamp(42); - let _ = Timestamp::dispatch(Call::set(46), Origin::NONE); + let _ = Timestamp::set(Origin::none(), 46); }); } } diff --git a/frame/transaction-payment/Cargo.toml b/frame/transaction-payment/Cargo.toml index e1abb00cbf25e..f7a15d962b5f4 100644 --- a/frame/transaction-payment/Cargo.toml +++ b/frame/transaction-payment/Cargo.toml @@ -12,7 +12,8 @@ description = "FRAME pallet to manage transaction payments" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } +serde = { version = "1.0.101", optional = true } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } @@ -29,6 +30,7 @@ sp-storage = { version = "2.0.0-rc3", path = "../../primitives/storage" } [features] default = ["std"] std = [ + "serde", "codec/std", "sp-std/std", "sp-runtime/std", diff --git a/frame/transaction-payment/rpc/Cargo.toml b/frame/transaction-payment/rpc/Cargo.toml index 2f1e0f06d78bb..22be6e700b0ea 100644 --- a/frame/transaction-payment/rpc/Cargo.toml +++ b/frame/transaction-payment/rpc/Cargo.toml @@ -12,7 +12,7 @@ description = "RPC interface for the transaction payment module." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } jsonrpc-core = "14.2.0" jsonrpc-core-client = "14.2.0" jsonrpc-derive = "14.2.1" diff --git a/frame/transaction-payment/rpc/runtime-api/Cargo.toml b/frame/transaction-payment/rpc/runtime-api/Cargo.toml index 8ffa6fb6ee5b2..e63b94cb4bc10 100644 --- a/frame/transaction-payment/rpc/runtime-api/Cargo.toml +++ b/frame/transaction-payment/rpc/runtime-api/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } sp-api = { version = "2.0.0-rc3", default-features = false, path = "../../../../primitives/api" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../../../primitives/std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../../../primitives/runtime" } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../../../support" } diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index d76bcd8770364..d36417c372799 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -44,7 +44,7 @@ use frame_support::{ dispatch::DispatchResult, }; use sp_runtime::{ - FixedI128, FixedPointNumber, FixedPointOperand, + FixedU128, FixedPointNumber, FixedPointOperand, Perquintill, RuntimeDebug, transaction_validity::{ TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError, TransactionValidity, @@ -59,13 +59,125 @@ use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo; mod migration; /// Fee multiplier. -pub type Multiplier = FixedI128; +pub type Multiplier = FixedU128; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; +/// A struct to update the weight multiplier per block. It implements `Convert`, meaning that it can convert the previous multiplier to the next one. This should +/// be called on `on_finalize` of a block, prior to potentially cleaning the weight data from the +/// system module. +/// +/// given: +/// s = previous block weight +/// s'= ideal block weight +/// m = maximum block weight +/// diff = (s - s')/m +/// v = 0.00001 +/// t1 = (v * diff) +/// t2 = (v * diff)^2 / 2 +/// then: +/// next_multiplier = prev_multiplier * (1 + t1 + t2) +/// +/// Where `(s', v)` must be given as the `Get` implementation of the `T` generic type. Moreover, `M` +/// must provide the minimum allowed value for the multiplier. Note that a runtime should ensure +/// with tests that the combination of this `M` and `V` is not such that the multiplier can drop to +/// zero and never recover. +/// +/// note that `s'` is interpreted as a portion in the _normal transaction_ capacity of the block. +/// For example, given `s' == 0.25` and `AvailableBlockRatio = 0.75`, then the target fullness is +/// _0.25 of the normal capacity_ and _0.1875 of the entire block_. +/// +/// This implementation implies the bound: +/// - `v ≤ p / k * (s − s')` +/// - or, solving for `p`: `p >= v * k * (s - s')` +/// +/// where `p` is the amount of change over `k` blocks. +/// +/// Hence: +/// - in a fully congested chain: `p >= v * k * (1 - s')`. +/// - in an empty chain: `p >= v * k * (-s')`. +/// +/// For example, when all blocks are full and there are 28800 blocks per day (default in `substrate-node`) +/// and v == 0.00001, s' == 0.1875, we'd have: +/// +/// p >= 0.00001 * 28800 * 0.8125 +/// p >= 0.234 +/// +/// Meaning that fees can change by around ~23% per day, given extreme congestion. +/// +/// More info can be found at: +/// https://w3f-research.readthedocs.io/en/latest/polkadot/Token%20Economics.html +pub struct TargetedFeeAdjustment(sp_std::marker::PhantomData<(T, S, V, M)>); + +impl Convert for TargetedFeeAdjustment + where T: frame_system::Trait, S: Get, V: Get, M: Get, +{ + fn convert(previous: Multiplier) -> Multiplier { + // Defensive only. The multiplier in storage should always be at most positive. Nonetheless + // we recover here in case of errors, because any value below this would be stale and can + // never change. + let min_multiplier = M::get(); + let previous = previous.max(min_multiplier); + + // the computed ratio is only among the normal class. + let normal_max_weight = + ::AvailableBlockRatio::get() * + ::MaximumBlockWeight::get(); + let normal_block_weight = + >::block_weight() + .get(frame_support::weights::DispatchClass::Normal) + .min(normal_max_weight); + + let s = S::get(); + let v = V::get(); + + let target_weight = (s * normal_max_weight) as u128; + let block_weight = normal_block_weight as u128; + + // determines if the first_term is positive + let positive = block_weight >= target_weight; + let diff_abs = block_weight.max(target_weight) - block_weight.min(target_weight); + + // defensive only, a test case assures that the maximum weight diff can fit in Multiplier + // without any saturation. + let diff = Multiplier::saturating_from_rational(diff_abs, normal_max_weight.max(1)); + let diff_squared = diff.saturating_mul(diff); + + let v_squared_2 = v.saturating_mul(v) / Multiplier::saturating_from_integer(2); + + let first_term = v.saturating_mul(diff); + let second_term = v_squared_2.saturating_mul(diff_squared); + + if positive { + let excess = first_term.saturating_add(second_term).saturating_mul(previous); + previous.saturating_add(excess).max(min_multiplier) + } else { + // Defensive-only: first_term > second_term. Safe subtraction. + let negative = first_term.saturating_sub(second_term).saturating_mul(previous); + previous.saturating_sub(negative).max(min_multiplier) + } + } +} + +/// Storage releases of the module. +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)] +enum Releases { + /// Original version of the module. + V1Ancient, + /// One that bumps the usage to FixedU128 from FixedI128. + V2, +} + +impl Default for Releases { + fn default() -> Self { + Releases::V1Ancient + } +} + pub trait Trait: frame_system::Trait { /// The currency type in which fees will be paid. type Currency: Currency + Send + Sync; @@ -87,7 +199,9 @@ pub trait Trait: frame_system::Trait { decl_storage! { trait Store for Module as TransactionPayment { - pub NextFeeMultiplier get(fn next_fee_multiplier): Multiplier = Multiplier::from_inner(0); + pub NextFeeMultiplier get(fn next_fee_multiplier): Multiplier = Multiplier::saturating_from_integer(1); + + StorageVersion build(|_: &GenesisConfig| Releases::V2): Releases; } } @@ -106,8 +220,17 @@ decl_module! { }); } - fn on_runtime_upgrade() -> Weight { - migration::on_runtime_upgrade::() + fn integrity_test() { + // given weight == u64, we build multipliers from `diff` of two weight values, which can + // at most be MaximumBlockWeight. Make sure that this can fit in a multiplier without + // loss. + use sp_std::convert::TryInto; + assert!( + ::max_value() >= + Multiplier::checked_from_integer( + ::MaximumBlockWeight::get().try_into().unwrap() + ).unwrap(), + ); } } } @@ -163,7 +286,7 @@ impl Module where /// the minimum fee for a transaction to be included in a block. /// /// ```ignore - /// inclusion_fee = base_fee + targeted_fee_adjustment * (len_fee + weight_fee); + /// inclusion_fee = base_fee + len_fee + [targeted_fee_adjustment * weight_fee]; /// final_fee = inclusion_fee + tip; /// ``` pub fn compute_fee( @@ -200,16 +323,21 @@ impl Module where if pays_fee == Pays::Yes { let len = >::from(len); let per_byte = T::TransactionByteFee::get(); - let len_fee = per_byte.saturating_mul(len); - let unadjusted_weight_fee = Self::weight_to_fee(weight); - // the adjustable part of the fee - let adjustable_fee = len_fee.saturating_add(unadjusted_weight_fee); - let targeted_fee_adjustment = NextFeeMultiplier::get(); - let adjusted_fee = targeted_fee_adjustment.saturating_mul_acc_int(adjustable_fee); + // length fee. this is not adjusted. + let fixed_len_fee = per_byte.saturating_mul(len); + + // the adjustable part of the fee. + let unadjusted_weight_fee = Self::weight_to_fee(weight); + let multiplier = Self::next_fee_multiplier(); + // final adjusted weight fee. + let adjusted_weight_fee = multiplier.saturating_mul_int(unadjusted_weight_fee); let base_fee = Self::weight_to_fee(T::ExtrinsicBaseWeight::get()); - base_fee.saturating_add(adjusted_fee).saturating_add(tip) + base_fee + .saturating_add(fixed_len_fee) + .saturating_add(adjusted_weight_fee) + .saturating_add(tip) } else { tip } @@ -219,12 +347,12 @@ impl Module where impl Module { /// Compute the fee for the specified weight. /// - /// This fee is already adjusted by the per block fee adjustment factor and is therefore - /// the share that the weight contributes to the overall fee of a transaction. + /// This fee is already adjusted by the per block fee adjustment factor and is therefore the + /// share that the weight contributes to the overall fee of a transaction. /// - /// This function is generic in order to supply the contracts module with a way - /// to calculate the gas price. The contracts module is not able to put the necessary - /// `BalanceOf` contraints on its trait. This function is not to be used by this module. + /// This function is generic in order to supply the contracts module with a way to calculate the + /// gas price. The contracts module is not able to put the necessary `BalanceOf` constraints + /// on its trait. This function is not to be used by this module. pub fn weight_to_fee_with_adjustment(weight: Weight) -> Balance where Balance: UniqueSaturatedFrom { @@ -434,6 +562,7 @@ mod tests { } impl frame_system::Trait for Runtime { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -455,7 +584,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = pallet_balances::AccountData; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } @@ -625,21 +755,21 @@ mod tests { .execute_with(|| { let len = 10; - NextFeeMultiplier::put(Multiplier::saturating_from_rational(1, 2)); + NextFeeMultiplier::put(Multiplier::saturating_from_rational(3, 2)); let pre = ChargeTransactionPayment::::from(5 /* tipped */) .pre_dispatch(&2, CALL, &info_from_weight(100), len) .unwrap(); - // 5 base fee, 3/2 * 10 byte fee, 3/2 * 100 weight fee, 5 tip - assert_eq!(Balances::free_balance(2), 200 - 5 - 15 - 150 - 5); + // 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 150 - 5); assert!( ChargeTransactionPayment:: ::post_dispatch(pre, &info_from_weight(100), &post_info_from_weight(50), len, &Ok(())) .is_ok() ); - // 75 (3/2 of the returned 50 units of weight ) is refunded - assert_eq!(Balances::free_balance(2), 200 - 5 - 15 - 75 - 5); + // 75 (3/2 of the returned 50 units of weight) is refunded + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 75 - 5); }); } @@ -713,7 +843,7 @@ mod tests { .execute_with(|| { // all fees should be x1.5 - NextFeeMultiplier::put(Multiplier::saturating_from_rational(1, 2)); + NextFeeMultiplier::put(Multiplier::saturating_from_rational(3, 2)); let len = 10; assert!( @@ -721,7 +851,14 @@ mod tests { .pre_dispatch(&1, CALL, &info_from_weight(3), len) .is_ok() ); - assert_eq!(Balances::free_balance(1), 100 - 10 - 5 - (10 + 3) * 3 / 2); + assert_eq!( + Balances::free_balance(1), + 100 // original + - 10 // tip + - 5 // base + - 10 // len + - (3 * 3 / 2) // adjusted weight + ); }) } @@ -741,7 +878,7 @@ mod tests { .execute_with(|| { // all fees should be x1.5 - NextFeeMultiplier::put(Multiplier::saturating_from_rational(1, 2)); + NextFeeMultiplier::put(Multiplier::saturating_from_rational(3, 2)); assert_eq!( TransactionPayment::query_info(xt, len), @@ -750,10 +887,8 @@ mod tests { class: info.class, partial_fee: 5 * 2 /* base * weight_fee */ - + ( - len as u64 /* len * 1 */ - + info.weight.min(MaximumBlockWeight::get()) as u64 * 2 /* weight * weight_to_fee */ - ) * 3 / 2 + + len as u64 /* len * 1 */ + + info.weight.min(MaximumBlockWeight::get()) as u64 * 2 * 3 / 2 /* weight */ }, ); @@ -770,7 +905,7 @@ mod tests { .execute_with(|| { // Next fee multiplier is zero - assert_eq!(NextFeeMultiplier::get(), Multiplier::saturating_from_integer(0)); + assert_eq!(NextFeeMultiplier::get(), Multiplier::one()); // Tip only, no fees works let dispatch_info = DispatchInfo { @@ -809,8 +944,8 @@ mod tests { .build() .execute_with(|| { - // Add a next fee multiplier - NextFeeMultiplier::put(Multiplier::saturating_from_rational(1, 2)); // = 1/2 = .5 + // Add a next fee multiplier. Fees will be x3/2. + NextFeeMultiplier::put(Multiplier::saturating_from_rational(3, 2)); // Base fee is unaffected by multiplier let dispatch_info = DispatchInfo { weight: 0, @@ -826,10 +961,10 @@ mod tests { pays_fee: Pays::Yes, }; // 123 weight, 456 length, 100 base - // adjustable fee = (123 * 1) + (456 * 10) = 4683 - // adjusted fee = (4683 * .5) + 4683 = 7024.5 -> 7024 - // final fee = 100 + 7024 + 789 tip = 7913 - assert_eq!(Module::::compute_fee(456, &dispatch_info, 789), 7913); + assert_eq!( + Module::::compute_fee(456, &dispatch_info, 789), + 100 + (3 * 123 / 2) + 4560 + 789, + ); }); } @@ -842,9 +977,10 @@ mod tests { .build() .execute_with(|| { - // Add a next fee multiplier - NextFeeMultiplier::put(Multiplier::saturating_from_rational(-1, 2)); // = -1/2 = -.5 - // Base fee is unaffected by multiplier + // Add a next fee multiplier. All fees will be x1/2. + NextFeeMultiplier::put(Multiplier::saturating_from_rational(1, 2)); + + // Base fee is unaffected by multiplier. let dispatch_info = DispatchInfo { weight: 0, class: DispatchClass::Operational, @@ -852,17 +988,17 @@ mod tests { }; assert_eq!(Module::::compute_fee(0, &dispatch_info, 0), 100); - // Everything works together :) + // Everything works together. let dispatch_info = DispatchInfo { weight: 123, class: DispatchClass::Operational, pays_fee: Pays::Yes, }; // 123 weight, 456 length, 100 base - // adjustable fee = (123 * 1) + (456 * 10) = 4683 - // adjusted fee = 4683 - (4683 * -.5) = 4683 - 2341.5 = 4683 - 2341 = 2342 - // final fee = 100 + 2342 + 789 tip = 3231 - assert_eq!(Module::::compute_fee(456, &dispatch_info, 789), 3231); + assert_eq!( + Module::::compute_fee(456, &dispatch_info, 789), + 100 + (123 / 2) + 4560 + 789, + ); }); } @@ -998,7 +1134,7 @@ mod tests { let len = 10; let tip = 5; - NextFeeMultiplier::put(Multiplier::saturating_from_rational(1, 4)); + NextFeeMultiplier::put(Multiplier::saturating_from_rational(5, 4)); let pre = ChargeTransactionPayment::::from(tip) .pre_dispatch(&2, CALL, &info, len) @@ -1012,11 +1148,8 @@ mod tests { let actual_fee = Module:: ::compute_actual_fee(len as u32, &info, &post_info, tip); - // 33 weight, 10 length, 7 base - // adjustable fee = (33 * 1) + (10 * 1) = 43 - // adjusted fee = 43 + (43 * .25) = 43 + 10.75 = 43 + 10 = 53 - // final fee = 7 + 53 + 5 tip = 65 - assert_eq!(actual_fee, 65); + // 33 weight, 10 length, 7 base, 5 tip + assert_eq!(actual_fee, 7 + 10 + (33 * 5 / 4) + 5); assert_eq!(refund_based_fee, actual_fee); }); } diff --git a/frame/transaction-payment/src/migration.rs b/frame/transaction-payment/src/migration.rs index 7b937bdc7f9b8..82592078cced3 100644 --- a/frame/transaction-payment/src/migration.rs +++ b/frame/transaction-payment/src/migration.rs @@ -44,8 +44,7 @@ fn rename_and_convert() -> Weight { let raw_multiplier = next_fee_multiplier.into_inner() as i128; // Fixed64 used 10^9 precision, where Fixed128 uses 10^18, so we need to add 9 zeros. let new_raw_multiplier: i128 = raw_multiplier.saturating_mul(1_000_000_000); - let mult = Multiplier::from(new_raw_multiplier); - put_storage_value(b"TransactionPayment", b"NextFeeMultiplier", &[], mult); + put_storage_value(b"TransactionPayment", b"NextFeeMultiplier", &[], new_raw_multiplier); writes += 2; } reads += 1; diff --git a/frame/treasury/Cargo.toml b/frame/treasury/Cargo.toml index 4c0aae3713e1b..338a6f1dec43a 100644 --- a/frame/treasury/Cargo.toml +++ b/frame/treasury/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 62555adffe357..e643a79203848 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -102,7 +102,7 @@ use sp_runtime::{Permill, ModuleId, Percent, RuntimeDebug, traits::{ use frame_support::weights::{Weight, DispatchClass}; use frame_support::traits::{Contains, ContainsLengthBound, EnsureOrigin}; use codec::{Encode, Decode}; -use frame_system::{self as system, ensure_signed, ensure_root}; +use frame_system::{self as system, ensure_signed}; mod tests; mod benchmarking; @@ -378,9 +378,7 @@ decl_module! { /// # #[weight = (130_000_000 + T::DbWeight::get().reads_writes(2, 2), DispatchClass::Operational)] fn reject_proposal(origin, #[compact] proposal_id: ProposalIndex) { - T::RejectOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::RejectOrigin::ensure_origin(origin)?; let proposal = >::take(&proposal_id).ok_or(Error::::InvalidProposalIndex)?; let value = proposal.bond; @@ -400,9 +398,7 @@ decl_module! { /// # #[weight = (34_000_000 + T::DbWeight::get().reads_writes(2, 1), DispatchClass::Operational)] fn approve_proposal(origin, #[compact] proposal_id: ProposalIndex) { - T::ApproveOrigin::try_origin(origin) - .map(|_| ()) - .or_else(ensure_root)?; + T::ApproveOrigin::ensure_origin(origin)?; ensure!(>::contains_key(proposal_id), Error::::InvalidProposalIndex); Approvals::mutate(|v| v.push(proposal_id)); diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index 95303b9241cf0..82fbd4ad6938a 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -60,6 +60,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -272,7 +273,7 @@ fn close_tip_works() { assert_noop!(Treasury::close_tip(Origin::signed(0), h.into()), Error::::Premature); System::set_block_number(2); - assert_noop!(Treasury::close_tip(Origin::NONE, h.into()), BadOrigin); + assert_noop!(Treasury::close_tip(Origin::none(), h.into()), BadOrigin); assert_ok!(Treasury::close_tip(Origin::signed(0), h.into())); assert_eq!(Balances::free_balance(3), 10); @@ -382,7 +383,7 @@ fn accepted_spend_proposal_ignored_outside_spend_period() { Balances::make_free_balance_be(&Treasury::account_id(), 101); assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + assert_ok!(Treasury::approve_proposal(Origin::root(), 0)); >::on_initialize(1); assert_eq!(Balances::free_balance(3), 0); @@ -409,7 +410,7 @@ fn rejected_spend_proposal_ignored_on_spend_period() { Balances::make_free_balance_be(&Treasury::account_id(), 101); assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); + assert_ok!(Treasury::reject_proposal(Origin::root(), 0)); >::on_initialize(2); assert_eq!(Balances::free_balance(3), 0); @@ -423,22 +424,22 @@ fn reject_already_rejected_spend_proposal_fails() { Balances::make_free_balance_be(&Treasury::account_id(), 101); assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); - assert_noop!(Treasury::reject_proposal(Origin::ROOT, 0), Error::::InvalidProposalIndex); + assert_ok!(Treasury::reject_proposal(Origin::root(), 0)); + assert_noop!(Treasury::reject_proposal(Origin::root(), 0), Error::::InvalidProposalIndex); }); } #[test] fn reject_non_existent_spend_proposal_fails() { new_test_ext().execute_with(|| { - assert_noop!(Treasury::reject_proposal(Origin::ROOT, 0), Error::::InvalidProposalIndex); + assert_noop!(Treasury::reject_proposal(Origin::root(), 0), Error::::InvalidProposalIndex); }); } #[test] fn accept_non_existent_spend_proposal_fails() { new_test_ext().execute_with(|| { - assert_noop!(Treasury::approve_proposal(Origin::ROOT, 0), Error::::InvalidProposalIndex); + assert_noop!(Treasury::approve_proposal(Origin::root(), 0), Error::::InvalidProposalIndex); }); } @@ -448,8 +449,8 @@ fn accept_already_rejected_spend_proposal_fails() { Balances::make_free_balance_be(&Treasury::account_id(), 101); assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); - assert_noop!(Treasury::approve_proposal(Origin::ROOT, 0), Error::::InvalidProposalIndex); + assert_ok!(Treasury::reject_proposal(Origin::root(), 0)); + assert_noop!(Treasury::approve_proposal(Origin::root(), 0), Error::::InvalidProposalIndex); }); } @@ -460,7 +461,7 @@ fn accepted_spend_proposal_enacted_on_spend_period() { assert_eq!(Treasury::pot(), 100); assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + assert_ok!(Treasury::approve_proposal(Origin::root(), 0)); >::on_initialize(2); assert_eq!(Balances::free_balance(3), 100); @@ -475,7 +476,7 @@ fn pot_underflow_should_not_diminish() { assert_eq!(Treasury::pot(), 100); assert_ok!(Treasury::propose_spend(Origin::signed(0), 150, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + assert_ok!(Treasury::approve_proposal(Origin::root(), 0)); >::on_initialize(2); assert_eq!(Treasury::pot(), 100); // Pot hasn't changed @@ -497,13 +498,13 @@ fn treasury_account_doesnt_get_deleted() { let treasury_balance = Balances::free_balance(&Treasury::account_id()); assert_ok!(Treasury::propose_spend(Origin::signed(0), treasury_balance, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + assert_ok!(Treasury::approve_proposal(Origin::root(), 0)); >::on_initialize(2); assert_eq!(Treasury::pot(), 100); // Pot hasn't changed assert_ok!(Treasury::propose_spend(Origin::signed(0), Treasury::pot(), 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + assert_ok!(Treasury::approve_proposal(Origin::root(), 1)); >::on_initialize(4); assert_eq!(Treasury::pot(), 0); // Pot is emptied @@ -527,9 +528,9 @@ fn inexistent_account_works() { assert_eq!(Treasury::pot(), 0); // Pot is empty assert_ok!(Treasury::propose_spend(Origin::signed(0), 99, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + assert_ok!(Treasury::approve_proposal(Origin::root(), 0)); assert_ok!(Treasury::propose_spend(Origin::signed(0), 1, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + assert_ok!(Treasury::approve_proposal(Origin::root(), 1)); >::on_initialize(2); assert_eq!(Treasury::pot(), 0); // Pot hasn't changed assert_eq!(Balances::free_balance(3), 0); // Balance of `3` hasn't changed diff --git a/frame/utility/Cargo.toml b/frame/utility/Cargo.toml index 65eae9d4cc774..f14274d709ea1 100644 --- a/frame/utility/Cargo.toml +++ b/frame/utility/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } frame-support = { version = "2.0.0-rc3", default-features = false, path = "../support" } frame-system = { version = "2.0.0-rc3", default-features = false, path = "../system" } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } diff --git a/frame/utility/src/lib.rs b/frame/utility/src/lib.rs index 34385b67864c5..3759a2afcd814 100644 --- a/frame/utility/src/lib.rs +++ b/frame/utility/src/lib.rs @@ -54,9 +54,10 @@ use sp_std::prelude::*; use codec::{Encode, Decode}; use sp_core::TypeId; use sp_io::hashing::blake2_256; -use frame_support::{decl_module, decl_event, decl_error, decl_storage, Parameter, ensure}; -use frame_support::{traits::{Filter, FilterStack, ClearFilterGuard}, - weights::{Weight, GetDispatchInfo, DispatchClass, FunctionOf, Pays}, dispatch::PostDispatchInfo, +use frame_support::{decl_module, decl_event, decl_storage, Parameter}; +use frame_support::{ + traits::{OriginTrait, UnfilteredDispatchable}, + weights::{Weight, GetDispatchInfo, DispatchClass}, dispatch::PostDispatchInfo, }; use frame_system::{self as system, ensure_signed, ensure_root}; use sp_runtime::{DispatchError, DispatchResult, traits::Dispatchable}; @@ -71,23 +72,14 @@ pub trait Trait: frame_system::Trait { /// The overarching call type. type Call: Parameter + Dispatchable - + GetDispatchInfo + From>; - - /// Is a given call compatible with the proxying subsystem? - type IsCallable: FilterStack<::Call>; + + GetDispatchInfo + From> + + UnfilteredDispatchable; } decl_storage! { trait Store for Module as Utility {} } -decl_error! { - pub enum Error for Module { - /// A call with a `false` `IsCallable` filter was attempted. - Uncallable, - } -} - decl_event! { /// Events type. pub enum Event { @@ -96,8 +88,6 @@ decl_event! { BatchInterrupted(u32, DispatchError), /// Batch of dispatches completed fully with no error. BatchCompleted, - /// A call with a `false` IsCallable filter was attempted. - Uncallable(u32), } } @@ -111,20 +101,18 @@ impl TypeId for IndexedUtilityModuleId { decl_module! { pub struct Module for enum Call where origin: T::Origin { - type Error = Error; - /// Deposit one of this module's events by using the default implementation. fn deposit_event() = default; /// Send a batch of dispatch calls. /// - /// This will execute until the first one fails and then stop. Calls must fulfil the - /// `IsCallable` filter unless the origin is `Root`. - /// /// May be called from any origin. /// /// - `calls`: The calls to be dispatched from the same origin. /// + /// If origin is root then call are dispatch without checking origin filter. (This includes + /// bypassing `frame_system::Trait::BaseCallFilter`). + /// /// # /// - Base weight: 14.39 + .987 * c µs /// - Plus the sum of the weights of the `calls`. @@ -136,14 +124,12 @@ decl_module! { /// `BatchInterrupted` event is deposited, along with the number of successful calls made /// and the error of the failed call. If all were successful, then the `BatchCompleted` /// event is deposited. - #[weight = FunctionOf( - |args: (&Vec<::Call>,)| { - args.0.iter() - .map(|call| call.get_dispatch_info().weight) - .fold(15_000_000, |a: Weight, n| a.saturating_add(n).saturating_add(1_000_000)) - }, - |args: (&Vec<::Call>,)| { - let all_operational = args.0.iter() + #[weight = ( + calls.iter() + .map(|call| call.get_dispatch_info().weight) + .fold(15_000_000, |a: Weight, n| a.saturating_add(n).saturating_add(1_000_000)), + { + let all_operational = calls.iter() .map(|call| call.get_dispatch_info().class) .all(|class| class == DispatchClass::Operational); if all_operational { @@ -152,16 +138,15 @@ decl_module! { DispatchClass::Normal } }, - Pays::Yes, )] fn batch(origin, calls: Vec<::Call>) { let is_root = ensure_root(origin.clone()).is_ok(); for (index, call) in calls.into_iter().enumerate() { - if !is_root && !T::IsCallable::filter(&call) { - Self::deposit_event(Event::Uncallable(index as u32)); - return Ok(()) - } - let result = call.dispatch(origin.clone()); + let result = if is_root { + call.dispatch_bypass_filter(origin.clone()) + } else { + call.dispatch(origin.clone()) + }; if let Err(e) = result { Self::deposit_event(Event::BatchInterrupted(index as u32, e.error)); return Ok(()); @@ -172,9 +157,6 @@ decl_module! { /// Send a call through an indexed pseudonym of the sender. /// - /// The call must fulfil only the pre-cleared `IsCallable` filter (i.e. only the level of - /// filtering that remains after calling `take()`). - /// /// NOTE: If you need to ensure that any account-based filtering is honored (i.e. because /// you expect `proxy` to have been used prior in the call stack and you want it to apply to /// any sub-accounts), then use `as_limited_sub` instead. @@ -185,19 +167,14 @@ decl_module! { /// - Base weight: 2.861 µs /// - Plus the weight of the `call` /// # - #[weight = FunctionOf( - |args: (&u16, &Box<::Call>)| { - args.1.get_dispatch_info().weight.saturating_add(3_000_000) - }, - |args: (&u16, &Box<::Call>)| args.1.get_dispatch_info().class, - Pays::Yes, + #[weight = ( + call.get_dispatch_info().weight.saturating_add(3_000_000), + call.get_dispatch_info().class, )] fn as_sub(origin, index: u16, call: Box<::Call>) -> DispatchResult { let who = ensure_signed(origin)?; - // We're now executing as a freshly authenticated new account, so the previous call - // restrictions no longer apply. - let _guard = ClearFilterGuard::::Call>::new(); - ensure!(T::IsCallable::filter(&call), Error::::Uncallable); + + // This is a freshly authenticated new account, the origin restrictions doesn't apply. let pseudonym = Self::sub_account_id(who, index); call.dispatch(frame_system::RawOrigin::Signed(pseudonym).into()) .map(|_| ()).map_err(|e| e.error) @@ -205,7 +182,8 @@ decl_module! { /// Send a call through an indexed pseudonym of the sender. /// - /// Calls must each fulfil the `IsCallable` filter; it is not cleared before. + /// Filter from origin are passed along. The call will be dispatched with an origin which + /// use the same filter as the origin of this call. /// /// NOTE: If you need to ensure that any account-based filtering is not honored (i.e. /// because you expect `proxy` to have been used prior in the call stack and you do not want @@ -217,19 +195,16 @@ decl_module! { /// - Base weight: 2.861 µs /// - Plus the weight of the `call` /// # - #[weight = FunctionOf( - |args: (&u16, &Box<::Call>)| { - args.1.get_dispatch_info().weight.saturating_add(3_000_000) - }, - |args: (&u16, &Box<::Call>)| args.1.get_dispatch_info().class, - Pays::Yes, + #[weight = ( + call.get_dispatch_info().weight.saturating_add(3_000_000), + call.get_dispatch_info().class, )] fn as_limited_sub(origin, index: u16, call: Box<::Call>) -> DispatchResult { - let who = ensure_signed(origin)?; - ensure!(T::IsCallable::filter(&call), Error::::Uncallable); + let mut origin = origin; + let who = ensure_signed(origin.clone())?; let pseudonym = Self::sub_account_id(who, index); - call.dispatch(frame_system::RawOrigin::Signed(pseudonym).into()) - .map(|_| ()).map_err(|e| e.error) + origin.set_caller_from(frame_system::RawOrigin::Signed(pseudonym)); + call.dispatch(origin).map(|_| ()).map_err(|e| e.error) } } } diff --git a/frame/utility/src/tests.rs b/frame/utility/src/tests.rs index 6fb4ad26bfcae..0656b50b614d0 100644 --- a/frame/utility/src/tests.rs +++ b/frame/utility/src/tests.rs @@ -23,7 +23,7 @@ use super::*; use frame_support::{ assert_ok, assert_noop, impl_outer_origin, parameter_types, impl_outer_dispatch, - weights::Weight, impl_outer_event + weights::Weight, impl_outer_event, dispatch::DispatchError, traits::Filter, storage, }; use sp_core::H256; use sp_runtime::{Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header}; @@ -59,6 +59,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = TestBaseCallFilter; type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -99,8 +100,8 @@ parameter_types! { pub const MultisigDepositFactor: u64 = 1; pub const MaxSignatories: u16 = 3; } -pub struct TestIsCallable; -impl Filter for TestIsCallable { +pub struct TestBaseCallFilter; +impl Filter for TestBaseCallFilter { fn filter(c: &Call) -> bool { match *c { Call::Balances(_) => true, @@ -108,17 +109,9 @@ impl Filter for TestIsCallable { } } } -impl FilterStack for TestIsCallable { - type Stack = (); - fn push(_: impl Fn(&Call) -> bool + 'static) {} - fn pop() {} - fn take() -> Self::Stack { () } - fn restore(_: Self::Stack) {} -} impl Trait for Test { type Event = TestEvent; type Call = Call; - type IsCallable = TestIsCallable; } type System = frame_system::Module; type Balances = pallet_balances::Module; @@ -172,21 +165,26 @@ fn as_sub_filters() { Origin::signed(1), 1, Box::new(Call::System(frame_system::Call::remark(vec![]))), - ), Error::::Uncallable); + ), DispatchError::BadOrigin); }); } #[test] fn batch_with_root_works() { new_test_ext().execute_with(|| { + let k = b"a".to_vec(); + let call = Call::System(frame_system::Call::set_storage(vec![(k.clone(), k.clone())])); + assert!(!TestBaseCallFilter::filter(&call)); assert_eq!(Balances::free_balance(1), 10); assert_eq!(Balances::free_balance(2), 10); - assert_ok!(Utility::batch(Origin::ROOT, vec![ + assert_ok!(Utility::batch(Origin::root(), vec![ + Call::Balances(BalancesCall::force_transfer(1, 2, 5)), Call::Balances(BalancesCall::force_transfer(1, 2, 5)), - Call::Balances(BalancesCall::force_transfer(1, 2, 5)) + call, // Check filters are correctly bypassed ])); assert_eq!(Balances::free_balance(1), 0); assert_eq!(Balances::free_balance(2), 20); + assert_eq!(storage::unhashed::get_raw(&k), Some(k)); }); } @@ -214,7 +212,7 @@ fn batch_with_signed_filters() { Call::System(frame_system::Call::remark(vec![])) ]), ); - expect_event(Event::Uncallable(0)); + expect_event(Event::BatchInterrupted(0, DispatchError::BadOrigin)); }); } diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index 885768e3657fc..a98a59acef1cf 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } enumflags2 = { version = "0.6.2" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index 16e6b0cf5fc36..8ed1bde542168 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -58,7 +58,7 @@ use frame_support::traits::{ Currency, LockableCurrency, VestingSchedule, WithdrawReason, LockIdentifier, ExistenceRequirement, Get, MigrateAccount, }; -use frame_system::{self as system, ensure_signed}; +use frame_system::{self as system, ensure_signed, ensure_root}; mod benchmarking; @@ -266,6 +266,47 @@ decl_module! { Ok(()) } + + /// Force a vested transfer. + /// + /// The dispatch origin for this call must be _Root_. + /// + /// - `source`: The account whose funds should be transferred. + /// - `target`: The account that should be transferred the vested funds. + /// - `amount`: The amount of funds to transfer and will be vested. + /// - `schedule`: The vesting schedule attached to the transfer. + /// + /// Emits `VestingCreated`. + /// + /// # + /// - `O(1)`. + /// - DbWeight: 4 Reads, 4 Writes + /// - Reads: Vesting Storage, Balances Locks, Target Account, Source Account + /// - Writes: Vesting Storage, Balances Locks, Target Account, Source Account + /// - Benchmark: 100.3 + .365 * l µs (min square analysis) + /// - Using 100 µs fixed. Assuming less than 50 locks on any user, else we may want factor in number of locks. + /// # + #[weight = 100_000_000 + T::DbWeight::get().reads_writes(4, 4)] + pub fn force_vested_transfer( + origin, + source: ::Source, + target: ::Source, + schedule: VestingInfo, T::BlockNumber>, + ) -> DispatchResult { + ensure_root(origin)?; + ensure!(schedule.locked >= T::MinVestedTransfer::get(), Error::::AmountLow); + + let target = T::Lookup::lookup(target)?; + let source = T::Lookup::lookup(source)?; + ensure!(!Vesting::::contains_key(&target), Error::::ExistingVestingSchedule); + + T::Currency::transfer(&source, &target, schedule.locked, ExistenceRequirement::AllowDeath)?; + + Self::add_vesting_schedule(&target, schedule.locked, schedule.per_block, schedule.starting_block) + .expect("user does not have an existing vesting schedule; q.e.d."); + + Ok(()) + } } } @@ -367,8 +408,9 @@ mod tests { use sp_runtime::{ Perbill, testing::Header, - traits::{BlakeTwo256, IdentityLookup, Identity}, + traits::{BlakeTwo256, IdentityLookup, Identity, BadOrigin}, }; + use frame_system::RawOrigin; impl_outer_origin! { pub enum Origin for Test where system = frame_system {} @@ -386,6 +428,7 @@ mod tests { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Trait for Test { + type BaseCallFilter = (); type Origin = Origin; type Index = u64; type BlockNumber = u64; @@ -407,7 +450,8 @@ mod tests { type Version = (); type ModuleToIndex = (); type AccountData = pallet_balances::AccountData; - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } impl pallet_balances::Trait for Test { @@ -723,4 +767,94 @@ mod tests { assert_eq!(user4_free_balance, 256 * 40); }); } + + #[test] + fn force_vested_transfer_works() { + ExtBuilder::default() + .existential_deposit(256) + .build() + .execute_with(|| { + let user3_free_balance = Balances::free_balance(&3); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user3_free_balance, 256 * 30); + assert_eq!(user4_free_balance, 256 * 40); + // Account 4 should not have any vesting yet. + assert_eq!(Vesting::vesting(&4), None); + // Make the schedule for the new transfer. + let new_vesting_schedule = VestingInfo { + locked: 256 * 5, + per_block: 64, // Vesting over 20 blocks + starting_block: 10, + }; + assert_noop!(Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule), BadOrigin); + assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, new_vesting_schedule)); + // Now account 4 should have vesting. + assert_eq!(Vesting::vesting(&4), Some(new_vesting_schedule)); + // Ensure the transfer happened correctly. + let user3_free_balance_updated = Balances::free_balance(&3); + assert_eq!(user3_free_balance_updated, 256 * 25); + let user4_free_balance_updated = Balances::free_balance(&4); + assert_eq!(user4_free_balance_updated, 256 * 45); + // Account 4 has 5 * 256 locked. + assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + + System::set_block_number(20); + assert_eq!(System::block_number(), 20); + + // Account 4 has 5 * 64 units vested by block 20. + assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64)); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + // Account 4 has fully vested. + assert_eq!(Vesting::vesting_balance(&4), Some(0)); + }); + } + + #[test] + fn force_vested_transfer_correctly_fails() { + ExtBuilder::default() + .existential_deposit(256) + .build() + .execute_with(|| { + let user2_free_balance = Balances::free_balance(&2); + let user4_free_balance = Balances::free_balance(&4); + assert_eq!(user2_free_balance, 256 * 20); + assert_eq!(user4_free_balance, 256 * 40); + // Account 2 should already have a vesting schedule. + let user2_vesting_schedule = VestingInfo { + locked: 256 * 20, + per_block: 256, // Vesting over 20 blocks + starting_block: 10, + }; + assert_eq!(Vesting::vesting(&2), Some(user2_vesting_schedule)); + + // The vesting schedule we will try to create, fails due to pre-existence of schedule. + let new_vesting_schedule = VestingInfo { + locked: 256 * 5, + per_block: 64, // Vesting over 20 blocks + starting_block: 10, + }; + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 4, 2, new_vesting_schedule), + Error::::ExistingVestingSchedule, + ); + + // Fails due to too low transfer amount. + let new_vesting_schedule_too_low = VestingInfo { + locked: 256 * 1, + per_block: 64, + starting_block: 10, + }; + assert_noop!( + Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, new_vesting_schedule_too_low), + Error::::AmountLow, + ); + + // Verify no currency transfer happened. + assert_eq!(user2_free_balance, 256 * 20); + assert_eq!(user4_free_balance, 256 * 40); + }); + } } diff --git a/primitives/api/Cargo.toml b/primitives/api/Cargo.toml index 6e465869753dc..46bd9164ac8f7 100644 --- a/primitives/api/Cargo.toml +++ b/primitives/api/Cargo.toml @@ -12,7 +12,7 @@ description = "Substrate runtime api primitives" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } sp-api-proc-macro = { version = "2.0.0-rc3", path = "proc-macro" } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../core" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } diff --git a/primitives/api/proc-macro/src/decl_runtime_apis.rs b/primitives/api/proc-macro/src/decl_runtime_apis.rs index 7e1391b7b57bd..93ec09d0e615b 100644 --- a/primitives/api/proc-macro/src/decl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/decl_runtime_apis.rs @@ -191,7 +191,8 @@ fn generate_native_call_generators(decl: &ItemTrait) -> Result { input: &I, error_desc: &'static str, ) -> std::result::Result { - ::decode( + ::decode_with_depth_limit( + #crate_::MAX_EXTRINSIC_DEPTH, &mut &#crate_::Encode::encode(input)[..], ).map_err(|e| format!("{} {}", error_desc, e.what())) } diff --git a/primitives/api/proc-macro/src/impl_runtime_apis.rs b/primitives/api/proc-macro/src/impl_runtime_apis.rs index 2878bd2c13683..4b5c1c4706939 100644 --- a/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -34,7 +34,7 @@ use syn::{ fold::{self, Fold}, parse_quote, }; -use std::{collections::HashSet, iter}; +use std::collections::HashSet; /// Unique identifier used to make the hidden includes unique for this macro. const HIDDEN_INCLUDES_ID: &str = "IMPL_RUNTIME_APIS"; @@ -71,10 +71,8 @@ fn generate_impl_call( let params = extract_parameter_names_types_and_borrows(signature, AllowSelfRefInParameters::No)?; let c = generate_crate_access(HIDDEN_INCLUDES_ID); - let c_iter = iter::repeat(&c); let fn_name = &signature.ident; - let fn_name_str = iter::repeat(fn_name.to_string()); - let input = iter::repeat(input); + let fn_name_str = fn_name.to_string(); let pnames = params.iter().map(|v| &v.0); let pnames2 = params.iter().map(|v| &v.0); let ptypes = params.iter().map(|v| &v.1); @@ -82,12 +80,14 @@ fn generate_impl_call( Ok( quote!( - #( - let #pnames : #ptypes = match #c_iter::Decode::decode(&mut #input) { - Ok(input) => input, + let (#( #pnames ),*) : ( #( #ptypes ),* ) = + match #c::DecodeLimit::decode_all_with_depth_limit( + #c::MAX_EXTRINSIC_DEPTH, + &mut #input, + ) { + Ok(res) => res, Err(e) => panic!("Bad input data provided to {}: {}", #fn_name_str, e.what()), }; - )* #[allow(deprecated)] <#runtime as #impl_trait>::#fn_name(#( #pborrow #pnames2 ),*) @@ -135,7 +135,7 @@ fn generate_impl_calls( /// Generate the dispatch function that is used in native to call into the runtime. fn generate_dispatch_function(impls: &[ItemImpl]) -> Result { - let data = Ident::new("data", Span::call_site()); + let data = Ident::new("__sp_api__input_data", Span::call_site()); let c = generate_crate_access(HIDDEN_INCLUDES_ID); let impl_calls = generate_impl_calls(impls, &data)? .into_iter() @@ -257,6 +257,7 @@ fn generate_runtime_api_base_structures() -> Result { &self, map_call: F, ) -> std::result::Result where Self: Sized { + self.changes.borrow_mut().start_transaction(); *self.commit_on_success.borrow_mut() = false; let res = map_call(self); *self.commit_on_success.borrow_mut() = true; @@ -366,6 +367,9 @@ fn generate_runtime_api_base_structures() -> Result { &self, call_api_at: F, ) -> std::result::Result<#crate_::NativeOrEncoded, E> { + if *self.commit_on_success.borrow() { + self.changes.borrow_mut().start_transaction(); + } let res = call_api_at( &self.call, self, @@ -381,11 +385,16 @@ fn generate_runtime_api_base_structures() -> Result { } fn commit_on_ok(&self, res: &std::result::Result) { + let proof = "\ + We only close a transaction when we opened one ourself. + Other parts of the runtime that make use of transactions (state-machine) + also balance their transactions. The runtime cannot close client initiated + transactions. qed"; if *self.commit_on_success.borrow() { if res.is_err() { - self.changes.borrow_mut().discard_prospective(); + self.changes.borrow_mut().rollback_transaction().expect(proof); } else { - self.changes.borrow_mut().commit_prospective(); + self.changes.borrow_mut().commit_transaction().expect(proof); } } } diff --git a/primitives/api/src/lib.rs b/primitives/api/src/lib.rs index ec15c1eae71d0..0aaf72e2d2b24 100644 --- a/primitives/api/src/lib.rs +++ b/primitives/api/src/lib.rs @@ -69,11 +69,14 @@ pub use sp_std::{slice, mem}; #[cfg(feature = "std")] use sp_std::result; #[doc(hidden)] -pub use codec::{Encode, Decode}; +pub use codec::{Encode, Decode, DecodeLimit}; use sp_core::OpaqueMetadata; #[cfg(feature = "std")] use std::{panic::UnwindSafe, cell::RefCell}; +/// Maximum nesting level for extrinsics. +pub const MAX_EXTRINSIC_DEPTH: u32 = 256; + /// Declares given traits as runtime apis. /// /// The macro will create two declarations, one for using on the client side and one for using diff --git a/primitives/api/test/Cargo.toml b/primitives/api/test/Cargo.toml index 79bd37c826506..04181d93f0910 100644 --- a/primitives/api/test/Cargo.toml +++ b/primitives/api/test/Cargo.toml @@ -19,7 +19,7 @@ sp-runtime = { version = "2.0.0-rc3", path = "../../runtime" } sp-blockchain = { version = "2.0.0-rc3", path = "../../blockchain" } sp-consensus = { version = "0.8.0-rc3", path = "../../../primitives/consensus/common" } sc-block-builder = { version = "0.8.0-rc3", path = "../../../client/block-builder" } -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } sp-state-machine = { version = "0.8.0-rc3", path = "../../../primitives/state-machine" } trybuild = "1.0.17" rustversion = "1.0.0" diff --git a/primitives/api/test/tests/runtime_calls.rs b/primitives/api/test/tests/runtime_calls.rs index 555104446ae2e..6717ab7a3bb92 100644 --- a/primitives/api/test/tests/runtime_calls.rs +++ b/primitives/api/test/tests/runtime_calls.rs @@ -207,3 +207,14 @@ fn record_proof_works() { &runtime_code, ).expect("Executes block while using the proof backend"); } + +#[test] +fn call_runtime_api_with_multiple_arguments() { + let client = TestClientBuilder::new().set_execution_strategy(ExecutionStrategy::Both).build(); + + let data = vec![1, 2, 4, 5, 6, 7, 8, 8, 10, 12]; + let block_id = BlockId::Number(client.chain_info().best_number); + client.runtime_api() + .test_multiple_arguments(&block_id, data.clone(), data.clone(), data.len() as u32) + .unwrap(); +} diff --git a/primitives/api/test/tests/ui/mock_only_one_error_type.stderr b/primitives/api/test/tests/ui/mock_only_one_error_type.stderr index b190c2134fafa..65d05e83a7f69 100644 --- a/primitives/api/test/tests/ui/mock_only_one_error_type.stderr +++ b/primitives/api/test/tests/ui/mock_only_one_error_type.stderr @@ -5,21 +5,26 @@ error: Error type can not change between runtime apis | ^^^^ error[E0277]: the trait bound `u32: std::convert::From` is not satisfied - --> $DIR/mock_only_one_error_type.rs:15:1 - | -15 | / sp_api::mock_impl_runtime_apis! { -16 | | impl Api for MockApi { -17 | | type Error = u32; -18 | | -... | -26 | | } -27 | | } - | |_^ the trait `std::convert::From` is not implemented for `u32` - | - = help: the following implementations were found: - > - > - > - > - and 18 others - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + --> $DIR/mock_only_one_error_type.rs:15:1 + | +15 | / sp_api::mock_impl_runtime_apis! { +16 | | impl Api for MockApi { +17 | | type Error = u32; +18 | | +... | +26 | | } +27 | | } + | |_^ the trait `std::convert::From` is not implemented for `u32` + | + ::: $WORKSPACE/primitives/api/src/lib.rs:350:35 + | +350 | type Error: std::fmt::Debug + From; + | ------------ required by this bound in `sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ApiErrorExt` + | + = help: the following implementations were found: + > + > + > + > + and 18 others + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/primitives/application-crypto/Cargo.toml b/primitives/application-crypto/Cargo.toml index ebc716cd729e2..29f385a54a13f 100644 --- a/primitives/application-crypto/Cargo.toml +++ b/primitives/application-crypto/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-core = { version = "2.0.0-rc3", default-features = false, path = "../core" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/io" } diff --git a/primitives/arithmetic/Cargo.toml b/primitives/arithmetic/Cargo.toml index 0912d6a69ea3b..b4c655c968d9d 100644 --- a/primitives/arithmetic/Cargo.toml +++ b/primitives/arithmetic/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } integer-sqrt = "0.1.2" num-traits = { version = "0.2.8", default-features = false } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } diff --git a/primitives/arithmetic/src/fixed_point.rs b/primitives/arithmetic/src/fixed_point.rs index 55581ff54cece..8653ee2c8f7c8 100644 --- a/primitives/arithmetic/src/fixed_point.rs +++ b/primitives/arithmetic/src/fixed_point.rs @@ -214,12 +214,12 @@ pub trait FixedPointNumber: self.into_inner() == Self::Inner::one() } - /// Checks if the number is positive. + /// Returns `true` if `self` is positive and `false` if the number is zero or negative. fn is_positive(self) -> bool { - self.into_inner() >= Self::Inner::zero() + self.into_inner() > Self::Inner::zero() } - /// Checks if the number is negative. + /// Returns `true` if `self` is negative and `false` if the number is zero or positive. fn is_negative(self) -> bool { self.into_inner() < Self::Inner::zero() } @@ -372,6 +372,23 @@ macro_rules! implement_fixed { } } + impl $name { + /// const version of `FixedPointNumber::from_inner`. + pub const fn from_inner(inner: $inner_type) -> Self { + Self(inner) + } + + #[cfg(any(feature = "std", test))] + pub fn from_fraction(x: f64) -> Self { + Self((x * (::DIV as f64)) as $inner_type) + } + + #[cfg(any(feature = "std", test))] + pub fn to_fraction(self) -> f64 { + self.0 as f64 / ::DIV as f64 + } + } + impl Saturating for $name { fn saturating_add(self, rhs: Self) -> Self { Self(self.0.saturating_add(rhs.0)) @@ -1376,6 +1393,23 @@ macro_rules! implement_fixed { assert_eq!(d.checked_div(&$name::zero()), None); } + #[test] + fn is_positive_negative_works() { + let one = $name::one(); + assert!(one.is_positive()); + assert!(!one.is_negative()); + + let zero = $name::zero(); + assert!(!zero.is_positive()); + assert!(!zero.is_negative()); + + if $signed { + let minus_one = $name::saturating_from_integer(-1); + assert!(minus_one.is_negative()); + assert!(!minus_one.is_positive()); + } + } + #[test] fn trunc_works() { let n = $name::saturating_from_rational(5, 2).trunc(); diff --git a/primitives/authority-discovery/Cargo.toml b/primitives/authority-discovery/Cargo.toml index 4201cd342bd5b..584aef986a7fb 100644 --- a/primitives/authority-discovery/Cargo.toml +++ b/primitives/authority-discovery/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path = "../application-crypto" } -codec = { package = "parity-scale-codec", default-features = false, version = "1.3.0" } +codec = { package = "parity-scale-codec", default-features = false, version = "1.3.1" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } sp-api = { version = "2.0.0-rc3", default-features = false, path = "../api" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../runtime" } diff --git a/primitives/authorship/Cargo.toml b/primitives/authorship/Cargo.toml index 4ca6f06207c1b..eb52ca3e0cb45 100644 --- a/primitives/authorship/Cargo.toml +++ b/primitives/authorship/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] sp-inherents = { version = "2.0.0-rc3", default-features = false, path = "../inherents" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../runtime" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } [features] default = [ "std" ] diff --git a/primitives/block-builder/Cargo.toml b/primitives/block-builder/Cargo.toml index 968107e69acad..8f8976949dce7 100644 --- a/primitives/block-builder/Cargo.toml +++ b/primitives/block-builder/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../runtime" } sp-api = { version = "2.0.0-rc3", default-features = false, path = "../api" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } sp-inherents = { version = "2.0.0-rc3", default-features = false, path = "../inherents" } [features] diff --git a/primitives/blockchain/Cargo.toml b/primitives/blockchain/Cargo.toml index bf1e5d8354a78..b4c22a524a56d 100644 --- a/primitives/blockchain/Cargo.toml +++ b/primitives/blockchain/Cargo.toml @@ -17,7 +17,7 @@ log = "0.4.8" lru = "0.4.0" parking_lot = "0.10.0" derive_more = "0.99.2" -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-consensus = { version = "0.8.0-rc3", path = "../consensus/common" } sp-runtime = { version = "2.0.0-rc3", path = "../runtime" } sp-block-builder = { version = "2.0.0-rc3", path = "../block-builder" } diff --git a/primitives/consensus/aura/Cargo.toml b/primitives/consensus/aura/Cargo.toml index 9dddc47fe27e2..24b82f4642b8a 100644 --- a/primitives/consensus/aura/Cargo.toml +++ b/primitives/consensus/aura/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path = "../../application-crypto" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../std" } sp-api = { version = "2.0.0-rc3", default-features = false, path = "../../api" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../runtime" } diff --git a/primitives/consensus/babe/Cargo.toml b/primitives/consensus/babe/Cargo.toml index 4884e9a9f4e23..978b415dc54be 100644 --- a/primitives/consensus/babe/Cargo.toml +++ b/primitives/consensus/babe/Cargo.toml @@ -13,10 +13,11 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path = "../../application-crypto" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } merlin = { version = "2.0", default-features = false } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../std" } sp-api = { version = "2.0.0-rc3", default-features = false, path = "../../api" } +sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../core" } sp-consensus = { version = "0.8.0-rc3", optional = true, path = "../common" } sp-consensus-vrf = { version = "0.8.0-rc3", path = "../vrf", default-features = false } sp-inherents = { version = "2.0.0-rc3", default-features = false, path = "../../inherents" } @@ -26,6 +27,7 @@ sp-timestamp = { version = "2.0.0-rc3", default-features = false, path = "../../ [features] default = ["std"] std = [ + "sp-core/std", "sp-application-crypto/std", "codec/std", "merlin/std", diff --git a/primitives/consensus/babe/src/lib.rs b/primitives/consensus/babe/src/lib.rs index 9848715a47f6f..10d4aa5ae50b7 100644 --- a/primitives/consensus/babe/src/lib.rs +++ b/primitives/consensus/babe/src/lib.rs @@ -31,6 +31,8 @@ pub use merlin::Transcript; use codec::{Encode, Decode}; use sp_std::vec::Vec; use sp_runtime::{ConsensusEngineId, RuntimeDebug}; +#[cfg(feature = "std")] +use sp_core::vrf::{VRFTranscriptData, VRFTranscriptValue}; use crate::digests::{NextEpochDescriptor, NextConfigDescriptor}; mod app { @@ -94,6 +96,23 @@ pub fn make_transcript( transcript } +/// Make a VRF transcript data container +#[cfg(feature = "std")] +pub fn make_transcript_data( + randomness: &Randomness, + slot_number: u64, + epoch: u64, +) -> VRFTranscriptData { + VRFTranscriptData { + label: &BABE_ENGINE_ID, + items: vec![ + ("slot number", VRFTranscriptValue::U64(slot_number)), + ("current epoch", VRFTranscriptValue::U64(epoch)), + ("chain randomness", VRFTranscriptValue::Bytes(&randomness[..])), + ] + } +} + /// An consensus log item for BABE. #[derive(Decode, Encode, Clone, PartialEq, Eq)] pub enum ConsensusLog { diff --git a/primitives/consensus/common/Cargo.toml b/primitives/consensus/common/Cargo.toml index 3f256d3f736ce..26fea3704517a 100644 --- a/primitives/consensus/common/Cargo.toml +++ b/primitives/consensus/common/Cargo.toml @@ -26,10 +26,11 @@ sp-std = { version = "2.0.0-rc3", path = "../../std" } sp-version = { version = "2.0.0-rc3", path = "../../version" } sp-runtime = { version = "2.0.0-rc3", path = "../../runtime" } sp-utils = { version = "2.0.0-rc3", path = "../../utils" } -codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } parking_lot = "0.10.0" serde = { version = "1.0", features = ["derive"] } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0-rc3"} +wasm-timer = "0.2.4" [dev-dependencies] sp-test-primitives = { version = "2.0.0-rc3", path = "../../test-primitives" } diff --git a/primitives/consensus/common/src/import_queue.rs b/primitives/consensus/common/src/import_queue.rs index 1a034e11d6644..94228a266385f 100644 --- a/primitives/consensus/common/src/import_queue.rs +++ b/primitives/consensus/common/src/import_queue.rs @@ -259,7 +259,7 @@ pub(crate) fn import_single_block_metered, Transaction r => return Ok(r), // Any other successful result means that the block is already imported. } - let started = std::time::Instant::now(); + let started = wasm_timer::Instant::now(); let (mut import_block, maybe_keys) = verifier.verify(block_origin, header, justification, block.body) .map_err(|msg| { if let Some(ref peer) = peer { diff --git a/primitives/consensus/common/src/import_queue/basic_queue.rs b/primitives/consensus/common/src/import_queue/basic_queue.rs index 33c3da910d23b..8eb194841f196 100644 --- a/primitives/consensus/common/src/import_queue/basic_queue.rs +++ b/primitives/consensus/common/src/import_queue/basic_queue.rs @@ -61,7 +61,7 @@ impl BasicQueue { block_import: BoxBlockImport, justification_import: Option>, finality_proof_import: Option>, - spawner: &impl sp_core::traits::SpawnBlocking, + spawner: &impl sp_core::traits::SpawnNamed, prometheus_registry: Option<&Registry>, ) -> Self { let (result_sender, result_port) = buffered_link::buffered_link(); diff --git a/primitives/consensus/common/src/offline_tracker.rs b/primitives/consensus/common/src/offline_tracker.rs index 9269640ffc8e4..b96498041f25d 100644 --- a/primitives/consensus/common/src/offline_tracker.rs +++ b/primitives/consensus/common/src/offline_tracker.rs @@ -18,7 +18,8 @@ //! Tracks offline validators. use std::collections::HashMap; -use std::time::{Instant, Duration}; +use std::time::Duration; +use wasm_timer::Instant; // time before we report a validator. const REPORT_TIME: Duration = Duration::from_secs(60 * 5); diff --git a/primitives/consensus/pow/Cargo.toml b/primitives/consensus/pow/Cargo.toml index f8b254ff6ef4e..9f9fedb76c5d3 100644 --- a/primitives/consensus/pow/Cargo.toml +++ b/primitives/consensus/pow/Cargo.toml @@ -16,7 +16,7 @@ sp-api = { version = "2.0.0-rc3", default-features = false, path = "../../api" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../../runtime" } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../core" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } [features] default = ["std"] diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index e1a281da6b0a8..3c37f57e7072e 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] derive_more = "0.99.2" sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } log = { version = "0.4.8", default-features = false } serde = { version = "1.0.101", optional = true, features = ["derive"] } byteorder = { version = "1.3.2", default-features = false } @@ -59,6 +59,7 @@ hex-literal = "0.2.1" rand = "0.7.2" criterion = "0.2.11" serde_json = "1.0" +rand_chacha = "0.2.2" [[bench]] name = "bench" diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 73134dcbfa9da..9b84bd84ca261 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -357,12 +357,20 @@ macro_rules! ss58_address_format { ( $( $identifier:tt => ($number:expr, $name:expr, $desc:tt) )* ) => ( /// A known address (sub)format/network ID for SS58. #[derive(Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug))] pub enum Ss58AddressFormat { $(#[doc = $desc] $identifier),*, /// Use a manually provided numeric value. Custom(u8), } + #[cfg(feature = "std")] + impl std::fmt::Display for Ss58AddressFormat { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } + } + static ALL_SS58_ADDRESS_FORMATS: [Ss58AddressFormat; 0 $(+ { let _ = $number; 1})*] = [ $(Ss58AddressFormat::$identifier),*, ]; @@ -462,6 +470,8 @@ ss58_address_format!( (16, "kulupu", "Kulupu mainnet, standard account (*25519).") DarwiniaAccount => (18, "darwinia", "Darwinia Chain mainnet, standard account (*25519).") + StafiAccount => + (20, "stafi", "Stafi mainnet, standard account (*25519).") RobonomicsAccount => (32, "robonomics", "Any Robonomics network standard account (*25519).") CentrifugeAccount => diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index 56dbbc7b7898d..1038c887e2174 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -73,6 +73,8 @@ pub mod traits; pub mod testing; #[cfg(feature = "std")] pub mod tasks; +#[cfg(feature = "std")] +pub mod vrf; pub use self::hash::{H160, H256, H512, convert_hash}; pub use self::uint::{U256, U512}; @@ -93,9 +95,16 @@ pub use sp_std; /// Context for executing a call into the runtime. pub enum ExecutionContext { - /// Context for general importing (including own blocks). + /// Context used for general block import (including locally authored blocks). Importing, - /// Context used when syncing the blockchain. + /// Context used for importing blocks as part of an initial sync of the blockchain. + /// + /// We distinguish between major sync and import so that validators who are running + /// their initial sync (or catching up after some time offline) can use the faster + /// native runtime (since we can reasonably assume the network as a whole has already + /// come to a broad conensus on the block and it probably hasn't been crafted + /// specifically to attack this node), but when importing blocks at the head of the + /// chain in normal operation they can use the safer Wasm version. Syncing, /// Context used for block construction. BlockConstruction, diff --git a/primitives/core/src/offchain/storage.rs b/primitives/core/src/offchain/storage.rs index 52a7bbe857d9d..7d7c711ed95f0 100644 --- a/primitives/core/src/offchain/storage.rs +++ b/primitives/core/src/offchain/storage.rs @@ -101,8 +101,9 @@ pub enum OffchainOverlayedChange { pub enum OffchainOverlayedChanges { /// Writing overlay changes to the offchain worker database is disabled by configuration. Disabled, - /// Overlay changes can be recorded using the inner collection of this variant. - Enabled(HashMap, OffchainOverlayedChange>), + /// Overlay changes can be recorded using the inner collection of this variant, + /// where the identifier is the tuple of `(prefix, key)`. + Enabled(HashMap<(Vec, Vec), OffchainOverlayedChange>), } impl Default for OffchainOverlayedChanges { @@ -140,23 +141,21 @@ impl OffchainOverlayedChanges { /// Remove a key and its associated value from the offchain database. pub fn remove(&mut self, prefix: &[u8], key: &[u8]) { if let Self::Enabled(ref mut storage) = self { - let key: Vec = prefix.iter().chain(key).cloned().collect(); - let _ = storage.insert(key, OffchainOverlayedChange::Remove); + let _ = storage.insert((prefix.to_vec(), key.to_vec()), OffchainOverlayedChange::Remove); } } /// Set the value associated with a key under a prefix to the value provided. pub fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]) { if let Self::Enabled(ref mut storage) = self { - let key = prefix.iter().chain(key).cloned().collect(); - let _ = storage.insert(key, OffchainOverlayedChange::SetValue(value.to_vec())); + let _ = storage.insert((prefix.to_vec(), key.to_vec()), OffchainOverlayedChange::SetValue(value.to_vec())); } } /// Obtain a associated value to the given key in storage with prefix. pub fn get(&self, prefix: &[u8], key: &[u8]) -> Option { if let Self::Enabled(ref storage) = self { - let key: Vec = prefix.iter().chain(key).cloned().collect(); + let key = (prefix.to_vec(), key.to_vec()); storage.get(&key).cloned() } else { None @@ -168,11 +167,11 @@ use std::collections::hash_map; /// Iterate by reference over the prepared offchain worker storage changes. pub struct OffchainOverlayedChangesIter<'i> { - inner: Option, OffchainOverlayedChange>>, + inner: Option, Vec), OffchainOverlayedChange>>, } impl<'i> Iterator for OffchainOverlayedChangesIter<'i> { - type Item = (&'i Vec, &'i OffchainOverlayedChange); + type Item = (&'i (Vec, Vec), &'i OffchainOverlayedChange); fn next(&mut self) -> Option { if let Some(ref mut iter) = self.inner { iter.next() @@ -197,11 +196,11 @@ impl<'i> OffchainOverlayedChangesIter<'i> { /// Iterate by value over the prepared offchain worker storage changes. pub struct OffchainOverlayedChangesIntoIter { - inner: Option,OffchainOverlayedChange>>, + inner: Option,Vec),OffchainOverlayedChange>>, } impl Iterator for OffchainOverlayedChangesIntoIter { - type Item = (Vec, OffchainOverlayedChange); + type Item = ((Vec, Vec), OffchainOverlayedChange); fn next(&mut self) -> Option { if let Some(ref mut iter) = self.inner { iter.next() @@ -225,11 +224,11 @@ impl OffchainOverlayedChangesIntoIter { /// Iterate over all items while draining them from the collection. pub struct OffchainOverlayedChangesDrain<'d> { - inner: Option,OffchainOverlayedChange>>, + inner: Option, Vec), OffchainOverlayedChange>>, } impl<'d> Iterator for OffchainOverlayedChangesDrain<'d> { - type Item = (Vec, OffchainOverlayedChange); + type Item = ((Vec, Vec), OffchainOverlayedChange); fn next(&mut self) -> Option { if let Some(ref mut iter) = self.inner { iter.next() @@ -286,9 +285,13 @@ mod test { ooc.set(STORAGE_PREFIX, b"ppp", b"rrr"); let mut iter = ooc.into_iter(); - let mut k = STORAGE_PREFIX.to_vec(); - k.extend_from_slice(&b"ppp"[..]); - assert_eq!(iter.next(), Some((k, OffchainOverlayedChange::SetValue(b"rrr".to_vec())))); + assert_eq!( + iter.next(), + Some( + ((STORAGE_PREFIX.to_vec(), b"ppp".to_vec()), + OffchainOverlayedChange::SetValue(b"rrr".to_vec())) + ) + ); assert_eq!(iter.next(), None); } } diff --git a/primitives/core/src/offchain/testing.rs b/primitives/core/src/offchain/testing.rs index 76cf8915f2054..a14e906f54308 100644 --- a/primitives/core/src/offchain/testing.rs +++ b/primitives/core/src/offchain/testing.rs @@ -26,7 +26,7 @@ use std::{ }; use crate::offchain::{ self, - storage::InMemOffchainStorage, + storage::{InMemOffchainStorage, OffchainOverlayedChange, OffchainOverlayedChanges}, HttpError, HttpRequestId as RequestId, HttpRequestStatus as RequestStatus, @@ -36,6 +36,7 @@ use crate::offchain::{ TransactionPool, OffchainStorage, }; + use parking_lot::RwLock; /// Pending request. @@ -61,6 +62,57 @@ pub struct PendingRequest { pub response_headers: Vec<(String, String)>, } +/// Sharable "persistent" offchain storage for test. +#[derive(Debug, Clone, Default)] +pub struct TestPersistentOffchainDB { + persistent: Arc>, +} + +impl TestPersistentOffchainDB { + /// Create a new and empty offchain storage db for persistent items + pub fn new() -> Self { + Self { + persistent: Arc::new(RwLock::new(InMemOffchainStorage::default())) + } + } + + /// Apply a set of off-chain changes directly to the test backend + pub fn apply_offchain_changes(&mut self, changes: &mut OffchainOverlayedChanges) { + let mut me = self.persistent.write(); + for ((_prefix, key), value_operation) in changes.drain() { + match value_operation { + OffchainOverlayedChange::SetValue(val) => me.set(b"", key.as_slice(), val.as_slice()), + OffchainOverlayedChange::Remove => me.remove(b"", key.as_slice()), + } + } + } +} + +impl OffchainStorage for TestPersistentOffchainDB { + fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]) { + self.persistent.write().set(prefix, key, value); + } + + fn remove(&mut self, prefix: &[u8], key: &[u8]) { + self.persistent.write().remove(prefix, key); + } + + fn get(&self, prefix: &[u8], key: &[u8]) -> Option> { + self.persistent.read().get(prefix, key) + } + + fn compare_and_set( + &mut self, + prefix: &[u8], + key: &[u8], + old_value: Option<&[u8]>, + new_value: &[u8], + ) -> bool { + self.persistent.write().compare_and_set(prefix, key, old_value, new_value) + } +} + + /// Internal state of the externalities. /// /// This can be used in tests to respond or assert stuff about interactions. @@ -70,7 +122,7 @@ pub struct OffchainState { pub requests: BTreeMap, expected_requests: BTreeMap, /// Persistent local storage - pub persistent_storage: InMemOffchainStorage, + pub persistent_storage: TestPersistentOffchainDB, /// Local storage pub local_storage: InMemOffchainStorage, /// A supposedly random seed. @@ -145,6 +197,13 @@ impl TestOffchainExt { let state = ext.0.clone(); (ext, state) } + + /// Create new `TestOffchainExt` and a reference to the internal state. + pub fn with_offchain_db(offchain_db: TestPersistentOffchainDB) -> (Self, Arc>) { + let (ext, state) = Self::new(); + ext.0.write().persistent_storage = offchain_db; + (ext, state) + } } impl offchain::Externalities for TestOffchainExt { @@ -174,17 +233,17 @@ impl offchain::Externalities for TestOffchainExt { fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) { let mut state = self.0.write(); match kind { - StorageKind::LOCAL => &mut state.local_storage, - StorageKind::PERSISTENT => &mut state.persistent_storage, - }.set(b"", key, value); + StorageKind::LOCAL => state.local_storage.set(b"", key, value), + StorageKind::PERSISTENT => state.persistent_storage.set(b"", key, value), + }; } fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) { let mut state = self.0.write(); match kind { - StorageKind::LOCAL => &mut state.local_storage, - StorageKind::PERSISTENT => &mut state.persistent_storage, - }.remove(b"", key); + StorageKind::LOCAL => state.local_storage.remove(b"", key), + StorageKind::PERSISTENT => state.persistent_storage.remove(b"", key), + }; } fn local_storage_compare_and_set( @@ -196,17 +255,17 @@ impl offchain::Externalities for TestOffchainExt { ) -> bool { let mut state = self.0.write(); match kind { - StorageKind::LOCAL => &mut state.local_storage, - StorageKind::PERSISTENT => &mut state.persistent_storage, - }.compare_and_set(b"", key, old_value, new_value) + StorageKind::LOCAL => state.local_storage.compare_and_set(b"", key, old_value, new_value), + StorageKind::PERSISTENT => state.persistent_storage.compare_and_set(b"", key, old_value, new_value), + } } fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option> { let state = self.0.read(); match kind { - StorageKind::LOCAL => &state.local_storage, - StorageKind::PERSISTENT => &state.persistent_storage, - }.get(b"", key) + StorageKind::LOCAL => state.local_storage.get(b"", key), + StorageKind::PERSISTENT => state.persistent_storage.get(b"", key), + } } fn http_request_start(&mut self, method: &str, uri: &str, meta: &[u8]) -> Result { diff --git a/primitives/core/src/testing.rs b/primitives/core/src/testing.rs index e14eb6a7f37c6..1d88e1fad5513 100644 --- a/primitives/core/src/testing.rs +++ b/primitives/core/src/testing.rs @@ -22,10 +22,12 @@ use crate::crypto::KeyTypeId; use crate::{ crypto::{Pair, Public, CryptoTypePublicPair}, ed25519, sr25519, ecdsa, - traits::BareCryptoStoreError + traits::Error, + vrf::{VRFTranscriptData, VRFSignature, make_transcript}, }; #[cfg(feature = "std")] use std::collections::HashSet; + /// Key type for generic Ed25519 key. pub const ED25519: KeyTypeId = KeyTypeId(*b"ed25"); /// Key type for generic Sr 25519 key. @@ -76,7 +78,7 @@ impl KeyStore { #[cfg(feature = "std")] impl crate::traits::BareCryptoStore for KeyStore { - fn keys(&self, id: KeyTypeId) -> Result, BareCryptoStoreError> { + fn keys(&self, id: KeyTypeId) -> Result, Error> { self.keys .get(&id) .map(|map| { @@ -106,11 +108,11 @@ impl crate::traits::BareCryptoStore for KeyStore { &mut self, id: KeyTypeId, seed: Option<&str>, - ) -> Result { + ) -> Result { match seed { Some(seed) => { let pair = sr25519::Pair::from_string(seed, None) - .map_err(|_| BareCryptoStoreError::ValidationError("Generates an `sr25519` pair.".to_owned()))?; + .map_err(|_| Error::ValidationError("Generates an `sr25519` pair.".to_owned()))?; self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), seed.into()); Ok(pair.public()) }, @@ -137,11 +139,11 @@ impl crate::traits::BareCryptoStore for KeyStore { &mut self, id: KeyTypeId, seed: Option<&str>, - ) -> Result { + ) -> Result { match seed { Some(seed) => { let pair = ed25519::Pair::from_string(seed, None) - .map_err(|_| BareCryptoStoreError::ValidationError("Generates an `ed25519` pair.".to_owned()))?; + .map_err(|_| Error::ValidationError("Generates an `ed25519` pair.".to_owned()))?; self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), seed.into()); Ok(pair.public()) }, @@ -168,11 +170,11 @@ impl crate::traits::BareCryptoStore for KeyStore { &mut self, id: KeyTypeId, seed: Option<&str>, - ) -> Result { + ) -> Result { match seed { Some(seed) => { let pair = ecdsa::Pair::from_string(seed, None) - .map_err(|_| BareCryptoStoreError::ValidationError("Generates an `ecdsa` pair.".to_owned()))?; + .map_err(|_| Error::ValidationError("Generates an `ecdsa` pair.".to_owned()))?; self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), seed.into()); Ok(pair.public()) }, @@ -201,7 +203,7 @@ impl crate::traits::BareCryptoStore for KeyStore { &self, id: KeyTypeId, keys: Vec, - ) -> std::result::Result, BareCryptoStoreError> { + ) -> std::result::Result, Error> { let provided_keys = keys.into_iter().collect::>(); let all_keys = self.keys(id)?.into_iter().collect::>(); @@ -213,31 +215,48 @@ impl crate::traits::BareCryptoStore for KeyStore { id: KeyTypeId, key: &CryptoTypePublicPair, msg: &[u8], - ) -> Result, BareCryptoStoreError> { + ) -> Result, Error> { use codec::Encode; match key.0 { ed25519::CRYPTO_ID => { let key_pair: ed25519::Pair = self .ed25519_key_pair(id, &ed25519::Public::from_slice(key.1.as_slice())) - .ok_or(BareCryptoStoreError::PairNotFound("ed25519".to_owned()))?; + .ok_or(Error::PairNotFound("ed25519".to_owned()))?; return Ok(key_pair.sign(msg).encode()); } sr25519::CRYPTO_ID => { let key_pair: sr25519::Pair = self .sr25519_key_pair(id, &sr25519::Public::from_slice(key.1.as_slice())) - .ok_or(BareCryptoStoreError::PairNotFound("sr25519".to_owned()))?; + .ok_or(Error::PairNotFound("sr25519".to_owned()))?; return Ok(key_pair.sign(msg).encode()); } ecdsa::CRYPTO_ID => { let key_pair: ecdsa::Pair = self .ecdsa_key_pair(id, &ecdsa::Public::from_slice(key.1.as_slice())) - .ok_or(BareCryptoStoreError::PairNotFound("ecdsa".to_owned()))?; + .ok_or(Error::PairNotFound("ecdsa".to_owned()))?; return Ok(key_pair.sign(msg).encode()); } - _ => Err(BareCryptoStoreError::KeyNotSupported(id)) + _ => Err(Error::KeyNotSupported(id)) } } + + fn sr25519_vrf_sign( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + transcript_data: VRFTranscriptData, + ) -> Result { + let transcript = make_transcript(transcript_data); + let pair = self.sr25519_key_pair(key_type, public) + .ok_or(Error::PairNotFound("Not found".to_owned()))?; + + let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); + Ok(VRFSignature { + output: inout.to_output(), + proof, + }) + } } /// Macro for exporting functions from wasm in with the expected signature for using it with the @@ -358,10 +377,13 @@ impl SpawnBlockingExecutor { } #[cfg(feature = "std")] -impl crate::traits::SpawnBlocking for SpawnBlockingExecutor { +impl crate::traits::SpawnNamed for SpawnBlockingExecutor { fn spawn_blocking(&self, _: &'static str, future: futures::future::BoxFuture<'static, ()>) { self.0.spawn_ok(future); } + fn spawn(&self, _: &'static str, future: futures::future::BoxFuture<'static, ()>) { + self.0.spawn_ok(future); + } } #[cfg(test)] @@ -369,6 +391,7 @@ mod tests { use super::*; use crate::sr25519; use crate::testing::{ED25519, SR25519}; + use crate::vrf::VRFTranscriptValue; #[test] fn store_key_and_extract() { @@ -400,4 +423,42 @@ mod tests { assert!(public_keys.contains(&key_pair.public().into())); } + + #[test] + fn vrf_sign() { + let store = KeyStore::new(); + + let secret_uri = "//Alice"; + let key_pair = sr25519::Pair::from_string(secret_uri, None).expect("Generates key pair"); + + let transcript_data = VRFTranscriptData { + label: b"Test", + items: vec![ + ("one", VRFTranscriptValue::U64(1)), + ("two", VRFTranscriptValue::U64(2)), + ("three", VRFTranscriptValue::Bytes("test".as_bytes())), + ] + }; + + let result = store.read().sr25519_vrf_sign( + SR25519, + &key_pair.public(), + transcript_data.clone(), + ); + assert!(result.is_err()); + + store.write().insert_unknown( + SR25519, + secret_uri, + key_pair.public().as_ref(), + ).expect("Inserts unknown key"); + + let result = store.read().sr25519_vrf_sign( + SR25519, + &key_pair.public(), + transcript_data, + ); + + assert!(result.is_ok()); + } } diff --git a/primitives/core/src/traits.rs b/primitives/core/src/traits.rs index 880b34a1ed191..4481145818f26 100644 --- a/primitives/core/src/traits.rs +++ b/primitives/core/src/traits.rs @@ -19,9 +19,9 @@ use crate::{ crypto::{KeyTypeId, CryptoTypePublicPair}, + vrf::{VRFTranscriptData, VRFSignature}, ed25519, sr25519, ecdsa, }; - use std::{ borrow::Cow, fmt::{Debug, Display}, @@ -33,7 +33,7 @@ pub use sp_externalities::{Externalities, ExternalitiesExt}; /// BareCryptoStore error #[derive(Debug, derive_more::Display)] -pub enum BareCryptoStoreError { +pub enum Error { /// Public key type is not supported #[display(fmt="Key not supported: {:?}", _0)] KeyNotSupported(KeyTypeId), @@ -64,7 +64,7 @@ pub trait BareCryptoStore: Send + Sync { &mut self, id: KeyTypeId, seed: Option<&str>, - ) -> Result; + ) -> Result; /// Returns all ed25519 public keys for the given key type. fn ed25519_public_keys(&self, id: KeyTypeId) -> Vec; /// Generate a new ed25519 key pair for the given key type and an optional seed. @@ -76,7 +76,7 @@ pub trait BareCryptoStore: Send + Sync { &mut self, id: KeyTypeId, seed: Option<&str>, - ) -> Result; + ) -> Result; /// Returns all ecdsa public keys for the given key type. fn ecdsa_public_keys(&self, id: KeyTypeId) -> Vec; /// Generate a new ecdsa key pair for the given key type and an optional seed. @@ -88,7 +88,7 @@ pub trait BareCryptoStore: Send + Sync { &mut self, id: KeyTypeId, seed: Option<&str>, - ) -> Result; + ) -> Result; /// Insert a new key. This doesn't require any known of the crypto; but a public key must be /// manually provided. @@ -108,11 +108,11 @@ pub trait BareCryptoStore: Send + Sync { &self, id: KeyTypeId, keys: Vec - ) -> Result, BareCryptoStoreError>; + ) -> Result, Error>; /// List all supported keys /// /// Returns a set of public keys the signer supports. - fn keys(&self, id: KeyTypeId) -> Result, BareCryptoStoreError>; + fn keys(&self, id: KeyTypeId) -> Result, Error>; /// Checks if the private keys for the given public key and key type combinations exist. /// @@ -131,7 +131,7 @@ pub trait BareCryptoStore: Send + Sync { id: KeyTypeId, key: &CryptoTypePublicPair, msg: &[u8], - ) -> Result, BareCryptoStoreError>; + ) -> Result, Error>; /// Sign with any key /// @@ -144,7 +144,7 @@ pub trait BareCryptoStore: Send + Sync { id: KeyTypeId, keys: Vec, msg: &[u8] - ) -> Result<(CryptoTypePublicPair, Vec), BareCryptoStoreError> { + ) -> Result<(CryptoTypePublicPair, Vec), Error> { if keys.len() == 1 { return self.sign_with(id, &keys[0], msg).map(|s| (keys[0].clone(), s)); } else { @@ -154,7 +154,7 @@ pub trait BareCryptoStore: Send + Sync { } } } - Err(BareCryptoStoreError::KeyNotSupported(id)) + Err(Error::KeyNotSupported(id)) } /// Sign with all keys @@ -163,15 +163,36 @@ pub trait BareCryptoStore: Send + Sync { /// each key given that the key is supported. /// /// Returns a list of `Result`s each representing the SCALE encoded - /// signature of each key or a BareCryptoStoreError for non-supported keys. + /// signature of each key or a Error for non-supported keys. fn sign_with_all( &self, id: KeyTypeId, keys: Vec, msg: &[u8], - ) -> Result, BareCryptoStoreError>>, ()>{ + ) -> Result, Error>>, ()>{ Ok(keys.iter().map(|k| self.sign_with(id, k, msg)).collect()) } + + /// Generate VRF signature for given transcript data. + /// + /// Receives KeyTypeId and Public key to be able to map + /// them to a private key that exists in the keystore which + /// is, in turn, used for signing the provided transcript. + /// + /// Returns a result containing the signature data. + /// Namely, VRFOutput and VRFProof which are returned + /// inside the `VRFSignature` container struct. + /// + /// This function will return an error in the cases where + /// the public key and key type provided do not match a private + /// key in the keystore. Or, in the context of remote signing + /// an error could be a network one. + fn sr25519_vrf_sign( + &self, + key_type: KeyTypeId, + public: &sr25519::Public, + transcript_data: VRFTranscriptData, + ) -> Result; } /// A pointer to the key store. @@ -349,10 +370,14 @@ impl TaskExecutorExt { } } -/// Something that can spawn a blocking future. -pub trait SpawnBlocking { +/// Something that can spawn futures (blocking and non-blocking) with am assigned name. +pub trait SpawnNamed { /// Spawn the given blocking future. /// /// The given `name` is used to identify the future in tracing. fn spawn_blocking(&self, name: &'static str, future: futures::future::BoxFuture<'static, ()>); + /// Spawn the given non-blocking future. + /// + /// The given `name` is used to identify the future in tracing. + fn spawn(&self, name: &'static str, future: futures::future::BoxFuture<'static, ()>); } diff --git a/primitives/core/src/vrf.rs b/primitives/core/src/vrf.rs new file mode 100644 index 0000000000000..d392587cb72e7 --- /dev/null +++ b/primitives/core/src/vrf.rs @@ -0,0 +1,99 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! VRF-specifc data types and helpers + +use codec::Encode; +use merlin::Transcript; +use schnorrkel::vrf::{VRFOutput, VRFProof}; +/// An enum whose variants represent possible +/// accepted values to construct the VRF transcript +#[derive(Clone, Encode)] +pub enum VRFTranscriptValue<'a> { + /// Value is an array of bytes + Bytes(&'a [u8]), + /// Value is a u64 integer + U64(u64), +} +/// VRF Transcript data +#[derive(Clone, Encode)] +pub struct VRFTranscriptData<'a> { + /// The transcript's label + pub label: &'static [u8], + /// Additional data to be registered into the transcript + pub items: Vec<(&'static str, VRFTranscriptValue<'a>)>, +} +/// VRF signature data +pub struct VRFSignature { + /// The VRFOutput serialized + pub output: VRFOutput, + /// The calculated VRFProof + pub proof: VRFProof, +} + +/// Construct a `Transcript` object from data. +/// +/// Returns `merlin::Transcript` +pub fn make_transcript(data: VRFTranscriptData) -> Transcript { + let mut transcript = Transcript::new(data.label); + for (label, value) in data.items.into_iter() { + match value { + VRFTranscriptValue::Bytes(bytes) => { + transcript.append_message(label.as_bytes(), &bytes); + }, + VRFTranscriptValue::U64(val) => { + transcript.append_u64(label.as_bytes(), val); + } + } + } + transcript +} + + +#[cfg(test)] +mod tests { + use super::*; + use crate::vrf::VRFTranscriptValue; + use rand::RngCore; + use rand_chacha::{ + rand_core::SeedableRng, + ChaChaRng, + }; + + #[test] + fn transcript_creation_matches() { + let mut orig_transcript = Transcript::new(b"My label"); + orig_transcript.append_u64(b"one", 1); + orig_transcript.append_message(b"two", "test".as_bytes()); + + let new_transcript = make_transcript(VRFTranscriptData { + label: b"My label", + items: vec![ + ("one", VRFTranscriptValue::U64(1)), + ("two", VRFTranscriptValue::Bytes("test".as_bytes())), + ], + }); + let test = |t: Transcript| -> [u8; 16] { + let mut b = [0u8; 16]; + t.build_rng() + .finalize(&mut ChaChaRng::from_seed([0u8;32])) + .fill_bytes(&mut b); + b + }; + debug_assert!(test(orig_transcript) == test(new_transcript)); + } +} diff --git a/primitives/database/src/lib.rs b/primitives/database/src/lib.rs index bc4c11f60a98d..1fb7b156661fc 100644 --- a/primitives/database/src/lib.rs +++ b/primitives/database/src/lib.rs @@ -165,6 +165,12 @@ pub trait Database: Send + Sync { } } +impl std::fmt::Debug for dyn Database { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Database") + } +} + /// Call `f` with the value previously stored against `key` and return the result, or `None` if /// `key` is not currently in the database. /// diff --git a/primitives/externalities/Cargo.toml b/primitives/externalities/Cargo.toml index faa95fd9a1269..3af61bbeb0e8c 100644 --- a/primitives/externalities/Cargo.toml +++ b/primitives/externalities/Cargo.toml @@ -16,4 +16,4 @@ targets = ["x86_64-unknown-linux-gnu"] sp-storage = { version = "2.0.0-rc3", path = "../storage" } sp-std = { version = "2.0.0-rc3", path = "../std" } environmental = { version = "1.1.1" } -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } diff --git a/primitives/externalities/src/lib.rs b/primitives/externalities/src/lib.rs index cfb1d0878a491..210fe5b4ef009 100644 --- a/primitives/externalities/src/lib.rs +++ b/primitives/externalities/src/lib.rs @@ -195,6 +195,29 @@ pub trait Externalities: ExtensionStore { /// The returned hash is defined by the `Block` and is SCALE encoded. fn storage_changes_root(&mut self, parent: &[u8]) -> Result>, ()>; + /// Start a new nested transaction. + /// + /// This allows to either commit or roll back all changes made after this call to the + /// top changes or the default child changes. For every transaction there cam be a + /// matching call to either `storage_rollback_transaction` or `storage_commit_transaction`. + /// Any transactions that are still open after returning from runtime are committed + /// automatically. + /// + /// Changes made without any open transaction are committed immediately. + fn storage_start_transaction(&mut self); + + /// Rollback the last transaction started by `storage_start_transaction`. + /// + /// Any changes made during that storage transaction are discarded. Returns an error when + /// no transaction is open that can be closed. + fn storage_rollback_transaction(&mut self) -> Result<(), ()>; + + /// Commit the last transaction started by `storage_start_transaction`. + /// + /// Any changes made during that storage transaction are committed. Returns an error when + /// no transaction is open that can be closed. + fn storage_commit_transaction(&mut self) -> Result<(), ()>; + /// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! /// Benchmarking related functionality and shouldn't be used anywhere else! /// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/primitives/finality-grandpa/Cargo.toml b/primitives/finality-grandpa/Cargo.toml index 254c27e8dd684..27315b0ff96ae 100644 --- a/primitives/finality-grandpa/Cargo.toml +++ b/primitives/finality-grandpa/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path = "../application-crypto" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } grandpa = { package = "finality-grandpa", version = "0.12.3", default-features = false, features = ["derive-codec"] } log = { version = "0.4.8", optional = true } serde = { version = "1.0.101", optional = true, features = ["derive"] } diff --git a/primitives/finality-grandpa/src/lib.rs b/primitives/finality-grandpa/src/lib.rs index 2e81c8cecbb70..889468a352819 100644 --- a/primitives/finality-grandpa/src/lib.rs +++ b/primitives/finality-grandpa/src/lib.rs @@ -29,6 +29,9 @@ use codec::{Encode, Decode, Input, Codec}; use sp_runtime::{ConsensusEngineId, RuntimeDebug, traits::NumberFor}; use sp_std::borrow::Cow; use sp_std::vec::Vec; +#[cfg(feature = "std")] +use sp_core::traits::BareCryptoStorePtr; +use sp_std::convert::TryInto; #[cfg(feature = "std")] use log::debug; @@ -370,25 +373,31 @@ where /// Localizes the message to the given set and round and signs the payload. #[cfg(feature = "std")] pub fn sign_message( + keystore: BareCryptoStorePtr, message: grandpa::Message, - pair: &AuthorityPair, + public: AuthorityId, round: RoundNumber, set_id: SetId, -) -> grandpa::SignedMessage +) -> Option> where H: Encode, N: Encode, { - use sp_core::Pair; + use sp_core::crypto::Public; + use sp_application_crypto::AppKey; let encoded = localized_payload(round, set_id, &message); - let signature = pair.sign(&encoded[..]); + let signature = keystore.read() + .sign_with(AuthorityId::ID, &public.to_public_crypto_pair(), &encoded[..]) + .ok()? + .try_into() + .ok()?; - grandpa::SignedMessage { + Some(grandpa::SignedMessage { message, signature, - id: pair.public(), - } + id: public, + }) } /// WASM function call to check for pending changes. diff --git a/primitives/finality-tracker/Cargo.toml b/primitives/finality-tracker/Cargo.toml index 779507ea817ce..60ed88c110a32 100644 --- a/primitives/finality-tracker/Cargo.toml +++ b/primitives/finality-tracker/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME module that tracks the last finalized block, as perceived b targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } sp-inherents = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/inherents" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } diff --git a/primitives/inherents/Cargo.toml b/primitives/inherents/Cargo.toml index 2e3820d392bdb..503aa29d29c4d 100644 --- a/primitives/inherents/Cargo.toml +++ b/primitives/inherents/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] parking_lot = { version = "0.10.0", optional = true } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../core" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } derive_more = { version = "0.99.2", optional = true } [features] diff --git a/primitives/io/Cargo.toml b/primitives/io/Cargo.toml index 353532b1b4caa..8bb113b1f1213 100644 --- a/primitives/io/Cargo.toml +++ b/primitives/io/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } hash-db = { version = "0.15.2", default-features = false } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../core" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } @@ -24,6 +24,7 @@ sp-wasm-interface = { version = "2.0.0-rc3", path = "../../primitives/wasm-inter sp-runtime-interface = { version = "2.0.0-rc3", default-features = false, path = "../runtime-interface" } sp-trie = { version = "2.0.0-rc3", optional = true, path = "../../primitives/trie" } sp-externalities = { version = "0.8.0-rc3", optional = true, path = "../externalities" } +sp-tracing = { version = "2.0.0-rc3", default-features = false, path = "../tracing" } log = { version = "0.4.8", optional = true } futures = { version = "0.3.1", features = ["thread-pool"], optional = true } parking_lot = { version = "0.10.0", optional = true } diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 8d81a84c4c88a..c75c8e67cc29a 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -155,6 +155,46 @@ pub trait Storage { fn next_key(&mut self, key: &[u8]) -> Option> { self.next_storage_key(&key) } + + /// Start a new nested transaction. + /// + /// This allows to either commit or roll back all changes that are made after this call. + /// For every transaction there must be a matching call to either `rollback_transaction` + /// or `commit_transaction`. This is also effective for all values manipulated using the + /// `DefaultChildStorage` API. + /// + /// # Warning + /// + /// This is a low level API that is potentially dangerous as it can easily result + /// in unbalanced transactions. For example, FRAME users should use high level storage + /// abstractions. + fn start_transaction(&mut self) { + self.storage_start_transaction(); + } + + /// Rollback the last transaction started by `start_transaction`. + /// + /// Any changes made during that transaction are discarded. + /// + /// # Panics + /// + /// Will panic if there is no open transaction. + fn rollback_transaction(&mut self) { + self.storage_rollback_transaction() + .expect("No open transaction that can be rolled back."); + } + + /// Commit the last transaction started by `start_transaction`. + /// + /// Any changes made during that transaction are committed. + /// + /// # Panics + /// + /// Will panic if there is no open transaction. + fn commit_transaction(&mut self) { + self.storage_commit_transaction() + .expect("No open transaction that can be committed."); + } } /// Interface for accessing the child storage for default child trie, @@ -216,7 +256,7 @@ pub trait DefaultChildStorage { /// Clear a child storage key. /// /// For the default child storage at `storage_key`, clear value at `key`. - fn clear ( + fn clear( &mut self, storage_key: &[u8], key: &[u8], @@ -965,6 +1005,55 @@ pub trait Logging { } } +#[cfg(feature = "std")] +sp_externalities::decl_extension! { + /// Extension to allow running traces in wasm via Proxy + pub struct TracingProxyExt(sp_tracing::proxy::TracingProxy); +} + +/// Interface that provides functions for profiling the runtime. +#[runtime_interface] +pub trait WasmTracing { + /// To create and enter a `tracing` span, using `sp_tracing::proxy` + /// Returns 0 value to indicate that no further traces should be attempted + fn enter_span(&mut self, target: &str, name: &str) -> u64 { + if sp_tracing::wasm_tracing_enabled() { + match self.extension::() { + Some(proxy) => return proxy.enter_span(target, name), + None => { + if self.register_extension(TracingProxyExt(sp_tracing::proxy::TracingProxy::new())).is_ok() { + if let Some(proxy) = self.extension::() { + return proxy.enter_span(target, name); + } + } else { + log::warn!( + target: "tracing", + "Unable to register extension: TracingProxyExt" + ); + } + } + } + } + log::debug!( + target: "tracing", + "Notify to runtime that tracing is disabled." + ); + 0 + } + + /// Exit a `tracing` span, using `sp_tracing::proxy` + fn exit_span(&mut self, id: u64) { + if let Some(proxy) = self.extension::() { + proxy.exit_span(id) + } else { + log::warn!( + target: "tracing", + "Unable to load extension: TracingProxyExt" + ); + } + } +} + /// Wasm-only interface that provides functions for interacting with the sandbox. #[runtime_interface(wasm_only)] pub trait Sandbox { @@ -1111,6 +1200,7 @@ pub type SubstrateHostFunctions = ( storage::HostFunctions, default_child_storage::HostFunctions, misc::HostFunctions, + wasm_tracing::HostFunctions, offchain::HostFunctions, crypto::HostFunctions, hashing::HostFunctions, diff --git a/primitives/npos-elections/Cargo.toml b/primitives/npos-elections/Cargo.toml index 3e425f2adc491..7982c8ce4d8cb 100644 --- a/primitives/npos-elections/Cargo.toml +++ b/primitives/npos-elections/Cargo.toml @@ -21,7 +21,6 @@ sp-arithmetic = { version = "2.0.0-rc3", default-features = false, path = "../ar [dev-dependencies] substrate-test-utils = { version = "2.0.0-rc3", path = "../../test-utils" } rand = "0.7.3" -sp-npos-elections = { version = "2.0.0-rc3", path = "." } sp-runtime = { version = "2.0.0-rc3", path = "../../primitives/runtime" } [features] diff --git a/primitives/npos-elections/compact/src/assignment.rs b/primitives/npos-elections/compact/src/assignment.rs index fb3d4330b06e8..96c68ece92a19 100644 --- a/primitives/npos-elections/compact/src/assignment.rs +++ b/primitives/npos-elections/compact/src/assignment.rs @@ -18,8 +18,8 @@ //! Code generation for the ratio assignment type. use crate::field_name_for; -use proc_macro2::{TokenStream as TokenStream2}; -use syn::{GenericArgument}; +use proc_macro2::TokenStream as TokenStream2; +use syn::GenericArgument; use quote::quote; fn from_impl(count: usize) -> TokenStream2 { diff --git a/primitives/npos-elections/compact/src/lib.rs b/primitives/npos-elections/compact/src/lib.rs index 022782a7dd9cf..1b88ff6531081 100644 --- a/primitives/npos-elections/compact/src/lib.rs +++ b/primitives/npos-elections/compact/src/lib.rs @@ -224,17 +224,19 @@ fn struct_def( } fn imports() -> Result { - let sp_phragmen_imports = match crate_name("sp-npos-elections") { - Ok(sp_npos_elections) => { - let ident = syn::Ident::new(&sp_npos_elections, Span::call_site()); - quote!( extern crate #ident as _phragmen; ) + if std::env::var("CARGO_PKG_NAME").unwrap() == "sp-npos-elections" { + Ok(quote! { + use crate as _phragmen; + }) + } else { + match crate_name("sp-npos-elections") { + Ok(sp_npos_elections) => { + let ident = syn::Ident::new(&sp_npos_elections, Span::call_site()); + Ok(quote!( extern crate #ident as _phragmen; )) + }, + Err(e) => Err(syn::Error::new(Span::call_site(), &e)), } - Err(e) => return Err(syn::Error::new(Span::call_site(), &e)), - }; - - Ok(quote!( - #sp_phragmen_imports - )) + } } struct CompactSolutionDef { diff --git a/primitives/npos-elections/src/tests.rs b/primitives/npos-elections/src/tests.rs index 47d619339be5d..08923c69499c3 100644 --- a/primitives/npos-elections/src/tests.rs +++ b/primitives/npos-elections/src/tests.rs @@ -17,8 +17,6 @@ //! Tests for npos-elections. -#![cfg(test)] - use crate::mock::*; use crate::{ seq_phragmen, balance_solution, build_support_map, is_score_better, helpers::*, @@ -772,10 +770,12 @@ fn score_comparison_large_value() { mod compact { use codec::{Decode, Encode}; - use crate::{generate_compact_solution_type, VoteWeight}; - use super::{AccountId}; + use super::AccountId; // these need to come from the same dev-dependency `sp-npos-elections`, not from the crate. - use sp_npos_elections::{Assignment, StakedAssignment, Error as PhragmenError, ExtendedBalance}; + use crate::{ + generate_compact_solution_type, VoteWeight, Assignment, StakedAssignment, + Error as PhragmenError, ExtendedBalance, + }; use sp_std::{convert::{TryInto, TryFrom}, fmt::Debug}; use sp_arithmetic::Percent; diff --git a/primitives/rpc/src/number.rs b/primitives/rpc/src/number.rs index 63aa643fb6fca..3d7e74753526c 100644 --- a/primitives/rpc/src/number.rs +++ b/primitives/rpc/src/number.rs @@ -15,65 +15,79 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Chain RPC Block number type. +//! A number type that can be serialized both as a number or a string that encodes a number in a +//! string. -use serde::{Serialize, Deserialize}; use std::{convert::TryFrom, fmt::Debug}; +use serde::{Serialize, Deserialize}; use sp_core::U256; -/// RPC Block number type +/// A number type that can be serialized both as a number or a string that encodes a number in a +/// string. +/// +/// We allow two representations of the block number as input. Either we deserialize to the type +/// that is specified in the block type or we attempt to parse given hex value. /// -/// We allow two representations of the block number as input. -/// Either we deserialize to the type that is specified in the block type -/// or we attempt to parse given hex value. -/// We do that for consistency with the returned type, default generic header -/// serializes block number as hex to avoid overflows in JavaScript. -#[derive(Serialize, Deserialize, Debug, PartialEq)] +/// The primary motivation for having this type is to avoid overflows when using big integers in +/// JavaScript (which we consider as an important RPC API consumer). +#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq)] #[serde(untagged)] -pub enum NumberOrHex { - /// The original header number type of block. - Number(Number), - /// Hex representation of the block number. +pub enum NumberOrHex { + /// The number represented directly. + Number(u64), + /// Hex representation of the number. Hex(U256), } -impl + From + Debug + PartialOrd> NumberOrHex { - /// Attempts to convert into concrete block number. - /// - /// Fails in case hex number is too big. - pub fn to_number(self) -> Result { - let num = match self { - NumberOrHex::Number(n) => n, - NumberOrHex::Hex(h) => { - let l = h.low_u64(); - if U256::from(l) != h { - return Err(format!("`{}` does not fit into u64 type; unsupported for now.", h)) - } else { - Number::try_from(l) - .map_err(|_| format!("`{}` does not fit into block number type.", h))? - } - }, - }; - // FIXME <2329>: Database seems to limit the block number to u32 for no reason - if num > Number::from(u32::max_value()) { - return Err(format!("`{:?}` > u32::max_value(), the max block number is u32.", num)) +impl NumberOrHex { + /// Converts this number into an U256. + pub fn into_u256(self) -> U256 { + match self { + NumberOrHex::Number(n) => n.into(), + NumberOrHex::Hex(h) => h, } - Ok(num) } } -impl From for NumberOrHex { +impl From for NumberOrHex { fn from(n: u64) -> Self { NumberOrHex::Number(n) } } -impl From for NumberOrHex { +impl From for NumberOrHex { fn from(n: U256) -> Self { NumberOrHex::Hex(n) } } +/// An error type that signals an out-of-range conversion attempt. +pub struct TryFromIntError(pub(crate) ()); + +impl TryFrom for u32 { + type Error = TryFromIntError; + fn try_from(num_or_hex: NumberOrHex) -> Result { + let num_or_hex = num_or_hex.into_u256(); + if num_or_hex > U256::from(u32::max_value()) { + return Err(TryFromIntError(())); + } else { + Ok(num_or_hex.as_u32()) + } + } +} + +impl TryFrom for u64 { + type Error = TryFromIntError; + fn try_from(num_or_hex: NumberOrHex) -> Result { + let num_or_hex = num_or_hex.into_u256(); + if num_or_hex > U256::from(u64::max_value()) { + return Err(TryFromIntError(())); + } else { + Ok(num_or_hex.as_u64()) + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -81,10 +95,11 @@ mod tests { #[test] fn should_serialize_and_deserialize() { - assert_deser(r#""0x1234""#, NumberOrHex::::Hex(0x1234.into())); - assert_deser(r#""0x0""#, NumberOrHex::::Hex(0.into())); - assert_deser(r#"5"#, NumberOrHex::Number(5_u64)); - assert_deser(r#"10000"#, NumberOrHex::Number(10000_u32)); - assert_deser(r#"0"#, NumberOrHex::Number(0_u16)); + assert_deser(r#""0x1234""#, NumberOrHex::Hex(0x1234.into())); + assert_deser(r#""0x0""#, NumberOrHex::Hex(0.into())); + assert_deser(r#"5"#, NumberOrHex::Number(5)); + assert_deser(r#"10000"#, NumberOrHex::Number(10000)); + assert_deser(r#"0"#, NumberOrHex::Number(0)); + assert_deser(r#"1000000000000"#, NumberOrHex::Number(1000000000000)); } } diff --git a/primitives/runtime-interface/Cargo.toml b/primitives/runtime-interface/Cargo.toml index 3a3d625b5fc90..12d070b47c2d3 100644 --- a/primitives/runtime-interface/Cargo.toml +++ b/primitives/runtime-interface/Cargo.toml @@ -18,7 +18,7 @@ sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } sp-tracing = { version = "2.0.0-rc3", default-features = false, path = "../tracing" } sp-runtime-interface-proc-macro = { version = "2.0.0-rc3", path = "proc-macro" } sp-externalities = { version = "0.8.0-rc3", optional = true, path = "../externalities" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } static_assertions = "1.0.0" primitive-types = { version = "0.7.0", default-features = false } diff --git a/primitives/runtime-interface/test/src/lib.rs b/primitives/runtime-interface/test/src/lib.rs index 06bc4e8ed8d46..109caab6062f8 100644 --- a/primitives/runtime-interface/test/src/lib.rs +++ b/primitives/runtime-interface/test/src/lib.rs @@ -55,7 +55,6 @@ fn call_wasm_method_with_result( &mut ext_ext, sp_core::traits::MissingHostFunctions::Disallow, ).map_err(|e| format!("Failed to execute `{}`: {}", method, e))?; - Ok(ext) } diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index a81c2515c8174..d3508c0e8b5af 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../core" } sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path = "../application-crypto" } sp-arithmetic = { version = "2.0.0-rc3", default-features = false, path = "../arithmetic" } @@ -28,6 +28,7 @@ impl-trait-for-tuples = "0.1.3" sp-inherents = { version = "2.0.0-rc3", default-features = false, path = "../inherents" } parity-util-mem = { version = "0.6.1", default-features = false, features = ["primitive-types"] } hash256-std-hasher = { version = "0.15.2", default-features = false } +either = { version = "1.5", default-features = false } [dev-dependencies] serde_json = "1.0.41" @@ -51,4 +52,5 @@ std = [ "sp-inherents/std", "parity-util-mem/std", "hash256-std-hasher/std", + "either/use_std", ] diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index fe156fe738773..a8a518fd7b692 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -81,6 +81,8 @@ pub use sp_arithmetic::biguint; pub use random_number_generator::RandomNumberGenerator; +pub use either::Either; + /// An abstraction over justification for a block's validity under a consensus algorithm. /// /// Essentially a finality proof. The exact formulation will vary between consensus diff --git a/primitives/runtime/src/offchain/storage_lock.rs b/primitives/runtime/src/offchain/storage_lock.rs index f8defa422459f..4718d2e3ddea1 100644 --- a/primitives/runtime/src/offchain/storage_lock.rs +++ b/primitives/runtime/src/offchain/storage_lock.rs @@ -264,6 +264,24 @@ impl<'a, L: Lockable> StorageLock<'a, L> { } } + /// Extend active lock's deadline + fn extend_active_lock(&mut self) -> Result<::Deadline, ()> { + let res = self.value_ref.mutate(|s: Option>| -> Result<::Deadline, ()> { + match s { + // lock is present and is still active, extend the lock. + Some(Some(deadline)) if !::has_expired(&deadline) => + Ok(self.lockable.deadline()), + // other cases + _ => Err(()), + } + }); + match res { + Ok(Ok(deadline)) => Ok(deadline), + Ok(Err(_)) => Err(()), + Err(e) => Err(e), + } + } + /// Internal lock helper to avoid lifetime conflicts. fn try_lock_inner( &mut self, @@ -337,6 +355,19 @@ impl<'a, 'b, L: Lockable> StorageLockGuard<'a, 'b, L> { pub fn forget(mut self) { let _ = self.lock.take(); } + + /// Extend the lock by guard deadline if it already exists. + /// + /// i.e. large sets of items for which it is hard to calculate a + /// meaning full conservative deadline which does not block for a + /// very long time on node termination. + pub fn extend_lock(&mut self) -> Result<::Deadline, ()> { + if let Some(ref mut lock) = self.lock { + lock.extend_active_lock() + } else { + Err(()) + } + } } impl<'a, 'b, L: Lockable> Drop for StorageLockGuard<'a, 'b, L> { @@ -512,4 +543,51 @@ mod tests { let opt = state.read().persistent_storage.get(b"", b"lock_3"); assert!(opt.is_some()); } + + #[test] + fn extend_active_lock() { + let (offchain, state) = testing::TestOffchainExt::new(); + let mut t = TestExternalities::default(); + t.register_extension(OffchainExt::new(offchain)); + + t.execute_with(|| { + let lock_expiration = Duration::from_millis(300); + + let mut lock = StorageLock::<'_, Time>::with_deadline(b"lock_4", lock_expiration); + let mut guard = lock.lock(); + + // sleep_until < lock_expiration + offchain::sleep_until(offchain::timestamp().add(Duration::from_millis(200))); + + // the lock is still active, extend it successfully + assert_eq!(guard.extend_lock().is_ok(), true); + + // sleep_until < deadline + offchain::sleep_until(offchain::timestamp().add(Duration::from_millis(200))); + + // the lock is still active, try_lock will fail + let mut lock = StorageLock::<'_, Time>::with_deadline(b"lock_4", lock_expiration); + let res = lock.try_lock(); + assert_eq!(res.is_ok(), false); + + // sleep again untill sleep_until > deadline + offchain::sleep_until(offchain::timestamp().add(Duration::from_millis(200))); + + // the lock has expired, failed to extend it + assert_eq!(guard.extend_lock().is_ok(), false); + guard.forget(); + + // try_lock will succeed + let mut lock = StorageLock::<'_, Time>::with_deadline(b"lock_4", lock_expiration); + let res = lock.try_lock(); + assert!(res.is_ok()); + let guard = res.unwrap(); + + guard.forget(); + }); + + // lock must have been cleared at this point + let opt = state.read().persistent_storage.get(b"", b"lock_4"); + assert_eq!(opt.unwrap(), vec![132_u8, 3u8, 0, 0, 0, 0, 0, 0]); // 132 + 256 * 3 = 900 + } } diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index fb46ba1dfa92b..b1739269e66be 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -144,7 +144,7 @@ impl< } /// An error type that indicates that the origin is invalid. -#[derive(Encode, Decode)] +#[derive(Encode, Decode, RuntimeDebug)] pub struct BadOrigin; impl From for &'static str { diff --git a/primitives/runtime/src/transaction_validity.rs b/primitives/runtime/src/transaction_validity.rs index fc2465a068fe3..1aad9e75aec34 100644 --- a/primitives/runtime/src/transaction_validity.rs +++ b/primitives/runtime/src/transaction_validity.rs @@ -104,7 +104,7 @@ impl From for &'static str { InvalidTransaction::BadMandatory => "A call was labelled as mandatory, but resulted in an Error.", InvalidTransaction::MandatoryDispatch => - "Tranaction dispatch is mandatory; transactions may not have mandatory dispatches.", + "Transaction dispatch is mandatory; transactions may not have mandatory dispatches.", InvalidTransaction::Custom(_) => "InvalidTransaction custom error", } } diff --git a/primitives/sandbox/Cargo.toml b/primitives/sandbox/Cargo.toml index 1a2175aebde30..dfd3a440535e8 100755 --- a/primitives/sandbox/Cargo.toml +++ b/primitives/sandbox/Cargo.toml @@ -17,7 +17,7 @@ sp-core = { version = "2.0.0-rc3", default-features = false, path = "../core" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } sp-io = { version = "2.0.0-rc3", default-features = false, path = "../io" } sp-wasm-interface = { version = "2.0.0-rc3", default-features = false, path = "../wasm-interface" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } [dev-dependencies] wabt = "0.9.2" diff --git a/primitives/session/Cargo.toml b/primitives/session/Cargo.toml index b3dd297ceb1a4..4abcb80d24162 100644 --- a/primitives/session/Cargo.toml +++ b/primitives/session/Cargo.toml @@ -12,7 +12,7 @@ description = "Primitives for sessions" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-api = { version = "2.0.0-rc3", default-features = false, path = "../api" } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../core" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } diff --git a/primitives/staking/Cargo.toml b/primitives/staking/Cargo.toml index ce44d8a0f7f78..7ec400d74a3db 100644 --- a/primitives/staking/Cargo.toml +++ b/primitives/staking/Cargo.toml @@ -12,7 +12,7 @@ description = "A crate which contains primitives that are useful for implementat targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../runtime" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } diff --git a/primitives/state-machine/Cargo.toml b/primitives/state-machine/Cargo.toml index 22dc73fc7e701..29c8676f7e1cb 100644 --- a/primitives/state-machine/Cargo.toml +++ b/primitives/state-machine/Cargo.toml @@ -16,19 +16,22 @@ targets = ["x86_64-unknown-linux-gnu"] log = "0.4.8" parking_lot = "0.10.0" hash-db = "0.15.2" -trie-db = "0.20.1" +trie-db = "0.21.0" trie-root = "0.16.0" sp-trie = { version = "2.0.0-rc3", path = "../trie" } sp-core = { version = "2.0.0-rc3", path = "../core" } sp-panic-handler = { version = "2.0.0-rc3", path = "../panic-handler" } -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } num-traits = "0.2.8" rand = "0.7.2" sp-externalities = { version = "0.8.0-rc3", path = "../externalities" } +itertools = "0.9" +smallvec = "1.4" [dev-dependencies] hex-literal = "0.2.1" sp-runtime = { version = "2.0.0-rc3", path = "../runtime" } +pretty_assertions = "0.6.1" [features] default = [] diff --git a/primitives/state-machine/src/basic.rs b/primitives/state-machine/src/basic.rs index 917e41f33d78b..dbb4c6c2b82f2 100644 --- a/primitives/state-machine/src/basic.rs +++ b/primitives/state-machine/src/basic.rs @@ -307,6 +307,18 @@ impl Externalities for BasicExternalities { Ok(None) } + fn storage_start_transaction(&mut self) { + unimplemented!("Transactions are not supported by BasicExternalities"); + } + + fn storage_rollback_transaction(&mut self) -> Result<(), ()> { + unimplemented!("Transactions are not supported by BasicExternalities"); + } + + fn storage_commit_transaction(&mut self) -> Result<(), ()> { + unimplemented!("Transactions are not supported by BasicExternalities"); + } + fn wipe(&mut self) {} fn commit(&mut self) {} diff --git a/primitives/state-machine/src/changes_trie/build.rs b/primitives/state-machine/src/changes_trie/build.rs index f9698f1a31dbf..bf910e2c4f7fb 100644 --- a/primitives/state-machine/src/changes_trie/build.rs +++ b/primitives/state-machine/src/changes_trie/build.rs @@ -25,7 +25,7 @@ use num_traits::One; use crate::{ StorageKey, backend::Backend, - overlayed_changes::OverlayedChanges, + overlayed_changes::{OverlayedChanges, OverlayedValue}, trie_backend_essence::TrieBackendEssence, changes_trie::{ AnchorBlockId, ConfigurationRange, Storage, BlockNumber, @@ -43,7 +43,7 @@ pub(crate) fn prepare_input<'a, B, H, Number>( backend: &'a B, storage: &'a dyn Storage, config: ConfigurationRange<'a, Number>, - changes: &'a OverlayedChanges, + overlay: &'a OverlayedChanges, parent: &'a AnchorBlockId, ) -> Result<( impl Iterator> + 'a, @@ -60,7 +60,7 @@ pub(crate) fn prepare_input<'a, B, H, Number>( let (extrinsics_input, children_extrinsics_input) = prepare_extrinsics_input( backend, &number, - changes, + overlay, )?; let (digest_input, mut children_digest_input, digest_input_blocks) = prepare_digest_input::( parent, @@ -96,7 +96,7 @@ pub(crate) fn prepare_input<'a, B, H, Number>( fn prepare_extrinsics_input<'a, B, H, Number>( backend: &'a B, block: &Number, - changes: &'a OverlayedChanges, + overlay: &'a OverlayedChanges, ) -> Result<( impl Iterator> + 'a, BTreeMap, impl Iterator> + 'a>, @@ -108,20 +108,21 @@ fn prepare_extrinsics_input<'a, B, H, Number>( { let mut children_result = BTreeMap::new(); - for child_info in changes.child_infos() { + for (child_changes, child_info) in overlay.children() { let child_index = ChildIndex:: { block: block.clone(), storage_key: child_info.prefixed_storage_key(), }; let iter = prepare_extrinsics_input_inner( - backend, block, changes, - Some(child_info.clone()) + backend, block, overlay, + Some(child_info.clone()), + child_changes, )?; children_result.insert(child_index, iter); } - let top = prepare_extrinsics_input_inner(backend, block, changes, None)?; + let top = prepare_extrinsics_input_inner(backend, block, overlay, None, overlay.changes())?; Ok((top, children_result)) } @@ -129,40 +130,38 @@ fn prepare_extrinsics_input<'a, B, H, Number>( fn prepare_extrinsics_input_inner<'a, B, H, Number>( backend: &'a B, block: &Number, - changes: &'a OverlayedChanges, + overlay: &'a OverlayedChanges, child_info: Option, + changes: impl Iterator ) -> Result> + 'a, String> where B: Backend, H: Hasher, Number: BlockNumber, { - changes.changes(child_info.as_ref()) - .filter(|( _, v)| v.extrinsics().is_some()) + changes + .filter(|( _, v)| v.extrinsics().next().is_some()) .try_fold(BTreeMap::new(), |mut map: BTreeMap<&[u8], (ExtrinsicIndex, Vec)>, (k, v)| { match map.entry(k) { Entry::Vacant(entry) => { // ignore temporary values (values that have null value at the end of operation // AND are not in storage at the beginning of operation if let Some(child_info) = child_info.as_ref() { - if !changes.child_storage(child_info, k).map(|v| v.is_some()).unwrap_or_default() { + if !overlay.child_storage(child_info, k).map(|v| v.is_some()).unwrap_or_default() { if !backend.exists_child_storage(&child_info, k) .map_err(|e| format!("{}", e))? { return Ok(map); } } } else { - if !changes.storage(k).map(|v| v.is_some()).unwrap_or_default() { + if !overlay.storage(k).map(|v| v.is_some()).unwrap_or_default() { if !backend.exists_storage(k).map_err(|e| format!("{}", e))? { return Ok(map); } } }; - let extrinsics = v.extrinsics() - .expect("filtered by filter() call above; qed") - .cloned() - .collect(); + let extrinsics = v.extrinsics().cloned().collect(); entry.insert((ExtrinsicIndex { block: block.clone(), key: k.to_vec(), @@ -173,9 +172,7 @@ fn prepare_extrinsics_input_inner<'a, B, H, Number>( // AND we are checking it before insertion let extrinsics = &mut entry.get_mut().1; extrinsics.extend( - v.extrinsics() - .expect("filtered by filter() call above; qed") - .cloned() + v.extrinsics().cloned() ); extrinsics.sort_unstable(); }, @@ -404,6 +401,8 @@ mod test { let mut changes = OverlayedChanges::default(); changes.set_collect_extrinsics(true); + changes.start_transaction(); + changes.set_extrinsic_index(1); changes.set_storage(vec![101], Some(vec![203])); @@ -411,7 +410,7 @@ mod test { changes.set_storage(vec![100], Some(vec![202])); changes.set_child_storage(&child_info_1, vec![100], Some(vec![202])); - changes.commit_prospective(); + changes.commit_transaction().unwrap(); changes.set_extrinsic_index(0); changes.set_storage(vec![100], Some(vec![0])); diff --git a/primitives/state-machine/src/ext.rs b/primitives/state-machine/src/ext.rs index 7e805250e726a..2cd63cde975de 100644 --- a/primitives/state-machine/src/ext.rs +++ b/primitives/state-machine/src/ext.rs @@ -37,6 +37,10 @@ use std::{error, fmt, any::{Any, TypeId}}; use log::{warn, trace}; const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within runtime"; +const BENCHMARKING_FN: &str = "\ + This is a special fn only for benchmarking where a database commit happens from the runtime. + For that reason client started transactions before calling into runtime are not allowed. + Without client transactions the loop condition garantuees the success of the tx close."; /// Errors that can occur when interacting with the externalities. #[derive(Debug, Copy, Clone)] @@ -147,7 +151,7 @@ where self.backend.pairs().iter() .map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec()))) - .chain(self.overlay.changes(None).map(|(k, v)| (k.clone(), v.value().cloned()))) + .chain(self.overlay.changes().map(|(k, v)| (k.clone(), v.value().cloned()))) .collect::>() .into_iter() .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val))) @@ -477,15 +481,14 @@ where ); root.encode() } else { + let root = if let Some((changes, info)) = self.overlay.child_changes(storage_key) { + let delta = changes.map(|(k, v)| (k.as_ref(), v.value().map(AsRef::as_ref))); + Some(self.backend.child_storage_root(info, delta)) + } else { + None + }; - if let Some(child_info) = self.overlay.default_child_info(storage_key) { - let (root, is_empty, _) = { - let delta = self.overlay.changes(Some(child_info)) - .map(|(k, v)| (k.as_ref(), v.value().map(AsRef::as_ref))); - - self.backend.child_storage_root(child_info, delta) - }; - + if let Some((root, is_empty, _)) = root { let root = root.encode(); // We store update in the overlay in order to be able to use 'self.storage_transaction' // cache. This is brittle as it rely on Ext only querying the trie backend for @@ -547,20 +550,37 @@ where root.map(|r| r.map(|o| o.encode())) } + fn storage_start_transaction(&mut self) { + self.overlay.start_transaction() + } + + fn storage_rollback_transaction(&mut self) -> Result<(), ()> { + self.mark_dirty(); + self.overlay.rollback_transaction().map_err(|_| ()) + } + + fn storage_commit_transaction(&mut self) -> Result<(), ()> { + self.overlay.commit_transaction().map_err(|_| ()) + } + fn wipe(&mut self) { - self.overlay.discard_prospective(); + for _ in 0..self.overlay.transaction_depth() { + self.overlay.rollback_transaction().expect(BENCHMARKING_FN); + } self.overlay.drain_storage_changes( &self.backend, None, Default::default(), self.storage_transaction_cache, ).expect(EXT_NOT_ALLOWED_TO_FAIL); - self.storage_transaction_cache.reset(); - self.backend.wipe().expect(EXT_NOT_ALLOWED_TO_FAIL) + self.backend.wipe().expect(EXT_NOT_ALLOWED_TO_FAIL); + self.mark_dirty(); } fn commit(&mut self) { - self.overlay.commit_prospective(); + for _ in 0..self.overlay.transaction_depth() { + self.overlay.commit_transaction().expect(BENCHMARKING_FN); + } let changes = self.overlay.drain_storage_changes( &self.backend, None, @@ -571,7 +591,7 @@ where changes.transaction_storage_root, changes.transaction, ).expect(EXT_NOT_ALLOWED_TO_FAIL); - self.storage_transaction_cache.reset(); + self.mark_dirty(); } } diff --git a/primitives/state-machine/src/lib.rs b/primitives/state-machine/src/lib.rs index 693a7bc12fad0..e5e48bc47cd48 100644 --- a/primitives/state-machine/src/lib.rs +++ b/primitives/state-machine/src/lib.rs @@ -79,6 +79,10 @@ pub use in_memory_backend::new_in_mem; pub use stats::{UsageInfo, UsageUnit, StateMachineStats}; pub use sp_core::traits::CloneableSpawn; +const PROOF_CLOSE_TRANSACTION: &str = "\ + Closing a transaction that was started in this function. Client initiated transactions + are protected from being closed by the runtime. qed"; + type CallResult = Result, E>; /// Default handler of the execution manager. @@ -297,6 +301,8 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where None => &mut cache, }; + self.overlay.enter_runtime().expect("StateMachine is never called from the runtime; qed"); + let mut ext = Ext::new( self.overlay, self.offchain_overlay, @@ -324,6 +330,9 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where native_call, ); + self.overlay.exit_runtime() + .expect("Runtime is not able to call this function in the overlay; qed"); + trace!( target: "state", "{:04x}: Return. Native={:?}, Result={:?}", id, @@ -347,11 +356,11 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where CallResult, ) -> CallResult { - let pending_changes = self.overlay.clone_pending(); + self.overlay.start_transaction(); let (result, was_native) = self.execute_aux(true, native_call.take()); if was_native { - self.overlay.replace_pending(pending_changes); + self.overlay.rollback_transaction().expect(PROOF_CLOSE_TRANSACTION); let (wasm_result, _) = self.execute_aux( false, native_call, @@ -366,6 +375,7 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where on_consensus_failure(wasm_result, result) } } else { + self.overlay.commit_transaction().expect(PROOF_CLOSE_TRANSACTION); result } } @@ -378,16 +388,17 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where R: Decode + Encode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, { - let pending_changes = self.overlay.clone_pending(); + self.overlay.start_transaction(); let (result, was_native) = self.execute_aux( true, native_call.take(), ); if !was_native || result.is_ok() { + self.overlay.commit_transaction().expect(PROOF_CLOSE_TRANSACTION); result } else { - self.overlay.replace_pending(pending_changes); + self.overlay.rollback_transaction().expect(PROOF_CLOSE_TRANSACTION); let (wasm_result, _) = self.execute_aux( false, native_call, @@ -977,7 +988,7 @@ mod tests { let mut overlay = OverlayedChanges::default(); overlay.set_storage(b"aba".to_vec(), Some(b"1312".to_vec())); overlay.set_storage(b"bab".to_vec(), Some(b"228".to_vec())); - overlay.commit_prospective(); + overlay.start_transaction(); overlay.set_storage(b"abd".to_vec(), Some(b"69".to_vec())); overlay.set_storage(b"bbd".to_vec(), Some(b"42".to_vec())); @@ -994,10 +1005,10 @@ mod tests { ); ext.clear_prefix(b"ab"); } - overlay.commit_prospective(); + overlay.commit_transaction().unwrap(); assert_eq!( - overlay.changes(None).map(|(k, v)| (k.clone(), v.value().cloned())) + overlay.changes().map(|(k, v)| (k.clone(), v.value().cloned())) .collect::>(), map![ b"abc".to_vec() => None.into(), @@ -1083,7 +1094,7 @@ mod tests { Some(vec![reference_data[0].clone()].encode()), ); } - overlay.commit_prospective(); + overlay.start_transaction(); { let mut ext = Ext::new( &mut overlay, @@ -1102,7 +1113,7 @@ mod tests { Some(reference_data.encode()), ); } - overlay.discard_prospective(); + overlay.rollback_transaction().unwrap(); { let ext = Ext::new( &mut overlay, @@ -1145,7 +1156,7 @@ mod tests { ext.clear_storage(key.as_slice()); ext.storage_append(key.clone(), Item::InitializationItem.encode()); } - overlay.commit_prospective(); + overlay.start_transaction(); // For example, first transaction resulted in panic during block building { @@ -1170,7 +1181,7 @@ mod tests { Some(vec![Item::InitializationItem, Item::DiscardedItem].encode()), ); } - overlay.discard_prospective(); + overlay.rollback_transaction().unwrap(); // Then we apply next transaction which is valid this time. { @@ -1196,7 +1207,7 @@ mod tests { ); } - overlay.commit_prospective(); + overlay.start_transaction(); // Then only initlaization item and second (commited) item should persist. { @@ -1306,4 +1317,42 @@ mod tests { } assert!(!duplicate); } + + #[test] + fn set_storage_empty_allowed() { + let initial: BTreeMap<_, _> = map![ + b"aaa".to_vec() => b"0".to_vec(), + b"bbb".to_vec() => b"".to_vec() + ]; + let mut state = InMemoryBackend::::from(initial); + let backend = state.as_trie_backend().unwrap(); + + let mut overlay = OverlayedChanges::default(); + overlay.start_transaction(); + overlay.set_storage(b"ccc".to_vec(), Some(b"".to_vec())); + assert_eq!(overlay.storage(b"ccc"), Some(Some(&[][..]))); + overlay.commit_transaction().unwrap(); + overlay.start_transaction(); + assert_eq!(overlay.storage(b"ccc"), Some(Some(&[][..]))); + assert_eq!(overlay.storage(b"bbb"), None); + + { + let mut offchain_overlay = Default::default(); + let mut cache = StorageTransactionCache::default(); + let mut ext = Ext::new( + &mut overlay, + &mut offchain_overlay, + &mut cache, + backend, + changes_trie::disabled_state::<_, u64>(), + None, + ); + assert_eq!(ext.storage(b"bbb"), Some(vec![])); + assert_eq!(ext.storage(b"ccc"), Some(vec![])); + ext.clear_storage(b"ccc"); + assert_eq!(ext.storage(b"ccc"), None); + } + overlay.commit_transaction().unwrap(); + assert_eq!(overlay.storage(b"ccc"), Some(None)); + } } diff --git a/primitives/state-machine/src/overlayed_changes/changeset.rs b/primitives/state-machine/src/overlayed_changes/changeset.rs new file mode 100644 index 0000000000000..fe43c0ea99d89 --- /dev/null +++ b/primitives/state-machine/src/overlayed_changes/changeset.rs @@ -0,0 +1,752 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +//! Houses the code that implements the transactional overlay storage. + +use super::{StorageKey, StorageValue}; + +use itertools::Itertools; +use std::collections::{HashSet, BTreeMap, BTreeSet}; +use smallvec::SmallVec; +use log::warn; + +const PROOF_OVERLAY_NON_EMPTY: &str = "\ + An OverlayValue is always created with at least one transaction and dropped as soon + as the last transaction is removed; qed"; + +type DirtyKeysSets = SmallVec<[HashSet; 5]>; +type Transactions = SmallVec<[InnerValue; 5]>; + +/// Error returned when trying to commit or rollback while no transaction is open or +/// when the runtime is trying to close a transaction started by the client. +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] +pub struct NoOpenTransaction; + +/// Error when calling `enter_runtime` when already being in runtime execution mode. +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] +pub struct AlreadyInRuntime; + +/// Error when calling `exit_runtime` when not being in runtime exection mdde. +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] +pub struct NotInRuntime; + +/// Describes in which mode the node is currently executing. +#[derive(Debug, Clone, Copy)] +pub enum ExecutionMode { + /// Exeuting in client mode: Removal of all transactions possible. + Client, + /// Executing in runtime mode: Transactions started by the client are protected. + Runtime, +} + +#[derive(Debug, Default, Clone)] +#[cfg_attr(test, derive(PartialEq))] +struct InnerValue { + /// Current value. None if value has been deleted. + value: Option, + /// The set of extrinsic indices where the values has been changed. + /// Is filled only if runtime has announced changes trie support. + extrinsics: BTreeSet, +} + +/// An overlay that contains all versions of a value for a specific key. +#[derive(Debug, Default, Clone)] +#[cfg_attr(test, derive(PartialEq))] +pub struct OverlayedValue { + /// The individual versions of that value. + /// One entry per transactions during that the value was actually written. + transactions: Transactions, +} + +/// Holds a set of changes with the ability modify them using nested transactions. +#[derive(Debug, Default, Clone)] +pub struct OverlayedChangeSet { + /// Stores the changes that this overlay constitutes. + changes: BTreeMap, + /// Stores which keys are dirty per transaction. Needed in order to determine which + /// values to merge into the parent transaction on commit. The length of this vector + /// therefore determines how many nested transactions are currently open (depth). + dirty_keys: DirtyKeysSets, + /// The number of how many transactions beginning from the first transactions are started + /// by the client. Those transactions are protected against close (commit, rollback) + /// when in runtime mode. + num_client_transactions: usize, + /// Determines whether the node is using the overlay from the client or the runtime. + execution_mode: ExecutionMode, +} + +impl Default for ExecutionMode { + fn default() -> Self { + Self::Client + } +} + +impl OverlayedValue { + /// The value as seen by the current transaction. + pub fn value(&self) -> Option<&StorageValue> { + self.transactions.last().expect(PROOF_OVERLAY_NON_EMPTY).value.as_ref() + } + + /// Unique list of extrinsic indices which modified the value. + pub fn extrinsics(&self) -> impl Iterator { + self.transactions.iter().flat_map(|t| t.extrinsics.iter()).unique() + } + + /// Mutable reference to the most recent version. + fn value_mut(&mut self) -> &mut Option { + &mut self.transactions.last_mut().expect(PROOF_OVERLAY_NON_EMPTY).value + } + + /// Remove the last version and return it. + fn pop_transaction(&mut self) -> InnerValue { + self.transactions.pop().expect(PROOF_OVERLAY_NON_EMPTY) + } + + /// Mutable reference to the set which holds the indices for the **current transaction only**. + fn transaction_extrinsics_mut(&mut self) -> &mut BTreeSet { + &mut self.transactions.last_mut().expect(PROOF_OVERLAY_NON_EMPTY).extrinsics + } + + /// Writes a new version of a value. + /// + /// This makes sure that the old version is not overwritten and can be properly + /// rolled back when required. + fn set( + &mut self, + value: Option, + first_write_in_tx: bool, + at_extrinsic: Option, + ) { + if first_write_in_tx || self.transactions.is_empty() { + self.transactions.push(InnerValue { + value, + .. Default::default() + }); + } else { + *self.value_mut() = value; + } + + if let Some(extrinsic) = at_extrinsic { + self.transaction_extrinsics_mut().insert(extrinsic); + } + } +} + +/// Inserts a key into the dirty set. +/// +/// Returns true iff we are currently have at least one open transaction and if this +/// is the first write to the given key that transaction. +fn insert_dirty(set: &mut DirtyKeysSets, key: StorageKey) -> bool { + set.last_mut().map(|dk| dk.insert(key)).unwrap_or_default() +} + +impl OverlayedChangeSet { + /// Create a new changeset at the same transaction state but without any contents. + /// + /// This changeset might be created when there are already open transactions. + /// We need to catch up here so that the child is at the same transaction depth. + pub fn spawn_child(&self) -> Self { + use std::iter::repeat; + Self { + dirty_keys: repeat(HashSet::new()).take(self.transaction_depth()).collect(), + num_client_transactions: self.num_client_transactions, + execution_mode: self.execution_mode, + .. Default::default() + } + } + + /// True if no changes at all are contained in the change set. + pub fn is_empty(&self) -> bool { + self.changes.is_empty() + } + + /// Get an optional reference to the value stored for the specified key. + pub fn get(&self, key: &[u8]) -> Option<&OverlayedValue> { + self.changes.get(key) + } + + /// Set a new value for the specified key. + /// + /// Can be rolled back or committed when called inside a transaction. + pub fn set( + &mut self, + key: StorageKey, + value: Option, + at_extrinsic: Option, + ) { + let overlayed = self.changes.entry(key.clone()).or_default(); + overlayed.set(value, insert_dirty(&mut self.dirty_keys, key), at_extrinsic); + } + + /// Get a mutable reference for a value. + /// + /// Can be rolled back or committed when called inside a transaction. + #[must_use = "A change was registered, so this value MUST be modified."] + pub fn modify( + &mut self, + key: StorageKey, + init: impl Fn() -> StorageValue, + at_extrinsic: Option, + ) -> &mut Option { + let overlayed = self.changes.entry(key.clone()).or_default(); + let first_write_in_tx = insert_dirty(&mut self.dirty_keys, key); + let clone_into_new_tx = if let Some(tx) = overlayed.transactions.last() { + if first_write_in_tx { + Some(tx.value.clone()) + } else { + None + } + } else { + Some(Some(init())) + }; + + if let Some(cloned) = clone_into_new_tx { + overlayed.set(cloned, first_write_in_tx, at_extrinsic); + } + overlayed.value_mut() + } + + /// Set all values to deleted which are matched by the predicate. + /// + /// Can be rolled back or committed when called inside a transaction. + pub fn clear_where( + &mut self, + predicate: impl Fn(&[u8], &OverlayedValue) -> bool, + at_extrinsic: Option, + ) { + for (key, val) in self.changes.iter_mut().filter(|(k, v)| predicate(k, v)) { + val.set(None, insert_dirty(&mut self.dirty_keys, key.to_owned()), at_extrinsic); + } + } + + /// Get a list of all changes as seen by current transaction. + pub fn changes(&self) -> impl Iterator { + self.changes.iter() + } + + /// Get the change that is next to the supplied key. + pub fn next_change(&self, key: &[u8]) -> Option<(&[u8], &OverlayedValue)> { + use std::ops::Bound; + let range = (Bound::Excluded(key), Bound::Unbounded); + self.changes.range::<[u8], _>(range).next().map(|(k, v)| (&k[..], v)) + } + + /// Consume this changeset and return all committed changes. + /// + /// Panics: + /// Panics if there are open transactions: `transaction_depth() > 0` + pub fn drain_commited(self) -> impl Iterator)> { + assert!(self.transaction_depth() == 0, "Drain is not allowed with open transactions."); + self.changes.into_iter().map(|(k, mut v)| (k, v.pop_transaction().value)) + } + + /// Returns the current nesting depth of the transaction stack. + /// + /// A value of zero means that no transaction is open and changes are committed on write. + pub fn transaction_depth(&self) -> usize { + self.dirty_keys.len() + } + + /// Call this before transfering control to the runtime. + /// + /// This protects all existing transactions from being removed by the runtime. + /// Calling this while already inside the runtime will return an error. + pub fn enter_runtime(&mut self) -> Result<(), AlreadyInRuntime> { + if let ExecutionMode::Runtime = self.execution_mode { + return Err(AlreadyInRuntime); + } + self.execution_mode = ExecutionMode::Runtime; + self.num_client_transactions = self.transaction_depth(); + Ok(()) + } + + /// Call this when control returns from the runtime. + /// + /// This commits all dangling transaction left open by the runtime. + /// Calling this while already outside the runtime will return an error. + pub fn exit_runtime(&mut self) -> Result<(), NotInRuntime> { + if let ExecutionMode::Client = self.execution_mode { + return Err(NotInRuntime); + } + self.execution_mode = ExecutionMode::Client; + if self.has_open_runtime_transactions() { + warn!( + "{} storage transactions are left open by the runtime. Those will be rolled back.", + self.transaction_depth() - self.num_client_transactions, + ); + } + while self.has_open_runtime_transactions() { + self.rollback_transaction() + .expect("The loop condition checks that the transaction depth is > 0; qed"); + } + Ok(()) + } + + /// Start a new nested transaction. + /// + /// This allows to either commit or roll back all changes that were made while this + /// transaction was open. Any transaction must be closed by either `commit_transaction` + /// or `rollback_transaction` before this overlay can be converted into storage changes. + /// + /// Changes made without any open transaction are committed immediately. + pub fn start_transaction(&mut self) { + self.dirty_keys.push(Default::default()); + } + + /// Rollback the last transaction started by `start_transaction`. + /// + /// Any changes made during that transaction are discarded. Returns an error if + /// there is no open transaction that can be rolled back. + pub fn rollback_transaction(&mut self) -> Result<(), NoOpenTransaction> { + self.close_transaction(true) + } + + /// Commit the last transaction started by `start_transaction`. + /// + /// Any changes made during that transaction are committed. Returns an error if + /// there is no open transaction that can be committed. + pub fn commit_transaction(&mut self) -> Result<(), NoOpenTransaction> { + self.close_transaction(false) + } + + fn close_transaction(&mut self, rollback: bool) -> Result<(), NoOpenTransaction> { + // runtime is not allowed to close transactions started by the client + if let ExecutionMode::Runtime = self.execution_mode { + if !self.has_open_runtime_transactions() { + return Err(NoOpenTransaction) + } + } + + for key in self.dirty_keys.pop().ok_or(NoOpenTransaction)? { + let overlayed = self.changes.get_mut(&key).expect("\ + A write to an OverlayedValue is recorded in the dirty key set. Before an + OverlayedValue is removed, its containing dirty set is removed. This + function is only called for keys that are in the dirty set. qed\ + "); + + if rollback { + overlayed.pop_transaction(); + + // We need to remove the key as an `OverlayValue` with no transactions + // violates its invariant of always having at least one transaction. + if overlayed.transactions.is_empty() { + self.changes.remove(&key); + } + } else { + let has_predecessor = if let Some(dirty_keys) = self.dirty_keys.last_mut() { + // Not the last tx: Did the previous tx write to this key? + !dirty_keys.insert(key) + } else { + // Last tx: Is there already a value in the committed set? + // Check against one rather than empty because the current tx is still + // in the list as it is popped later in this function. + overlayed.transactions.len() > 1 + }; + + // We only need to merge if there is an pre-existing value. It may be a value from + // the previous transaction or a value committed without any open transaction. + if has_predecessor { + let dropped_tx = overlayed.pop_transaction(); + *overlayed.value_mut() = dropped_tx.value; + overlayed.transaction_extrinsics_mut().extend(dropped_tx.extrinsics); + } + } + } + + Ok(()) + } + + fn has_open_runtime_transactions(&self) -> bool { + self.transaction_depth() > self.num_client_transactions + } +} + +#[cfg(test)] +mod test { + use super::*; + use pretty_assertions::assert_eq; + + type Changes<'a> = Vec<(&'a [u8], (Option<&'a [u8]>, Vec))>; + type Drained<'a> = Vec<(&'a [u8], Option<&'a [u8]>)>; + + fn assert_changes(is: &OverlayedChangeSet, expected: &Changes) { + let is: Changes = is.changes().map(|(k, v)| { + (k.as_ref(), (v.value().map(AsRef::as_ref), v.extrinsics().cloned().collect())) + }).collect(); + assert_eq!(&is, expected); + } + + fn assert_drained_changes(is: OverlayedChangeSet, expected: Changes) { + let is = is.drain_commited().collect::>(); + let expected = expected + .iter() + .map(|(k, v)| (k.to_vec(), v.0.map(From::from))).collect::>(); + assert_eq!(is, expected); + } + + fn assert_drained(is: OverlayedChangeSet, expected: Drained) { + let is = is.drain_commited().collect::>(); + let expected = expected + .iter() + .map(|(k, v)| (k.to_vec(), v.map(From::from))).collect::>(); + assert_eq!(is, expected); + } + + #[test] + fn no_transaction_works() { + let mut changeset = OverlayedChangeSet::default(); + assert_eq!(changeset.transaction_depth(), 0); + + changeset.set(b"key0".to_vec(), Some(b"val0".to_vec()), Some(1)); + changeset.set(b"key1".to_vec(), Some(b"val1".to_vec()), Some(2)); + changeset.set(b"key0".to_vec(), Some(b"val0-1".to_vec()), Some(9)); + + assert_drained(changeset, vec![ + (b"key0", Some(b"val0-1")), + (b"key1", Some(b"val1")), + ]); + } + + #[test] + fn transaction_works() { + let mut changeset = OverlayedChangeSet::default(); + assert_eq!(changeset.transaction_depth(), 0); + + // no transaction: committed on set + changeset.set(b"key0".to_vec(), Some(b"val0".to_vec()), Some(1)); + changeset.set(b"key1".to_vec(), Some(b"val1".to_vec()), Some(1)); + changeset.set(b"key0".to_vec(), Some(b"val0-1".to_vec()), Some(10)); + + changeset.start_transaction(); + assert_eq!(changeset.transaction_depth(), 1); + + // we will commit that later + changeset.set(b"key42".to_vec(), Some(b"val42".to_vec()), Some(42)); + changeset.set(b"key99".to_vec(), Some(b"val99".to_vec()), Some(99)); + + changeset.start_transaction(); + assert_eq!(changeset.transaction_depth(), 2); + + // we will roll that back + changeset.set(b"key42".to_vec(), Some(b"val42-rolled".to_vec()), Some(421)); + changeset.set(b"key7".to_vec(), Some(b"val7-rolled".to_vec()), Some(77)); + changeset.set(b"key0".to_vec(), Some(b"val0-rolled".to_vec()), Some(1000)); + changeset.set(b"key5".to_vec(), Some(b"val5-rolled".to_vec()), None); + + // changes contain all changes not only the commmited ones. + let all_changes: Changes = vec![ + (b"key0", (Some(b"val0-rolled"), vec![1, 10, 1000])), + (b"key1", (Some(b"val1"), vec![1])), + (b"key42", (Some(b"val42-rolled"), vec![42, 421])), + (b"key5", (Some(b"val5-rolled"), vec![])), + (b"key7", (Some(b"val7-rolled"), vec![77])), + (b"key99", (Some(b"val99"), vec![99])), + ]; + assert_changes(&changeset, &all_changes); + + // this should be no-op + changeset.start_transaction(); + assert_eq!(changeset.transaction_depth(), 3); + changeset.start_transaction(); + assert_eq!(changeset.transaction_depth(), 4); + changeset.rollback_transaction().unwrap(); + assert_eq!(changeset.transaction_depth(), 3); + changeset.commit_transaction().unwrap(); + assert_eq!(changeset.transaction_depth(), 2); + assert_changes(&changeset, &all_changes); + + // roll back our first transactions that actually contains something + changeset.rollback_transaction().unwrap(); + assert_eq!(changeset.transaction_depth(), 1); + + let rolled_back: Changes = vec![ + (b"key0", (Some(b"val0-1"), vec![1, 10])), + (b"key1", (Some(b"val1"), vec![1])), + (b"key42", (Some(b"val42"), vec![42])), + (b"key99", (Some(b"val99"), vec![99])), + ]; + assert_changes(&changeset, &rolled_back); + + changeset.commit_transaction().unwrap(); + assert_eq!(changeset.transaction_depth(), 0); + assert_changes(&changeset, &rolled_back); + + assert_drained_changes(changeset, rolled_back); + } + + #[test] + fn transaction_commit_then_rollback_works() { + let mut changeset = OverlayedChangeSet::default(); + assert_eq!(changeset.transaction_depth(), 0); + + changeset.set(b"key0".to_vec(), Some(b"val0".to_vec()), Some(1)); + changeset.set(b"key1".to_vec(), Some(b"val1".to_vec()), Some(1)); + changeset.set(b"key0".to_vec(), Some(b"val0-1".to_vec()), Some(10)); + + changeset.start_transaction(); + assert_eq!(changeset.transaction_depth(), 1); + + changeset.set(b"key42".to_vec(), Some(b"val42".to_vec()), Some(42)); + changeset.set(b"key99".to_vec(), Some(b"val99".to_vec()), Some(99)); + + changeset.start_transaction(); + assert_eq!(changeset.transaction_depth(), 2); + + changeset.set(b"key42".to_vec(), Some(b"val42-rolled".to_vec()), Some(421)); + changeset.set(b"key7".to_vec(), Some(b"val7-rolled".to_vec()), Some(77)); + changeset.set(b"key0".to_vec(), Some(b"val0-rolled".to_vec()), Some(1000)); + changeset.set(b"key5".to_vec(), Some(b"val5-rolled".to_vec()), None); + + let all_changes: Changes = vec![ + (b"key0", (Some(b"val0-rolled"), vec![1, 10, 1000])), + (b"key1", (Some(b"val1"), vec![1])), + (b"key42", (Some(b"val42-rolled"), vec![42, 421])), + (b"key5", (Some(b"val5-rolled"), vec![])), + (b"key7", (Some(b"val7-rolled"), vec![77])), + (b"key99", (Some(b"val99"), vec![99])), + ]; + assert_changes(&changeset, &all_changes); + + // this should be no-op + changeset.start_transaction(); + assert_eq!(changeset.transaction_depth(), 3); + changeset.start_transaction(); + assert_eq!(changeset.transaction_depth(), 4); + changeset.rollback_transaction().unwrap(); + assert_eq!(changeset.transaction_depth(), 3); + changeset.commit_transaction().unwrap(); + assert_eq!(changeset.transaction_depth(), 2); + assert_changes(&changeset, &all_changes); + + changeset.commit_transaction().unwrap(); + assert_eq!(changeset.transaction_depth(), 1); + + assert_changes(&changeset, &all_changes); + + changeset.rollback_transaction().unwrap(); + assert_eq!(changeset.transaction_depth(), 0); + + let rolled_back: Changes = vec![ + (b"key0", (Some(b"val0-1"), vec![1, 10])), + (b"key1", (Some(b"val1"), vec![1])), + ]; + assert_changes(&changeset, &rolled_back); + + assert_drained_changes(changeset, rolled_back); + } + + #[test] + fn modify_works() { + let mut changeset = OverlayedChangeSet::default(); + assert_eq!(changeset.transaction_depth(), 0); + let init = || b"valinit".to_vec(); + + // committed set + changeset.set(b"key0".to_vec(), Some(b"val0".to_vec()), Some(0)); + changeset.set(b"key1".to_vec(), None, Some(1)); + let val = changeset.modify(b"key3".to_vec(), init, Some(3)); + assert_eq!(val, &Some(b"valinit".to_vec())); + val.as_mut().unwrap().extend_from_slice(b"-modified"); + + changeset.start_transaction(); + assert_eq!(changeset.transaction_depth(), 1); + changeset.start_transaction(); + assert_eq!(changeset.transaction_depth(), 2); + + // non existing value -> init value should be returned + let val = changeset.modify(b"key2".to_vec(), init, Some(2)); + assert_eq!(val, &Some(b"valinit".to_vec())); + val.as_mut().unwrap().extend_from_slice(b"-modified"); + + // existing value should be returned by modify + let val = changeset.modify(b"key0".to_vec(), init, Some(10)); + assert_eq!(val, &Some(b"val0".to_vec())); + val.as_mut().unwrap().extend_from_slice(b"-modified"); + + // should work for deleted keys + let val = changeset.modify(b"key1".to_vec(), init, Some(20)); + assert_eq!(val, &None); + *val = Some(b"deleted-modified".to_vec()); + + let all_changes: Changes = vec![ + (b"key0", (Some(b"val0-modified"), vec![0, 10])), + (b"key1", (Some(b"deleted-modified"), vec![1, 20])), + (b"key2", (Some(b"valinit-modified"), vec![2])), + (b"key3", (Some(b"valinit-modified"), vec![3])), + ]; + assert_changes(&changeset, &all_changes); + changeset.commit_transaction().unwrap(); + assert_eq!(changeset.transaction_depth(), 1); + assert_changes(&changeset, &all_changes); + + changeset.rollback_transaction().unwrap(); + assert_eq!(changeset.transaction_depth(), 0); + let rolled_back: Changes = vec![ + (b"key0", (Some(b"val0"), vec![0])), + (b"key1", (None, vec![1])), + (b"key3", (Some(b"valinit-modified"), vec![3])), + ]; + assert_changes(&changeset, &rolled_back); + assert_drained_changes(changeset, rolled_back); + } + + #[test] + fn clear_works() { + let mut changeset = OverlayedChangeSet::default(); + + changeset.set(b"key0".to_vec(), Some(b"val0".to_vec()), Some(1)); + changeset.set(b"key1".to_vec(), Some(b"val1".to_vec()), Some(2)); + changeset.set(b"del1".to_vec(), Some(b"delval1".to_vec()), Some(3)); + changeset.set(b"del2".to_vec(), Some(b"delval2".to_vec()), Some(4)); + + changeset.start_transaction(); + + changeset.clear_where(|k, _| k.starts_with(b"del"), Some(5)); + + assert_changes(&changeset, &vec![ + (b"del1", (None, vec![3, 5])), + (b"del2", (None, vec![4, 5])), + (b"key0", (Some(b"val0"), vec![1])), + (b"key1", (Some(b"val1"), vec![2])), + ]); + + changeset.rollback_transaction().unwrap(); + + assert_changes(&changeset, &vec![ + (b"del1", (Some(b"delval1"), vec![3])), + (b"del2", (Some(b"delval2"), vec![4])), + (b"key0", (Some(b"val0"), vec![1])), + (b"key1", (Some(b"val1"), vec![2])), + ]); + } + + #[test] + fn next_change_works() { + let mut changeset = OverlayedChangeSet::default(); + + changeset.set(b"key0".to_vec(), Some(b"val0".to_vec()), Some(0)); + changeset.set(b"key1".to_vec(), Some(b"val1".to_vec()), Some(1)); + changeset.set(b"key2".to_vec(), Some(b"val2".to_vec()), Some(2)); + + changeset.start_transaction(); + + changeset.set(b"key3".to_vec(), Some(b"val3".to_vec()), Some(3)); + changeset.set(b"key4".to_vec(), Some(b"val4".to_vec()), Some(4)); + changeset.set(b"key11".to_vec(), Some(b"val11".to_vec()), Some(11)); + + assert_eq!(changeset.next_change(b"key0").unwrap().0, b"key1"); + assert_eq!(changeset.next_change(b"key0").unwrap().1.value(), Some(&b"val1".to_vec())); + assert_eq!(changeset.next_change(b"key1").unwrap().0, b"key11"); + assert_eq!(changeset.next_change(b"key1").unwrap().1.value(), Some(&b"val11".to_vec())); + assert_eq!(changeset.next_change(b"key11").unwrap().0, b"key2"); + assert_eq!(changeset.next_change(b"key11").unwrap().1.value(), Some(&b"val2".to_vec())); + assert_eq!(changeset.next_change(b"key2").unwrap().0, b"key3"); + assert_eq!(changeset.next_change(b"key2").unwrap().1.value(), Some(&b"val3".to_vec())); + assert_eq!(changeset.next_change(b"key3").unwrap().0, b"key4"); + assert_eq!(changeset.next_change(b"key3").unwrap().1.value(), Some(&b"val4".to_vec())); + assert_eq!(changeset.next_change(b"key4"), None); + + changeset.rollback_transaction().unwrap(); + + assert_eq!(changeset.next_change(b"key0").unwrap().0, b"key1"); + assert_eq!(changeset.next_change(b"key0").unwrap().1.value(), Some(&b"val1".to_vec())); + assert_eq!(changeset.next_change(b"key1").unwrap().0, b"key2"); + assert_eq!(changeset.next_change(b"key1").unwrap().1.value(), Some(&b"val2".to_vec())); + assert_eq!(changeset.next_change(b"key11").unwrap().0, b"key2"); + assert_eq!(changeset.next_change(b"key11").unwrap().1.value(), Some(&b"val2".to_vec())); + assert_eq!(changeset.next_change(b"key2"), None); + assert_eq!(changeset.next_change(b"key3"), None); + assert_eq!(changeset.next_change(b"key4"), None); + + } + + #[test] + fn no_open_tx_commit_errors() { + let mut changeset = OverlayedChangeSet::default(); + assert_eq!(changeset.transaction_depth(), 0); + assert_eq!(changeset.commit_transaction(), Err(NoOpenTransaction)); + } + + #[test] + fn no_open_tx_rollback_errors() { + let mut changeset = OverlayedChangeSet::default(); + assert_eq!(changeset.transaction_depth(), 0); + assert_eq!(changeset.rollback_transaction(), Err(NoOpenTransaction)); + } + + #[test] + fn unbalanced_transactions_errors() { + let mut changeset = OverlayedChangeSet::default(); + changeset.start_transaction(); + changeset.commit_transaction().unwrap(); + assert_eq!(changeset.commit_transaction(), Err(NoOpenTransaction)); + } + + #[test] + #[should_panic] + fn drain_with_open_transaction_panics() { + let mut changeset = OverlayedChangeSet::default(); + changeset.start_transaction(); + let _ = changeset.drain_commited(); + } + + #[test] + fn runtime_cannot_close_client_tx() { + let mut changeset = OverlayedChangeSet::default(); + changeset.start_transaction(); + changeset.enter_runtime().unwrap(); + changeset.start_transaction(); + changeset.commit_transaction().unwrap(); + assert_eq!(changeset.commit_transaction(), Err(NoOpenTransaction)); + assert_eq!(changeset.rollback_transaction(), Err(NoOpenTransaction)); + } + + #[test] + fn exit_runtime_closes_runtime_tx() { + let mut changeset = OverlayedChangeSet::default(); + + changeset.start_transaction(); + + changeset.set(b"key0".to_vec(), Some(b"val0".to_vec()), Some(1)); + + changeset.enter_runtime().unwrap(); + changeset.start_transaction(); + changeset.set(b"key1".to_vec(), Some(b"val1".to_vec()), Some(2)); + changeset.exit_runtime().unwrap(); + + changeset.commit_transaction().unwrap(); + assert_eq!(changeset.transaction_depth(), 0); + + assert_drained(changeset, vec![ + (b"key0", Some(b"val0")), + ]); + } + + #[test] + fn enter_exit_runtime_fails_when_already_in_requested_mode() { + let mut changeset = OverlayedChangeSet::default(); + + assert_eq!(changeset.exit_runtime(), Err(NotInRuntime)); + assert_eq!(changeset.enter_runtime(), Ok(())); + assert_eq!(changeset.enter_runtime(), Err(AlreadyInRuntime)); + assert_eq!(changeset.exit_runtime(), Ok(())); + assert_eq!(changeset.exit_runtime(), Err(NotInRuntime)); + } +} diff --git a/primitives/state-machine/src/overlayed_changes.rs b/primitives/state-machine/src/overlayed_changes/mod.rs similarity index 50% rename from primitives/state-machine/src/overlayed_changes.rs rename to primitives/state-machine/src/overlayed_changes/mod.rs index b0259c2b8592d..9a2b1c4197310 100644 --- a/primitives/state-machine/src/overlayed_changes.rs +++ b/primitives/state-machine/src/overlayed_changes/mod.rs @@ -17,6 +17,8 @@ //! The overlayed changes to state. +mod changeset; + use crate::{ backend::Backend, ChangesTrieTransaction, changes_trie::{ @@ -25,14 +27,16 @@ use crate::{ }, stats::StateMachineStats, }; +use self::changeset::OverlayedChangeSet; -use std::{mem, ops, collections::{HashMap, BTreeMap, BTreeSet}}; +use std::collections::HashMap; use codec::{Decode, Encode}; -use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo, ChildType}; +use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo}; use sp_core::offchain::storage::OffchainOverlayedChanges; - use hash_db::Hasher; +pub use self::changeset::{OverlayedValue, NoOpenTransaction, AlreadyInRuntime, NotInRuntime}; + /// Storage key. pub type StorageKey = Vec; @@ -45,43 +49,21 @@ pub type StorageCollection = Vec<(StorageKey, Option)>; /// In memory arrays of storage values for multiple child tries. pub type ChildStorageCollection = Vec<(StorageKey, StorageCollection)>; -/// The overlayed changes to state to be queried on top of the backend. +/// The set of changes that are overlaid onto the backend. /// -/// A transaction shares all prospective changes within an inner overlay -/// that can be cleared. +/// It allows changes to be modified using nestable transactions. #[derive(Debug, Default, Clone)] pub struct OverlayedChanges { - /// Changes that are not yet committed. - prospective: OverlayedChangeSet, - /// Committed changes. - committed: OverlayedChangeSet, + /// Top level storage changes. + top: OverlayedChangeSet, + /// Child storage changes. The map key is the child storage key without the common prefix. + children: HashMap, /// True if extrinsics stats must be collected. collect_extrinsics: bool, /// Collect statistic on this execution. stats: StateMachineStats, } -/// The storage value, used inside OverlayedChanges. -#[derive(Debug, Default, Clone)] -#[cfg_attr(test, derive(PartialEq))] -pub struct OverlayedValue { - /// Current value. None if value has been deleted. - value: Option, - /// The set of extrinsic indices where the values has been changed. - /// Is filled only if runtime has announced changes trie support. - extrinsics: Option>, -} - -/// Prospective or committed overlayed change set. -#[derive(Debug, Default, Clone)] -#[cfg_attr(test, derive(PartialEq))] -pub struct OverlayedChangeSet { - /// Top level storage changes. - top: BTreeMap, - /// Child storage changes. The map key is the child storage key without the common prefix. - children_default: HashMap, ChildInfo)>, -} - /// A storage changes structure that can be generated by the data collected in [`OverlayedChanges`]. /// /// This contains all the changes to the storage and transactions to apply theses changes to the @@ -174,45 +156,10 @@ impl Default for StorageChanges } } -#[cfg(test)] -impl std::iter::FromIterator<(StorageKey, OverlayedValue)> for OverlayedChangeSet { - fn from_iter>(iter: T) -> Self { - Self { - top: iter.into_iter().collect(), - children_default: Default::default(), - } - } -} - -impl OverlayedValue { - /// The most recent value contained in this overlay. - pub fn value(&self) -> Option<&StorageValue> { - self.value.as_ref() - } - - /// List of indices of extrinsics which modified the value using this overlay. - pub fn extrinsics(&self) -> Option> { - self.extrinsics.as_ref().map(|v| v.iter()) - } -} - -impl OverlayedChangeSet { - /// Whether the change set is empty. - pub fn is_empty(&self) -> bool { - self.top.is_empty() && self.children_default.is_empty() - } - - /// Clear the change set. - pub fn clear(&mut self) { - self.top.clear(); - self.children_default.clear(); - } -} - impl OverlayedChanges { - /// Whether the overlayed changes are empty. + /// Whether no changes are contained in the top nor in any of the child changes. pub fn is_empty(&self) -> bool { - self.prospective.is_empty() && self.committed.is_empty() + self.top.is_empty() && self.children.is_empty() } /// Ask to collect/not to collect extrinsics indices where key(s) has been changed. @@ -224,326 +171,241 @@ impl OverlayedChanges { /// to the backend); Some(None) if the key has been deleted. Some(Some(...)) for a key whose /// value has been set. pub fn storage(&self, key: &[u8]) -> Option> { - self.prospective.top.get(key) - .or_else(|| self.committed.top.get(key)) - .map(|x| { - let size_read = x.value.as_ref().map(|x| x.len() as u64).unwrap_or(0); - self.stats.tally_read_modified(size_read); - x.value.as_ref().map(AsRef::as_ref) - }) - } - - /// Returns mutable reference to current changed value (prospective). - /// If there is no value in the overlay, the default callback is used to initiate - /// the value. - /// Warning this function register a change, so the mutable reference MUST be modified. + self.top.get(key).map(|x| { + let value = x.value(); + let size_read = value.map(|x| x.len() as u64).unwrap_or(0); + self.stats.tally_read_modified(size_read); + value.map(AsRef::as_ref) + }) + } + + /// Returns mutable reference to current value. + /// If there is no value in the overlay, the given callback is used to initiate the value. + /// Warning this function registers a change, so the mutable reference MUST be modified. + /// + /// Can be rolled back or committed when called inside a transaction. #[must_use = "A change was registered, so this value MUST be modified."] pub fn value_mut_or_insert_with( &mut self, key: &[u8], init: impl Fn() -> StorageValue, ) -> &mut StorageValue { - let extrinsic_index = self.extrinsic_index(); - let committed = &self.committed.top; - - let mut entry = self.prospective.top.entry(key.to_vec()) - .or_insert_with(|| { - if let Some(overlay_state) = committed.get(key).cloned() { - overlay_state - } else { - OverlayedValue { value: Some(init()), ..Default::default() } - } - }); - - //if was deleted initialise back with empty vec - if entry.value.is_none() { - entry.value = Some(Default::default()); - } - if let Some(extrinsic) = extrinsic_index { - entry.extrinsics.get_or_insert_with(Default::default) - .insert(extrinsic); - } - entry.value.as_mut().expect("Initialized above; qed") + let value = self.top.modify(key.to_owned(), init, self.extrinsic_index()); + + // if the value was deleted initialise it back with an empty vec + value.get_or_insert_with(StorageValue::default) } /// Returns a double-Option: None if the key is unknown (i.e. and the query should be referred /// to the backend); Some(None) if the key has been deleted. Some(Some(...)) for a key whose /// value has been set. pub fn child_storage(&self, child_info: &ChildInfo, key: &[u8]) -> Option> { - if let Some(map) = self.prospective.children_default.get(child_info.storage_key()) { - if let Some(val) = map.0.get(key) { - let size_read = val.value.as_ref().map(|x| x.len() as u64).unwrap_or(0); - self.stats.tally_read_modified(size_read); - return Some(val.value.as_ref().map(AsRef::as_ref)); - } - } - - if let Some(map) = self.committed.children_default.get(child_info.storage_key()) { - if let Some(val) = map.0.get(key) { - let size_read = val.value.as_ref().map(|x| x.len() as u64).unwrap_or(0); - self.stats.tally_read_modified(size_read); - return Some(val.value.as_ref().map(AsRef::as_ref)); - } - } - - None + let map = self.children.get(child_info.storage_key())?; + let value = map.0.get(key)?.value(); + let size_read = value.map(|x| x.len() as u64).unwrap_or(0); + self.stats.tally_read_modified(size_read); + Some(value.map(AsRef::as_ref)) } - /// Inserts the given key-value pair into the prospective change set. + /// Set a new value for the specified key. /// - /// `None` can be used to delete a value specified by the given key. + /// Can be rolled back or committed when called inside a transaction. pub(crate) fn set_storage(&mut self, key: StorageKey, val: Option) { let size_write = val.as_ref().map(|x| x.len() as u64).unwrap_or(0); self.stats.tally_write_overlay(size_write); - let extrinsic_index = self.extrinsic_index(); - let entry = self.prospective.top.entry(key).or_default(); - entry.value = val; - - if let Some(extrinsic) = extrinsic_index { - entry.extrinsics.get_or_insert_with(Default::default) - .insert(extrinsic); - } + self.top.set(key, val, self.extrinsic_index()); } - /// Inserts the given key-value pair into the prospective child change set. + /// Set a new value for the specified key and child. /// /// `None` can be used to delete a value specified by the given key. + /// + /// Can be rolled back or committed when called inside a transaction. pub(crate) fn set_child_storage( &mut self, child_info: &ChildInfo, key: StorageKey, val: Option, ) { + let extrinsic_index = self.extrinsic_index(); let size_write = val.as_ref().map(|x| x.len() as u64).unwrap_or(0); self.stats.tally_write_overlay(size_write); - let extrinsic_index = self.extrinsic_index(); let storage_key = child_info.storage_key().to_vec(); - let map_entry = self.prospective.children_default.entry(storage_key) - .or_insert_with(|| (Default::default(), child_info.to_owned())); - let updatable = map_entry.1.try_update(child_info); + let top = &self.top; + let (changeset, info) = self.children.entry(storage_key).or_insert_with(|| + ( + top.spawn_child(), + child_info.to_owned() + ) + ); + let updatable = info.try_update(child_info); debug_assert!(updatable); - - let entry = map_entry.0.entry(key).or_default(); - entry.value = val; - - if let Some(extrinsic) = extrinsic_index { - entry.extrinsics.get_or_insert_with(Default::default) - .insert(extrinsic); - } + changeset.set(key, val, extrinsic_index); } /// Clear child storage of given storage key. /// - /// NOTE that this doesn't take place immediately but written into the prospective - /// change set, and still can be reverted by [`discard_prospective`]. - /// - /// [`discard_prospective`]: #method.discard_prospective + /// Can be rolled back or committed when called inside a transaction. pub(crate) fn clear_child_storage( &mut self, child_info: &ChildInfo, ) { let extrinsic_index = self.extrinsic_index(); - let storage_key = child_info.storage_key(); - let map_entry = self.prospective.children_default.entry(storage_key.to_vec()) - .or_insert_with(|| (Default::default(), child_info.to_owned())); - let updatable = map_entry.1.try_update(child_info); + let storage_key = child_info.storage_key().to_vec(); + let top = &self.top; + let (changeset, info) = self.children.entry(storage_key).or_insert_with(|| + ( + top.spawn_child(), + child_info.to_owned() + ) + ); + let updatable = info.try_update(child_info); debug_assert!(updatable); - - map_entry.0.values_mut().for_each(|e| { - if let Some(extrinsic) = extrinsic_index { - e.extrinsics.get_or_insert_with(Default::default) - .insert(extrinsic); - } - - e.value = None; - }); - - if let Some((committed_map, _child_info)) = self.committed.children_default.get(storage_key) { - for (key, value) in committed_map.iter() { - if !map_entry.0.contains_key(key) { - map_entry.0.insert(key.clone(), OverlayedValue { - value: None, - extrinsics: extrinsic_index.map(|i| { - let mut e = value.extrinsics.clone() - .unwrap_or_else(|| BTreeSet::default()); - e.insert(i); - e - }), - }); - } - } - } + changeset.clear_where(|_, _| true, extrinsic_index); } /// Removes all key-value pairs which keys share the given prefix. /// - /// NOTE that this doesn't take place immediately but written into the prospective - /// change set, and still can be reverted by [`discard_prospective`]. - /// - /// [`discard_prospective`]: #method.discard_prospective + /// Can be rolled back or committed when called inside a transaction. pub(crate) fn clear_prefix(&mut self, prefix: &[u8]) { - let extrinsic_index = self.extrinsic_index(); - - // Iterate over all prospective and mark all keys that share - // the given prefix as removed (None). - for (key, entry) in self.prospective.top.iter_mut() { - if key.starts_with(prefix) { - entry.value = None; - - if let Some(extrinsic) = extrinsic_index { - entry.extrinsics.get_or_insert_with(Default::default) - .insert(extrinsic); - } - } - } - - // Then do the same with keys from committed changes. - // NOTE that we are making changes in the prospective change set. - for key in self.committed.top.keys() { - if key.starts_with(prefix) { - let entry = self.prospective.top.entry(key.clone()).or_default(); - entry.value = None; - - if let Some(extrinsic) = extrinsic_index { - entry.extrinsics.get_or_insert_with(Default::default) - .insert(extrinsic); - } - } - } + self.top.clear_where(|key, _| key.starts_with(prefix), self.extrinsic_index()); } + /// Removes all key-value pairs which keys share the given prefix. + /// + /// Can be rolled back or committed when called inside a transaction pub(crate) fn clear_child_prefix( &mut self, child_info: &ChildInfo, prefix: &[u8], ) { let extrinsic_index = self.extrinsic_index(); - let storage_key = child_info.storage_key(); - let map_entry = self.prospective.children_default.entry(storage_key.to_vec()) - .or_insert_with(|| (Default::default(), child_info.to_owned())); - let updatable = map_entry.1.try_update(child_info); + let storage_key = child_info.storage_key().to_vec(); + let top = &self.top; + let (changeset, info) = self.children.entry(storage_key).or_insert_with(|| + ( + top.spawn_child(), + child_info.to_owned() + ) + ); + let updatable = info.try_update(child_info); debug_assert!(updatable); + changeset.clear_where(|key, _| key.starts_with(prefix), extrinsic_index); + } + + /// Returns the current nesting depth of the transaction stack. + /// + /// A value of zero means that no transaction is open and changes are committed on write. + pub fn transaction_depth(&self) -> usize { + // The top changeset and all child changesets transact in lockstep and are + // therefore always at the same transaction depth. + self.top.transaction_depth() + } + + /// Start a new nested transaction. + /// + /// This allows to either commit or roll back all changes that where made while this + /// transaction was open. Any transaction must be closed by either `rollback_transaction` or + /// `commit_transaction` before this overlay can be converted into storage changes. + /// + /// Changes made without any open transaction are committed immediatly. + pub fn start_transaction(&mut self) { + self.top.start_transaction(); + for (_, (changeset, _)) in self.children.iter_mut() { + changeset.start_transaction(); + } + } - for (key, entry) in map_entry.0.iter_mut() { - if key.starts_with(prefix) { - entry.value = None; + /// Rollback the last transaction started by `start_transaction`. + /// + /// Any changes made during that transaction are discarded. Returns an error if + /// there is no open transaction that can be rolled back. + pub fn rollback_transaction(&mut self) -> Result<(), NoOpenTransaction> { + self.top.rollback_transaction()?; + self.children.retain(|_, (changeset, _)| { + changeset.rollback_transaction() + .expect("Top and children changesets are started in lockstep; qed"); + !changeset.is_empty() + }); + Ok(()) + } - if let Some(extrinsic) = extrinsic_index { - entry.extrinsics.get_or_insert_with(Default::default) - .insert(extrinsic); - } - } + /// Commit the last transaction started by `start_transaction`. + /// + /// Any changes made during that transaction are committed. Returns an error if there + /// is no open transaction that can be committed. + pub fn commit_transaction(&mut self) -> Result<(), NoOpenTransaction> { + self.top.commit_transaction()?; + for (_, (changeset, _)) in self.children.iter_mut() { + changeset.commit_transaction() + .expect("Top and children changesets are started in lockstep; qed"); } + Ok(()) + } - if let Some((child_committed, _child_info)) = self.committed.children_default.get(storage_key) { - // Then do the same with keys from committed changes. - // NOTE that we are making changes in the prospective change set. - for key in child_committed.keys() { - if key.starts_with(prefix) { - let entry = map_entry.0.entry(key.clone()).or_default(); - entry.value = None; - - if let Some(extrinsic) = extrinsic_index { - entry.extrinsics.get_or_insert_with(Default::default) - .insert(extrinsic); - } - } - } + /// Call this before transfering control to the runtime. + /// + /// This protects all existing transactions from being removed by the runtime. + /// Calling this while already inside the runtime will return an error. + pub fn enter_runtime(&mut self) -> Result<(), AlreadyInRuntime> { + self.top.enter_runtime()?; + for (_, (changeset, _)) in self.children.iter_mut() { + changeset.enter_runtime() + .expect("Top and children changesets are entering runtime in lockstep; qed") } + Ok(()) } - /// Discard prospective changes to state. - pub fn discard_prospective(&mut self) { - self.prospective.clear(); - } - - /// Commit prospective changes to state. - pub fn commit_prospective(&mut self) { - if self.committed.is_empty() { - mem::swap(&mut self.prospective, &mut self.committed); - } else { - let top_to_commit = mem::replace(&mut self.prospective.top, BTreeMap::new()); - for (key, val) in top_to_commit.into_iter() { - let entry = self.committed.top.entry(key).or_default(); - entry.value = val.value; - - if let Some(prospective_extrinsics) = val.extrinsics { - entry.extrinsics.get_or_insert_with(Default::default) - .extend(prospective_extrinsics); - } - } - for (storage_key, (map, child_info)) in self.prospective.children_default.drain() { - let child_content = self.committed.children_default.entry(storage_key) - .or_insert_with(|| (Default::default(), child_info)); - // No update to child info at this point (will be needed for deletion). - for (key, val) in map.into_iter() { - let entry = child_content.0.entry(key).or_default(); - entry.value = val.value; - - if let Some(prospective_extrinsics) = val.extrinsics { - entry.extrinsics.get_or_insert_with(Default::default) - .extend(prospective_extrinsics); - } - } - } + /// Call this when control returns from the runtime. + /// + /// This commits all dangling transaction left open by the runtime. + /// Calling this while outside the runtime will return an error. + pub fn exit_runtime(&mut self) -> Result<(), NotInRuntime> { + self.top.exit_runtime()?; + for (_, (changeset, _)) in self.children.iter_mut() { + changeset.exit_runtime() + .expect("Top and children changesets are entering runtime in lockstep; qed"); } + Ok(()) } - /// Consume `OverlayedChanges` and take committed set. + /// Consume all changes (top + children) and return them. + /// + /// After calling this function no more changes are contained in this changeset. /// /// Panics: - /// Will panic if there are any uncommitted prospective changes. + /// Panics if `transaction_depth() > 0` fn drain_committed(&mut self) -> ( impl Iterator)>, impl Iterator)>, ChildInfo))>, ) { - assert!(self.prospective.is_empty()); + use std::mem::take; ( - std::mem::take(&mut self.committed.top) - .into_iter() - .map(|(k, v)| (k, v.value)), - std::mem::take(&mut self.committed.children_default) - .into_iter() - .map(|(sk, (v, ci))| (sk, (v.into_iter().map(|(k, v)| (k, v.value)), ci))), + take(&mut self.top).drain_commited(), + take(&mut self.children).into_iter() + .map(|(key, (val, info))| ( + key, + (val.drain_commited(), info) + ) + ), ) } - /// Get an iterator over all pending and committed child tries in the overlay. - pub fn child_infos(&self) -> impl IntoIterator { - self.committed.children_default.iter() - .chain(self.prospective.children_default.iter()) - .map(|(_, v)| &v.1).collect::>() - } - - /// Get an iterator over all pending and committed changes. - /// - /// Supplying `None` for `child_info` will only return changes that are in the top - /// trie. Specifying some `child_info` will return only the changes in that - /// child trie. - pub fn changes(&self, child_info: Option<&ChildInfo>) - -> impl Iterator - { - let (committed, prospective) = if let Some(child_info) = child_info { - match child_info.child_type() { - ChildType::ParentKeyId => ( - self.committed.children_default.get(child_info.storage_key()).map(|c| &c.0), - self.prospective.children_default.get(child_info.storage_key()).map(|c| &c.0), - ), - } - } else { - (Some(&self.committed.top), Some(&self.prospective.top)) - }; - committed.into_iter().flatten().chain(prospective.into_iter().flatten()) + /// Get an iterator over all child changes as seen by the current transaction. + pub fn children(&self) + -> impl Iterator, &ChildInfo)> { + self.children.iter().map(|(_, v)| (v.0.changes(), &v.1)) } - /// Return a clone of the currently pending changes. - pub fn clone_pending(&self) -> OverlayedChangeSet { - self.prospective.clone() + /// Get an iterator over all top changes as been by the current transaction. + pub fn changes(&self) -> impl Iterator { + self.top.changes() } - /// Replace the currently pending changes. - pub fn replace_pending(&mut self, pending: OverlayedChangeSet) { - self.prospective = pending; + /// Get an optional iterator over all child changes stored under the supplied key. + pub fn child_changes(&self, key: &[u8]) + -> Option<(impl Iterator, &ChildInfo)> { + self.children.get(key).map(|(overlay, info)| (overlay.changes(), info)) } /// Convert this instance with all changes into a [`StorageChanges`] instance. @@ -607,10 +469,7 @@ impl OverlayedChanges { /// Inserts storage entry responsible for current extrinsic index. #[cfg(test)] pub(crate) fn set_extrinsic_index(&mut self, extrinsic_index: u32) { - self.prospective.top.insert(EXTRINSIC_INDEX.to_vec(), OverlayedValue { - value: Some(extrinsic_index.encode()), - extrinsics: None, - }); + self.top.set(EXTRINSIC_INDEX.to_vec(), Some(extrinsic_index.encode()), None); } /// Returns current extrinsic index to use in changes trie construction. @@ -629,7 +488,8 @@ impl OverlayedChanges { } } - /// Generate the storage root using `backend` and all changes from `prospective` and `committed`. + /// Generate the storage root using `backend` and all changes + /// as seen by the current transaction. /// /// Returns the storage root and caches storage transaction in the given `cache`. pub fn storage_root>( @@ -639,35 +499,13 @@ impl OverlayedChanges { ) -> H::Out where H::Out: Ord + Encode, { - let child_storage_keys = self.prospective.children_default.keys() - .chain(self.committed.children_default.keys()); - let child_delta_iter = child_storage_keys.map(|storage_key| - ( - self.default_child_info(storage_key) - .expect("child info initialized in either committed or prospective"), - self.committed.children_default.get(storage_key) - .into_iter() - .flat_map(|(map, _)| - map.iter().map(|(k, v)| (&k[..], v.value().map(|v| &v[..]))) - ) - .chain( - self.prospective.children_default.get(storage_key) - .into_iter() - .flat_map(|(map, _)| - map.iter().map(|(k, v)| (&k[..], v.value().map(|v| &v[..]))) - ) - ), - ) - ); + let delta = self.changes().map(|(k, v)| (&k[..], v.value().map(|v| &v[..]))); + let child_delta = self.children() + .map(|(changes, info)| (info, changes.map( + |(k, v)| (&k[..], v.value().map(|v| &v[..])) + ))); - // compute and memoize - let delta = self.committed - .top - .iter() - .map(|(k, v)| (&k[..], v.value().map(|v| &v[..]))) - .chain(self.prospective.top.iter().map(|(k, v)| (&k[..], v.value().map(|v| &v[..])))); - - let (root, transaction) = backend.full_storage_root(delta, child_delta_iter); + let (root, transaction) = backend.full_storage_root(delta, child_delta); cache.transaction = Some(transaction); cache.transaction_storage_root = Some(root); @@ -704,41 +542,10 @@ impl OverlayedChanges { }) } - /// Get child info for a storage key. - /// Take the latest value so prospective first. - pub fn default_child_info(&self, storage_key: &[u8]) -> Option<&ChildInfo> { - if let Some((_, ci)) = self.prospective.children_default.get(storage_key) { - return Some(&ci); - } - if let Some((_, ci)) = self.committed.children_default.get(storage_key) { - return Some(&ci); - } - None - } - /// Returns the next (in lexicographic order) storage key in the overlayed alongside its value. /// If no value is next then `None` is returned. pub fn next_storage_key_change(&self, key: &[u8]) -> Option<(&[u8], &OverlayedValue)> { - let range = (ops::Bound::Excluded(key), ops::Bound::Unbounded); - - let next_prospective_key = self.prospective.top - .range::<[u8], _>(range) - .next() - .map(|(k, v)| (&k[..], v)); - - let next_committed_key = self.committed.top - .range::<[u8], _>(range) - .next() - .map(|(k, v)| (&k[..], v)); - - match (next_committed_key, next_prospective_key) { - // Committed is strictly less than prospective - (Some(committed_key), Some(prospective_key)) if committed_key.0 < prospective_key.0 => - Some(committed_key), - (committed_key, None) => committed_key, - // Prospective key is less or equal to committed or committed doesn't exist - (_, prospective_key) => prospective_key, - } + self.top.next_change(key) } /// Returns the next (in lexicographic order) child storage key in the overlayed alongside its @@ -748,48 +555,32 @@ impl OverlayedChanges { storage_key: &[u8], key: &[u8] ) -> Option<(&[u8], &OverlayedValue)> { - let range = (ops::Bound::Excluded(key), ops::Bound::Unbounded); - - let next_prospective_key = self.prospective.children_default.get(storage_key) - .and_then(|(map, _)| map.range::<[u8], _>(range).next().map(|(k, v)| (&k[..], v))); - - let next_committed_key = self.committed.children_default.get(storage_key) - .and_then(|(map, _)| map.range::<[u8], _>(range).next().map(|(k, v)| (&k[..], v))); - - match (next_committed_key, next_prospective_key) { - // Committed is strictly less than prospective - (Some(committed_key), Some(prospective_key)) if committed_key.0 < prospective_key.0 => - Some(committed_key), - (committed_key, None) => committed_key, - // Prospective key is less or equal to committed or committed doesn't exist - (_, prospective_key) => prospective_key, - } - } -} - -#[cfg(test)] -impl From> for OverlayedValue { - fn from(value: Option) -> OverlayedValue { - OverlayedValue { value, ..Default::default() } + self.children + .get(storage_key) + .and_then(|(overlay, _)| + overlay.next_change(key) + ) } } #[cfg(test)] mod tests { use hex_literal::hex; - use sp_core::{ - Blake2Hasher, traits::Externalities, storage::well_known_keys::EXTRINSIC_INDEX, - }; + use sp_core::{Blake2Hasher, traits::Externalities}; use crate::InMemoryBackend; use crate::ext::Ext; use super::*; + use std::collections::BTreeMap; - fn strip_extrinsic_index(map: &BTreeMap) - -> BTreeMap - { - let mut clone = map.clone(); - clone.remove(&EXTRINSIC_INDEX.to_vec()); - clone + fn assert_extrinsics( + overlay: &OverlayedChangeSet, + key: impl AsRef<[u8]>, + expected: Vec, + ) { + assert_eq!( + overlay.get(key.as_ref()).unwrap().extrinsics().cloned().collect::>(), + expected + ) } #[test] @@ -800,23 +591,28 @@ mod tests { assert!(overlayed.storage(&key).is_none()); + overlayed.start_transaction(); + overlayed.set_storage(key.clone(), Some(vec![1, 2, 3])); assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); - overlayed.commit_prospective(); + overlayed.commit_transaction().unwrap(); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); + overlayed.start_transaction(); + overlayed.set_storage(key.clone(), Some(vec![])); assert_eq!(overlayed.storage(&key).unwrap(), Some(&[][..])); overlayed.set_storage(key.clone(), None); assert!(overlayed.storage(&key).unwrap().is_none()); - overlayed.discard_prospective(); + overlayed.rollback_transaction().unwrap(); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); overlayed.set_storage(key.clone(), None); - overlayed.commit_prospective(); assert!(overlayed.storage(&key).unwrap().is_none()); } @@ -829,18 +625,18 @@ mod tests { (b"doug".to_vec(), b"notadog".to_vec()), ].into_iter().collect(); let backend = InMemoryBackend::::from(initial); - let mut overlay = OverlayedChanges { - committed: vec![ - (b"dog".to_vec(), Some(b"puppy".to_vec()).into()), - (b"dogglesworth".to_vec(), Some(b"catYYY".to_vec()).into()), - (b"doug".to_vec(), Some(vec![]).into()), - ].into_iter().collect(), - prospective: vec![ - (b"dogglesworth".to_vec(), Some(b"cat".to_vec()).into()), - (b"doug".to_vec(), None.into()), - ].into_iter().collect(), - ..Default::default() - }; + let mut overlay = OverlayedChanges::default(); + overlay.set_collect_extrinsics(false); + + overlay.start_transaction(); + overlay.set_storage(b"dog".to_vec(), Some(b"puppy".to_vec())); + overlay.set_storage(b"dogglesworth".to_vec(), Some(b"catYYY".to_vec())); + overlay.set_storage(b"doug".to_vec(), Some(vec![])); + overlay.commit_transaction().unwrap(); + + overlay.start_transaction(); + overlay.set_storage(b"dogglesworth".to_vec(), Some(b"cat".to_vec())); + overlay.set_storage(b"doug".to_vec(), None); let mut offchain_overlay = Default::default(); let mut cache = StorageTransactionCache::default(); @@ -862,6 +658,8 @@ mod tests { let mut overlay = OverlayedChanges::default(); overlay.set_collect_extrinsics(true); + overlay.start_transaction(); + overlay.set_storage(vec![100], Some(vec![101])); overlay.set_extrinsic_index(0); @@ -873,17 +671,11 @@ mod tests { overlay.set_extrinsic_index(2); overlay.set_storage(vec![1], Some(vec![6])); - assert_eq!(strip_extrinsic_index(&overlay.prospective.top), - vec![ - (vec![1], OverlayedValue { value: Some(vec![6]), - extrinsics: Some(vec![0, 2].into_iter().collect()) }), - (vec![3], OverlayedValue { value: Some(vec![4]), - extrinsics: Some(vec![1].into_iter().collect()) }), - (vec![100], OverlayedValue { value: Some(vec![101]), - extrinsics: Some(vec![NO_EXTRINSIC_INDEX].into_iter().collect()) }), - ].into_iter().collect()); + assert_extrinsics(&overlay.top, vec![1], vec![0, 2]); + assert_extrinsics(&overlay.top, vec![3], vec![1]); + assert_extrinsics(&overlay.top, vec![100], vec![NO_EXTRINSIC_INDEX]); - overlay.commit_prospective(); + overlay.start_transaction(); overlay.set_extrinsic_index(3); overlay.set_storage(vec![3], Some(vec![7])); @@ -891,75 +683,53 @@ mod tests { overlay.set_extrinsic_index(4); overlay.set_storage(vec![1], Some(vec![8])); - assert_eq!(strip_extrinsic_index(&overlay.committed.top), - vec![ - (vec![1], OverlayedValue { value: Some(vec![6]), - extrinsics: Some(vec![0, 2].into_iter().collect()) }), - (vec![3], OverlayedValue { value: Some(vec![4]), - extrinsics: Some(vec![1].into_iter().collect()) }), - (vec![100], OverlayedValue { value: Some(vec![101]), - extrinsics: Some(vec![NO_EXTRINSIC_INDEX].into_iter().collect()) }), - ].into_iter().collect()); - - assert_eq!(strip_extrinsic_index(&overlay.prospective.top), - vec![ - (vec![1], OverlayedValue { value: Some(vec![8]), - extrinsics: Some(vec![4].into_iter().collect()) }), - (vec![3], OverlayedValue { value: Some(vec![7]), - extrinsics: Some(vec![3].into_iter().collect()) }), - ].into_iter().collect()); - - overlay.commit_prospective(); - - assert_eq!(strip_extrinsic_index(&overlay.committed.top), - vec![ - (vec![1], OverlayedValue { value: Some(vec![8]), - extrinsics: Some(vec![0, 2, 4].into_iter().collect()) }), - (vec![3], OverlayedValue { value: Some(vec![7]), - extrinsics: Some(vec![1, 3].into_iter().collect()) }), - (vec![100], OverlayedValue { value: Some(vec![101]), - extrinsics: Some(vec![NO_EXTRINSIC_INDEX].into_iter().collect()) }), - ].into_iter().collect()); - - assert_eq!(overlay.prospective, - Default::default()); + assert_extrinsics(&overlay.top, vec![1], vec![0, 2, 4]); + assert_extrinsics(&overlay.top, vec![3], vec![1, 3]); + assert_extrinsics(&overlay.top, vec![100], vec![NO_EXTRINSIC_INDEX]); + + overlay.rollback_transaction().unwrap(); + + assert_extrinsics(&overlay.top, vec![1], vec![0, 2]); + assert_extrinsics(&overlay.top, vec![3], vec![1]); + assert_extrinsics(&overlay.top, vec![100], vec![NO_EXTRINSIC_INDEX]); } #[test] fn next_storage_key_change_works() { let mut overlay = OverlayedChanges::default(); + overlay.start_transaction(); overlay.set_storage(vec![20], Some(vec![20])); overlay.set_storage(vec![30], Some(vec![30])); overlay.set_storage(vec![40], Some(vec![40])); - overlay.commit_prospective(); + overlay.commit_transaction().unwrap(); overlay.set_storage(vec![10], Some(vec![10])); overlay.set_storage(vec![30], None); // next_prospective < next_committed let next_to_5 = overlay.next_storage_key_change(&[5]).unwrap(); assert_eq!(next_to_5.0.to_vec(), vec![10]); - assert_eq!(next_to_5.1.value, Some(vec![10])); + assert_eq!(next_to_5.1.value(), Some(&vec![10])); // next_committed < next_prospective let next_to_10 = overlay.next_storage_key_change(&[10]).unwrap(); assert_eq!(next_to_10.0.to_vec(), vec![20]); - assert_eq!(next_to_10.1.value, Some(vec![20])); + assert_eq!(next_to_10.1.value(), Some(&vec![20])); // next_committed == next_prospective let next_to_20 = overlay.next_storage_key_change(&[20]).unwrap(); assert_eq!(next_to_20.0.to_vec(), vec![30]); - assert_eq!(next_to_20.1.value, None); + assert_eq!(next_to_20.1.value(), None); // next_committed, no next_prospective let next_to_30 = overlay.next_storage_key_change(&[30]).unwrap(); assert_eq!(next_to_30.0.to_vec(), vec![40]); - assert_eq!(next_to_30.1.value, Some(vec![40])); + assert_eq!(next_to_30.1.value(), Some(&vec![40])); overlay.set_storage(vec![50], Some(vec![50])); // next_prospective, no next_committed let next_to_40 = overlay.next_storage_key_change(&[40]).unwrap(); assert_eq!(next_to_40.0.to_vec(), vec![50]); - assert_eq!(next_to_40.1.value, Some(vec![50])); + assert_eq!(next_to_40.1.value(), Some(&vec![50])); } #[test] @@ -968,37 +738,38 @@ mod tests { let child_info = &child_info; let child = child_info.storage_key(); let mut overlay = OverlayedChanges::default(); + overlay.start_transaction(); overlay.set_child_storage(child_info, vec![20], Some(vec![20])); overlay.set_child_storage(child_info, vec![30], Some(vec![30])); overlay.set_child_storage(child_info, vec![40], Some(vec![40])); - overlay.commit_prospective(); + overlay.commit_transaction().unwrap(); overlay.set_child_storage(child_info, vec![10], Some(vec![10])); overlay.set_child_storage(child_info, vec![30], None); // next_prospective < next_committed let next_to_5 = overlay.next_child_storage_key_change(child, &[5]).unwrap(); assert_eq!(next_to_5.0.to_vec(), vec![10]); - assert_eq!(next_to_5.1.value, Some(vec![10])); + assert_eq!(next_to_5.1.value(), Some(&vec![10])); // next_committed < next_prospective let next_to_10 = overlay.next_child_storage_key_change(child, &[10]).unwrap(); assert_eq!(next_to_10.0.to_vec(), vec![20]); - assert_eq!(next_to_10.1.value, Some(vec![20])); + assert_eq!(next_to_10.1.value(), Some(&vec![20])); // next_committed == next_prospective let next_to_20 = overlay.next_child_storage_key_change(child, &[20]).unwrap(); assert_eq!(next_to_20.0.to_vec(), vec![30]); - assert_eq!(next_to_20.1.value, None); + assert_eq!(next_to_20.1.value(), None); // next_committed, no next_prospective let next_to_30 = overlay.next_child_storage_key_change(child, &[30]).unwrap(); assert_eq!(next_to_30.0.to_vec(), vec![40]); - assert_eq!(next_to_30.1.value, Some(vec![40])); + assert_eq!(next_to_30.1.value(), Some(&vec![40])); overlay.set_child_storage(child_info, vec![50], Some(vec![50])); // next_prospective, no next_committed let next_to_40 = overlay.next_child_storage_key_change(child, &[40]).unwrap(); assert_eq!(next_to_40.0.to_vec(), vec![50]); - assert_eq!(next_to_40.1.value, Some(vec![50])); + assert_eq!(next_to_40.1.value(), Some(&vec![50])); } } diff --git a/primitives/state-machine/src/read_only.rs b/primitives/state-machine/src/read_only.rs index 817282f8e71a3..2a5d7fda364de 100644 --- a/primitives/state-machine/src/read_only.rs +++ b/primitives/state-machine/src/read_only.rs @@ -170,6 +170,18 @@ impl<'a, H: Hasher, B: 'a + Backend> Externalities for ReadOnlyExternalities< unimplemented!("storage_changes_root is not supported in ReadOnlyExternalities") } + fn storage_start_transaction(&mut self) { + unimplemented!("Transactions are not supported by ReadOnlyExternalities"); + } + + fn storage_rollback_transaction(&mut self) -> Result<(), ()> { + unimplemented!("Transactions are not supported by ReadOnlyExternalities"); + } + + fn storage_commit_transaction(&mut self) -> Result<(), ()> { + unimplemented!("Transactions are not supported by ReadOnlyExternalities"); + } + fn wipe(&mut self) {} fn commit(&mut self) {} diff --git a/primitives/state-machine/src/testing.rs b/primitives/state-machine/src/testing.rs index 71124a68bb5cf..cccb044f7e33d 100644 --- a/primitives/state-machine/src/testing.rs +++ b/primitives/state-machine/src/testing.rs @@ -31,7 +31,10 @@ use crate::{ }, }; use sp_core::{ - offchain::storage::OffchainOverlayedChanges, + offchain::{ + testing::TestPersistentOffchainDB, + storage::OffchainOverlayedChanges + }, storage::{ well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES, is_child_storage_key}, Storage, @@ -47,6 +50,7 @@ where { overlay: OverlayedChanges, offchain_overlay: OffchainOverlayedChanges, + offchain_db: TestPersistentOffchainDB, storage_transaction_cache: StorageTransactionCache< as Backend>::Transaction, H, N >, @@ -108,9 +112,12 @@ impl TestExternalities extensions.register(sp_core::traits::TaskExecutorExt(sp_core::tasks::executor())); + let offchain_db = TestPersistentOffchainDB::new(); + TestExternalities { overlay, offchain_overlay, + offchain_db, changes_trie_config, extensions, changes_trie_storage: ChangesTrieInMemoryStorage::new(), @@ -119,6 +126,16 @@ impl TestExternalities } } + /// Move offchain changes from overlay to the persistent store. + pub fn persist_offchain_overlay(&mut self) { + self.offchain_db.apply_offchain_changes(&mut self.offchain_overlay); + } + + /// A shared reference type around the offchain worker storage. + pub fn offchain_db(&self) -> TestPersistentOffchainDB { + self.offchain_db.clone() + } + /// Insert key/value into backend pub fn insert(&mut self, k: StorageKey, v: StorageValue) { self.backend.insert(vec![(None, vec![(k, Some(v))])]); @@ -136,15 +153,15 @@ impl TestExternalities /// Return a new backend with all pending value. pub fn commit_all(&self) -> InMemoryBackend { - let top: Vec<_> = self.overlay.changes(None) + let top: Vec<_> = self.overlay.changes() .map(|(k, v)| (k.clone(), v.value().cloned())) .collect(); let mut transaction = vec![(None, top)]; - for child_info in self.overlay.child_infos() { + for (child_changes, child_info) in self.overlay.children() { transaction.push(( Some(child_info.clone()), - self.overlay.changes(Some(child_info)) + child_changes .map(|(k, v)| (k.clone(), v.value().cloned())) .collect(), )) @@ -225,7 +242,7 @@ impl sp_externalities::ExtensionStore for TestExternalities where #[cfg(test)] mod tests { use super::*; - use sp_core::traits::Externalities; + use sp_core::{H256, traits::Externalities}; use sp_runtime::traits::BlakeTwo256; use hex_literal::hex; @@ -236,8 +253,8 @@ mod tests { ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec()); ext.set_storage(b"dog".to_vec(), b"puppy".to_vec()); ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec()); - const ROOT: [u8; 32] = hex!("555d4777b52e9196e3f6373c556cc661e79cd463f881ab9e921e70fc30144bf4"); - assert_eq!(&ext.storage_root()[..], &ROOT); + let root = H256::from(hex!("2a340d3dfd52f5992c6b117e9e45f479e6da5afffafeb26ab619cf137a95aeb8")); + assert_eq!(H256::from_slice(ext.storage_root().as_slice()), root); } #[test] diff --git a/primitives/test-primitives/Cargo.toml b/primitives/test-primitives/Cargo.toml index 87cb398579ea7..abc47f6f9aa72 100644 --- a/primitives/test-primitives/Cargo.toml +++ b/primitives/test-primitives/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path = "../application-crypto" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../core" } serde = { version = "1.0.101", optional = true, features = ["derive"] } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../runtime" } diff --git a/primitives/timestamp/Cargo.toml b/primitives/timestamp/Cargo.toml index 59c090eb46606..5b2217f0f30a1 100644 --- a/primitives/timestamp/Cargo.toml +++ b/primitives/timestamp/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] sp-api = { version = "2.0.0-rc3", default-features = false, path = "../api" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../runtime" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-inherents = { version = "2.0.0-rc3", default-features = false, path = "../inherents" } impl-trait-for-tuples = "0.1.3" wasm-timer = { version = "0.2", optional = true } diff --git a/primitives/tracing/Cargo.toml b/primitives/tracing/Cargo.toml index f0560adb06f3a..e47d9859c902d 100644 --- a/primitives/tracing/Cargo.toml +++ b/primitives/tracing/Cargo.toml @@ -13,7 +13,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] tracing = { version = "0.1.13", optional = true } +rental = { version = "0.5.5", optional = true } +log = { version = "0.4.8", optional = true } [features] default = [ "std" ] -std = [ "tracing" ] +std = [ "tracing", "rental", "log" ] diff --git a/primitives/tracing/src/lib.rs b/primitives/tracing/src/lib.rs index fa43f812d22cd..e82d8861cd3f5 100644 --- a/primitives/tracing/src/lib.rs +++ b/primitives/tracing/src/lib.rs @@ -19,13 +19,35 @@ //! //! To trace functions or invidual code in Substrate, this crate provides [`tracing_span`] //! and [`enter_span`]. See the individual docs for how to use these macros. - +//! +//! Note that to allow traces from wasm execution environment there are +//! 2 reserved identifiers for tracing `Field` recording, stored in the consts: +//! `WASM_TARGET_KEY` and `WASM_NAME_KEY` - if you choose to record fields, you +//! must ensure that your identifiers do not clash with either of these. +//! +//! Additionally, we have a const: `WASM_TRACE_IDENTIFIER`, which holds a span name used +//! to signal that the 'actual' span name and target should be retrieved instead from +//! the associated Fields mentioned above. #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "std")] +#[macro_use] +extern crate rental; + #[cfg(feature = "std")] #[doc(hidden)] pub use tracing; +#[cfg(feature = "std")] +pub mod proxy; + +#[cfg(feature = "std")] +use std::sync::atomic::{AtomicBool, Ordering}; + +/// Flag to signal whether to run wasm tracing +#[cfg(feature = "std")] +static WASM_TRACING_ENABLED: AtomicBool = AtomicBool::new(false); + /// Runs given code within a tracing span, measuring it's execution time. /// /// If tracing is not enabled, the code is still executed. @@ -83,3 +105,13 @@ macro_rules! if_tracing { macro_rules! if_tracing { ( $if:expr ) => {{}} } + +#[cfg(feature = "std")] +pub fn wasm_tracing_enabled() -> bool { + WASM_TRACING_ENABLED.load(Ordering::Relaxed) +} + +#[cfg(feature = "std")] +pub fn set_wasm_tracing(b: bool) { + WASM_TRACING_ENABLED.store(b, Ordering::Relaxed) +} \ No newline at end of file diff --git a/primitives/tracing/src/proxy.rs b/primitives/tracing/src/proxy.rs new file mode 100644 index 0000000000000..270f57aaa69f0 --- /dev/null +++ b/primitives/tracing/src/proxy.rs @@ -0,0 +1,165 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Proxy to allow entering tracing spans from wasm. +//! +//! Use `enter_span` and `exit_span` to surround the code that you wish to trace +use rental; +use tracing::info_span; + +/// Used to identify a proxied WASM trace +pub const WASM_TRACE_IDENTIFIER: &'static str = "WASM_TRACE"; +/// Used to extract the real `target` from the associated values of the span +pub const WASM_TARGET_KEY: &'static str = "proxied_wasm_target"; +/// Used to extract the real `name` from the associated values of the span +pub const WASM_NAME_KEY: &'static str = "proxied_wasm_name"; + +const MAX_SPANS_LEN: usize = 1000; + +rental! { + pub mod rent_span { + #[rental] + pub struct SpanAndGuard { + span: Box, + guard: tracing::span::Entered<'span>, + } + } +} + +/// Requires a tracing::Subscriber to process span traces, +/// this is available when running with client (and relevant cli params). +pub struct TracingProxy { + next_id: u64, + spans: Vec<(u64, rent_span::SpanAndGuard)>, +} + +impl Drop for TracingProxy { + fn drop(&mut self) { + if !self.spans.is_empty() { + log::debug!( + target: "tracing", + "Dropping TracingProxy with {} un-exited spans, marking as not valid", self.spans.len() + ); + while let Some((_, mut sg)) = self.spans.pop() { + sg.rent_all_mut(|s| { s.span.record("is_valid_trace", &false); }); + } + } + } +} + +impl TracingProxy { + pub fn new() -> TracingProxy { + TracingProxy { + next_id: 0, + spans: Vec::new(), + } + } +} + +impl TracingProxy { + /// Create and enter a `tracing` Span, returning the span id, + /// which should be passed to `exit_span(id)` to signal that the span should exit. + pub fn enter_span(&mut self, proxied_wasm_target: &str, proxied_wasm_name: &str) -> u64 { + // The identifiers `proxied_wasm_target` and `proxied_wasm_name` must match their associated const, + // WASM_TARGET_KEY and WASM_NAME_KEY. + let span = info_span!(WASM_TRACE_IDENTIFIER, is_valid_trace = true, proxied_wasm_target, proxied_wasm_name); + self.next_id += 1; + let sg = rent_span::SpanAndGuard::new( + Box::new(span), + |span| span.enter(), + ); + self.spans.push((self.next_id, sg)); + if self.spans.len() > MAX_SPANS_LEN { + // This is to prevent unbounded growth of Vec and could mean one of the following: + // 1. Too many nested spans, or MAX_SPANS_LEN is too low. + // 2. Not correctly exiting spans due to misconfiguration / misuse + log::warn!( + target: "tracing", + "TracingProxy MAX_SPANS_LEN exceeded, removing oldest span." + ); + let mut sg = self.spans.remove(0).1; + sg.rent_all_mut(|s| { s.span.record("is_valid_trace", &false); }); + } + self.next_id + } + + /// Exit a span by dropping it along with it's associated guard. + pub fn exit_span(&mut self, id: u64) { + if self.spans.last().map(|l| id > l.0).unwrap_or(true) { + log::warn!(target: "tracing", "Span id not found in TracingProxy: {}", id); + return; + } + let mut last_span = self.spans.pop().expect("Just checked that there is an element to pop; qed"); + while id < last_span.0 { + log::warn!( + target: "tracing", + "TracingProxy Span ids not equal! id parameter given: {}, last span: {}", + id, + last_span.0, + ); + last_span.1.rent_all_mut(|s| { s.span.record("is_valid_trace", &false); }); + if let Some(s) = self.spans.pop() { + last_span = s; + } else { + log::warn!(target: "tracing", "Span id not found in TracingProxy {}", id); + return; + } + } + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + fn create_spans(proxy: &mut TracingProxy, qty: usize) -> Vec { + let mut spans = Vec::new(); + for n in 0..qty { + spans.push(proxy.enter_span("target", &format!("{}", n))); + } + spans + } + + #[test] + fn max_spans_len_respected() { + let mut proxy = TracingProxy::new(); + let _spans = create_spans(&mut proxy, MAX_SPANS_LEN + 10); + assert_eq!(proxy.spans.len(), MAX_SPANS_LEN); + // ensure oldest spans removed + assert_eq!(proxy.spans[0].0, 11); + } + + #[test] + fn handles_span_exit_scenarios() { + let mut proxy = TracingProxy::new(); + let _spans = create_spans(&mut proxy, 10); + assert_eq!(proxy.spans.len(), 10); + // exit span normally + proxy.exit_span(10); + assert_eq!(proxy.spans.len(), 9); + // skip and exit outer span without exiting inner, id: 8 instead of 9 + proxy.exit_span(8); + // should have also removed the inner span that was lost + assert_eq!(proxy.spans.len(), 7); + // try to exit span not held + proxy.exit_span(9); + assert_eq!(proxy.spans.len(), 7); + // exit all spans + proxy.exit_span(1); + assert_eq!(proxy.spans.len(), 0); + } +} diff --git a/primitives/transaction-pool/Cargo.toml b/primitives/transaction-pool/Cargo.toml index 94daf519dbcf8..6417ae8c29d31 100644 --- a/primitives/transaction-pool/Cargo.toml +++ b/primitives/transaction-pool/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", optional = true } +codec = { package = "parity-scale-codec", version = "1.3.1", optional = true } derive_more = { version = "0.99.2", optional = true } futures = { version = "0.3.1", optional = true } log = { version = "0.4.8", optional = true } diff --git a/primitives/transaction-pool/src/pool.rs b/primitives/transaction-pool/src/pool.rs index 2824c96f30ab6..b00c283ac743c 100644 --- a/primitives/transaction-pool/src/pool.rs +++ b/primitives/transaction-pool/src/pool.rs @@ -141,6 +141,8 @@ pub type BlockHash

= <

::Block as BlockT>::Hash; pub type TransactionFor

= <

::Block as BlockT>::Extrinsic; /// Type of transactions event stream for a pool. pub type TransactionStatusStreamFor

= TransactionStatusStream, BlockHash

>; +/// Transaction type for a local pool. +pub type LocalTransactionFor

= <

::Block as BlockT>::Extrinsic; /// Typical future type used in transaction pool api. pub type PoolFuture = std::pin::Pin> + Send>>; @@ -273,6 +275,28 @@ pub trait MaintainedTransactionPool: TransactionPool { fn maintain(&self, event: ChainEvent) -> Pin + Send>>; } +/// Transaction pool interface for submitting local transactions that exposes a +/// blocking interface for submission. +pub trait LocalTransactionPool: Send + Sync { + /// Block type. + type Block: BlockT; + /// Transaction hash type. + type Hash: Hash + Eq + Member + Serialize; + /// Error type. + type Error: From + crate::error::IntoPoolError; + + /// Submits the given local unverified transaction to the pool blocking the + /// current thread for any necessary pre-verification. + /// NOTE: It MUST NOT be used for transactions that originate from the + /// network or RPC, since the validation is performed with + /// `TransactionSource::Local`. + fn submit_local( + &self, + at: &BlockId, + xt: LocalTransactionFor, + ) -> Result; +} + /// An abstraction for transaction pool. /// /// This trait is used by offchain calls to be able to submit transactions. @@ -291,7 +315,7 @@ pub trait OffchainSubmitTransaction: Send + Sync { ) -> Result<(), ()>; } -impl OffchainSubmitTransaction for TPool { +impl OffchainSubmitTransaction for TPool { fn submit_at( &self, at: &BlockId, @@ -303,15 +327,14 @@ impl OffchainSubmitTransaction for TPool { extrinsic ); - let result = futures::executor::block_on(self.submit_one( - &at, TransactionSource::Local, extrinsic, - )); + let result = self.submit_local(&at, extrinsic); - result.map(|_| ()) - .map_err(|e| log::warn!( + result.map(|_| ()).map_err(|e| { + log::warn!( target: "txpool", "(offchain call) Error submitting a transaction to the pool: {:?}", e - )) + ) + }) } } diff --git a/primitives/trie/Cargo.toml b/primitives/trie/Cargo.toml index c436092c099a6..d99a3d1ae702a 100644 --- a/primitives/trie/Cargo.toml +++ b/primitives/trie/Cargo.toml @@ -17,16 +17,16 @@ name = "bench" harness = false [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } hash-db = { version = "0.15.2", default-features = false } -trie-db = { version = "0.20.1", default-features = false } +trie-db = { version = "0.21.0", default-features = false } trie-root = { version = "0.16.0", default-features = false } -memory-db = { version = "0.20.0", default-features = false } +memory-db = { version = "0.21.0", default-features = false } sp-core = { version = "2.0.0-rc3", default-features = false, path = "../core" } [dev-dependencies] -trie-bench = "0.21.0" +trie-bench = "0.22.0" trie-standardmap = "0.15.2" criterion = "0.2.11" hex-literal = "0.2.1" diff --git a/primitives/trie/src/lib.rs b/primitives/trie/src/lib.rs index db471fd713733..7d1879a4f9f74 100644 --- a/primitives/trie/src/lib.rs +++ b/primitives/trie/src/lib.rs @@ -51,6 +51,7 @@ pub struct Layout(sp_std::marker::PhantomData); impl TrieLayout for Layout { const USE_EXTENSION: bool = false; + const ALLOW_EMPTY: bool = true; type Hash = H; type Codec = NodeCodec; } diff --git a/primitives/utils/Cargo.toml b/primitives/utils/Cargo.toml index c5b74f98a9517..9ae7beb1ffbc2 100644 --- a/primitives/utils/Cargo.toml +++ b/primitives/utils/Cargo.toml @@ -13,6 +13,7 @@ futures = "0.3.4" futures-core = "0.3.4" lazy_static = "1.4.0" prometheus = "0.8.0" +futures-timer = "3.0.2" [features] default = ["metered"] diff --git a/primitives/utils/src/lib.rs b/primitives/utils/src/lib.rs index 644e94651d69d..77bcd096561b4 100644 --- a/primitives/utils/src/lib.rs +++ b/primitives/utils/src/lib.rs @@ -18,4 +18,5 @@ //! Utilities Primitives for Substrate pub mod metrics; -pub mod mpsc; \ No newline at end of file +pub mod mpsc; +pub mod status_sinks; diff --git a/client/service/src/status_sinks.rs b/primitives/utils/src/status_sinks.rs similarity index 98% rename from client/service/src/status_sinks.rs rename to primitives/utils/src/status_sinks.rs index c3de468ab06fd..47bccebb960b4 100644 --- a/client/service/src/status_sinks.rs +++ b/primitives/utils/src/status_sinks.rs @@ -19,7 +19,7 @@ use std::time::Duration; use std::pin::Pin; use std::task::{Poll, Context}; use futures_timer::Delay; -use sp_utils::mpsc::TracingUnboundedSender; +use crate::mpsc::TracingUnboundedSender; /// Holds a list of `UnboundedSender`s, each associated with a certain time period. Every time the /// period elapses, we push an element on the sender. @@ -109,7 +109,7 @@ impl futures::Future for YieldAfter { mod tests { use super::StatusSinks; use futures::prelude::*; - use sp_utils::mpsc::tracing_unbounded; + use crate::mpsc::tracing_unbounded; use std::time::Duration; use std::task::Poll; diff --git a/primitives/version/Cargo.toml b/primitives/version/Cargo.toml index eb8c8450756d3..18357953d7143 100644 --- a/primitives/version/Cargo.toml +++ b/primitives/version/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] impl-serde = { version = "0.2.3", optional = true } serde = { version = "1.0.101", optional = true, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../std" } sp-runtime = { version = "2.0.0-rc3", default-features = false, path = "../runtime" } diff --git a/primitives/wasm-interface/Cargo.toml b/primitives/wasm-interface/Cargo.toml index 39684a0381516..c2e70ce1e4557 100644 --- a/primitives/wasm-interface/Cargo.toml +++ b/primitives/wasm-interface/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] wasmi = { version = "0.6.2", optional = true } impl-trait-for-tuples = "0.1.2" sp-std = { version = "2.0.0-rc3", path = "../std", default-features = false } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } [features] default = [ "std" ] diff --git a/test-utils/client/Cargo.toml b/test-utils/client/Cargo.toml index a6924e4f27409..f5604ceb23b64 100644 --- a/test-utils/client/Cargo.toml +++ b/test-utils/client/Cargo.toml @@ -13,6 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sc-client-api = { version = "2.0.0-rc3", path = "../../client/api" } +sc-light = { version = "2.0.0-rc3", path = "../../client/light" } sc-client-db = { version = "0.8.0-rc3", features = ["test-helpers"], path = "../../client/db" } sp-consensus = { version = "0.8.0-rc3", path = "../../primitives/consensus/common" } sc-executor = { version = "0.8.0-rc3", path = "../../client/executor" } @@ -21,7 +22,7 @@ sc-service = { version = "0.8.0-rc3", default-features = false, features = ["tes futures = "0.3.4" hash-db = "0.15.2" sp-keyring = { version = "2.0.0-rc3", path = "../../primitives/keyring" } -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } sp-core = { version = "2.0.0-rc3", path = "../../primitives/core" } sp-runtime = { version = "2.0.0-rc3", path = "../../primitives/runtime" } sp-blockchain = { version = "2.0.0-rc3", path = "../../primitives/blockchain" } diff --git a/test-utils/client/src/lib.rs b/test-utils/client/src/lib.rs index ffd93970f41da..2ab9e4066ddd1 100644 --- a/test-utils/client/src/lib.rs +++ b/test-utils/client/src/lib.rs @@ -46,7 +46,7 @@ use sp_runtime::traits::{Block as BlockT, BlakeTwo256}; use sc_service::client::{LocalCallExecutor, ClientConfig}; /// Test client light database backend. -pub type LightBackend = client::light::backend::Backend< +pub type LightBackend = sc_light::Backend< sc_client_db::light::LightStorage, BlakeTwo256, >; diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index a4e4bd1f164ed..e307522ead9cd 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -17,11 +17,11 @@ sp-application-crypto = { version = "2.0.0-rc3", default-features = false, path sp-consensus-aura = { version = "0.8.0-rc3", default-features = false, path = "../../primitives/consensus/aura" } sp-consensus-babe = { version = "0.8.0-rc3", default-features = false, path = "../../primitives/consensus/babe" } sp-block-builder = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/block-builder" } -codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } frame-executive = { version = "2.0.0-rc3", default-features = false, path = "../../frame/executive" } sp-inherents = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/inherents" } sp-keyring = { version = "2.0.0-rc3", optional = true, path = "../../primitives/keyring" } -memory-db = { version = "0.20.0", default-features = false } +memory-db = { version = "0.21.0", default-features = false } sp-offchain = { path = "../../primitives/offchain", default-features = false, version = "2.0.0-rc3"} sp-core = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/core" } sp-std = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/std" } @@ -39,7 +39,7 @@ pallet-timestamp = { version = "2.0.0-rc3", default-features = false, path = ".. sp-finality-grandpa = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/finality-grandpa" } sp-trie = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/trie" } sp-transaction-pool = { version = "2.0.0-rc3", default-features = false, path = "../../primitives/transaction-pool" } -trie-db = { version = "0.20.1", default-features = false } +trie-db = { version = "0.21.0", default-features = false } parity-util-mem = { version = "0.6.1", default-features = false, features = ["primitive-types"] } sc-service = { version = "0.8.0-rc3", default-features = false, optional = true, features = ["test-helpers"], path = "../../client/service" } diff --git a/test-utils/runtime/client/Cargo.toml b/test-utils/runtime/client/Cargo.toml index d59a5f1fdaa36..7a69f5ed225fd 100644 --- a/test-utils/runtime/client/Cargo.toml +++ b/test-utils/runtime/client/Cargo.toml @@ -12,6 +12,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] +sc-light = { version = "2.0.0-rc3", path = "../../../client/light" } sp-consensus = { version = "0.8.0-rc3", path = "../../../primitives/consensus/common" } sc-block-builder = { version = "0.8.0-rc3", path = "../../../client/block-builder" } substrate-test-client = { version = "2.0.0-rc3", path = "../../client" } @@ -20,7 +21,7 @@ substrate-test-runtime = { version = "2.0.0-rc3", path = "../../runtime" } sp-runtime = { version = "2.0.0-rc3", path = "../../../primitives/runtime" } sp-api = { version = "2.0.0-rc3", path = "../../../primitives/api" } sp-blockchain = { version = "2.0.0-rc3", path = "../../../primitives/blockchain" } -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } sc-client-api = { version = "2.0.0-rc3", path = "../../../client/api" } sc-consensus = { version = "0.8.0-rc3", path = "../../../client/consensus/common" } sc-service = { version = "0.8.0-rc3", default-features = false, path = "../../../client/service" } diff --git a/test-utils/runtime/client/src/lib.rs b/test-utils/runtime/client/src/lib.rs index f2e049bc0f56b..4e9034fb4d493 100644 --- a/test-utils/runtime/client/src/lib.rs +++ b/test-utils/runtime/client/src/lib.rs @@ -35,9 +35,9 @@ use sp_core::{sr25519, ChangesTrieConfiguration}; use sp_core::storage::{ChildInfo, Storage, StorageChild}; use substrate_test_runtime::genesismap::{GenesisConfig, additional_storage_with_genesis}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Hash as HashT, NumberFor, HashFor}; -use sc_service::client::light::fetcher::{ - Fetcher, RemoteHeaderRequest, RemoteReadRequest, RemoteReadChildRequest, +use sc_client_api::light::{ RemoteCallRequest, RemoteChangesRequest, RemoteBodyRequest, + Fetcher, RemoteHeaderRequest, RemoteReadRequest, RemoteReadChildRequest, }; /// A prelude to import in tests. @@ -75,10 +75,10 @@ pub type Executor = client::LocalCallExecutor< pub type LightBackend = substrate_test_client::LightBackend; /// Test client light executor. -pub type LightExecutor = client::light::call_executor::GenesisCallExecutor< +pub type LightExecutor = sc_light::GenesisCallExecutor< LightBackend, client::LocalCallExecutor< - client::light::backend::Backend< + sc_light::Backend< sc_client_db::light::LightStorage, HashFor >, @@ -347,7 +347,7 @@ pub fn new_light() -> ( ) { let storage = sc_client_db::light::LightStorage::new_test(); - let blockchain = Arc::new(client::light::blockchain::Blockchain::new(storage)); + let blockchain = Arc::new(sc_light::Blockchain::new(storage)); let backend = Arc::new(LightBackend::new(blockchain.clone())); let executor = new_native_executor(); let local_call_executor = client::LocalCallExecutor::new(backend.clone(), executor, sp_core::tasks::executor(), Default::default()); diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index 9ce6b21434f67..34d98d300b7f7 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -184,7 +184,7 @@ impl ExtrinsicT for Extrinsic { } impl sp_runtime::traits::Dispatchable for Extrinsic { - type Origin = (); + type Origin = Origin; type Trait = (); type Info = (); type PostInfo = (); @@ -313,6 +313,9 @@ cfg_if! { fn test_ecdsa_crypto() -> (ecdsa::AppSignature, ecdsa::AppPublic); /// Run various tests against storage. fn test_storage(); + /// Test that ensures that we can call a function that takes multiple + /// arguments. + fn test_multiple_arguments(data: Vec, other: Vec, num: u32); } } } else { @@ -359,6 +362,9 @@ cfg_if! { fn test_ecdsa_crypto() -> (ecdsa::AppSignature, ecdsa::AppPublic); /// Run various tests against storage. fn test_storage(); + /// Test that ensures that we can call a function that takes multiple + /// arguments. + fn test_multiple_arguments(data: Vec, other: Vec, num: u32); } } } @@ -401,6 +407,7 @@ parameter_types! { } impl frame_system::Trait for Runtime { + type BaseCallFilter = (); type Origin = Origin; type Call = Extrinsic; type Index = u64; @@ -422,7 +429,8 @@ impl frame_system::Trait for Runtime { type Version = (); type ModuleToIndex = (); type AccountData = (); - type MigrateAccount = (); type OnNewAccount = (); + type MigrateAccount = (); + type OnNewAccount = (); type OnKilledAccount = (); } @@ -640,6 +648,11 @@ cfg_if! { test_read_storage(); test_read_child_storage(); } + + fn test_multiple_arguments(data: Vec, other: Vec, num: u32) { + assert_eq!(&data[..], &other[..]); + assert_eq!(data.len(), num as usize); + } } impl sp_consensus_aura::AuraApi for Runtime { @@ -861,6 +874,11 @@ cfg_if! { test_read_storage(); test_read_child_storage(); } + + fn test_multiple_arguments(data: Vec, other: Vec, num: u32) { + assert_eq!(&data[..], &other[..]); + assert_eq!(data.len(), num as usize); + } } impl sp_consensus_aura::AuraApi for Runtime { diff --git a/test-utils/runtime/transaction-pool/Cargo.toml b/test-utils/runtime/transaction-pool/Cargo.toml index 0dc14f4edfc4c..e5c93ef8ad736 100644 --- a/test-utils/runtime/transaction-pool/Cargo.toml +++ b/test-utils/runtime/transaction-pool/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] substrate-test-runtime-client = { version = "2.0.0-rc3", path = "../client" } parking_lot = "0.10.0" -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } sp-blockchain = { version = "2.0.0-rc3", path = "../../../primitives/blockchain" } sp-runtime = { version = "2.0.0-rc3", path = "../../../primitives/runtime" } sp-transaction-pool = { version = "2.0.0-rc3", path = "../../../primitives/transaction-pool" } diff --git a/utils/browser/src/lib.rs b/utils/browser/src/lib.rs index 408ba24cfed22..799fe9788ca57 100644 --- a/utils/browser/src/lib.rs +++ b/utils/browser/src/lib.rs @@ -17,7 +17,6 @@ use futures01::sync::mpsc as mpsc01; use log::{debug, info}; -use std::sync::Arc; use sc_network::config::TransportConfig; use sc_service::{ AbstractService, RpcSession, Role, Configuration, @@ -64,7 +63,7 @@ where network, telemetry_endpoints: chain_spec.telemetry_endpoints().clone(), chain_spec: Box::new(chain_spec), - task_executor: Arc::new(move |fut, _| wasm_bindgen_futures::spawn_local(fut)), + task_executor: (|fut, _| wasm_bindgen_futures::spawn_local(fut)).into(), telemetry_external_transport: Some(transport), role: Role::Light, database: { @@ -86,6 +85,7 @@ where pruning: Default::default(), rpc_cors: Default::default(), rpc_http: Default::default(), + rpc_ipc: Default::default(), rpc_ws: Default::default(), rpc_ws_max_connections: Default::default(), rpc_methods: Default::default(), @@ -97,6 +97,11 @@ where wasm_method: Default::default(), max_runtime_instances: 8, announce_block: true, + base_path: None, + informant_output_format: sc_informant::OutputFormat { + enable_color: false, + prefix: String::new(), + }, }; Ok(config) @@ -116,11 +121,6 @@ struct RpcMessage { /// Create a Client object that connects to a service. pub fn start_client(mut service: impl AbstractService) -> Client { - // Spawn informant - wasm_bindgen_futures::spawn_local( - sc_informant::build(&service, sc_informant::OutputFormat::Plain).map(drop) - ); - // We dispatch a background task responsible for processing the service. // // The main action performed by the code below consists in polling the service with diff --git a/utils/fork-tree/Cargo.toml b/utils/fork-tree/Cargo.toml index de3f19f0a1ae0..6c8410ab76920 100644 --- a/utils/fork-tree/Cargo.toml +++ b/utils/fork-tree/Cargo.toml @@ -13,4 +13,4 @@ documentation = "https://docs.rs/fork-tree" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "1.3.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } diff --git a/utils/frame/benchmarking-cli/Cargo.toml b/utils/frame/benchmarking-cli/Cargo.toml index d53c4c2fe06b5..364dc472cb2ae 100644 --- a/utils/frame/benchmarking-cli/Cargo.toml +++ b/utils/frame/benchmarking-cli/Cargo.toml @@ -22,7 +22,7 @@ sp-externalities = { version = "0.8.0-rc3", path = "../../../primitives/external sp-runtime = { version = "2.0.0-rc3", path = "../../../primitives/runtime" } sp-state-machine = { version = "0.8.0-rc3", path = "../../../primitives/state-machine" } structopt = "0.3.8" -codec = { version = "1.3.0", package = "parity-scale-codec" } +codec = { version = "1.3.1", package = "parity-scale-codec" } [features] default = ["db"] diff --git a/utils/frame/benchmarking-cli/src/lib.rs b/utils/frame/benchmarking-cli/src/lib.rs index 7704f032b87fa..149b971577fab 100644 --- a/utils/frame/benchmarking-cli/src/lib.rs +++ b/utils/frame/benchmarking-cli/src/lib.rs @@ -21,7 +21,7 @@ use sc_cli::{ExecutionStrategy, WasmExecutionMethod}; use std::fmt::Debug; /// The `benchmark` command used to benchmark FRAME Pallets. -#[derive(Debug, structopt::StructOpt, Clone)] +#[derive(Debug, structopt::StructOpt)] pub struct BenchmarkCmd { /// Select a FRAME Pallet to benchmark, or `*` for all (in which case `extrinsic` must be `*`). #[structopt(short, long)] diff --git a/utils/frame/rpc/support/Cargo.toml b/utils/frame/rpc/support/Cargo.toml index 092c2c402da2b..d7e4259635bc4 100644 --- a/utils/frame/rpc/support/Cargo.toml +++ b/utils/frame/rpc/support/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] futures = { version = "0.3.0", features = ["compat"] } jsonrpc-client-transports = { version = "14.2.0", default-features = false, features = ["http"] } jsonrpc-core = "14.2.0" -codec = { package = "parity-scale-codec", version = "1" } +codec = { package = "parity-scale-codec", version = "1.3.1" } serde = "1" frame-support = { version = "2.0.0-rc3", path = "../../../../frame/support" } sp-storage = { version = "2.0.0-rc3", path = "../../../../primitives/storage" } diff --git a/utils/frame/rpc/system/Cargo.toml b/utils/frame/rpc/system/Cargo.toml index 11afd3b841ed3..a03a08b3ff846 100644 --- a/utils/frame/rpc/system/Cargo.toml +++ b/utils/frame/rpc/system/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sc-client-api = { version = "2.0.0-rc3", path = "../../../../client/api" } -codec = { package = "parity-scale-codec", version = "1.3.0" } +codec = { package = "parity-scale-codec", version = "1.3.1" } futures = { version = "0.3.4", features = ["compat"] } jsonrpc-core = "14.2.0" jsonrpc-core-client = "14.2.0" @@ -26,6 +26,8 @@ frame-system-rpc-runtime-api = { version = "2.0.0-rc3", path = "../../../../fram sp-core = { version = "2.0.0-rc3", path = "../../../../primitives/core" } sp-blockchain = { version = "2.0.0-rc3", path = "../../../../primitives/blockchain" } sp-transaction-pool = { version = "2.0.0-rc3", path = "../../../../primitives/transaction-pool" } +sp-block-builder = { version = "2.0.0-rc3", path = "../../../../primitives/block-builder" } +sc-rpc-api = { version = "0.8.0-rc3", path = "../../../../client/rpc-api" } [dev-dependencies] substrate-test-runtime-client = { version = "2.0.0-rc3", path = "../../../../test-utils/runtime/client" } diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index a3ce1466f6fe9..6927f05b4f05b 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -22,8 +22,8 @@ use std::sync::Arc; use codec::{self, Codec, Decode, Encode}; use sc_client_api::light::{future_header, RemoteBlockchain, Fetcher, RemoteCallRequest}; use jsonrpc_core::{ - Error, ErrorCode, - futures::future::{result, Future}, + Error as RpcError, ErrorCode, + futures::future::{self as rpc_future,result, Future}, }; use jsonrpc_derive::rpc; use futures::future::{ready, TryFutureExt}; @@ -35,18 +35,20 @@ use sp_runtime::{ generic::BlockId, traits, }; -use sp_core::hexdisplay::HexDisplay; +use sp_core::{hexdisplay::HexDisplay, Bytes}; use sp_transaction_pool::{TransactionPool, InPoolTransaction}; +use sp_block_builder::BlockBuilder; +use sc_rpc_api::DenyUnsafe; pub use frame_system_rpc_runtime_api::AccountNonceApi; pub use self::gen_client::Client as SystemClient; /// Future that resolves to account nonce. -pub type FutureResult = Box + Send>; +pub type FutureResult = Box + Send>; /// System RPC methods. #[rpc] -pub trait SystemApi { +pub trait SystemApi { /// Returns the next valid index (aka nonce) for given account. /// /// This method takes into consideration all pending transactions @@ -54,34 +56,57 @@ pub trait SystemApi { /// it fallbacks to query the index from the runtime (aka. state nonce). #[rpc(name = "system_accountNextIndex", alias("account_nextIndex"))] fn nonce(&self, account: AccountId) -> FutureResult; + + /// Dry run an extrinsic at a given block. Return SCALE encoded ApplyExtrinsicResult. + #[rpc(name = "system_dryRun", alias("system_dryRunAt"))] + fn dry_run(&self, extrinsic: Bytes, at: Option) -> FutureResult; +} + +/// Error type of this RPC api. +pub enum Error { + /// The transaction was not decodable. + DecodeError, + /// The call to runtime failed. + RuntimeError, } -const RUNTIME_ERROR: i64 = 1; +impl From for i64 { + fn from(e: Error) -> i64 { + match e { + Error::RuntimeError => 1, + Error::DecodeError => 2, + } + } +} /// An implementation of System-specific RPC methods on full client. pub struct FullSystem { client: Arc, pool: Arc

, + deny_unsafe: DenyUnsafe, _marker: std::marker::PhantomData, } impl FullSystem { /// Create new `FullSystem` given client and transaction pool. - pub fn new(client: Arc, pool: Arc

) -> Self { + pub fn new(client: Arc, pool: Arc

, deny_unsafe: DenyUnsafe,) -> Self { FullSystem { client, pool, + deny_unsafe, _marker: Default::default(), } } } -impl SystemApi for FullSystem +impl SystemApi<::Hash, AccountId, Index> + for FullSystem where C: sp_api::ProvideRuntimeApi, C: HeaderBackend, C: Send + Sync + 'static, C::Api: AccountNonceApi, + C::Api: BlockBuilder, P: TransactionPool + 'static, Block: traits::Block, AccountId: Clone + std::fmt::Display + Codec, @@ -93,8 +118,8 @@ where let best = self.client.info().best_hash; let at = BlockId::hash(best); - let nonce = api.account_nonce(&at, account.clone()).map_err(|e| Error { - code: ErrorCode::ServerError(RUNTIME_ERROR), + let nonce = api.account_nonce(&at, account.clone()).map_err(|e| RpcError { + code: ErrorCode::ServerError(Error::RuntimeError.into()), message: "Unable to query nonce.".into(), data: Some(format!("{:?}", e).into()), })?; @@ -104,6 +129,38 @@ where Box::new(result(get_nonce())) } + + fn dry_run(&self, extrinsic: Bytes, at: Option<::Hash>) -> FutureResult { + if let Err(err) = self.deny_unsafe.check_if_safe() { + return Box::new(rpc_future::err(err.into())); + } + + let dry_run = || { + let api = self.client.runtime_api(); + let at = BlockId::::hash(at.unwrap_or_else(|| + // If the block hash is not supplied assume the best block. + self.client.info().best_hash + )); + + let uxt: ::Extrinsic = Decode::decode(&mut &*extrinsic).map_err(|e| RpcError { + code: ErrorCode::ServerError(Error::DecodeError.into()), + message: "Unable to dry run extrinsic.".into(), + data: Some(format!("{:?}", e).into()), + })?; + + let result = api.apply_extrinsic(&at, uxt) + .map_err(|e| RpcError { + code: ErrorCode::ServerError(Error::RuntimeError.into()), + message: "Unable to dry run extrinsic.".into(), + data: Some(format!("{:?}", e).into()), + })?; + + Ok(Encode::encode(&result).into()) + }; + + + Box::new(result(dry_run())) + } } /// An implementation of System-specific RPC methods on light client. @@ -131,7 +188,8 @@ impl LightSystem { } } -impl SystemApi for LightSystem +impl SystemApi<::Hash, AccountId, Index> + for LightSystem where P: TransactionPool + 'static, C: HeaderBackend, @@ -165,8 +223,8 @@ where ).compat(); let future_nonce = future_nonce.and_then(|nonce| Decode::decode(&mut &nonce[..]) .map_err(|e| ClientError::CallResultDecode("Cannot decode account nonce", e))); - let future_nonce = future_nonce.map_err(|e| Error { - code: ErrorCode::ServerError(RUNTIME_ERROR), + let future_nonce = future_nonce.map_err(|e| RpcError { + code: ErrorCode::ServerError(Error::RuntimeError.into()), message: "Unable to query nonce.".into(), data: Some(format!("{:?}", e).into()), }); @@ -176,6 +234,14 @@ where Box::new(future_nonce) } + + fn dry_run(&self, _extrinsic: Bytes, _at: Option<::Hash>) -> FutureResult { + Box::new(result(Err(RpcError { + code: ErrorCode::MethodNotFound, + message: "Unable to dry run extrinsic.".into(), + data: None, + }))) + } } /// Adjust account nonce from state, so that tx with the nonce will be @@ -224,6 +290,7 @@ mod tests { use futures::executor::block_on; use substrate_test_runtime_client::{runtime::Transfer, AccountKeyring}; use sc_transaction_pool::{BasicPool, FullChainApi}; + use sp_runtime::{ApplyExtrinsicResult, transaction_validity::{TransactionValidityError, InvalidTransaction}}; #[test] fn should_return_next_nonce_for_some_account() { @@ -255,7 +322,7 @@ mod tests { let ext1 = new_transaction(1); block_on(pool.submit_one(&BlockId::number(0), source, ext1)).unwrap(); - let accounts = FullSystem::new(client, pool); + let accounts = FullSystem::new(client, pool, DenyUnsafe::Yes); // when let nonce = accounts.nonce(AccountKeyring::Alice.into()); @@ -263,4 +330,91 @@ mod tests { // then assert_eq!(nonce.wait().unwrap(), 2); } + + #[test] + fn dry_run_should_deny_unsafe() { + let _ = env_logger::try_init(); + + // given + let client = Arc::new(substrate_test_runtime_client::new()); + let pool = Arc::new( + BasicPool::new( + Default::default(), + Arc::new(FullChainApi::new(client.clone())), + None, + ).0 + ); + + let accounts = FullSystem::new(client, pool, DenyUnsafe::Yes); + + // when + let res = accounts.dry_run(vec![].into(), None); + + // then + assert_eq!(res.wait(), Err(RpcError::method_not_found())); + } + + #[test] + fn dry_run_should_work() { + let _ = env_logger::try_init(); + + // given + let client = Arc::new(substrate_test_runtime_client::new()); + let pool = Arc::new( + BasicPool::new( + Default::default(), + Arc::new(FullChainApi::new(client.clone())), + None, + ).0 + ); + + let accounts = FullSystem::new(client, pool, DenyUnsafe::No); + + let tx = Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Bob.into(), + amount: 5, + nonce: 0, + }.into_signed_tx(); + + // when + let res = accounts.dry_run(tx.encode().into(), None); + + // then + let bytes = res.wait().unwrap().0; + let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_slice()).unwrap(); + assert_eq!(apply_res, Ok(Ok(()))); + } + + #[test] + fn dry_run_should_indicate_error() { + let _ = env_logger::try_init(); + + // given + let client = Arc::new(substrate_test_runtime_client::new()); + let pool = Arc::new( + BasicPool::new( + Default::default(), + Arc::new(FullChainApi::new(client.clone())), + None, + ).0 + ); + + let accounts = FullSystem::new(client, pool, DenyUnsafe::No); + + let tx = Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Bob.into(), + amount: 5, + nonce: 100, + }.into_signed_tx(); + + // when + let res = accounts.dry_run(tx.encode().into(), None); + + // then + let bytes = res.wait().unwrap().0; + let apply_res: ApplyExtrinsicResult = Decode::decode(&mut bytes.as_slice()).unwrap(); + assert_eq!(apply_res, Err(TransactionValidityError::Invalid(InvalidTransaction::Stale))); + } }