Compare commits

...

187 Commits

Author SHA1 Message Date
Mark Sinclair 4ca8cdcf5a Testing CI 2021-11-24 18:01:32 +00:00
Aid19801 c8f2548090 Merge pull request #915 from nymtech/fix-mob-datagrids
Fix Mobile View for MUI data-grid (CARD 108)
2021-11-24 13:28:36 +00:00
Aid Thompson 62f20a905e Alignment on Overview page 2021-11-24 10:45:54 +00:00
Jędrzej Stuczyński a3dd94507a Feature/total delegation bucket (#913)
* Preliminary attempt at creating StoredMixnodeBond

* Fixed tests

* Removed dead code

* Moved StoredMixnodeBond to storage.rs

* Removed redundant clone
2021-11-23 16:36:48 +00:00
Aid Thompson dd36b329d5 renamed back to universal-datagrid 2021-11-23 16:03:39 +00:00
Aid Thompson 581af4a295 gateways page performant 2021-11-23 15:49:05 +00:00
Jędrzej Stuczyński 085761a9fb Feature/batch delegator rewarding (#898)
* Unnrolled the loop into separate function

* Ugly way of saving rewarding status

* Initial way of rewarding next page of delegators

* Attribute passing

* Promoted transactions to directory

* Moved rewarding-related functionalities into separate file

* [ci skip] Generate TS types

* Better errors on double rewarding attempt

* Removed old rewarding call

* Test fixes

* Some cleanup

* Paged mixnode rewarding test + serde fixes

* Tests for delegator rewarding

* ExecuteMsg for MixDelegatorRewarding

* Made validator-api code compliable

with bunch of todo!() macros

* Removed Option wrapper from params in MixnodeToReward

* Calculating uptime for entire epoch

* Created shared MIXNODE_DELEGATORS_PAGE_LIMIT constant

* Using new rewarding messages in validator API

* cargo fmt

* Updated wallet state types

* Additional test for correct rewarding information

* Query for rewarding status

* Additional test regarding delegator rewarding

* Client methods for obtaining rewarding status

* Validator API checking for full rewarding

* Removed unused field from validator api config template

* Waiting for MINIMUM number of test routes

* Waiting initialisation_backoff in the early return case

* Fixes crash condition in validator API when calculating last day uptime

* Fixed typo

* Dealing with the case of rewarding mixnode with 0 uptime

* Removed temporary unwrap

* Guarding against 0-size rewarded/active sets

Co-authored-by: jstuczyn <jstuczyn@users.noreply.github.com>
2021-11-23 15:36:20 +00:00
Aid Thompson 58537224df mixnode datagrid performant 2021-11-23 15:20:23 +00:00
Jędrzej Stuczyński 78a5dbbf05 Fixes crash condition in validator API when calculating last day uptime (#909) 2021-11-23 14:47:49 +00:00
AniaPiotrowska ab7f24dc1f Merge pull request #837 from nymtech/feature/vouchers
Feature/vouchers
2021-11-22 19:34:58 +00:00
Jędrzej Stuczyński 289605f36b Bugfix/monitor initial values wait (#907)
* Waiting for MINIMUM number of test routes

* Waiting initialisation_backoff in the early return case
2021-11-18 17:16:15 +00:00
Tommy Verrall 4f6bf3423b Merge pull request #905 from nymtech/update/readme
Update README.md
2021-11-18 09:59:55 +00:00
Tommy Verrall 04a32156d4 Merge pull request #891 from nymtech/update/remove-gateway-delegations
remove delegation and undelegation from gateways
2021-11-18 09:42:34 +00:00
Tommy Verrall 427880d23f Merge pull request #896 from nymtech/bug-fix/macos-keyboard-shortcuts
Bug fix/macos keyboard shortcuts
2021-11-18 09:22:33 +00:00
Tommy Verrall a5e47dead8 Update readme
. If `Webview2` is not installed on Windows, that app will not launch correctly. There's two ways to update if you're running on an older version of windows. 
- Update the Edge browser in your current OS
- Update via the installer now provided in the README documentation.
2021-11-18 09:18:04 +00:00
Tommy Verrall 297b7b2355 Merge pull request #904 from nymtech/bug-bond-denom
BUG: Bond cell denom
2021-11-17 12:30:45 +00:00
Tommy Verrall d7f3abfe7d Merge pull request #903 from nymtech/test/fix-detail-tables
Explorer UI tests missing data-testid
2021-11-17 12:29:34 +00:00
Aid Thompson 234952a6bb removed typo 2021-11-17 11:50:03 +00:00
Tommy Verrall 930561faf5 Explorer UI tests
. This `data-testid` was missing and is needed after changes to DataGrid into MUI-Table
2021-11-17 11:20:43 +00:00
Tommy Verrall 17507bc8c5 Merge pull request #899 from nymtech/test/wallet-readme-fix
Fix up Nym-Wallet README.md
2021-11-16 14:17:15 +00:00
Tommy Verrall 12f8e453ea Fix typo. 2021-11-16 10:20:50 +00:00
Tommy Verrall 97ef024b8a Fix up Nym-Wallet README.md
. Provide a descriptive overview
. Remove old references
2021-11-16 10:17:30 +00:00
Aid19801 3dc94223ae Bug mapp nodemap (#897)
* desktop onMouseEnter and leave handlers added

* clicking nav option closes drawer

* All AC met

* removed dead bool

* added hamburger to AppBar for mobile users small refactor

* nuts and bolts working needs a lot of tidying though

* Functionally complete.

* change hardcode colors to theming

* UI theme and border changes

* bug fix removed collapse nav flicker

* dead code

* resetting drawer to closed onload

* removal of hardcoded color

* made null functions into optional props

* Nested sx styling not global

* paperprops now working

* fixed breaking changes for DataGrid

* linting fix

* linting fix

* optional chaining for nodemap search

* leaner filter func

Co-authored-by: Aid Thompson <adrian@nymtech.net>
2021-11-12 18:22:03 +00:00
Aid19801 50cd18a926 Add a Mobile Nav to the Network Explorer (#895)
* desktop onMouseEnter and leave handlers added

* clicking nav option closes drawer

* All AC met

* removed dead bool

* added hamburger to AppBar for mobile users small refactor

* nuts and bolts working needs a lot of tidying though

* Functionally complete.

* change hardcode colors to theming

* UI theme and border changes

* bug fix removed collapse nav flicker

* dead code

* resetting drawer to closed onload

* removal of hardcoded color

* made null functions into optional props

* Nested sx styling not global

* paperprops now working

* fixed breaking changes for DataGrid

* linting fix

* linting fix

Co-authored-by: Aid Thompson <adrian@nymtech.net>
2021-11-12 16:14:37 +00:00
Tommy e5051f98c5 Adding a few more menu options 2021-11-12 11:23:36 +00:00
fmtabbara 2f69958e21 macos keyboard shortcut fix 2021-11-12 10:29:49 +00:00
fmtabbara d427591a66 dont commite dist folder contents 2021-11-12 10:29:33 +00:00
fmtabbara 0257c42ee9 update tauri bundle identifier 2021-11-12 10:29:04 +00:00
fmtabbara 8ebfc72da8 remove contents of dist folder 2021-11-12 10:28:39 +00:00
Aid19801 897d51cba0 Change MixnodeDetail page's datagrid into a reuseable table component (#887)
* basic table working

* broken out into sep component

* table is more dynamic now

* linting fixes

* Added types for Columns

* hangover from eslint work moving to diff PR

* Changed To DetailTable

* Killed unused flex val in Columns

* merge conflicts resolved

Co-authored-by: Aid Thompson <adrian@nymtech.net>
2021-11-12 10:28:12 +00:00
Tommy Verrall 1f1d91bd1e Adding data-test-ids for the explorer (#885)
* Adding all the data-test-ids

Test ID's for doing automation on explorer

* Applied lint-fix

Linting

* More data ids
2021-11-11 15:31:30 +00:00
Mark Sinclair 9d63a30b07 Merge pull request #890 from nymtech/feature/nym-wallet-rename
Feature/nym wallet rename
2021-11-11 15:00:55 +00:00
Tommy 5b22b1c298 getting branch up to date with tauri changes and webriver changes 2021-11-11 11:02:39 +00:00
Dave Hrycyszyn 0302d9ae5d Merge branch 'feature/nym-wallet-rename' of github.com:nymtech/nym into feature/nym-wallet-rename 2021-11-11 10:50:12 +00:00
Bogdan-Ștefan Neacşu eb6949528a Fix network monitor template (#892) 2021-11-11 10:36:43 +00:00
Mark Sinclair 41b77c3ad5 Merge pull request #894 from nymtech/feature-gate-ts-rs
Only use ts-rs in tests
2021-11-11 10:34:04 +00:00
Mark Sinclair 2f78f5eb26 Merge pull request #884 from nymtech/test/fix-gh-action
Fix path for github action running tauri-wallet-tests
2021-11-11 10:33:02 +00:00
Dave Hrycyszyn cdbd731f10 Changed all workflow references to nym-wallet 2021-11-11 10:25:51 +01:00
Dave Hrycyszyn 5cb4949d46 Renaming tauri-wallet to nym-wallet
Hopefully this will make it a bit clearer as to what people should
compile if they want the wallet.
2021-11-11 10:25:51 +01:00
Mark Sinclair 9ec0b30812 GitHub Actions: move explorer actions to k8s hosted runners 2021-11-10 16:08:15 +00:00
Jędrzej Stuczyński 7e1cf2f105 Feature/rewarding interval updates (#880)
* Introduced rewarding_interval_nonce to contract state

* Queries for ibid.

* Mixnode demanded set size

* Routes for obtaining demanded/active mixnode sets

* Testing only demanded nodes

* Typo

* Initial state

* Feature-locking unused imports

* Generating pseudorandom (with deterministic seed) demanded mixnodes set

* cargo fmt

* Fixed tauri state

* Renamed network monitor address to the rewarding validator

* [ci skip] Generate TS types

* Notice for the future

* Transactions to begin/finish mixnode rewarding + double rewarding protection

* Validator API using new contract calls

* Removed dead code from an old experiment

* [ci skip] Generate TS types

* Removed unused import

* Renamed 'demanded' set to 'rewarded' set

* Some renaming action

* [ci skip] Generate TS types

* Fixed post-merge dependency issue in tests

* Post merge test fix

Co-authored-by: jstuczyn <jstuczyn@users.noreply.github.com>
2021-11-10 15:28:16 +00:00
Mark Sinclair 04636a569a GitHub Actions: fix up reference to action 2021-11-10 15:24:47 +00:00
Mark Sinclair 52f5b980a4 GitHub Actions: use dynamic matrix for os types and rust versions 2021-11-10 15:15:31 +00:00
Mark Sinclair 0e4f833715 GitHub Actions: fix up 2021-11-10 14:44:52 +00:00
Mark Sinclair d22914c4dc GitHub Actions: fix up 2021-11-10 14:43:32 +00:00
Mark Sinclair 7d506e6e2a Slim down GitHub Actions 2021-11-10 14:34:02 +00:00
Drazen Urch c7007de1ea Only use ts-rs in tests 2021-11-10 15:02:59 +01:00
Bogdan-Ștefan Neacşu 10bf70b22b Feature/bandwidth token (#832)
* Remove check for bandwidth for incoming packets

We should only accunt for packets that the client inputs to the mixnet

* Introduce BandwidthController for both types of bandwidth creds

* Add some non-coconut token bandwidth handling

* Use thiserror for gateway-client lib

* Add error handling

* Unable to build for wasm for now

* Fix wasm strange error

* Disable non-coconut credentials for wasm client

* Check for status and throw the error up

* Send encrypted token cred from client

* Gateway receive message and signature validation

* Put the correct amount of tokens that were burned

* [ci skip] Generate TS types

* Eth endpoint and secret key as config parameters

* Add eth_endpoint config argument for gateway

* Update test as well

* Separate panicable code from the safe one

* Move some bandwidth controller panics up the call stack

* Save contract corresponding to the eth endpoint

* Fix template

* Pass the web3 interface as well

* Made event reads possible in gateway

* Add checks for event data

* Cosmos contract for double spending prevention

* Add workflow for the new contract

* Add validator rest URL to config

* Rename eth_events to erc20_bridge

* Pass cosmos mnemonic as well, and put the nymd client in ERC20Bridge

* Call cosmos contract for final verification

* Ask for config parameters in cli

* Fix various stuff

* Increase timeout to allow gateway to check the two chains

* Put some logs for the new flow

* Set consumed bandwidth invariantly of coconut feature

* Fix clippy error

* Add non-coconut checks

* Use 2018 rust instead of 2021

* More verbose nymd error

* Explicitly specify TOKENS_TO_BURN constant

* Put eth burn function in a constant

* Replace to_vec & append with iter & chain

* Test for (de)serialization of TokenCredential

* Minor rename

* Separate credential creation from bandwidth claiming

* Switch from panics to errors when claiming coconut bandwidth

* Another append changed to chain

* Update QA cosmos contract address

* Simplify build/test/clippy separation on coconut feature

* Fix bad features arg positioning

* Use the start_after in cosmos contract query

* Set a limit in line with a range on cosmos queries

* Added unit tests for new cosmos contract

* Fix bandwidth_remaining comparation

* Get remaining bandwidth from gateway

* Add contract build flag

* Add a useful info log

* Use a more robust eth depth for release builds

* Include recipt logs in error message

* Fix clippy for tests

* Use Arc instead of clone

* Rename as_bytes to to_bytes

* Make signature verification in contract more verbose

* Missed rename of paging constant

* Fix gateway start with coconut enabled

* Rename function to claim_token

* Simplify nymd client setup

* Check with block buffer on gateway as well

* Update comment of double spending protection

* Correct contract address

* Backup the keypairs used for buying tokens, in case of error cases

* Don't take any chances with the gateway timeout

* [ci skip] Generate TS types

* Updated cosmos contract to latest QA address

* Add cli options for eth

* Update network monitor timeout value as well

Co-authored-by: neacsu <neacsu@users.noreply.github.com>
2021-11-10 14:53:56 +02:00
Jędrzej Stuczyński d952f3233a Active sets => Rewarded + Active/Idle sets (#864)
* Introduced rewarding_interval_nonce to contract state

* Queries for ibid.

* Mixnode demanded set size

* Routes for obtaining demanded/active mixnode sets

* Testing only demanded nodes

* Typo

* Initial state

* Feature-locking unused imports

* Generating pseudorandom (with deterministic seed) demanded mixnodes set

* cargo fmt

* Fixed tauri state

* [ci skip] Generate TS types

* Renamed 'demanded' set to 'rewarded' set

* Some renaming action

* [ci skip] Generate TS types

* Fixed post-merge dependency issue in tests

Co-authored-by: jstuczyn <jstuczyn@users.noreply.github.com>
2021-11-10 12:52:13 +00:00
fmtabbara b1178b7ad5 remove delegation and undelegation from gateways 2021-11-10 10:42:45 +00:00
Jędrzej Stuczyński af4ac1b7c9 Reverted gateway registration handshake to its 0.11.0 version (#882) 2021-11-10 11:16:39 +01:00
Drazen Urch eae4276381 Tokenomics rewards (#802)
* Initial analysis

* Implement core rewards distribution

* Tests and refactoring for better testability

* Feature gate ts-rs in mixnet-contract

* No more floats

* Fix performance calculation

* Add migration

* Bandwidth fix, reduce inflation pool size

* Update tokenomics

* Refactor rewarding and replace num crate

* Address review comments

* Cleanup, better test values

* Simplify formula

* Cleanup, add rewarding formulas to README

* Address review comments

* Cleanup rebase

* [ci skip] Generate TS types

* fmt

Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>
2021-11-10 11:14:06 +01:00
Dave Hrycyszyn d8fab74df6 Changed all workflow references to nym-wallet 2021-11-09 17:04:07 +00:00
Dave Hrycyszyn b8c184cc60 Renaming tauri-wallet to nym-wallet
Hopefully this will make it a bit clearer as to what people should
compile if they want the wallet.
2021-11-09 16:55:49 +00:00
Jędrzej Stuczyński 8bcc931d9b Feature/removal of monitor good nodes (#833)
* Removed separated ipv4 and ipv6 testing

* Testing network using chosen core nodes

This should have probably been like 20 independent commits... sorry...

* SQL migrations for updated schema

* SQL updates

* Using absolute uptime directly

* New uptime calculations

* Config entries, more DB work, some cleanup

* Additional API query routes

* More SQL and API work

* Changed `_` to `-` in new routes

* Removed good topology from config

* Fixed gateways reader yield condition

* Initial gateways pinger

* Minor cleanup and logging level decreases

* Missing trait derivations

* Further logging adjustments

* Unused commented out import

* Claiming additional bandwidth in coconut feature when low

* Fixed build with coconut feature

* Minimum number of test routes

* Making beta/nightly clippy happier
2021-11-09 12:25:41 +00:00
Mark Sinclair 955ef7b871 GitHub Actions: only run job to generate types when not in a pull request (#886) 2021-11-09 11:56:43 +00:00
Mark Sinclair 9ee7ad4fa8 GitHub Actions: only run job to generate types when not in a pull request 2021-11-09 11:42:00 +00:00
Tommy Verrall 79aa1febbe Run all the tests 2021-11-08 15:01:21 +00:00
Tommy 4ed9d3a297 Updated rust version for installation 2021-11-08 14:55:49 +00:00
Tommy 97dee2e1a0 Fix path for github action running tauri-wallet-tests
. Applied prettier to the code base
. Removed delegation to gateway tests
2021-11-08 14:54:01 +00:00
Fouad 32c4974c44 Merge pull request #873 from nymtech/update/remove-gateway-delegation
remove gateway selection on delegation and undelegation pages
2021-11-08 15:31:58 +01:00
Mark Sinclair 5dadd73dfd Network Explorer (#881)
* fixed styled component

* dynamic colour for isSelected

* corrected type names

* wrapped nav for routing and positioning of main section and dynamic colour for selected section

* overview info panes added

* quick refactor break out components separately

* WorldMap implemented but not data

* map changed and updated to shades correctly

* live data in cards

* added any types for react simple map

* nested routing added but needs tidying and types refactored

* added tooltip to worldmap

* worldmap killed unused props

* updated MUI version to stable v5

* dark mode and ContentCard refactor complete

* refactor of DarkMode context and API into class and context setup

* context refactor for multiple APIs at top level

* mui typography used for error msging instead of jsx/html

* added typeDefs for node api types

* small changes to sx styling

* added types for api responses and main context

* promiseAll for better error handling of individual async calls

* switch out to live API for country nodes

* removal of unnecessary type any and shortening sx style block

* routing and basic mixnodes table and linking

* fixed TS error handling and ts exclude files

* refactor of class API fetch reqs

* renaming to more appropriate explorer-api

* broken - passing to Fouad

* fix for types in context main

* mixnode detail page

* rebasing back before fetch mixnode by ID was implemented

* added basic cache for huge dump of mixnode data

* broken mixnodes context

* fixed mixnode detail fetch

* added hardcoded BondBreakdown section

* added 2 col table for detail page and small refactor of ApiState type for consistent use throughout app

* basic chart with basic dark/theme implemented - no live data

* added scrollToTop useRef for Detail page

* tidied grid items

* media qry for smaller screens

* small changes

* added live data to bonds breakdown 1/2

* small changes/tweaks

* Bondbreakdown retrieves live data

* mixnode stats using live data

* added node status live data

* uptime story added with live data

* date formatting added

* mixnode map

* error handling for mixnode stats

* error handling for port stats

* improved error handling for table - unfinished

* error handling for mixnode table

* handle Loading state for 2colSmallTable

* Uptime story loading handling

* set up data grid component

* remove mixnode value check as handled inside MixnodesDataGrid component

* use loading prop in data grid component

* undo unintentional code formatting

* map blur and linkable data-grid added

* getting ready for gateways and removing con logs

* quickfix for map blur

* PR comment changes

* refactored data grid for reuseability

* Link to open Big Dipper for Blocks

* passing element to title instead of string for routing to Big Dipper

* quick fix for element passed as title for contentCard

* fix for colour coding nodes

* nuts and bolts of search and results per page are working

* media query for responsive search and no-per-page toolbar

* broke out search and pagesize to separate toolbar

* fix for going back to mixnodes datagrid and refetching

* corrected typings for WorldMap

* removed API for topojson

* Cleaner implementation of formatting inline for datagrid

* added Type to Datagrid Rows for mixnode

* removed optional from type for Datagrid

* added page listing the Gateway nodes

* adding clickable location to handleSearch

* tidying util functions and removing dead useEffect

* Add missing constant

* Validators link to Big Dipper

* added validators link to side nav as per Issues card

* SVG icons

* PR tweak to move logic to routes

* removed dead code post rebase

* fixed light dark mode for DataGrid

* light dark mode works on SVGs in Nav

* moving logic back to Nav to avoid window object issues

* neater ternary for SVG icons dark mode

* Better Linking/Styling for cells

* corrected prop/attr name in svg to Reactify it

* moved api url to constants

* SVGs dark now governed by context not props so reverting renderIcon method back to key value setup

* percentage for bond total added

* SVGs for Overview cards Mixnodes Gateways and Validators

* decimalised formatted punks and % of bond for BondBreakdown card

* number formatting via validator module

* adding cossmjs math pkg

* unfinished refactor BondBreakdown

* first few ui tweaks

* Adding google font Open Sans as per designs

* DataGrid unstylable in theme so nuking in css

* adding theming to Block Height card not hardcoded colours

* DataGrid styling

* Nav styling colours but without hover fix

* theme for bond breakdown

* killing con logs

* Datagrid styling

* Nav bar working

* added lines to nav

* removed cursive from fallback fonts

* trimming and refactoring

* removed dead isActive code from nav options

* Color correction for theme on 2col table

* Moved cell styles out to UniversalDatagrid for reuseability

* Nav colors moved to theme

* Removing comments and dead code

* DataGrid UI improvements

* theming for Overview content card

* Bonds updated from UPUNK to PUNK

* corrected SVG warning on stroke-width

* added Boolean class instead of ternary

* fixing up svg attr to jsx props

* merging UPUNK changes into ui-tweaks

* corrected SVG warning on stroke-width

* added Boolean class instead of ternary

* last instance of Boolean

* BondBreakdown handles 0 delegations

* formatting for webpack config and svgs

* Add `npm run lint` and `npm run lint:fix` targets to `package.json`

* Allow `.vscode` directories - exclude them individually like has been done already in the `.gitignore` directory

* Add `vscode` action to run `eslint` on save for the `/explorer/**` sub-directory

* eslint auto fix

* Fix some easy eslint issues

* removing grid pipes and pastel map colors

* Grid xl lg values to align with Search Toolbar

* GitHub Actions: do not trigger Rust actions when the paths are only `/explorer/**`

See https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#onpushpull_requestpaths for details.

* GitHub Actions: run eslint and annotate pull requests

* socials added to Appbar and Footer

* smaller darkmode icon for mobile

* cleaner code for nav dark light selector

* almost all lint fixes

* post linting Nav fix

* killed con log and removed unused dep

* ref type and removed 1x ts ignore from worldMap

* disabl nested tern w/ nav is refactored on diff br

* icons smaller put into mui List format

* Added hover effect to match DarkLight switch

* ts ignore for worldMap vs no ts decl

* parking changes

* Flipped to MUI SVGs

* re-added external links to Socials

* nav functionality working

* spacing on Mixnode detail page

* datagrid alignment & detail page spacing

* map but no datagrid yet

* killed old SVGs now using mui icons

* added palette instead of strings

* Mixnode Map page working still needs tidying

* better lg xl responsive on Overview and sanitized Page Titles

* removed typography from imports as unused now

* search, sort working & added LG XL responsiveness

* Routing root reqs direct to Overview

* basic 404 page and btn back to overview

* killed fragments and comments

* updated Bond total in column

* Change bond col to type number not text

* Added field to DataGrid and updated MixnodeToGridrow logic

* Added type number so sorting works properly.

* added %self to Detail page

* basic scroll working desktop

* delegations now popout and scroll according to designs

* added stickyHeader and killed dead code

* ExpandMore only renders if delegations exist

* killed old svg icons

* added theme to Overview SVGs

* bringing Title into other pages

* linting fix

* pagination and spacing of gateways cols

* linting fix

* style override for pagination

* added hamburger and changed appbar to fixed

* bringing in other lint fix to pass linting

* PR feedback changes

* Add README.md for theme customisation

* Add hook to get app state context

* Add Nym theme typings to MUI `Theme` types

* Use new theme provider

* Fixing up components to use theme typings
Updated Overview Footer and ContentCard
Footer and Nav socials
Title, Nav chevron and Nav SVGs
Overview SVGs
Light Dark switch
BondBreakdown and 2 col table
DataGrid and 2col Tables
WorldMap UptimeChart and theme changes
WorldMap colors
merge changes
added StatsCard for overview

* Bug fix: do no close drawer when clicking on mixnodes or gateways

* Theme primary colour set to orange highlight, so that default/primary actions are clear to the user. This fixes colours on the pagination page list.

* Fixing up map projection

* Map view uses stroke colour from theme

* added useTheme from correct pkg

* react types upgrade to kill SXProps issue

* SXProps fix removing dead mixins from fixed AppBar

* Scale of Map changed to see more countries

* return type for main Context required

* Fixed map so more countries show

* type for useMainContext hook added

* Remove unused file

* Tidy up imports

* Remove use of `any` by using strongly typed hook to get the app state

* Remove module declaration so that @types/react-simple-maps is used

* API map response changed to indexed object

* Map view uses correct typings from `@types/react-simple-maps` and `d3-scale`

* Make content responsive and fills the view when screen widest

* Link network explorer in title to overview page

* Increase size of card headers to differentiate

* Fix column widths

* Fixed icons showing incorrectly the stats card in mixnode detail view

* Set default sort on mixnodes and gateways to be `bond` descending.

There is an error in MUI data-grid that does not adjust the sort caret based on initial `sortModel` value. Needs investigation.

* GitHub Action for deployment: prefix with network explorer to stop collisions with other deployment projects

* Mixnode list: fix up header title for `host`

* Fix up notification URLs and tidy up readme

* Fix up license information

Co-authored-by: Adrian Thompson <adrian@nymtech.net>
Co-authored-by: Adrian Thompson <adrianthompson@Adrians-MacBook-Air.local>
Co-authored-by: fmtabbara <fmtabbara@hotmail.co.uk>
Co-authored-by: Aid19801 <adrianThompson19801@gmail.com>
2021-11-08 12:23:27 +00:00
Mark Sinclair 6ad5badef4 GitHub Action to publish wallet
This is placeholder to allow the action to trigger in branches
2021-11-08 11:03:49 +00:00
Bogdan-Ștefan Neacşu 480ad18b2e Put client_address and id in the correct order (#875) 2021-11-04 16:33:18 +02:00
Jędrzej Stuczyński cdb21f418b Made daily uptime calculation be independent of epoch rewarding (#860)
* Made daily uptime calculation be independent of epoch rewarding

It was moved into a separate timer

* Don't lookup active gateways during rewarding
2021-11-04 13:06:20 +00:00
Jędrzej Stuczyński bd4c18c723 Set MSRV on all binaries to 1.56 (#872) 2021-11-03 20:07:43 +00:00
fmtabbara e7716ae852 remove gateway selection on delegation and undelegation pages 2021-11-03 15:50:36 +00:00
Jędrzej Stuczyński b19528d47e Removed gateway rewarding and delegation (#856)
* Removed gateway rewarding and delegation

* Removed redundant error variants

* [ci skip] Generate TS types

* Test fixes

Co-authored-by: jstuczyn <jstuczyn@users.noreply.github.com>
2021-11-03 15:01:59 +00:00
Tommy Verrall ef88ce9252 Merge pull request #871 from nymtech/bug-fix/keyboard-shortcuts-macos
add native window items (copy/paste) via tauri
2021-11-03 14:24:00 +00:00
Bogdan-Ștefan Neacşu 488f1c7742 Remove stale migration code (#868) 2021-11-03 16:01:13 +02:00
fmtabbara 63c698d903 add native menu items via tauri 2021-11-03 11:42:47 +00:00
Jędrzej Stuczyński 7e1c3b4501 Chore/cosmrs update (#862)
* Re-enabled `get_tx` endpoint

* Updated cosmrs to 0.3 and prost to 0.9

* [ci skip] Generate TS types

Co-authored-by: jstuczyn <jstuczyn@users.noreply.github.com>
2021-11-03 11:03:18 +00:00
Jędrzej Stuczyński e1d7772a78 Removed epoch rewarding variance (#857) 2021-11-03 11:02:44 +00:00
Tommy Verrall 9db589e0b3 Merge pull request #842 from nymtech/bug-fix/success-overflow
fix delegate success overflow
2021-11-03 10:33:31 +00:00
Tommy Verrall 57c8d69785 Merge branch 'develop' into bug-fix/success-overflow 2021-11-03 10:32:55 +00:00
Jędrzej Stuczyński 4a9cc5b757 Fixed most recent nightly clippy warnings (#865) 2021-11-02 10:48:54 +02:00
Tommy Verrall 06aac9393b Merge pull request #855 from nymtech/feature-request-template
Update feature-request template
2021-11-01 11:31:24 +00:00
Tommy Verrall f4c5b1d67f Update feature-request template 2021-11-01 11:05:54 +00:00
Tommy Verrall 2e0aba6100 Merge pull request #854 from nymtech/issue-template
Update issue templates
2021-11-01 10:51:37 +00:00
Tommy Verrall 8c6549c5dd Update report.md
consolidated the component area
2021-11-01 10:05:24 +00:00
Tommy Verrall 7b431ebfa8 Update .github/ISSUE_TEMPLATE/report.md
Co-authored-by: Mark Sinclair <mmsinclair@gmail.com>
2021-11-01 09:55:12 +00:00
Tommy Verrall 3152fd6887 Update .github/ISSUE_TEMPLATE/report.md
Co-authored-by: Mark Sinclair <mmsinclair@gmail.com>
2021-11-01 09:54:35 +00:00
Tommy Verrall e03e7f186a Delete create-an-issue.md 2021-11-01 09:47:10 +00:00
Tommy Verrall ec056a548f Delete issue-report.md 2021-11-01 09:46:59 +00:00
Tommy Verrall 24aa075a07 Update issue templates
new issue template for individuals
2021-11-01 09:41:29 +00:00
Jędrzej Stuczyński 8488c4cb0f Overflow checks in release (#846) 2021-10-28 13:07:08 +01:00
Tommy Verrall 20c96b940f Update nym_wallet.yml
check binary exists after name change
2021-10-26 14:10:23 +01:00
Tommy Verrall cfb43699d6 Update nym_wallet.yml
last check to run single test
2021-10-26 13:04:52 +01:00
Tommy Verrall 79e7b5b938 Update nym_wallet.yml
add directory
2021-10-26 12:43:44 +01:00
Tommy Verrall 47156e693c Update nym_wallet.yml
address directory
2021-10-26 12:20:36 +01:00
Mark Sinclair b61f6c8833 Update nym_wallet.yml 2021-10-26 12:32:39 +03:00
Mark Sinclair cc3b710af9 Delete nym-wallet-tests.yml 2021-10-26 12:23:06 +03:00
Mark Sinclair 4de3f16bb3 Merge pull request #841 from nymtech/feature/tauri-wallet-tests
Feature NYM wallet webdriverio test
2021-10-26 10:21:45 +01:00
fmtabbara 7aad7daa5e fix overflow 2021-10-22 17:52:15 +01:00
Tommy 4107d670cf remove stray character 2021-10-22 17:44:58 +01:00
Tommy 284e797fb3 update readme 2021-10-22 17:43:15 +01:00
Tommy 8a63df3e82 remove duplicate loglevel output 2021-10-22 17:27:32 +01:00
Tommy 7b8731ddcc line endings again 2021-10-22 17:25:56 +01:00
Tommy 1017c9c642 more line endings 2021-10-22 17:24:58 +01:00
Tommy 4e0c59a8f1 sort out line endings 2021-10-22 17:24:05 +01:00
Tommy f6d85a0784 remove empty comment 2021-10-22 17:21:25 +01:00
Tommy 6516811276 add assertion on sending funds
need to improve the calculator
2021-10-22 17:20:00 +01:00
Tommy Verrall ca0ed3594f Update nym_wallet.yml
line spacing between on push and defaults
2021-10-22 15:38:42 +01:00
Tommy Verrall a28ad4f81a Merge pull request #840 from nymtech/feature/nym-wallet-tests
Update nym_wallet.yml
2021-10-22 15:32:18 +01:00
Tommy 0ea3183288 set logging to silent 2021-10-22 14:08:54 +01:00
Tommy c6ff7c51f0 setting build path for running the binary 2021-10-22 13:55:48 +01:00
Tommy Verrall 0240ab6144 Update nym_wallet.yml
Draft at the moment whilst figuring out config data
2021-10-22 13:37:24 +01:00
Tommy 2746ec49a3 Fixing bonding tests 2021-10-22 12:29:40 +01:00
Tommy b3f1c943a7 Uncomment test script 2021-10-22 10:33:28 +01:00
Tommy 7654ede904 Update tests to use new selectors, implement reporting
. Use accessible ID's to identify elements
. Implement reporting
. Clean up 
. Input more notes in README.md
2021-10-22 10:30:17 +01:00
Tommy c882487d01 change casing on identifiers to kebab-casing 2021-10-21 11:18:45 +01:00
Tommy 865e4839f3 merging develop 2021-10-21 11:04:57 +01:00
Tommy 5a270f6489 pushing new wallet create - WIP 2021-10-21 11:02:28 +01:00
Tommy Verrall 6776bb4bab Merge pull request #836 from nymtech/feature/add-dataids-wallet
Apply readable ids to elements on Nym Wallet
2021-10-21 10:28:36 +01:00
Tommy cbe9bad66e Apply readable test-dataIds to elements on Nym Wallet
To easily identify elements on the wallet to help with automation.
2021-10-20 16:39:30 +01:00
Tommy Verrall 5761e1c1a4 Merge pull request #831 from nymtech/update/use-nym-icons
update app name and icons
2021-10-19 14:21:51 +01:00
Mark Sinclair 062a5edf76 Merge pull request #806 from nymtech/bugfix/network-explorer-api-geoip
Bug fix: Network Explorer: Add freegeoip API key and split out tasks for country distributions
2021-10-19 11:53:22 +01:00
Jędrzej Stuczyński a009e76568 Feature locked client-side bandwidth metering (#820) 2021-10-19 11:30:34 +02:00
Jędrzej Stuczyński dcc9b856d7 Flag to only run coconut-related functionalities (#824) 2021-10-19 11:30:07 +02:00
Mark Sinclair 592e52b333 Hush nightly clippy, hush 2021-10-19 10:20:00 +01:00
Mark Sinclair bc923be862 Update README 2021-10-19 10:20:00 +01:00
Mark Sinclair e880631500 Remove unused imports 2021-10-19 10:20:00 +01:00
Mark Sinclair d9b0834476 Review feedback 2021-10-19 10:20:00 +01:00
Mark Sinclair 2fda22e168 Run new tasks at startup 2021-10-19 10:20:00 +01:00
Mark Sinclair a5091cd124 Split refreshing of mixnode cache into a separate task 2021-10-19 10:20:00 +01:00
Mark Sinclair b2ec19ece4 Split geolcation into its own task the picks off mixnodes that don't have locations or ones where the cache TTL has expired 2021-10-19 10:20:00 +01:00
Mark Sinclair 72e64cfab7 Split the mixnode country distribution calculation into its own task and get data from the location cache 2021-10-19 10:20:00 +01:00
Mark Sinclair 7eacb6b57e Add TTL to location cache 2021-10-19 10:20:00 +01:00
Mark Sinclair e0e1ca992f Move locations to their own cache 2021-10-19 10:20:00 +01:00
Mark Sinclair f1a628dee4 Change freegeoip API hostname 2021-10-19 10:20:00 +01:00
Mark Sinclair 81d49dc4b9 Add thiserror crate 2021-10-19 10:20:00 +01:00
Mark Sinclair 7e581183f4 Network Explorer API: add env var GEO_IP_SERVICE_API_KEY to provide an API key for https://freegeoip.app/ 2021-10-19 10:20:00 +01:00
fmtabbara a9f3fa0c72 update app name and icons 2021-10-19 09:08:07 +01:00
Tommy e6c5a97f8c set to silent logging 2021-10-18 12:18:18 +01:00
Tommy efb9b70293 merge develop 2021-10-18 12:17:44 +01:00
Tommy 666c233d6c remove workflow 2021-10-18 12:14:57 +01:00
Mark Sinclair 4307756719 Network Explorer: GitHub Actions
- change notification channel
- remove trigger on PR
2021-10-18 12:05:21 +01:00
Mark Sinclair 622d0d3ff9 Merge pull request #829 from nymtech/nym-wallet-test-yml
Create nym-wallet-tests.yml
2021-10-18 11:22:54 +01:00
Tommy Verrall ad763c2131 Update nym_wallet.yml
remove clippy
2021-10-18 11:18:59 +01:00
Jędrzej Stuczyński 87a2ec9468 Updated CODEOWNERS (#828) 2021-10-18 08:41:27 +01:00
Tommy dd590e8779 Update Readme for windows. 2021-10-15 18:17:51 +01:00
Tommy 9555f3a2cd testing tauri-wallet-tests.yml 2021-10-15 18:06:24 +01:00
Tommy Verrall 0b79fc33d7 nym-wallet-tests.yml 2021-10-15 18:05:16 +01:00
Tommy Verrall b7e14489da Create nym-wallet-tests.yml
action for building and running the tauri-wallet-tests
2021-10-15 17:46:53 +01:00
Tommy 84b426042e wrong workflow check in 2021-10-15 17:24:35 +01:00
Tommy f683b9e770 Tauri Wallet Test
. Currently developed against a Linux machine
. POM design
. README.md states how to get up and running on Linux 
WIP - areas of focus inbound ->
. CI
. Test suite configuration
. Working on multiple platforms
. Improve selector selection to reduce fragility in the future
2021-10-15 17:05:00 +01:00
Fouad 5fb2c9d822 Merge pull request #827 from nymtech/tauri-wallet-patch
Tauri wallet
2021-10-15 15:19:01 +01:00
Jędrzej Stuczyński 0f7280c227 Fixed most recent nightly clippy warnings (#817)
* Fixed most recent nightly clippy warnings

* Missing wasm32 changes
2021-10-15 08:57:16 +01:00
mmsinclair 4ebca86a66 [ci skip] Generate TS types 2021-10-14 15:41:13 +00:00
fmtabbara 1a0c6eed27 Tauri wallet 2021-10-14 16:30:09 +01:00
Bogdan-Ștefan Neacşu 673e13ec57 Change false to true, as for mixnodes (#822) 2021-10-13 19:28:47 +03:00
Jędrzej Stuczyński 7caac334f4 Feature/resending rewards on timeout (#810)
* Additional methods on nymd client

* Checking for time out errors

* Attempting retransmission on suspected time out

* Don't report error if tx is a mempool duplicate
2021-10-11 10:48:59 +01:00
Jędrzej Stuczyński 4e0e081b3e Feature/coconut feature (#805)
* 'Coconut' feature in gateway

* Enabled coconut feature in gateway-requests

* Native client coconut feature

* Ibid for socks5 client

* Ibid for wasm client

* Coconut feature flag for validator-api

* Added coconut feature flag to our CI

* build.yml typo

* Continue on windows errors

* Missing quote

* Another typo in build.yml

* Reclaiming disk space when building for windows on CI
2021-10-11 09:17:58 +01:00
Jędrzej Stuczyński 51dc8c81ed Rocket picking up environment from Rocket.toml again (#801)
* Rocket picking up environment from Rocket.toml again

* Removed unused import
2021-10-08 14:33:29 +01:00
Max H 727eedf7d3 updated docs link 2021-10-05 13:34:18 +02:00
Mark Sinclair e37fddf3ab Network Explorer: GitHub Actions - notification channel 2021-10-05 11:53:17 +01:00
Mark Sinclair 885679e575 Network Explorer: GitHub Actions - notification channel 2021-10-05 11:46:41 +01:00
Mark Sinclair 36554c3c34 Network Explorer: GitHub Actions - notification messages 2021-10-05 11:39:33 +01:00
Mark Sinclair 2197062372 Network Explorer: GitHub Actions - notification messages 2021-10-04 11:22:37 +01:00
Mark Sinclair 487b894f3d Network Explorer: GitHub Actions - send message content via stdin 2021-10-04 10:53:39 +01:00
Mark Sinclair a03603b274 Network Explorer: GitHub Actions - fix JSON quote escaping 2021-10-04 10:45:02 +01:00
Mark Sinclair e6879dba63 Network Explorer: GitHub Actions - handle env differently 2021-10-01 15:47:57 +01:00
Mark Sinclair 68a43e33b9 Network Explorer: GitHub Actions - change notifications to be generated from template files 2021-10-01 15:44:26 +01:00
Bogdan-Ștefan Neacşu db2ab41636 Remove migration code (#796) 2021-10-01 16:55:48 +03:00
Mark Sinclair c624327a7a Network Explorer: GitHub Action - add keybase notifications 2021-10-01 14:28:13 +01:00
Mark Sinclair 32ff5cbc1e Network Explorer: GitHub Actions - fix dist path 2021-09-30 16:02:51 +01:00
Mark Sinclair 9ba203c7b3 Network Explorer: GitHub Actions: rsync install 2021-09-30 15:52:02 +01:00
Mark Sinclair 729625ef4f Network Explorer: GitHub Action: install rsync 2021-09-30 15:49:39 +01:00
Mark Sinclair fd3923a5f9 Network Explorer: GitHub Actions - disable tests 2021-09-30 15:37:52 +01:00
Mark Sinclair c2fd07ea18 Fix typo 2021-09-30 15:29:23 +01:00
Mark Sinclair d76a2d84b5 Add GitHub Action to build Network Explorer 2021-09-30 15:19:23 +01:00
Bogdan-Ștefan Neacşu 2a98f7482d Feature/migrate hidden delegations (#786)
* Remove migration code

* Added function to iterate over delegation of variable type

* Add unit tests

* Refactored some naming and reused mix/gateway functionality

* Borrow bucket instead of move

* Linked with existing delegations function

* Migration of left-over delegations

* Remove unused imports

* Put a gateway test as well, next to the mix one

* Expose queries for all delegations

* Change break point

* Added client side calls to the new queries

* Fix clippy

* Added pagination and read check tests

* Fix gateway test from the last commit

* Test functions for (de)serialization of identity and owner (in)to storage keys

* Add delegation function unit test

* Feature guard import

* Changed UnpackedDelegation from type to struct

* Remove mutable parameter and put start_after in returned value

* Made all delegations into iterator for OOM safety

* Fix clippy

* Add test for delegations iterator size in memory

* Change map with if let for ease of read

* Use DENOM instead of hardcoded value
2021-09-30 15:04:33 +03:00
Jędrzej Stuczyński 12637d93ff Feature/persistent gateway storage (#784)
* Sqlx struct stub

* Initial schema

* Initial error enum

* Managed for persisted shared keys

* Initial inbox manager

* Comments

* Using new database in clients handler

* Extending gateway storage API

* tokio::main + placeholder values

* Removed old client store

* Simplified logic of async packet processing

* Renamed table + not null restriction

* BandwidthManager

* Removed sled dependency

* Using centralised storage for bandwidth

* Dead code removal

* WIP connection_handler split and simplification

Maybe it doesn't look like it right now, but once completed it will remove bunch of redundant checks for Nones etc

* Further more explicit clients handler split

* Minor cleanup

* Temporary store for active client handles

* Fixed error types

* Error trait on iv and encrypted address

* Authentication and registration moved to the handler

* Removal of clients handler

* Further logic simplification + returned explicit bandwidth values

* Further cleanup and comments

* Updated config with relevant changes

* Basic bandwidth tracking in client

* FreshHandle doc comments + fixed stagger issue

* Removed side-effects from .map

* More doc comments

* Database migration on build

* Increased default claimed bandwidth

* Renaming

* Fixed client determining available bandwidth

* Removed dead sql table that might be used in the future

* Windows workaround

* Comment

* Return error rather than cap credential
2021-09-30 09:45:21 +01:00
Jędrzej Stuczyński f63aba9058 Removes code of executed migrations (#793) 2021-09-28 10:13:05 +01:00
Jędrzej Stuczyński 668325a4ce Feature/active sets (#764)
* Added separate gateway active set size

* Grabbing contract state

* Defined PartialOrd on MixnodeBond and GatewayBond

* Some initial stub for active set

* Unit tests for mixnode and gateway bond partialord implementation

* Obtaining active sets

* Active nodes routes

* Additional methods on validator client

* Added state migration

* Feature locking unused import

* Fixed State test fixture

* Included block height for partial_ord

* Missing post-merge imports

* api on the client for active nodes

* Native/socks5/wasm clients using active nodes

* Rewarding only active nodes

* Updated validator client StateParams definition

* Gateway active set size

* Contract migration update

* Cargo fmt

* Updated TauriStateParams

* [ci skip] Generate TS types

Co-authored-by: jstuczyn <jstuczyn@users.noreply.github.com>
2021-09-28 09:38:22 +01:00
Jędrzej Stuczyński e891c68158 Created getters for AccountData (#787) 2021-09-27 12:16:16 +02:00
Jędrzej Stuczyński b1fb032f43 Removed SQLx offline mode artifact (#790) 2021-09-27 12:06:13 +02:00
Jędrzej Stuczyński 23ea82952e Bugfix/validator api windows build (#791)
* Use https://github.com/nymtech/nym/pull/784/commits/3eceb349c695372cbb7a3bee9aa5e090da7c9c4d trick to make build.rs work with windows

* Re-enabled validator-api windows CI build
2021-09-27 12:05:53 +02:00
Drazen Urch 9a65e44166 Add Tauri wallet
--
Squashed commit of the following:

commit 3f4f9073b7
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Mon Sep 27 11:38:12 2021 +0200

    Delete .lock for merge

commit bf502f6338
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Mon Sep 27 11:36:51 2021 +0200

    Squashed commit of the following:

    commit 020cad897d
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Fri Sep 24 15:49:21 2021 +0100

        Feature/rust rewarding (#750)

        * Calculating gas fees

        * Ability to set custom fees

        * Added extra test

        * Removed commented code

        * Moved all msg types to common contract crate

        * Temporarily disabling get_tx method

        * Finishing up nymd client API

        * Comment fix

        * Remaining fee values

        * Some cleanup

        * Removed needless borrow

        * Fixed imports in contract tests

        * Moved error types around

        * New ValidatorClient

        * Experiment with new type of defaults

        * Removed dead module

        * Dealt with unwrap

        * Migrated mixnode to use new validator client

        * Migrated gateway to use new validator client

        * Mixnode and gateway adjustments

        * More exported defaults

        * Clients using new validator client

        * Fixed mixnode upgrade

        * Moved default values to a new crate

        * Changed behaviour of validator client features

        * Migrated basic functions of validator api

        * Updated config + fixed startup

        * Fixed wasm client build

        * Integration with the explorer api

        * Removed tokio dev dependency

        * Needless borrow

        * Fixex wasm client build

        * Fixed tauri client build

        * Needless borrows

        * New tables for rewarding

        * Updated cosmos-sdk version

        * Removed reward-specific node status routes

        * New rewarding-specific config entries

        * Additional network defaults

        * Initial periodic rewards from validator api

        * Replaced print with log

        * Filtering nodes with uptime > 0

        * Additional failure logging statements

        * Fixed operation ordering

        * Adjusted next rewarding epoch determination

        * Modified rewarding behaviour to keep track of rewarding in progress

        * Improved error message on config load failure

        * Additional log statement

        * Adjusted rewarding gas limit calculation

        * Made naming slightly more consistent

        * Fixed incorrect parentheses placement

        * Fixed fee calculation

        * Cargo fmt

        * Removed failed merge artifacts

        * Introduced comment for any future reward modification

        * typos

        * Helper functions for the future

        * Making @mfahampshire 's life easier

        * Redesigned epoch + rewarding skipped epochs (if possible)

        * Removed old merge artifacts

        * Naming consistency

        * Constraining arguments

        * Removed unnecessary if branch

        * Ignore monitor check for current epoch

        * Additional checks for current epoch data

        * Monitor threshold check

        * cargo fmt

        * Fixed post-merge issues in transactions.rs

    commit 5dfaff6296
    Author: Drazen Urch <drazen@urch.eu>
    Date:   Tue Sep 21 10:59:17 2021 +0200

        Update hmac and blake3 (#673)

        * Update hmac and blake3

        * Remain paranoid for `0.*` crates

        * Most paranoid versions :)

        * Updated aes and using published version of blake3

        Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>

    commit 5b03982043
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Mon Sep 20 10:40:03 2021 +0100

        Replaced unwrap_or_else with unwrap_or_default (#780)

    commit 8f6856d6fb
    Author: Drazen Urch <drazen@urch.eu>
    Date:   Mon Sep 20 11:15:06 2021 +0200

        Make fee helpers public (#777)

        Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>

commit 80664b911f
Merge: f66ea059 46149012
Author: Fouad <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 23 20:59:27 2021 +0100

    Merge pull request #783 from nymtech/tauri-wallet-frontend

    tauri wallet front-end

commit 46149012bd
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 23 17:42:34 2021 +0100

    PR updates

commit de601c319a
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 23 17:06:47 2021 +0100

    PR updates

commit 7318de23f2
Author: max <mfahampshire@gmail.com>
Date:   Wed Sep 22 18:07:29 2021 +0200

    added info on nym-wallet (tauri) in readme

commit 56e07753ea
Author: max <mfahampshire@gmail.com>
Date:   Wed Sep 22 14:44:22 2021 +0200

    added minimal readme for wallet

commit 21b008fae9
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Sep 13 13:30:05 2021 +0100

    use wrapper functions for send and delegate

    form updates

    update working

    finish bonding and unbonding setup

    funds allocation check when bonding/sending/delegating

    update title

commit 27a202cbe8
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 17 11:32:41 2021 +0100

    integrate admin functions

    updates

    integrate admin form update function

commit 085538582b
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Fri Sep 17 10:43:26 2021 +0200

    Admin functions, reorganize code

commit f66ea05929
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Fri Sep 17 10:43:26 2021 +0200

    Admin functions, reorganize code

commit 052c7188ec
Merge: f6c316ee 976dd7aa
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 17 09:38:19 2021 +0100

    merge develop

commit f6c316eea9
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Thu Sep 16 18:02:15 2021 +0200

    fix typo

commit f33defc645
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Thu Sep 16 17:36:24 2021 +0200

    Squashed commit of the following:

    commit 976dd7aae2
    Author: Drazen Urch <drazen@urch.eu>
    Date:   Wed Sep 15 17:28:49 2021 +0200

        Add block_height method to Delegation (#778)

        Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>

    commit 0d21f4e937
    Merge: e84af4f6 1403449a
    Author: Fouad <fmtabbara@hotmail.co.uk>
    Date:   Wed Sep 15 12:41:29 2021 +0100

        Merge pull request #776 from nymtech/update/re-enable-bonding

        re-enable bonding

    commit 1403449ad5
    Author: fmtabbara <fmtabbara@hotmail.co.uk>
    Date:   Tue Sep 14 16:00:21 2021 +0100

        enable bonding

    commit e84af4f601
    Author: Drazen Urch <drazen@urch.eu>
    Date:   Tue Sep 14 15:15:26 2021 +0200

        Migrate legacy delegation data (#771)

        * Skip ReadOnlyBucket deserialization errors

        * empty migration

        * clippy

        * cargo schema

        * Drop invalid delegation data

        * Dont drop old data

        * Add todo

        * Unify on type param

        * gateways are different

        * cargo fmt

        Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>

    commit 26b032c15c
    Merge: e1ddaff0 cba36253
    Author: Mark Sinclair <mmsinclair@gmail.com>
    Date:   Tue Sep 14 10:09:14 2021 +0100

        Merge pull request #774 from nymtech/feature/explorer-api-delegations

        Explorer-api: add API resource to show the delegations for each mix node

    commit cba3625394
    Author: Mark Sinclair <mmsinclair@gmail.com>
    Date:   Mon Sep 13 10:33:41 2021 +0100

        explorer-api: add API resource to show the delegations for each mix node

    commit e1ddaff04d
    Merge: 0b9c03ca 66ab5de4
    Author: Fouad <fmtabbara@hotmail.co.uk>
    Date:   Fri Sep 10 17:17:14 2021 +0100

        Merge pull request #772 from nymtech/update/disable-bonding

        add app alert

    commit 66ab5de442
    Author: fmtabbara <fmtabbara@hotmail.co.uk>
    Date:   Fri Sep 10 16:16:58 2021 +0100

        add app alert

    commit 0b9c03ca90
    Author: Dave Hrycyszyn <futurechimp@users.noreply.github.com>
    Date:   Fri Sep 10 11:23:21 2021 +0300

        Adding deps for building the Tauri wallet under Ubuntu (#770)

    commit c9dce0c1da
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Thu Sep 9 11:21:45 2021 +0200

        Feature/consumable bandwidth (#766)

        * Set actual value for bandwidth

        Also put it as a public attribute, such that it can be actively used
        by the credential consumer

        * Switch from sending Attribute structs to sending the actual attribute bytes over the wire

        * Add atomic bandwidth value to gateway

        * Consume bandwidth based on the mix packet size

        * Use Bandwidth struct for specific functionality

        * Move bandwidth code outside the dependency path of wasm client

        * Use u64 instead of AtomicU64, as the handling is not parallel

    commit e00e77db15
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Wed Sep 8 15:07:24 2021 +0200

        Feature/bond blockstamp (#760)

        * Add block_height to MixNode/GatewayBond

        * Reward based on blockstamp of bonded node or of delegation

        * Add specific tests

        * Add migration code

        * Apply doc nit

    commit 1074449f91
    Merge: 08276e6e 9a3d824a
    Author: Fouad <fmtabbara@hotmail.co.uk>
    Date:   Tue Sep 7 23:45:10 2021 +0100

        Merge pull request #767 from nymtech/update/remove-app-alert

        remove alert

    commit 08276e6e42
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Tue Sep 7 16:33:30 2021 +0200

        Remove migration code (#759)

    commit 9a3d824a4a
    Author: fmtabbara <fmtabbara@hotmail.co.uk>
    Date:   Mon Sep 6 20:39:24 2021 +0100

        remove alert

    commit 2789ee8f18
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Fri Sep 3 15:30:45 2021 +0300

        Update coconut-rs and use hash_to_scalar from there (#765)

        Failed tests are due to some nightly issue, not related to this PR

    commit a7ba643c35
    Merge: 28be53ee c42f3c68
    Author: Fouad <fmtabbara@hotmail.co.uk>
    Date:   Fri Sep 3 09:14:50 2021 +0100

        Merge pull request #762 from nymtech/feature/app-alert

        add app alert banner

    commit 28be53eefb
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Thu Sep 2 18:26:40 2021 +0300

        Add block_height in the Delegation structure as well (#757)

    commit 219c45a352
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Thu Sep 2 15:48:29 2021 +0100

        Updated cosmos-sdk (#761)

        * Updated cosmos-sdk

        * Re-exposing more things

    commit 1a3b83752e
    Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    Date:   Thu Sep 2 15:45:56 2021 +0100

        Bump next from 11.1.0 to 11.1.1 in /wallet-web (#758)

        Bumps [next](https://github.com/vercel/next.js) from 11.1.0 to 11.1.1.
        - [Release notes](https://github.com/vercel/next.js/releases)
        - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
        - [Commits](https://github.com/vercel/next.js/compare/v11.1.0...v11.1.1)

        ---
        updated-dependencies:
        - dependency-name: next
          dependency-type: direct:production
        ...

        Signed-off-by: dependabot[bot] <support@github.com>

        Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

    commit c42f3c6844
    Author: fmtabbara <fmtabbara@hotmail.co.uk>
    Date:   Thu Sep 2 12:29:47 2021 +0100

        add app alert banner

    commit a5d3ba3900
    Merge: 92e13a5d cdf0d443
    Author: Mark Sinclair <mmsinclair@gmail.com>
    Date:   Tue Aug 31 15:59:11 2021 +0100

        Merge pull request #755 from nymtech/bugfix/explorer-api-ping

        Explorer API: port test now split out address resolution and add units tests

    commit cdf0d44341
    Author: Mark Sinclair <mmsinclair@gmail.com>
    Date:   Tue Aug 31 14:37:28 2021 +0100

        explorer-api: turned down logging from `error` to `warn`

    commit 92f976a45d
    Author: Mark Sinclair <mmsinclair@gmail.com>
    Date:   Fri Aug 27 11:34:21 2021 +0100

        explorer-api: sanitize hostname before running checks to avoid leading or trailing spaces that are known to exist in the current test net

    commit 2bc858cde3
    Author: Mark Sinclair <mmsinclair@gmail.com>
    Date:   Fri Aug 27 09:32:26 2021 +0100

        explorer-api: port test: split out address resolution and add units tests

    commit 92e13a5d00
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Tue Aug 31 14:51:15 2021 +0300

        Feature/add blockstamp (#756)

        * Add RawDelegationData

        * Fix current tests for the new stored data

        * Added migration commit. Will be reverted after doing the migration

        * New tests for block height

        * Use current blockstamp instead of 24h old one

        * Put _alot_ of migration stuff in the migrate function scope

    commit 122f5d9f2e
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Mon Aug 30 10:27:20 2021 +0300

        Feature/cred after handshake (#745)

        * Call perform_initial_authentication instead of register in clients

        * Refactor the register/authenticate functions a bit

        * Introduce Bandwidth request type

        * Add encryption layer to cred

        * Remove cred pass and check from handshake

        * Replaced unreachable!  with error

        * Changed decrypt_tagged signature to not take mutable ownership of data

        * Put handle_bandwidth work inside a function

        * Add check before unwrap

        * Remove unnecessary async

        * Decouple bandwidth credential from authentication

        * Use new_error for ServerResponse:Error

        * Send a fresh IV each time the BandwidthCredential request is sent

        * Remove unwrap of bincode::serialize

        * Add comment regarding Bandwidth response

        * Remove _mut from naming

        * Leave Debug trait alone, as the initial error doesn't reproduce anymore

        * Pass iv as Vec<u8> instead of base58 string

        * Renamed AuthenticationIV to IV, as it is now used for more the just authentication

        * Did some IV refactorization

    commit 982ee0266c
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Fri Aug 27 16:02:34 2021 +0300

        Feature/get own delegations (#748)

        * Introduce reverse delegation bucket

        * Add client command

        * Fix clippy error

        * Added tests in queries

        * Add tests in transactions

        * Migration code. Will be reverted after it's called on testnet

        * Replace unwrap with expect

        * Move some test code in the right file...

        ... to remove unnecessary auxiliary function.

        * Reduce the scope to migration auxiliary functions

        * Rename everything from [node]reverse to reverse[node]

        * Fix fmt

    commit 5f42a9bd05
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Fri Aug 27 13:52:18 2021 +0100

        NetworkMonitorBuilder - starting the monitor after rocket has launched (#754)

        * NetworkMonitorBuilder - starting the monitor after rocket has launched

        * Removed unused import

    commit 1811df9ddb
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Fri Aug 27 13:52:10 2021 +0100

        Enabled validators api argument (#753)

    commit 6bdfe7f895
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Thu Aug 26 11:21:01 2021 +0100

        Correctly bounding nominator of uptime calculation (#752)

    commit c6b286a1db
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Wed Aug 25 14:50:57 2021 +0100

        Fixed argument parsing for ipv6 'good' topology (#751)

    commit b3568a26f5
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Tue Aug 24 11:25:05 2021 +0300

        Revert "Migration commit, will be reverted after the testnet contract is updated" (#749)

        This reverts commit 38d868bcce.

    commit 15ae0f521e
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Mon Aug 23 10:26:51 2021 +0100

        Feature/more reliable uptime calculation (#747)

        * New database table holding monitor run info

        * SQL interface for new table

        * Updated uptime calculation to instead rely on number of monitor test runs

    commit 2923d4b872
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Thu Aug 19 22:03:07 2021 +0300

        Update template toml key (#746)

commit 424230c3bb
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Thu Sep 16 17:33:20 2021 +0200

    Squashed commit of the following:

    commit cddd9e8e4c
    Merge: 40fbdff0 976dd7aa
    Author: Drazen Urch <durch@users.noreply.guthub.com>
    Date:   Thu Sep 16 17:27:27 2021 +0200

        Merge branch 'develop' into tauri-wallet

    commit 976dd7aae2
    Author: Drazen Urch <drazen@urch.eu>
    Date:   Wed Sep 15 17:28:49 2021 +0200

        Add block_height method to Delegation (#778)

        Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>

    commit 0d21f4e937
    Merge: e84af4f6 1403449a
    Author: Fouad <fmtabbara@hotmail.co.uk>
    Date:   Wed Sep 15 12:41:29 2021 +0100

        Merge pull request #776 from nymtech/update/re-enable-bonding

        re-enable bonding

    commit 1403449ad5
    Author: fmtabbara <fmtabbara@hotmail.co.uk>
    Date:   Tue Sep 14 16:00:21 2021 +0100

        enable bonding

    commit e84af4f601
    Author: Drazen Urch <drazen@urch.eu>
    Date:   Tue Sep 14 15:15:26 2021 +0200

        Migrate legacy delegation data (#771)

        * Skip ReadOnlyBucket deserialization errors

        * empty migration

        * clippy

        * cargo schema

        * Drop invalid delegation data

        * Dont drop old data

        * Add todo

        * Unify on type param

        * gateways are different

        * cargo fmt

        Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>

    commit 26b032c15c
    Merge: e1ddaff0 cba36253
    Author: Mark Sinclair <mmsinclair@gmail.com>
    Date:   Tue Sep 14 10:09:14 2021 +0100

        Merge pull request #774 from nymtech/feature/explorer-api-delegations

        Explorer-api: add API resource to show the delegations for each mix node

    commit cba3625394
    Author: Mark Sinclair <mmsinclair@gmail.com>
    Date:   Mon Sep 13 10:33:41 2021 +0100

        explorer-api: add API resource to show the delegations for each mix node

    commit e1ddaff04d
    Merge: 0b9c03ca 66ab5de4
    Author: Fouad <fmtabbara@hotmail.co.uk>
    Date:   Fri Sep 10 17:17:14 2021 +0100

        Merge pull request #772 from nymtech/update/disable-bonding

        add app alert

    commit 66ab5de442
    Author: fmtabbara <fmtabbara@hotmail.co.uk>
    Date:   Fri Sep 10 16:16:58 2021 +0100

        add app alert

    commit 0b9c03ca90
    Author: Dave Hrycyszyn <futurechimp@users.noreply.github.com>
    Date:   Fri Sep 10 11:23:21 2021 +0300

        Adding deps for building the Tauri wallet under Ubuntu (#770)

    commit c9dce0c1da
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Thu Sep 9 11:21:45 2021 +0200

        Feature/consumable bandwidth (#766)

        * Set actual value for bandwidth

        Also put it as a public attribute, such that it can be actively used
        by the credential consumer

        * Switch from sending Attribute structs to sending the actual attribute bytes over the wire

        * Add atomic bandwidth value to gateway

        * Consume bandwidth based on the mix packet size

        * Use Bandwidth struct for specific functionality

        * Move bandwidth code outside the dependency path of wasm client

        * Use u64 instead of AtomicU64, as the handling is not parallel

    commit e00e77db15
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Wed Sep 8 15:07:24 2021 +0200

        Feature/bond blockstamp (#760)

        * Add block_height to MixNode/GatewayBond

        * Reward based on blockstamp of bonded node or of delegation

        * Add specific tests

        * Add migration code

        * Apply doc nit

    commit 1074449f91
    Merge: 08276e6e 9a3d824a
    Author: Fouad <fmtabbara@hotmail.co.uk>
    Date:   Tue Sep 7 23:45:10 2021 +0100

        Merge pull request #767 from nymtech/update/remove-app-alert

        remove alert

    commit 08276e6e42
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Tue Sep 7 16:33:30 2021 +0200

        Remove migration code (#759)

    commit 9a3d824a4a
    Author: fmtabbara <fmtabbara@hotmail.co.uk>
    Date:   Mon Sep 6 20:39:24 2021 +0100

        remove alert

    commit 2789ee8f18
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Fri Sep 3 15:30:45 2021 +0300

        Update coconut-rs and use hash_to_scalar from there (#765)

        Failed tests are due to some nightly issue, not related to this PR

    commit a7ba643c35
    Merge: 28be53ee c42f3c68
    Author: Fouad <fmtabbara@hotmail.co.uk>
    Date:   Fri Sep 3 09:14:50 2021 +0100

        Merge pull request #762 from nymtech/feature/app-alert

        add app alert banner

    commit 28be53eefb
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Thu Sep 2 18:26:40 2021 +0300

        Add block_height in the Delegation structure as well (#757)

    commit 219c45a352
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Thu Sep 2 15:48:29 2021 +0100

        Updated cosmos-sdk (#761)

        * Updated cosmos-sdk

        * Re-exposing more things

    commit 1a3b83752e
    Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    Date:   Thu Sep 2 15:45:56 2021 +0100

        Bump next from 11.1.0 to 11.1.1 in /wallet-web (#758)

        Bumps [next](https://github.com/vercel/next.js) from 11.1.0 to 11.1.1.
        - [Release notes](https://github.com/vercel/next.js/releases)
        - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
        - [Commits](https://github.com/vercel/next.js/compare/v11.1.0...v11.1.1)

        ---
        updated-dependencies:
        - dependency-name: next
          dependency-type: direct:production
        ...

        Signed-off-by: dependabot[bot] <support@github.com>

        Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

    commit c42f3c6844
    Author: fmtabbara <fmtabbara@hotmail.co.uk>
    Date:   Thu Sep 2 12:29:47 2021 +0100

        add app alert banner

    commit a5d3ba3900
    Merge: 92e13a5d cdf0d443
    Author: Mark Sinclair <mmsinclair@gmail.com>
    Date:   Tue Aug 31 15:59:11 2021 +0100

        Merge pull request #755 from nymtech/bugfix/explorer-api-ping

        Explorer API: port test now split out address resolution and add units tests

    commit cdf0d44341
    Author: Mark Sinclair <mmsinclair@gmail.com>
    Date:   Tue Aug 31 14:37:28 2021 +0100

        explorer-api: turned down logging from `error` to `warn`

    commit 92f976a45d
    Author: Mark Sinclair <mmsinclair@gmail.com>
    Date:   Fri Aug 27 11:34:21 2021 +0100

        explorer-api: sanitize hostname before running checks to avoid leading or trailing spaces that are known to exist in the current test net

    commit 2bc858cde3
    Author: Mark Sinclair <mmsinclair@gmail.com>
    Date:   Fri Aug 27 09:32:26 2021 +0100

        explorer-api: port test: split out address resolution and add units tests

    commit 92e13a5d00
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Tue Aug 31 14:51:15 2021 +0300

        Feature/add blockstamp (#756)

        * Add RawDelegationData

        * Fix current tests for the new stored data

        * Added migration commit. Will be reverted after doing the migration

        * New tests for block height

        * Use current blockstamp instead of 24h old one

        * Put _alot_ of migration stuff in the migrate function scope

    commit 122f5d9f2e
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Mon Aug 30 10:27:20 2021 +0300

        Feature/cred after handshake (#745)

        * Call perform_initial_authentication instead of register in clients

        * Refactor the register/authenticate functions a bit

        * Introduce Bandwidth request type

        * Add encryption layer to cred

        * Remove cred pass and check from handshake

        * Replaced unreachable!  with error

        * Changed decrypt_tagged signature to not take mutable ownership of data

        * Put handle_bandwidth work inside a function

        * Add check before unwrap

        * Remove unnecessary async

        * Decouple bandwidth credential from authentication

        * Use new_error for ServerResponse:Error

        * Send a fresh IV each time the BandwidthCredential request is sent

        * Remove unwrap of bincode::serialize

        * Add comment regarding Bandwidth response

        * Remove _mut from naming

        * Leave Debug trait alone, as the initial error doesn't reproduce anymore

        * Pass iv as Vec<u8> instead of base58 string

        * Renamed AuthenticationIV to IV, as it is now used for more the just authentication

        * Did some IV refactorization

    commit 982ee0266c
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Fri Aug 27 16:02:34 2021 +0300

        Feature/get own delegations (#748)

        * Introduce reverse delegation bucket

        * Add client command

        * Fix clippy error

        * Added tests in queries

        * Add tests in transactions

        * Migration code. Will be reverted after it's called on testnet

        * Replace unwrap with expect

        * Move some test code in the right file...

        ... to remove unnecessary auxiliary function.

        * Reduce the scope to migration auxiliary functions

        * Rename everything from [node]reverse to reverse[node]

        * Fix fmt

    commit 5f42a9bd05
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Fri Aug 27 13:52:18 2021 +0100

        NetworkMonitorBuilder - starting the monitor after rocket has launched (#754)

        * NetworkMonitorBuilder - starting the monitor after rocket has launched

        * Removed unused import

    commit 1811df9ddb
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Fri Aug 27 13:52:10 2021 +0100

        Enabled validators api argument (#753)

    commit 6bdfe7f895
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Thu Aug 26 11:21:01 2021 +0100

        Correctly bounding nominator of uptime calculation (#752)

    commit c6b286a1db
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Wed Aug 25 14:50:57 2021 +0100

        Fixed argument parsing for ipv6 'good' topology (#751)

    commit b3568a26f5
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Tue Aug 24 11:25:05 2021 +0300

        Revert "Migration commit, will be reverted after the testnet contract is updated" (#749)

        This reverts commit 38d868bcce.

    commit 15ae0f521e
    Author: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
    Date:   Mon Aug 23 10:26:51 2021 +0100

        Feature/more reliable uptime calculation (#747)

        * New database table holding monitor run info

        * SQL interface for new table

        * Updated uptime calculation to instead rely on number of monitor test runs

    commit 2923d4b872
    Author: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
    Date:   Thu Aug 19 22:03:07 2021 +0300

        Update template toml key (#746)

commit cddd9e8e4c
Merge: 40fbdff0 976dd7aa
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Thu Sep 16 17:27:27 2021 +0200

    Merge branch 'develop' into tauri-wallet

commit 63bc42cd5f
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 16 10:01:53 2021 +0100

    update title

commit 9dc8ba7b77
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Sep 15 21:39:18 2021 +0100

    funds allocation check when bonding/sending/delegating

commit e130131f16
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Sep 15 20:37:24 2021 +0100

    finish bonding and unbonding setup

commit 64a5b4b593
Merge: 9934b9bc 40fbdff0
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Sep 14 12:08:38 2021 +0100

    Merge branch 'tauri-wallet' into tauri-wallet-frontend
    rust updates

commit 40fbdff05a
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Tue Sep 14 10:29:31 2021 +0200

    Coin and denom tests

commit 9934b9bc8a
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Sep 13 15:20:56 2021 +0100

    update working

commit 6954b383a7
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Sep 13 14:26:21 2021 +0100

    form updates

commit 4b5276e816
Merge: 56a95274 0278bd2c
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Sep 13 13:32:24 2021 +0100

    Merge branch 'tauri-wallet' into tauri-wallet-frontend
    rust updates

commit 0278bd2c26
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Mon Sep 13 14:31:33 2021 +0200

    Fix coin to cosmwasm coin

commit 56a9527497
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Sep 13 13:30:05 2021 +0100

    use wrapper functions for send and delegate

commit a601c28a20
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Mon Sep 13 13:07:57 2021 +0200

    Return useful info from bond/unbond

commit 005dd7513b
Merge: 3ebdc558 aaf5d186
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Mon Sep 13 12:58:01 2021 +0200

    Merge branch 'tauri-wallet-frontend' into tauri-wallet

commit 3ebdc55847
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Mon Sep 13 12:50:26 2021 +0200

    extract Coin and Denom

commit aaf5d18692
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Sep 13 11:19:28 2021 +0100

    update admin component

commit bcbec1f3e6
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Sun Sep 12 21:49:39 2021 +0100

    admin form style updates

commit c95005265d
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Sun Sep 12 01:26:57 2021 +0100

    button updates

commit b299c9e4b5
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Sun Sep 12 01:03:21 2021 +0100

    add qrcode to receive page

commit bacbd3dfce
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Sun Sep 12 00:40:53 2021 +0100

    fix bug

commit 55561fe1f7
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Sun Sep 12 00:26:15 2021 +0100

    add admin page

commit 0d01500b87
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Sat Sep 11 00:35:14 2021 +0100

    remove login deets

commit 74e34567b4
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Sat Sep 11 00:32:54 2021 +0100

    form updates

commit b77025bfd5
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Sat Sep 11 00:10:03 2021 +0100

    form updates

commit 4d831efcd6
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 10 22:33:00 2021 +0100

    more fees work

commit d227d20385
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 10 17:31:04 2021 +0100

    add gas fees to bond form

commit fb2d3bae3c
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 10 14:17:24 2021 +0100

    use new getFee api

commit 967d74eb19
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 10 11:15:52 2021 +0100

    add nvmrc file

commit 228df278d9
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 10 11:13:18 2021 +0100

    update tsconfig

commit 37da23ab1c
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 10 09:56:04 2021 +0100

    set up global error handling

commit 0c9cf7b5d9
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 10 09:16:45 2021 +0100

    finish create account

commit 262149078c
Merge: 08fd1c1b 3bcbb901
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 9 19:21:38 2021 +0100

    rust updates

commit 08fd1c1b47
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 9 19:00:30 2021 +0100

    make getBalance global

commit 3bcbb90127
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Thu Sep 9 17:47:11 2021 +0200

    fix docs

commit 8156ed0029
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Thu Sep 9 17:43:56 2021 +0200

    get_fee, create_new_account

commit 265f7a7c2e
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Thu Sep 9 13:21:27 2021 +0200

    Dedicated workspace, random_mnemonic, gas_limits

commit 5de8c9d1ed
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 9 11:35:49 2021 +0100

    style updates

commit 7658eec9b9
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 9 09:16:38 2021 +0100

    create account page

commit 99c49581df
Merge: 7a8ad138 926689da
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Sep 8 20:49:27 2021 +0100

    Merge branch 'tauri-wallet' into tauri-wallet-frontend
    rust updates

commit 926689da1d
Merge: 714171f4 b40be179
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Wed Sep 8 11:54:49 2021 +0200

    Merge branch 'tauri-wallet' of https://github.com/nymtech/nym into tauri-wallet

commit 714171f4e5
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Wed Sep 8 11:54:36 2021 +0200

    redundant into

commit 7a8ad1387d
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Sep 8 10:30:51 2021 +0100

    add eslint file

commit bd72426280
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Sep 7 23:44:02 2021 +0100

    start adding gas fees

commit 48d0f31d7e
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Sep 7 23:13:54 2021 +0100

    send updates

commit 4522c18a55
Merge: b9389f12 b40be179
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Sep 7 21:53:38 2021 +0100

    Merge branch 'tauri-wallet' into tauri-wallet-frontend
    rust updates

commit b9389f1235
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Sep 7 21:53:05 2021 +0100

    send updates

commit b40be179ae
Author: durch <durch@users.noreply.github.com>
Date:   Tue Sep 7 12:37:24 2021 +0000

    [ci skip] Generate TS types

commit 32ef9e019e
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Tue Sep 7 14:26:48 2021 +0200

    More verbose send response

commit 45a56a7088
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Sep 6 20:27:15 2021 +0100

    update logo

commit d50afd6113
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Mon Sep 6 20:00:47 2021 +0200

    Add coconut creds

commit 3205b1e0e6
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Sep 6 16:42:56 2021 +0100

    start work on send form

commit 53ea8486f8
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Mon Sep 6 17:36:46 2021 +0200

    get_gas_price, get_gas_limits

commit 43ababf8d4
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Mon Sep 6 17:31:08 2021 +0200

    Rework client errors

commit 5461574023
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Sep 6 13:23:20 2021 +0100

    small refactors

commit 01d2df7bb7
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Sep 6 11:12:39 2021 +0100

    set up form validation for undelegation

commit 7cff72757b
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Sun Sep 5 00:07:46 2021 +0100

    set up delgate request

commit 5bcbf45d16
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Sat Sep 4 22:52:02 2021 +0100

    onerror onsuccess added to bond form

commit 4295d75e0f
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 3 16:53:32 2021 +0100

    format bonding data pre request

commit 018666a614
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 3 16:37:51 2021 +0100

    layout updates

commit 15048524a7
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 3 16:25:15 2021 +0100

    layout updates

commit 2b792945cc
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 3 15:07:38 2021 +0100

    update type roots

commit b6193270a6
Merge: 0b4a8fe6 29973379
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 3 15:02:56 2021 +0100

    Merge branch 'tauri-wallet' into tauri-wallet-frontend
    rust updates

commit 0b4a8fe657
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 3 15:02:09 2021 +0100

    update type roots

commit 2997337948
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Fri Sep 3 15:29:20 2021 +0200

    printable balance should be major

commit e1bea43ff4
Author: Drazen Urch <drazen@urch.eu>
Date:   Fri Sep 3 15:22:48 2021 +0200

    Autogenerate types (#763)

    * Generate TS types on push

    * run shell

    * fix typo

    * pango

    * libpango-dev

    * hopefully all deps are in now :)

    Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>

commit 27ef28da8d
Merge: 42bf139e 87a0b05d
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 3 12:13:28 2021 +0100

    Merge branch 'tauri-wallet' into tauri-wallet-frontend
    rust updates

commit 42bf139ebb
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 3 12:12:52 2021 +0100

    update args collection function

commit 352862e4d0
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 3 11:42:31 2021 +0100

    delegation form validation

commit 9f9ab010d8
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Sep 3 11:39:17 2021 +0100

    integrate rust generated types

commit 87a0b05d1a
Author: Drazen Urch <drazen@urch.eu>
Date:   Fri Sep 3 07:43:29 2021 +0200

    Send

commit dbf82da9b6
Author: Drazen Urch <drazen@urch.eu>
Date:   Fri Sep 3 07:36:02 2021 +0200

    Delegation docs

commit 01a4305883
Author: Drazen Urch <drazen@urch.eu>
Date:   Fri Sep 3 07:33:11 2021 +0200

    Delegation

commit 704b3241ee
Author: Drazen Urch <drazen@urch.eu>
Date:   Fri Sep 3 06:58:57 2021 +0200

    Gateway bonding

commit 4513edae46
Merge: 5d3f1b86 f020b211
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 2 21:38:19 2021 +0100

    rust updates

commit f020b21106
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Thu Sep 2 21:08:19 2021 +0200

    node unbond

commit 2587906473
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Thu Sep 2 20:57:10 2021 +0200

    TS exports, rework internals

commit 5d3f1b86e8
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 2 17:29:03 2021 +0100

    delegate form validation

commit 5a17e48581
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 2 16:11:54 2021 +0100

    finish bond form validation

commit 41356f2181
Merge: bd7d7887 63107c2b
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 2 10:10:55 2021 +0100

    Merge branch 'tauri-wallet' into tauri-wallet-frontend
    merge rust updates

commit bd7d788741
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Thu Sep 2 10:10:18 2021 +0100

    more validation work

commit 63107c2bca
Author: Drazen Urch <drazen@urch.eu>
Date:   Thu Sep 2 10:27:49 2021 +0200

    WIP node bonding

commit f043639ad2
Merge: 1d2a1b26 6fb15fff
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Sep 1 19:48:22 2021 +0100

    Merge branch 'tauri-wallet' into tauri-wallet-frontend
    merge rust update

commit 1d2a1b2635
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Sep 1 19:47:41 2021 +0100

    start form validation

commit 6fb15fff8b
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Wed Sep 1 16:33:29 2021 +0200

    printable_balance

commit c69d7fa46f
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Sep 1 15:12:50 2021 +0100

    remove unused imports

commit c2ee02a2cf
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Sep 1 15:12:25 2021 +0100

    use printable balance

commit 6f5f0c00b5
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Sep 1 15:11:24 2021 +0100

    use printable balance

commit 12707c5f1e
Merge: 2aa7fa04 0daf89ee
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Sep 1 14:59:27 2021 +0100

    merge rust updates

commit 0daf89eeb4
Author: Drazen Urch <drazen@urch.eu>
Date:   Wed Sep 1 15:14:50 2021 +0200

    Utility native <> printable functions

commit 2aa7fa0426
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Sep 1 13:41:42 2021 +0100

    move balance function call to global state

commit f5aa6e2db2
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Sep 1 09:39:22 2021 +0100

    style updates

commit ee5d1c3419
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 31 20:57:21 2021 +0100

    fix nav styling

commit e68c261162
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 31 20:48:30 2021 +0100

    fix ts error

commit c7fe4cd24e
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 31 15:28:46 2021 +0100

    update imports

commit 1c690fc3d0
Merge: d8e6a5fb f56edb82
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 31 15:24:35 2021 +0100

    Merge branch 'tauri-wallet' into tauri-wallet-frontend
    merge rust updates

commit f56edb825a
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Tue Aug 31 15:22:10 2021 +0200

    Fix client address

commit d8e6a5fb2e
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 31 12:55:13 2021 +0100

    make typescript happy

commit b95893bb02
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 31 11:20:12 2021 +0100

    update balance page

commit 6bdff701b4
Merge: ccdb5911 eff38c84
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 31 11:05:51 2021 +0100

    merge backend updates

commit ccdb5911c6
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 31 09:35:24 2021 +0100

    update routing and use new sign in function

commit eff38c8466
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Tue Aug 31 07:38:25 2021 +0200

    some cleanup, get blockchain stuff working

commit 5f4fabc0b8
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Fri Aug 27 15:17:15 2021 +0200

    Add internal documentation scaffolding

commit e2dd1cc9ae
Merge: c7e622f2 42f75028
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Aug 25 14:51:29 2021 +0100

    Merge branch 'tauri-wallet' into tauri-wallet-frontend
    connect with mnemonic update

commit 42f75028bc
Author: Drazen Urch <durch@users.noreply.guthub.com>
Date:   Wed Aug 25 15:44:08 2021 +0200

    Resolve state deadlock

commit c7e622f284
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Aug 25 14:06:37 2021 +0100

    begin sign in rust integration

commit 248da351c6
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Aug 25 14:05:00 2021 +0100

    use color palette object

commit fe1c8a3b08
Merge: 84af9233 1bc17abb
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Aug 25 10:36:11 2021 +0100

    Merge branch 'tauri-wallet' into tauri-wallet-frontend
    merge rust api updates

commit 84af923389
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Wed Aug 25 10:35:42 2021 +0100

    update receive to use state value

commit 1bc17abbaa
Author: Drazen Urch <drazen@urch.eu>
Date:   Wed Aug 25 11:18:16 2021 +0200

    Add connect_with_mnemonic and get_balance tauri functions

commit b8c2735520
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 24 16:12:08 2021 +0100

    cargo lock update

commit 67fd4367ef
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 24 16:08:55 2021 +0100

    global theme update

commit 4540d2c447
Merge: ce757697 4ad25114
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 24 12:56:59 2021 +0100

    Merge branch 'feature/tauri-wallet-ui' into tauri-wallet
    merge with rust updates

commit 4ad25114c3
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 24 12:46:34 2021 +0100

    update nav cards

commit 3ce7888c07
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 24 11:59:10 2021 +0100

    address and balance cards

commit ce75769703
Author: Drazen Urch <drazen@urch.eu>
Date:   Tue Aug 24 12:54:49 2021 +0200

    Bootstrap nymd client with config

commit 66210658cb
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Tue Aug 24 10:35:54 2021 +0100

    add favicon

commit 728da763b3
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 22:54:30 2021 +0100

    fix padding issue

commit 3da08ee33c
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 22:49:39 2021 +0100

    style updates

commit 55977185fd
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 22:04:26 2021 +0100

    add unbond and undelegte pages

commit 1c8c0a47bc
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 21:34:14 2021 +0100

    receive page adjust margin

commit c161b2fe71
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 21:32:29 2021 +0100

    send wizard updates

commit eb91c1180d
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 17:23:25 2021 +0100

    update balance page

commit 13e357637b
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 17:18:07 2021 +0100

    style updates

commit 54c4bdb7d2
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 17:12:40 2021 +0100

    send wizard update

commit 9b5f50913f
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 14:56:52 2021 +0100

    add send wizard

commit 7cfa35b542
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 11:18:13 2021 +0100

    style updates

commit 581a6b0a6f
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 10:38:56 2021 +0100

    update nav bar

commit 85f23eb3d1
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 10:38:42 2021 +0100

    create delegation page

commit 17100fa7da
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Mon Aug 23 10:38:17 2021 +0100

    create node type selector component

commit 24af0c94bf
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Aug 20 22:26:14 2021 +0100

    add bond form

commit 8ec9e3121f
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Aug 20 17:35:59 2021 +0100

    side bar updates

commit 498dbb8ba3
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Aug 20 12:24:04 2021 +0100

    add balance page

commit 7345bd8148
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Aug 20 11:51:27 2021 +0100

    update navigation style

commit b405d2e4af
Author: fmtabbara <fmtabbara@hotmail.co.uk>
Date:   Fri Aug 20 09:03:21 2021 +0100

    set up tauri app with React frontend
2021-09-27 12:03:54 +02:00
Jędrzej Stuczyński 020cad897d Feature/rust rewarding (#750)
* Calculating gas fees

* Ability to set custom fees

* Added extra test

* Removed commented code

* Moved all msg types to common contract crate

* Temporarily disabling get_tx method

* Finishing up nymd client API

* Comment fix

* Remaining fee values

* Some cleanup

* Removed needless borrow

* Fixed imports in contract tests

* Moved error types around

* New ValidatorClient

* Experiment with new type of defaults

* Removed dead module

* Dealt with unwrap

* Migrated mixnode to use new validator client

* Migrated gateway to use new validator client

* Mixnode and gateway adjustments

* More exported defaults

* Clients using new validator client

* Fixed mixnode upgrade

* Moved default values to a new crate

* Changed behaviour of validator client features

* Migrated basic functions of validator api

* Updated config + fixed startup

* Fixed wasm client build

* Integration with the explorer api

* Removed tokio dev dependency

* Needless borrow

* Fixex wasm client build

* Fixed tauri client build

* Needless borrows

* New tables for rewarding

* Updated cosmos-sdk version

* Removed reward-specific node status routes

* New rewarding-specific config entries

* Additional network defaults

* Initial periodic rewards from validator api

* Replaced print with log

* Filtering nodes with uptime > 0

* Additional failure logging statements

* Fixed operation ordering

* Adjusted next rewarding epoch determination

* Modified rewarding behaviour to keep track of rewarding in progress

* Improved error message on config load failure

* Additional log statement

* Adjusted rewarding gas limit calculation

* Made naming slightly more consistent

* Fixed incorrect parentheses placement

* Fixed fee calculation

* Cargo fmt

* Removed failed merge artifacts

* Introduced comment for any future reward modification

* typos

* Helper functions for the future

* Making @mfahampshire 's life easier

* Redesigned epoch + rewarding skipped epochs (if possible)

* Removed old merge artifacts

* Naming consistency

* Constraining arguments

* Removed unnecessary if branch

* Ignore monitor check for current epoch

* Additional checks for current epoch data

* Monitor threshold check

* cargo fmt

* Fixed post-merge issues in transactions.rs
2021-09-24 15:49:21 +01:00
Drazen Urch 5dfaff6296 Update hmac and blake3 (#673)
* Update hmac and blake3

* Remain paranoid for `0.*` crates

* Most paranoid versions :)

* Updated aes and using published version of blake3

Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
2021-09-21 09:59:17 +01:00
Jędrzej Stuczyński 5b03982043 Replaced unwrap_or_else with unwrap_or_default (#780) 2021-09-20 10:40:03 +01:00
Drazen Urch 8f6856d6fb Make fee helpers public (#777)
Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>
2021-09-20 10:15:06 +01:00
Drazen Urch 976dd7aae2 Add block_height method to Delegation (#778)
Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>
2021-09-15 17:28:49 +02:00
Fouad 0d21f4e937 Merge pull request #776 from nymtech/update/re-enable-bonding
re-enable bonding
2021-09-15 12:41:29 +01:00
fmtabbara 1403449ad5 enable bonding 2021-09-14 16:00:21 +01:00
Drazen Urch e84af4f601 Migrate legacy delegation data (#771)
* Skip ReadOnlyBucket deserialization errors

* empty migration

* clippy

* cargo schema

* Drop invalid delegation data

* Dont drop old data

* Add todo

* Unify on type param

* gateways are different

* cargo fmt

Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>
2021-09-14 14:15:26 +01:00
471 changed files with 121659 additions and 11864 deletions
+40 -3
View File
@@ -1,3 +1,40 @@
# Global owners. If you want to set more specific (per-directory, per-language) owners, see the write-up at # Lines starting with '#' are comments.
# https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners # Each line is a file pattern followed by one or more owners.
* @durch @futurechimp @jstuczyn @neacsu
# More details are here: https://help.github.com/articles/about-codeowners/
# The '*' pattern is global owners.
# Order is important. The last matching pattern has the most precedence.
# The folders are ordered as follows:
# In each subsection folders are ordered first by depth, then alphabetically.
# This should make it easy to add new rules without breaking existing ones.
# Something weird not covered by anything else
* @futurechimp @mmsinclair
# Rust rules:
*.rs @durch @futurechimp @jstuczyn @neacsu
Cargo.* @durch @futurechimp @jstuczyn @neacsu
# JS rules:
*.js @mmsinclair @fmtabbara @Aid19801
*.ts @mmsinclair @fmtabbara @Aid19801
*.tsx @mmsinclair @fmtabbara @Aid19801
*.jsx @mmsinclair @fmtabbara @Aid19801
# Something looking like possible documentation rules:
*.md @mfahampshire
# our docker scripts
/docker/ @neacsu
# if there are any changes in the core crypto, I feel like Ania should take a look:
/common/crypto/ @aniampio
/common/nymsphinx/ @aniampio
# Explorer and wallet should probably get looked by the product team
/explorer/ @nymtech/product
/nym-wallet/ @nymtech/product
/wallet-web/ @nymtech/product
+32
View File
@@ -0,0 +1,32 @@
---
name: Feature request
about: Suggest an enhancement to the product
title: "[Feature Request]"
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is...
**Is your request a feature not related to an existing problem? A new feature.**
For example.
- Given I am using the nym wallet
- When I transfer nym tokens across the network
- Then I want to have an url link in the wallet which navigates outside the application to the nym-explorer
**Where does the feature fit in the Nym real estate?**
- Application / UI
**What is this solving?**
How will this improve the product...
**Is this an update to packages or libraries?**
If so, please list them. If not, please ignore this section.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+43
View File
@@ -0,0 +1,43 @@
---
name: Report
about: To help identify and reproduce issues
title: "[Issue]"
labels: bug, bug-needs-triage, qa
assignees: tommyv1987
---
**Describe the issue**
A clear and concise description of what the issue is...
**Expected behaviour**
A clear and concise description of what you expected to happen...
**Stack Traces**
If there are stack traces or logs, please provide them here...
**Steps to Reproduce**
Steps to reproduce the behaviour, if you're familiar with BDD syntax, please write it in this style:
- Given I was doing X
- And I installed Y
- When I actioned Y
- Then I expect Z
*An example:*
- Given I was setting up a mix-node following the instructions in the docs
- And I successfully bonded my node via the the wallet
- When I went to start my mixnode
- Then I was presented with an error
**Screenshots**
If applicable, add screenshots to help explain your problem...
**Which area of Nym were you using?**
- UI: [e.g. Websites - network-explorer, nym-website]
- Application: [e.g Gateway, Client, Wallet]
- OS: [e.g. Ubuntu 20.x, MacOs Big Sur, Windows 10]
- Browser: [e.g Chrome (if applicable)]
- Version: [e.g. nym binary(0.11.0), browser(94.0)]
**Additional context**
Please provide any other information
+68 -36
View File
@@ -1,70 +1,102 @@
name: Continuous integration name: Continuous integration
on: [push, pull_request] on:
push:
paths-ignore:
- 'explorer/**'
pull_request:
paths-ignore:
- 'explorer/**'
jobs: jobs:
ci: matrix_prep:
runs-on: ${{ matrix.os }} runs-on: ubuntu-latest
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' }} outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from build_matrix_includes.json
- uses: actions/checkout@v2
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/build_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
build:
needs: matrix_prep
strategy: strategy:
matrix: matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
rust: [stable, beta, nightly] runs-on: ${{ matrix.os }}
os: [ubuntu-latest, macos-latest, windows-latest] continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.os == 'windows-latest' }}
steps: steps:
- name: Install Dependencies (Linux) - name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
- uses: actions/checkout@v2 - name: Check out repository code
uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1 - name: Install rust toolchain
uses: actions-rs/toolchain@v1
with: with:
profile: minimal profile: minimal
toolchain: ${{ matrix.rust }} toolchain: ${{ matrix.rust }}
override: true override: true
components: rustfmt, clippy components: rustfmt, clippy
# Exclude validator API on Windows - name: Build all binaries
- uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: build
args: --all --exclude nym-validator-api
- uses: actions-rs/cargo@v1
if: ${{ matrix.os != 'windows-latest' }}
with: with:
command: build command: build
args: --all args: --all
# Exclude validator API on Windows - name: Run all tests
- uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: test
args: --all --exclude nym-validator-api
- uses: actions-rs/cargo@v1
if: ${{ matrix.os != 'windows-latest' }}
with: with:
command: test command: test
args: --all args: --all
- uses: actions-rs/cargo@v1 - name: Check formatting
uses: actions-rs/cargo@v1
with: with:
command: fmt command: fmt
args: --all -- --check args: --all -- --check
# Exclude validator API on Windows - uses: actions-rs/clippy-check@v1
- uses: actions-rs/cargo@v1 name: Clippy checks
if: ${{ matrix.rust != 'nightly' && matrix.os == 'windows-latest' }} # if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable'
with: with:
command: clippy token: ${{ secrets.GITHUB_TOKEN }}
args: --all --exclude nym-validator-api -- -D warnings args: --all-features
- uses: actions-rs/cargo@v1 - name: Run clippy
if: ${{ matrix.rust != 'nightly' && matrix.os != 'windows-latest' }} uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with: with:
command: clippy command: clippy
args: -- -D warnings args: -- -D warnings
# COCONUT stuff
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Build all binaries with coconut enabled
uses: actions-rs/cargo@v1
with:
command: build
args: --all --features=coconut
- name: Run all tests with coconut enabled
uses: actions-rs/cargo@v1
with:
command: test
args: --all --features=coconut
- name: Run clippy with coconut enabled
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --features=coconut -- -D warnings
@@ -0,0 +1,50 @@
[
{
"os":"ubuntu-latest",
"rust":"stable",
"runOnEvent":"always"
},
{
"os":"windows-latest",
"rust":"stable",
"runOnEvent":"pull_request"
},
{
"os":"macos-latest",
"rust":"stable",
"runOnEvent":"pull_request"
},
{
"os":"ubuntu-latest",
"rust":"beta",
"runOnEvent":"pull_request"
},
{
"os":"windows-latest",
"rust":"beta",
"runOnEvent":"pull_request"
},
{
"os":"macos-latest",
"rust":"beta",
"runOnEvent":"pull_request"
},
{
"os":"ubuntu-latest",
"rust":"nightly",
"runOnEvent":"pull_request"
},
{
"os":"windows-latest",
"rust":"nightly",
"runOnEvent":"pull_request"
},
{
"os":"macos-latest",
"rust":"nightly",
"runOnEvent":"pull_request"
}
]
-14
View File
@@ -1,14 +0,0 @@
name: Clippy check
on: push
jobs:
clippy_check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- run: rustup component add clippy
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features
@@ -0,0 +1,14 @@
[
{
"rust":"stable",
"runOnEvent":"always"
},
{
"rust":"beta",
"runOnEvent":"pull_request"
},
{
"rust":"nightly",
"runOnEvent":"pull_request"
}
]
@@ -0,0 +1,58 @@
name: ERC20 Bridge Contract
on: [ push, pull_request ]
jobs:
matrix_prep:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from build_matrix_includes.json
- uses: actions/checkout@v2
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/contract_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
erc20-bridge-contract:
needs: matrix_prep
strategy:
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
# since it's going to be compiled into wasm, there's absolutely
# no point in running CI on different OS-es
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.rust == 'nightly' }}
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
target: wasm32-unknown-unknown
override: true
components: rustfmt, clippy
- uses: actions-rs/cargo@v1
env:
RUSTFLAGS: '-C link-arg=-s'
with:
command: build
args: --manifest-path contracts/erc20-bridge/Cargo.toml --target wasm32-unknown-unknown
- uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path contracts/erc20-bridge/Cargo.toml
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path contracts/erc20-bridge/Cargo.toml -- --check
- uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --manifest-path contracts/erc20-bridge/Cargo.toml -- -D warnings
+24 -4
View File
@@ -1,16 +1,34 @@
name: Mixnet Contract name: Mixnet Contract
on: [push, pull_request] on:
push:
paths-ignore:
- 'explorer/**'
pull_request:
paths-ignore:
- 'explorer/**'
jobs: jobs:
ci: matrix_prep:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from build_matrix_includes.json
- uses: actions/checkout@v2
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/contract_matrix_includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
mixnet-contract:
# since it's going to be compiled into wasm, there's absolutely # since it's going to be compiled into wasm, there's absolutely
# no point in running CI on different OS-es # no point in running CI on different OS-es
runs-on: ubuntu-latest runs-on: ubuntu-latest
continue-on-error: ${{ matrix.rust == 'nightly' }} continue-on-error: ${{ matrix.rust == 'nightly' }}
needs: matrix_prep
strategy: strategy:
matrix: matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
rust: [ stable, beta, nightly ]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@@ -23,6 +41,8 @@ jobs:
components: rustfmt, clippy components: rustfmt, clippy
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
env:
RUSTFLAGS: '-C link-arg=-s'
with: with:
command: build command: build
args: --manifest-path contracts/mixnet/Cargo.toml --target wasm32-unknown-unknown args: --manifest-path contracts/mixnet/Cargo.toml --target wasm32-unknown-unknown
@@ -0,0 +1,23 @@
name: Linting for Network Explorer (eslint/prettier)
on:
pull_request:
paths:
- 'explorer/**'
defaults:
run:
working-directory: explorer
jobs:
build:
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
- run: npm install
- name: Run ESLint
# GitHub should automatically annotate the PR
run: npm run lint
+57
View File
@@ -0,0 +1,57 @@
name: CI for Network Explorer
on:
push:
paths:
- 'explorer/**'
defaults:
run:
working-directory: explorer
jobs:
build:
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v2
with:
node-version: '14'
- run: npm install
continue-on-error: true
- run: npm run test
continue-on-error: true
- run: npm run build
continue-on-error: true
- name: Deploy branch to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Keybase - Node Install
run: npm install
working-directory: .github/workflows/support-files/messages
- name: Keybase - Send Notification
env:
NYM_PROJECT_NAME: "Network Explorer"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
KEYBASE_NYM_CHANNEL: "ci-network-explorer"
IS_SUCCESS: "${{ job.status == 'success' }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/messages/entry_point_notifications.sh
+77
View File
@@ -0,0 +1,77 @@
name: Webdriverio tests for nym wallet
on:
push:
paths:
- "nym-wallet/**"
defaults:
run:
working-directory: nym-wallet
jobs:
test:
name: wallet tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Tauri dependencies
run: >
sudo apt-get update &&
sudo apt-get install -y
libgtk-3-dev
libgtksourceview-3.0-dev
webkit2gtk-4.0
libappindicator3-dev
webkit2gtk-driver
xvfb
- name: Install minimal stable
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- name: Node v16
uses: actions/setup-node@v1
with:
node-version: 16.x
- name: Install yarn for building application
run: yarn install
- name: Build application
run: yarn run webpack:build & yarn run tauri:build
- name: Check binary exists
run: |
cd target/release/
(test -f nym-wallet && echo nym binary exists) || echo wallet does not exist
- name: Install dependencies
run: yarn install
working-directory: nym-wallet/webdriver
- name: Remove existing user datafile
uses: JesseTG/rm@v1.0.2
with:
path: nym-wallet/webdriver/common/data/user-data.json
- name: Create user data json file
id: create-json
uses: jsdaniell/create-json@1.1.2
with:
name: "user-data.json"
json: ${{ secrets.WALLET_USERDATA }}
dir: "nym-wallet/webdriver/common/data/"
- name: Install tauri-driver
uses: actions-rs/cargo@v1
with:
command: install
args: tauri-driver
- name: Launch tests
run: xvfb-run yarn test:runall
working-directory: nym-wallet/webdriver
@@ -0,0 +1,2 @@
node_modules
.idea
@@ -0,0 +1,6 @@
{
"trailingComma": "all",
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2
}
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
# pass exit codes out to GitHub Actions
set -euxo pipefail
# change to the directory that contains this script
cd "${0%/*}"
# run the node script
node send_message.js
@@ -0,0 +1,11 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }}
> 🔴 **FAILURE** :cry:
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
Commit message:
```
{{ env.GIT_COMMIT_MESSAGE }}
```
@@ -0,0 +1,16 @@
{
"name": "send-keybase-message",
"description": "Sends a notification message with the keybase package that fails when piped into the keybase CLI",
"version": "1.0.0",
"private": true,
"scripts": {
"format": "prettier --write send_message.js"
},
"dependencies": {
"handlebars": "^4.7.7",
"keybase-bot": "^3.6.1"
},
"devDependencies": {
"prettier": "2.3.2"
}
}
@@ -0,0 +1,69 @@
const Bot = require('keybase-bot');
const Handlebars = require('handlebars');
const fs = require('fs');
async function main() {
const data = { env: process.env };
// const data = { ...PASTE TEST DATA HERE ... }; // -- DEV: uncomment to set test data
// validation of environment
if(!(process.env.NYM_PROJECT_NAME || data.env.NYM_PROJECT_NAME)) {
throw new Error('Please set env var NYM_PROJECT_NAME with the project name for displaying in notification messages');
}
const keybaseChannel = process.env.KEYBASE_NYM_CHANNEL || data.env.KEYBASE_NYM_CHANNEL;
if(!keybaseChannel) {
throw new Error('Please set env var KEYBASE_NYM_CHANNEL with the channel name for the notification message');
}
// extract the git branch name
const GIT_BRANCH_NAME = (process.env.GITHUB_REF || data.env.GITHUB_REF).split('/').slice(2).join('/');
data.env.GIT_BRANCH_NAME = GIT_BRANCH_NAME;
const source = fs
.readFileSync(process.env.IS_SUCCESS === 'true' ? 'success' : 'failure')
.toString();
const template = Handlebars.compile(source);
const result = template(data);
// -- DEV: uncomment to show what is available in the handlebars template / show the result
// console.dir({ data }, { depth: null });
// console.log(result);
const bot = new Bot();
try {
const username = process.env.KEYBASE_NYMBOT_USERNAME;
const paperkey = process.env.KEYBASE_NYMBOT_PAPERKEY;
if(!username) {
throw new Error('Username is not defined. Please set env var KEYBASE_NYMBOT_USERNAME');
}
if(!paperkey) {
throw new Error('Paperkey is not defined. Please set env var KEYBASE_NYMBOT_PAPERKEY');
}
console.log(`Initialising keybase with user "${username}" and key: "${'*'.repeat(paperkey.length)}"...`);
await bot.init(username, paperkey, { verbose: false });
const channel = {
name: 'nymtech_bot',
membersType: 'team',
topicName: keybaseChannel,
topic_type: 'CHAT',
};
const message = {
body: result,
};
console.log(`Sending to ${channel.name}#${channel.topicName}...`);
await bot.chat.send(channel, message);
console.log('Message sent!');
} catch (error) {
console.error(error);
process.exitCode = -1;
} finally {
await bot.deinit();
}
}
main();
@@ -0,0 +1,11 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
> ✅ **SUCCESS**
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
Commit message:
```
{{ env.GIT_COMMIT_MESSAGE }}
```
@@ -0,0 +1,12 @@
name: Publish Tauri Wallet
on:
push:
tags:
- nym-wallet-*
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Run a one-line script
run: echo Hello, world!
+29
View File
@@ -0,0 +1,29 @@
name: Generate TS types
on:
push:
paths-ignore:
- "explorer/**"
pull_request:
paths-ignore:
- "explorer/**"
jobs:
nym-wallet-types:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }}
steps:
- name: Prepare
run: sudo apt-get update && sudo apt-get install -y libpango1.0-dev libatk1.0-dev libgdk-pixbuf2.0-dev libsoup2.4-dev librust-gdk-dev libwebkit2gtk-4.0-dev
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Generate TS
run: cd nym-wallet/src-tauri && cargo test
- uses: EndBug/add-and-commit@v7.2.1 # https://github.com/marketplace/actions/add-commit
with:
add: '["nym-wallet"]'
message: "[ci skip] Generate TS types"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+13 -4
View File
@@ -1,9 +1,12 @@
name: Wasm Client name: Wasm Client
on: [push, pull_request] on:
pull_request:
paths-ignore:
- 'explorer/**'
jobs: jobs:
ci: wasm:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@@ -16,10 +19,16 @@ jobs:
override: true override: true
components: rustfmt, clippy components: rustfmt, clippy
# token credentials (non-coconut) don't work for wasm right now
# - uses: actions-rs/cargo@v1
# with:
# command: build
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --features=coconut
# for some reason this does not seem to work correctly, leave it for later, building is good enough for now # for some reason this does not seem to work correctly, leave it for later, building is good enough for now
# - uses: actions-rs/cargo@v1 # - uses: actions-rs/cargo@v1
@@ -36,4 +45,4 @@ jobs:
# - uses: actions-rs/cargo@v1 # - uses: actions-rs/cargo@v1
# with: # with:
# command: clippy # command: clippy
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings # args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings
+6 -1
View File
@@ -11,7 +11,6 @@ target
/.vscode/settings.json /.vscode/settings.json
validator/.vscode validator/.vscode
sample-configs/validator-config.toml sample-configs/validator-config.toml
.vscode
scripts/deploy_qa.sh scripts/deploy_qa.sh
scripts/run_gate.sh scripts/run_gate.sh
scripts/run_mix.sh scripts/run_mix.sh
@@ -25,8 +24,14 @@ v6-topology.json
/explorer/downloads/topology.json /explorer/downloads/topology.json
/explorer/public/downloads/mixmining.json /explorer/public/downloads/mixmining.json
/explorer/public/downloads/topology.json /explorer/public/downloads/topology.json
/nym-wallet/dist/*
/clients/validator/examples/nym-driver-example/current-contract.txt /clients/validator/examples/nym-driver-example/current-contract.txt
validator-api/v4.json validator-api/v4.json
validator-api/v6.json validator-api/v6.json
**/node_modules **/node_modules
validator-api/keypair validator-api/keypair
contracts/mixnet/code_id
contracts/mixnet/Justfile
contracts/mixnet/Makefile
validator-config
*.patch
Generated
+1529 -680
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -4,6 +4,7 @@
[profile.release] [profile.release]
panic = "abort" panic = "abort"
opt-level = "s" opt-level = "s"
overflow-checks = true
[profile.dev] [profile.dev]
panic = "abort" panic = "abort"
@@ -24,10 +25,12 @@ members = [
"common/config", "common/config",
"common/credentials", "common/credentials",
"common/crypto", "common/crypto",
"common/erc20-bridge-contract",
"common/mixnet-contract", "common/mixnet-contract",
"common/mixnode-common", "common/mixnode-common",
"common/network-defaults", "common/network-defaults",
"common/nonexhaustive-delayqueue", "common/nonexhaustive-delayqueue",
"common/nymcoconut",
"common/nymsphinx", "common/nymsphinx",
"common/nymsphinx/acknowledgements", "common/nymsphinx/acknowledgements",
"common/nymsphinx/addressing", "common/nymsphinx/addressing",
@@ -61,4 +64,4 @@ default-members = [
"validator-api", "validator-api",
] ]
exclude = ["explorer", "contracts"] exclude = ["explorer", "contracts", "tokenomics-py"]
+41 -3
View File
@@ -5,8 +5,6 @@ SPDX-License-Identifier: Apache-2.0
## The Nym Privacy Platform ## The Nym Privacy Platform
This repository contains the Nym mixnet.
The platform is composed of multiple Rust crates. Top-level executable binary crates include: The platform is composed of multiple Rust crates. Top-level executable binary crates include:
* nym-mixnode - shuffles [Sphinx](https://github.com/nymtech/sphinx) packets together to provide privacy against network-level attackers. * nym-mixnode - shuffles [Sphinx](https://github.com/nymtech/sphinx) packets together to provide privacy against network-level attackers.
@@ -15,6 +13,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
* nym-gateway - acts sort of like a mailbox for mixnet messages, removing the need for directly delivery to potentially offline or firewalled devices. * nym-gateway - acts sort of like a mailbox for mixnet messages, removing the need for directly delivery to potentially offline or firewalled devices.
* nym-network-monitor - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing"). * nym-network-monitor - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
* nym-explorer - a (projected) block explorer and (existing) mixnet viewer. * nym-explorer - a (projected) block explorer and (existing) mixnet viewer.
* nym-wallet (currently in development)- a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=for-the-badge)](https://opensource.org/licenses/Apache-2.0) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=for-the-badge)](https://opensource.org/licenses/Apache-2.0)
[![Build Status](https://img.shields.io/github/workflow/status/nymtech/nym/Continuous%20integration/develop?style=for-the-badge&logo=github-actions)](https://github.com/nymtech/nym/actions?query=branch%3Adevelop) [![Build Status](https://img.shields.io/github/workflow/status/nymtech/nym/Continuous%20integration/develop?style=for-the-badge&logo=github-actions)](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
@@ -22,7 +21,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
### Building ### Building
Platform build instructions are available on [our docs site](https://nymtech.net/docs). Platform build instructions are available on [our docs site](https://nymtech.net/docs/0.11.0/overview/index/).
### Developing ### Developing
@@ -32,6 +31,45 @@ There's a `.env.sample-dev` file provided which you can rename to `.env` if you
You can chat to us in [Keybase](https://keybase.io). Download their chat app, then click **Teams -> Join a team**. Type **nymtech.friends** into the team name and hit **continue**. For general chat, hang out in the **#general** channel. Our development takes places in the **#dev** channel. Node operators should be in the **#node-operators** channel. You can chat to us in [Keybase](https://keybase.io). Download their chat app, then click **Teams -> Join a team**. Type **nymtech.friends** into the team name and hit **continue**. For general chat, hang out in the **#general** channel. Our development takes places in the **#dev** channel. Node operators should be in the **#node-operators** channel.
### Rewards
Node, node operator and delegator rewards are determined according to the principles laid out in the section 6 of [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf). Below is a TLDR of the variables and formulas involved in calculating the epoch rewards. Initial reward pool is set to 250 million Nym, making the circulating supply 750 million Nym.
|Symbol|Definition|
|---|---|
|<img src="https://render.githubusercontent.com/render/math?math=R">|global share of rewards available, starts at 2% of the reward pool.
|<img src="https://render.githubusercontent.com/render/math?math=R_{i}">|node reward for mixnode `i`.
|<img src="https://render.githubusercontent.com/render/math?math=\sigma_{i}">|ratio of total node stake (node bond + all delegations) to the token circulating supply.
|<img src="https://render.githubusercontent.com/render/math?math=\lambda_{i}">|ratio of stake operator has plaged to their node to the token circulating supply.
|<img src="https://render.githubusercontent.com/render/math?math=\omega_{i}">|fraction of total effort undertaken by node `i`, set to `1/k` in testnet Milhon.
|<img src="https://render.githubusercontent.com/render/math?math=k">|number of nodes stakeholders are incentivised to create, set by the validators, a matter of governance. Currently determined by the `active set` size, and set to 5000 in testnet Milhon.
|<img src="https://render.githubusercontent.com/render/math?math=\alpha">|Sybil attack resistance parameter - the higher this parameter is set the stronger the reduction in competitivness gets for a Sybil attacker.
|<img src="https://render.githubusercontent.com/render/math?math=PM_{i}">|declared profit margin of operator `i`, defaults to 10% in testnet Milhon.
|<img src="https://render.githubusercontent.com/render/math?math=PF_{i}">|uptime of node `i`, scaled to 0 - 1, for the rewarding epoch
|<img src="https://render.githubusercontent.com/render/math?math=PP_{i}">|cost of operating node `i` for the duration of the rewarding eopoch, set to 40 Nym for testnet Milhon.
Node reward for node `i` is determined as:
<img src="https://render.githubusercontent.com/render/math?math=R_{i}=PF_{i} \cdot R \cdot (\sigma^'_{i} \cdot \omega_{i} \cdot k %2b \alpha \cdot \lambda^'_{i} \cdot \sigma^'_{i} \cdot k)/(1 %2b \alpha)">
where:
<img src="https://render.githubusercontent.com/render/math?math=\sigma^'_{i} = min\{\sigma_{i}, 1/k\}">
and
<img src="https://render.githubusercontent.com/render/math?math=\lambda^'_{i} = min\{\lambda_{i}, 1/k\}">
Operator of node `i` is credited with the following amount:
<img src="https://render.githubusercontent.com/render/math?math=min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}">
Delegate with stake `s` recieves:
<img src="https://render.githubusercontent.com/render/math?math=max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}">
where `s'` is stake `s` scaled over total token circulating supply.
### Licensing and copyright information ### Licensing and copyright information
This program is available as open source under the terms of the Apache 2.0 license. However, some elements are being licensed under CC0-1.0 and MIT. For accurate information, please check individual files. This program is available as open source under the terms of the Apache 2.0 license. However, some elements are being licensed under CC0-1.0 and MIT. For accurate information, please check individual files.
+3
View File
@@ -30,3 +30,6 @@ validator-client = { path = "../../common/client-libs/validator-client" }
[dev-dependencies] [dev-dependencies]
tempfile = "3.1.0" tempfile = "3.1.0"
[features]
coconut = []
@@ -40,7 +40,7 @@ impl MixTrafficController {
async fn on_messages(&mut self, mut mix_packets: Vec<MixPacket>) { async fn on_messages(&mut self, mut mix_packets: Vec<MixPacket>) {
debug_assert!(!mix_packets.is_empty()); debug_assert!(!mix_packets.is_empty());
let success = if mix_packets.len() == 1 { let result = if mix_packets.len() == 1 {
let mix_packet = mix_packets.pop().unwrap(); let mix_packet = mix_packets.pop().unwrap();
self.gateway_client.send_mix_packet(mix_packet).await self.gateway_client.send_mix_packet(mix_packet).await
} else { } else {
@@ -49,7 +49,7 @@ impl MixTrafficController {
.await .await
}; };
match success { match result {
Err(e) => { Err(e) => {
error!("Failed to send sphinx packet(s) to the gateway! - {:?}", e); error!("Failed to send sphinx packet(s) to the gateway! - {:?}", e);
self.consecutive_gateway_failure_count += 1; self.consecutive_gateway_failure_count += 1;
@@ -18,7 +18,7 @@ pub enum ReplyKeyStorageError {
/// Permanent storage for keys in all sent [`ReplySURB`] /// Permanent storage for keys in all sent [`ReplySURB`]
/// ///
/// Each sent out [`ReplySURB`] has a new key associated with it that is going to be used for /// Each sent out [`ReplySURB`] has a new key associated with it that is going to be used for
/// payload encryption. In order to decrypt whatever reply we receive, we need to know which /// payload encryption. In order to -decrypt whatever reply we receive, we need to know which
/// key to use for that purpose. We do it based on received `H(t)` which has to be included /// key to use for that purpose. We do it based on received `H(t)` which has to be included
/// with each reply. /// with each reply.
/// Moreover, there is no restriction when the [`ReplySURB`] might get used so we need to /// Moreover, there is no restriction when the [`ReplySURB`] might get used so we need to
@@ -107,7 +107,7 @@ impl TopologyAccessor {
self.inner.read().await.into() self.inner.read().await.into()
} }
async fn update_global_topology(&mut self, new_topology: Option<NymTopology>) { async fn update_global_topology(&self, new_topology: Option<NymTopology>) {
self.inner.write().await.update(new_topology); self.inner.write().await.update(new_topology);
} }
@@ -130,19 +130,26 @@ impl Default for TopologyAccessor {
pub struct TopologyRefresherConfig { pub struct TopologyRefresherConfig {
validator_api_urls: Vec<Url>, validator_api_urls: Vec<Url>,
refresh_rate: time::Duration, refresh_rate: time::Duration,
client_version: String,
} }
impl TopologyRefresherConfig { impl TopologyRefresherConfig {
pub fn new(validator_api_urls: Vec<Url>, refresh_rate: time::Duration) -> Self { pub fn new(
validator_api_urls: Vec<Url>,
refresh_rate: time::Duration,
client_version: String,
) -> Self {
TopologyRefresherConfig { TopologyRefresherConfig {
validator_api_urls, validator_api_urls,
refresh_rate, refresh_rate,
client_version,
} }
} }
} }
pub struct TopologyRefresher { pub struct TopologyRefresher {
validator_client: validator_client::ApiClient, validator_client: validator_client::ApiClient,
client_version: String,
validator_api_urls: Vec<Url>, validator_api_urls: Vec<Url>,
topology_accessor: TopologyAccessor, topology_accessor: TopologyAccessor,
@@ -158,6 +165,7 @@ impl TopologyRefresher {
TopologyRefresher { TopologyRefresher {
validator_client: validator_client::ApiClient::new(cfg.validator_api_urls[0].clone()), validator_client: validator_client::ApiClient::new(cfg.validator_api_urls[0].clone()),
client_version: cfg.client_version,
validator_api_urls: cfg.validator_api_urls, validator_api_urls: cfg.validator_api_urls,
topology_accessor, topology_accessor,
refresh_rate: cfg.refresh_rate, refresh_rate: cfg.refresh_rate,
@@ -177,12 +185,71 @@ impl TopologyRefresher {
.change_validator_api(self.validator_api_urls[self.currently_used_api].clone()) .change_validator_api(self.validator_api_urls[self.currently_used_api].clone())
} }
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> { /// Verifies whether nodes a reasonably distributed among all mix layers.
///
/// In ideal world we would have 33% nodes on layer 1, 33% on layer 2 and 33% on layer 3.
/// However, this is a rather unrealistic expectation, instead we check whether there exists
/// a layer with more than 66% of nodes or with fewer than 15% and if so, we trigger a failure.
///
/// # Arguments
///
/// * `topology`: active topology constructed from validator api data
/// * `mixnodes_count`: total number of active mixnodes
fn check_layer_distribution(
&self,
active_topology: &NymTopology,
mixnodes_count: usize,
) -> bool {
let mixes = active_topology.mixes();
if active_topology.gateways().is_empty() {
return false;
}
// trivial check to see if have at least a single node on each layer (regardless of active set size)
if mixes.get(&1).is_none() || mixes.get(&2).is_none() || mixes.get(&3).is_none() {
return false;
}
let upper_bound = (mixnodes_count as f32 * 0.66) as usize;
let lower_bound = (mixnodes_count as f32 * 0.15) as usize;
let layer1 = mixes.get(&1).unwrap().len();
let layer2 = mixes.get(&2).unwrap().len();
let layer3 = mixes.get(&3).unwrap().len();
if layer1 < lower_bound || layer1 > upper_bound {
warn!(
"nodes: {}, layer1: {}, layer2: {}, layer3: {}",
mixnodes_count, layer1, layer2, layer3
);
return false;
}
if layer2 < lower_bound || layer2 > upper_bound {
warn!(
"nodes: {}, layer1: {}, layer2: {}, layer3: {}",
mixnodes_count, layer1, layer2, layer3
);
return false;
}
if layer3 < lower_bound || layer3 > upper_bound {
warn!(
"nodes: {}, layer1: {}, layer2: {}, layer3: {}",
mixnodes_count, layer1, layer2, layer3
);
return false;
}
true
}
async fn get_current_compatible_topology(&self) -> Option<NymTopology> {
// TODO: optimization for the future: // TODO: optimization for the future:
// only refresh mixnodes on timer and refresh gateways only when // only refresh mixnodes on timer and refresh gateways only when
// we have to send to a new, unknown, gateway // we have to send to a new, unknown, gateway
let mixnodes = match self.validator_client.get_cached_mixnodes().await { let mixnodes = match self.validator_client.get_cached_active_mixnodes().await {
Err(err) => { Err(err) => {
error!("failed to get network mixnodes - {}", err); error!("failed to get network mixnodes - {}", err);
return None; return None;
@@ -198,11 +265,16 @@ impl TopologyRefresher {
Ok(gateways) => gateways, Ok(gateways) => gateways,
}; };
let topology = nym_topology_from_bonds(mixnodes, gateways); let mixnodes_count = mixnodes.len();
let topology =
nym_topology_from_bonds(mixnodes, gateways).filter_system_version(&self.client_version);
// TODO: I didn't want to change it now, but the expected system version should rather be put in config if !self.check_layer_distribution(&topology, mixnodes_count) {
// rather than pulled from package version of `client_core` warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used.");
Some(topology.filter_system_version(env!("CARGO_PKG_VERSION"))) None
} else {
Some(topology)
}
} }
pub async fn refresh(&mut self) { pub async fn refresh(&mut self) {
+67 -9
View File
@@ -22,7 +22,10 @@ const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20)
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50); const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000); const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_millis(1_500); // Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
pub fn missing_string_value() -> String { pub fn missing_string_value() -> String {
MISSING_VALUE.to_string() MISSING_VALUE.to_string()
@@ -100,6 +103,17 @@ impl<T: NymConfig> Config<T> {
self::Client::<T>::default_reply_encryption_key_store_path(&id); self::Client::<T>::default_reply_encryption_key_store_path(&id);
} }
#[cfg(not(feature = "coconut"))]
if self
.client
.backup_bandwidth_token_keys_dir
.as_os_str()
.is_empty()
{
self.client.backup_bandwidth_token_keys_dir =
self::Client::<T>::default_backup_bandwidth_token_keys_dir(&id);
}
self.client.id = id; self.client.id = id;
} }
@@ -111,6 +125,16 @@ impl<T: NymConfig> Config<T> {
self.client.gateway_listener = gateway_listener.into(); self.client.gateway_listener = gateway_listener.into();
} }
#[cfg(not(feature = "coconut"))]
pub fn with_eth_private_key<S: Into<String>>(&mut self, eth_private_key: S) {
self.client.eth_private_key = eth_private_key.into();
}
#[cfg(not(feature = "coconut"))]
pub fn with_eth_endpoint<S: Into<String>>(&mut self, eth_endpoint: S) {
self.client.eth_endpoint = eth_endpoint.into();
}
pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) { pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) {
self.client.validator_api_urls = validator_api_urls; self.client.validator_api_urls = validator_api_urls;
} }
@@ -173,6 +197,21 @@ impl<T: NymConfig> Config<T> {
self.client.gateway_listener.clone() self.client.gateway_listener.clone()
} }
#[cfg(not(feature = "coconut"))]
pub fn get_backup_bandwidth_token_keys_dir(&self) -> PathBuf {
self.client.backup_bandwidth_token_keys_dir.clone()
}
#[cfg(not(feature = "coconut"))]
pub fn get_eth_endpoint(&self) -> String {
self.client.eth_endpoint.clone()
}
#[cfg(not(feature = "coconut"))]
pub fn get_eth_private_key(&self) -> String {
self.client.eth_private_key.clone()
}
// Debug getters // Debug getters
pub fn get_average_packet_delay(&self) -> Duration { pub fn get_average_packet_delay(&self) -> Duration {
self.debug.average_packet_delay self.debug.average_packet_delay
@@ -268,6 +307,20 @@ pub struct Client<T> {
/// Address of the gateway listener to which all client requests should be sent. /// Address of the gateway listener to which all client requests should be sent.
gateway_listener: String, gateway_listener: String,
/// Path to directory containing public/private keys used for bandwidth token purchase.
/// Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
/// The public key is the name of the file, while the private key is the content.
#[cfg(not(feature = "coconut"))]
backup_bandwidth_token_keys_dir: PathBuf,
/// Ethereum private key.
#[cfg(not(feature = "coconut"))]
eth_private_key: String,
/// Address to an Ethereum full node.
#[cfg(not(feature = "coconut"))]
eth_endpoint: String,
/// nym_home_directory specifies absolute path to the home nym Clients directory. /// nym_home_directory specifies absolute path to the home nym Clients directory.
/// It is expected to use default value and hence .toml file should not redefine this field. /// It is expected to use default value and hence .toml file should not redefine this field.
nym_root_directory: PathBuf, nym_root_directory: PathBuf,
@@ -292,6 +345,12 @@ impl<T: NymConfig> Default for Client<T> {
reply_encryption_key_store_path: Default::default(), reply_encryption_key_store_path: Default::default(),
gateway_id: "".to_string(), gateway_id: "".to_string(),
gateway_listener: "".to_string(), gateway_listener: "".to_string(),
#[cfg(not(feature = "coconut"))]
backup_bandwidth_token_keys_dir: Default::default(),
#[cfg(not(feature = "coconut"))]
eth_private_key: "".to_string(),
#[cfg(not(feature = "coconut"))]
eth_endpoint: "".to_string(),
nym_root_directory: T::default_root_directory(), nym_root_directory: T::default_root_directory(),
super_struct: Default::default(), super_struct: Default::default(),
} }
@@ -326,18 +385,17 @@ impl<T: NymConfig> Client<T> {
fn default_reply_encryption_key_store_path(id: &str) -> PathBuf { fn default_reply_encryption_key_store_path(id: &str) -> PathBuf {
T::default_data_directory(Some(id)).join("reply_key_store") T::default_data_directory(Some(id)).join("reply_key_store")
} }
}
#[derive(Debug, Deserialize, PartialEq, Serialize)] #[cfg(not(feature = "coconut"))]
#[serde(deny_unknown_fields)] fn default_backup_bandwidth_token_keys_dir(id: &str) -> PathBuf {
pub struct Logging {} T::default_data_directory(Some(id)).join("backup_bandwidth_token_keys")
impl Default for Logging {
fn default() -> Self {
Logging {}
} }
} }
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Logging {}
#[derive(Debug, Deserialize, PartialEq, Serialize)] #[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)] #[serde(default, deny_unknown_fields)]
pub struct Debug { pub struct Debug {
+8 -3
View File
@@ -3,6 +3,7 @@ name = "nym-client"
version = "0.11.0" version = "0.11.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018" edition = "2018"
rust-version = "1.56"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -23,7 +24,7 @@ dirs = "3.0" # for determining default store directories in config
dotenv = "0.15.0" # for obtaining environmental variables (only used for RUST_LOG for time being) dotenv = "0.15.0" # for obtaining environmental variables (only used for RUST_LOG for time being)
log = "0.4" # self explanatory log = "0.4" # self explanatory
pretty_env_logger = "0.4" # for formatting log messages pretty_env_logger = "0.4" # for formatting log messages
rand = {version = "0.7.3", features = ["wasm-bindgen"]} # rng-related traits + some rng implementation to use rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use
serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization
sled = "0.34" # for storage of replySURB decryption keys sled = "0.34" # for storage of replySURB decryption keys
tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal"] } # async runtime tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal"] } # async runtime
@@ -31,8 +32,8 @@ tokio-tungstenite = "0.14" # websocket
## internal ## internal
client-core = { path = "../client-core" } client-core = { path = "../client-core" }
coconut-interface = { path = "../../common/coconut-interface" } coconut-interface = { path = "../../common/coconut-interface", optional = true }
credentials = { path = "../../common/credentials" } credentials = { path = "../../common/credentials", optional = true }
config = { path = "../../common/config" } config = { path = "../../common/config" }
crypto = { path = "../../common/crypto" } crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" } gateway-client = { path = "../../common/client-libs/gateway-client" }
@@ -43,6 +44,10 @@ topology = { path = "../../common/topology" }
websocket-requests = { path = "websocket-requests" } websocket-requests = { path = "websocket-requests" }
validator-client = { path = "../../common/client-libs/validator-client" } validator-client = { path = "../../common/client-libs/validator-client" }
version-checker = { path = "../../common/version-checker" } version-checker = { path = "../../common/version-checker" }
network-defaults = { path = "../../common/network-defaults" }
[features]
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
[dev-dependencies] [dev-dependencies]
serde_json = "1.0" # for the "textsend" example serde_json = "1.0" # for the "textsend" example
@@ -42,6 +42,17 @@ public_encryption_key_file = '{{ client.public_encryption_key_file }}'
# sent but not received back. # sent but not received back.
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}' reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
# Path to directory containing public/private keys used for bandwidth token purchase.
# Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
# The public key is the name of the file, while the private key is the content.
backup_bandwidth_token_keys_dir = '{{ client.backup_bandwidth_token_keys_dir }}'
# Ethereum private key.
eth_private_key = '{{ client.eth_private_key }}'
# Addess to an Ethereum full node.
eth_endpoint = '{{ client.eth_endpoint }}'
##### additional client config options ##### ##### additional client config options #####
# ID of the gateway from which the client should be fetching messages. # ID of the gateway from which the client should be fetching messages.
+18 -31
View File
@@ -1,8 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::{Config, SocketType};
use crate::websocket;
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream; use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
use client_core::client::inbound_messages::{ use client_core::client::inbound_messages::{
InputMessage, InputMessageReceiver, InputMessageSender, InputMessage, InputMessageReceiver, InputMessageSender,
@@ -22,11 +20,9 @@ use client_core::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig, TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
}; };
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use coconut_interface::Credential;
use credentials::bandwidth::prepare_for_spending;
use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::identity; use crypto::asymmetric::identity;
use futures::channel::mpsc; use futures::channel::mpsc;
use gateway_client::bandwidth::BandwidthController;
use gateway_client::{ use gateway_client::{
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver, AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
MixnetMessageSender, MixnetMessageSender,
@@ -38,6 +34,9 @@ use nymsphinx::anonymous_replies::ReplySurb;
use nymsphinx::receiver::ReconstructedMessage; use nymsphinx::receiver::ReconstructedMessage;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use crate::client::config::{Config, SocketType};
use crate::websocket;
pub(crate) mod config; pub(crate) mod config;
pub struct NymClient { pub struct NymClient {
@@ -166,30 +165,6 @@ impl NymClient {
.start(self.runtime.handle()) .start(self.runtime.handle())
} }
async fn prepare_credential(&self) -> Credential {
let verification_key = obtain_aggregate_verification_key(
&self.config.get_base().get_validator_api_endpoints(),
)
.await
.expect("could not obtain aggregate verification key of validators");
let bandwidth_credential = credentials::bandwidth::obtain_signature(
&self.key_manager.identity_keypair().public_key().to_bytes(),
&self.config.get_base().get_validator_api_endpoints(),
)
.await
.expect("could not obtain bandwidth credential");
// the above would presumably be loaded from a file
// the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff)
prepare_for_spending(
&self.key_manager.identity_keypair().public_key().to_bytes(),
&bandwidth_credential,
&verification_key,
)
.expect("could not prepare out bandwidth credential for spending")
}
fn start_gateway_client( fn start_gateway_client(
&mut self, &mut self,
mixnet_message_sender: MixnetMessageSender, mixnet_message_sender: MixnetMessageSender,
@@ -208,7 +183,18 @@ impl NymClient {
.expect("provided gateway id is invalid!"); .expect("provided gateway id is invalid!");
self.runtime.block_on(async { self.runtime.block_on(async {
let coconut_credential = self.prepare_credential().await; #[cfg(feature = "coconut")]
let bandwidth_controller = BandwidthController::new(
self.config.get_base().get_validator_api_endpoints(),
*self.key_manager.identity_keypair().public_key(),
);
#[cfg(not(feature = "coconut"))]
let bandwidth_controller = BandwidthController::new(
self.config.get_base().get_eth_endpoint(),
self.config.get_base().get_eth_private_key(),
self.config.get_base().get_backup_bandwidth_token_keys_dir(),
)
.expect("Could not create bandwidth controller");
let mut gateway_client = GatewayClient::new( let mut gateway_client = GatewayClient::new(
gateway_address, gateway_address,
@@ -218,7 +204,7 @@ impl NymClient {
mixnet_message_sender, mixnet_message_sender,
ack_sender, ack_sender,
self.config.get_base().get_gateway_response_timeout(), self.config.get_base().get_gateway_response_timeout(),
coconut_credential, Some(bandwidth_controller),
); );
gateway_client gateway_client
@@ -236,6 +222,7 @@ impl NymClient {
let topology_refresher_config = TopologyRefresherConfig::new( let topology_refresher_config = TopologyRefresherConfig::new(
self.config.get_base().get_validator_api_endpoints(), self.config.get_base().get_validator_api_endpoints(),
self.config.get_base().get_topology_refresh_rate(), self.config.get_base().get_topology_refresh_rate(),
env!("CARGO_PKG_VERSION").to_string(),
); );
let mut topology_refresher = let mut topology_refresher =
TopologyRefresher::new(topology_refresher_config, topology_accessor); TopologyRefresher::new(topology_refresher_config, topology_accessor);
+54 -26
View File
@@ -1,18 +1,23 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::Config;
use crate::commands::override_config;
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use client_core::client::key_manager::KeyManager; use client_core::client::key_manager::KeyManager;
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use coconut_interface::Credential; #[cfg(feature = "coconut")]
use coconut_interface::{hash_to_scalar, Credential, Parameters};
use config::NymConfig; use config::NymConfig;
use credentials::bandwidth::prepare_for_spending; #[cfg(feature = "coconut")]
use credentials::coconut::bandwidth::{
obtain_signature, prepare_for_spending, BandwidthVoucherAttributes, TOTAL_ATTRIBUTES,
};
#[cfg(feature = "coconut")]
use credentials::obtain_aggregate_verification_key; use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::{encryption, identity}; use crypto::asymmetric::{encryption, identity};
use gateway_client::GatewayClient; use gateway_client::GatewayClient;
use gateway_requests::registration::handshake::SharedKeys; use gateway_requests::registration::handshake::SharedKeys;
#[cfg(feature = "coconut")]
use network_defaults::BANDWIDTH_VALUE;
use nymsphinx::addressing::clients::Recipient; use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity; use nymsphinx::addressing::nodes::NodeIdentity;
use rand::rngs::OsRng; use rand::rngs::OsRng;
@@ -24,8 +29,11 @@ use std::time::Duration;
use topology::{filter::VersionFilterable, gateway}; use topology::{filter::VersionFilterable, gateway};
use url::Url; use url::Url;
use crate::client::config::Config;
use crate::commands::override_config;
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
App::new("init") let app = App::new("init")
.about("Initialise a Nym client. Do this first!") .about("Initialise a Nym client. Do this first!")
.arg(Arg::with_name("id") .arg(Arg::with_name("id")
.long("id") .long("id")
@@ -39,9 +47,9 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
.takes_value(true) .takes_value(true)
) )
.arg(Arg::with_name("validators") .arg(Arg::with_name("validators")
.long("validators") .long("validators")
.help("Comma separated list of rest endpoints of the validators") .help("Comma separated list of rest endpoints of the validators")
.takes_value(true), .takes_value(true),
) )
.arg(Arg::with_name("disable-socket") .arg(Arg::with_name("disable-socket")
.long("disable-socket") .long("disable-socket")
@@ -57,37 +65,62 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
.long("fastmode") .long("fastmode")
.hidden(true) // this will prevent this flag from being displayed in `--help` .hidden(true) // this will prevent this flag from being displayed in `--help`
.help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init") .help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init")
) );
#[cfg(not(feature = "coconut"))]
let app = app
.arg(Arg::with_name("eth_endpoint")
.long("eth_endpoint")
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
.takes_value(true)
.required(true))
.arg(Arg::with_name("eth_private_key")
.long("eth_private_key")
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
.takes_value(true)
.required(true));
app
} }
// this behaviour should definitely be changed, we shouldn't // this behaviour should definitely be changed, we shouldn't
// need to get bandwidth credential for registration // need to get bandwidth credential for registration
async fn prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential { #[cfg(feature = "coconut")]
async fn _prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential {
let verification_key = obtain_aggregate_verification_key(validators) let verification_key = obtain_aggregate_verification_key(validators)
.await .await
.expect("could not obtain aggregate verification key of validators"); .expect("could not obtain aggregate verification key of validators");
let bandwidth_credential = credentials::bandwidth::obtain_signature(raw_identity, validators) let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
.await let bandwidth_credential_attributes = BandwidthVoucherAttributes {
.expect("could not obtain bandwidth credential"); serial_number: params.random_scalar(),
binding_number: params.random_scalar(),
voucher_value: hash_to_scalar(BANDWIDTH_VALUE.to_be_bytes()),
voucher_info: hash_to_scalar(String::from("BandwidthVoucher").as_bytes()),
};
prepare_for_spending(raw_identity, &bandwidth_credential, &verification_key) let bandwidth_credential =
.expect("could not prepare out bandwidth credential for spending") obtain_signature(&params, &bandwidth_credential_attributes, validators)
.await
.expect("could not obtain bandwidth credential");
prepare_for_spending(
raw_identity,
&bandwidth_credential,
&bandwidth_credential_attributes,
&verification_key,
)
.expect("could not prepare out bandwidth credential for spending")
} }
async fn register_with_gateway( async fn register_with_gateway(
gateway: &gateway::Node, gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>, our_identity: Arc<identity::KeyPair>,
validator_urls: Vec<Url>,
) -> Arc<SharedKeys> { ) -> Arc<SharedKeys> {
let timeout = Duration::from_millis(1500); let timeout = Duration::from_millis(1500);
let coconut_credential =
prepare_temporary_credential(&validator_urls, &our_identity.public_key().to_bytes()).await;
let mut gateway_client = GatewayClient::new_init( let mut gateway_client = GatewayClient::new_init(
gateway.clients_address(), gateway.clients_address(),
gateway.identity_key, gateway.identity_key,
our_identity.clone(), our_identity.clone(),
coconut_credential,
timeout, timeout,
); );
gateway_client gateway_client
@@ -210,13 +243,8 @@ pub fn execute(matches: &ArgMatches) {
config config
.get_base_mut() .get_base_mut()
.with_gateway_id(gate_details.identity_key.to_base58_string()); .with_gateway_id(gate_details.identity_key.to_base58_string());
let validator_urls = config.get_base().get_validator_api_endpoints(); let shared_keys =
let shared_keys = register_with_gateway( register_with_gateway(&gate_details, key_manager.identity_keypair()).await;
&gate_details,
key_manager.identity_keypair(),
validator_urls,
)
.await;
(shared_keys, gate_details.clients_address()) (shared_keys, gate_details.clients_address())
}; };
+9
View File
@@ -43,5 +43,14 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi
config = config.with_port(port.unwrap()); config = config.with_port(port.unwrap());
} }
#[cfg(not(feature = "coconut"))]
if let Some(eth_endpoint) = matches.value_of("eth_endpoint") {
config.get_base_mut().with_eth_endpoint(eth_endpoint);
}
#[cfg(not(feature = "coconut"))]
if let Some(eth_private_key) = matches.value_of("eth_private_key") {
config.get_base_mut().with_eth_private_key(eth_private_key);
}
config config
} }
+14 -2
View File
@@ -10,7 +10,7 @@ use log::*;
use version_checker::is_minor_version_compatible; use version_checker::is_minor_version_compatible;
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
App::new("run") let app = App::new("run")
.about("Run the Nym client with provided configuration client optionally overriding set parameters") .about("Run the Nym client with provided configuration client optionally overriding set parameters")
.arg(Arg::with_name("id") .arg(Arg::with_name("id")
.long("id") .long("id")
@@ -38,7 +38,19 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
.long("port") .long("port")
.help("Port for the socket (if applicable) to listen on") .help("Port for the socket (if applicable) to listen on")
.takes_value(true) .takes_value(true)
) );
#[cfg(not(feature = "coconut"))]
let app = app
.arg(Arg::with_name("eth_endpoint")
.long("eth_endpoint")
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
.takes_value(true))
.arg(Arg::with_name("eth_private_key")
.long("eth_private_key")
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
.takes_value(true));
app
} }
// this only checks compatibility between config the binary. It does not take into consideration // this only checks compatibility between config the binary. It does not take into consideration
+3 -3
View File
@@ -1,8 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub(crate) mod handler;
pub(crate) mod listener;
pub(crate) use handler::Handler; pub(crate) use handler::Handler;
pub(crate) use listener::Listener; pub(crate) use listener::Listener;
pub(crate) mod handler;
pub(crate) mod listener;
+8 -3
View File
@@ -3,6 +3,7 @@ name = "nym-socks5-client"
version = "0.11.0" version = "0.11.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2018" edition = "2018"
rust-version = "1.56"
[lib] [lib]
name = "nym_socks5" name = "nym_socks5"
@@ -24,17 +25,21 @@ url = "2.2"
# internal # internal
client-core = { path = "../client-core" } client-core = { path = "../client-core" }
coconut-interface = { path = "../../common/coconut-interface" } coconut-interface = { path = "../../common/coconut-interface", optional = true }
credentials = { path = "../../common/credentials" } credentials = { path = "../../common/credentials", optional = true }
config = { path = "../../common/config" } config = { path = "../../common/config" }
crypto = { path = "../../common/crypto" } crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" } gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" } gateway-requests = { path = "../../gateway/gateway-requests" }
nymsphinx = { path = "../../common/nymsphinx" } nymsphinx = { path = "../../common/nymsphinx" }
ordered-buffer = {path = "../../common/socks5/ordered-buffer"} ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
socks5-requests = { path = "../../common/socks5/requests" } socks5-requests = { path = "../../common/socks5/requests" }
topology = { path = "../../common/topology" } topology = { path = "../../common/topology" }
pemstore = { path = "../../common/pemstore" } pemstore = { path = "../../common/pemstore" }
proxy-helpers = { path = "../../common/socks5/proxy-helpers" } proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
validator-client = { path = "../../common/client-libs/validator-client" } validator-client = { path = "../../common/client-libs/validator-client" }
version-checker = { path = "../../common/version-checker" } version-checker = { path = "../../common/version-checker" }
network-defaults = { path = "../../common/network-defaults" }
[features]
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
@@ -42,6 +42,17 @@ public_encryption_key_file = '{{ client.public_encryption_key_file }}'
# sent but not received back. # sent but not received back.
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}' reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
# Path to directory containing public/private keys used for bandwidth token purchase.
# Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
# The public key is the name of the file, while the private key is the content.
backup_bandwidth_token_keys_dir = '{{ client.backup_bandwidth_token_keys_dir }}'
# Ethereum private key.
eth_private_key = '{{ client.eth_private_key }}'
# Addess to an Ethereum full node.
eth_endpoint = '{{ client.eth_endpoint }}'
##### additional client config options ##### ##### additional client config options #####
# ID of the gateway from which the client should be fetching messages. # ID of the gateway from which the client should be fetching messages.
+21 -34
View File
@@ -1,11 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::Config;
use crate::socks::{
authentication::{AuthenticationMethods, Authenticator, User},
server::SphinxSocksServer,
};
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream; use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
use client_core::client::inbound_messages::{ use client_core::client::inbound_messages::{
InputMessage, InputMessageReceiver, InputMessageSender, InputMessage, InputMessageReceiver, InputMessageSender,
@@ -23,11 +18,9 @@ use client_core::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig, TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
}; };
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use coconut_interface::Credential;
use credentials::bandwidth::prepare_for_spending;
use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::identity; use crypto::asymmetric::identity;
use futures::channel::mpsc; use futures::channel::mpsc;
use gateway_client::bandwidth::BandwidthController;
use gateway_client::{ use gateway_client::{
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver, AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
MixnetMessageSender, MixnetMessageSender,
@@ -37,6 +30,12 @@ use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity; use nymsphinx::addressing::nodes::NodeIdentity;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
use crate::client::config::Config;
use crate::socks::{
authentication::{AuthenticationMethods, Authenticator, User},
server::SphinxSocksServer,
};
pub(crate) mod config; pub(crate) mod config;
pub struct NymClient { pub struct NymClient {
@@ -154,30 +153,6 @@ impl NymClient {
.start(self.runtime.handle()) .start(self.runtime.handle())
} }
async fn prepare_credential(&self) -> Credential {
let verification_key = obtain_aggregate_verification_key(
&self.config.get_base().get_validator_api_endpoints(),
)
.await
.expect("could not obtain aggregate verification key of validators");
let bandwidth_credential = credentials::bandwidth::obtain_signature(
&self.key_manager.identity_keypair().public_key().to_bytes(),
&self.config.get_base().get_validator_api_endpoints(),
)
.await
.expect("could not obtain bandwidth credential");
// the above would presumably be loaded from a file
// the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff)
prepare_for_spending(
&self.key_manager.identity_keypair().public_key().to_bytes(),
&bandwidth_credential,
&verification_key,
)
.expect("could not prepare out bandwidth credential for spending")
}
fn start_gateway_client( fn start_gateway_client(
&mut self, &mut self,
mixnet_message_sender: MixnetMessageSender, mixnet_message_sender: MixnetMessageSender,
@@ -196,7 +171,18 @@ impl NymClient {
.expect("provided gateway id is invalid!"); .expect("provided gateway id is invalid!");
self.runtime.block_on(async { self.runtime.block_on(async {
let coconut_credential = self.prepare_credential().await; #[cfg(feature = "coconut")]
let bandwidth_controller = BandwidthController::new(
self.config.get_base().get_validator_api_endpoints(),
*self.key_manager.identity_keypair().public_key(),
);
#[cfg(not(feature = "coconut"))]
let bandwidth_controller = BandwidthController::new(
self.config.get_base().get_eth_endpoint(),
self.config.get_base().get_eth_private_key(),
self.config.get_base().get_backup_bandwidth_token_keys_dir(),
)
.expect("Could not create bandwidth controller");
let mut gateway_client = GatewayClient::new( let mut gateway_client = GatewayClient::new(
gateway_address, gateway_address,
@@ -206,7 +192,7 @@ impl NymClient {
mixnet_message_sender, mixnet_message_sender,
ack_sender, ack_sender,
self.config.get_base().get_gateway_response_timeout(), self.config.get_base().get_gateway_response_timeout(),
coconut_credential, Some(bandwidth_controller),
); );
gateway_client gateway_client
@@ -224,6 +210,7 @@ impl NymClient {
let topology_refresher_config = TopologyRefresherConfig::new( let topology_refresher_config = TopologyRefresherConfig::new(
self.config.get_base().get_validator_api_endpoints(), self.config.get_base().get_validator_api_endpoints(),
self.config.get_base().get_topology_refresh_rate(), self.config.get_base().get_topology_refresh_rate(),
env!("CARGO_PKG_VERSION").to_string(),
); );
let mut topology_refresher = let mut topology_refresher =
TopologyRefresher::new(topology_refresher_config, topology_accessor); TopologyRefresher::new(topology_refresher_config, topology_accessor);
+54 -26
View File
@@ -1,18 +1,23 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::Config;
use crate::commands::override_config;
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use client_core::client::key_manager::KeyManager; use client_core::client::key_manager::KeyManager;
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use coconut_interface::Credential; #[cfg(feature = "coconut")]
use coconut_interface::{hash_to_scalar, Credential, Parameters};
use config::NymConfig; use config::NymConfig;
use credentials::bandwidth::prepare_for_spending; #[cfg(feature = "coconut")]
use credentials::coconut::bandwidth::{
obtain_signature, prepare_for_spending, BandwidthVoucherAttributes, TOTAL_ATTRIBUTES,
};
#[cfg(feature = "coconut")]
use credentials::obtain_aggregate_verification_key; use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::{encryption, identity}; use crypto::asymmetric::{encryption, identity};
use gateway_client::GatewayClient; use gateway_client::GatewayClient;
use gateway_requests::registration::handshake::SharedKeys; use gateway_requests::registration::handshake::SharedKeys;
#[cfg(feature = "coconut")]
use network_defaults::BANDWIDTH_VALUE;
use nymsphinx::addressing::clients::Recipient; use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity; use nymsphinx::addressing::nodes::NodeIdentity;
use rand::{prelude::SliceRandom, rngs::OsRng, thread_rng}; use rand::{prelude::SliceRandom, rngs::OsRng, thread_rng};
@@ -22,8 +27,11 @@ use std::time::Duration;
use topology::{filter::VersionFilterable, gateway}; use topology::{filter::VersionFilterable, gateway};
use url::Url; use url::Url;
use crate::client::config::Config;
use crate::commands::override_config;
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
App::new("init") let app = App::new("init")
.about("Initialise a Nym client. Do this first!") .about("Initialise a Nym client. Do this first!")
.arg(Arg::with_name("id") .arg(Arg::with_name("id")
.long("id") .long("id")
@@ -43,9 +51,9 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
.takes_value(true) .takes_value(true)
) )
.arg(Arg::with_name("validators") .arg(Arg::with_name("validators")
.long("validators") .long("validators")
.help("Comma separated list of rest endpoints of the validators") .help("Comma separated list of rest endpoints of the validators")
.takes_value(true), .takes_value(true),
) )
.arg(Arg::with_name("port") .arg(Arg::with_name("port")
.short("p") .short("p")
@@ -57,37 +65,62 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
.long("fastmode") .long("fastmode")
.hidden(true) // this will prevent this flag from being displayed in `--help` .hidden(true) // this will prevent this flag from being displayed in `--help`
.help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init") .help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init")
) );
#[cfg(not(feature = "coconut"))]
let app = app
.arg(Arg::with_name("eth_endpoint")
.long("eth_endpoint")
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
.takes_value(true)
.required(true))
.arg(Arg::with_name("eth_private_key")
.long("eth_private_key")
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
.takes_value(true)
.required(true));
app
} }
// this behaviour should definitely be changed, we shouldn't // this behaviour should definitely be changed, we shouldn't
// need to get bandwidth credential for registration // need to get bandwidth credential for registration
async fn prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential { #[cfg(feature = "coconut")]
async fn _prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential {
let verification_key = obtain_aggregate_verification_key(validators) let verification_key = obtain_aggregate_verification_key(validators)
.await .await
.expect("could not obtain aggregate verification key of validators"); .expect("could not obtain aggregate verification key of validators");
let bandwidth_credential = credentials::bandwidth::obtain_signature(raw_identity, validators) let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
.await let bandwidth_credential_attributes = BandwidthVoucherAttributes {
.expect("could not obtain bandwidth credential"); serial_number: params.random_scalar(),
binding_number: params.random_scalar(),
voucher_value: hash_to_scalar(BANDWIDTH_VALUE.to_be_bytes()),
voucher_info: hash_to_scalar("BandwidthVoucher"),
};
prepare_for_spending(raw_identity, &bandwidth_credential, &verification_key) let bandwidth_credential =
.expect("could not prepare out bandwidth credential for spending") obtain_signature(&params, &bandwidth_credential_attributes, validators)
.await
.expect("could not obtain bandwidth credential");
prepare_for_spending(
raw_identity,
&bandwidth_credential,
&bandwidth_credential_attributes,
&verification_key,
)
.expect("could not prepare out bandwidth credential for spending")
} }
async fn register_with_gateway( async fn register_with_gateway(
gateway: &gateway::Node, gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>, our_identity: Arc<identity::KeyPair>,
validator_urls: Vec<Url>,
) -> Arc<SharedKeys> { ) -> Arc<SharedKeys> {
let timeout = Duration::from_millis(1500); let timeout = Duration::from_millis(1500);
let coconut_credential =
prepare_temporary_credential(&validator_urls, &our_identity.public_key().to_bytes()).await;
let mut gateway_client = GatewayClient::new_init( let mut gateway_client = GatewayClient::new_init(
gateway.clients_address(), gateway.clients_address(),
gateway.identity_key, gateway.identity_key,
our_identity.clone(), our_identity.clone(),
coconut_credential,
timeout, timeout,
); );
gateway_client gateway_client
@@ -211,13 +244,8 @@ pub fn execute(matches: &ArgMatches) {
config config
.get_base_mut() .get_base_mut()
.with_gateway_id(gate_details.identity_key.to_base58_string()); .with_gateway_id(gate_details.identity_key.to_base58_string());
let validator_urls = config.get_base().get_validator_api_endpoints(); let shared_keys =
let shared_keys = register_with_gateway( register_with_gateway(&gate_details, key_manager.identity_keypair()).await;
&gate_details,
key_manager.identity_keypair(),
validator_urls,
)
.await;
(shared_keys, gate_details.clients_address()) (shared_keys, gate_details.clients_address())
}; };
+9
View File
@@ -39,5 +39,14 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi
config = config.with_port(port.unwrap()); config = config.with_port(port.unwrap());
} }
#[cfg(not(feature = "coconut"))]
if let Some(eth_endpoint) = matches.value_of("eth_endpoint") {
config.get_base_mut().with_eth_endpoint(eth_endpoint);
}
#[cfg(not(feature = "coconut"))]
if let Some(eth_private_key) = matches.value_of("eth_private_key") {
config.get_base_mut().with_eth_private_key(eth_private_key);
}
config config
} }
+14 -2
View File
@@ -10,7 +10,7 @@ use log::*;
use version_checker::is_minor_version_compatible; use version_checker::is_minor_version_compatible;
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
App::new("run") let app = App::new("run")
.about("Run the Nym client with provided configuration client optionally overriding set parameters") .about("Run the Nym client with provided configuration client optionally overriding set parameters")
.arg(Arg::with_name("id") .arg(Arg::with_name("id")
.long("id") .long("id")
@@ -44,7 +44,19 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
.long("port") .long("port")
.help("Port for the socket to listen on") .help("Port for the socket to listen on")
.takes_value(true) .takes_value(true)
) );
#[cfg(not(feature = "coconut"))]
let app = app
.arg(Arg::with_name("eth_endpoint")
.long("eth_endpoint")
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
.takes_value(true))
.arg(Arg::with_name("eth_private_key")
.long("eth_private_key")
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
.takes_value(true));
app
} }
// this only checks compatibility between config the binary. It does not take into consideration // this only checks compatibility between config the binary. It does not take into consideration
+29 -17
View File
@@ -3,21 +3,24 @@
windows_subsystem = "windows" windows_subsystem = "windows"
)] )]
use std::sync::Arc;
use tokio::sync::RwLock;
use url::Url;
use coconut_interface::{ use coconut_interface::{
self, hash_to_scalar, Attribute, Credential, Parameters, Signature, Theta, VerificationKey, self, hash_to_scalar, Attribute, Credential, Parameters, Signature, Theta, VerificationKey,
}; };
use credentials::{obtain_aggregate_signature, obtain_aggregate_verification_key}; use credentials::{obtain_aggregate_signature, obtain_aggregate_verification_key};
use std::sync::Arc;
use tokio::sync::RwLock;
use url::Url;
struct State { struct State {
signatures: Vec<Signature>, signatures: Vec<Signature>,
n_attributes: u32, n_attributes: u32,
params: Parameters, params: Parameters,
public_attributes_bytes: Vec<Vec<u8>>, serial_number: Attribute,
public_attributes: Vec<Attribute>, binding_number: Attribute,
private_attributes: Vec<Attribute>, voucher_value: Attribute,
voucher_info: Attribute,
aggregated_verification_key: Option<VerificationKey>, aggregated_verification_key: Option<VerificationKey>,
} }
@@ -37,9 +40,10 @@ impl State {
signatures: Vec::new(), signatures: Vec::new(),
n_attributes, n_attributes,
params, params,
public_attributes_bytes, serial_number: private_attributes[0],
public_attributes, binding_number: private_attributes[1],
private_attributes, voucher_value: public_attributes[0],
voucher_info: public_attributes[1],
aggregated_verification_key: None, aggregated_verification_key: None,
} }
} }
@@ -63,8 +67,8 @@ async fn randomise_credential(
) -> Result<Vec<Signature>, String> { ) -> Result<Vec<Signature>, String> {
let mut state = state.write().await; let mut state = state.write().await;
let signature = state.signatures.remove(idx); let signature = state.signatures.remove(idx);
let new = signature.randomise(&state.params); let (new_signature, _) = signature.randomise(&state.params);
state.signatures.insert(idx, new); state.signatures.insert(idx, new_signature);
Ok(state.signatures.clone()) Ok(state.signatures.clone())
} }
@@ -117,14 +121,15 @@ async fn prove_credential(
let state = state.read().await; let state = state.read().await;
if let Some(signature) = state.signatures.get(idx) { if let Some(signature) = state.signatures.get(idx) {
match coconut_interface::prove_credential( match coconut_interface::prove_bandwidth_credential(
&state.params, &state.params,
&verification_key, &verification_key,
signature, signature,
&state.private_attributes, state.serial_number,
state.binding_number,
) { ) {
Ok(theta) => Ok(theta), Ok(theta) => Ok(theta),
Err(e) => Err(format!("{}", e)), Err(e) => Err(format!("{:?}", e)),
} }
} else { } else {
Err("Got invalid Signature idx".to_string()) Err("Got invalid Signature idx".to_string())
@@ -144,10 +149,15 @@ async fn verify_credential(
let state = state.read().await; let state = state.read().await;
let public_attributes_bytes = vec![
state.voucher_value.to_bytes().to_vec(),
state.voucher_info.to_bytes().to_vec(),
];
let credential = Credential::new( let credential = Credential::new(
state.n_attributes, state.n_attributes,
theta, theta,
state.public_attributes_bytes.clone(), public_attributes_bytes,
state state
.signatures .signatures
.get(idx) .get(idx)
@@ -164,11 +174,13 @@ async fn get_credential(
) -> Result<Vec<Signature>, String> { ) -> Result<Vec<Signature>, String> {
let guard = state.read().await; let guard = state.read().await;
let parsed_urls = parse_url_validators(&validator_urls)?; let parsed_urls = parse_url_validators(&validator_urls)?;
let public_attributes = vec![guard.voucher_value, guard.voucher_info];
let private_attributes = vec![guard.serial_number, guard.binding_number];
let signature = obtain_aggregate_signature( let signature = obtain_aggregate_signature(
&guard.params, &guard.params,
&guard.public_attributes, &public_attributes,
&guard.private_attributes, &private_attributes,
&parsed_urls, &parsed_urls,
) )
.await .await
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@nymproject/nym-validator-client", "name": "@nymproject/nym-validator-client",
"version": "0.17.0", "version": "0.18.0",
"description": "A TypeScript client for interacting with smart contracts in Nym validators", "description": "A TypeScript client for interacting with smart contracts in Nym validators",
"repository": "https://github.com/nymtech/nym", "repository": "https://github.com/nymtech/nym",
"main": "./dist/index.js", "main": "./dist/index.js",
+2 -2
View File
@@ -1,7 +1,7 @@
import { GatewayBond } from "../types"; import {GatewayBond, PagedGatewayResponse} from "../types";
import {INetClient} from "../net-client" import {INetClient} from "../net-client"
import {IQueryClient} from "../query-client"; import {IQueryClient} from "../query-client";
import {PagedGatewayResponse, VALIDATOR_API_GATEWAYS, VALIDATOR_API_PORT} from "../index"; import {VALIDATOR_API_GATEWAYS, VALIDATOR_API_PORT} from "../index";
import axios from "axios"; import axios from "axios";
+3 -3
View File
@@ -1,13 +1,13 @@
import { MixNodeBond } from "../types"; import {MixNodeBond, PagedMixnodeResponse} from "../types";
import { INetClient } from "../net-client" import { INetClient } from "../net-client"
import {IQueryClient} from "../query-client"; import {IQueryClient} from "../query-client";
import {PagedMixnodeResponse, VALIDATOR_API_MIXNODES, VALIDATOR_API_PORT} from "../index"; import {VALIDATOR_API_MIXNODES, VALIDATOR_API_PORT} from "../index";
import axios from "axios"; import axios from "axios";
export { MixnodesCache }; export { MixnodesCache };
/** /**
* There are serious limits in smart contract systems, but we need to keep track of * There are serious limits in smart contract systems, but we need to keep track of
* potentially thousands of nodes. MixnodeCache instances repeatedly make requests for * potentially thousands of nodes. MixnodeCache instances repeatedly make requests for
* paged data about what mixnodes exist, and keep them locally in memory so that they're * paged data about what mixnodes exist, and keep them locally in memory so that they're
* available for querying. * available for querying.
+22 -84
View File
@@ -1,9 +1,19 @@
import NetClient, { INetClient } from "./net-client"; import NetClient, {INetClient} from "./net-client";
import { Gateway, GatewayBond, MixNode, MixNodeBond, SendRequest } from "./types"; import {
import { Bip39, Random } from "@cosmjs/crypto"; StateParams,
import { DirectSecp256k1HdWallet, EncodeObject } from "@cosmjs/proto-signing"; Delegation,
PagedMixDelegationsResponse,
PagedGatewayDelegationsResponse,
MixNodeBond,
MixNode,
GatewayBond,
Gateway,
SendRequest
} from "./types";
import {Bip39, Random} from "@cosmjs/crypto";
import {DirectSecp256k1HdWallet, EncodeObject} from "@cosmjs/proto-signing";
import MixnodesCache from "./caches/mixnodes"; import MixnodesCache from "./caches/mixnodes";
import { buildFeeTable, coin, Coin, coins, StdFee } from "@cosmjs/stargate"; import {buildFeeTable, coin, Coin, coins, StdFee} from "@cosmjs/stargate";
import { import {
ExecuteResult, ExecuteResult,
InstantiateOptions, InstantiateOptions,
@@ -22,17 +32,17 @@ import {
nativeToPrintable nativeToPrintable
} from "./currency"; } from "./currency";
import GatewaysCache from "./caches/gateways"; import GatewaysCache from "./caches/gateways";
import QueryClient, { IQueryClient } from "./query-client"; import QueryClient, {IQueryClient} from "./query-client";
import { nymGasLimits, nymGasPrice } from "./stargate-helper"; import {nymGasLimits, nymGasPrice} from "./stargate-helper";
import { BroadcastTxSuccess, isBroadcastTxFailure } from "@cosmjs/stargate"; import {BroadcastTxSuccess, isBroadcastTxFailure} from "@cosmjs/stargate";
import { makeBankMsgSend } from "./utils"; import {makeBankMsgSend} from "./utils";
export const VALIDATOR_API_PORT = "8080"; export const VALIDATOR_API_PORT = "8080";
export const VALIDATOR_API_GATEWAYS = "v1/gateways"; export const VALIDATOR_API_GATEWAYS = "v1/gateways";
export const VALIDATOR_API_MIXNODES = "v1/mixnodes"; export const VALIDATOR_API_MIXNODES = "v1/mixnodes";
export { coins, coin }; export {coins, coin};
export { Coin }; export {Coin};
export { export {
displayAmountToNative, displayAmountToNative,
nativeCoinToDisplay, nativeCoinToDisplay,
@@ -42,7 +52,7 @@ export {
MappedCoin, MappedCoin,
CoinMap CoinMap
} }
export { nymGasLimits, nymGasPrice } export {nymGasLimits, nymGasPrice}
export default class ValidatorClient { export default class ValidatorClient {
private readonly client: INetClient | IQueryClient private readonly client: INetClient | IQueryClient
@@ -619,75 +629,3 @@ export default class ValidatorClient {
} }
} }
/// One page of a possible multi-page set of mixnodes. The paging interface is quite
/// inconvenient, as we don't have the two pieces of information we need to know
/// in order to do paging nicely (namely `currentPage` and `totalPages` parameters).
///
/// Instead, we have only `start_next_page_after`, i.e. the key of the last record
/// on this page. In order to get the *next* page, CosmWasm looks at that value,
/// finds the next record, and builds the next page starting there. This happens
/// **in series** rather than **in parallel** (!).
///
/// So we have some consistency problems:
///
/// * we can't make requests at a given block height, so the result set
/// which we assemble over time may change while requests are being made.
/// * at some point we will make a request for a `start_next_page_after` key
/// which has just been deleted from the database.
///
/// TODO: more robust error handling on the "deleted key" case.
export type PagedMixnodeResponse = {
nodes: MixNodeBond[],
per_page: number, // TODO: camelCase
start_next_after: string, // TODO: camelCase
}
// a temporary way of achieving the same paging behaviour for the gateways
// the same points made for `PagedResponse` stand here.
export type PagedGatewayResponse = {
nodes: GatewayBond[],
per_page: number, // TODO: camelCase
start_next_after: string, // TODO: camelCase
}
export type MixOwnershipResponse = {
address: string,
has_node: boolean,
}
export type GatewayOwnershipResponse = {
address: string,
has_gateway: boolean,
}
export type StateParams = {
epoch_length: number,
// ideally I'd want to define those as `number` rather than `string`, but
// rust-side they are defined as Uint128 and Decimal that don't have
// native javascript representations and therefore are interpreted as strings after deserialization
minimum_mixnode_bond: string,
minimum_gateway_bond: string,
mixnode_bond_reward_rate: string,
gateway_bond_reward_rate: string,
mixnode_delegation_reward_rate: string,
gateway_delegation_reward_rate: string,
mixnode_active_set_size: number,
}
export type Delegation = {
owner: string,
amount: Coin,
}
export type PagedMixDelegationsResponse = {
node_owner: string,
delegations: Delegation[],
start_next_after: string
}
export type PagedGatewayDelegationsResponse = {
node_owner: string,
delegations: Delegation[],
start_next_after: string
}
+1 -1
View File
@@ -6,7 +6,7 @@ import {
PagedGatewayResponse, PagedMixDelegationsResponse, PagedGatewayResponse, PagedMixDelegationsResponse,
PagedMixnodeResponse, PagedMixnodeResponse,
StateParams StateParams
} from "./index"; } from "./types";
import { DirectSecp256k1HdWallet, EncodeObject } from "@cosmjs/proto-signing"; import { DirectSecp256k1HdWallet, EncodeObject } from "@cosmjs/proto-signing";
import { Coin, StdFee } from "@cosmjs/stargate"; import { Coin, StdFee } from "@cosmjs/stargate";
import { BroadcastTxResponse } from "@cosmjs/stargate" import { BroadcastTxResponse } from "@cosmjs/stargate"
+1 -1
View File
@@ -7,7 +7,7 @@ import {
PagedGatewayResponse, PagedMixDelegationsResponse, PagedGatewayResponse, PagedMixDelegationsResponse,
PagedMixnodeResponse, PagedMixnodeResponse,
StateParams StateParams
} from "./index"; } from "./types";
export interface IQueryClient { export interface IQueryClient {
getBalance(address: string, stakeDenom: string): Promise<Coin | null>; getBalance(address: string, stakeDenom: string): Promise<Coin | null>;
+75
View File
@@ -1,5 +1,80 @@
import { Coin } from "@cosmjs/stargate"; import { Coin } from "@cosmjs/stargate";
/// One page of a possible multi-page set of mixnodes. The paging interface is quite
/// inconvenient, as we don't have the two pieces of information we need to know
/// in order to do paging nicely (namely `currentPage` and `totalPages` parameters).
///
/// Instead, we have only `start_next_page_after`, i.e. the key of the last record
/// on this page. In order to get the *next* page, CosmWasm looks at that value,
/// finds the next record, and builds the next page starting there. This happens
/// **in series** rather than **in parallel** (!).
///
/// So we have some consistency problems:
///
/// * we can't make requests at a given block height, so the result set
/// which we assemble over time may change while requests are being made.
/// * at some point we will make a request for a `start_next_page_after` key
/// which has just been deleted from the database.
///
/// TODO: more robust error handling on the "deleted key" case.
export type PagedMixnodeResponse = {
nodes: MixNodeBond[],
per_page: number, // TODO: camelCase
start_next_after: string, // TODO: camelCase
}
// a temporary way of achieving the same paging behaviour for the gateways
// the same points made for `PagedResponse` stand here.
export type PagedGatewayResponse = {
nodes: GatewayBond[],
per_page: number, // TODO: camelCase
start_next_after: string, // TODO: camelCase
}
export type MixOwnershipResponse = {
address: string,
has_node: boolean,
}
export type GatewayOwnershipResponse = {
address: string,
has_gateway: boolean,
}
export type StateParams = {
epoch_length: number,
// ideally I'd want to define those as `number` rather than `string`, but
// rust-side they are defined as Uint128 and Decimal that don't have
// native javascript representations and therefore are interpreted as strings after deserialization
minimum_mixnode_bond: string,
minimum_gateway_bond: string,
mixnode_bond_reward_rate: string,
gateway_bond_reward_rate: string,
mixnode_delegation_reward_rate: string,
gateway_delegation_reward_rate: string,
mixnode_active_set_size: number,
gateway_active_set_size: number,
}
export type Delegation = {
owner: string,
amount: Coin,
}
export type PagedMixDelegationsResponse = {
node_owner: string,
delegations: Delegation[],
start_next_after: string
}
export type PagedGatewayDelegationsResponse = {
node_owner: string,
delegations: Delegation[],
start_next_after: string
}
export enum Layer { export enum Layer {
Gateway, Gateway,
One, One,
+4 -2
View File
@@ -7,6 +7,7 @@ keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
license = "Apache-2.0" license = "Apache-2.0"
repository = "https://github.com/nymtech/nym" repository = "https://github.com/nymtech/nym"
description = "A webassembly client which can be used to interact with the the Nym privacy platform. Wasm is used for Sphinx packet generation." description = "A webassembly client which can be used to interact with the the Nym privacy platform. Wasm is used for Sphinx packet generation."
rust-version = "1.56"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
@@ -14,6 +15,7 @@ crate-type = ["cdylib", "rlib"]
[features] [features]
default = ["console_error_panic_hook"] default = ["console_error_panic_hook"]
offline-test = [] offline-test = []
coconut = ["coconut-interface", "credentials", "gateway-client/coconut"]
[dependencies] [dependencies]
futures = "0.3" futures = "0.3"
@@ -25,8 +27,8 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] }
url = "2.2" url = "2.2"
# internal # internal
coconut-interface = { path = "../../common/coconut-interface" } coconut-interface = { path = "../../common/coconut-interface", optional = true }
credentials = { path = "../../common/credentials" } credentials = { path = "../../common/credentials", optional = true }
crypto = { path = "../../common/crypto" } crypto = { path = "../../common/crypto" }
nymsphinx = { path = "../../common/nymsphinx" } nymsphinx = { path = "../../common/nymsphinx" }
topology = { path = "../../common/topology" } topology = { path = "../../common/topology" }
+12 -27
View File
@@ -1,11 +1,9 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use coconut_interface::Credential;
use credentials::bandwidth::prepare_for_spending;
use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::{encryption, identity}; use crypto::asymmetric::{encryption, identity};
use futures::channel::mpsc; use futures::channel::mpsc;
use gateway_client::bandwidth::BandwidthController;
use gateway_client::GatewayClient; use gateway_client::GatewayClient;
use nymsphinx::acknowledgements::AckKey; use nymsphinx::acknowledgements::AckKey;
use nymsphinx::addressing::clients::Recipient; use nymsphinx::addressing::clients::Recipient;
@@ -101,35 +99,22 @@ impl NymClient {
self.self_recipient().to_string() self.self_recipient().to_string()
} }
async fn prepare_credential(validators: &[Url], identity_bytes: &[u8]) -> Credential {
let verification_key = obtain_aggregate_verification_key(validators)
.await
.expect("could not obtain aggregate verification key of validators");
let bandwidth_credential =
credentials::bandwidth::obtain_signature(identity_bytes, validators)
.await
.expect("could not obtain bandwidth credential");
// the above would presumably be loaded from a file
// the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff)
prepare_for_spending(identity_bytes, &bandwidth_credential, &verification_key)
.expect("could not prepare out bandwidth credential for spending")
}
// Right now it's impossible to have async exported functions to take `&self` rather than self // Right now it's impossible to have async exported functions to take `&self` rather than self
pub async fn initial_setup(self) -> Self { pub async fn initial_setup(self) -> Self {
let validator_server = self.validator_server.clone(); #[cfg(feature = "coconut")]
let identity_public_key = self.identity.public_key().clone(); let bandwidth_controller = Some(BandwidthController::new(
vec![self.validator_server.clone()],
*self.identity.public_key(),
));
#[cfg(not(feature = "coconut"))]
let bandwidth_controller = None;
let mut client = self.get_and_update_topology().await; let mut client = self.get_and_update_topology().await;
let gateway = client.choose_gateway(); let gateway = client.choose_gateway();
let (mixnet_messages_sender, mixnet_messages_receiver) = mpsc::unbounded(); let (mixnet_messages_sender, mixnet_messages_receiver) = mpsc::unbounded();
let (ack_sender, ack_receiver) = mpsc::unbounded(); let (ack_sender, ack_receiver) = mpsc::unbounded();
let coconut_credential =
Self::prepare_credential(&vec![validator_server], &identity_public_key.to_bytes())
.await;
let mut gateway_client = GatewayClient::new( let mut gateway_client = GatewayClient::new(
gateway.clients_address(), gateway.clients_address(),
Arc::clone(&client.identity), Arc::clone(&client.identity),
@@ -138,7 +123,7 @@ impl NymClient {
mixnet_messages_sender, mixnet_messages_sender,
ack_sender, ack_sender,
DEFAULT_GATEWAY_RESPONSE_TIMEOUT, DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
coconut_credential, bandwidth_controller,
); );
gateway_client gateway_client
@@ -263,8 +248,8 @@ impl NymClient {
pub(crate) async fn get_nym_topology(&self) -> NymTopology { pub(crate) async fn get_nym_topology(&self) -> NymTopology {
let validator_client = validator_client::ApiClient::new(self.validator_server.clone()); let validator_client = validator_client::ApiClient::new(self.validator_server.clone());
let mixnodes = match validator_client.get_cached_mixnodes().await { let mixnodes = match validator_client.get_cached_active_mixnodes().await {
Err(err) => panic!("{}", err), Err(err) => panic!("{:?}", err),
Ok(mixes) => mixes, Ok(mixes) => mixes,
}; };
+15 -1
View File
@@ -10,14 +10,19 @@ edition = "2018"
# TODO: (for this and other crates), similarly to 'tokio', import only required "futures" modules rather than # TODO: (for this and other crates), similarly to 'tokio', import only required "futures" modules rather than
# the entire crate # the entire crate
futures = "0.3" futures = "0.3"
json = "0.12.4"
log = "0.4" log = "0.4"
thiserror = "1.0"
url = "2.2"
rand = { version = "0.7.3", features = ["wasm-bindgen"] } rand = { version = "0.7.3", features = ["wasm-bindgen"] }
# internal # internal
credentials = { path = "../../credentials" }
crypto = { path = "../../crypto" } crypto = { path = "../../crypto" }
gateway-requests = { path = "../../../gateway/gateway-requests" } gateway-requests = { path = "../../../gateway/gateway-requests" }
nymsphinx = { path = "../../nymsphinx" } nymsphinx = { path = "../../nymsphinx" }
coconut-interface = { path = "../../coconut-interface" } coconut-interface = { path = "../../coconut-interface", optional = true }
network-defaults = { path = "../../network-defaults" }
[dependencies.tungstenite] [dependencies.tungstenite]
version = "0.13" version = "0.13"
@@ -31,6 +36,12 @@ features = ["macros", "rt", "net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
version = "0.14" version = "0.14"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.secp256k1]
version = "0.20.3"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.web3]
version = "0.17.0"
# wasm-only dependencies # wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen] [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
version = "0.2" version = "0.2"
@@ -57,3 +68,6 @@ features = ["js"]
[dev-dependencies] [dev-dependencies]
# for tests # for tests
#url = "2.1" #url = "2.1"
[features]
coconut = ["gateway-requests/coconut", "coconut-interface"]
@@ -0,0 +1,247 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "coconut")]
use credentials::coconut::{
bandwidth::{
obtain_signature, prepare_for_spending, BandwidthVoucherAttributes, TOTAL_ATTRIBUTES,
},
utils::obtain_aggregate_verification_key,
};
#[cfg(not(feature = "coconut"))]
use credentials::token::bandwidth::TokenCredential;
#[cfg(not(feature = "coconut"))]
use crypto::asymmetric::identity;
use crypto::asymmetric::identity::PublicKey;
use network_defaults::BANDWIDTH_VALUE;
#[cfg(not(feature = "coconut"))]
use network_defaults::{
eth_contract::ETH_JSON_ABI, ETH_BURN_FUNCTION_NAME, ETH_CONTRACT_ADDRESS, ETH_MIN_BLOCK_DEPTH,
TOKENS_TO_BURN,
};
#[cfg(not(feature = "coconut"))]
use rand::rngs::OsRng;
#[cfg(not(feature = "coconut"))]
use secp256k1::SecretKey;
#[cfg(not(feature = "coconut"))]
use std::io::Write;
#[cfg(not(feature = "coconut"))]
use std::str::FromStr;
#[cfg(not(feature = "coconut"))]
use web3::{
contract::{Contract, Options},
transports::Http,
types::{Address, Bytes, U256, U64},
Web3,
};
use crate::error::GatewayClientError;
#[cfg(not(feature = "coconut"))]
pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
Contract::from_json(
web3.eth(),
Address::from(ETH_CONTRACT_ADDRESS),
json::parse(ETH_JSON_ABI)
.expect("Invalid json abi")
.dump()
.as_bytes(),
)
.expect("Invalid json abi")
}
#[derive(Clone)]
pub struct BandwidthController {
#[cfg(feature = "coconut")]
validator_endpoints: Vec<url::Url>,
#[cfg(feature = "coconut")]
identity: PublicKey,
#[cfg(not(feature = "coconut"))]
contract: Contract<Http>,
#[cfg(not(feature = "coconut"))]
eth_private_key: SecretKey,
#[cfg(not(feature = "coconut"))]
backup_bandwidth_token_keys_dir: std::path::PathBuf,
}
impl BandwidthController {
#[cfg(feature = "coconut")]
pub fn new(validator_endpoints: Vec<url::Url>, identity: PublicKey) -> Self {
BandwidthController {
validator_endpoints,
identity,
}
}
#[cfg(not(feature = "coconut"))]
pub fn new(
eth_endpoint: String,
eth_private_key: String,
backup_bandwidth_token_keys_dir: std::path::PathBuf,
) -> Result<Self, GatewayClientError> {
// Fail early, on invalid url
let transport =
Http::new(&eth_endpoint).map_err(|_| GatewayClientError::InvalidURL(eth_endpoint))?;
let web3 = web3::Web3::new(transport);
// Fail early, on invalid abi
let contract = eth_contract(web3);
let eth_private_key = secp256k1::SecretKey::from_str(&eth_private_key)
.map_err(|_| GatewayClientError::InvalidEthereumPrivateKey)?;
Ok(BandwidthController {
contract,
eth_private_key,
backup_bandwidth_token_keys_dir,
})
}
#[cfg(not(feature = "coconut"))]
fn backup_keypair(&self, keypair: &identity::KeyPair) -> Result<(), GatewayClientError> {
std::fs::create_dir_all(&self.backup_bandwidth_token_keys_dir)?;
let file_path = self
.backup_bandwidth_token_keys_dir
.join(keypair.public_key().to_base58_string());
let mut file = std::fs::File::create(file_path)?;
file.write_all(&keypair.private_key().to_bytes())?;
Ok(())
}
#[cfg(feature = "coconut")]
pub async fn prepare_coconut_credential(
&self,
) -> Result<coconut_interface::Credential, GatewayClientError> {
let verification_key = obtain_aggregate_verification_key(&self.validator_endpoints).await?;
let params = coconut_interface::Parameters::new(TOTAL_ATTRIBUTES).unwrap();
// TODO: Decide what is the value and additional info associated with the bandwidth voucher
let bandwidth_credential_attributes = BandwidthVoucherAttributes {
serial_number: params.random_scalar(),
binding_number: params.random_scalar(),
voucher_value: coconut_interface::hash_to_scalar(BANDWIDTH_VALUE.to_be_bytes()),
voucher_info: coconut_interface::hash_to_scalar(
String::from("BandwidthVoucher").as_bytes(),
),
};
let bandwidth_credential = obtain_signature(
&params,
&bandwidth_credential_attributes,
&self.validator_endpoints,
)
.await?;
// the above would presumably be loaded from a file
// the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff)
Ok(prepare_for_spending(
&self.identity.to_bytes(),
&bandwidth_credential,
&bandwidth_credential_attributes,
&verification_key,
)?)
}
#[cfg(not(feature = "coconut"))]
pub async fn prepare_token_credential(
&self,
gateway_identity: PublicKey,
) -> Result<TokenCredential, GatewayClientError> {
let mut rng = OsRng;
let kp = identity::KeyPair::new(&mut rng);
self.backup_keypair(&kp)?;
let verification_key = *kp.public_key();
let signed_verification_key = kp.private_key().sign(&verification_key.to_bytes());
self.buy_token_credential(verification_key, signed_verification_key)
.await?;
let message: Vec<u8> = verification_key
.to_bytes()
.iter()
.chain(gateway_identity.to_bytes().iter())
.copied()
.collect();
let signature = kp.private_key().sign(&message);
Ok(TokenCredential::new(
verification_key,
gateway_identity,
BANDWIDTH_VALUE,
signature,
))
}
#[cfg(not(feature = "coconut"))]
pub async fn buy_token_credential(
&self,
verification_key: PublicKey,
signed_verification_key: identity::Signature,
) -> Result<(), GatewayClientError> {
// 0 means a transaction failure, 1 means success
let confirmations = if cfg!(debug_assertions) {
1
} else {
ETH_MIN_BLOCK_DEPTH
};
// 15 seconds per confirmation block + 10 seconds of network overhead
log::info!(
"Waiting for Ethereum transaction. This should take about {} seconds",
confirmations * 15 + 10
);
let recipt = self
.contract
.signed_call_with_confirmations(
ETH_BURN_FUNCTION_NAME,
(
U256::from(TOKENS_TO_BURN),
U256::from(&verification_key.to_bytes()),
Bytes(signed_verification_key.to_bytes().to_vec()),
),
Options::default(),
confirmations,
&self.eth_private_key,
)
.await?;
if Some(U64::from(0)) == recipt.status {
Err(GatewayClientError::BurnTokenError(
web3::Error::InvalidResponse(format!(
"Transaction status is 0 (failure): {:?}",
recipt.logs,
)),
))
} else {
log::info!(
"Bought bandwidth on Ethereum: {} MB",
BANDWIDTH_VALUE / 1024 / 1024
);
Ok(())
}
}
}
#[cfg(not(feature = "coconut"))]
#[cfg(test)]
mod tests {
use network_defaults::ETH_EVENT_NAME;
use super::*;
#[test]
fn parse_contract() {
let transport =
Http::new("https://rinkeby.infura.io/v3/00000000000000000000000000000000").unwrap();
let web3 = web3::Web3::new(transport);
// test no panic occurs
eth_contract(web3);
}
#[test]
fn check_event_name_constant_against_abi() {
let transport =
Http::new("https://rinkeby.infura.io/v3/00000000000000000000000000000000").unwrap();
let web3 = web3::Web3::new(transport);
let contract = eth_contract(web3);
assert!(contract.abi().event(ETH_EVENT_NAME).is_ok());
}
}
+139 -59
View File
@@ -1,6 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::bandwidth::BandwidthController;
use crate::cleanup_socket_message; use crate::cleanup_socket_message;
use crate::error::GatewayClientError; use crate::error::GatewayClientError;
use crate::packet_router::PacketRouter; use crate::packet_router::PacketRouter;
@@ -8,7 +9,10 @@ pub use crate::packet_router::{
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender, AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
}; };
use crate::socket_state::{PartiallyDelegated, SocketState}; use crate::socket_state::{PartiallyDelegated, SocketState};
#[cfg(feature = "coconut")]
use coconut_interface::Credential; use coconut_interface::Credential;
#[cfg(not(feature = "coconut"))]
use credentials::token::bandwidth::TokenCredential;
use crypto::asymmetric::identity; use crypto::asymmetric::identity;
use futures::{FutureExt, SinkExt, StreamExt}; use futures::{FutureExt, SinkExt, StreamExt};
use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes; use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
@@ -36,8 +40,7 @@ const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
pub struct GatewayClient { pub struct GatewayClient {
authenticated: bool, authenticated: bool,
// TODO: This should be replaced by an actual bandwidth value, with 0 meaning no bandwidth bandwidth_remaining: i64,
has_bandwidth: bool,
gateway_address: String, gateway_address: String,
gateway_identity: identity::PublicKey, gateway_identity: identity::PublicKey,
local_identity: Arc<identity::KeyPair>, local_identity: Arc<identity::KeyPair>,
@@ -45,6 +48,7 @@ pub struct GatewayClient {
connection: SocketState, connection: SocketState,
packet_router: PacketRouter, packet_router: PacketRouter,
response_timeout_duration: Duration, response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController>,
// reconnection related variables // reconnection related variables
/// Specifies whether client should try to reconnect to gateway on connection failure. /// Specifies whether client should try to reconnect to gateway on connection failure.
@@ -54,7 +58,6 @@ pub struct GatewayClient {
reconnection_attempts: usize, reconnection_attempts: usize,
/// Delay between each subsequent reconnection attempt. /// Delay between each subsequent reconnection attempt.
reconnection_backoff: Duration, reconnection_backoff: Duration,
coconut_credential: Credential,
} }
impl GatewayClient { impl GatewayClient {
@@ -68,11 +71,11 @@ impl GatewayClient {
mixnet_message_sender: MixnetMessageSender, mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender, ack_sender: AcknowledgementSender,
response_timeout_duration: Duration, response_timeout_duration: Duration,
coconut_credential: Credential, bandwidth_controller: Option<BandwidthController>,
) -> Self { ) -> Self {
GatewayClient { GatewayClient {
authenticated: false, authenticated: false,
has_bandwidth: false, bandwidth_remaining: 0,
gateway_address, gateway_address,
gateway_identity, gateway_identity,
local_identity, local_identity,
@@ -80,10 +83,10 @@ impl GatewayClient {
connection: SocketState::NotConnected, connection: SocketState::NotConnected,
packet_router: PacketRouter::new(ack_sender, mixnet_message_sender), packet_router: PacketRouter::new(ack_sender, mixnet_message_sender),
response_timeout_duration, response_timeout_duration,
bandwidth_controller,
should_reconnect_on_failure: true, should_reconnect_on_failure: true,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS, reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF, reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
coconut_credential,
} }
} }
@@ -104,7 +107,6 @@ impl GatewayClient {
gateway_address: String, gateway_address: String,
gateway_identity: identity::PublicKey, gateway_identity: identity::PublicKey,
local_identity: Arc<identity::KeyPair>, local_identity: Arc<identity::KeyPair>,
coconut_credential: Credential,
response_timeout_duration: Duration, response_timeout_duration: Duration,
) -> Self { ) -> Self {
use futures::channel::mpsc; use futures::channel::mpsc;
@@ -117,7 +119,7 @@ impl GatewayClient {
GatewayClient { GatewayClient {
authenticated: false, authenticated: false,
has_bandwidth: false, bandwidth_remaining: 0,
gateway_address, gateway_address,
gateway_identity, gateway_identity,
local_identity, local_identity,
@@ -125,10 +127,10 @@ impl GatewayClient {
connection: SocketState::NotConnected, connection: SocketState::NotConnected,
packet_router, packet_router,
response_timeout_duration, response_timeout_duration,
bandwidth_controller: None,
should_reconnect_on_failure: false, should_reconnect_on_failure: false,
reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS, reconnection_attempts: DEFAULT_RECONNECTION_ATTEMPTS,
reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF, reconnection_backoff: DEFAULT_RECONNECTION_BACKOFF,
coconut_credential,
} }
} }
@@ -136,10 +138,14 @@ impl GatewayClient {
self.gateway_identity self.gateway_identity
} }
pub fn remaining_bandwidth(&self) -> i64 {
self.bandwidth_remaining
}
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
async fn _close_connection(&mut self) -> Result<(), GatewayClientError> { async fn _close_connection(&mut self) -> Result<(), GatewayClientError> {
match std::mem::replace(&mut self.connection, SocketState::NotConnected) { match std::mem::replace(&mut self.connection, SocketState::NotConnected) {
SocketState::Available(mut socket) => Ok(socket.close(None).await?), SocketState::Available(mut socket) => Ok((*socket).close(None).await?),
SocketState::PartiallyDelegated(_) => { SocketState::PartiallyDelegated(_) => {
unreachable!("this branch should have never been reached!") unreachable!("this branch should have never been reached!")
} }
@@ -151,7 +157,7 @@ impl GatewayClient {
async fn _close_connection(&mut self) -> Result<(), GatewayClientError> { async fn _close_connection(&mut self) -> Result<(), GatewayClientError> {
match std::mem::replace(&mut self.connection, SocketState::NotConnected) { match std::mem::replace(&mut self.connection, SocketState::NotConnected) {
SocketState::Available(mut socket) => { SocketState::Available(mut socket) => {
socket.close(None).await; (*socket).close(None).await;
Ok(()) Ok(())
} }
SocketState::PartiallyDelegated(_) => { SocketState::PartiallyDelegated(_) => {
@@ -176,7 +182,7 @@ impl GatewayClient {
Err(e) => return Err(GatewayClientError::NetworkError(e)), Err(e) => return Err(GatewayClientError::NetworkError(e)),
}; };
self.connection = SocketState::Available(ws_stream); self.connection = SocketState::Available(Box::new(ws_stream));
Ok(()) Ok(())
} }
@@ -187,7 +193,7 @@ impl GatewayClient {
Err(e) => return Err(GatewayClientError::NetworkErrorWasm(e)), Err(e) => return Err(GatewayClientError::NetworkErrorWasm(e)),
}; };
self.connection = SocketState::Available(ws_stream); self.connection = SocketState::Available(Box::new(ws_stream));
Ok(()) Ok(())
} }
@@ -430,8 +436,12 @@ impl GatewayClient {
ClientControlRequest::new_authenticate(self_address, encrypted_address, iv).into(); ClientControlRequest::new_authenticate(self_address, encrypted_address, iv).into();
match self.send_websocket_message(msg).await? { match self.send_websocket_message(msg).await? {
ServerResponse::Authenticate { status } => { ServerResponse::Authenticate {
status,
bandwidth_remaining,
} => {
self.authenticated = status; self.authenticated = status;
self.bandwidth_remaining = bandwidth_remaining;
Ok(()) Ok(())
} }
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)), ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
@@ -456,6 +466,53 @@ impl GatewayClient {
} }
} }
#[cfg(feature = "coconut")]
async fn claim_coconut_bandwidth(
&mut self,
credential: Credential,
) -> Result<(), GatewayClientError> {
let mut rng = OsRng;
let iv = IV::new_random(&mut rng);
let msg = ClientControlRequest::new_enc_coconut_bandwidth_credential(
&credential,
self.shared_key.as_ref().unwrap(),
iv,
)
.ok_or(GatewayClientError::SerializeCredential)?
.into();
self.bandwidth_remaining = match self.send_websocket_message(msg).await? {
ServerResponse::Bandwidth { available_total } => Ok(available_total),
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
_ => Err(GatewayClientError::UnexpectedResponse),
}?;
Ok(())
}
#[cfg(not(feature = "coconut"))]
async fn claim_token_bandwidth(
&mut self,
credential: TokenCredential,
) -> Result<(), GatewayClientError> {
let mut rng = OsRng;
let iv = IV::new_random(&mut rng);
let msg = ClientControlRequest::new_enc_token_bandwidth_credential(
&credential,
self.shared_key.as_ref().unwrap(),
iv,
)
.into();
self.bandwidth_remaining = match self.send_websocket_message(msg).await? {
ServerResponse::Bandwidth { available_total } => Ok(available_total),
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
_ => Err(GatewayClientError::UnexpectedResponse),
}?;
Ok(())
}
pub async fn claim_bandwidth(&mut self) -> Result<(), GatewayClientError> { pub async fn claim_bandwidth(&mut self) -> Result<(), GatewayClientError> {
if !self.authenticated { if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated); return Err(GatewayClientError::NotAuthenticated);
@@ -463,23 +520,38 @@ impl GatewayClient {
if self.shared_key.is_none() { if self.shared_key.is_none() {
return Err(GatewayClientError::NoSharedKeyAvailable); return Err(GatewayClientError::NoSharedKeyAvailable);
} }
if self.bandwidth_controller.is_none() {
return Err(GatewayClientError::NoBandwidthControllerAvailable);
}
let mut rng = OsRng; warn!("Not enough bandwidth. Trying to get more bandwidth, this might take a while");
let iv = IV::new_random(&mut rng);
let msg = ClientControlRequest::new_enc_bandwidth_credential( #[cfg(feature = "coconut")]
&self.coconut_credential, let credential = self
self.shared_key.as_ref().unwrap(), .bandwidth_controller
iv, .as_ref()
) .unwrap()
.ok_or(GatewayClientError::SerializeCredential)? .prepare_coconut_credential()
.into(); .await?;
self.has_bandwidth = match self.send_websocket_message(msg).await? { #[cfg(not(feature = "coconut"))]
ServerResponse::Bandwidth { status } => Ok(status), let credential = self
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)), .bandwidth_controller
_ => Err(GatewayClientError::UnexpectedResponse), .as_ref()
}?; .unwrap()
Ok(()) .prepare_token_credential(self.gateway_identity)
.await?;
#[cfg(feature = "coconut")]
return self.claim_coconut_bandwidth(credential).await;
#[cfg(not(feature = "coconut"))]
return self.claim_token_bandwidth(credential).await;
}
fn estimate_required_bandwidth(&self, packets: &[MixPacket]) -> i64 {
packets
.iter()
.map(|packet| packet.sphinx_packet().len())
.sum::<usize>() as i64
} }
pub async fn batch_send_mix_packets( pub async fn batch_send_mix_packets(
@@ -489,8 +561,16 @@ impl GatewayClient {
if !self.authenticated { if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated); return Err(GatewayClientError::NotAuthenticated);
} }
if !self.has_bandwidth { if self.estimate_required_bandwidth(&packets) > self.bandwidth_remaining {
return Err(GatewayClientError::NotEnoughBandwidth); // Try to claim more bandwidth first, and return an error only if that is still not
// enough (the current granularity for bandwidth should be sufficient)
self.claim_bandwidth().await?;
if self.estimate_required_bandwidth(&packets) > self.bandwidth_remaining {
return Err(GatewayClientError::NotEnoughBandwidth(
self.estimate_required_bandwidth(&packets),
self.bandwidth_remaining,
));
}
} }
if !self.connection.is_established() { if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished); return Err(GatewayClientError::ConnectionNotEstablished);
@@ -521,15 +601,10 @@ impl GatewayClient {
} }
} }
pub async fn send_ping_message(&mut self) -> Result<(), GatewayClientError> { async fn send_with_reconnection_on_failure(
if !self.connection.is_established() { &mut self,
return Err(GatewayClientError::ConnectionNotEstablished); msg: Message,
} ) -> Result<(), GatewayClientError> {
// as per RFC6455 section 5.5.2, `Ping frame MAY include "Application data".`
// so we don't need to include any here.
let msg = Message::Ping(Vec::new());
if let Err(err) = self.send_websocket_message_without_response(msg).await { if let Err(err) = self.send_websocket_message_without_response(msg).await {
if err.is_closed_connection() && self.should_reconnect_on_failure { if err.is_closed_connection() && self.should_reconnect_on_failure {
info!("Going to attempt a reconnection"); info!("Going to attempt a reconnection");
@@ -542,6 +617,17 @@ impl GatewayClient {
} }
} }
pub async fn send_ping_message(&mut self) -> Result<(), GatewayClientError> {
if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished);
}
// as per RFC6455 section 5.5.2, `Ping frame MAY include "Application data".`
// so we don't need to include any here.
let msg = Message::Ping(Vec::new());
self.send_with_reconnection_on_failure(msg).await
}
// TODO: possibly make responses optional // TODO: possibly make responses optional
pub async fn send_mix_packet( pub async fn send_mix_packet(
&mut self, &mut self,
@@ -550,8 +636,16 @@ impl GatewayClient {
if !self.authenticated { if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated); return Err(GatewayClientError::NotAuthenticated);
} }
if !self.has_bandwidth { if (mix_packet.sphinx_packet().len() as i64) > self.bandwidth_remaining {
return Err(GatewayClientError::NotEnoughBandwidth); // Try to claim more bandwidth first, and return an error only if that is still not
// enough
self.claim_bandwidth().await?;
if (mix_packet.sphinx_packet().len() as i64) > self.bandwidth_remaining {
return Err(GatewayClientError::NotEnoughBandwidth(
mix_packet.sphinx_packet().len() as i64,
self.bandwidth_remaining,
));
}
} }
if !self.connection.is_established() { if !self.connection.is_established() {
return Err(GatewayClientError::ConnectionNotEstablished); return Err(GatewayClientError::ConnectionNotEstablished);
@@ -563,17 +657,7 @@ impl GatewayClient {
.as_ref() .as_ref()
.expect("no shared key present even though we're authenticated!"), .expect("no shared key present even though we're authenticated!"),
); );
self.send_with_reconnection_on_failure(msg).await
if let Err(err) = self.send_websocket_message_without_response(msg).await {
if err.is_closed_connection() && self.should_reconnect_on_failure {
info!("Going to attempt a reconnection");
self.attempt_reconnection().await
} else {
Err(err)
}
} else {
Ok(())
}
} }
async fn recover_socket_connection(&mut self) -> Result<(), GatewayClientError> { async fn recover_socket_connection(&mut self) -> Result<(), GatewayClientError> {
@@ -589,7 +673,7 @@ impl GatewayClient {
_ => unreachable!(), _ => unreachable!(),
}; };
self.connection = SocketState::Available(conn); self.connection = SocketState::Available(Box::new(conn));
Ok(()) Ok(())
} }
@@ -598,9 +682,6 @@ impl GatewayClient {
if !self.authenticated { if !self.authenticated {
return Err(GatewayClientError::NotAuthenticated); return Err(GatewayClientError::NotAuthenticated);
} }
if !self.has_bandwidth {
return Err(GatewayClientError::NotEnoughBandwidth);
}
if self.connection.is_partially_delegated() { if self.connection.is_partially_delegated() {
return Ok(()); return Ok(());
} }
@@ -612,7 +693,7 @@ impl GatewayClient {
match std::mem::replace(&mut self.connection, SocketState::Invalid) { match std::mem::replace(&mut self.connection, SocketState::Invalid) {
SocketState::Available(conn) => { SocketState::Available(conn) => {
PartiallyDelegated::split_and_listen_for_mixnet_messages( PartiallyDelegated::split_and_listen_for_mixnet_messages(
conn, *conn,
self.packet_router.clone(), self.packet_router.clone(),
Arc::clone( Arc::clone(
self.shared_key self.shared_key
@@ -633,7 +714,6 @@ impl GatewayClient {
self.establish_connection().await?; self.establish_connection().await?;
} }
let shared_key = self.perform_initial_authentication().await?; let shared_key = self.perform_initial_authentication().await?;
self.claim_bandwidth().await?;
// this call is NON-blocking // this call is NON-blocking
self.start_listening_for_mixnet_messages()?; self.start_listening_for_mixnet_messages()?;
+63 -76
View File
@@ -2,39 +2,83 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use gateway_requests::registration::handshake::error::HandshakeError; use gateway_requests::registration::handshake::error::HandshakeError;
use std::fmt::{self, Error, Formatter};
use std::io; use std::io;
use thiserror::Error;
use tungstenite::Error as WsError; use tungstenite::Error as WsError;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsValue; use wasm_bindgen::JsValue;
#[cfg(not(feature = "coconut"))]
use web3::Error as Web3Error;
#[derive(Debug)] #[derive(Debug, Error)]
pub enum GatewayClientError { pub enum GatewayClientError {
#[error("Connection to the gateway is not established")]
ConnectionNotEstablished, ConnectionNotEstablished,
#[error("Gateway returned an error response - {0}")]
GatewayError(String), GatewayError(String),
NetworkError(WsError),
#[error("There was a network error - {0}")]
NetworkError(#[from] WsError),
// TODO: see if `JsValue` is a reasonable type for this // TODO: see if `JsValue` is a reasonable type for this
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[error("There was a network error")]
NetworkErrorWasm(JsValue), NetworkErrorWasm(JsValue),
NoSharedKeyAvailable, #[cfg(not(feature = "coconut"))]
ConnectionAbruptlyClosed, #[error("Could not backup keypair - {0}")]
MalformedResponse, IOError(#[from] std::io::Error),
SerializeCredential,
NotAuthenticated,
NotEnoughBandwidth,
UnexpectedResponse,
ConnectionInInvalidState,
RegistrationFailure(HandshakeError),
AuthenticationFailure,
Timeout,
}
impl From<WsError> for GatewayClientError { #[cfg(not(feature = "coconut"))]
fn from(err: WsError) -> Self { #[error("Could not burn ERC20 token in Ethereum smart contract - {0}")]
GatewayClientError::NetworkError(err) BurnTokenError(#[from] Web3Error),
}
#[cfg(not(feature = "coconut"))]
#[error("Invalid Ethereum private key")]
InvalidEthereumPrivateKey,
#[error("Invalid URL - {0}")]
InvalidURL(String),
#[error("No shared key was provided or obtained")]
NoSharedKeyAvailable,
#[error("No bandwidth controller provided")]
NoBandwidthControllerAvailable,
#[error("Credential error - {0}")]
CredentialError(#[from] credentials::error::Error),
#[error("Connection was abruptly closed")]
ConnectionAbruptlyClosed,
#[error("Received response was malformed")]
MalformedResponse,
#[error("Credential could not be serialized")]
SerializeCredential,
#[error("Client is not authenticated")]
NotAuthenticated,
#[error("Client does not have enough bandwidth: estimated {0}, remaining: {1}")]
NotEnoughBandwidth(i64, i64),
#[error("Received an unexpected response")]
UnexpectedResponse,
#[error("Connection is in an invalid state - please send a bug report")]
ConnectionInInvalidState,
#[error("Failed to finish registration handshake - {0}")]
RegistrationFailure(HandshakeError),
#[error("Authentication failure")]
AuthenticationFailure,
#[error("Timed out")]
Timeout,
} }
impl GatewayClientError { impl GatewayClientError {
@@ -54,60 +98,3 @@ impl GatewayClientError {
} }
} }
} }
#[cfg(target_arch = "wasm32")]
impl From<JsValue> for GatewayClientError {
fn from(err: JsValue) -> Self {
GatewayClientError::NetworkErrorWasm(err)
}
}
// better human readable representation of the error, mostly so that GatewayClientError
// would implement std::error::Error
impl fmt::Display for GatewayClientError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
match self {
GatewayClientError::ConnectionNotEstablished => {
write!(f, "connection to the gateway is not established")
}
GatewayClientError::NoSharedKeyAvailable => {
write!(f, "no shared key was provided or obtained")
}
GatewayClientError::NotAuthenticated => write!(f, "client is not authenticated"),
GatewayClientError::NetworkError(err) => {
write!(f, "there was a network error - {}", err)
}
#[cfg(target_arch = "wasm32")]
GatewayClientError::NetworkErrorWasm(err) => {
write!(f, "there was a network error - {:?}", err)
}
GatewayClientError::ConnectionAbruptlyClosed => {
write!(f, "connection was abruptly closed")
}
GatewayClientError::Timeout => write!(f, "timed out"),
GatewayClientError::MalformedResponse => write!(f, "received response was malformed"),
GatewayClientError::ConnectionInInvalidState => write!(
f,
"connection is in an invalid state - please send a bug report"
),
GatewayClientError::RegistrationFailure(handshake_err) => write!(
f,
"failed to finish registration handshake - {}",
handshake_err
),
GatewayClientError::AuthenticationFailure => write!(f, "authentication failure"),
GatewayClientError::GatewayError(err) => {
write!(f, "gateway returned an error response - {}", err)
}
GatewayClientError::UnexpectedResponse => write!(f, "received an unexpected response"),
GatewayClientError::NotEnoughBandwidth => {
write!(f, "client does not have enough bandwidth")
}
GatewayClientError::SerializeCredential => {
write!(f, "credential could not be serialized")
}
}
}
}
@@ -8,6 +8,7 @@ pub use packet_router::{
}; };
use tungstenite::{protocol::Message, Error as WsError}; use tungstenite::{protocol::Message, Error as WsError};
pub mod bandwidth;
pub mod client; pub mod client;
pub mod error; pub mod error;
pub mod packet_router; pub mod packet_router;
@@ -119,7 +119,7 @@ impl PartiallyDelegated {
} }
.is_err() .is_err()
{ {
panic!("failed to send back `mixnet_receiver_future` result on the oneshot channel") warn!("failed to send back `mixnet_receiver_future` result on the oneshot channel")
} }
}; };
@@ -173,9 +173,9 @@ impl PartiallyDelegated {
// this call failing is incredibly unlikely, but not impossible. // this call failing is incredibly unlikely, but not impossible.
// basically the gateway connection must have failed after executing previous line but // basically the gateway connection must have failed after executing previous line but
// before starting execution of this one. // before starting execution of this one.
if notify.send(()).is_err() { notify
return Err(GatewayClientError::ConnectionAbruptlyClosed); .send(())
} .map_err(|_| GatewayClientError::ConnectionAbruptlyClosed)?;
let stream_results: Result<_, GatewayClientError> = stream_receiver let stream_results: Result<_, GatewayClientError> = stream_receiver
.await .await
@@ -193,7 +193,7 @@ impl PartiallyDelegated {
// by notifying the future owning it to finish the execution and awaiting the result // by notifying the future owning it to finish the execution and awaiting the result
// which should be almost immediate (or an invalid state which should never, ever happen) // which should be almost immediate (or an invalid state which should never, ever happen)
pub(crate) enum SocketState { pub(crate) enum SocketState {
Available(WsConn), Available(Box<WsConn>),
PartiallyDelegated(PartiallyDelegated), PartiallyDelegated(PartiallyDelegated),
NotConnected, NotConnected,
Invalid, Invalid,
@@ -3,6 +3,7 @@ name = "validator-client"
version = "0.1.0" version = "0.1.0"
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
edition = "2018" edition = "2018"
rust-version = "1.56"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -25,12 +26,13 @@ network-defaults = { path = "../../network-defaults" }
async-trait = { version = "0.1.51", optional = true } async-trait = { version = "0.1.51", optional = true }
bip39 = { version = "1", features = ["rand"], optional = true } bip39 = { version = "1", features = ["rand"], optional = true }
config = { path = "../../config", optional = true } config = { path = "../../config", optional = true }
cosmrs = { version = "0.1", features = ["rpc", "bip32", "cosmwasm"], optional = true } cosmrs = { version = "0.3", features = ["rpc", "bip32", "cosmwasm"], optional = true }
prost = { version = "0.7", default-features = false, optional = true } prost = { version = "0.9", default-features = false, optional = true }
flate2 = { version = "1.0.20", optional = true } flate2 = { version = "1.0.20", optional = true }
sha2 = { version = "0.9.5", optional = true } sha2 = { version = "0.9.5", optional = true }
itertools = { version = "0.10", optional = true } itertools = { version = "0.10", optional = true }
cosmwasm-std = { git = "https://github.com/jstuczyn/cosmwasm", branch="0.14.1-updatedk256", optional = true } cosmwasm-std = { git = "https://github.com/jstuczyn/cosmwasm", branch="0.14.1-updatedk256", optional = true }
ts-rs = {version = "5.1", optional = true}
[features] [features]
nymd-client = ["async-trait", "bip39", "config", "cosmrs", "prost", "flate2", "sha2", "itertools", "cosmwasm-std"] nymd-client = ["async-trait", "bip39", "config", "cosmrs", "prost", "flate2", "sha2", "itertools", "cosmwasm-std"]
+103 -94
View File
@@ -5,9 +5,14 @@
use crate::nymd::{ use crate::nymd::{
error::NymdError, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient, error::NymdError, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient,
}; };
#[cfg(feature = "nymd-client")]
use mixnet_contract::StateParams;
use crate::{validator_api, ValidatorClientError}; use crate::{validator_api, ValidatorClientError};
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse}; use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
use mixnet_contract::{GatewayBond, MixNodeBond}; use mixnet_contract::{GatewayBond, MixNodeBond, MixnodeRewardingStatusResponse};
#[cfg(feature = "nymd-client")]
use mixnet_contract::{RawDelegationData, RewardingIntervalResponse};
use url::Url; use url::Url;
#[cfg(feature = "nymd-client")] #[cfg(feature = "nymd-client")]
@@ -19,7 +24,6 @@ pub struct Config {
mixnode_page_limit: Option<u32>, mixnode_page_limit: Option<u32>,
gateway_page_limit: Option<u32>, gateway_page_limit: Option<u32>,
mixnode_delegations_page_limit: Option<u32>, mixnode_delegations_page_limit: Option<u32>,
gateway_delegations_page_limit: Option<u32>,
} }
#[cfg(feature = "nymd-client")] #[cfg(feature = "nymd-client")]
@@ -36,7 +40,6 @@ impl Config {
mixnode_page_limit: None, mixnode_page_limit: None,
gateway_page_limit: None, gateway_page_limit: None,
mixnode_delegations_page_limit: None, mixnode_delegations_page_limit: None,
gateway_delegations_page_limit: None,
} }
} }
@@ -54,11 +57,6 @@ impl Config {
self.mixnode_delegations_page_limit = limit; self.mixnode_delegations_page_limit = limit;
self self
} }
pub fn with_gateway_delegations_page_limit(mut self, limit: Option<u32>) -> Config {
self.gateway_delegations_page_limit = limit;
self
}
} }
#[cfg(feature = "nymd-client")] #[cfg(feature = "nymd-client")]
@@ -69,7 +67,6 @@ pub struct Client<C> {
mixnode_page_limit: Option<u32>, mixnode_page_limit: Option<u32>,
gateway_page_limit: Option<u32>, gateway_page_limit: Option<u32>,
mixnode_delegations_page_limit: Option<u32>, mixnode_delegations_page_limit: Option<u32>,
gateway_delegations_page_limit: Option<u32>,
// ideally they would have been read-only, but unfortunately rust doesn't have such features // ideally they would have been read-only, but unfortunately rust doesn't have such features
pub validator_api: validator_api::Client, pub validator_api: validator_api::Client,
@@ -95,7 +92,6 @@ impl Client<SigningNymdClient> {
mixnode_page_limit: config.mixnode_page_limit, mixnode_page_limit: config.mixnode_page_limit,
gateway_page_limit: config.gateway_page_limit, gateway_page_limit: config.gateway_page_limit,
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit, mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
gateway_delegations_page_limit: config.gateway_delegations_page_limit,
validator_api: validator_api_client, validator_api: validator_api_client,
nymd: nymd_client, nymd: nymd_client,
}) })
@@ -131,7 +127,6 @@ impl Client<QueryNymdClient> {
mixnode_page_limit: config.mixnode_page_limit, mixnode_page_limit: config.mixnode_page_limit,
gateway_page_limit: config.gateway_page_limit, gateway_page_limit: config.gateway_page_limit,
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit, mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
gateway_delegations_page_limit: config.gateway_delegations_page_limit,
validator_api: validator_api_client, validator_api: validator_api_client,
nymd: nymd_client, nymd: nymd_client,
}) })
@@ -158,6 +153,10 @@ impl<C> Client<C> {
self.mixnet_contract_address = Some(mixnet_contract_address) self.mixnet_contract_address = Some(mixnet_contract_address)
} }
pub fn get_mixnet_contract_address(&self) -> Option<cosmrs::AccountId> {
self.mixnet_contract_address.clone()
}
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> { pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_mixnodes().await?) Ok(self.validator_api.get_mixnodes().await?)
} }
@@ -166,6 +165,64 @@ impl<C> Client<C> {
Ok(self.validator_api.get_gateways().await?) Ok(self.validator_api.get_gateways().await?)
} }
pub async fn get_state_params(&self) -> Result<StateParams, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_state_params().await?)
}
pub async fn get_current_rewarding_interval(
&self,
) -> Result<RewardingIntervalResponse, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_current_rewarding_interval().await?)
}
pub async fn get_rewarding_status(
&self,
mix_identity: mixnet_contract::IdentityKey,
rewarding_interval_nonce: u32,
) -> Result<MixnodeRewardingStatusResponse, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self
.nymd
.get_rewarding_status(mix_identity, rewarding_interval_nonce)
.await?)
}
pub async fn get_reward_pool(&self) -> Result<u128, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_reward_pool().await?.u128())
}
pub async fn get_circulating_supply(&self) -> Result<u128, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_circulating_supply().await?.u128())
}
pub async fn get_sybil_resistance_percent(&self) -> Result<u8, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_sybil_resistance_percent().await?)
}
pub async fn get_epoch_reward_percent(&self) -> Result<u8, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
Ok(self.nymd.get_epoch_reward_percent().await?)
}
// basically handles paging for us // basically handles paging for us
pub async fn get_all_nymd_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> pub async fn get_all_nymd_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
where where
@@ -213,7 +270,7 @@ impl<C> Client<C> {
Ok(gateways) Ok(gateways)
} }
pub async fn get_all_nymd_mixnode_delegations( pub async fn get_all_nymd_single_mixnode_delegations(
&self, &self,
identity: mixnet_contract::IdentityKey, identity: mixnet_contract::IdentityKey,
) -> Result<Vec<mixnet_contract::Delegation>, ValidatorClientError> ) -> Result<Vec<mixnet_contract::Delegation>, ValidatorClientError>
@@ -243,6 +300,34 @@ impl<C> Client<C> {
Ok(delegations) Ok(delegations)
} }
pub async fn get_all_nymd_mixnode_delegations(
&self,
) -> Result<Vec<mixnet_contract::UnpackedDelegation<RawDelegationData>>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
let mut delegations = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.nymd
.get_all_mix_delegations_paged(
start_after.take(),
self.mixnode_delegations_page_limit,
)
.await?;
delegations.append(&mut paged_response.delegations);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(delegations)
}
pub async fn get_all_nymd_reverse_mixnode_delegations( pub async fn get_all_nymd_reverse_mixnode_delegations(
&self, &self,
delegation_owner: &cosmrs::AccountId, delegation_owner: &cosmrs::AccountId,
@@ -295,88 +380,6 @@ impl<C> Client<C> {
Ok(delegations) Ok(delegations)
} }
pub async fn get_all_nymd_gateway_delegations(
&self,
identity: mixnet_contract::IdentityKey,
) -> Result<Vec<mixnet_contract::Delegation>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
let mut delegations = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.nymd
.get_gateway_delegations(
identity.clone(),
start_after.take(),
self.gateway_delegations_page_limit,
)
.await?;
delegations.append(&mut paged_response.delegations);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(delegations)
}
pub async fn get_all_nymd_reverse_gateway_delegations(
&self,
delegation_owner: &cosmrs::AccountId,
) -> Result<Vec<mixnet_contract::IdentityKey>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
let mut delegations = Vec::new();
let mut start_after = None;
loop {
let mut paged_response = self
.nymd
.get_reverse_gateway_delegations_paged(
mixnet_contract::Addr::unchecked(delegation_owner.as_ref()),
start_after.take(),
self.mixnode_delegations_page_limit,
)
.await?;
delegations.append(&mut paged_response.delegated_nodes);
if let Some(start_after_res) = paged_response.start_next_after {
start_after = Some(start_after_res)
} else {
break;
}
}
Ok(delegations)
}
pub async fn get_all_nymd_gateway_delegations_of_owner(
&self,
delegation_owner: &cosmrs::AccountId,
) -> Result<Vec<mixnet_contract::Delegation>, ValidatorClientError>
where
C: CosmWasmClient + Sync,
{
let mut delegations = Vec::new();
for node_identity in self
.get_all_nymd_reverse_gateway_delegations(delegation_owner)
.await?
{
let delegation = self
.nymd
.get_gateway_delegation(node_identity, delegation_owner)
.await?;
delegations.push(delegation);
}
Ok(delegations)
}
pub async fn blind_sign( pub async fn blind_sign(
&self, &self,
request_body: &BlindSignRequestBody, request_body: &BlindSignRequestBody,
@@ -410,6 +413,12 @@ impl ApiClient {
self.validator_api.change_url(new_endpoint); self.validator_api.change_url(new_endpoint);
} }
pub async fn get_cached_active_mixnodes(
&self,
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_active_mixnodes().await?)
}
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> { pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
Ok(self.validator_api.get_mixnodes().await?) Ok(self.validator_api.get_mixnodes().await?)
} }
@@ -22,7 +22,7 @@ use cosmrs::rpc::query::Query;
use cosmrs::rpc::{self, HttpClient, Order}; use cosmrs::rpc::{self, HttpClient, Order};
use cosmrs::tendermint::abci::Transaction; use cosmrs::tendermint::abci::Transaction;
use cosmrs::tendermint::{abci, block, chain}; use cosmrs::tendermint::{abci, block, chain};
use cosmrs::{AccountId, Coin, Denom}; use cosmrs::{tx, AccountId, Coin, Denom};
use prost::Message; use prost::Message;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
@@ -153,12 +153,9 @@ pub trait CosmWasmClient: rpc::Client {
.map_err(|_| NymdError::SerializationError("Coins".to_owned())) .map_err(|_| NymdError::SerializationError("Coins".to_owned()))
} }
// disabled until https://github.com/tendermint/tendermint/issues/6802 async fn get_tx(&self, id: tx::Hash) -> Result<TxResponse, NymdError> {
// and consequently https://github.com/informalsystems/tendermint-rs/issues/942 is resolved Ok(self.tx(id, false).await?)
// }
// async fn get_tx(&self, id: tx::Hash) -> Result<TxResponse, NymdError> {
// Ok(self.tx(id, false).await?)
// }
async fn search_tx(&self, query: Query) -> Result<Vec<TxResponse>, NymdError> { async fn search_tx(&self, query: Query) -> Result<Vec<TxResponse>, NymdError> {
// according to https://docs.tendermint.com/master/rpc/#/Info/tx_search // according to https://docs.tendermint.com/master/rpc/#/Info/tx_search
@@ -13,8 +13,9 @@ use cosmrs::distribution::MsgWithdrawDelegatorReward;
use cosmrs::rpc::endpoint::broadcast; use cosmrs::rpc::endpoint::broadcast;
use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, SimpleRequest}; use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, SimpleRequest};
use cosmrs::staking::{MsgDelegate, MsgUndelegate}; use cosmrs::staking::{MsgDelegate, MsgUndelegate};
use cosmrs::tx::{Fee, Msg, MsgType, SignDoc, SignerInfo}; use cosmrs::tx::{Fee, Msg, SignDoc, SignerInfo};
use cosmrs::{cosmwasm, rpc, tx, AccountId, Coin}; use cosmrs::{cosmwasm, rpc, tx, AccountId, Any, Coin};
use log::debug;
use serde::Serialize; use serde::Serialize;
use sha2::Digest; use sha2::Digest;
use sha2::Sha256; use sha2::Sha256;
@@ -51,7 +52,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
.unwrap_or_default(), .unwrap_or_default(),
instantiate_permission: Default::default(), instantiate_permission: Default::default(),
} }
.to_msg() .to_any()
.map_err(|_| NymdError::SerializationError("MsgStoreCode".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgStoreCode".to_owned()))?;
let tx_res = self let tx_res = self
@@ -113,7 +114,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
init_msg: serde_json::to_vec(msg)?, init_msg: serde_json::to_vec(msg)?,
funds: options.map(|options| options.funds).unwrap_or_default(), funds: options.map(|options| options.funds).unwrap_or_default(),
} }
.to_msg() .to_any()
.map_err(|_| NymdError::SerializationError("MsgInstantiateContract".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgInstantiateContract".to_owned()))?;
let tx_res = self let tx_res = self
@@ -153,7 +154,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
new_admin: new_admin.clone(), new_admin: new_admin.clone(),
contract: contract_address.clone(), contract: contract_address.clone(),
} }
.to_msg() .to_any()
.map_err(|_| NymdError::SerializationError("MsgUpdateAdmin".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgUpdateAdmin".to_owned()))?;
let tx_res = self let tx_res = self
@@ -178,7 +179,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
sender: sender_address.clone(), sender: sender_address.clone(),
contract: contract_address.clone(), contract: contract_address.clone(),
} }
.to_msg() .to_any()
.map_err(|_| NymdError::SerializationError("MsgClearAdmin".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgClearAdmin".to_owned()))?;
let tx_res = self let tx_res = self
@@ -210,7 +211,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
code_id, code_id,
migrate_msg: serde_json::to_vec(msg)?, migrate_msg: serde_json::to_vec(msg)?,
} }
.to_msg() .to_any()
.map_err(|_| NymdError::SerializationError("MsgMigrateContract".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgMigrateContract".to_owned()))?;
let tx_res = self let tx_res = self
@@ -242,7 +243,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
msg: serde_json::to_vec(msg)?, msg: serde_json::to_vec(msg)?,
funds, funds,
} }
.to_msg() .to_any()
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))?;
let tx_res = self let tx_res = self
@@ -256,6 +257,48 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
}) })
} }
async fn execute_multiple<I, M>(
&self,
sender_address: &AccountId,
contract_address: &AccountId,
msgs: I,
fee: Fee,
memo: impl Into<String> + Send + 'static,
) -> Result<ExecuteResult, NymdError>
where
I: IntoIterator<Item = (M, Vec<Coin>)> + Send,
M: Serialize,
{
let messages = msgs
.into_iter()
.map(|(msg, funds)| {
cosmwasm::MsgExecuteContract {
sender: sender_address.clone(),
contract: contract_address.clone(),
msg: serde_json::to_vec(&msg)?,
funds,
}
.to_any()
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))
})
.collect::<Result<_, _>>()?;
let tx_res = self
.sign_and_broadcast_commit(sender_address, messages, fee, memo)
.await?
.check_response()?;
debug!(
"gas wanted: {:?}, gas used: {:?}",
tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used
);
Ok(ExecuteResult {
logs: parse_raw_logs(tx_res.deliver_tx.log)?,
transaction_hash: tx_res.hash,
})
}
async fn send_tokens( async fn send_tokens(
&self, &self,
sender_address: &AccountId, sender_address: &AccountId,
@@ -269,7 +312,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
to_address: recipient_address.clone(), to_address: recipient_address.clone(),
amount, amount,
} }
.to_msg() .to_any()
.map_err(|_| NymdError::SerializationError("MsgSend".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgSend".to_owned()))?;
self.sign_and_broadcast_commit(sender_address, vec![send_msg], fee, memo) self.sign_and_broadcast_commit(sender_address, vec![send_msg], fee, memo)
@@ -289,7 +332,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
validator_address: validator_address.to_owned(), validator_address: validator_address.to_owned(),
amount, amount,
} }
.to_msg() .to_any()
.map_err(|_| NymdError::SerializationError("MsgDelegate".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgDelegate".to_owned()))?;
self.sign_and_broadcast_commit(delegator_address, vec![delegate_msg], fee, memo) self.sign_and_broadcast_commit(delegator_address, vec![delegate_msg], fee, memo)
@@ -309,7 +352,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
validator_address: validator_address.to_owned(), validator_address: validator_address.to_owned(),
amount: Some(amount), amount: Some(amount),
} }
.to_msg() .to_any()
.map_err(|_| NymdError::SerializationError("MsgUndelegate".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgUndelegate".to_owned()))?;
self.sign_and_broadcast_commit(delegator_address, vec![undelegate_msg], fee, memo) self.sign_and_broadcast_commit(delegator_address, vec![undelegate_msg], fee, memo)
@@ -327,7 +370,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
delegator_address: delegator_address.to_owned(), delegator_address: delegator_address.to_owned(),
validator_address: validator_address.to_owned(), validator_address: validator_address.to_owned(),
} }
.to_msg() .to_any()
.map_err(|_| NymdError::SerializationError("MsgWithdrawDelegatorReward".to_owned()))?; .map_err(|_| NymdError::SerializationError("MsgWithdrawDelegatorReward".to_owned()))?;
self.sign_and_broadcast_commit(delegator_address, vec![withdraw_msg], fee, memo) self.sign_and_broadcast_commit(delegator_address, vec![withdraw_msg], fee, memo)
@@ -338,7 +381,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
async fn sign_and_broadcast_async( async fn sign_and_broadcast_async(
&self, &self,
signer_address: &AccountId, signer_address: &AccountId,
messages: Vec<Msg>, messages: Vec<Any>,
fee: Fee, fee: Fee,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<broadcast::tx_async::Response, NymdError> { ) -> Result<broadcast::tx_async::Response, NymdError> {
@@ -354,7 +397,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
async fn sign_and_broadcast_sync( async fn sign_and_broadcast_sync(
&self, &self,
signer_address: &AccountId, signer_address: &AccountId,
messages: Vec<Msg>, messages: Vec<Any>,
fee: Fee, fee: Fee,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<broadcast::tx_sync::Response, NymdError> { ) -> Result<broadcast::tx_sync::Response, NymdError> {
@@ -370,7 +413,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
async fn sign_and_broadcast_commit( async fn sign_and_broadcast_commit(
&self, &self,
signer_address: &AccountId, signer_address: &AccountId,
messages: Vec<Msg>, messages: Vec<Any>,
fee: Fee, fee: Fee,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<broadcast::tx_commit::Response, NymdError> { ) -> Result<broadcast::tx_commit::Response, NymdError> {
@@ -385,7 +428,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
fn sign_direct( fn sign_direct(
&self, &self,
signer_address: &AccountId, signer_address: &AccountId,
messages: Vec<Msg>, messages: Vec<Any>,
fee: Fee, fee: Fee,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
signer_data: SignerData, signer_data: SignerData,
@@ -423,7 +466,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
async fn sign( async fn sign(
&self, &self,
signer_address: &AccountId, signer_address: &AccountId,
messages: Vec<Msg>, messages: Vec<Any>,
fee: Fee, fee: Fee,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<tx::Raw, NymdError> { ) -> Result<tx::Raw, NymdError> {
@@ -442,6 +485,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
} }
} }
#[derive(Debug)]
pub struct Client { pub struct Client {
rpc_client: HttpClient, rpc_client: HttpClient,
signer: DirectSecp256k1HdWallet, signer: DirectSecp256k1HdWallet,
@@ -462,7 +506,7 @@ impl Client {
#[async_trait] #[async_trait]
impl rpc::Client for Client { impl rpc::Client for Client {
async fn perform<R>(&self, request: R) -> rpc::Result<R::Response> async fn perform<R>(&self, request: R) -> Result<R::Response, rpc::Error>
where where
R: SimpleRequest, R: SimpleRequest,
{ {
@@ -3,10 +3,15 @@
use crate::nymd::cosmwasm_client::types::ContractCodeId; use crate::nymd::cosmwasm_client::types::ContractCodeId;
use cosmrs::tendermint::block; use cosmrs::tendermint::block;
use cosmrs::{bip32, rpc, tx, AccountId}; use cosmrs::{bip32, tx, AccountId};
use std::io; use std::io;
use thiserror::Error; use thiserror::Error;
pub use cosmrs::rpc::error::{
Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail,
};
pub use cosmrs::rpc::response_error::{Code, ResponseError};
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum NymdError { pub enum NymdError {
#[error("No contract address is available to perform the call")] #[error("No contract address is available to perform the call")]
@@ -31,7 +36,7 @@ pub enum NymdError {
InvalidTxHash(String), InvalidTxHash(String),
#[error("There was an issue with a tendermint RPC request - {0}")] #[error("There was an issue with a tendermint RPC request - {0}")]
TendermintError(#[from] rpc::Error), TendermintError(#[from] TendermintRpcError),
#[error("There was an issue when attempting to serialize data")] #[error("There was an issue when attempting to serialize data")]
SerializationError(String), SerializationError(String),
@@ -98,3 +103,56 @@ pub enum NymdError {
#[error("The provided gas price is malformed")] #[error("The provided gas price is malformed")]
MalformedGasPrice, MalformedGasPrice,
} }
impl NymdError {
pub fn is_tendermint_response_timeout(&self) -> bool {
match &self {
NymdError::TendermintError(TendermintRpcError(
TendermintRpcErrorDetail::Response(err),
_,
)) => {
let response = &err.source;
if response.code() == Code::InternalError {
// 0.34 (and earlier) versions of tendermint seemed to be using phrase "timed out waiting ..."
// (https://github.com/tendermint/tendermint/blob/v0.34.13/rpc/core/mempool.go#L124)
// while 0.35+ has "timeout waiting for ..."
// https://github.com/tendermint/tendermint/blob/v0.35.0-rc3/internal/rpc/core/mempool.go#L99
// note that as of the time of writing this comment (08.10.2021), the most recent version
// of cosmos-sdk (v0.44.1) uses tendermint 0.34.13
if let Some(data) = response.data() {
data.contains("timed out") || data.contains("timeout")
} else {
false
}
} else {
false
}
}
_ => false,
}
}
pub fn is_tendermint_response_duplicate(&self) -> bool {
match &self {
NymdError::TendermintError(TendermintRpcError(
TendermintRpcErrorDetail::Response(err),
_,
)) => {
let response = &err.source;
if response.code() == Code::InternalError {
// this particular error message seems to be unchanged between 0.34 and newer versions
// https://github.com/tendermint/tendermint/blob/v0.34.13/mempool/errors.go#L10
// https://github.com/tendermint/tendermint/blob/v0.35.0-rc3/types/mempool.go#L10
if let Some(data) = response.data() {
data.contains("tx already exists in cache")
} else {
false
}
} else {
false
}
}
_ => false,
}
}
}
@@ -4,9 +4,11 @@
use crate::nymd::GasPrice; use crate::nymd::GasPrice;
use cosmrs::tx::{Fee, Gas}; use cosmrs::tx::{Fee, Gas};
use cosmrs::Coin; use cosmrs::Coin;
use cosmwasm_std::Uint128; use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum Operation { pub enum Operation {
Upload, Upload,
Init, Init,
@@ -21,25 +23,41 @@ pub enum Operation {
BondGateway, BondGateway,
UnbondGateway, UnbondGateway,
DelegateToGateway,
UndelegateFromGateway,
UpdateStateParams, UpdateStateParams,
BeginMixnodeRewarding,
FinishMixnodeRewarding,
} }
pub(crate) fn calculate_fee(gas_price: &GasPrice, gas_limit: Gas) -> Coin { pub(crate) fn calculate_fee(gas_price: &GasPrice, gas_limit: Gas) -> Coin {
let limit_uint128 = Uint128::from(gas_limit.value()); gas_price * gas_limit
let amount = gas_price.amount * limit_uint128; }
assert!(amount.u128() <= u64::MAX as u128);
Coin { impl fmt::Display for Operation {
denom: gas_price.denom.clone(), fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
amount: (amount.u128() as u64).into(), match *self {
Operation::Upload => f.write_str("Upload"),
Operation::Init => f.write_str("Init"),
Operation::Migrate => f.write_str("Migrate"),
Operation::ChangeAdmin => f.write_str("ChangeAdmin"),
Operation::Send => f.write_str("Send"),
Operation::BondMixnode => f.write_str("BondMixnode"),
Operation::UnbondMixnode => f.write_str("UnbondMixnode"),
Operation::BondGateway => f.write_str("BondGateway"),
Operation::UnbondGateway => f.write_str("UnbondGateway"),
Operation::DelegateToMixnode => f.write_str("DelegateToMixnode"),
Operation::UndelegateFromMixnode => f.write_str("UndelegateFromMixnode"),
Operation::UpdateStateParams => f.write_str("UpdateStateParams"),
Operation::BeginMixnodeRewarding => f.write_str("BeginMixnodeRewarding"),
Operation::FinishMixnodeRewarding => f.write_str("FinishMixnodeRewarding"),
}
} }
} }
impl Operation { impl Operation {
// TODO: some value tweaking // TODO: some value tweaking
pub(crate) fn default_gas_limit(&self) -> Gas { pub fn default_gas_limit(&self) -> Gas {
match self { match self {
Operation::Upload => 2_500_000u64.into(), Operation::Upload => 2_500_000u64.into(),
Operation::Init => 500_000u64.into(), Operation::Init => 500_000u64.into(),
@@ -54,23 +72,27 @@ impl Operation {
Operation::BondGateway => 175_000u64.into(), Operation::BondGateway => 175_000u64.into(),
Operation::UnbondGateway => 175_000u64.into(), Operation::UnbondGateway => 175_000u64.into(),
Operation::DelegateToGateway => 175_000u64.into(),
Operation::UndelegateFromGateway => 175_000u64.into(),
Operation::UpdateStateParams => 175_000u64.into(), Operation::UpdateStateParams => 175_000u64.into(),
Operation::BeginMixnodeRewarding => 175_000u64.into(),
Operation::FinishMixnodeRewarding => 175_000u64.into(),
} }
} }
pub(crate) fn determine_fee(&self, gas_price: &GasPrice, gas_limit: Option<Gas>) -> Fee { pub(crate) fn determine_custom_fee(gas_price: &GasPrice, gas_limit: Gas) -> Fee {
// we need to know 2 of the following 3 parameters (the third one is being implicit) in order to construct Fee: // we need to know 2 of the following 3 parameters (the third one is being implicit) in order to construct Fee:
// (source: https://docs.cosmos.network/v0.42/basics/gas-fees.html) // (source: https://docs.cosmos.network/v0.42/basics/gas-fees.html)
// - gas price // - gas price
// - gas limit // - gas limit
// - fees // - fees
let gas_limit = gas_limit.unwrap_or_else(|| self.default_gas_limit());
let fee = calculate_fee(gas_price, gas_limit); let fee = calculate_fee(gas_price, gas_limit);
Fee::from_amount_and_gas(fee, gas_limit) Fee::from_amount_and_gas(fee, gas_limit)
} }
pub(crate) fn determine_fee(&self, gas_price: &GasPrice, gas_limit: Option<Gas>) -> Fee {
let gas_limit = gas_limit.unwrap_or_else(|| self.default_gas_limit());
Self::determine_custom_fee(gas_price, gas_limit)
}
} }
#[cfg(test)] #[cfg(test)]
@@ -3,8 +3,10 @@
use crate::nymd::error::NymdError; use crate::nymd::error::NymdError;
use config::defaults; use config::defaults;
use cosmrs::Denom; use cosmrs::tx::Gas;
use cosmwasm_std::Decimal; use cosmrs::{Coin, Denom};
use cosmwasm_std::{Decimal, Fraction, Uint128};
use std::ops::Mul;
use std::str::FromStr; use std::str::FromStr;
/// A gas price, i.e. the price of a single unit of gas. This is typically a fraction of /// A gas price, i.e. the price of a single unit of gas. This is typically a fraction of
@@ -18,6 +20,36 @@ pub struct GasPrice {
pub denom: Denom, pub denom: Denom,
} }
impl<'a> Mul<Gas> for &'a GasPrice {
type Output = Coin;
fn mul(self, gas_limit: Gas) -> Self::Output {
let limit_uint128 = Uint128::from(gas_limit.value());
let mut amount = self.amount * limit_uint128;
let gas_price_numerator = self.amount.numerator();
let gas_price_denominator = self.amount.denominator();
// gas price is a fraction of the smallest fee token unit, so we must ensure that
// for any multiplication, we have rounded up
//
// I don't really like the this solution as it has a theoretical chance of
// overflowing (internally cosmwasm uses U256 to avoid that)
// however, realistically that is impossible to happen as the resultant value
// would have to be way higher than our token limit of 10^15 (1 billion of tokens * 1 million for denomination)
// and max value of u128 is approximately 10^38
if limit_uint128.u128() * gas_price_numerator > amount.u128() * gas_price_denominator {
amount += Uint128::new(1);
}
assert!(amount.u128() <= u64::MAX as u128);
Coin {
denom: self.denom.clone(),
amount: (amount.u128() as u64).into(),
}
}
}
impl FromStr for GasPrice { impl FromStr for GasPrice {
type Err = NymdError; type Err = NymdError;
@@ -78,4 +110,15 @@ mod tests {
assert!("0.025 upunk".parse::<GasPrice>().is_err()); assert!("0.025 upunk".parse::<GasPrice>().is_err());
assert!("0.025UPUNK".parse::<GasPrice>().is_err()); assert!("0.025UPUNK".parse::<GasPrice>().is_err());
} }
#[test]
fn gas_limit_multiplication() {
// real world example that caused an issue when the result was rounded down
let gas_price: GasPrice = "0.025upunk".parse().unwrap();
let gas_limit: Gas = 157500u64.into();
let fee = &gas_price * gas_limit;
// the failing behaviour was result value of 3937
assert_eq!(fee.amount, 3938u64.into());
}
} }
@@ -4,22 +4,20 @@
use crate::nymd::cosmwasm_client::signing_client; use crate::nymd::cosmwasm_client::signing_client;
use crate::nymd::cosmwasm_client::types::{ use crate::nymd::cosmwasm_client::types::{
ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, InstantiateResult, ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, InstantiateResult,
MigrateResult, UploadMeta, UploadResult, MigrateResult, SequenceResponse, UploadMeta, UploadResult,
}; };
use crate::nymd::error::NymdError; use crate::nymd::error::NymdError;
use crate::nymd::fee_helpers::Operation; use crate::nymd::fee_helpers::Operation;
use crate::nymd::wallet::DirectSecp256k1HdWallet; use crate::nymd::wallet::DirectSecp256k1HdWallet;
use cosmrs::rpc::endpoint::broadcast; use cosmrs::rpc::endpoint::broadcast;
use cosmrs::rpc::{Error as TendermintRpcError, HttpClientUrl}; use cosmrs::rpc::{Error as TendermintRpcError, HttpClientUrl};
use cosmrs::tx::{Fee, Gas}; use cosmwasm_std::{Coin, Uint128};
use cosmwasm_std::Coin;
use mixnet_contract::{ use mixnet_contract::{
Addr, Delegation, ExecuteMsg, Gateway, GatewayOwnershipResponse, IdentityKey, Addr, Delegation, ExecuteMsg, Gateway, GatewayOwnershipResponse, IdentityKey,
LayerDistribution, MixNode, MixOwnershipResponse, PagedGatewayDelegationsResponse, LayerDistribution, MixNode, MixOwnershipResponse, MixnodeRewardingStatusResponse,
PagedGatewayResponse, PagedMixDelegationsResponse, PagedMixnodeResponse, PagedAllDelegationsResponse, PagedGatewayResponse, PagedMixDelegationsResponse,
PagedReverseGatewayDelegationsResponse, PagedReverseMixDelegationsResponse, QueryMsg, PagedMixnodeResponse, PagedReverseMixDelegationsResponse, QueryMsg, RawDelegationData,
StateParams, RewardingIntervalResponse, StateParams,
}; };
use serde::Serialize; use serde::Serialize;
use std::collections::HashMap; use std::collections::HashMap;
@@ -29,16 +27,21 @@ pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient; pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
pub use crate::nymd::gas_price::GasPrice; pub use crate::nymd::gas_price::GasPrice;
pub use cosmrs::rpc::HttpClient as QueryNymdClient; pub use cosmrs::rpc::HttpClient as QueryNymdClient;
pub use cosmrs::tendermint::block::Height;
pub use cosmrs::tendermint::hash;
pub use cosmrs::tendermint::Time as TendermintTime;
pub use cosmrs::tx::{Fee, Gas};
pub use cosmrs::Coin as CosmosCoin; pub use cosmrs::Coin as CosmosCoin;
pub use cosmrs::{AccountId, Denom}; pub use cosmrs::{AccountId, Decimal, Denom};
pub use signing_client::Client as SigningNymdClient; pub use signing_client::Client as SigningNymdClient;
pub mod cosmwasm_client; pub mod cosmwasm_client;
pub mod error; pub mod error;
pub(crate) mod fee_helpers; pub mod fee_helpers;
pub mod gas_price; pub mod gas_price;
pub mod wallet; pub mod wallet;
#[derive(Debug)]
pub struct NymdClient<C> { pub struct NymdClient<C> {
client: C, client: C,
contract_address: Option<AccountId>, contract_address: Option<AccountId>,
@@ -124,6 +127,14 @@ impl<C> NymdClient<C> {
self.custom_gas_limits.insert(operation, limit); self.custom_gas_limits.insert(operation, limit);
} }
pub fn get_gas_price(&self) -> GasPrice {
self.gas_price.clone()
}
pub fn get_custom_gas_limits(&self) -> HashMap<Operation, Gas> {
self.custom_gas_limits.clone()
}
pub fn contract_address(&self) -> Result<&AccountId, NymdError> { pub fn contract_address(&self) -> Result<&AccountId, NymdError> {
self.contract_address self.contract_address
.as_ref() .as_ref()
@@ -145,11 +156,51 @@ impl<C> NymdClient<C> {
&self.client_address.as_ref().unwrap()[0] &self.client_address.as_ref().unwrap()[0]
} }
fn get_fee(&self, operation: Operation) -> Fee { pub async fn account_sequence(&self) -> Result<SequenceResponse, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
self.client.get_sequence(self.address()).await
}
pub fn get_fee(&self, operation: Operation) -> Fee {
let gas_limit = self.custom_gas_limits.get(&operation).cloned(); let gas_limit = self.custom_gas_limits.get(&operation).cloned();
operation.determine_fee(&self.gas_price, gas_limit) operation.determine_fee(&self.gas_price, gas_limit)
} }
pub fn calculate_custom_fee(&self, gas_limit: impl Into<Gas>) -> Fee {
Operation::determine_custom_fee(&self.gas_price, gas_limit.into())
}
pub async fn get_current_block_timestamp(&self) -> Result<TendermintTime, NymdError>
where
C: CosmWasmClient + Sync,
{
Ok(self.client.get_block(None).await?.block.header.time)
}
pub async fn get_current_block_height(&self) -> Result<Height, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.get_height().await
}
/// Obtains the hash of a block specified by the provided height.
///
/// # Arguments
///
/// * `height`: height of the block for which we want to obtain the hash.
pub async fn get_block_hash(&self, height: u32) -> Result<hash::Hash, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client
.get_block(Some(height))
.await
.map(|block| block.block_id.hash)
}
pub async fn get_balance(&self, address: &AccountId) -> Result<Option<CosmosCoin>, NymdError> pub async fn get_balance(&self, address: &AccountId) -> Result<Option<CosmosCoin>, NymdError>
where where
C: CosmWasmClient + Sync, C: CosmWasmClient + Sync,
@@ -167,6 +218,35 @@ impl<C> NymdClient<C> {
.await .await
} }
pub async fn get_current_rewarding_interval(
&self,
) -> Result<RewardingIntervalResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::CurrentRewardingInterval {};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
pub async fn get_rewarding_status(
&self,
mix_identity: mixnet_contract::IdentityKey,
rewarding_interval_nonce: u32,
) -> Result<MixnodeRewardingStatusResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetRewardingStatus {
mix_identity,
rewarding_interval_nonce,
};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
pub async fn get_layer_distribution(&self) -> Result<LayerDistribution, NymdError> pub async fn get_layer_distribution(&self) -> Result<LayerDistribution, NymdError>
where where
C: CosmWasmClient + Sync, C: CosmWasmClient + Sync,
@@ -177,6 +257,46 @@ impl<C> NymdClient<C> {
.await .await
} }
pub async fn get_reward_pool(&self) -> Result<Uint128, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetRewardPool {};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
pub async fn get_circulating_supply(&self) -> Result<Uint128, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetCirculatingSupply {};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
pub async fn get_sybil_resistance_percent(&self) -> Result<u8, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetSybilResistancePercent {};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
pub async fn get_epoch_reward_percent(&self) -> Result<u8, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetEpochRewardPercent {};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
/// Checks whether there is a bonded mixnode associated with the provided client's address /// Checks whether there is a bonded mixnode associated with the provided client's address
pub async fn owns_mixnode(&self, address: &AccountId) -> Result<bool, NymdError> pub async fn owns_mixnode(&self, address: &AccountId) -> Result<bool, NymdError>
where where
@@ -262,6 +382,25 @@ impl<C> NymdClient<C> {
.await .await
} }
/// Gets list of all mixnode delegations on particular page.
pub async fn get_all_mix_delegations_paged(
&self,
// I really hate mixing cosmwasm and cosmos-sdk types here...
start_after: Option<Vec<u8>>,
page_limit: Option<u32>,
) -> Result<PagedAllDelegationsResponse<RawDelegationData>, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetAllMixDelegations {
start_after,
limit: page_limit,
};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
/// Gets list of all the mixnodes on which a particular address delegated. /// Gets list of all the mixnodes on which a particular address delegated.
pub async fn get_reverse_mix_delegations_paged( pub async fn get_reverse_mix_delegations_paged(
&self, &self,
@@ -300,64 +439,6 @@ impl<C> NymdClient<C> {
.await .await
} }
/// Gets list of all delegations towards particular mixnode on particular page.
pub async fn get_gateway_delegations(
&self,
gateway_identity: IdentityKey,
start_after: Option<Addr>,
page_limit: Option<u32>,
) -> Result<PagedGatewayDelegationsResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetGatewayDelegations {
gateway_identity,
start_after,
limit: page_limit,
};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
/// Gets list of all the gateways on which a particular address delegated.
pub async fn get_reverse_gateway_delegations_paged(
&self,
delegation_owner: Addr,
start_after: Option<IdentityKey>,
page_limit: Option<u32>,
) -> Result<PagedReverseGatewayDelegationsResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetReverseGatewayDelegations {
delegation_owner,
start_after,
limit: page_limit,
};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
/// Checks value of delegation of given client towards particular gateway.
pub async fn get_gateway_delegation(
&self,
gateway_identity: IdentityKey,
delegator: &AccountId,
) -> Result<Delegation, NymdError>
where
C: CosmWasmClient + Sync,
{
let request = QueryMsg::GetGatewayDelegation {
gateway_identity,
address: Addr::unchecked(delegator.as_ref()),
};
self.client
.query_contract_smart(self.contract_address()?, &request)
.await
}
/// Send funds from one address to another /// Send funds from one address to another
pub async fn send( pub async fn send(
&self, &self,
@@ -391,6 +472,23 @@ impl<C> NymdClient<C> {
.await .await
} }
pub async fn execute_multiple<I, M>(
&self,
contract_address: &AccountId,
msgs: I,
fee: Fee,
memo: impl Into<String> + Send + 'static,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
I: IntoIterator<Item = (M, Vec<CosmosCoin>)> + Send,
M: Serialize,
{
self.client
.execute_multiple(self.address(), contract_address, msgs, fee, memo)
.await
}
pub async fn upload( pub async fn upload(
&self, &self,
wasm_code: Vec<u8>, wasm_code: Vec<u8>,
@@ -517,15 +615,17 @@ impl<C> NymdClient<C> {
/// Delegates specified amount of stake to particular mixnode. /// Delegates specified amount of stake to particular mixnode.
pub async fn delegate_to_mixnode( pub async fn delegate_to_mixnode(
&self, &self,
mix_identity: IdentityKey, mix_identity: &str,
amount: Coin, amount: &Coin,
) -> Result<ExecuteResult, NymdError> ) -> Result<ExecuteResult, NymdError>
where where
C: SigningCosmWasmClient + Sync, C: SigningCosmWasmClient + Sync,
{ {
let fee = self.get_fee(Operation::DelegateToMixnode); let fee = self.get_fee(Operation::DelegateToMixnode);
let req = ExecuteMsg::DelegateToMixnode { mix_identity }; let req = ExecuteMsg::DelegateToMixnode {
mix_identity: mix_identity.to_string(),
};
self.client self.client
.execute( .execute(
self.address(), self.address(),
@@ -533,7 +633,7 @@ impl<C> NymdClient<C> {
&req, &req,
fee, fee,
"Delegating to mixnode from rust!", "Delegating to mixnode from rust!",
vec![cosmwasm_coin_to_cosmos_coin(amount)], vec![cosmwasm_coin_ptr_to_cosmos_coin(amount)],
) )
.await .await
} }
@@ -541,14 +641,16 @@ impl<C> NymdClient<C> {
/// Removes stake delegation from a particular mixnode. /// Removes stake delegation from a particular mixnode.
pub async fn remove_mixnode_delegation( pub async fn remove_mixnode_delegation(
&self, &self,
mix_identity: IdentityKey, mix_identity: &str,
) -> Result<ExecuteResult, NymdError> ) -> Result<ExecuteResult, NymdError>
where where
C: SigningCosmWasmClient + Sync, C: SigningCosmWasmClient + Sync,
{ {
let fee = self.get_fee(Operation::UndelegateFromMixnode); let fee = self.get_fee(Operation::UndelegateFromMixnode);
let req = ExecuteMsg::UndelegateFromMixnode { mix_identity }; let req = ExecuteMsg::UndelegateFromMixnode {
mix_identity: mix_identity.to_string(),
};
self.client self.client
.execute( .execute(
self.address(), self.address(),
@@ -605,53 +707,6 @@ impl<C> NymdClient<C> {
.await .await
} }
/// Delegates specified amount of stake to particular gateway.
pub async fn delegate_to_gateway(
&self,
gateway_identity: IdentityKey,
amount: Coin,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.get_fee(Operation::DelegateToGateway);
let req = ExecuteMsg::DelegateToGateway { gateway_identity };
self.client
.execute(
self.address(),
self.contract_address()?,
&req,
fee,
"Delegating to gateway from rust!",
vec![cosmwasm_coin_to_cosmos_coin(amount)],
)
.await
}
/// Removes stake delegation from a particular gateway.
pub async fn remove_gateway_delegation(
&self,
gateway_identity: IdentityKey,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.get_fee(Operation::UndelegateFromGateway);
let req = ExecuteMsg::UndelegateFromGateway { gateway_identity };
self.client
.execute(
self.address(),
self.contract_address()?,
&req,
fee,
"Removing gateway delegation from rust!",
Vec::new(),
)
.await
}
pub async fn update_state_params( pub async fn update_state_params(
&self, &self,
new_params: StateParams, new_params: StateParams,
@@ -673,6 +728,54 @@ impl<C> NymdClient<C> {
) )
.await .await
} }
pub async fn begin_mixnode_rewarding(
&self,
rewarding_interval_nonce: u32,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.get_fee(Operation::BeginMixnodeRewarding);
let req = ExecuteMsg::BeginMixnodeRewarding {
rewarding_interval_nonce,
};
self.client
.execute(
self.address(),
self.contract_address()?,
&req,
fee,
"Beginning mixnode rewarding procedure",
Vec::new(),
)
.await
}
pub async fn finish_mixnode_rewarding(
&self,
rewarding_interval_nonce: u32,
) -> Result<ExecuteResult, NymdError>
where
C: SigningCosmWasmClient + Sync,
{
let fee = self.get_fee(Operation::FinishMixnodeRewarding);
let req = ExecuteMsg::FinishMixnodeRewarding {
rewarding_interval_nonce,
};
self.client
.execute(
self.address(),
self.contract_address()?,
&req,
fee,
"Finishing mixnode rewarding procedure",
Vec::new(),
)
.await
}
} }
fn cosmwasm_coin_to_cosmos_coin(coin: Coin) -> CosmosCoin { fn cosmwasm_coin_to_cosmos_coin(coin: Coin) -> CosmosCoin {
@@ -682,3 +785,11 @@ fn cosmwasm_coin_to_cosmos_coin(coin: Coin) -> CosmosCoin {
amount: (coin.amount.u128() as u64).into(), amount: (coin.amount.u128() as u64).into(),
} }
} }
fn cosmwasm_coin_ptr_to_cosmos_coin(coin: &Coin) -> CosmosCoin {
CosmosCoin {
denom: coin.denom.parse().unwrap(),
// this might be a bit iffy, cosmwasm coin stores value as u128, while cosmos does it as u64
amount: (coin.amount.u128() as u64).into(),
}
}
@@ -10,6 +10,7 @@ use cosmrs::tx::SignDoc;
use cosmrs::{tx, AccountId}; use cosmrs::{tx, AccountId};
/// Derivation information required to derive a keypair and an address from a mnemonic. /// Derivation information required to derive a keypair and an address from a mnemonic.
#[derive(Debug)]
struct Secp256k1Derivation { struct Secp256k1Derivation {
hd_path: DerivationPath, hd_path: DerivationPath,
prefix: String, prefix: String,
@@ -23,8 +24,23 @@ pub struct AccountData {
pub(crate) private_key: SigningKey, pub(crate) private_key: SigningKey,
} }
impl AccountData {
pub fn address(&self) -> &AccountId {
&self.address
}
pub fn public_key(&self) -> PublicKey {
self.public_key
}
pub fn private_key(&self) -> &SigningKey {
&self.private_key
}
}
type Secp256k1Keypair = (SigningKey, PublicKey); type Secp256k1Keypair = (SigningKey, PublicKey);
#[derive(Debug)]
pub struct DirectSecp256k1HdWallet { pub struct DirectSecp256k1HdWallet {
/// Base secret /// Base secret
secret: bip39::Mnemonic, secret: bip39::Mnemonic,
@@ -68,6 +68,11 @@ impl Client {
.await .await
} }
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE])
.await
}
pub async fn blind_sign( pub async fn blind_sign(
&self, &self,
request_body: &BlindSignRequestBody, request_body: &BlindSignRequestBody,
@@ -7,5 +7,7 @@ pub const API_VERSION: &str = VALIDATOR_API_VERSION;
pub const MIXNODES: &str = "mixnodes"; pub const MIXNODES: &str = "mixnodes";
pub const GATEWAYS: &str = "gateways"; pub const GATEWAYS: &str = "gateways";
pub const ACTIVE: &str = "active";
pub const COCONUT_BLIND_SIGN: &str = "blind_sign"; pub const COCONUT_BLIND_SIGN: &str = "blind_sign";
pub const COCONUT_VERIFICATION_KEY: &str = "verification_key"; pub const COCONUT_VERIFICATION_KEY: &str = "verification_key";
+1 -1
View File
@@ -8,4 +8,4 @@ description = "Crutch library until there is proper SerDe support for coconut st
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
getset = "0.1.1" getset = "0.1.1"
coconut-rs = { git = "https://github.com/nymtech/coconut.git", branch = "0.5.0" } nymcoconut = {path = "../nymcoconut" }
+6 -6
View File
@@ -4,7 +4,7 @@
use getset::{CopyGetters, Getters}; use getset::{CopyGetters, Getters};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub use coconut_rs::*; pub use nymcoconut::*;
#[derive(Serialize, Deserialize, Getters, CopyGetters, Clone)] #[derive(Serialize, Deserialize, Getters, CopyGetters, Clone)]
pub struct Credential { pub struct Credential {
@@ -42,7 +42,7 @@ impl Credential {
.iter() .iter()
.map(hash_to_scalar) .map(hash_to_scalar)
.collect::<Vec<Attribute>>(); .collect::<Vec<Attribute>>();
coconut_rs::verify_credential(&params, verification_key, &self.theta, &public_attributes) nymcoconut::verify_credential(&params, verification_key, &self.theta, &public_attributes)
} }
} }
@@ -84,7 +84,7 @@ pub struct BlindSignRequestBody {
#[getset(get = "pub")] #[getset(get = "pub")]
blind_sign_request: BlindSignRequest, blind_sign_request: BlindSignRequest,
#[getset(get = "pub")] #[getset(get = "pub")]
public_key: coconut_rs::PublicKey, public_key: nymcoconut::PublicKey,
public_attributes: Vec<String>, public_attributes: Vec<String>,
#[getset(get = "pub")] #[getset(get = "pub")]
total_params: u32, total_params: u32,
@@ -92,13 +92,13 @@ pub struct BlindSignRequestBody {
impl BlindSignRequestBody { impl BlindSignRequestBody {
pub fn new( pub fn new(
blind_sign_request: BlindSignRequest, blind_sign_request: &BlindSignRequest,
public_key: &coconut_rs::PublicKey, public_key: &nymcoconut::PublicKey,
public_attributes: &[Attribute], public_attributes: &[Attribute],
total_params: u32, total_params: u32,
) -> BlindSignRequestBody { ) -> BlindSignRequestBody {
BlindSignRequestBody { BlindSignRequestBody {
blind_sign_request, blind_sign_request: blind_sign_request.clone(),
public_key: public_key.clone(), public_key: public_key.clone(),
public_attributes: public_attributes public_attributes: public_attributes
.iter() .iter()
+2
View File
@@ -11,4 +11,6 @@ url = "2.2"
# I guess temporarily until we get serde support in coconut up and running # I guess temporarily until we get serde support in coconut up and running
coconut-interface = { path = "../coconut-interface" } coconut-interface = { path = "../coconut-interface" }
crypto = { path = "../crypto" }
network-defaults = { path = "../network-defaults" }
validator-client = { path = "../client-libs/validator-client" } validator-client = { path = "../client-libs/validator-client" }
-48
View File
@@ -1,48 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// for time being assume the bandwidth credential consists of public identity of the requester
// and private (though known... just go along with it) infinite bandwidth value
// right now this has no double-spending protection, spender binding, etc
// it's the simplest possible case
use url::Url;
use crate::error::Error;
use crate::utils::{obtain_aggregate_signature, prepare_credential_for_spending};
use coconut_interface::{hash_to_scalar, Credential, Parameters, Signature, VerificationKey};
const BANDWIDTH_VALUE: u64 = 1024 * 1024; // 1 MB
pub const PUBLIC_ATTRIBUTES: u32 = 1;
pub const PRIVATE_ATTRIBUTES: u32 = 1;
pub const TOTAL_ATTRIBUTES: u32 = PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES;
// TODO: this definitely has to be moved somewhere else. It's just a temporary solution
pub async fn obtain_signature(raw_identity: &[u8], validators: &[Url]) -> Result<Signature, Error> {
let public_attributes = vec![hash_to_scalar(BANDWIDTH_VALUE.to_be_bytes())];
let private_attributes = vec![hash_to_scalar(raw_identity)];
let params = Parameters::new(TOTAL_ATTRIBUTES)?;
obtain_aggregate_signature(&params, &public_attributes, &private_attributes, validators).await
}
pub fn prepare_for_spending(
raw_identity: &[u8],
signature: &Signature,
verification_key: &VerificationKey,
) -> Result<Credential, Error> {
let public_attributes = vec![BANDWIDTH_VALUE.to_be_bytes().to_vec()];
let private_attributes = vec![raw_identity.to_vec()];
let params = Parameters::new(TOTAL_ATTRIBUTES)?;
prepare_credential_for_spending(
&params,
public_attributes,
private_attributes,
signature,
verification_key,
)
}
@@ -0,0 +1,77 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// for time being assume the bandwidth credential consists of public identity of the requester
// and private (though known... just go along with it) infinite bandwidth value
// right now this has no double-spending protection, spender binding, etc
// it's the simplest possible case
use coconut_interface::{
Credential, Parameters, PrivateAttribute, PublicAttribute, Signature, VerificationKey,
};
use network_defaults::BANDWIDTH_VALUE;
use url::Url;
use crate::error::Error;
use super::utils::{obtain_aggregate_signature, prepare_credential_for_spending};
pub const PUBLIC_ATTRIBUTES: u32 = 2;
pub const PRIVATE_ATTRIBUTES: u32 = 2;
pub const TOTAL_ATTRIBUTES: u32 = PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES;
pub struct BandwidthVoucherAttributes {
// a random secret value generated by the client used for double-spending detection
pub serial_number: PrivateAttribute,
// a random secret value generated by the client used to bind multiple credentials together
pub binding_number: PrivateAttribute,
// the value (e.g., bandwidth) encoded in this voucher
pub voucher_value: PublicAttribute,
// a field with public information, e.g., type of voucher, epoch etc.
pub voucher_info: PublicAttribute,
}
impl BandwidthVoucherAttributes {
pub fn get_public_attributes(&self) -> Vec<PublicAttribute> {
vec![self.voucher_value, self.voucher_info]
}
pub fn get_private_attributes(&self) -> Vec<PrivateAttribute> {
vec![self.serial_number, self.binding_number]
}
}
// TODO: this definitely has to be moved somewhere else. It's just a temporary solution
pub async fn obtain_signature(
params: &Parameters,
attributes: &BandwidthVoucherAttributes,
validators: &[Url],
) -> Result<Signature, Error> {
let public_attributes = attributes.get_public_attributes();
let private_attributes = attributes.get_private_attributes();
obtain_aggregate_signature(params, &public_attributes, &private_attributes, validators).await
}
pub fn prepare_for_spending(
raw_identity: &[u8],
signature: &Signature,
attributes: &BandwidthVoucherAttributes,
verification_key: &VerificationKey,
) -> Result<Credential, Error> {
let public_attributes = vec![
raw_identity.to_vec(),
BANDWIDTH_VALUE.to_be_bytes().to_vec(),
];
let params = Parameters::new(TOTAL_ATTRIBUTES)?;
prepare_credential_for_spending(
&params,
public_attributes,
attributes.serial_number,
attributes.binding_number,
signature,
verification_key,
)
}
+5
View File
@@ -0,0 +1,5 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod bandwidth;
pub mod utils;
@@ -1,14 +1,16 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::error::Error;
use coconut_interface::{ use coconut_interface::{
aggregate_signature_shares, aggregate_verification_keys, hash_to_scalar, prepare_blind_sign, aggregate_signature_shares, aggregate_verification_keys, prepare_blind_sign,
prove_credential, Attribute, BlindSignRequestBody, Credential, Parameters, Signature, prove_bandwidth_credential, Attribute, BlindSignRequestBody, Credential, Parameters, Signature,
SignatureShare, VerificationKey, SignatureShare, VerificationKey,
}; };
use url::Url; use url::Url;
use crate::coconut::bandwidth::PRIVATE_ATTRIBUTES;
use crate::error::Error;
/// Contacts all provided validators and then aggregate their verification keys. /// Contacts all provided validators and then aggregate their verification keys.
/// ///
/// # Arguments /// # Arguments
@@ -63,17 +65,18 @@ async fn obtain_partial_credential(
public_attributes: &[Attribute], public_attributes: &[Attribute],
private_attributes: &[Attribute], private_attributes: &[Attribute],
client: &validator_client::ApiClient, client: &validator_client::ApiClient,
validator_vk: &VerificationKey,
) -> Result<Signature, Error> { ) -> Result<Signature, Error> {
let elgamal_keypair = coconut_interface::elgamal_keygen(params); let elgamal_keypair = coconut_interface::elgamal_keygen(params);
let blind_sign_request = prepare_blind_sign( let blind_sign_request = prepare_blind_sign(
params, params,
elgamal_keypair.public_key(), &elgamal_keypair,
private_attributes, private_attributes,
public_attributes, public_attributes,
)?; )?;
let blind_sign_request_body = BlindSignRequestBody::new( let blind_sign_request_body = BlindSignRequestBody::new(
blind_sign_request, &blind_sign_request,
elgamal_keypair.public_key(), elgamal_keypair.public_key(),
public_attributes, public_attributes,
(public_attributes.len() + private_attributes.len()) as u32, (public_attributes.len() + private_attributes.len()) as u32,
@@ -83,7 +86,17 @@ async fn obtain_partial_credential(
.blind_sign(&blind_sign_request_body) .blind_sign(&blind_sign_request_body)
.await? .await?
.blinded_signature; .blinded_signature;
Ok(blinded_signature.unblind(elgamal_keypair.private_key()))
let unblinded_signature = blinded_signature.unblind(
params,
elgamal_keypair.private_key(),
validator_vk,
private_attributes,
public_attributes,
&blind_sign_request.get_commitment_hash(),
)?;
Ok(unblinded_signature)
} }
pub async fn obtain_aggregate_signature( pub async fn obtain_aggregate_signature(
@@ -97,40 +110,76 @@ pub async fn obtain_aggregate_signature(
} }
let mut shares = Vec::with_capacity(validators.len()); let mut shares = Vec::with_capacity(validators.len());
let mut validators_partial_vks: Vec<VerificationKey> = Vec::with_capacity(validators.len());
let mut client = validator_client::ApiClient::new(validators[0].clone()); let mut client = validator_client::ApiClient::new(validators[0].clone());
let first = let validator_partial_vk = client.get_coconut_verification_key().await?;
obtain_partial_credential(params, public_attributes, private_attributes, &client).await?; validators_partial_vks.push(validator_partial_vk.key.clone());
let first = obtain_partial_credential(
params,
public_attributes,
private_attributes,
&client,
&validator_partial_vk.key,
)
.await?;
shares.push(SignatureShare::new(first, 0)); shares.push(SignatureShare::new(first, 0));
for (id, validator_url) in validators.iter().enumerate().skip(1) { for (id, validator_url) in validators.iter().enumerate().skip(1) {
client.change_validator_api(validator_url.clone()); client.change_validator_api(validator_url.clone());
let signature = let validator_partial_vk = client.get_coconut_verification_key().await?;
obtain_partial_credential(params, public_attributes, private_attributes, &client) validators_partial_vks.push(validator_partial_vk.key.clone());
.await?; let signature = obtain_partial_credential(
params,
public_attributes,
private_attributes,
&client,
&validator_partial_vk.key,
)
.await?;
let share = SignatureShare::new(signature, id as u64); let share = SignatureShare::new(signature, id as u64);
shares.push(share) shares.push(share)
} }
Ok(aggregate_signature_shares(&shares)?) let mut attributes = Vec::with_capacity(private_attributes.len() + public_attributes.len());
attributes.extend_from_slice(private_attributes);
attributes.extend_from_slice(public_attributes);
let mut indices: Vec<u64> = Vec::with_capacity(validators_partial_vks.len());
for i in 1..validators_partial_vks.len() {
indices.push(i as u64);
}
let verification_key =
aggregate_verification_keys(&validators_partial_vks, Some(indices.as_ref()))?;
Ok(aggregate_signature_shares(
params,
&verification_key,
&attributes,
&shares,
)?)
} }
// TODO: better type flow // TODO: better type flow
pub fn prepare_credential_for_spending( pub fn prepare_credential_for_spending(
params: &Parameters, params: &Parameters,
public_attributes: Vec<Vec<u8>>, public_attributes: Vec<Vec<u8>>,
private_attributes: Vec<Vec<u8>>, serial_number: Attribute,
binding_number: Attribute,
signature: &Signature, signature: &Signature,
verification_key: &VerificationKey, verification_key: &VerificationKey,
) -> Result<Credential, Error> { ) -> Result<Credential, Error> {
let private_attributes = private_attributes let theta = prove_bandwidth_credential(
.iter() params,
.map(hash_to_scalar) verification_key,
.collect::<Vec<Attribute>>(); signature,
let theta = prove_credential(params, verification_key, signature, &private_attributes)?; serial_number,
binding_number,
)?;
Ok(Credential::new( Ok(Credential::new(
(public_attributes.len() + private_attributes.len()) as u32, (public_attributes.len() + PRIVATE_ATTRIBUTES as usize) as u32,
theta, theta,
public_attributes, public_attributes,
signature, signature,
+3 -3
View File
@@ -1,8 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub mod bandwidth; pub mod coconut;
pub mod error; pub mod error;
mod utils; pub mod token;
pub use utils::{obtain_aggregate_signature, obtain_aggregate_verification_key}; pub use coconut::utils::{obtain_aggregate_signature, obtain_aggregate_verification_key};
+140
View File
@@ -0,0 +1,140 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crypto::asymmetric::identity::{PublicKey, Signature, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH};
use crate::error::Error;
use std::convert::TryInto;
#[cfg(not(feature = "coconut"))]
pub struct TokenCredential {
verification_key: PublicKey,
gateway_identity: PublicKey,
bandwidth: u64,
signature: Signature,
}
#[cfg(not(feature = "coconut"))]
impl TokenCredential {
pub fn new(
verification_key: PublicKey,
gateway_identity: PublicKey,
bandwidth: u64,
signature: Signature,
) -> Self {
TokenCredential {
verification_key,
gateway_identity,
bandwidth,
signature,
}
}
pub fn verification_key(&self) -> PublicKey {
self.verification_key
}
pub fn gateway_identity(&self) -> PublicKey {
self.gateway_identity
}
pub fn bandwidth(&self) -> u64 {
self.bandwidth
}
pub fn signature_bytes(&self) -> [u8; 64] {
self.signature.to_bytes()
}
pub fn verify_signature(&self) -> bool {
let message: Vec<u8> = self
.verification_key
.to_bytes()
.iter()
.chain(self.gateway_identity.to_bytes().iter())
.copied()
.collect();
self.verification_key
.verify(&message, &self.signature)
.is_ok()
}
pub fn to_bytes(&self) -> Vec<u8> {
self.verification_key
.to_bytes()
.iter()
.chain(self.gateway_identity.to_bytes().iter())
.chain(self.bandwidth.to_be_bytes().iter())
.chain(self.signature.to_bytes().iter())
.copied()
.collect()
}
pub fn from_bytes(b: &[u8]) -> Result<Self, Error> {
if b.len() != 2 * PUBLIC_KEY_LENGTH + 8 + SIGNATURE_LENGTH {
return Err(Error::BandwidthCredentialError);
}
let verification_key = PublicKey::from_bytes(&b[..PUBLIC_KEY_LENGTH])
.map_err(|_| Error::BandwidthCredentialError)?;
let gateway_identity = PublicKey::from_bytes(&b[PUBLIC_KEY_LENGTH..2 * PUBLIC_KEY_LENGTH])
.map_err(|_| Error::BandwidthCredentialError)?;
let bandwidth = u64::from_be_bytes(
b[2 * PUBLIC_KEY_LENGTH..2 * PUBLIC_KEY_LENGTH + 8]
.try_into()
// unwrapping is safe because we know we have 8 bytes
.unwrap(),
);
let signature = Signature::from_bytes(&b[2 * PUBLIC_KEY_LENGTH + 8..])
.map_err(|_| Error::BandwidthCredentialError)?;
Ok(TokenCredential {
verification_key,
gateway_identity,
bandwidth,
signature,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "coconut"))]
#[test]
fn token_serde() {
// pre-generated, valid values
let verification_key = PublicKey::from_bytes(&[
103, 105, 71, 177, 149, 245, 26, 32, 73, 121, 76, 50, 94, 88, 119, 231, 91, 229, 167,
56, 39, 62, 185, 39, 83, 246, 153, 27, 17, 155, 109, 73,
])
.unwrap();
let gateway_identity = PublicKey::from_bytes(&[
37, 113, 137, 189, 157, 82, 35, 2, 187, 136, 61, 119, 98, 5, 245, 82, 46, 124, 67, 45,
165, 255, 53, 222, 185, 252, 6, 148, 128, 15, 206, 19,
])
.unwrap();
let signature = Signature::from_bytes(&[
117, 251, 162, 217, 57, 2, 50, 210, 206, 81, 236, 90, 74, 201, 69, 237, 240, 247, 214,
158, 220, 89, 235, 222, 85, 134, 73, 73, 8, 60, 25, 39, 183, 28, 83, 193, 31, 174, 25,
24, 38, 215, 205, 228, 159, 135, 35, 4, 171, 59, 100, 157, 12, 249, 77, 52, 143, 4, 32,
28, 147, 70, 182, 14,
])
.unwrap();
let credential = TokenCredential::new(verification_key, gateway_identity, 1024, signature);
let serialized_credential = credential.to_bytes();
let deserialized_credential = TokenCredential::from_bytes(&serialized_credential).unwrap();
assert_eq!(
credential.verification_key,
deserialized_credential.verification_key
);
assert_eq!(
credential.gateway_identity,
deserialized_credential.gateway_identity
);
assert_eq!(credential.bandwidth, deserialized_credential.bandwidth);
assert_eq!(
credential.signature.to_bytes(),
deserialized_credential.signature.to_bytes()
);
}
}
+4
View File
@@ -0,0 +1,4 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod bandwidth;
+7 -8
View File
@@ -7,15 +7,14 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
aes-ctr = "0.6.0" aes = { version = "0.7.4", features = ["ctr"] }
bs58 = "0.4" bs58 = "0.4.0"
blake3 = "0.3" blake3 = { version = "1.0.0", features = ["traits-preview"] }
#blake3 = { version = "0.3", features = ["traits-preview"]} digest = "0.9.0"
digest = "0.9"
generic-array = "0.14" generic-array = "0.14"
hkdf = "0.10" hkdf = "0.11.0"
hmac = "0.8" hmac = "0.11.0"
cipher = "0.2" cipher = "0.3.0"
x25519-dalek = "1.1" x25519-dalek = "1.1"
ed25519-dalek = "1.0" ed25519-dalek = "1.0"
log = "0.4" log = "0.4"
+2 -9
View File
@@ -5,18 +5,13 @@ use digest::{BlockInput, FixedOutput, Reset, Update};
use generic_array::ArrayLength; use generic_array::ArrayLength;
use hkdf::Hkdf; use hkdf::Hkdf;
#[derive(Debug)]
pub enum HkdfError {
InvalidOkmLength,
}
/// Perform HKDF `extract` then `expand` as a single step. /// Perform HKDF `extract` then `expand` as a single step.
pub fn extract_then_expand<D>( pub fn extract_then_expand<D>(
salt: Option<&[u8]>, salt: Option<&[u8]>,
ikm: &[u8], ikm: &[u8],
info: Option<&[u8]>, info: Option<&[u8]>,
okm_length: usize, okm_length: usize,
) -> Result<Vec<u8>, HkdfError> ) -> Result<Vec<u8>, hkdf::InvalidLength>
where where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone, D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>, D::BlockSize: ArrayLength<u8>,
@@ -27,9 +22,7 @@ where
let hkdf = Hkdf::<D>::new(salt, ikm); let hkdf = Hkdf::<D>::new(salt, ikm);
let mut okm = vec![0u8; okm_length]; let mut okm = vec![0u8; okm_length];
if hkdf.expand(info.unwrap_or_else(|| &[]), &mut okm).is_err() { hkdf.expand(info.unwrap_or_else(|| &[]), &mut okm)?;
return Err(HkdfError::InvalidOkmLength);
}
Ok(okm) Ok(okm)
} }
+2 -2
View File
@@ -18,7 +18,7 @@ where
D::OutputSize: ArrayLength<u8>, D::OutputSize: ArrayLength<u8>,
{ {
let mut hmac = let mut hmac =
Hmac::<D>::new_varkey(key).expect("HMAC should be able to take key of any size!"); Hmac::<D>::new_from_slice(key).expect("HMAC should be able to take key of any size!");
hmac.update(data); hmac.update(data);
hmac.finalize() hmac.finalize()
} }
@@ -31,7 +31,7 @@ where
D::OutputSize: ArrayLength<u8>, D::OutputSize: ArrayLength<u8>,
{ {
let mut hmac = let mut hmac =
Hmac::<D>::new_varkey(key).expect("HMAC should be able to take key of any size!"); Hmac::<D>::new_from_slice(key).expect("HMAC should be able to take key of any size!");
hmac.update(data); hmac.update(data);
// note, under the hood ct_eq is called // note, under the hood ct_eq is called
hmac.verify(tag).is_ok() hmac.verify(tag).is_ok()
+1 -1
View File
@@ -13,7 +13,7 @@ pub use generic_array;
// with the below my idea was to try to introduce having a single place of importing all hashing, encryption, // with the below my idea was to try to introduce having a single place of importing all hashing, encryption,
// etc. algorithms and import them elsewhere as needed via common/crypto // etc. algorithms and import them elsewhere as needed via common/crypto
pub use aes_ctr; pub use aes;
pub use blake3; pub use blake3;
// TODO: this function uses all three modules: asymmetric crypto, symmetric crypto and derives key..., // TODO: this function uses all three modules: asymmetric crypto, symmetric crypto and derives key...,
+7 -7
View File
@@ -3,7 +3,7 @@
use crate::asymmetric::encryption; use crate::asymmetric::encryption;
use crate::hkdf; use crate::hkdf;
use cipher::stream::{Key, NewStreamCipher, SyncStreamCipher}; use cipher::{CipherKey, NewCipher, StreamCipher};
use digest::{BlockInput, FixedOutput, Reset, Update}; use digest::{BlockInput, FixedOutput, Reset, Update};
use generic_array::{typenum::Unsigned, ArrayLength}; use generic_array::{typenum::Unsigned, ArrayLength};
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
@@ -13,9 +13,9 @@ use rand::{CryptoRng, RngCore};
pub fn new_ephemeral_shared_key<C, D, R>( pub fn new_ephemeral_shared_key<C, D, R>(
rng: &mut R, rng: &mut R,
remote_key: &encryption::PublicKey, remote_key: &encryption::PublicKey,
) -> (encryption::KeyPair, Key<C>) ) -> (encryption::KeyPair, CipherKey<C>)
where where
C: SyncStreamCipher + NewStreamCipher, C: StreamCipher + NewCipher,
D: Update + BlockInput + FixedOutput + Reset + Default + Clone, D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>, D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>, D::OutputSize: ArrayLength<u8>,
@@ -31,7 +31,7 @@ where
.expect("somehow too long okm was provided"); .expect("somehow too long okm was provided");
let derived_shared_key = let derived_shared_key =
Key::<C>::from_exact_iter(okm).expect("okm was expanded to incorrect length!"); CipherKey::<C>::from_exact_iter(okm).expect("okm was expanded to incorrect length!");
(ephemeral_keypair, derived_shared_key) (ephemeral_keypair, derived_shared_key)
} }
@@ -40,9 +40,9 @@ where
pub fn recompute_shared_key<C, D>( pub fn recompute_shared_key<C, D>(
remote_key: &encryption::PublicKey, remote_key: &encryption::PublicKey,
local_key: &encryption::PrivateKey, local_key: &encryption::PrivateKey,
) -> Key<C> ) -> CipherKey<C>
where where
C: SyncStreamCipher + NewStreamCipher, C: StreamCipher + NewCipher,
D: Update + BlockInput + FixedOutput + Reset + Default + Clone, D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>, D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>, D::OutputSize: ArrayLength<u8>,
@@ -53,5 +53,5 @@ where
let okm = hkdf::extract_then_expand::<D>(None, &dh_result, None, C::KeySize::to_usize()) let okm = hkdf::extract_then_expand::<D>(None, &dh_result, None, C::KeySize::to_usize())
.expect("somehow too long okm was provided"); .expect("somehow too long okm was provided");
Key::<C>::from_exact_iter(okm).expect("okm was expanded to incorrect length!") CipherKey::<C>::from_exact_iter(okm).expect("okm was expanded to incorrect length!")
} }
+23 -18
View File
@@ -1,13 +1,14 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net> // Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use cipher::stream::{Nonce, StreamCipher, SyncStreamCipher}; use cipher::{Nonce, StreamCipher};
use generic_array::{typenum::Unsigned, GenericArray}; use generic_array::{typenum::Unsigned, GenericArray};
use rand::{CryptoRng, RngCore}; use rand::{CryptoRng, RngCore};
// re-export this for ease of use // re-export this for ease of use
pub use cipher::stream::{Key, NewStreamCipher}; pub use cipher::{CipherKey, NewCipher};
// SECURITY:
// TODO: note that this is not the most secure approach here // TODO: note that this is not the most secure approach here
// we are not using nonces properly but instead "kinda" thinking of them as IVs. // we are not using nonces properly but instead "kinda" thinking of them as IVs.
// Nonce require, as the name suggest, being only seen once. Ever. // Nonce require, as the name suggest, being only seen once. Ever.
@@ -20,9 +21,9 @@ pub use cipher::stream::{Key, NewStreamCipher};
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
pub type IV<C> = Nonce<C>; pub type IV<C> = Nonce<C>;
pub fn generate_key<C, R>(rng: &mut R) -> Key<C> pub fn generate_key<C, R>(rng: &mut R) -> CipherKey<C>
where where
C: NewStreamCipher, C: NewCipher,
R: RngCore + CryptoRng, R: RngCore + CryptoRng,
{ {
let mut key = GenericArray::default(); let mut key = GenericArray::default();
@@ -32,7 +33,7 @@ where
pub fn random_iv<C, R>(rng: &mut R) -> IV<C> pub fn random_iv<C, R>(rng: &mut R) -> IV<C>
where where
C: NewStreamCipher, C: NewCipher,
R: RngCore + CryptoRng, R: RngCore + CryptoRng,
{ {
let mut iv = GenericArray::default(); let mut iv = GenericArray::default();
@@ -42,14 +43,14 @@ where
pub fn zero_iv<C>() -> IV<C> pub fn zero_iv<C>() -> IV<C>
where where
C: NewStreamCipher, C: NewCipher,
{ {
GenericArray::default() GenericArray::default()
} }
pub fn iv_from_slice<C>(b: &[u8]) -> &IV<C> pub fn iv_from_slice<C>(b: &[u8]) -> &IV<C>
where where
C: NewStreamCipher, C: NewCipher,
{ {
if b.len() != C::NonceSize::to_usize() { if b.len() != C::NonceSize::to_usize() {
// `from_slice` would have caused a panic about this issue anyway. // `from_slice` would have caused a panic about this issue anyway.
@@ -66,38 +67,42 @@ where
// TODO: there's really no way to use more parts of the keystream if it was required at some point. // TODO: there's really no way to use more parts of the keystream if it was required at some point.
// However, do we really expect to ever need it? // However, do we really expect to ever need it?
pub fn encrypt<C>(key: &Key<C>, iv: &IV<C>, data: &[u8]) -> Vec<u8> #[inline]
pub fn encrypt<C>(key: &CipherKey<C>, iv: &IV<C>, data: &[u8]) -> Vec<u8>
where where
C: SyncStreamCipher + NewStreamCipher, C: StreamCipher + NewCipher,
{ {
let mut ciphertext = data.to_vec(); let mut ciphertext = data.to_vec();
encrypt_in_place::<C>(key, iv, &mut ciphertext); encrypt_in_place::<C>(key, iv, &mut ciphertext);
ciphertext ciphertext
} }
pub fn encrypt_in_place<C>(key: &Key<C>, iv: &IV<C>, data: &mut [u8]) #[inline]
pub fn encrypt_in_place<C>(key: &CipherKey<C>, iv: &IV<C>, data: &mut [u8])
where where
C: SyncStreamCipher + NewStreamCipher, C: StreamCipher + NewCipher,
{ {
let mut cipher = C::new(key, iv); let mut cipher = C::new(key, iv);
cipher.encrypt(data) cipher.apply_keystream(data)
} }
pub fn decrypt<C>(key: &Key<C>, iv: &IV<C>, ciphertext: &[u8]) -> Vec<u8> #[inline]
pub fn decrypt<C>(key: &CipherKey<C>, iv: &IV<C>, ciphertext: &[u8]) -> Vec<u8>
where where
C: SyncStreamCipher + NewStreamCipher, C: StreamCipher + NewCipher,
{ {
let mut data = ciphertext.to_vec(); let mut data = ciphertext.to_vec();
decrypt_in_place::<C>(key, iv, &mut data); decrypt_in_place::<C>(key, iv, &mut data);
data data
} }
pub fn decrypt_in_place<C>(key: &Key<C>, iv: &IV<C>, data: &mut [u8]) #[inline]
pub fn decrypt_in_place<C>(key: &CipherKey<C>, iv: &IV<C>, data: &mut [u8])
where where
C: SyncStreamCipher + NewStreamCipher, C: StreamCipher + NewCipher,
{ {
let mut cipher = C::new(key, iv); let mut cipher = C::new(key, iv);
cipher.decrypt(data) cipher.apply_keystream(data)
} }
#[cfg(test)] #[cfg(test)]
@@ -108,7 +113,7 @@ mod tests {
#[cfg(test)] #[cfg(test)]
mod aes_ctr128 { mod aes_ctr128 {
use super::*; use super::*;
use aes_ctr::Aes128Ctr; use aes::Aes128Ctr;
#[test] #[test]
fn zero_iv_is_actually_zero() { fn zero_iv_is_actually_zero() {
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "erc20-bridge-contract"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
+45
View File
@@ -0,0 +1,45 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
// Serializable structures for what we find in common/crypto
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, JsonSchema)]
pub struct PublicKey([u8; 32]);
impl PublicKey {
pub fn new(bytes: [u8; 32]) -> Self {
PublicKey(bytes)
}
pub fn to_bytes(&self) -> [u8; 32] {
self.0
}
}
impl AsRef<[u8]> for PublicKey {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Signature([u8; 32], [u8; 32]);
impl Signature {
pub fn new(bytes: [u8; 64]) -> Self {
let mut sig1 = [0u8; 32];
let mut sig2 = [0u8; 32];
sig1.copy_from_slice(&bytes[..32]);
sig2.copy_from_slice(&bytes[32..]);
Signature(sig1, sig2)
}
pub fn to_bytes(&self) -> [u8; 64] {
let mut res = [0u8; 64];
res[..32].copy_from_slice(&self.0);
res[32..].copy_from_slice(&self.1);
res
}
}
+3
View File
@@ -0,0 +1,3 @@
pub mod keys;
pub mod msg;
pub mod payment;
+30
View File
@@ -0,0 +1,30 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::keys::PublicKey;
use crate::payment::LinkPaymentData;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InstantiateMsg {}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
LinkPayment { data: LinkPaymentData },
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
GetPayments {
limit: Option<u32>,
start_after: Option<PublicKey>,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {}
@@ -0,0 +1,73 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::keys::{PublicKey, Signature};
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct Payment {
verification_key: PublicKey,
gateway_identity: PublicKey,
bandwidth: u64,
}
impl Payment {
pub fn new(verification_key: PublicKey, gateway_identity: PublicKey, bandwidth: u64) -> Self {
Payment {
verification_key,
gateway_identity,
bandwidth,
}
}
pub fn verification_key(&self) -> PublicKey {
self.verification_key
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct LinkPaymentData {
pub verification_key: PublicKey,
pub gateway_identity: PublicKey,
pub bandwidth: u64,
pub signature: Signature,
}
impl LinkPaymentData {
pub fn new(
verification_key: [u8; 32],
gateway_identity: [u8; 32],
bandwidth: u64,
signature: [u8; 64],
) -> Self {
LinkPaymentData {
verification_key: PublicKey::new(verification_key),
gateway_identity: PublicKey::new(gateway_identity),
bandwidth,
signature: Signature::new(signature),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct PagedPaymentResponse {
pub payments: Vec<Payment>,
pub per_page: usize,
pub start_next_after: Option<PublicKey>,
}
impl PagedPaymentResponse {
pub fn new(
payments: Vec<Payment>,
per_page: usize,
start_next_after: Option<PublicKey>,
) -> Self {
PagedPaymentResponse {
payments,
per_page,
start_next_after,
}
}
}
+10 -1
View File
@@ -8,9 +8,18 @@ edition = "2018"
[dependencies] [dependencies]
# this branch is identical to 0.14.1 with addition of updated k256 dependency required to help poor cargo choose correct version # this branch is identical to 0.14.1 with addition of updated k256 dependency required to help poor cargo choose correct version
cosmwasm-std = { git = "https://github.com/jstuczyn/cosmwasm", branch="0.14.1-updatedk256" } cosmwasm-std = { git = "https://github.com/jstuczyn/cosmwasm", branch = "0.14.1-updatedk256" }
#cosmwasm-std = { version = "0.14.1" } #cosmwasm-std = { version = "0.14.1" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_repr = "0.1" serde_repr = "0.1"
schemars = "0.8" schemars = "0.8"
ts-rs = { version = "5.1", optional = true }
thiserror = "1.0"
network-defaults = { path = "../network-defaults" }
fixed = { version = "1.1", features = ["serde"] }
az = "1.1"
log = "0.4.14"
[features]
default = []
+28 -34
View File
@@ -7,7 +7,24 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Display; use std::fmt::Display;
#[derive(Debug, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct UnpackedDelegation<T> {
pub owner: Addr,
pub node_identity: IdentityKey,
pub delegation_data: T,
}
impl<T> UnpackedDelegation<T> {
pub fn new(owner: Addr, node_identity: IdentityKey, delegation_data: T) -> Self {
UnpackedDelegation {
owner,
node_identity,
delegation_data,
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct RawDelegationData { pub struct RawDelegationData {
pub amount: Uint128, pub amount: Uint128,
pub block_height: u64, pub block_height: u64,
@@ -45,6 +62,10 @@ impl Delegation {
pub fn owner(&self) -> Addr { pub fn owner(&self) -> Addr {
self.owner.clone() self.owner.clone()
} }
pub fn block_height(&self) -> u64 {
self.block_height
}
} }
impl Display for Delegation { impl Display for Delegation {
@@ -100,43 +121,16 @@ impl PagedReverseMixDelegationsResponse {
} }
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct PagedGatewayDelegationsResponse { pub struct PagedAllDelegationsResponse<T> {
pub node_identity: IdentityKey, pub delegations: Vec<UnpackedDelegation<T>>,
pub delegations: Vec<Delegation>, pub start_next_after: Option<Vec<u8>>,
pub start_next_after: Option<Addr>,
} }
impl PagedGatewayDelegationsResponse { impl<T> PagedAllDelegationsResponse<T> {
pub fn new( pub fn new(delegations: Vec<UnpackedDelegation<T>>, start_next_after: Option<Vec<u8>>) -> Self {
node_identity: IdentityKey, PagedAllDelegationsResponse {
delegations: Vec<Delegation>,
start_next_after: Option<Addr>,
) -> Self {
PagedGatewayDelegationsResponse {
node_identity,
delegations, delegations,
start_next_after, start_next_after,
} }
} }
} }
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct PagedReverseGatewayDelegationsResponse {
pub delegation_owner: Addr,
pub delegated_nodes: Vec<IdentityKey>,
pub start_next_after: Option<IdentityKey>,
}
impl PagedReverseGatewayDelegationsResponse {
pub fn new(
delegation_owner: Addr,
delegated_nodes: Vec<IdentityKey>,
start_next_after: Option<IdentityKey>,
) -> Self {
PagedReverseGatewayDelegationsResponse {
delegation_owner,
delegated_nodes,
start_next_after,
}
}
}
+9
View File
@@ -0,0 +1,9 @@
use thiserror::Error;
#[derive(Error, Debug, PartialEq)]
pub enum MixnetContractError {
#[error("Overflow Error")]
OverflowError(#[from] cosmwasm_std::OverflowError),
#[error("reward_blockstamp field not set, set_reward_blockstamp must be called before attempting to issue rewards")]
BlockstampNotSet,
}
+119 -7
View File
@@ -2,14 +2,14 @@
#![allow(clippy::field_reassign_with_default)] #![allow(clippy::field_reassign_with_default)]
use crate::{IdentityKey, SphinxKey}; use crate::{IdentityKey, SphinxKey};
use cosmwasm_std::{coin, Addr, Coin}; use cosmwasm_std::{Addr, Coin};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt::Display; use std::fmt::Display;
use crate::current_block_height; #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)]
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct Gateway { pub struct Gateway {
pub host: String, pub host: String,
pub mix_port: u16, pub mix_port: u16,
@@ -24,9 +24,7 @@ pub struct Gateway {
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct GatewayBond { pub struct GatewayBond {
pub bond_amount: Coin, pub bond_amount: Coin,
pub total_delegation: Coin,
pub owner: Addr, pub owner: Addr,
#[serde(default = "current_block_height")]
pub block_height: u64, pub block_height: u64,
pub gateway: Gateway, pub gateway: Gateway,
} }
@@ -34,7 +32,6 @@ pub struct GatewayBond {
impl GatewayBond { impl GatewayBond {
pub fn new(bond_amount: Coin, owner: Addr, block_height: u64, gateway: Gateway) -> Self { pub fn new(bond_amount: Coin, owner: Addr, block_height: u64, gateway: Gateway) -> Self {
GatewayBond { GatewayBond {
total_delegation: coin(0, &bond_amount.denom),
bond_amount, bond_amount,
owner, owner,
block_height, block_height,
@@ -59,6 +56,39 @@ impl GatewayBond {
} }
} }
impl PartialOrd for GatewayBond {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
// first remove invalid cases
if self.bond_amount.denom != other.bond_amount.denom {
return None;
}
// try to order by total bond
let bond_cmp = self
.bond_amount
.amount
.partial_cmp(&other.bond_amount.amount)?;
if bond_cmp != Ordering::Equal {
return Some(bond_cmp);
}
// then check block height
let height_cmp = self.block_height.partial_cmp(&other.block_height)?;
if height_cmp != Ordering::Equal {
return Some(height_cmp);
}
// finally go by the rest of the fields in order. It doesn't really matter at this point
// but we should be deterministic.
let owner_cmp = self.owner.partial_cmp(&other.owner)?;
if owner_cmp != Ordering::Equal {
return Some(owner_cmp);
}
self.gateway.partial_cmp(&other.gateway)
}
}
impl Display for GatewayBond { impl Display for GatewayBond {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!( write!(
@@ -95,3 +125,85 @@ pub struct GatewayOwnershipResponse {
pub address: Addr, pub address: Addr,
pub has_gateway: bool, pub has_gateway: bool,
} }
#[cfg(test)]
mod tests {
use super::*;
fn gateway_fixture() -> Gateway {
Gateway {
host: "1.1.1.1".to_string(),
mix_port: 123,
clients_port: 456,
location: "foomplandia".to_string(),
sphinx_key: "sphinxkey".to_string(),
identity_key: "identitykey".to_string(),
version: "0.11.0".to_string(),
}
}
#[test]
fn gateway_bond_partial_ord() {
let _150foos = Coin::new(150, "foo");
let _140foos = Coin::new(140, "foo");
let _50foos = Coin::new(50, "foo");
let _0foos = Coin::new(0, "foo");
let gate1 = GatewayBond {
bond_amount: _150foos.clone(),
owner: Addr::unchecked("foo1"),
block_height: 100,
gateway: gateway_fixture(),
};
let gate2 = GatewayBond {
bond_amount: _150foos,
owner: Addr::unchecked("foo2"),
block_height: 120,
gateway: gateway_fixture(),
};
let gate3 = GatewayBond {
bond_amount: _50foos,
owner: Addr::unchecked("foo3"),
block_height: 120,
gateway: gateway_fixture(),
};
let gate4 = GatewayBond {
bond_amount: _140foos,
owner: Addr::unchecked("foo4"),
block_height: 120,
gateway: gateway_fixture(),
};
let gate5 = GatewayBond {
bond_amount: _0foos,
owner: Addr::unchecked("foo5"),
block_height: 120,
gateway: gateway_fixture(),
};
// summary:
// gate1: 150bond, foo1, 100
// gate2: 150bond, foo2, 120
// gate3: 50bond, foo3, 120
// gate4: 140bond, foo4, 120
// gate5: 0bond, foo5, 120
// highest total bond is used
// finally just the rest of the fields
// gate1 has higher total than gate4 or gate5
assert!(gate1 > gate4);
assert!(gate1 > gate5);
// gate1 has the same total as gate3, however, gate1 has more tokens in bond
assert!(gate1 > gate3);
// same case for gate4 and gate5
assert!(gate4 > gate5);
// same bond and delegation, so it's just ordered by height
assert!(gate1 < gate2);
}
}
+7 -11
View File
@@ -2,24 +2,20 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
mod delegation; mod delegation;
pub mod error;
mod gateway; mod gateway;
mod mixnode; pub mod mixnode;
mod msg; mod msg;
mod types; mod types;
pub const MIXNODE_DELEGATORS_PAGE_LIMIT: usize = 250;
pub use cosmwasm_std::{Addr, Coin}; pub use cosmwasm_std::{Addr, Coin};
pub use delegation::{ pub use delegation::{
Delegation, PagedGatewayDelegationsResponse, PagedMixDelegationsResponse, Delegation, PagedAllDelegationsResponse, PagedMixDelegationsResponse,
PagedReverseGatewayDelegationsResponse, PagedReverseMixDelegationsResponse, RawDelegationData, PagedReverseMixDelegationsResponse, RawDelegationData, UnpackedDelegation,
}; };
pub use gateway::{Gateway, GatewayBond, GatewayOwnershipResponse, PagedGatewayResponse}; pub use gateway::{Gateway, GatewayBond, GatewayOwnershipResponse, PagedGatewayResponse};
pub use mixnode::{Layer, MixNode, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse}; pub use mixnode::{Layer, MixNode, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse};
pub use msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; pub use msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
pub use types::{IdentityKey, IdentityKeyRef, LayerDistribution, SphinxKey, StateParams}; pub use types::*;
use std::sync::atomic::{AtomicU64, Ordering};
pub static CURRENT_BLOCK_HEIGHT: AtomicU64 = AtomicU64::new(0);
pub fn current_block_height() -> u64 {
CURRENT_BLOCK_HEIGHT.load(Ordering::Relaxed)
}
+469 -5
View File
@@ -2,15 +2,24 @@
#![allow(clippy::field_reassign_with_default)] #![allow(clippy::field_reassign_with_default)]
use crate::{IdentityKey, SphinxKey}; use crate::{IdentityKey, SphinxKey};
use cosmwasm_std::{coin, Addr, Coin}; use az::CheckedCast;
use cosmwasm_std::{coin, Addr, Coin, Uint128};
use log::error;
use network_defaults::{DEFAULT_OPERATOR_EPOCH_COST, DEFAULT_PROFIT_MARGIN};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_repr::{Deserialize_repr, Serialize_repr};
use std::cmp::Ordering;
use std::fmt::Display; use std::fmt::Display;
use crate::current_block_height; type U128 = fixed::types::U75F53; // u128 with 18 significant digits
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] fixed::const_fixed_from_int! {
const ONE: U128 = 1;
}
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)]
pub struct MixNode { pub struct MixNode {
pub host: String, pub host: String,
pub mix_port: u16, pub mix_port: u16,
@@ -22,7 +31,19 @@ pub struct MixNode {
pub version: String, pub version: String,
} }
#[derive(Copy, Clone, Debug, Serialize_repr, PartialEq, Deserialize_repr, JsonSchema)] #[derive(
Copy,
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize_repr,
Deserialize_repr,
JsonSchema,
)]
#[repr(u8)] #[repr(u8)]
pub enum Layer { pub enum Layer {
Gateway = 0, Gateway = 0,
@@ -31,15 +52,190 @@ pub enum Layer {
Three = 3, Three = 3,
} }
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
pub struct NodeRewardParams {
period_reward_pool: Uint128,
k: Uint128,
reward_blockstamp: u64,
circulating_supply: Uint128,
uptime: Uint128,
sybil_resistance_percent: u8,
}
impl NodeRewardParams {
pub fn new(
period_reward_pool: u128,
k: u128,
reward_blockstamp: u64,
circulating_supply: u128,
uptime: u128,
sybil_resistance_percent: u8,
) -> NodeRewardParams {
NodeRewardParams {
period_reward_pool: Uint128(period_reward_pool),
k: Uint128(k),
reward_blockstamp,
circulating_supply: Uint128(circulating_supply),
uptime: Uint128(uptime),
sybil_resistance_percent,
}
}
pub fn performance(&self) -> U128 {
U128::from_num(self.uptime.u128()) / U128::from_num(100)
}
pub fn operator_cost(&self) -> U128 {
U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_EPOCH_COST as u128)
}
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
self.reward_blockstamp = blockstamp;
}
pub fn period_reward_pool(&self) -> u128 {
self.period_reward_pool.u128()
}
pub fn k(&self) -> u128 {
self.k.u128()
}
pub fn circulating_supply(&self) -> u128 {
self.circulating_supply.u128()
}
pub fn reward_blockstamp(&self) -> u64 {
self.reward_blockstamp
}
pub fn uptime(&self) -> u128 {
self.uptime.u128()
}
pub fn one_over_k(&self) -> U128 {
ONE / U128::from_num(self.k.u128())
}
pub fn alpha(&self) -> U128 {
U128::from_num(self.sybil_resistance_percent) / U128::from_num(100)
}
}
// cosmwasm's limited serde doesn't work with U128 directly
#[allow(non_snake_case)]
pub mod fixed_U128_as_string {
use super::U128;
use serde::de::Error;
use serde::Deserialize;
use std::str::FromStr;
pub fn serialize<S>(val: &U128, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s = (*val).to_string();
serializer.serialize_str(&s)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<U128, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
U128::from_str(&s).map_err(|err| {
D::Error::custom(format!(
"failed to deserialize U128 with its string representation - {}",
err
))
})
}
}
// everything required to reward delegator of given mixnode
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
pub struct DelegatorRewardParams {
node_reward_params: NodeRewardParams,
// to be completely honest I don't understand all consequences of using `#[schemars(with = "String")]`
// for U128 here, but it seems that CosmWasm is using the same attribute for their Uint128
#[schemars(with = "String")]
#[serde(with = "fixed_U128_as_string")]
sigma: U128,
#[schemars(with = "String")]
#[serde(with = "fixed_U128_as_string")]
profit_margin: U128,
#[schemars(with = "String")]
#[serde(with = "fixed_U128_as_string")]
node_profit: U128,
}
impl DelegatorRewardParams {
pub fn new(mixnode_bond: &MixNodeBond, node_reward_params: NodeRewardParams) -> Self {
DelegatorRewardParams {
sigma: mixnode_bond.sigma(&node_reward_params),
profit_margin: mixnode_bond.profit_margin(),
node_profit: mixnode_bond.node_profit(&node_reward_params),
node_reward_params,
}
}
pub fn determine_delegation_reward(&self, delegation_amount: Uint128) -> u128 {
// change all values into their fixed representations
let delegation_amount = U128::from_num(delegation_amount.u128());
let circulating_supply = U128::from_num(self.node_reward_params.circulating_supply());
let scaled_delegation_amount = delegation_amount / circulating_supply;
let delegator_reward =
(ONE - self.profit_margin) * scaled_delegation_amount / self.sigma * self.node_profit;
let reward = delegator_reward.max(U128::ZERO);
if let Some(int_reward) = reward.checked_cast() {
int_reward
} else {
error!(
"Could not cast delegator reward ({}) to u128, returning 0",
reward,
);
0u128
}
}
pub fn node_reward_params(&self) -> &NodeRewardParams {
&self.node_reward_params
}
}
#[derive(Debug)]
pub struct NodeRewardResult {
reward: U128,
lambda: U128,
sigma: U128,
}
impl NodeRewardResult {
pub fn reward(&self) -> U128 {
self.reward
}
pub fn lambda(&self) -> U128 {
self.lambda
}
pub fn sigma(&self) -> U128 {
self.sigma
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
pub struct MixNodeBond { pub struct MixNodeBond {
pub bond_amount: Coin, pub bond_amount: Coin,
pub total_delegation: Coin, pub total_delegation: Coin,
pub owner: Addr, pub owner: Addr,
pub layer: Layer, pub layer: Layer,
#[serde(default = "current_block_height")]
pub block_height: u64, pub block_height: u64,
pub mix_node: MixNode, pub mix_node: MixNode,
pub profit_margin_percent: Option<u8>,
} }
impl MixNodeBond { impl MixNodeBond {
@@ -49,6 +245,7 @@ impl MixNodeBond {
layer: Layer, layer: Layer,
block_height: u64, block_height: u64,
mix_node: MixNode, mix_node: MixNode,
profit_margin_percent: Option<u8>,
) -> Self { ) -> Self {
MixNodeBond { MixNodeBond {
total_delegation: coin(0, &bond_amount.denom), total_delegation: coin(0, &bond_amount.denom),
@@ -57,9 +254,15 @@ impl MixNodeBond {
layer, layer,
block_height, block_height,
mix_node, mix_node,
profit_margin_percent,
} }
} }
pub fn profit_margin(&self) -> U128 {
U128::from_num(self.profit_margin_percent.unwrap_or(DEFAULT_PROFIT_MARGIN))
/ U128::from_num(100)
}
pub fn identity(&self) -> &String { pub fn identity(&self) -> &String {
&self.mix_node.identity_key &self.mix_node.identity_key
} }
@@ -75,6 +278,170 @@ impl MixNodeBond {
pub fn mix_node(&self) -> &MixNode { pub fn mix_node(&self) -> &MixNode {
&self.mix_node &self.mix_node
} }
pub fn total_stake(&self) -> Option<u128> {
if self.bond_amount.denom != self.total_delegation.denom {
None
} else {
Some(self.bond_amount.amount.u128() + self.total_delegation.amount.u128())
}
}
pub fn total_delegation(&self) -> Coin {
self.total_delegation.clone()
}
pub fn bond_to_circulating_supply(&self, circulating_supply: u128) -> U128 {
U128::from_num(self.bond_amount().amount.u128()) / U128::from_num(circulating_supply)
}
pub fn total_stake_to_circulating_supply(&self, circulating_supply: u128) -> U128 {
U128::from_num(self.bond_amount().amount.u128() + self.total_delegation().amount.u128())
/ U128::from_num(circulating_supply)
}
pub fn lambda(&self, params: &NodeRewardParams) -> U128 {
// Ratio of a bond to the token circulating supply
let bond_to_circulating_supply_ratio =
self.bond_to_circulating_supply(params.circulating_supply());
bond_to_circulating_supply_ratio.min(params.one_over_k())
}
pub fn sigma(&self, params: &NodeRewardParams) -> U128 {
// Ratio of a delegation to the the token circulating supply
let total_stake_to_circulating_supply_ratio =
self.total_stake_to_circulating_supply(params.circulating_supply());
total_stake_to_circulating_supply_ratio.min(params.one_over_k())
}
pub fn reward(&self, params: &NodeRewardParams) -> NodeRewardResult {
// Assuming uniform work distribution across the network this is one_over_k * k
let omega_k = ONE;
let lambda = self.lambda(params);
let sigma = self.sigma(params);
let reward = params.performance()
* params.period_reward_pool()
* (sigma * omega_k + params.alpha() * lambda * sigma * params.k())
/ (ONE + params.alpha());
NodeRewardResult {
reward,
lambda,
sigma,
}
}
pub fn node_profit(&self, params: &NodeRewardParams) -> U128 {
if self.reward(params).reward() < params.operator_cost() {
U128::from_num(0)
} else {
self.reward(params).reward() - params.operator_cost()
}
}
pub fn operator_reward(&self, params: &NodeRewardParams) -> u128 {
let reward = self.reward(params);
let profit = if reward.reward < params.operator_cost() {
U128::from_num(0)
} else {
reward.reward - params.operator_cost()
};
let operator_base_reward = reward.reward.min(params.operator_cost());
let operator_reward = (self.profit_margin()
+ (ONE - self.profit_margin()) * reward.lambda / reward.sigma)
* profit;
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0));
if let Some(int_reward) = reward.checked_cast() {
int_reward
} else {
error!(
"Could not cast reward ({}) to u128, returning 0 - mixnode {}",
reward,
self.identity()
);
0u128
}
}
pub fn sigma_ratio(&self, params: &NodeRewardParams) -> U128 {
if self.total_stake_to_circulating_supply(params.circulating_supply()) < params.one_over_k()
{
self.total_stake_to_circulating_supply(params.circulating_supply())
} else {
params.one_over_k()
}
}
pub fn reward_delegation(&self, delegation_amount: Uint128, params: &NodeRewardParams) -> u128 {
let reward_params = DelegatorRewardParams::new(self, *params);
reward_params.determine_delegation_reward(delegation_amount)
}
}
impl PartialOrd for MixNodeBond {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
// first remove invalid cases
if self.bond_amount.denom != self.total_delegation.denom {
return None;
}
if other.bond_amount.denom != other.total_delegation.denom {
return None;
}
if self.bond_amount.denom != other.bond_amount.denom {
return None;
}
// try to order by total bond + delegation
let total_cmp = (self.bond_amount.amount + self.total_delegation.amount)
.partial_cmp(&(self.bond_amount.amount + self.total_delegation.amount))?;
if total_cmp != Ordering::Equal {
return Some(total_cmp);
}
// then if those are equal, prefer higher bond over delegation
let bond_cmp = self
.bond_amount
.amount
.partial_cmp(&other.bond_amount.amount)?;
if bond_cmp != Ordering::Equal {
return Some(bond_cmp);
}
// then look at delegation (I'm not sure we can get here, but better safe than sorry)
let delegation_cmp = self
.total_delegation
.amount
.partial_cmp(&other.total_delegation.amount)?;
if delegation_cmp != Ordering::Equal {
return Some(delegation_cmp);
}
// then check block height
let height_cmp = self.block_height.partial_cmp(&other.block_height)?;
if height_cmp != Ordering::Equal {
return Some(height_cmp);
}
// finally go by the rest of the fields in order. It doesn't really matter at this point
// but we should be deterministic.
let owner_cmp = self.owner.partial_cmp(&other.owner)?;
if owner_cmp != Ordering::Equal {
return Some(owner_cmp);
}
let layer_cmp = self.layer.partial_cmp(&other.layer)?;
if layer_cmp != Ordering::Equal {
return Some(layer_cmp);
}
self.mix_node.partial_cmp(&other.mix_node)
}
} }
impl Display for MixNodeBond { impl Display for MixNodeBond {
@@ -113,3 +480,100 @@ pub struct MixOwnershipResponse {
pub address: Addr, pub address: Addr,
pub has_node: bool, pub has_node: bool,
} }
#[cfg(test)]
mod tests {
use super::*;
fn mixnode_fixture() -> MixNode {
MixNode {
host: "1.1.1.1".to_string(),
mix_port: 123,
verloc_port: 456,
http_api_port: 789,
sphinx_key: "sphinxkey".to_string(),
identity_key: "identitykey".to_string(),
version: "0.11.0".to_string(),
}
}
#[test]
fn mixnode_bond_partial_ord() {
let _150foos = Coin::new(150, "foo");
let _50foos = Coin::new(50, "foo");
let _0foos = Coin::new(0, "foo");
let mix1 = MixNodeBond {
bond_amount: _150foos.clone(),
total_delegation: _50foos.clone(),
owner: Addr::unchecked("foo1"),
layer: Layer::One,
block_height: 100,
mix_node: mixnode_fixture(),
profit_margin_percent: Some(10),
};
let mix2 = MixNodeBond {
bond_amount: _150foos.clone(),
total_delegation: _50foos.clone(),
owner: Addr::unchecked("foo2"),
layer: Layer::One,
block_height: 120,
mix_node: mixnode_fixture(),
profit_margin_percent: Some(10),
};
let mix3 = MixNodeBond {
bond_amount: _50foos,
total_delegation: _150foos.clone(),
owner: Addr::unchecked("foo3"),
layer: Layer::One,
block_height: 120,
mix_node: mixnode_fixture(),
profit_margin_percent: Some(10),
};
let mix4 = MixNodeBond {
bond_amount: _150foos.clone(),
total_delegation: _0foos.clone(),
owner: Addr::unchecked("foo4"),
layer: Layer::One,
block_height: 120,
mix_node: mixnode_fixture(),
profit_margin_percent: Some(10),
};
let mix5 = MixNodeBond {
bond_amount: _0foos,
total_delegation: _150foos,
owner: Addr::unchecked("foo5"),
layer: Layer::One,
block_height: 120,
mix_node: mixnode_fixture(),
profit_margin_percent: Some(10),
};
// summary:
// mix1: 150bond + 50delegation, foo1, 100
// mix2: 150bond + 50delegation, foo2, 120
// mix3: 50bond + 150delegation, foo3, 120
// mix4: 150bond + 0delegation, foo4, 120
// mix5: 0bond + 150delegation, foo5, 120
// highest total bond+delegation is used
// then bond followed by delegation
// finally just the rest of the fields
// mix1 has higher total than mix4 or mix5
assert!(mix1 > mix4);
assert!(mix1 > mix5);
// mix1 has the same total as mix3, however, mix1 has more tokens in bond
assert!(mix1 > mix3);
// same case for mix4 and mix5
assert!(mix4 > mix5);
// same bond and delegation, so it's just ordered by height
assert!(mix1 < mix2);
}
}
+29 -24
View File
@@ -1,6 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::mixnode::NodeRewardParams;
use crate::StateParams; use crate::StateParams;
use crate::{Gateway, IdentityKey, MixNode}; use crate::{Gateway, IdentityKey, MixNode};
use cosmwasm_std::Addr; use cosmwasm_std::Addr;
@@ -31,24 +32,29 @@ pub enum ExecuteMsg {
mix_identity: IdentityKey, mix_identity: IdentityKey,
}, },
DelegateToGateway { BeginMixnodeRewarding {
gateway_identity: IdentityKey, // nonce of the current rewarding interval
rewarding_interval_nonce: u32,
}, },
UndelegateFromGateway { FinishMixnodeRewarding {
gateway_identity: IdentityKey, // nonce of the current rewarding interval
rewarding_interval_nonce: u32,
}, },
RewardMixnode { RewardMixnodeV2 {
identity: IdentityKey, identity: IdentityKey,
// percentage value in range 0-100 // percentage value in range 0-100
uptime: u32, params: NodeRewardParams,
// nonce of the current rewarding interval
rewarding_interval_nonce: u32,
}, },
RewardGateway { RewardNextMixDelegators {
identity: IdentityKey, mix_identity: IdentityKey,
// percentage value in range 0-100 // nonce of the current rewarding interval
uptime: u32, rewarding_interval_nonce: u32,
}, },
} }
@@ -70,11 +76,16 @@ pub enum QueryMsg {
address: Addr, address: Addr,
}, },
StateParams {}, StateParams {},
CurrentRewardingInterval {},
GetMixDelegations { GetMixDelegations {
mix_identity: IdentityKey, mix_identity: IdentityKey,
start_after: Option<Addr>, start_after: Option<Addr>,
limit: Option<u32>, limit: Option<u32>,
}, },
GetAllMixDelegations {
start_after: Option<Vec<u8>>,
limit: Option<u32>,
},
GetReverseMixDelegations { GetReverseMixDelegations {
delegation_owner: Addr, delegation_owner: Addr,
start_after: Option<IdentityKey>, start_after: Option<IdentityKey>,
@@ -84,21 +95,15 @@ pub enum QueryMsg {
mix_identity: IdentityKey, mix_identity: IdentityKey,
address: Addr, address: Addr,
}, },
GetGatewayDelegations {
gateway_identity: IdentityKey,
start_after: Option<Addr>,
limit: Option<u32>,
},
GetReverseGatewayDelegations {
delegation_owner: Addr,
start_after: Option<IdentityKey>,
limit: Option<u32>,
},
GetGatewayDelegation {
gateway_identity: IdentityKey,
address: Addr,
},
LayerDistribution {}, LayerDistribution {},
GetRewardPool {},
GetCirculatingSupply {},
GetEpochRewardPercent {},
GetSybilResistancePercent {},
GetRewardingStatus {
mix_identity: IdentityKey,
rewarding_interval_nonce: u32,
},
} }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
+50 -26
View File
@@ -1,8 +1,9 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::mixnode::DelegatorRewardParams;
use crate::Layer; use crate::Layer;
use cosmwasm_std::{Decimal, Uint128}; use cosmwasm_std::Uint128;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
@@ -26,53 +27,76 @@ impl LayerDistribution {
} }
} }
#[derive(Debug, Default, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)]
pub struct RewardingIntervalResponse {
pub current_rewarding_interval_starting_block: u64,
pub current_rewarding_interval_nonce: u32,
pub rewarding_in_progress: bool,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct StateParams { pub struct StateParams {
pub epoch_length: u32, // length of an epoch, expressed in hours // so currently epoch_length is being unused and validator API performs rewarding
// based on its own epoch length config value. I guess that's fine for time being
// however, in the future, the contract constant should be controlling it instead.
// pub epoch_length: u32, // length of a rewarding epoch/interval, expressed in hours
pub minimum_mixnode_bond: Uint128, // minimum amount a mixnode must bond to get into the system pub minimum_mixnode_bond: Uint128, // minimum amount a mixnode must bond to get into the system
pub minimum_gateway_bond: Uint128, // minimum amount a gateway must bond to get into the system pub minimum_gateway_bond: Uint128, // minimum amount a gateway must bond to get into the system
pub mixnode_bond_reward_rate: Decimal, // annual reward rate, expressed as a decimal like 1.25
pub gateway_bond_reward_rate: Decimal, // annual reward rate, expressed as a decimal like 1.25 // number of mixnode that are going to get rewarded during current rewarding interval (k_m)
pub mixnode_delegation_reward_rate: Decimal, // annual reward rate, expressed as a decimal like 1.25 // based on overall demand for private bandwidth-
pub gateway_delegation_reward_rate: Decimal, // annual reward rate, expressed as a decimal like 1.25 pub mixnode_rewarded_set_size: u32,
// subset of rewarded mixnodes that are actively receiving mix traffic
// used to handle shorter-term (e.g. hourly) fluctuations of demand
pub mixnode_active_set_size: u32, pub mixnode_active_set_size: u32,
} }
impl Display for StateParams { impl Display for StateParams {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Contract state parameters: [ ")?; write!(f, "Contract state parameters: [ ")?;
write!(f, "epoch length: {}; ", self.epoch_length)?;
write!(f, "minimum mixnode bond: {}; ", self.minimum_mixnode_bond)?; write!(f, "minimum mixnode bond: {}; ", self.minimum_mixnode_bond)?;
write!(f, "minimum gateway bond: {}; ", self.minimum_gateway_bond)?; write!(f, "minimum gateway bond: {}; ", self.minimum_gateway_bond)?;
write!( write!(
f, f,
"mixnode bond reward rate: {}; ", "mixnode rewarded set size: {}",
self.mixnode_bond_reward_rate self.mixnode_rewarded_set_size
)?; )?;
write!( write!(
f, f,
"gateway bond reward rate: {}; ", "mixnode active set size: {}",
self.gateway_bond_reward_rate
)?;
write!(
f,
"mixnode delegation reward rate: {}; ",
self.mixnode_delegation_reward_rate
)?;
write!(
f,
"gateway delegation reward rate: {}; ",
self.gateway_delegation_reward_rate
)?;
write!(
f,
"mixnode active set size: {} ]",
self.mixnode_active_set_size self.mixnode_active_set_size
) )
} }
} }
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct RewardingResult {
pub operator_reward: Uint128,
pub total_delegator_reward: Uint128,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PendingDelegatorRewarding {
// keep track of the running rewarding results so we'd known how much was the operator and its delegators rewarded
pub running_results: RewardingResult,
pub next_start: String,
pub rewarding_params: DelegatorRewardParams,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum RewardingStatus {
Complete(RewardingResult),
PendingNextDelegatorPage(PendingDelegatorRewarding),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MixnodeRewardingStatusResponse {
pub status: Option<RewardingStatus>,
}
// type aliases for better reasoning about available data // type aliases for better reasoning about available data
pub type IdentityKey = String; pub type IdentityKey = String;
pub type IdentityKeyRef<'a> = &'a str; pub type IdentityKeyRef<'a> = &'a str;

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