profile
viewpoint
Mazdak Farrokhzad Centril Sweden http://centril.me Compiler engineer and former Rust Language and Release team member.

AltSysrq/proptest 628

Hypothesis-like property testing for Rust

Centril/dbg 5

Implementation of https://github.com/rust-lang/rfcs/pull/2173 in stable rust

Centril/firefox-line 4

Rewrite of Mozilla Labs Prospector Oneliner 2 with Addons SDK

Centril/aTetria 2

Initial push of Framework + Model + input processors.

Centril/android-numberpicker 1

A backport of the Android 4.2 NumberPicker

Centril/bin-util 1

small utility scripts, mostly git related.

Centril/consensusbot 1

Next gen rfcbot

Centril/android-project-template 0

template project for android.

Centril/areweasyncyet.rs 0

Are we async yet?

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha cf7441df13b12934603b9332ca3d1ab611dd5531

committee: mesh-1065 regression test

view details

push time in 14 hours

push eventPolymathNetwork/Polymesh

Francisco Miguel García

commit sha c9b2dadc271aa88c212891230055a76e89608b23

Confidential Scope claim (#499) * New confidential pallet * New confidential pallet * Add UT for confidential module * New confidential pallet * Add UT for confidential module * Manual WASM build * Customized WasmBuilder added * V1 build.rs fixed * Cryptography dep used from git * RNG with hosted functions * Add Crtyptography as submodule * Update CI SSH keys * Subtle requires nightly build * Fix Confidential's UT * Subtle requires nightly build * Add CDD_ID * CDD Claim support CDD_ID * Fix UT with two step registration * Fix CDD_ID errors on CLI test * Update cryptography to Master * CodeReview changes * CodeReview changes * CodeReview changes: Move crypto repo * CodeReview: Move crypt lib * At runtime level fixed * Remove custom substrate * Use `cryptography` version * CodeReview: changes requested * CodeReview: Remove dbg messages * IdentityId as 255bit, improve Scalar conversions * CodeReview: Remove warnings * Use fixed Settlement branch * CodeReview: Remove unused code * CodeReview: Better fn name * New predicate to validate Investor ID ZK proofs * Investor ZkProof predicate * Verify CDD_ID on adding confidential claim * Add error case on confidential tests * Add new types to Schema * CodeReview: Remove `.expect` * Update cargo.lock * Update cryptography Co-authored-by: Adam Dossa <adam.dossa@gmail.com>

view details

Vladimir Komendantskiy

commit sha 42367bfd879cb2673de7b94e45428680f69bf045

MESH-1169: rename master and signing keys (#509) * renamed master and signing to primary and secondary * review comments * removed the obsolete storage item and its runtime upgrade function * removed identity runtime storage version * unused import removed * Revert "unused import removed" This reverts commit 56cf40d9cf0016bedeb4cce4b8113ab0497ade3f. * Revert "removed identity runtime storage version" This reverts commit cc6e1356cf2a39ef636da5abb748cd4569830154. * Revert "removed the obsolete storage item and its runtime upgrade function" This reverts commit ed73c611f15b9863ac6bcb09b5d1565e9389be3c. * removed identity storage version Co-authored-by: Adam Dossa <adam.dossa@gmail.com>

view details

Mazdak Farrokhzad

commit sha 59ac3ea1903b50738d530308d6cb8ed9e28f02c7

pips: cleanup unnecessary bounds

view details

Mazdak Farrokhzad

commit sha c72533bdeffa7502ecea4123b146f994780dab32

pips/amend_proposal: code reuse

view details

Mazdak Farrokhzad

commit sha c908060c102163855679d75d0bf097b7dbbadc32

pips: define snapshot data model

view details

Mazdak Farrokhzad

commit sha bc902a98e60bb6113a91f3ec72e4077368c2fce9

pips: add clear_snapshot + snapshot dispatchables

view details

Mazdak Farrokhzad

commit sha 64094ea8ff91d7bb629c6fdea9196bc6798e332d

pips: add enact_snapshot_results dispatchable, etc.

view details

Mazdak Farrokhzad

commit sha 845073ccc09aefbe0dab8999cf59fdec4e8adba4

pips: drive-by perf tweaks

view details

Mazdak Farrokhzad

commit sha 99ce8efb7493ffde9627aee6bda63d68f5387949

pips: scheduled referendums -> execution schedule

view details

Mazdak Farrokhzad

commit sha 164b58a495fed3146cbe2be2c2ea10be04838f2a

pips: tweak kill(reject)/prune_proposal

view details

Mazdak Farrokhzad

commit sha 875d582e7c865dfdbd571616832ad762b27e2675

pips: remove state/concepts from v1

view details

Mazdak Farrokhzad

commit sha 5d50440366137e416105cf26c634b95da09bc39c

pips: remove some referendum logic

view details

Mazdak Farrokhzad

commit sha 00fa5ae25af437d09433cff6d2131dceccb91714

pips: remove more referendum stuff

view details

Mazdak Farrokhzad

commit sha 0124f69bdce6d0d019ec143e2efad14e05d02dd5

pips: reject/prune_proposal: unschedule if needed

view details

Mazdak Farrokhzad

commit sha daa28b475db4b5e8cb237a4b30c3ed751d9be551

pips: remove duration/end concept

view details

Mazdak Farrokhzad

commit sha 131e16e5e6dcf36fa942c606f868584a1d25528a

pips: remove quorum threshold

view details

Mazdak Farrokhzad

commit sha b7e36d9ed20b37e85c4ab94e296d82d10a915089

pips: maybe_unsnapshot_pip when rejecting/pruning

view details

Mazdak Farrokhzad

commit sha e25f1bcc7c695970062b9d31dcf255fdc7559af3

pips: define proposer structure

view details

Mazdak Farrokhzad

commit sha edffb5301121dd2afcc0ea546797c002fe1e05f4

pips: add todo re. events

view details

Mazdak Farrokhzad

commit sha ce40c31ea03478224011a428ec9dc3290c46022c

pips: renaming, cleanup, & emit event

view details

push time in 15 hours

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 933fe69d23a243316e00a3427bb00d8c1ffde12e

pips: address review comments

view details

Mazdak Farrokhzad

commit sha ac94d625eb583ae277d0975ecb88a8f1f6489a8f

pips: update schema with new types

view details

push time in 15 hours

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 821c8b47181288c952dfe462426d1a86817035ad

misc cleanup

view details

push time in 17 hours

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 5762c021f4bb308270dfc4db6d28f6a7071dcb9b

misc cleanup

view details

push time in 17 hours

Pull request review commentPolymathNetwork/Polymesh

[WIP] Governance / Mesh 1146, etc.

 impl pallet_staking::Trait for Runtime { parameter_types! {     pub const MotionDuration: BlockNumber = 0; }++/// Voting majority origin for `Instance`.+type VMO<Instance> = committee::EnsureProportionAtLeast<_1, _2, AccountId, Instance>;

Per discussion on slack: just a private type alias & VMO is PascalCase cause its an initialism.

Centril

comment created time in 17 hours

push eventPolymathNetwork/Polymesh

Satyam Agrawal

commit sha 6eb67bb85d0fa9ef1fe4608a06f6c13cf5fc7a53

Mesh 1132/Update weight polynomial (#491) * changes in weight polynomial * change weight of dispatchables * remove un-necessary import * increase weight * bump up the weight * fix minor tests * bumpup the weights * fix test Co-authored-by: satyam <satyam@secureblocks.io> Co-authored-by: Adam Dossa <adam.dossa@gmail.com>

view details

Mazdak Farrokhzad

commit sha 1d10b370cfd5ef34cb0de54bf43b36bb4a259d33

pips: cleanup unnecessary bounds

view details

Mazdak Farrokhzad

commit sha bcca69f31b52853fc7e58f38b4c2fd441d568773

pips/amend_proposal: code reuse

view details

Mazdak Farrokhzad

commit sha d5661ef0c9090aff93f0097cd723a1fe33fbd09c

pips: define snapshot data model

view details

Mazdak Farrokhzad

commit sha 243f5b334be5459cf39e2a1b6e5a94b7c5a9c808

pips: add clear_snapshot + snapshot dispatchables

view details

Mazdak Farrokhzad

commit sha 572ffbd756f4e4d37ba93f9b666ab247a89f0bc6

pips: add enact_snapshot_results dispatchable, etc.

view details

Mazdak Farrokhzad

commit sha 725367596701f04d7fae2ed41c8181c51c8ba8e2

pips: drive-by perf tweaks

view details

Mazdak Farrokhzad

commit sha e03cb47e4da7b47ab36e7dcc5fb2a176b1dc992d

pips: scheduled referendums -> execution schedule

view details

Mazdak Farrokhzad

commit sha b8ab9bf6346253ea18439696e9e6733a408d4fec

pips: tweak kill(reject)/prune_proposal

view details

Mazdak Farrokhzad

commit sha 92d59d0ee22398eb80206cd80d43998636facb00

pips: remove state/concepts from v1

view details

Mazdak Farrokhzad

commit sha 05b79dae2fe20922160104b00725692b4a635d5c

pips: remove some referendum logic

view details

Mazdak Farrokhzad

commit sha 96b4bc9949c231fff26477c219dd0c01567dcf15

pips: remove more referendum stuff

view details

Mazdak Farrokhzad

commit sha e2f28587b7e6d4fe21abf6e274890a41e53dfbd4

pips: reject/prune_proposal: unschedule if needed

view details

Mazdak Farrokhzad

commit sha b5ccced774fc6c709e2d2bf367f19d38548bbdbc

pips: remove duration/end concept

view details

Mazdak Farrokhzad

commit sha b85decd9c08bdf8c5d5f869ab87b3130c4c6fdac

pips: remove quorum threshold

view details

Mazdak Farrokhzad

commit sha 6eaf7bb6710d307418abc6a6c6058fa3b8df27c2

pips: maybe_unsnapshot_pip when rejecting/pruning

view details

Mazdak Farrokhzad

commit sha b2ffa4ea5e7470726bc05a986795acbb05c2c82f

pips: define proposer structure

view details

Mazdak Farrokhzad

commit sha bfb67d4f11ee241d126eb0f10d275fe76cf36175

pips: add todo re. events

view details

Mazdak Farrokhzad

commit sha 43e8f796966a0447648d5a1996bc8489649b912b

pips: renaming, cleanup, & emit event

view details

Mazdak Farrokhzad

commit sha d84b3b03de5b65aa42292fdbf35323a290c73cbc

pips: add Committee::Upgrade

view details

push time in 17 hours

push eventPolymathNetwork/Polymesh

Francisco Miguel García

commit sha da400ddcb2dffbd30c6208fa6b984a6d70493c5d

CDD_ID support (#456) * New confidential pallet * New confidential pallet * Add UT for confidential module * New confidential pallet * Add UT for confidential module * Manual WASM build * Customized WasmBuilder added * V1 build.rs fixed * Cryptography dep used from git * RNG with hosted functions * Add Crtyptography as submodule * Update CI SSH keys * Subtle requires nightly build * Fix Confidential's UT * Subtle requires nightly build * Add CDD_ID * CDD Claim support CDD_ID * Fix UT with two step registration * Fix CDD_ID errors on CLI test * Update cryptography to Master * CodeReview changes * CodeReview changes * CodeReview changes: Move crypto repo * CodeReview: Move crypt lib * At runtime level fixed * Remove custom substrate * Use `cryptography` version * CodeReview: changes requested * CodeReview: Remove dbg messages * IdentityId as 255bit, improve Scalar conversions * CodeReview: Remove warnings * Use fixed Settlement branch * CodeReview: Remove unused code * CodeReview: Better fn name

view details

CJP10

commit sha 96ca42a628021e630d3ffac40499650d074b7282

MESH-1087/Depreciate Simple Token (#488) * Rebasing MESH-1087 # Conflicts: # pallets/runtime/develop/src/runtime.rs # pallets/runtime/testnet-v1/src/runtime.rs # pallets/runtime/tests/src/dividend_test.rs # pallets/runtime/tests/src/simple_token_test.rs * fix broken test * remove unused event

view details

Mazdak Farrokhzad

commit sha 3a85cd94266ba39c7a2252445c63411a685be793

mesh-1129 use `TestStorage` & `ExtBuilder` for `transaction_payment_test`. (#493) * mesh-1129, part1: use `TestStorage` for `transaction_payment_test`. * mesh-1129: cleanup unused stuff * mesh-1129: existential_deposit -> balance_factor + cleanup * mesh-1129: nix base_fee & add base_weight * mesh-1129: more builder methods * mesh-1129: ext_builder: zero insignificant values * mesh-1129: nix duplicate ExtBuilder Co-authored-by: Adam Dossa <adam.dossa@gmail.com>

view details

Satyam Agrawal

commit sha 5cc8fb038b2097455c31f5b1a3bd4f495b85f8b5

Upgrade the substrate version to rc4+ to support the ext_get_runtime_… (#498) * upgrade the substrate version to rc4+ to support the ext_get_runtime_storage and ext_dispatch * update reference of submodules * update reference of cryptography module Co-authored-by: satyam <satyam@secureblocks.io> Co-authored-by: Adam Dossa <adam.dossa@gmail.com> Co-authored-by: poly-auto-merge[bot] <65769705+poly-auto-merge[bot]@users.noreply.github.com>

view details

CJP10

commit sha 8a5c36666e4145ac0d1b84af8b661b529313bfe0

MESH-1116\Clean up clippy lints and cargo warnings (#495) * first pass at lints * Update pallets/runtime/common/src/sto_capped.rs Co-authored-by: Mazdak Farrokhzad <twingoow@gmail.com> * correctly apply fixes to lint * fix bad merge Co-authored-by: Mazdak Farrokhzad <twingoow@gmail.com> Co-authored-by: Adam Dossa <adam.dossa@gmail.com> Co-authored-by: poly-auto-merge[bot] <65769705+poly-auto-merge[bot]@users.noreply.github.com>

view details

Claus Strommer

commit sha 305323757611e350c4ae191050b6f56a3af1cc95

Devops 131/concourse core integration (#505) * Concourse pipelines * Use secrets for Slack webhook URLs * Updated secret references * Production readiness updates * Add dockerfiles * Update secrets * Fix resource order * Updte pipeline secrets * Concourse pipelines * Use secrets for Slack webhook URLs * Updated secret references * Production readiness updates * Add dockerfiles * Update secrets * Fix resource order * Updte pipeline secrets * DEVOPS-131/concourse-core-integration * Remove `-j 1` fallback builds * Don't sync `cargo build` and `cargo test` caches. * DEVOPS-131/concourse-core-integration * Add timeout to integration tests * DEVOPS-131/concourse-core-integration Make disabling of fork PRs explicit * Enable submodule cloning * Add dev tag to github-pr resource docker image Workaround to enable submodules. See https://github.com/telia-oss/github-pr-resource/issues/206 * DEVOPS-131/concourse-core-integration Submodule credentials workaround * DEVOPS-131/concourse-core-integration Fix file path in condition * DEVOPS-131/concourse-core-integration * Update cargo test step to fetch submodules * DEVOPS-131/concourse-core-integration * Update cargo test packages * DEVOPS-131/concourse-core-integration Enable toolchain for `cargo test` * DEVOPS-131/concourse-core-integration * Update `cargo test` container * DEVOPS-131/concourse-core-integration * Update `master` and `tooling` branch and PR pipelines * Update build step to have the binary report the build version

view details

Jeremías Díaz

commit sha ba34d2e0362dab51d479ded6d7a0cfec4fa190a1

fix: use correct type for rules (#510)

view details

Mudit Gupta

commit sha ff554b36825e850633036b41d045a34e635e3969

formatting (#512)

view details

Claus Strommer

commit sha 611a968642e7ca987784ff4d61b39e1a746224b0

DEVOPS-177/Remove-concourse-commit-filters (#508) * remove commit filters from git resources Co-authored-by: Adam Dossa <adam.dossa@gmail.com> Co-authored-by: poly-auto-merge[bot] <65769705+poly-auto-merge[bot]@users.noreply.github.com>

view details

Mazdak Farrokhzad

commit sha 43eb45838b61802ea6411942d0b20e350046503f

pips: cleanup unnecessary bounds

view details

Mazdak Farrokhzad

commit sha 611104d2a3b5f1e7b26559e68c66c4bcdd19cc08

pips/amend_proposal: code reuse

view details

Mazdak Farrokhzad

commit sha 3b50180706ec283feb050c4998918c43e90baacb

pips: define snapshot data model

view details

Mazdak Farrokhzad

commit sha bc759806cd207561326ccb1b78c1923211156a1e

pips: add clear_snapshot + snapshot dispatchables

view details

Mazdak Farrokhzad

commit sha c66b6bd355175e95c8e4c3c297c4b7f18605851d

pips: add enact_snapshot_results dispatchable, etc.

view details

Mazdak Farrokhzad

commit sha efc03f0375f2b15f076650f4ed7219fd6a9753c2

pips: drive-by perf tweaks

view details

Mazdak Farrokhzad

commit sha 560a5575a91ea77986ffe56e19426608f0cf919a

pips: scheduled referendums -> execution schedule

view details

Mazdak Farrokhzad

commit sha 5ed2f564462d2331da40565988629cda55cfff25

pips: tweak kill(reject)/prune_proposal

view details

Mazdak Farrokhzad

commit sha dcbbe709978a3227243c32fbae295f141e75de91

pips: remove state/concepts from v1

view details

Mazdak Farrokhzad

commit sha 9a400566634fe150c7d0c5dab2fedda20571d33a

pips: remove some referendum logic

view details

Mazdak Farrokhzad

commit sha c9ee59fedda75cc1e0015a5b67349ef66275fcf1

pips: remove more referendum stuff

view details

push time in 18 hours

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 5aea5af758c1ca52a6d678c63de9b57cd618ba16

pips: fix misc bugs, etc.

view details

Mazdak Farrokhzad

commit sha 627c5113bc94d2a95371906a331e5059a4e889f5

pips: ravamp tests

view details

Mazdak Farrokhzad

commit sha 6d4fc2fd69d18edbdf9d5b4010cc53a19252530e

pips: fix debug problems

view details

push time in 20 hours

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 705978284ce878bad0657ec6b926251303674441

wip

view details

Mazdak Farrokhzad

commit sha a6a232dd51037fe88e25924ecec49b0e70acf7f6

pips: fix debug problems

view details

push time in 21 hours

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha b4751a9446d87ec249b7763629b07f03975539fc

wip again

view details

Mazdak Farrokhzad

commit sha c51b26a42732bea144543022376b19e7f8216804

pips: fix debug problems

view details

push time in 21 hours

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 25e099bfed7e14743ed452c0f7d27ba346f7ba15

most wip

view details

push time in a day

PR opened PolymathNetwork/Polymesh

[WIP] Governance / Mesh 1146, etc.

As requested on a meeting, I'm opening this PR as a WIP, and I plan to finish tests for the last extrinsic, rebase, and then it should be ready for review..

Feel free to leave comments, etc.

cc @adamdossa @maxsam4

+2673 -2071

0 comment

14 changed files

pr created time in 4 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha d882688fcf7ac07007348d99502806a515c34320

wip

view details

Mazdak Farrokhzad

commit sha 8c4c8fd9b425eee37ca582971b21be6a6a64e183

more wip

view details

push time in 4 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 898eed332d708ee6118b44b8cae9a66042ca34a5

pips: nix beneficiaries logic

view details

Mazdak Farrokhzad

commit sha 8ed0140ec0b3955817ea193673c58e3730621dae

wip

view details

push time in 5 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha ee25ca593b103317941f99b65b4993747f95370a

pips/on_runtime_upgrade: fix ProposalResult for nays

view details

push time in 6 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 617621b485da20405324b1936c035fb9e670a89e

pips/committee: simplify, only expose consensus mechanism

view details

Mazdak Farrokhzad

commit sha 70d34067227baa9f8a983a3fd413bd9aa146e1d7

pips: tweak `enact_snapshot_results` for better security.

view details

push time in 6 days

issue commentAltSysrq/proptest

How do I use HashMapStrategy? Documentation unclear, has no examples.

Sure, you can use .prop_filter on the key_strat, value_strat, or the final hash_map(...) strategy. Doing so on the latter will give you a &HashMap<K, V> which you can apply a predicate to. You can also ignore irrelevant maps in each test itself as well, but that will scale less well if the strategy is used a lot. If you can create a correct key/value/map by construction, then that is preferable to filtering (see the book / docs on prop_filter for elaboration).

dyc3

comment created time in 7 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha cb25e06473bbfb2dfa67a33e6810a61fdfa4e1ec

pips: set_release_coordinator directly via governance

view details

Mazdak Farrokhzad

commit sha e51f10f51f2734a7eb7e2b3fed0d353c7cbb4104

pips/committee: enable rejection + fix threshold usage on member removal

view details

Mazdak Farrokhzad

commit sha 1b4f67d3353f178b16eea87e1bb5ed9e953486e4

pips/committee: update the tests

view details

push time in 7 days

issue commentAltSysrq/proptest

How do I use HashMapStrategy? Documentation unclear, has no examples.

HashMapStrategy is literally a thin wrapper around a strategy for creating Vec<(Key, Value)> up to the specified size and filtered to not go below the minumum.

So if you e.g., use hash_map(key_strat, val_strat, 5...10), then what happens is that a vec((key_strat, val_strat), 5..10) is created, and then this is mapped using vec.into_iter().collect(), giving you a HashMap of 5..10 elements with keys drawn from key_strat and values from val_strat.

Note that most RangeXYZ types implement Into<SizeRange>, which is what hash_map accepts, so you can use the normal Rust range syntax.

I hope that answers your question.

dyc3

comment created time in 7 days

delete branch PolymathNetwork/Polymesh

delete branch : mesh-1129

delete time in 7 days

PR opened PolymathNetwork/Polymesh

Reviewers
pips: bug fix, use `=`, not `+=` to aggregate nays

Fixes a bug in the nay-votes aggregation in the PIPs module.

+2 -2

0 comment

1 changed file

pr created time in 7 days

create barnchPolymathNetwork/Polymesh

branch : fix-pips-nays

created branch time in 7 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 846797d9c98bce9892c988d2e957d43fc85c1f11

pips: resolve some todos

view details

push time in 8 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 61a2df3a4374417ffdc74860f126b34ae26971a8

pips: simplify committee code

view details

Mazdak Farrokhzad

commit sha 77525627398cbc5daae71484dcc80175244cf24f

pips: expose voting-majority stuff to committee

view details

push time in 8 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 637ef2179e722de53bc412ab21332f29e40a820b

pips: simplify 'execute_proposal'

view details

Mazdak Farrokhzad

commit sha 1034c4df19092df9255aecef4aa7b9c4c8b60134

pips: update module docs

view details

push time in 11 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha a65c7ebc034099e220239b1e8cdc98694f7d9b57

pips: limit active pips count

view details

push time in 11 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha fa57684e6d9047dabec0883379de0fa9c1241636

pips: use '.into()' instead of '?'

view details

Mazdak Farrokhzad

commit sha d959bad74484fc54ad41ff19438516bdc9ffe616

pips: simplify 'unsafe_vote'

view details

Mazdak Farrokhzad

commit sha 5de493545195de535e77b264475a81001806c8cd

pips: override_execution_schedule -> reschedule_execution

view details

Mazdak Farrokhzad

commit sha 12983720c0fd174864daad9a781730cb91be9d0b

pips/snapshot: only community pips

view details

Mazdak Farrokhzad

commit sha a6ddb55df6e966a35efda939a723abaab3b78b8d

pips: flesh out `ensure_signed_by` re. committees. Add dummy technical & upgrade committees.

view details

Mazdak Farrokhzad

commit sha 7a86502b5a5a117cf6fa385e9b322578a4e7769c

pips: add 'approve_committee_proposal', fix bugs, & cleanup

view details

Mazdak Farrokhzad

commit sha 29f6aea32395ca0040f6386ee72f53721cea05e8

pips/enact_snapshot_result: SkippedCount -> u8

view details

push time in 11 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 717b634ee586cb197d512e6345d415e7b8b71ff6

pips: remove resolved todos

view details

push time in 11 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha c95ae65a8f23957789b1bf7ab0a376efcb519a59

pips: set min_proposal_deposit = 0

view details

push time in 11 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 891972d2495a6990a60fb273efb6f1d65e395f06

pips: integrate `Proposer`. todo: define committee origins & use in `ensure_signed_by`.

view details

Mazdak Farrokhzad

commit sha 26f07dd676e925882e259f2e91bf6cc48a763e1b

pips: nix outdated referendum stuff.

view details

Mazdak Farrokhzad

commit sha 4f59af118a1a41bb55ff590f598699fd00133897

pips: fix set_proposal_cool_off_period's event

view details

push time in 11 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 80fbd679523d8a3733c14398403bf0566efee59d

pips: clear a done todo

view details

Mazdak Farrokhzad

commit sha d661dee2e8d8bc1b1c0fae52b41c9f35122603c2

pips: add a todo

view details

Mazdak Farrokhzad

commit sha f828ef60877bec169842af69faca54f4eb53a504

wip: use Proposer

view details

push time in 12 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha e3a8b22be12ece6a23bd1b4d5c575df6f3e0bfbc

pips: add Committee::Upgrade

view details

Mazdak Farrokhzad

commit sha 6cb6df81d545f0b5d27fd8630e7ca5ba3a80e5e5

pips: Vote::None => Option::None

view details

Mazdak Farrokhzad

commit sha 0e675d2b44b910cced7339515e23eca6bd0901ee

pips: simplify 'Vote'

view details

push time in 12 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 02be4059faee6870a05023e4e160156f318e46fd

pips: renaming, cleanup, & emit event

view details

push time in 12 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha fb45b546c23b6f61d471294dedcfcc3b56cc4d01

pips: renaming & cleanup

view details

push time in 12 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 96c752f621dbf117c85a9b45589744e3deb8a372

pips: add todo re. events

view details

push time in 12 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 0542adb9d2dc0318464b36ef09e08293085e9405

pips: maybe_unsnapshot_pip when rejecting/pruning

view details

Mazdak Farrokhzad

commit sha 8fddb735543c7fe182a63908514a0927157bf500

pips: define proposer structure

view details

push time in 12 days

create barnchPolymathNetwork/Polymesh

branch : mesh-1146

created branch time in 12 days

delete branch PolymathNetwork/Polymesh

delete branch : mesh-1129-p2

delete time in 13 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1132/Update weight polynomial

 use polymesh_primitives::{traits::IdentityCurrency, Signatory, TransactionError}  use codec::{Decode, Encode}; use frame_support::{-    decl_module, decl_storage,+    debug, decl_module, decl_storage,

Unused?

satyamakgec

comment created time in 14 days

Pull request review commentPolymathNetwork/Polymesh

MESH-1116\Clean up clippy lints and cargo warnings

 impl<T: Trait> Module<T> {                     0                 }); -            let era_length = session_index-                .checked_sub(current_era_start_session_index)-                .unwrap_or(0); // Must never happen.

Should the comment be kept?

CJP10

comment created time in 14 days

Pull request review commentPolymathNetwork/Polymesh

MESH-1116\Clean up clippy lints and cargo warnings

 decl_module! {             <StosByToken<T>>::insert((ticker, sto_count), sto);             <StoCount>::insert(ticker, new_sto_count); -            if simple_token_ticker.len() > 0 {+            if simple_token_ticker.is_empty() {
            if !simple_token_ticker.is_empty() {
CJP10

comment created time in 14 days

Pull request review commentPolymathNetwork/Polymesh

MESH-1116\Clean up clippy lints and cargo warnings

 impl<T: Trait> Module<T> {     ) -> bool {         match authorization_data {
        type_of_auth == match authorization_data {

(factor out equality check from each branch)

CJP10

comment created time in 14 days

Pull request review commentPolymathNetwork/Polymesh

MESH-1116\Clean up clippy lints and cargo warnings

 impl<T: Trait> Module<T> {                 let timelock = Self::timelock();                 if timelock.is_zero() {                     let _ = Self::handle_bridge_tx_now(bridge_tx, false)?;-                    return Ok(());+                    Ok(())                 } else {                     let _ = Self::handle_bridge_tx_later(bridge_tx, timelock)?;-                    return Ok(());+                    Ok(())

Branches have identical tails, move Ok(()) out of the branch.

CJP10

comment created time in 14 days

PR opened PolymathNetwork/Polymesh

Reviewers
mesh-1129: Use `TestStorage` & `ExtBuilder` for `transaction_payment_test`

Based on https://github.com/PolymathNetwork/Polymesh/pull/493 (first commit).

  • Resolves MESH 1129: transaction_payment_test now uses TestStorage and ExtBuilder.
  • Some other drive-by cleanup of unused imports.
+204 -484

0 comment

12 changed files

pr created time in 14 days

create barnchPolymathNetwork/Polymesh

branch : mesh-1129-p2

created branch time in 14 days

issue commentbillziss-gh/sshfs-win

net use: error 67 with `\\sshfs.k\`, works with `\\sshfs\` & `sshfs-win svc \sshfs.k\`

Ah, I see; that explains it then. :)

I was hoping that ssh-agent would help alleviate this issue, as I don't have to input the passphrase normally when using just plain ssh nas, but it doesn't seem like it does.

Would it be possible to use ssh-agent with SSHFS-Win somehow or alternatively pass the passphrase?

Centril

comment created time in 14 days

delete branch PolymathNetwork/Polymesh

delete branch : pin-2020-07-26

delete time in 15 days

issue commentbillziss-gh/sshfs-win

net use: error 67 with `\\sshfs.k\`, works with `\\sshfs\` & `sshfs-win svc \sshfs.k\`

Hmm; so I tried the following sequence:

λ  & 'C:\Program Files (x86)\WinFsp\bin\launchctl-x64.exe' list
OK
sshfs.k centril@centrilnas2!9099

λ  & 'C:\Program Files (x86)\WinFsp\bin\launchctl-x64.exe' stop sshfs.k centril@centrilnas2!9099
OK

λ  & 'C:\Program Files (x86)\WinFsp\bin\launchctl-x64.exe' list
OK

λ  net use Y: "\\sshfs.k\centril@centrilnas2!9099"
System error 67 has occurred.

The network name cannot be found.

λ  & 'C:\Program Files (x86)\WinFsp\bin\launchctl-x64.exe' list
OK
sshfs.k centril@centrilnas2!9099

λ  & 'C:\Program Files (x86)\WinFsp\bin\launchctl-x64.exe' info sshfs.k centril@centrilnas2!9099
OK
sshfs.k centril@centrilnas2!9099
"C:\Program Files\SSHFS-Win\bin\sshfs-win.exe" svc "\sshfs.k\centril@centrilnas2!9099" "Y:" "CENTRILG55\twing"

At this point, the path Y:\ doesn't work. So I tried:

λ "C:\Program Files\SSHFS-Win\bin\sshfs-win.exe" svc "\sshfs.k\centril@centrilnas2!9099" "Y:" "CENTRILG55\twing"
Warning: Permanently added '[centrilnas2]:9099,[192.168.0.115]:9099' (ECDSA) to the list of known hosts.
Enter passphrase for key '/cygdrive/c/Users/twing/.ssh/id_rsa':
The service sshfs has been started.

Now Y:\ shows up in the file system and is usable, but closing the terminal unmount it.

Centril

comment created time in 15 days

Pull request review commentPolymathNetwork/Polymesh

[WIP] Mesh 1079/permission type

+// This file is part of the Polymesh distribution (https://github.com/PolymathNetwork/Polymesh).+// Copyright (c) 2020 Polymath+//+// 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, version 3.+//+// 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 <http://www.gnu.org/licenses/>.++use codec::{Decode, Encode};+#[cfg(feature = "std")]+use sp_runtime::{Deserialize, Serialize};+use sp_std::prelude::Vec;++/// Ordering in a lattice, for example, the lattice of subsets of a set.+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub enum LatticeOrdering {+    /// Inclusion of the first subset into the second subset.+    Less,+    /// Set equality.+    Equal,+    /// The subsets are pairwise different.+    Incomparable,+    /// Inclusion of the second subset into the first subset.+    Greater,+}++/// The lattice order trait.+pub trait LatticeOrd {+    /// The lattice comparison.+    fn lattice_cmp(&self, other: &Self) -> LatticeOrdering;+}++/// The type of subsets of an open set of elements of type `A` where the whole set is always+/// considered to be bigger than any finite set of its elements. This is true for infinite+/// sets. When talking about finite sets, we have to add that they are _open_.+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub enum Subset<A> {+    /// The set of all elements.+    All,+    /// A subset of given elements. It is strictly contained in [`Subset::All`].+    Elems(Vec<A>),+}++impl<A> Default for Subset<A> {+    fn default() -> Self {+        Self::All+    }+}++impl<A> LatticeOrd for Subset<A>+where+    A: Clone + PartialEq,+{+    fn lattice_cmp(&self, other: &Self) -> LatticeOrdering {+        match (self, other) {+            (Subset::All, Subset::All) => LatticeOrdering::Equal,+            (_, Subset::All) => LatticeOrdering::Less,+            (Subset::All, _) => LatticeOrdering::Greater,+            (Subset::Elems(a), Subset::Elems(b)) => {+                let mut a_minus_b = a.clone();+                // Subtract `b` from a copy of `a`.+                a_minus_b.retain(|elem| !b.contains(elem));+                let mut b_minus_a = b.clone();+                // Subtract `a` from a copy of `b`.+                b_minus_a.retain(|elem| !a.contains(elem));+                match (a_minus_b.is_empty(), b_minus_a.is_empty()) {+                    (true, true) => LatticeOrdering::Equal,+                    (true, false) => LatticeOrdering::Less,+                    (false, true) => LatticeOrdering::Greater,+                    _ => LatticeOrdering::Incomparable,+                }+            }+        }+    }+}++impl<A> Subset<A>+where+    A: Clone + PartialEq,+{+    /// Constructs the empty subset.+    pub fn empty() -> Self {+        Subset::Elems(Vec::new())+    }++    /// Constructs a subset with one element.+    pub fn elem(a: A) -> Self {+        Subset::Elems(Vec::from([a]))+    }++    /// Computes whether the first subset is greater than or equal to the second subset.+    pub fn ge(&self, other: &Self) -> bool {+        let o = self.lattice_cmp(other);+        o == LatticeOrdering::Greater || o == LatticeOrdering::Equal
        matches!(self.lattice_cmp(other), LatticeOrdering::Greater | LatticeOrdering::Equal)

(Prefer pattern matching where possible; it potentially reduces code bloat / improves perf)

vkomenda

comment created time in 15 days

PR opened PolymathNetwork/Polymesh

Reviewers
mesh-1129, part1: use `TestStorage` for `transaction_payment_test`.
  • Resolves MESH 1129 partially: transaction_payment_test now uses TestStorage. The next and final step is to get rid of its custom ExtBuilder but this PR is I think a good mid-point.
  • Some other drive-by cleanup of unused imports.
+96 -341

0 comment

6 changed files

pr created time in 15 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 11cd459232d5e726e67b29ad75a0e2e303772e33

mesh-1129, part1: use `TestStorage` for `transaction_payment_test`.

view details

push time in 15 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha ea6215d65ca9063dc069b9ae57a28d8b53ce31e4

mesh-1129, part1: use `TestStorage` for `transaction_payment_test`.

view details

push time in 15 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 536f15ada616fd8cb10649ae636a577f88d4ec34

wip; make zero_... work

view details

Mazdak Farrokhzad

commit sha f84b6c1c96f0ddd2298132d556da815f7119ef25

progress

view details

push time in 15 days

create barnchPolymathNetwork/Polymesh

branch : mesh-1129

created branch time in 15 days

PR opened PolymathNetwork/Polymesh

Reviewers
Bump `rust-toolchain` to `nightly-2020-07-26`

Most recent nightly. Most tools are present in it. Recent soundness hole, however unlikely to trigger, is fixed in it.

+1 -1

0 comment

1 changed file

pr created time in 15 days

create barnchPolymathNetwork/Polymesh

branch : pin-2020-07-26

created branch time in 15 days

issue commentbillziss-gh/sshfs-win

net use: error 67 with `\\sshfs.k\`, works with `\\sshfs\` & `sshfs-win svc \sshfs.k\`

I get two events:

Log Name:      Application
Source:        WinFsp
Date:          2020-07-27 08:59:43
Event ID:      1
Task Category: None
Level:         Information
Keywords:      Classic
User:          N/A
Computer:      centrilg55
Description:
launcher-x64: create sshfs.k\centril@centrilnas2!9099 = c0000035
Event Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="WinFsp" />
    <EventID Qualifiers="24576">1</EventID>
    <Level>4</Level>
    <Task>0</Task>
    <Keywords>0x80000000000000</Keywords>
    <TimeCreated SystemTime="2020-07-27T06:59:43.332682400Z" />
    <EventRecordID>10904</EventRecordID>
    <Channel>Application</Channel>
    <Computer>centrilg55</Computer>
    <Security />
  </System>
  <EventData>
    <Data>launcher-x64</Data>
    <Data>create sshfs.k\centril@centrilnas2!9099 = c0000035</Data>
  </EventData>
</Event>

Log Name:      Application
Source:        WinFsp
Date:          2020-07-27 08:59:11
Event ID:      1
Task Category: None
Level:         Information
Keywords:      Classic
User:          N/A
Computer:      centrilg55
Description:
launcher-x64: create sshfs.k\centril@centrilnas2!9099 = c0000035
Event Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="WinFsp" />
    <EventID Qualifiers="24576">1</EventID>
    <Level>4</Level>
    <Task>0</Task>
    <Keywords>0x80000000000000</Keywords>
    <TimeCreated SystemTime="2020-07-27T06:59:11.077534300Z" />
    <EventRecordID>10903</EventRecordID>
    <Channel>Application</Channel>
    <Computer>centrilg55</Computer>
    <Security />
  </System>
  <EventData>
    <Data>launcher-x64</Data>
    <Data>create sshfs.k\centril@centrilnas2!9099 = c0000035</Data>
  </EventData>
</Event>
Centril

comment created time in 15 days

delete branch PolymathNetwork/Polymesh

delete branch : headless-server-notes

delete time in 16 days

issue commentbillziss-gh/sshfs-win

net use: error 67 with `\\sshfs.k\`, works with `\\sshfs\` & `sshfs-win svc \sshfs.k\`

Like so:

Key Name:          HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\WinFsp\Services\sshfs.k
Class Name:        <NO CLASS>
Last Write Time:   2020-07-26 - 15:35
Value 0
  Name:            Executable
  Type:            REG_SZ
  Data:            C:\Program Files\SSHFS-Win\bin\sshfs-win.exe

Value 1
  Name:            CommandLine
  Type:            REG_SZ
  Data:            svc %1 %2 %U

Value 2
  Name:            Security
  Type:            REG_SZ
  Data:            D:P(A;;RPWPLC;;;WD)

Value 3
  Name:            JobControl
  Type:            REG_DWORD
  Data:            0x1

Value 4
  Name:            Credentials
  Type:            REG_DWORD
  Data:            0
Centril

comment created time in 16 days

issue openedbillziss-gh/sshfs-win

net use: error 67 with `\\sshfs.k\`, works with `\\sshfs\` & `sshfs-win svc \sshfs.k\`

I'm trying to move over my server to only allow key authentication and disable passwords; unfortunately, sshfs is my last dependency that doesn't want to play ball.

Everything has been working well for a year with net use Y: \\sshfs\centril@centrilnas2.

Now, using the latest release (sshfs-win@v3.5.20160 & winfsp@v1.8B1), I cannot seem to get sshfs.k to work via net use (or the corresponding Windows UI). When I do net use Y: \\sshfs.k\centril@centrilnas2, I get back:

λ net use Y: \\sshfs.k\centril@centrilnas2
System error 67 has occurred.

The network name cannot be found.

Meanwhile, with sshfs-win svc, I am able to use key authentication:

λ "C:\Program Files\SSHFS-Win\bin\sshfs-win.exe" svc \sshfs.k\centril@centrilnas2 Y:
Warning: Permanently added 'centrilnas2,192.168.0.115' (ECDSA) to the list of known hosts.
Enter passphrase for key '/cygdrive/c/Users/twing/.ssh/id_rsa':
The service sshfs has been started.

(and the drive shows up as a mapped drive).

created time in 16 days

delete branch PolymathNetwork/Polymesh

delete branch : mesh-1119

delete time in 19 days

fork Centril/ethereumbook

Mastering Ethereum, by Andreas M. Antonopoulos, Gavin Wood

https://ethereumbook.info/

fork in 19 days

pull request commentPolymathNetwork/Polymesh

Mesh 1119: Split `ProposalAmended` in two + code deduping

Decided to drop the bool-isomorphic enum to appease CI & also threw in some more refactoring.

Centril

comment created time in 20 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha b17fcf2322acb94207f3546e90078a948de80cf4

pips: switch back to 'bool' & more refactoring

view details

push time in 20 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1119: Split `ProposalAmended` in two + code deduping

 pub struct PipsMetadata<T: Trait> {     pub cool_off_until: T::BlockNumber, } +/// Was the deposit of a vote increased or decreased?+#[derive(Clone, Copy, Eq, PartialEq, Debug, Decode, Encode)]

Fixed. :)

Centril

comment created time in 20 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha cd49c54d76cca7787cbb3d2f9eebeadc0d5e57d6

pips: de-dup some common logic re. vote/cancel/bonding

view details

push time in 20 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha 8fef0e943f2bfba7d549b8bbb2598a0ded8d049c

pips: de-dup some common logic re. vote/cancel/bonding

view details

push time in 20 days

PR opened PolymathNetwork/Polymesh

Reviewers
Mesh 1119: Split `ProposalAmended` in two + code deduping
  • Implements Mesh 1119 by splitting ProposalAmended into ProposalDetailsAmended & ProposalBondAdjusted (+ adding url & description). (First commit)

  • Throws in some code refactoring (Second commit)

+90 -64

0 comment

1 changed file

pr created time in 20 days

create barnchPolymathNetwork/Polymesh

branch : mesh-1119

created branch time in 20 days

pull request commentPolymathNetwork/Polymesh

Mesh 1124/refactor settlements using transaction rollbacks

Also, another thing to consider is the _ => ... branch in fn unchecked_release_locks. One thing you'll often see in rust-lang/rust is to avoid _ => ... arms and instead exhaustively list all the variants. This is done to force us to consider the logic should new variants be added. It makes sense to do that in sensitive code-bases where executing the wrong code may have severe consequences, and Polymesh seems like such a codebase to me.

maxsam4

comment created time in 20 days

delete branch PolymathNetwork/Polymesh

delete branch : pin-2020-07-11

delete time in 20 days

pull request commentPolymathNetwork/Polymesh

Pin `rust-toolchain` to 2020-07-11

@maxsam4 Addressed. :)

Centril

comment created time in 20 days

push eventPolymathNetwork/Polymesh

Mazdak Farrokhzad

commit sha b624280563ee8fa15a2ae2f4d4871e540e9a39c3

scripts/init.sh: nix 'rustup update's

view details

Mazdak Farrokhzad

commit sha 179cc5be72ff9d4e2ac5bf4e86f77c5846b082e1

scripts/init.sh: nix '+nightly' for wasm-gc

view details

push time in 20 days

PR opened PolymathNetwork/Polymesh

Reviewers
Pin `rust-toolchain` to 2020-07-11
  • Adds rust-toolchain with 2020-07-11 as the last known good nightly while https://github.com/rust-lang/rust/issues/74614 is pending a fix.

  • Updates script/init.sh to query rust-toolchain and install the toolchain + wasm32 for it.

+6 -1

0 comment

2 changed files

pr created time in 20 days

create barnchPolymathNetwork/Polymesh

branch : pin-2020-07-11

created branch time in 20 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

+#![cfg_attr(not(feature = "std"), no_std)]++use codec::{Decode, Encode};+use frame_support::{+    decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure,+    IterableStorageDoubleMap,+};+use frame_system::{self as system, ensure_signed};+use polymesh_common_utilities::{identity::Trait as IdentityTrait, CommonTrait, Context};+use polymesh_primitives::{IdentityId, PortfolioId, PortfolioName, PortfolioNumber, Ticker};+use sp_arithmetic::traits::Saturating;+use sp_std::{convert::TryFrom, prelude::Vec};++type Identity<T> = pallet_identity::Module<T>;++/// The ticker and balance of an asset to be moved from one portfolio to another.+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]+pub struct MovePortfolioItem<Balance> {+    /// The asset ticker.+    pub ticker: Ticker,+    /// The asset balance.+    pub amount: Balance,+}++impl<Balance> Default for MovePortfolioItem<Balance>+where+    Balance: Default,+{+    fn default() -> MovePortfolioItem<Balance> {+        MovePortfolioItem {+            ticker: Ticker::default(),+            amount: Balance::default(),+        }+    }+}++pub trait Trait: CommonTrait + IdentityTrait {+    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;+}++decl_storage! {+    trait Store for Module<T: Trait> as Session {+        /// The set of existing portfolios with their names. If a certain pair of a DID and+        /// portfolio number maps to `None` then such a portfolio doesn't exist. Conversely, if a+        /// pair maps to `Some(name)` then such a portfolio exists and is called `name`.+        pub Portfolios get(fn portfolios):+            double_map hasher(blake2_128_concat) IdentityId, hasher(twox_64_concat) PortfolioNumber =>+            Option<PortfolioName>;+        /// Asset balances of portfolios.+        pub PortfolioAssetBalances get(fn portfolio_asset_balances):+            double_map hasher(blake2_128_concat) PortfolioId, hasher(blake2_128_concat) Ticker =>+            T::Balance;+        /// The next portfolio sequence number of an identity.+        pub NextPortfolioNumber get(fn next_portfolio_number):+            map hasher(blake2_128_concat) IdentityId => PortfolioNumber;+    }+}++decl_event! {+    pub enum Event<T> where+        Balance = <T as CommonTrait>::Balance,+    {+        /// The portfolio has been successfully created.+        PortfolioCreated(IdentityId, PortfolioNumber, PortfolioName),+        /// The portfolio has been successfully removed.+        PortfolioDeleted(IdentityId, PortfolioNumber),+        /// A token amount has been moved from one portfolio to another. `None` denotes the default+        /// portfolio of the DID.+        MovedBetweenPortfolios(+            IdentityId,+            Option<PortfolioNumber>,+            Option<PortfolioNumber>,+            Ticker,+            Balance+        ),+        /// The portfolio identified with `num` has been renamed to `name`.+        PortfolioRenamed(IdentityId, PortfolioNumber, PortfolioName),+        /// All non-default portfolio numbers and names of a DID.+        UserPortfolios(IdentityId, Vec<(PortfolioNumber, PortfolioName)>),+    }+}++decl_error! {+    pub enum Error for Module<T: Trait> {+        /// The portfolio doesn't exist.+        PortfolioDoesNotExist,+        /// Insufficient balance for a transaction.+        InsufficientPortfolioBalance,+        /// The source and destination portfolios should be different.+        DestinationIsSamePortfolio,+        /// The portfolio couldn't be renamed because the chosen name is already in use.+        PortfolioNameAlreadyInUse,+    }+}++decl_module! {+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {+        type Error = Error<T>;++        /// The event logger.+        fn deposit_event() = default;++        /// Creates a portfolio.+        #[weight = 200_000]+        pub fn create_portfolio(origin, name: PortfolioName) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            let name_uniq = <Portfolios>::iter_prefix(&did).all(|n| n.1 != name);+            ensure!(name_uniq, Error::<T>::PortfolioNameAlreadyInUse);+            let num = Self::get_next_portfolio_number(&did);+            <Portfolios>::insert(&did, &num, name.clone());+            Self::deposit_event(RawEvent::PortfolioCreated(did, num, name));+            Ok(())+        }++        /// Deletes a user portfolio and moves all its assets to the default portfolio.+        #[weight = 1_000_000]+        pub fn delete_portfolio(origin, num: PortfolioNumber) -> DispatchResult {

Hehe; That's always how I've done it & seen it done in e.g., rust-lang/rust; it works pretty well, but it's only a convention if others agree. 😉

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

+#![cfg_attr(not(feature = "std"), no_std)]++use codec::{Decode, Encode};+use frame_support::{+    decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure,+    IterableStorageDoubleMap,+};+use frame_system::{self as system, ensure_signed};+use polymesh_common_utilities::{identity::Trait as IdentityTrait, CommonTrait, Context};+use polymesh_primitives::{IdentityId, PortfolioId, PortfolioName, PortfolioNumber, Ticker};+use sp_arithmetic::traits::Saturating;+use sp_std::{convert::TryFrom, prelude::Vec};++type Identity<T> = pallet_identity::Module<T>;++/// The ticker and balance of an asset to be moved from one portfolio to another.+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]+pub struct MovePortfolioItem<Balance> {+    /// The asset ticker.+    pub ticker: Ticker,+    /// The asset balance.+    pub amount: Balance,+}++impl<Balance> Default for MovePortfolioItem<Balance>+where+    Balance: Default,+{+    fn default() -> MovePortfolioItem<Balance> {+        MovePortfolioItem {+            ticker: Ticker::default(),+            amount: Balance::default(),+        }+    }+}++pub trait Trait: CommonTrait + IdentityTrait {+    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;+}++decl_storage! {+    trait Store for Module<T: Trait> as Session {+        /// The set of existing portfolios with their names. If a certain pair of a DID and+        /// portfolio number maps to `None` then such a portfolio doesn't exist. Conversely, if a+        /// pair maps to `Some(name)` then such a portfolio exists and is called `name`.+        pub Portfolios get(fn portfolios):+            double_map hasher(blake2_128_concat) IdentityId, hasher(twox_64_concat) PortfolioNumber =>+            Option<PortfolioName>;+        /// Asset balances of portfolios.+        pub PortfolioAssetBalances get(fn portfolio_asset_balances):+            double_map hasher(blake2_128_concat) PortfolioId, hasher(blake2_128_concat) Ticker =>+            T::Balance;+        /// The next portfolio sequence number of an identity.+        pub NextPortfolioNumber get(fn next_portfolio_number):+            map hasher(blake2_128_concat) IdentityId => PortfolioNumber;+    }+}++decl_event! {+    pub enum Event<T> where+        Balance = <T as CommonTrait>::Balance,+    {+        /// The portfolio has been successfully created.+        PortfolioCreated(IdentityId, PortfolioNumber, PortfolioName),+        /// The portfolio has been successfully removed.+        PortfolioDeleted(IdentityId, PortfolioNumber),+        /// A token amount has been moved from one portfolio to another. `None` denotes the default+        /// portfolio of the DID.+        MovedBetweenPortfolios(+            IdentityId,+            Option<PortfolioNumber>,+            Option<PortfolioNumber>,+            Ticker,+            Balance+        ),+        /// The portfolio identified with `num` has been renamed to `name`.+        PortfolioRenamed(IdentityId, PortfolioNumber, PortfolioName),+        /// All non-default portfolio numbers and names of a DID.+        UserPortfolios(IdentityId, Vec<(PortfolioNumber, PortfolioName)>),+    }+}++decl_error! {+    pub enum Error for Module<T: Trait> {+        /// The portfolio doesn't exist.+        PortfolioDoesNotExist,+        /// Insufficient balance for a transaction.+        InsufficientPortfolioBalance,+        /// The source and destination portfolios should be different.+        DestinationIsSamePortfolio,+        /// The portfolio couldn't be renamed because the chosen name is already in use.+        PortfolioNameAlreadyInUse,+    }+}++decl_module! {+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {+        type Error = Error<T>;++        /// The event logger.+        fn deposit_event() = default;++        /// Creates a portfolio.+        #[weight = 200_000]+        pub fn create_portfolio(origin, name: PortfolioName) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            let name_uniq = <Portfolios>::iter_prefix(&did).all(|n| n.1 != name);+            ensure!(name_uniq, Error::<T>::PortfolioNameAlreadyInUse);+            let num = Self::get_next_portfolio_number(&did);+            <Portfolios>::insert(&did, &num, name.clone());+            Self::deposit_event(RawEvent::PortfolioCreated(did, num, name));+            Ok(())+        }++        /// Deletes a user portfolio and moves all its assets to the default portfolio.+        #[weight = 1_000_000]+        pub fn delete_portfolio(origin, num: PortfolioNumber) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            ensure!(Self::portfolios(&did, &num).is_some(), Error::<T>::PortfolioDoesNotExist);+            let portfolio_id = PortfolioId::user_portfolio(did, num);+            let def_portfolio_id = PortfolioId::default_portfolio(did);+            for (ticker, balance) in <PortfolioAssetBalances<T>>::iter_prefix(&portfolio_id) {+                <PortfolioAssetBalances<T>>::mutate(&def_portfolio_id, ticker, |v| {+                    *v = v.saturating_add(balance)+                });+                Self::deposit_event(RawEvent::MovedBetweenPortfolios(+                    did,+                    Some(num),+                    None,+                    ticker,+                    balance,+                ));+            }+            <PortfolioAssetBalances<T>>::remove_prefix(&portfolio_id);+            <Portfolios>::remove(&did, &num);+            Self::deposit_event(RawEvent::PortfolioDeleted(did, num));+            Ok(())+        }++        /// Moves a token amount from one portfolio of an identity to another portfolio of the same+        /// identity.+        #[weight = 250_000 + 250_000 * u64::try_from(items.len()).unwrap_or_default()]+        pub fn move_portfolio(+            origin,+            from_num: Option<PortfolioNumber>,+            to_num: Option<PortfolioNumber>,+            items: Vec<MovePortfolioItem<<T as CommonTrait>::Balance>>,+        ) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            ensure!(from_num != to_num, Error::<T>::DestinationIsSamePortfolio);+            if let Some(from_num) = from_num {+                ensure!(Self::portfolios(&did, from_num).is_some(), Error::<T>::PortfolioDoesNotExist);+            }+            if let Some(to_num) = to_num {+                ensure!(Self::portfolios(&did, to_num).is_some(), Error::<T>::PortfolioDoesNotExist);+            }+            let from_portfolio_id = from_num+                .and_then(|num| Some(PortfolioId::user_portfolio(did, num)))+                .unwrap_or_else(|| PortfolioId::default_portfolio(did));+            let to_portfolio_id = to_num+                .and_then(|num| Some(PortfolioId::user_portfolio(did, num)))+                .unwrap_or_else(|| PortfolioId::default_portfolio(did));+            for item in items {+                let from_balance = Self::portfolio_asset_balances(&from_portfolio_id, &item.ticker);+                ensure!(from_balance >= item.amount, Error::<T>::InsufficientPortfolioBalance);+                <PortfolioAssetBalances<T>>::insert(+                    &from_portfolio_id,+                    &item.ticker,+                    from_balance - item.amount+                );+                let to_balance = Self::portfolio_asset_balances(&to_portfolio_id, &item.ticker);+                <PortfolioAssetBalances<T>>::insert(+                    &to_portfolio_id,+                    &item.ticker,+                    to_balance.saturating_add(item.amount)+                );+                Self::deposit_event(RawEvent::MovedBetweenPortfolios(+                    did,+                    from_num,+                    to_num,+                    item.ticker,+                    item.amount+                ));+            }+            Ok(())+        }++        /// Renames a non-default portfolio.+        #[weight = 500_000]+        pub fn rename_portfolio(

Personally, I would deduplicate the let name_uniq and the subsequent ensure!; that logic is at least common.

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 decl_module! {             let mut issued_in_this_round = Self::issued_in_funding_round(&ticker_round);              // A round of per-investor checks-            for i in 0..issue_asset_items.len() {+            for IssueAssetItem { investor_did, value } in &issue_asset_items {                 ensure!(-                    Self::check_granularity(&ticker, issue_asset_items[i].value),+                    Self::check_granularity(&ticker, *value),                     Error::<T>::InvalidGranularity                 );                 let updated_total_supply = token                     .total_supply-                    .checked_add(&issue_asset_items[i].value)+                    .checked_add(value)                     .ok_or(Error::<T>::TotalSupplyOverflow)?;                 ensure!(updated_total_supply <= MAX_SUPPLY.into(), Error::<T>::TotalSupplyAboveLimit); -                current_balances.push(Self::balance(&ticker, &issue_asset_items[i].investor_did));-                updated_balances.push(current_balances[i]-                    .checked_add(&issue_asset_items[i].value)-                    .ok_or(Error::<T>::BalanceOverflow)?);+                let bals = Self::balance(&ticker, *investor_did);+                current_total_balances.push(bals.total);+                // No check since the total balance is always less than or equal to the total

Slight preference for the former I guess, but very slight. :)

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

+#![cfg_attr(not(feature = "std"), no_std)]++use codec::{Decode, Encode};+use frame_support::{+    decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure,+    IterableStorageDoubleMap,+};+use frame_system::{self as system, ensure_signed};+use polymesh_common_utilities::{identity::Trait as IdentityTrait, CommonTrait, Context};+use polymesh_primitives::{IdentityId, PortfolioId, PortfolioName, PortfolioNumber, Ticker};+use sp_arithmetic::traits::Saturating;+use sp_std::{convert::TryFrom, prelude::Vec};++type Identity<T> = pallet_identity::Module<T>;++/// The ticker and balance of an asset to be moved from one portfolio to another.+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]+pub struct MovePortfolioItem<Balance> {+    /// The asset ticker.+    pub ticker: Ticker,+    /// The asset balance.+    pub amount: Balance,+}++impl<Balance> Default for MovePortfolioItem<Balance>+where+    Balance: Default,+{+    fn default() -> MovePortfolioItem<Balance> {+        MovePortfolioItem {+            ticker: Ticker::default(),+            amount: Balance::default(),+        }+    }+}++pub trait Trait: CommonTrait + IdentityTrait {+    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;+}++decl_storage! {+    trait Store for Module<T: Trait> as Session {+        /// The set of existing portfolios with their names. If a certain pair of a DID and+        /// portfolio number maps to `None` then such a portfolio doesn't exist. Conversely, if a+        /// pair maps to `Some(name)` then such a portfolio exists and is called `name`.+        pub Portfolios get(fn portfolios):+            double_map hasher(blake2_128_concat) IdentityId, hasher(twox_64_concat) PortfolioNumber =>+            Option<PortfolioName>;+        /// Asset balances of portfolios.+        pub PortfolioAssetBalances get(fn portfolio_asset_balances):+            double_map hasher(blake2_128_concat) PortfolioId, hasher(blake2_128_concat) Ticker =>+            T::Balance;+        /// The next portfolio sequence number of an identity.+        pub NextPortfolioNumber get(fn next_portfolio_number):+            map hasher(blake2_128_concat) IdentityId => PortfolioNumber;+    }+}++decl_event! {+    pub enum Event<T> where+        Balance = <T as CommonTrait>::Balance,+    {+        /// The portfolio has been successfully created.+        PortfolioCreated(IdentityId, PortfolioNumber, PortfolioName),+        /// The portfolio has been successfully removed.+        PortfolioDeleted(IdentityId, PortfolioNumber),+        /// A token amount has been moved from one portfolio to another. `None` denotes the default+        /// portfolio of the DID.+        MovedBetweenPortfolios(+            IdentityId,+            Option<PortfolioNumber>,+            Option<PortfolioNumber>,+            Ticker,+            Balance+        ),+        /// The portfolio identified with `num` has been renamed to `name`.+        PortfolioRenamed(IdentityId, PortfolioNumber, PortfolioName),+        /// All non-default portfolio numbers and names of a DID.+        UserPortfolios(IdentityId, Vec<(PortfolioNumber, PortfolioName)>),+    }+}++decl_error! {+    pub enum Error for Module<T: Trait> {+        /// The portfolio doesn't exist.+        PortfolioDoesNotExist,+        /// Insufficient balance for a transaction.+        InsufficientPortfolioBalance,+        /// The source and destination portfolios should be different.+        DestinationIsSamePortfolio,+        /// The portfolio couldn't be renamed because the chosen name is already in use.+        PortfolioNameAlreadyInUse,+    }+}++decl_module! {+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {+        type Error = Error<T>;++        /// The event logger.+        fn deposit_event() = default;++        /// Creates a portfolio.+        #[weight = 200_000]+        pub fn create_portfolio(origin, name: PortfolioName) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            let name_uniq = <Portfolios>::iter_prefix(&did).all(|n| n.1 != name);+            ensure!(name_uniq, Error::<T>::PortfolioNameAlreadyInUse);+            let num = Self::get_next_portfolio_number(&did);+            <Portfolios>::insert(&did, &num, name.clone());+            Self::deposit_event(RawEvent::PortfolioCreated(did, num, name));+            Ok(())+        }++        /// Deletes a user portfolio and moves all its assets to the default portfolio.+        #[weight = 1_000_000]+        pub fn delete_portfolio(origin, num: PortfolioNumber) -> DispatchResult {

I see; I use vertical spaces as a grouping mechanism and to reduce clutter:

// Foo section of code.
foo_step1();
foo_step2();

// Bar section of code.
bar_step1();
bar_step2();

This makes it clearer that the comment above is a section comment, as opposed to a comment regarding just *_step1();. You could write out this fact in the comment, but that seems to me overly verbose. Anyways, to each their own, and if you prefer it this way in your code then go for it. :)

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

+#![cfg_attr(not(feature = "std"), no_std)]++use codec::{Decode, Encode};+use frame_support::{+    decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure,+    IterableStorageDoubleMap,+};+use frame_system::{self as system, ensure_signed};+use polymesh_common_utilities::{identity::Trait as IdentityTrait, CommonTrait, Context};+use polymesh_primitives::{IdentityId, PortfolioId, PortfolioName, PortfolioNumber, Ticker};+use sp_arithmetic::traits::Saturating;+use sp_std::{convert::TryFrom, prelude::Vec};++type Identity<T> = pallet_identity::Module<T>;++/// The ticker and balance of an asset to be moved from one portfolio to another.+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]+pub struct MovePortfolioItem<Balance> {+    /// The asset ticker.+    pub ticker: Ticker,+    /// The asset balance.+    pub amount: Balance,+}++impl<Balance> Default for MovePortfolioItem<Balance>+where+    Balance: Default,+{+    fn default() -> MovePortfolioItem<Balance> {+        MovePortfolioItem {+            ticker: Ticker::default(),+            amount: Balance::default(),+        }+    }+}++pub trait Trait: CommonTrait + IdentityTrait {+    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;+}++decl_storage! {+    trait Store for Module<T: Trait> as Session {+        /// The set of existing portfolios with their names. If a certain pair of a DID and+        /// portfolio number maps to `None` then such a portfolio doesn't exist. Conversely, if a+        /// pair maps to `Some(name)` then such a portfolio exists and is called `name`.+        pub Portfolios get(fn portfolios):+            double_map hasher(blake2_128_concat) IdentityId, hasher(twox_64_concat) PortfolioNumber =>+            Option<PortfolioName>;+        /// Asset balances of portfolios.+        pub PortfolioAssetBalances get(fn portfolio_asset_balances):+            double_map hasher(blake2_128_concat) PortfolioId, hasher(blake2_128_concat) Ticker =>+            T::Balance;+        /// The next portfolio sequence number of an identity.+        pub NextPortfolioNumber get(fn next_portfolio_number):+            map hasher(blake2_128_concat) IdentityId => PortfolioNumber;+    }+}++decl_event! {+    pub enum Event<T> where+        Balance = <T as CommonTrait>::Balance,+    {+        /// The portfolio has been successfully created.+        PortfolioCreated(IdentityId, PortfolioNumber, PortfolioName),+        /// The portfolio has been successfully removed.+        PortfolioDeleted(IdentityId, PortfolioNumber),+        /// A token amount has been moved from one portfolio to another. `None` denotes the default+        /// portfolio of the DID.+        MovedBetweenPortfolios(+            IdentityId,+            Option<PortfolioNumber>,+            Option<PortfolioNumber>,+            Ticker,+            Balance+        ),+        /// The portfolio identified with `num` has been renamed to `name`.+        PortfolioRenamed(IdentityId, PortfolioNumber, PortfolioName),+        /// All non-default portfolio numbers and names of a DID.+        UserPortfolios(IdentityId, Vec<(PortfolioNumber, PortfolioName)>),+    }+}++decl_error! {+    pub enum Error for Module<T: Trait> {+        /// The portfolio doesn't exist.+        PortfolioDoesNotExist,+        /// Insufficient balance for a transaction.+        InsufficientPortfolioBalance,+        /// The source and destination portfolios should be different.+        DestinationIsSamePortfolio,+        /// The portfolio couldn't be renamed because the chosen name is already in use.+        PortfolioNameAlreadyInUse,+    }+}++decl_module! {+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {+        type Error = Error<T>;++        /// The event logger.+        fn deposit_event() = default;++        /// Creates a portfolio.+        #[weight = 200_000]+        pub fn create_portfolio(origin, name: PortfolioName) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            let name_uniq = <Portfolios>::iter_prefix(&did).all(|n| n.1 != name);+            ensure!(name_uniq, Error::<T>::PortfolioNameAlreadyInUse);+            let num = Self::get_next_portfolio_number(&did);+            <Portfolios>::insert(&did, &num, name.clone());+            Self::deposit_event(RawEvent::PortfolioCreated(did, num, name));+            Ok(())+        }++        /// Deletes a user portfolio and moves all its assets to the default portfolio.+        #[weight = 1_000_000]+        pub fn delete_portfolio(origin, num: PortfolioNumber) -> DispatchResult {

Ellipsed comments? (The reason I suggested vertical spaces is that otherwise the code becomes overly dense and there's no "breathing" if you catch my drift.)

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

+#![cfg_attr(not(feature = "std"), no_std)]++use codec::{Decode, Encode};+use frame_support::{+    decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure,+    IterableStorageDoubleMap,+};+use frame_system::{self as system, ensure_signed};+use polymesh_common_utilities::{identity::Trait as IdentityTrait, CommonTrait, Context};+use polymesh_primitives::{IdentityId, PortfolioId, PortfolioName, PortfolioNumber, Ticker};+use sp_arithmetic::traits::Saturating;+use sp_std::{convert::TryFrom, prelude::Vec};++type Identity<T> = pallet_identity::Module<T>;++/// The ticker and balance of an asset to be moved from one portfolio to another.+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]+pub struct MovePortfolioItem<Balance> {+    /// The asset ticker.+    pub ticker: Ticker,+    /// The asset balance.+    pub amount: Balance,+}++impl<Balance> Default for MovePortfolioItem<Balance>+where+    Balance: Default,+{+    fn default() -> MovePortfolioItem<Balance> {+        MovePortfolioItem {+            ticker: Ticker::default(),+            amount: Balance::default(),+        }+    }+}++pub trait Trait: CommonTrait + IdentityTrait {+    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;+}++decl_storage! {+    trait Store for Module<T: Trait> as Session {+        /// The set of existing portfolios with their names. If a certain pair of a DID and+        /// portfolio number maps to `None` then such a portfolio doesn't exist. Conversely, if a+        /// pair maps to `Some(name)` then such a portfolio exists and is called `name`.+        pub Portfolios get(fn portfolios):+            double_map hasher(blake2_128_concat) IdentityId, hasher(twox_64_concat) PortfolioNumber =>+            Option<PortfolioName>;+        /// Asset balances of portfolios.+        pub PortfolioAssetBalances get(fn portfolio_asset_balances):+            double_map hasher(blake2_128_concat) PortfolioId, hasher(blake2_128_concat) Ticker =>+            T::Balance;+        /// The next portfolio sequence number of an identity.+        pub NextPortfolioNumber get(fn next_portfolio_number):+            map hasher(blake2_128_concat) IdentityId => PortfolioNumber;+    }+}++decl_event! {+    pub enum Event<T> where+        Balance = <T as CommonTrait>::Balance,+    {+        /// The portfolio has been successfully created.+        PortfolioCreated(IdentityId, PortfolioNumber, PortfolioName),+        /// The portfolio has been successfully removed.+        PortfolioDeleted(IdentityId, PortfolioNumber),+        /// A token amount has been moved from one portfolio to another. `None` denotes the default

Ah; if you're using automated tooling to format these then they'll indeed be broken quickly.

This isn't at all important to me, but I figured I'd mention it in case you'd hadn't considered & might adopt it as a habit (as I eventually did upon a recommendation by someone else). My approach is to use semantic linefeeds in new code or if I'm substantially changing the text of a doc comment, but it wouldn't occur to me to periodically maintain doc comments like that. Basically, if you apply semantic linefeeds, then great, if not, then no big deal!

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 decl_module! {             let mut issued_in_this_round = Self::issued_in_funding_round(&ticker_round);              // A round of per-investor checks-            for i in 0..issue_asset_items.len() {+            for IssueAssetItem { investor_did, value } in &issue_asset_items {                 ensure!(-                    Self::check_granularity(&ticker, issue_asset_items[i].value),+                    Self::check_granularity(&ticker, *value),                     Error::<T>::InvalidGranularity                 );                 let updated_total_supply = token                     .total_supply-                    .checked_add(&issue_asset_items[i].value)+                    .checked_add(value)                     .ok_or(Error::<T>::TotalSupplyOverflow)?;                 ensure!(updated_total_supply <= MAX_SUPPLY.into(), Error::<T>::TotalSupplyAboveLimit); -                current_balances.push(Self::balance(&ticker, &issue_asset_items[i].investor_did));-                updated_balances.push(current_balances[i]-                    .checked_add(&issue_asset_items[i].value)-                    .ok_or(Error::<T>::BalanceOverflow)?);+                let bals = Self::balance(&ticker, *investor_did);+                current_total_balances.push(bals.total);+                // No check since the total balance is always less than or equal to the total

No opinion re. "to". For consistency, if you want to change to <= then ideally >= would be used also.

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

+#![cfg_attr(not(feature = "std"), no_std)]++use codec::{Decode, Encode};+use frame_support::{+    decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure,+    IterableStorageDoubleMap,+};+use frame_system::{self as system, ensure_signed};+use polymesh_common_utilities::{identity::Trait as IdentityTrait, CommonTrait, Context};+use polymesh_primitives::{IdentityId, PortfolioId, PortfolioName, PortfolioNumber, Ticker};+use sp_arithmetic::traits::Saturating;+use sp_std::{convert::TryFrom, prelude::Vec};++type Identity<T> = pallet_identity::Module<T>;++/// The ticker and balance of an asset to be moved from one portfolio to another.+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]+pub struct MovePortfolioItem<Balance> {+    /// The asset ticker.+    pub ticker: Ticker,+    /// The asset balance.+    pub amount: Balance,+}++impl<Balance> Default for MovePortfolioItem<Balance>+where+    Balance: Default,+{+    fn default() -> MovePortfolioItem<Balance> {+        MovePortfolioItem {+            ticker: Ticker::default(),+            amount: Balance::default(),+        }+    }+}++pub trait Trait: CommonTrait + IdentityTrait {+    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;+}++decl_storage! {+    trait Store for Module<T: Trait> as Session {+        /// The set of existing portfolios with their names. If a certain pair of a DID and+        /// portfolio number maps to `None` then such a portfolio doesn't exist. Conversely, if a+        /// pair maps to `Some(name)` then such a portfolio exists and is called `name`.+        pub Portfolios get(fn portfolios):+            double_map hasher(blake2_128_concat) IdentityId, hasher(twox_64_concat) PortfolioNumber =>+            Option<PortfolioName>;+        /// Asset balances of portfolios.+        pub PortfolioAssetBalances get(fn portfolio_asset_balances):+            double_map hasher(blake2_128_concat) PortfolioId, hasher(blake2_128_concat) Ticker =>+            T::Balance;+        /// The next portfolio sequence number of an identity.+        pub NextPortfolioNumber get(fn next_portfolio_number):+            map hasher(blake2_128_concat) IdentityId => PortfolioNumber;+    }+}++decl_event! {+    pub enum Event<T> where+        Balance = <T as CommonTrait>::Balance,+    {+        /// The portfolio has been successfully created.+        PortfolioCreated(IdentityId, PortfolioNumber, PortfolioName),+        /// The portfolio has been successfully removed.+        PortfolioDeleted(IdentityId, PortfolioNumber),+        /// A token amount has been moved from one portfolio to another. `None` denotes the default+        /// portfolio of the DID.+        MovedBetweenPortfolios(+            IdentityId,+            Option<PortfolioNumber>,+            Option<PortfolioNumber>,+            Ticker,+            Balance+        ),+        /// The portfolio identified with `num` has been renamed to `name`.+        PortfolioRenamed(IdentityId, PortfolioNumber, PortfolioName),+        /// All non-default portfolio numbers and names of a DID.+        UserPortfolios(IdentityId, Vec<(PortfolioNumber, PortfolioName)>),+    }+}++decl_error! {+    pub enum Error for Module<T: Trait> {+        /// The portfolio doesn't exist.+        PortfolioDoesNotExist,+        /// Insufficient balance for a transaction.+        InsufficientPortfolioBalance,+        /// The source and destination portfolios should be different.+        DestinationIsSamePortfolio,+        /// The portfolio couldn't be renamed because the chosen name is already in use.+        PortfolioNameAlreadyInUse,+    }+}++decl_module! {+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {+        type Error = Error<T>;++        /// The event logger.+        fn deposit_event() = default;++        /// Creates a portfolio.+        #[weight = 200_000]+        pub fn create_portfolio(origin, name: PortfolioName) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            let name_uniq = <Portfolios>::iter_prefix(&did).all(|n| n.1 != name);+            ensure!(name_uniq, Error::<T>::PortfolioNameAlreadyInUse);+            let num = Self::get_next_portfolio_number(&did);+            <Portfolios>::insert(&did, &num, name.clone());+            Self::deposit_event(RawEvent::PortfolioCreated(did, num, name));+            Ok(())+        }++        /// Deletes a user portfolio and moves all its assets to the default portfolio.+        #[weight = 1_000_000]+        pub fn delete_portfolio(origin, num: PortfolioNumber) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            ensure!(Self::portfolios(&did, &num).is_some(), Error::<T>::PortfolioDoesNotExist);+            let portfolio_id = PortfolioId::user_portfolio(did, num);+            let def_portfolio_id = PortfolioId::default_portfolio(did);+            for (ticker, balance) in <PortfolioAssetBalances<T>>::iter_prefix(&portfolio_id) {+                <PortfolioAssetBalances<T>>::mutate(&def_portfolio_id, ticker, |v| {+                    *v = v.saturating_add(balance)+                });+                Self::deposit_event(RawEvent::MovedBetweenPortfolios(+                    did,+                    Some(num),+                    None,+                    ticker,+                    balance,+                ));+            }+            <PortfolioAssetBalances<T>>::remove_prefix(&portfolio_id);+            <Portfolios>::remove(&did, &num);+            Self::deposit_event(RawEvent::PortfolioDeleted(did, num));+            Ok(())+        }++        /// Moves a token amount from one portfolio of an identity to another portfolio of the same+        /// identity.+        #[weight = 250_000 + 250_000 * u64::try_from(items.len()).unwrap_or_default()]+        pub fn move_portfolio(+            origin,+            from_num: Option<PortfolioNumber>,+            to_num: Option<PortfolioNumber>,+            items: Vec<MovePortfolioItem<<T as CommonTrait>::Balance>>,+        ) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            ensure!(from_num != to_num, Error::<T>::DestinationIsSamePortfolio);+            if let Some(from_num) = from_num {+                ensure!(Self::portfolios(&did, from_num).is_some(), Error::<T>::PortfolioDoesNotExist);+            }+            if let Some(to_num) = to_num {+                ensure!(Self::portfolios(&did, to_num).is_some(), Error::<T>::PortfolioDoesNotExist);+            }+            let from_portfolio_id = from_num+                .and_then(|num| Some(PortfolioId::user_portfolio(did, num)))+                .unwrap_or_else(|| PortfolioId::default_portfolio(did));+            let to_portfolio_id = to_num+                .and_then(|num| Some(PortfolioId::user_portfolio(did, num)))+                .unwrap_or_else(|| PortfolioId::default_portfolio(did));+            for item in items {+                let from_balance = Self::portfolio_asset_balances(&from_portfolio_id, &item.ticker);+                ensure!(from_balance >= item.amount, Error::<T>::InsufficientPortfolioBalance);+                <PortfolioAssetBalances<T>>::insert(+                    &from_portfolio_id,+                    &item.ticker,+                    from_balance - item.amount+                );+                let to_balance = Self::portfolio_asset_balances(&to_portfolio_id, &item.ticker);+                <PortfolioAssetBalances<T>>::insert(+                    &to_portfolio_id,+                    &item.ticker,+                    to_balance.saturating_add(item.amount)+                );+                Self::deposit_event(RawEvent::MovedBetweenPortfolios(+                    did,+                    from_num,+                    to_num,+                    item.ticker,+                    item.amount+                ));+            }+            Ok(())+        }++        /// Renames a non-default portfolio.+        #[weight = 500_000]+        pub fn rename_portfolio(+            origin,+            num: PortfolioNumber,+            to_name: PortfolioName,+        ) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            ensure!(Self::portfolios(&did, &num).is_some(), Error::<T>::PortfolioDoesNotExist);+            let name_uniq = <Portfolios>::iter_prefix(&did).all(|n| n.1 != to_name);+            ensure!(name_uniq, Error::<T>::PortfolioNameAlreadyInUse);+            <Portfolios>::mutate(&did, &num, |p| *p = Some(to_name.clone()));+            Self::deposit_event(RawEvent::PortfolioRenamed(+                did,+                num,+                to_name,+            ));+            Ok(())+        }+    }+}++impl<T: Trait> Module<T> {+    /// Returns the ticker balance of the identity's default portfolio.+    pub fn default_portfolio_balance(+        did: IdentityId,+        ticker: &Ticker,+    ) -> <T as CommonTrait>::Balance {+        Self::portfolio_asset_balances(PortfolioId::default_portfolio(did), ticker)+    }++    /// Returns the ticker balance of an identity's user portfolio.+    pub fn user_portfolio_balance(+        did: IdentityId,+        num: PortfolioNumber,+        ticker: &Ticker,+    ) -> <T as CommonTrait>::Balance {+        Self::portfolio_asset_balances(PortfolioId::user_portfolio(did, num), ticker)+    }++    /// Sets the ticker balance of the identity's default portfolio to the given value.+    pub fn set_default_portfolio_balance(+        did: IdentityId,+        ticker: &Ticker,+        balance: <T as CommonTrait>::Balance,+    ) {+        <PortfolioAssetBalances<T>>::insert(PortfolioId::default_portfolio(did), ticker, balance);+    }++    /// Returns the next portfolio number of a given identity and increments the stored number.+    fn get_next_portfolio_number(did: &IdentityId) -> PortfolioNumber {+        let num = Self::next_portfolio_number(did);+        <NextPortfolioNumber>::insert(did, num + 1);+        num+    }++    /// An RPC function that lists all user-defined portfolio number-name pairs.+    pub fn rpc_get_portfolios(+        did: IdentityId,+    ) -> core::result::Result<Vec<(PortfolioNumber, PortfolioName)>, &'static str> {

I see; fwiw it's also used below, but an absolute path to a Result struck me as unusual. :)

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 decl_module! {             let mut issued_in_this_round = Self::issued_in_funding_round(&ticker_round);              // A round of per-investor checks-            for i in 0..issue_asset_items.len() {+            for IssueAssetItem { investor_did, value } in &issue_asset_items {                 ensure!(-                    Self::check_granularity(&ticker, issue_asset_items[i].value),+                    Self::check_granularity(&ticker, *value),                     Error::<T>::InvalidGranularity                 );                 let updated_total_supply = token                     .total_supply-                    .checked_add(&issue_asset_items[i].value)+                    .checked_add(value)                     .ok_or(Error::<T>::TotalSupplyOverflow)?;                 ensure!(updated_total_supply <= MAX_SUPPLY.into(), Error::<T>::TotalSupplyAboveLimit); -                current_balances.push(Self::balance(&ticker, &issue_asset_items[i].investor_did));-                updated_balances.push(current_balances[i]-                    .checked_add(&issue_asset_items[i].value)-                    .ok_or(Error::<T>::BalanceOverflow)?);+                let bals = Self::balance(&ticker, *investor_did);+                current_total_balances.push(bals.total);+                // No check since the total balance is always less than or equal to the total

A nit, but using <= reads a bit quicker imo and requires less thinking for me than "less than or equal", but ymmv. :)

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 impl Printable for IdentityId {     } } +/// A wrapper for a portfolio name. It is used for non-default (aka "user") portfolios only since+/// default ones are nameless.+#[derive(+    Decode, Encode, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, VecU8StrongTyped,+)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub struct PortfolioName(pub Vec<u8>);++/// The unique ID of a non-default portfolio.+pub type PortfolioNumber = u64;++#[derive(Decode, Encode, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub enum PortfolioKind {+    /// The default portfolio of a DID.+    Default,+    /// A user-defined portfolio of a DID.+    User(PortfolioNumber),+}++impl Default for PortfolioKind {+    fn default() -> Self {+        PortfolioKind::Default+    }+}++/// The ID of a portfolio.+#[derive(Decode, Encode, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub struct PortfolioId {+    /// The DID of the portfolio.+    did: IdentityId,+    /// The kind of the portfolio: either default or user.+    kind: PortfolioKind,+}++impl Default for PortfolioId {+    fn default() -> Self {+        PortfolioId {+            did: IdentityId::default(),+            ..Default::default()+        }+    }+}++impl Printable for PortfolioId {+    fn print(&self) {+        self.did.print();+        sp_io::misc::print_utf8(b"/");+        match self.kind {+            PortfolioKind::Default => {+                sp_io::misc::print_utf8(b"default");+            }+            PortfolioKind::User(num) => {+                sp_io::misc::print_hex(&num.to_be_bytes());+            }+        }+    }+}++impl PortfolioId {+    /// Returns the default portfolio of `did`.+    pub fn default_portfolio(did: IdentityId) -> PortfolioId {+        PortfolioId {+            did,+            kind: PortfolioKind::Default,+        }+    }

Imo best practice as it reduces the number of places you might need to make future changes & makes it obvious that you're referring to the current type, but it's just a nit. :)

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 impl Printable for IdentityId {     } } +/// A wrapper for a portfolio name. It is used for non-default (aka "user") portfolios only since+/// default ones are nameless.+#[derive(+    Decode, Encode, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, VecU8StrongTyped,+)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub struct PortfolioName(pub Vec<u8>);++/// The unique ID of a non-default portfolio.+pub type PortfolioNumber = u64;++#[derive(Decode, Encode, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub enum PortfolioKind {+    /// The default portfolio of a DID.+    Default,+    /// A user-defined portfolio of a DID.+    User(PortfolioNumber),+}++impl Default for PortfolioKind {+    fn default() -> Self {+        PortfolioKind::Default+    }+}++/// The ID of a portfolio.+#[derive(Decode, Encode, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Decode, Encode, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

+#![cfg_attr(not(feature = "std"), no_std)]++use codec::{Decode, Encode};+use frame_support::{+    decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure,+    IterableStorageDoubleMap,+};+use frame_system::{self as system, ensure_signed};+use polymesh_common_utilities::{identity::Trait as IdentityTrait, CommonTrait, Context};+use polymesh_primitives::{IdentityId, PortfolioId, PortfolioName, PortfolioNumber, Ticker};+use sp_arithmetic::traits::Saturating;+use sp_std::{convert::TryFrom, prelude::Vec};++type Identity<T> = pallet_identity::Module<T>;++/// The ticker and balance of an asset to be moved from one portfolio to another.+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]+pub struct MovePortfolioItem<Balance> {+    /// The asset ticker.+    pub ticker: Ticker,+    /// The asset balance.+    pub amount: Balance,+}++impl<Balance> Default for MovePortfolioItem<Balance>+where+    Balance: Default,+{+    fn default() -> MovePortfolioItem<Balance> {+        MovePortfolioItem {+            ticker: Ticker::default(),+            amount: Balance::default(),+        }+    }+}++pub trait Trait: CommonTrait + IdentityTrait {+    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;+}++decl_storage! {+    trait Store for Module<T: Trait> as Session {+        /// The set of existing portfolios with their names. If a certain pair of a DID and+        /// portfolio number maps to `None` then such a portfolio doesn't exist. Conversely, if a+        /// pair maps to `Some(name)` then such a portfolio exists and is called `name`.+        pub Portfolios get(fn portfolios):+            double_map hasher(blake2_128_concat) IdentityId, hasher(twox_64_concat) PortfolioNumber =>+            Option<PortfolioName>;+        /// Asset balances of portfolios.+        pub PortfolioAssetBalances get(fn portfolio_asset_balances):+            double_map hasher(blake2_128_concat) PortfolioId, hasher(blake2_128_concat) Ticker =>+            T::Balance;+        /// The next portfolio sequence number of an identity.+        pub NextPortfolioNumber get(fn next_portfolio_number):+            map hasher(blake2_128_concat) IdentityId => PortfolioNumber;+    }+}++decl_event! {+    pub enum Event<T> where+        Balance = <T as CommonTrait>::Balance,+    {+        /// The portfolio has been successfully created.+        PortfolioCreated(IdentityId, PortfolioNumber, PortfolioName),+        /// The portfolio has been successfully removed.+        PortfolioDeleted(IdentityId, PortfolioNumber),+        /// A token amount has been moved from one portfolio to another. `None` denotes the default+        /// portfolio of the DID.+        MovedBetweenPortfolios(+            IdentityId,+            Option<PortfolioNumber>,+            Option<PortfolioNumber>,+            Ticker,+            Balance+        ),+        /// The portfolio identified with `num` has been renamed to `name`.+        PortfolioRenamed(IdentityId, PortfolioNumber, PortfolioName),+        /// All non-default portfolio numbers and names of a DID.+        UserPortfolios(IdentityId, Vec<(PortfolioNumber, PortfolioName)>),+    }+}++decl_error! {+    pub enum Error for Module<T: Trait> {+        /// The portfolio doesn't exist.+        PortfolioDoesNotExist,+        /// Insufficient balance for a transaction.+        InsufficientPortfolioBalance,+        /// The source and destination portfolios should be different.+        DestinationIsSamePortfolio,+        /// The portfolio couldn't be renamed because the chosen name is already in use.+        PortfolioNameAlreadyInUse,+    }+}++decl_module! {+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {+        type Error = Error<T>;++        /// The event logger.+        fn deposit_event() = default;++        /// Creates a portfolio.+        #[weight = 200_000]+        pub fn create_portfolio(origin, name: PortfolioName) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            let name_uniq = <Portfolios>::iter_prefix(&did).all(|n| n.1 != name);+            ensure!(name_uniq, Error::<T>::PortfolioNameAlreadyInUse);+            let num = Self::get_next_portfolio_number(&did);+            <Portfolios>::insert(&did, &num, name.clone());+            Self::deposit_event(RawEvent::PortfolioCreated(did, num, name));+            Ok(())+        }++        /// Deletes a user portfolio and moves all its assets to the default portfolio.+        #[weight = 1_000_000]+        pub fn delete_portfolio(origin, num: PortfolioNumber) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            ensure!(Self::portfolios(&did, &num).is_some(), Error::<T>::PortfolioDoesNotExist);+            let portfolio_id = PortfolioId::user_portfolio(did, num);+            let def_portfolio_id = PortfolioId::default_portfolio(did);+            for (ticker, balance) in <PortfolioAssetBalances<T>>::iter_prefix(&portfolio_id) {+                <PortfolioAssetBalances<T>>::mutate(&def_portfolio_id, ticker, |v| {+                    *v = v.saturating_add(balance)+                });+                Self::deposit_event(RawEvent::MovedBetweenPortfolios(+                    did,+                    Some(num),+                    None,+                    ticker,+                    balance,+                ));+            }+            <PortfolioAssetBalances<T>>::remove_prefix(&portfolio_id);+            <Portfolios>::remove(&did, &num);+            Self::deposit_event(RawEvent::PortfolioDeleted(did, num));+            Ok(())+        }++        /// Moves a token amount from one portfolio of an identity to another portfolio of the same+        /// identity.+        #[weight = 250_000 + 250_000 * u64::try_from(items.len()).unwrap_or_default()]+        pub fn move_portfolio(+            origin,+            from_num: Option<PortfolioNumber>,+            to_num: Option<PortfolioNumber>,+            items: Vec<MovePortfolioItem<<T as CommonTrait>::Balance>>,+        ) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            ensure!(from_num != to_num, Error::<T>::DestinationIsSamePortfolio);+            if let Some(from_num) = from_num {+                ensure!(Self::portfolios(&did, from_num).is_some(), Error::<T>::PortfolioDoesNotExist);+            }+            if let Some(to_num) = to_num {+                ensure!(Self::portfolios(&did, to_num).is_some(), Error::<T>::PortfolioDoesNotExist);+            }+            let from_portfolio_id = from_num+                .and_then(|num| Some(PortfolioId::user_portfolio(did, num)))+                .unwrap_or_else(|| PortfolioId::default_portfolio(did));+            let to_portfolio_id = to_num+                .and_then(|num| Some(PortfolioId::user_portfolio(did, num)))+                .unwrap_or_else(|| PortfolioId::default_portfolio(did));+            for item in items {+                let from_balance = Self::portfolio_asset_balances(&from_portfolio_id, &item.ticker);+                ensure!(from_balance >= item.amount, Error::<T>::InsufficientPortfolioBalance);+                <PortfolioAssetBalances<T>>::insert(+                    &from_portfolio_id,+                    &item.ticker,+                    from_balance - item.amount+                );+                let to_balance = Self::portfolio_asset_balances(&to_portfolio_id, &item.ticker);+                <PortfolioAssetBalances<T>>::insert(+                    &to_portfolio_id,+                    &item.ticker,+                    to_balance.saturating_add(item.amount)+                );+                Self::deposit_event(RawEvent::MovedBetweenPortfolios(+                    did,+                    from_num,+                    to_num,+                    item.ticker,+                    item.amount+                ));+            }+            Ok(())+        }++        /// Renames a non-default portfolio.+        #[weight = 500_000]+        pub fn rename_portfolio(

Logic largely duplicated from create_portfolio, dedup?

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

+#![cfg_attr(not(feature = "std"), no_std)]++use codec::{Decode, Encode};+use frame_support::{+    decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure,+    IterableStorageDoubleMap,+};+use frame_system::{self as system, ensure_signed};+use polymesh_common_utilities::{identity::Trait as IdentityTrait, CommonTrait, Context};+use polymesh_primitives::{IdentityId, PortfolioId, PortfolioName, PortfolioNumber, Ticker};+use sp_arithmetic::traits::Saturating;+use sp_std::{convert::TryFrom, prelude::Vec};++type Identity<T> = pallet_identity::Module<T>;++/// The ticker and balance of an asset to be moved from one portfolio to another.+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]+pub struct MovePortfolioItem<Balance> {+    /// The asset ticker.+    pub ticker: Ticker,+    /// The asset balance.+    pub amount: Balance,+}++impl<Balance> Default for MovePortfolioItem<Balance>+where+    Balance: Default,+{+    fn default() -> MovePortfolioItem<Balance> {+        MovePortfolioItem {+            ticker: Ticker::default(),+            amount: Balance::default(),+        }+    }+}++pub trait Trait: CommonTrait + IdentityTrait {+    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;+}++decl_storage! {+    trait Store for Module<T: Trait> as Session {+        /// The set of existing portfolios with their names. If a certain pair of a DID and+        /// portfolio number maps to `None` then such a portfolio doesn't exist. Conversely, if a+        /// pair maps to `Some(name)` then such a portfolio exists and is called `name`.+        pub Portfolios get(fn portfolios):+            double_map hasher(blake2_128_concat) IdentityId, hasher(twox_64_concat) PortfolioNumber =>+            Option<PortfolioName>;+        /// Asset balances of portfolios.+        pub PortfolioAssetBalances get(fn portfolio_asset_balances):+            double_map hasher(blake2_128_concat) PortfolioId, hasher(blake2_128_concat) Ticker =>+            T::Balance;+        /// The next portfolio sequence number of an identity.+        pub NextPortfolioNumber get(fn next_portfolio_number):+            map hasher(blake2_128_concat) IdentityId => PortfolioNumber;+    }+}++decl_event! {+    pub enum Event<T> where+        Balance = <T as CommonTrait>::Balance,+    {+        /// The portfolio has been successfully created.+        PortfolioCreated(IdentityId, PortfolioNumber, PortfolioName),+        /// The portfolio has been successfully removed.+        PortfolioDeleted(IdentityId, PortfolioNumber),+        /// A token amount has been moved from one portfolio to another. `None` denotes the default+        /// portfolio of the DID.+        MovedBetweenPortfolios(+            IdentityId,+            Option<PortfolioNumber>,+            Option<PortfolioNumber>,+            Ticker,+            Balance+        ),+        /// The portfolio identified with `num` has been renamed to `name`.+        PortfolioRenamed(IdentityId, PortfolioNumber, PortfolioName),+        /// All non-default portfolio numbers and names of a DID.+        UserPortfolios(IdentityId, Vec<(PortfolioNumber, PortfolioName)>),+    }+}++decl_error! {+    pub enum Error for Module<T: Trait> {+        /// The portfolio doesn't exist.+        PortfolioDoesNotExist,+        /// Insufficient balance for a transaction.+        InsufficientPortfolioBalance,+        /// The source and destination portfolios should be different.+        DestinationIsSamePortfolio,+        /// The portfolio couldn't be renamed because the chosen name is already in use.+        PortfolioNameAlreadyInUse,+    }+}++decl_module! {+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {+        type Error = Error<T>;++        /// The event logger.+        fn deposit_event() = default;++        /// Creates a portfolio.+        #[weight = 200_000]+        pub fn create_portfolio(origin, name: PortfolioName) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            let name_uniq = <Portfolios>::iter_prefix(&did).all(|n| n.1 != name);+            ensure!(name_uniq, Error::<T>::PortfolioNameAlreadyInUse);+            let num = Self::get_next_portfolio_number(&did);+            <Portfolios>::insert(&did, &num, name.clone());+            Self::deposit_event(RawEvent::PortfolioCreated(did, num, name));+            Ok(())+        }++        /// Deletes a user portfolio and moves all its assets to the default portfolio.+        #[weight = 1_000_000]+        pub fn delete_portfolio(origin, num: PortfolioNumber) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            ensure!(Self::portfolios(&did, &num).is_some(), Error::<T>::PortfolioDoesNotExist);+            let portfolio_id = PortfolioId::user_portfolio(did, num);+            let def_portfolio_id = PortfolioId::default_portfolio(did);+            for (ticker, balance) in <PortfolioAssetBalances<T>>::iter_prefix(&portfolio_id) {+                <PortfolioAssetBalances<T>>::mutate(&def_portfolio_id, ticker, |v| {+                    *v = v.saturating_add(balance)+                });+                Self::deposit_event(RawEvent::MovedBetweenPortfolios(+                    did,+                    Some(num),+                    None,+                    ticker,+                    balance,+                ));+            }+            <PortfolioAssetBalances<T>>::remove_prefix(&portfolio_id);+            <Portfolios>::remove(&did, &num);+            Self::deposit_event(RawEvent::PortfolioDeleted(did, num));+            Ok(())+        }++        /// Moves a token amount from one portfolio of an identity to another portfolio of the same+        /// identity.+        #[weight = 250_000 + 250_000 * u64::try_from(items.len()).unwrap_or_default()]+        pub fn move_portfolio(+            origin,+            from_num: Option<PortfolioNumber>,+            to_num: Option<PortfolioNumber>,+            items: Vec<MovePortfolioItem<<T as CommonTrait>::Balance>>,+        ) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            ensure!(from_num != to_num, Error::<T>::DestinationIsSamePortfolio);+            if let Some(from_num) = from_num {+                ensure!(Self::portfolios(&did, from_num).is_some(), Error::<T>::PortfolioDoesNotExist);+            }+            if let Some(to_num) = to_num {+                ensure!(Self::portfolios(&did, to_num).is_some(), Error::<T>::PortfolioDoesNotExist);+            }+            let from_portfolio_id = from_num+                .and_then(|num| Some(PortfolioId::user_portfolio(did, num)))+                .unwrap_or_else(|| PortfolioId::default_portfolio(did));+            let to_portfolio_id = to_num+                .and_then(|num| Some(PortfolioId::user_portfolio(did, num)))+                .unwrap_or_else(|| PortfolioId::default_portfolio(did));+            for item in items {+                let from_balance = Self::portfolio_asset_balances(&from_portfolio_id, &item.ticker);+                ensure!(from_balance >= item.amount, Error::<T>::InsufficientPortfolioBalance);+                <PortfolioAssetBalances<T>>::insert(+                    &from_portfolio_id,+                    &item.ticker,+                    from_balance - item.amount+                );+                let to_balance = Self::portfolio_asset_balances(&to_portfolio_id, &item.ticker);+                <PortfolioAssetBalances<T>>::insert(+                    &to_portfolio_id,+                    &item.ticker,+                    to_balance.saturating_add(item.amount)+                );+                Self::deposit_event(RawEvent::MovedBetweenPortfolios(+                    did,+                    from_num,+                    to_num,+                    item.ticker,+                    item.amount+                ));+            }+            Ok(())+        }++        /// Renames a non-default portfolio.+        #[weight = 500_000]+        pub fn rename_portfolio(+            origin,+            num: PortfolioNumber,+            to_name: PortfolioName,+        ) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            ensure!(Self::portfolios(&did, &num).is_some(), Error::<T>::PortfolioDoesNotExist);+            let name_uniq = <Portfolios>::iter_prefix(&did).all(|n| n.1 != to_name);+            ensure!(name_uniq, Error::<T>::PortfolioNameAlreadyInUse);+            <Portfolios>::mutate(&did, &num, |p| *p = Some(to_name.clone()));+            Self::deposit_event(RawEvent::PortfolioRenamed(+                did,+                num,+                to_name,+            ));+            Ok(())+        }+    }+}++impl<T: Trait> Module<T> {+    /// Returns the ticker balance of the identity's default portfolio.+    pub fn default_portfolio_balance(+        did: IdentityId,+        ticker: &Ticker,+    ) -> <T as CommonTrait>::Balance {+        Self::portfolio_asset_balances(PortfolioId::default_portfolio(did), ticker)+    }++    /// Returns the ticker balance of an identity's user portfolio.+    pub fn user_portfolio_balance(+        did: IdentityId,+        num: PortfolioNumber,+        ticker: &Ticker,+    ) -> <T as CommonTrait>::Balance {+        Self::portfolio_asset_balances(PortfolioId::user_portfolio(did, num), ticker)+    }++    /// Sets the ticker balance of the identity's default portfolio to the given value.+    pub fn set_default_portfolio_balance(+        did: IdentityId,+        ticker: &Ticker,+        balance: <T as CommonTrait>::Balance,+    ) {+        <PortfolioAssetBalances<T>>::insert(PortfolioId::default_portfolio(did), ticker, balance);+    }++    /// Returns the next portfolio number of a given identity and increments the stored number.+    fn get_next_portfolio_number(did: &IdentityId) -> PortfolioNumber {+        let num = Self::next_portfolio_number(did);+        <NextPortfolioNumber>::insert(did, num + 1);+        num+    }++    /// An RPC function that lists all user-defined portfolio number-name pairs.+    pub fn rpc_get_portfolios(+        did: IdentityId,+    ) -> core::result::Result<Vec<(PortfolioNumber, PortfolioName)>, &'static str> {

Cannot import Result?

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

+#![cfg_attr(not(feature = "std"), no_std)]++use codec::{Decode, Encode};+use frame_support::{+    decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure,+    IterableStorageDoubleMap,+};+use frame_system::{self as system, ensure_signed};+use polymesh_common_utilities::{identity::Trait as IdentityTrait, CommonTrait, Context};+use polymesh_primitives::{IdentityId, PortfolioId, PortfolioName, PortfolioNumber, Ticker};+use sp_arithmetic::traits::Saturating;+use sp_std::{convert::TryFrom, prelude::Vec};++type Identity<T> = pallet_identity::Module<T>;++/// The ticker and balance of an asset to be moved from one portfolio to another.+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]+pub struct MovePortfolioItem<Balance> {+    /// The asset ticker.+    pub ticker: Ticker,+    /// The asset balance.+    pub amount: Balance,+}++impl<Balance> Default for MovePortfolioItem<Balance>+where+    Balance: Default,+{+    fn default() -> MovePortfolioItem<Balance> {+        MovePortfolioItem {+            ticker: Ticker::default(),+            amount: Balance::default(),+        }+    }+}++pub trait Trait: CommonTrait + IdentityTrait {+    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;+}++decl_storage! {+    trait Store for Module<T: Trait> as Session {+        /// The set of existing portfolios with their names. If a certain pair of a DID and+        /// portfolio number maps to `None` then such a portfolio doesn't exist. Conversely, if a+        /// pair maps to `Some(name)` then such a portfolio exists and is called `name`.+        pub Portfolios get(fn portfolios):+            double_map hasher(blake2_128_concat) IdentityId, hasher(twox_64_concat) PortfolioNumber =>+            Option<PortfolioName>;+        /// Asset balances of portfolios.+        pub PortfolioAssetBalances get(fn portfolio_asset_balances):+            double_map hasher(blake2_128_concat) PortfolioId, hasher(blake2_128_concat) Ticker =>+            T::Balance;+        /// The next portfolio sequence number of an identity.+        pub NextPortfolioNumber get(fn next_portfolio_number):+            map hasher(blake2_128_concat) IdentityId => PortfolioNumber;+    }+}++decl_event! {+    pub enum Event<T> where+        Balance = <T as CommonTrait>::Balance,+    {+        /// The portfolio has been successfully created.+        PortfolioCreated(IdentityId, PortfolioNumber, PortfolioName),+        /// The portfolio has been successfully removed.+        PortfolioDeleted(IdentityId, PortfolioNumber),+        /// A token amount has been moved from one portfolio to another. `None` denotes the default+        /// portfolio of the DID.+        MovedBetweenPortfolios(+            IdentityId,+            Option<PortfolioNumber>,+            Option<PortfolioNumber>,+            Ticker,+            Balance+        ),+        /// The portfolio identified with `num` has been renamed to `name`.+        PortfolioRenamed(IdentityId, PortfolioNumber, PortfolioName),+        /// All non-default portfolio numbers and names of a DID.+        UserPortfolios(IdentityId, Vec<(PortfolioNumber, PortfolioName)>),+    }+}++decl_error! {+    pub enum Error for Module<T: Trait> {+        /// The portfolio doesn't exist.+        PortfolioDoesNotExist,+        /// Insufficient balance for a transaction.+        InsufficientPortfolioBalance,+        /// The source and destination portfolios should be different.+        DestinationIsSamePortfolio,+        /// The portfolio couldn't be renamed because the chosen name is already in use.+        PortfolioNameAlreadyInUse,+    }+}++decl_module! {+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {+        type Error = Error<T>;++        /// The event logger.+        fn deposit_event() = default;++        /// Creates a portfolio.+        #[weight = 200_000]+        pub fn create_portfolio(origin, name: PortfolioName) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            let name_uniq = <Portfolios>::iter_prefix(&did).all(|n| n.1 != name);+            ensure!(name_uniq, Error::<T>::PortfolioNameAlreadyInUse);+            let num = Self::get_next_portfolio_number(&did);+            <Portfolios>::insert(&did, &num, name.clone());+            Self::deposit_event(RawEvent::PortfolioCreated(did, num, name));+            Ok(())+        }++        /// Deletes a user portfolio and moves all its assets to the default portfolio.+        #[weight = 1_000_000]+        pub fn delete_portfolio(origin, num: PortfolioNumber) -> DispatchResult {

Nit: Consider adding some comments and space between the various groupings here, e.g., for the initial verification (including until ensure!), the transference to the default portfolio, etc.

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 decl_module! {             };              <Tokens<T>>::insert(&ticker, token);-            <BalanceOf<T>>::insert(ticker, did, total_supply);+            let beneficiary_did = treasury_did.unwrap_or_else(|| did);
            let beneficiary_did = treasury_did.unwrap_or(did);
vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 impl Printable for IdentityId {     } } +/// A wrapper for a portfolio name. It is used for non-default (aka "user") portfolios only since+/// default ones are nameless.+#[derive(+    Decode, Encode, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, VecU8StrongTyped,+)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub struct PortfolioName(pub Vec<u8>);++/// The unique ID of a non-default portfolio.+pub type PortfolioNumber = u64;++#[derive(Decode, Encode, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub enum PortfolioKind {+    /// The default portfolio of a DID.+    Default,+    /// A user-defined portfolio of a DID.+    User(PortfolioNumber),+}++impl Default for PortfolioKind {+    fn default() -> Self {+        PortfolioKind::Default+    }+}++/// The ID of a portfolio.+#[derive(Decode, Encode, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub struct PortfolioId {+    /// The DID of the portfolio.+    did: IdentityId,+    /// The kind of the portfolio: either default or user.+    kind: PortfolioKind,+}++impl Default for PortfolioId {+    fn default() -> Self {+        PortfolioId {+            did: IdentityId::default(),+            ..Default::default()+        }+    }+}
vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 impl Printable for IdentityId {     } } +/// A wrapper for a portfolio name. It is used for non-default (aka "user") portfolios only since+/// default ones are nameless.+#[derive(+    Decode, Encode, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, VecU8StrongTyped,+)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub struct PortfolioName(pub Vec<u8>);++/// The unique ID of a non-default portfolio.+pub type PortfolioNumber = u64;++#[derive(Decode, Encode, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub enum PortfolioKind {+    /// The default portfolio of a DID.+    Default,+    /// A user-defined portfolio of a DID.+    User(PortfolioNumber),+}++impl Default for PortfolioKind {+    fn default() -> Self {+        PortfolioKind::Default+    }+}++/// The ID of a portfolio.+#[derive(Decode, Encode, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]+pub struct PortfolioId {+    /// The DID of the portfolio.+    did: IdentityId,+    /// The kind of the portfolio: either default or user.+    kind: PortfolioKind,+}++impl Default for PortfolioId {+    fn default() -> Self {+        PortfolioId {+            did: IdentityId::default(),+            ..Default::default()+        }+    }+}++impl Printable for PortfolioId {+    fn print(&self) {+        self.did.print();+        sp_io::misc::print_utf8(b"/");+        match self.kind {+            PortfolioKind::Default => {+                sp_io::misc::print_utf8(b"default");+            }+            PortfolioKind::User(num) => {+                sp_io::misc::print_hex(&num.to_be_bytes());+            }+        }+    }+}++impl PortfolioId {+    /// Returns the default portfolio of `did`.+    pub fn default_portfolio(did: IdentityId) -> PortfolioId {+        PortfolioId {+            did,+            kind: PortfolioKind::Default,+        }+    }

Nit:

    pub fn default_portfolio(did: IdentityId) -> Self {
        Self {
            did,
            kind: PortfolioKind::Default,
        }
    }
vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 decl_module! {              //Decrease total supply             let mut token = Self::token_details(&ticker);-            token.total_supply = token.total_supply.checked_sub(&value)-                .ok_or(Error::<T>::BalanceOverflow)?;+            // No check since the total supply is always greater than or equal to the default+            // portfolio balance. The default portfolio balance is already checked above.+            token.total_supply = token.total_supply - value;
            token.total_supply -= value;
vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 decl_module! {             let mut issued_in_this_round = Self::issued_in_funding_round(&ticker_round);              // A round of per-investor checks-            for i in 0..issue_asset_items.len() {+            for IssueAssetItem { investor_did, value } in &issue_asset_items {                 ensure!(-                    Self::check_granularity(&ticker, issue_asset_items[i].value),+                    Self::check_granularity(&ticker, *value),

(Also, the function is pretty large, so I'd suggest splitting it, if possible.)

vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 decl_module! {             let mut issued_in_this_round = Self::issued_in_funding_round(&ticker_round);              // A round of per-investor checks-            for i in 0..issue_asset_items.len() {+            for IssueAssetItem { investor_did, value } in &issue_asset_items {                 ensure!(-                    Self::check_granularity(&ticker, issue_asset_items[i].value),+                    Self::check_granularity(&ticker, *value),                     Error::<T>::InvalidGranularity                 );                 let updated_total_supply = token                     .total_supply-                    .checked_add(&issue_asset_items[i].value)+                    .checked_add(value)                     .ok_or(Error::<T>::TotalSupplyOverflow)?;                 ensure!(updated_total_supply <= MAX_SUPPLY.into(), Error::<T>::TotalSupplyAboveLimit); -                current_balances.push(Self::balance(&ticker, &issue_asset_items[i].investor_did));-                updated_balances.push(current_balances[i]-                    .checked_add(&issue_asset_items[i].value)-                    .ok_or(Error::<T>::BalanceOverflow)?);+                let bals = Self::balance(&ticker, *investor_did);+                current_total_balances.push(bals.total);+                // No check since the total balance is always less than or equal to the total
                // No check since the total balance is always <= to the total
vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 decl_module! {             )?;             <IssuedInFundingRound<T>>::insert(&ticker_round, issued_in_this_round);             // Update investor balances and emit events quoting the updated total token balance issued.-            for i in 0..issue_asset_items.len() {-                Self::_update_checkpoint(&ticker, issue_asset_items[i].investor_did, current_balances[i]);-                <BalanceOf<T>>::insert(ticker, issue_asset_items[i].investor_did, updated_balances[i]);-                <statistics::Module<T>>::update_transfer_stats(&ticker, None, Some(updated_balances[i]), issue_asset_items[i].value);+            for (i, IssueAssetItem { investor_did, value }) in issue_asset_items.iter().enumerate() {+                Self::_update_checkpoint(&ticker, *investor_did, current_total_balances[i]);+                let (tot, def) = updated_balances[i];
                let (total, default) = updated_balances[i];
vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 decl_module! {              //Decrease total supply             let mut token = Self::token_details(&ticker);-            token.total_supply = token.total_supply.checked_sub(&value)-                .ok_or(Error::<T>::BalanceOverflow)?;+            // No check since the total supply is always greater than or equal to the default+            // portfolio balance. The default portfolio balance is already checked above.+            token.total_supply = token.total_supply - value;
            token.total_supply -= value;
vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

 decl_module! {             let mut issued_in_this_round = Self::issued_in_funding_round(&ticker_round);              // A round of per-investor checks-            for i in 0..issue_asset_items.len() {+            for IssueAssetItem { investor_did, value } in &issue_asset_items {                 ensure!(-                    Self::check_granularity(&ticker, issue_asset_items[i].value),+                    Self::check_granularity(&ticker, *value),                     Error::<T>::InvalidGranularity                 );                 let updated_total_supply = token                     .total_supply-                    .checked_add(&issue_asset_items[i].value)+                    .checked_add(value)                     .ok_or(Error::<T>::TotalSupplyOverflow)?;                 ensure!(updated_total_supply <= MAX_SUPPLY.into(), Error::<T>::TotalSupplyAboveLimit); -                current_balances.push(Self::balance(&ticker, &issue_asset_items[i].investor_did));-                updated_balances.push(current_balances[i]-                    .checked_add(&issue_asset_items[i].value)-                    .ok_or(Error::<T>::BalanceOverflow)?);+                let bals = Self::balance(&ticker, *investor_did);+                current_total_balances.push(bals.total);+                // No check since the total balance is always less than or equal to the total+                // supply. The total supply is already checked above.+                let updated_total_balance = bals.total + *value;+                // No check since the default portfolio balance is always less than or equal to the+                // total supply. The total supply is already checked above.+                let updated_def_balance = bals.portfolio + *value;+                updated_balances.push((updated_total_balance, updated_def_balance));                  // verify transfer check                 ensure!(-                    Self::_is_valid_transfer(&ticker, sender.clone(),  None, Some(issue_asset_items[i].investor_did), issue_asset_items[i].value)? == ERC1400_TRANSFER_SUCCESS,+                    Self::_is_valid_transfer(&ticker, sender.clone(),  None, Some(*investor_did), *value)? == ERC1400_TRANSFER_SUCCESS,                     Error::<T>::InvalidTransfer                 ); -                issued_in_this_round = issued_in_this_round-                    .checked_add(&issue_asset_items[i].value)-                    .ok_or(Error::<T>::FundingRoundTotalOverflow)?;+                // No check since the issued balance is always less than or equal to the total+                // supply. The total supply is already checked above.+                issued_in_this_round = issued_in_this_round + *value;
                issued_in_this_round += *value;
vkomenda

comment created time in 21 days

Pull request review commentPolymathNetwork/Polymesh

Mesh 1076/portfolio

+#![cfg_attr(not(feature = "std"), no_std)]++use codec::{Decode, Encode};+use frame_support::{+    decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure,+    IterableStorageDoubleMap,+};+use frame_system::{self as system, ensure_signed};+use polymesh_common_utilities::{identity::Trait as IdentityTrait, CommonTrait, Context};+use polymesh_primitives::{IdentityId, PortfolioId, PortfolioName, PortfolioNumber, Ticker};+use sp_arithmetic::traits::Saturating;+use sp_std::{convert::TryFrom, prelude::Vec};++type Identity<T> = pallet_identity::Module<T>;++/// The ticker and balance of an asset to be moved from one portfolio to another.+#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]+pub struct MovePortfolioItem<Balance> {+    /// The asset ticker.+    pub ticker: Ticker,+    /// The asset balance.+    pub amount: Balance,+}++impl<Balance> Default for MovePortfolioItem<Balance>+where+    Balance: Default,+{+    fn default() -> MovePortfolioItem<Balance> {+        MovePortfolioItem {+            ticker: Ticker::default(),+            amount: Balance::default(),+        }+    }+}++pub trait Trait: CommonTrait + IdentityTrait {+    type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;+}++decl_storage! {+    trait Store for Module<T: Trait> as Session {+        /// The set of existing portfolios with their names. If a certain pair of a DID and+        /// portfolio number maps to `None` then such a portfolio doesn't exist. Conversely, if a+        /// pair maps to `Some(name)` then such a portfolio exists and is called `name`.+        pub Portfolios get(fn portfolios):+            double_map hasher(blake2_128_concat) IdentityId, hasher(twox_64_concat) PortfolioNumber =>+            Option<PortfolioName>;+        /// Asset balances of portfolios.+        pub PortfolioAssetBalances get(fn portfolio_asset_balances):+            double_map hasher(blake2_128_concat) PortfolioId, hasher(blake2_128_concat) Ticker =>+            T::Balance;+        /// The next portfolio sequence number of an identity.+        pub NextPortfolioNumber get(fn next_portfolio_number):+            map hasher(blake2_128_concat) IdentityId => PortfolioNumber;+    }+}++decl_event! {+    pub enum Event<T> where+        Balance = <T as CommonTrait>::Balance,+    {+        /// The portfolio has been successfully created.+        PortfolioCreated(IdentityId, PortfolioNumber, PortfolioName),+        /// The portfolio has been successfully removed.+        PortfolioDeleted(IdentityId, PortfolioNumber),+        /// A token amount has been moved from one portfolio to another. `None` denotes the default+        /// portfolio of the DID.+        MovedBetweenPortfolios(+            IdentityId,+            Option<PortfolioNumber>,+            Option<PortfolioNumber>,+            Ticker,+            Balance+        ),+        /// The portfolio identified with `num` has been renamed to `name`.+        PortfolioRenamed(IdentityId, PortfolioNumber, PortfolioName),+        /// All non-default portfolio numbers and names of a DID.+        UserPortfolios(IdentityId, Vec<(PortfolioNumber, PortfolioName)>),+    }+}++decl_error! {+    pub enum Error for Module<T: Trait> {+        /// The portfolio doesn't exist.+        PortfolioDoesNotExist,+        /// Insufficient balance for a transaction.+        InsufficientPortfolioBalance,+        /// The source and destination portfolios should be different.+        DestinationIsSamePortfolio,+        /// The portfolio couldn't be renamed because the chosen name is already in use.+        PortfolioNameAlreadyInUse,+    }+}++decl_module! {+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {+        type Error = Error<T>;++        /// The event logger.+        fn deposit_event() = default;++        /// Creates a portfolio.+        #[weight = 200_000]+        pub fn create_portfolio(origin, name: PortfolioName) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            let name_uniq = <Portfolios>::iter_prefix(&did).all(|n| n.1 != name);+            ensure!(name_uniq, Error::<T>::PortfolioNameAlreadyInUse);+            let num = Self::get_next_portfolio_number(&did);+            <Portfolios>::insert(&did, &num, name.clone());+            Self::deposit_event(RawEvent::PortfolioCreated(did, num, name));+            Ok(())+        }++        /// Deletes a user portfolio and moves all its assets to the default portfolio.+        #[weight = 1_000_000]+        pub fn delete_portfolio(origin, num: PortfolioNumber) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            ensure!(Self::portfolios(&did, &num).is_some(), Error::<T>::PortfolioDoesNotExist);+            let portfolio_id = PortfolioId::user_portfolio(did, num);+            let def_portfolio_id = PortfolioId::default_portfolio(did);+            for (ticker, balance) in <PortfolioAssetBalances<T>>::iter_prefix(&portfolio_id) {+                <PortfolioAssetBalances<T>>::mutate(&def_portfolio_id, ticker, |v| {+                    *v = v.saturating_add(balance)+                });+                Self::deposit_event(RawEvent::MovedBetweenPortfolios(+                    did,+                    Some(num),+                    None,+                    ticker,+                    balance,+                ));+            }+            <PortfolioAssetBalances<T>>::remove_prefix(&portfolio_id);+            <Portfolios>::remove(&did, &num);+            Self::deposit_event(RawEvent::PortfolioDeleted(did, num));+            Ok(())+        }++        /// Moves a token amount from one portfolio of an identity to another portfolio of the same+        /// identity.+        #[weight = 250_000 + 250_000 * u64::try_from(items.len()).unwrap_or_default()]+        pub fn move_portfolio(+            origin,+            from_num: Option<PortfolioNumber>,+            to_num: Option<PortfolioNumber>,+            items: Vec<MovePortfolioItem<<T as CommonTrait>::Balance>>,+        ) -> DispatchResult {+            let sender = ensure_signed(origin)?;+            let did = Context::current_identity_or::<Identity<T>>(&sender)?;+            ensure!(from_num != to_num, Error::<T>::DestinationIsSamePortfolio);+            if let Some(from_num) = from_num {+                ensure!(Self::portfolios(&did, from_num).is_some(), Error::<T>::PortfolioDoesNotExist);+            }+            if let Some(to_num) = to_num {+                ensure!(Self::portfolios(&did, to_num).is_some(), Error::<T>::PortfolioDoesNotExist);+            }+            let from_portfolio_id = from_num+                .and_then(|num| Some(PortfolioId::user_portfolio(did, num)))+                .unwrap_or_else(|| PortfolioId::default_portfolio(did));+            let to_portfolio_id = to_num+                .and_then(|num| Some(PortfolioId::user_portfolio(did, num)))+                .unwrap_or_else(|| PortfolioId::default_portfolio(did));+            for item in items {+                let from_balance = Self::portfolio_asset_balances(&from_portfolio_id, &item.ticker);+                ensure!(from_balance >= item.amount, Error::<T>::InsufficientPortfolioBalance);+                <PortfolioAssetBalances<T>>::insert(+                    &from_portfolio_id,+                    &item.ticker,+                    from_balance - item.amount
                    // Cannot underflow, as verified by `ensure!` above.
                    from_balance - item.amount
vkomenda

comment created time in 21 days

more