diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..70c468e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,49 @@ + + +# Changelog + +## [Unreleased] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4e31b6c --- /dev/null +++ b/LICENSE @@ -0,0 +1,204 @@ +Cosmos-SDK +License: Apache2.0 + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 All in Bits, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..462cfe1 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +#!/usr/bin/make -f + +all: test clean install lint + +# The below include contains the tools and runsim targets. +include contrib/devtools/Makefile + +build: + go build -mod=readonly -o build/dawnd ./cmd/dawnd + go build -mod=readonly -o build/dawncli ./cmd/dawncli + go build -mod=readonly -o build/dawnrelayer ./cmd/dawnrelayer + +clean: + rm -rf build/ + +install: + go install -mod=readonly ./cmd/dawnd + go install -mod=readonly ./cmd/dawncli + go install -mod=readonly ./cmd/dawnrelayer + +lint: + @echo "--> Running linter" + golangci-lint run + @find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s + go mod verify + +test: + go test ./... + +.PHONY: all build clean install test lint all \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4a0e34c --- /dev/null +++ b/README.md @@ -0,0 +1,309 @@ + +## Requirements + - Go 1.13 + +## DEPLOYMENT OF SMART CONTRACT + +### Set-up + +```bash +Into the Project Directory=> + +cd testnet-contracts/ + +# Create .env with sample environment variables +cp .env.example .env + +# Add environment variable MNEMONIC from your MetaMask account in .env file. + +# Add environment variable INFURA_PROJECT_ID from your Infura account in .env file. + +``` + +### Running the bridge on the Ropsten testnet + +cd testnet-contracts/ + +run the following commands.. +```bash + +# Deploy contract to ropsten network +yarn migrate --network ropsten + +# Get contract's address +yarn peggy:address --network ropsten + +``` + +## SET UP OF NODES/VALIDATORS + +### ON NODE-1 + +### Terminal-1 + +```bash + +# Install tools (golangci-lint v1.18) +make tools-clean +make tools + +# Install the app into your $GOBIN +make install + +# Now you should be able to run the following commands, confirming the build is successful: +dawnd help +dawncli help +dawnrelayer help +``` + +## Running and testing the application + +First, initialize a chain and create accounts. + +```bash +# Initialize the genesis.json file that will help you to bootstrap the network +dawnd init node1 --chain-id=dawn-protocol + +# Create a key to hold your validator account and for another test account +dawncli keys add validator1 +# Enter password + +dawncli keys add testuser +# Enter password + +# Edit the genesis.json file for customised stake denom +--> on terminal go to folder ~/.dawnd/config/genesis.json +--> Edit the staking section having bond_denom key from "stake" to "dawn" +--> Save and close the file. + + +# Initialize the genesis account and transaction +dawnd add-genesis-account $(dawncli keys show validator1 -a) 1000000000dawn,1000000000vspr + +# Create genesis transaction +dawnd gentx --name validator1 --amount 1000000dawn +# Enter password + +# Collect genesis transaction +dawnd collect-gentxs + +#Add Customisation to genesis file +--> on terminal go to folder ~/.dawnd/config/genesis.json +Replace '"staking"' section with the json mentioned in customised_genesis.json (which is in Project Directory) + +# Now its safe to start `dawnd` +dawnd start + +``` +## Terminal-2:Start the Relayer service + +For automated relaying, there is a relayer service that can be run that will automatically watch and relay events (local web socket and deployed address parameters may vary). + +```bash +# Check dawnrelayer connection to ebd +dawnrelayer status + +# Start dawnrelayer on the contract's deployed address with [PEGGY_DEPLOYED_ADDRESS] + +dawnrelayer init wss://ropsten.infura.io/ws [PEGGY_DEPLOYED_ADDRESS] LogLock\(bytes32,address,bytes,address,string,uint256,uint256\) validator1 --chain-id=dawn-protocol +# Enter password and press enter +# You should see a message like: Started ethereum websocket with provider: wss://ropsten.infura.io/ws \ Subscribed to contract events on address: [PEGGY_DEPLOYED_ADDRESS] +# The relayer will now watch the contract on Ropsten and create a claim whenever it detects a lock event. + +#Using the application from rest-server + +dawncli rest-server --trust-node + +``` + + +### ON NODE-2 + +### Terminal-1 + +```bash + +# Install tools (golangci-lint v1.18) +make tools-clean +make tools + +# Install the app into your $GOBIN +make install + +# Now you should be able to run the following commands, confirming the build is successful: +dawnd help +dawncli help +dawnrelayer help +``` + +## Running and testing the application + +```bash +# Initialize the genesis.json file with another moniker and same namechain +dawnd init node2 --chain-id=dawn-protocol + +# Create a key to hold your validator account and for another test account +dawncli keys add validator2 +# Enter password + +overwrite ~/.dawnd/config/genesis.json with first nodes genesis.json + +#change persistent_peers +#run `dawncli status` on first node to get id. +go to ~/.dawnd/config/config.toml +persistent_peers = "@:<26656>" + + +# Now its safe to start `dawnd` +dawnd start +``` + +### FROM NODE-1 + +```bash + +# Then, wait 10 seconds and from first node send tokens to valdator2 address for testing +dawncli tx send validator1 10000dawn --chain-id=dawn-protocol --yes +run "dawncli keys show validator2" on second node to get validator2 address. + +``` +### FROM NODE-2 + +## Terminal-2 + +```bash + +# Next, setup the staking module prerequisites +# First, create a validator and stake +dawncli tx staking create-validator \ + --amount=1000dawn \ + --pubkey=$(dawnd tendermint show-validator) \ + --moniker="node2" \ + --chain-id=dawn-protocol \ + --commission-rate="0.10" \ + --commission-max-rate="0.20" \ + --commission-max-change-rate="0.01" \ + --min-self-delegation="1" \ + --gas=200000 \ + --gas-prices="0.001dawn" \ + --from=validator2 + +``` + +## Terminal-3:Start the Relayer service + +For automated relaying, there is a relayer service that can be run that will automatically watch and relay events (local web socket and deployed address parameters may vary). + +```bash +# Check dawnrelayer connection to ebd +dawnrelayer status + +# Start dawnrelayer on the contract's deployed address with [PEGGY_DEPLOYED_ADDRESS] + +dawnrelayer init wss://ropsten.infura.io/ws [PEGGY_DEPLOYED_ADDRESS] LogLock\(bytes32,address,bytes,address,string,uint256,uint256\) validator2 --chain-id=dawn-protocol +# Enter password and press enter +# You should see a message like: Started ethereum websocket with provider: wss://ropsten.infura.io/ws \ Subscribed to contract events on address: [PEGGY_DEPLOYED_ADDRESS] +# The relayer will now watch the contract on Ropsten and create a claim whenever it detects a lock event. + +#Using the application from rest-server + +dawncli rest-server --trust-node + +``` + +### ON NODE-3 + +### Terminal-1 + +```bash + +# Install tools (golangci-lint v1.18) +make tools-clean +make tools + +# Install the app into your $GOBIN +make install + +# Now you should be able to run the following commands, confirming the build is successful: +dawnd help +dawncli help +dawnrelayer help +``` + +## Running and testing the application + +First, initialize a chain and create accounts. + +```bash +# Initialize the genesis.json file with another moniker and same namechain +dawnd init node3 --chain-id=dawn-protocol + +# Create a key to hold your validator account and for another test account +dawncli keys add validator3 +# Enter password + +overwrite ~/.dawnd/config/genesis.json with first nodes genesis.json + +#change persistent_peers + +go to ~/.dawnd/config/config.toml +persistent_peers = "id@first_node_ip:26656, id@second_node_ip:26659" +run "dawncli status" on first node to get first_node id. +run "dawncli status" on second node to get second_node id. + +# Now its safe to start `dawnd` +dawnd start + +``` +### FROM NODE-1 + +```bash + +# Then, wait 10 seconds and from first node send tokens to valdator3 address for testing +dawncli tx send validator1 10000dawn --chain-id=dawn-protocol --yes +run "dawncli keys show validator3" on third node to get validator3 address. + +``` +### FROM NODE-3 + +## Terminal-2 + +```bash + +# Next, setup the staking module prerequisites +# First, create a validator and stake +dawncli tx staking create-validator \ + --amount=900dawn \ + --pubkey=$(dawnd tendermint show-validator) \ + --moniker="node3" \ + --chain-id=dawn-protocol \ + --commission-rate="0.10" \ + --commission-max-rate="0.20" \ + --commission-max-change-rate="0.01" \ + --min-self-delegation="1" \ + --gas=200000 \ + --gas-prices="0.001dawn" \ + --from=validator3 + +``` +## Terminal-3:Start the Relayer service + +For automated relaying, there is a relayer service that can be run that will automatically watch and relay events (local web socket and deployed address parameters may vary). + +```bash +# Check dawnrelayer connection to ebd +dawnrelayer status + +# Start dawnrelayer on the contract's deployed address with [PEGGY_DEPLOYED_ADDRESS] + +dawnrelayer init wss://ropsten.infura.io/ws [PEGGY_DEPLOYED_ADDRESS] LogLock\(bytes32,address,bytes,address,string,uint256,uint256\) validator3 --chain-id=dawn-protocol +# Enter password and press enter +# You should see a message like: Started ethereum websocket with provider: wss://ropsten.infura.io/ws \ Subscribed to contract events on address: [PEGGY_DEPLOYED_ADDRESS] +# The relayer will now watch the contract on Ropsten and create a claim whenever it detects a lock event. + +#Using the application from rest-server + +dawncli rest-server --trust-node + +``` + diff --git a/app/app.go b/app/app.go new file mode 100644 index 0000000..6fcc488 --- /dev/null +++ b/app/app.go @@ -0,0 +1,246 @@ +package app + +import ( + "encoding/json" + "os" + + abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/version" + "github.com/dawn-protocol/dawn/x/ethbridge" + "github.com/dawn-protocol/dawn/x/oracle" + + bam "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/genaccounts" + "github.com/cosmos/cosmos-sdk/x/genutil" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/supply" +) + +const ( + appName = "ethereum-bridge" +) + +var ( + // DefaultCLIHome default home directories for dawncli + DefaultCLIHome = os.ExpandEnv("$HOME/.dawncli") + + // DefaultNodeHome sets the folder where the application data and configuration will be stored + DefaultNodeHome = os.ExpandEnv("$HOME/.dawnd") + + // ModuleBasics the module BasicManager is in charge of setting up basic, + // non-dependant module elements, such as codec registration + // and genesis verification. + ModuleBasics = module.NewBasicManager( + genaccounts.AppModuleBasic{}, + genutil.AppModuleBasic{}, + auth.AppModuleBasic{}, + bank.AppModuleBasic{}, + staking.AppModuleBasic{}, + params.AppModuleBasic{}, + supply.AppModuleBasic{}, + oracle.AppModuleBasic{}, + ethbridge.AppModuleBasic{}, + ) + + // module account permissions + maccPerms = map[string][]string{ + auth.FeeCollectorName: nil, + staking.BondedPoolName: {supply.Burner, supply.Staking}, + staking.NotBondedPoolName: {supply.Burner, supply.Staking}, + ethbridge.ModuleName: {supply.Burner, supply.Minter}, + } +) + +// MakeCodec generates the necessary codecs for Amino +func MakeCodec() *codec.Codec { + var cdc = codec.New() + ModuleBasics.RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + return cdc +} + +// EthereumBridgeApp defines the Ethereum-Cosmos peg-zone application +type EthereumBridgeApp struct { + *bam.BaseApp + cdc *codec.Codec + + // keys to access the substores + keys map[string]*sdk.KVStoreKey + tkeys map[string]*sdk.TransientStoreKey + + // SDK keepers + // TODO: add governance keeper + AccountKeeper auth.AccountKeeper + BankKeeper bank.Keeper + StakingKeeper staking.Keeper + SupplyKeeper supply.Keeper + ParamsKeeper params.Keeper + + // EthBridge keepers + OracleKeeper oracle.Keeper + + // the module manager + mm *module.Manager +} + +// NewEthereumBridgeApp is a constructor function for EthereumBridgeApp +func NewEthereumBridgeApp(logger log.Logger, db dbm.DB, + baseAppOptions ...func(*bam.BaseApp)) *EthereumBridgeApp { + + // First define the top level codec that will be shared by the different modules + cdc := MakeCodec() + + // BaseApp handles interactions with Tendermint through the ABCI protocol + bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc)) + + bApp.SetAppVersion(version.Version) + + keys := sdk.NewKVStoreKeys(bam.MainStoreKey, auth.StoreKey, staking.StoreKey, + supply.StoreKey, oracle.StoreKey, params.StoreKey) + tkeys := sdk.NewTransientStoreKeys(staking.TStoreKey, params.TStoreKey) + + // Here you initialize your application with the store keys it requires + app := &EthereumBridgeApp{ + BaseApp: bApp, + cdc: cdc, + keys: keys, + tkeys: tkeys, + } + + // init params keeper and subspaces + app.ParamsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace) + + authSubspace := app.ParamsKeeper.Subspace(auth.DefaultParamspace) + bankSubspace := app.ParamsKeeper.Subspace(bank.DefaultParamspace) + stakingSubspace := app.ParamsKeeper.Subspace(staking.DefaultParamspace) + + // add keepers + app.AccountKeeper = auth.NewAccountKeeper(app.cdc, keys[auth.StoreKey], authSubspace, auth.ProtoBaseAccount) + app.BankKeeper = bank.NewBaseKeeper(app.AccountKeeper, bankSubspace, bank.DefaultCodespace, app.ModuleAccountAddrs()) + app.SupplyKeeper = supply.NewKeeper(app.cdc, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms) + app.StakingKeeper = staking.NewKeeper(app.cdc, keys[staking.StoreKey], tkeys[staking.TStoreKey], + app.SupplyKeeper, stakingSubspace, staking.DefaultCodespace) + + app.OracleKeeper = oracle.NewKeeper(app.cdc, keys[oracle.StoreKey], app.StakingKeeper, oracle.DefaultCodespace, oracle.DefaultConsensusNeeded) + + // NOTE: Any module instantiated in the module manager that is later modified + // must be passed by reference here. + app.mm = module.NewManager( + genaccounts.NewAppModule(app.AccountKeeper), + genutil.NewAppModule(app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx), + auth.NewAppModule(app.AccountKeeper), + bank.NewAppModule(app.BankKeeper, app.AccountKeeper), + supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper), + staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), + oracle.NewAppModule(app.OracleKeeper), + ethbridge.NewAppModule(app.OracleKeeper, app.SupplyKeeper, ethbridge.DefaultCodespace, app.cdc), + ) + + app.mm.SetOrderEndBlockers(staking.ModuleName) + + // NOTE: The genutils module must occur after staking so that pools are + // properly initialized with tokens from genesis accounts. + app.mm.SetOrderInitGenesis( + genaccounts.ModuleName, staking.ModuleName, auth.ModuleName, bank.ModuleName, + supply.ModuleName, genutil.ModuleName, + ) + + // TODO: add simulator support + + app.mm.RegisterRoutes(app.Router(), app.QueryRouter()) + + // initialize BaseApp + app.SetInitChainer(app.InitChainer) + app.SetBeginBlocker(app.BeginBlocker) + app.SetAnteHandler(auth.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, auth.DefaultSigVerificationGasConsumer)) + app.SetEndBlocker(app.EndBlocker) + + // initialize stores + app.MountKVStores(keys) + app.MountTransientStores(tkeys) + + err := app.LoadLatestVersion(app.keys[bam.MainStoreKey]) + if err != nil { + cmn.Exit(err.Error()) + } + + return app +} + +// GenesisState represents chain state at the start of the chain. Any initial state (account balances) are stored here. +type GenesisState map[string]json.RawMessage + +func NewDefaultGenesisState() GenesisState { + return ModuleBasics.DefaultGenesis() +} + +// InitChainer application update at chain initialization +func (app *EthereumBridgeApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + var genesisState GenesisState + err := app.cdc.UnmarshalJSON(req.AppStateBytes, &genesisState) + if err != nil { + panic(err) + } + + return app.mm.InitGenesis(ctx, genesisState) +} + +// BeginBlocker application updates every begin block +func (app *EthereumBridgeApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { + return app.mm.BeginBlock(ctx, req) +} + +// EndBlocker application updates every end block +func (app *EthereumBridgeApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { + return app.mm.EndBlock(ctx, req) +} + +// LoadHeight loads a particular height +func (app *EthereumBridgeApp) LoadHeight(height int64) error { + return app.LoadVersion(height, app.keys[bam.MainStoreKey]) +} + +// ModuleAccountAddrs returns all the app's module account addresses. +func (app *EthereumBridgeApp) ModuleAccountAddrs() map[string]bool { + modAccAddrs := make(map[string]bool) + for acc := range maccPerms { + modAccAddrs[supply.NewModuleAddress(acc).String()] = true + } + + return modAccAddrs +} + +// Codec returns simapp's codec +func (app *EthereumBridgeApp) Codec() *codec.Codec { + return app.cdc +} + +// GetKey returns the KVStoreKey for the provided store key +func (app *EthereumBridgeApp) GetKey(storeKey string) *sdk.KVStoreKey { + return app.keys[storeKey] +} + +// GetTKey returns the TransientStoreKey for the provided store key +func (app *EthereumBridgeApp) GetTKey(storeKey string) *sdk.TransientStoreKey { + return app.tkeys[storeKey] +} + +// GetMaccPerms returns a copy of the module account permissions +func GetMaccPerms() map[string][]string { + dupMaccPerms := make(map[string][]string) + for k, v := range maccPerms { + dupMaccPerms[k] = v + } + return dupMaccPerms +} diff --git a/app/export.go b/app/export.go new file mode 100644 index 0000000..ac1e9ec --- /dev/null +++ b/app/export.go @@ -0,0 +1,28 @@ +package app + +import ( + "encoding/json" + + abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/cosmos/cosmos-sdk/x/staking" +) + +// ExportAppStateAndValidators export the state of the eth peg-zone for a genesis file +func (app *EthereumBridgeApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []string, +) (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { + // as if they could withdraw from the start of the next block + ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) + + genState := app.mm.ExportGenesis(ctx) + appState, err = codec.MarshalJSONIndent(app.cdc, genState) + if err != nil { + return nil, nil, err + } + + validators = staking.WriteValidators(ctx, app.StakingKeeper) + return appState, validators, nil +} diff --git a/cmd/dawncli/main.go b/cmd/dawncli/main.go new file mode 100644 index 0000000..d69e7b5 --- /dev/null +++ b/cmd/dawncli/main.go @@ -0,0 +1,162 @@ +package main + +import ( + "fmt" + "os" + "path" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/lcd" + "github.com/cosmos/cosmos-sdk/client/rpc" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + auth "github.com/cosmos/cosmos-sdk/x/auth" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + bank "github.com/cosmos/cosmos-sdk/x/bank" + bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" + + "github.com/dawn-protocol/dawn/app" +) + +func main() { + // Configure cobra to sort commands + cobra.EnableCommandSorting = false + + cdc := app.MakeCodec() + + // Read in the configuration file for the sdk + // TODO: set custom bech32 prefixes for peggy + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) + config.Seal() + + rootCmd := &cobra.Command{ + Use: "dawncli", + Short: "ethereum bridge client", + } + + // Add --chain-id to persistent flags and mark it required + rootCmd.PersistentFlags().String(client.FlagChainID, "", "Chain ID of tendermint node") + rootCmd.PersistentPreRunE = func(_ *cobra.Command, _ []string) error { + return initConfig(rootCmd) + } + + // Construct Root Command + rootCmd.AddCommand( + rpc.StatusCommand(), + client.ConfigCmd(app.DefaultCLIHome), + queryCmd(cdc), + txCmd(cdc), + client.LineBreak, + lcd.ServeCommand(cdc, registerRoutes), + client.LineBreak, + keys.Commands(), + client.LineBreak, + version.Cmd, + client.NewCompletionCmd(rootCmd, true), + ) + + executor := cli.PrepareMainCmd(rootCmd, "EB", app.DefaultCLIHome) + err := executor.Execute() + if err != nil { + fmt.Printf("Failed executing CLI command: %s, exiting...\n", err) + os.Exit(1) + } +} + +// registerRoutes registers the routes from the different modules for the LCD. +// NOTE: details on the routes added for each module are in the module documentation +// NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go +func registerRoutes(rs *lcd.RestServer) { + client.RegisterRoutes(rs.CliCtx, rs.Mux) + app.ModuleBasics.RegisterRESTRoutes(rs.CliCtx, rs.Mux) +} + +func queryCmd(cdc *amino.Codec) *cobra.Command { + queryCmd := &cobra.Command{ + Use: "query", + Aliases: []string{"q"}, + Short: "Querying subcommands", + } + + queryCmd.AddCommand( + authcmd.GetAccountCmd(cdc), + client.LineBreak, + rpc.ValidatorCommand(cdc), + rpc.BlockCommand(), + authcmd.QueryTxsByEventsCmd(cdc), + authcmd.QueryTxCmd(cdc), + client.LineBreak, + ) + + // add modules' query commands + app.ModuleBasics.AddQueryCommands(queryCmd, cdc) + + return queryCmd +} + +func txCmd(cdc *amino.Codec) *cobra.Command { + txCmd := &cobra.Command{ + Use: "tx", + Short: "Transactions subcommands", + } + + txCmd.AddCommand( + bankcmd.SendTxCmd(cdc), + client.LineBreak, + authcmd.GetSignCommand(cdc), + authcmd.GetMultiSignCommand(cdc), + client.LineBreak, + authcmd.GetBroadcastCommand(cdc), + authcmd.GetEncodeCommand(cdc), + client.LineBreak, + ) + + // add modules' tx commands + app.ModuleBasics.AddTxCommands(txCmd, cdc) + + // remove auth and bank commands as they're mounted under the root tx command + var cmdsToRemove []*cobra.Command + + for _, cmd := range txCmd.Commands() { + if cmd.Use == auth.ModuleName || cmd.Use == bank.ModuleName { + cmdsToRemove = append(cmdsToRemove, cmd) + } + } + + txCmd.RemoveCommand(cmdsToRemove...) + + return txCmd +} + +func initConfig(cmd *cobra.Command) error { + home, err := cmd.PersistentFlags().GetString(cli.HomeFlag) + if err != nil { + return err + } + + cfgFile := path.Join(home, "config", "config.toml") + if _, err := os.Stat(cfgFile); err == nil { + viper.SetConfigFile(cfgFile) + + if err := viper.ReadInConfig(); err != nil { + return err + } + } + if err := viper.BindPFlag(client.FlagChainID, cmd.PersistentFlags().Lookup(client.FlagChainID)); err != nil { + return err + } + if err := viper.BindPFlag(cli.EncodingFlag, cmd.PersistentFlags().Lookup(cli.EncodingFlag)); err != nil { + return err + } + return viper.BindPFlag(cli.OutputFlag, cmd.PersistentFlags().Lookup(cli.OutputFlag)) +} diff --git a/cmd/dawnd/main.go b/cmd/dawnd/main.go new file mode 100644 index 0000000..e7e996a --- /dev/null +++ b/cmd/dawnd/main.go @@ -0,0 +1,89 @@ +package main + +import ( + "encoding/json" + "io" + + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/x/staking" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/log" + + "github.com/dawn-protocol/dawn/app" + + abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/x/genaccounts" + genaccscli "github.com/cosmos/cosmos-sdk/x/genaccounts/client/cli" + genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" +) + +func main() { + cdc := app.MakeCodec() + + // TODO: set custom bech32 prefixes for peggy + config := sdk.GetConfig() + config.SetBech32PrefixForAccount("firstbloodaccaddr", "firstbloodaccpub") + config.SetBech32PrefixForValidator("firstbloodvaladdr", "firstbloodvalpub") + config.SetBech32PrefixForConsensusNode("firstbloodconsaddr", "firstbloodconspub") + config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) + + config.Seal() + + ctx := server.NewDefaultContext() + cobra.EnableCommandSorting = false + + rootCmd := &cobra.Command{ + Use: "dawnd", + Short: "Ethereum Bridge App Daemon (server)", + PersistentPreRunE: server.PersistentPreRunEFn(ctx), + } + + rootCmd.AddCommand(genutilcli.InitCmd(ctx, cdc, app.ModuleBasics, app.DefaultNodeHome)) + rootCmd.AddCommand(genutilcli.CollectGenTxsCmd(ctx, cdc, genaccounts.AppModuleBasic{}, app.DefaultNodeHome)) + rootCmd.AddCommand(genutilcli.GenTxCmd(ctx, cdc, app.ModuleBasics, staking.AppModuleBasic{}, + genaccounts.AppModuleBasic{}, app.DefaultNodeHome, app.DefaultCLIHome)) + rootCmd.AddCommand(genutilcli.ValidateGenesisCmd(ctx, cdc, app.ModuleBasics)) + rootCmd.AddCommand(genaccscli.AddGenesisAccountCmd(ctx, cdc, app.DefaultNodeHome, app.DefaultCLIHome)) + + server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators) + + // prepare and add flags + executor := cli.PrepareBaseCmd(rootCmd, "EB", app.DefaultNodeHome) + err := executor.Execute() + if err != nil { + panic(err) + } +} + +func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application { + return app.NewEthereumBridgeApp(logger, db, + baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)), + baseapp.SetHaltHeight(uint64(viper.GetInt(server.FlagHaltHeight)))) +} + +func exportAppStateAndTMValidators( + logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string, +) (json.RawMessage, []tmtypes.GenesisValidator, error) { + + if height != -1 { + ebApp := app.NewEthereumBridgeApp(logger, db) + err := ebApp.LoadHeight(height) + if err != nil { + return nil, nil, err + } + return ebApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) + } + ebApp := app.NewEthereumBridgeApp(logger, db) + return ebApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) +} diff --git a/cmd/dawnrelayer/contract/Peggy.sol b/cmd/dawnrelayer/contract/Peggy.sol new file mode 100644 index 0000000..d3cdcf2 --- /dev/null +++ b/cmd/dawnrelayer/contract/Peggy.sol @@ -0,0 +1,803 @@ +pragma solidity ^0.5.0; + +/** + * @title SafeMath + * @dev Unsigned math operations with safety checks that revert on error + */ +library SafeMath { + /** + * @dev Multiplies two unsigned integers, reverts on overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b); + + return c; + } + + /** + * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + require(b <= a); + uint256 c = a - b; + + return c; + } + + /** + * @dev Adds two unsigned integers, reverts on overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a); + + return c; + } + + /** + * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), + * reverts when dividing by zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + require(b != 0); + return a % b; + } +} + + +/** + * @title ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/20 + */ +interface IERC20 { + + function transfer(address to, uint256 value) external returns (bool); + + function approve(address spender, uint256 value) external returns (bool); + + function transferFrom(address from, address to, uint256 value) external returns (bool); + + function totalSupply() external view returns (uint256); + + function balanceOf(address who) external view returns (uint256); + + function allowance(address owner, address spender) external view returns (uint256); + + event Transfer(address indexed from, address indexed to, uint256 value); + + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +/** + * @title Standard ERC20 token + * + * @dev Implementation of the basic standard token. + * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md + * Originally based on code by FirstBlood: + * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol + * + * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for + * all accounts just by listening to said events. Note that this isn't required by the specification, and other + * compliant implementations may not do it. + */ +contract ERC20 is IERC20 { + using SafeMath for uint256; + + string public name; + string public symbol; + uint8 public decimals; + + mapping (address => uint256) private _balances; + + mapping (address => mapping (address => uint256)) private _allowed; + + uint256 private _totalSupply; + + /** + * @dev Total number of tokens in existence + */ + function totalSupply() public view returns (uint256) { + return _totalSupply; + } + + /** + * @dev Gets the balance of the specified address. + * @param owner The address to query the balance of. + * @return An uint256 representing the amount owned by the passed address. + */ + function balanceOf(address owner) public view returns (uint256) { + return _balances[owner]; + } + + /** + * @dev Function to check the amount of tokens that an owner allowed to a spender. + * @param owner address The address which owns the funds. + * @param spender address The address which will spend the funds. + * @return A uint256 specifying the amount of tokens still available for the spender. + */ + function allowance(address owner, address spender) public view returns (uint256) { + return _allowed[owner][spender]; + } + + /** + * @dev Transfer token for a specified address + * @param to The address to transfer to. + * @param value The amount to be transferred. + */ + function transfer(address to, uint256 value) public returns (bool) { + _transfer(msg.sender, to, value); + return true; + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * Beware that changing an allowance with this method brings the risk that someone may use both the old + * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * @param spender The address which will spend the funds. + * @param value The amount of tokens to be spent. + */ + function approve(address spender, uint256 value) public returns (bool) { + require(spender != address(0)); + + _allowed[msg.sender][spender] = value; + emit Approval(msg.sender, spender, value); + return true; + } + + /** + * @dev Transfer tokens from one address to another. + * Note that while this function emits an Approval event, this is not required as per the specification, + * and other compliant implementations may not emit the event. + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param value uint256 the amount of tokens to be transferred + */ + function transferFrom(address from, address to, uint256 value) public returns (bool) { + _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); + _transfer(from, to, value); + emit Approval(from, msg.sender, _allowed[from][msg.sender]); + return true; + } + + /** + * @dev Increase the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed_[_spender] == 0. To increment + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * Emits an Approval event. + * @param spender The address which will spend the funds. + * @param addedValue The amount of tokens to increase the allowance by. + */ + function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { + require(spender != address(0)); + + _allowed[msg.sender][spender] = _allowed[msg.sender][spender].add(addedValue); + emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); + return true; + } + + /** + * @dev Decrease the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed_[_spender] == 0. To decrement + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * Emits an Approval event. + * @param spender The address which will spend the funds. + * @param subtractedValue The amount of tokens to decrease the allowance by. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { + require(spender != address(0)); + + _allowed[msg.sender][spender] = _allowed[msg.sender][spender].sub(subtractedValue); + emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); + return true; + } + + /** + * @dev Transfer token for a specified addresses + * @param from The address to transfer from. + * @param to The address to transfer to. + * @param value The amount to be transferred. + */ + function _transfer(address from, address to, uint256 value) internal { + require(to != address(0)); + + _balances[from] = _balances[from].sub(value); + _balances[to] = _balances[to].add(value); + emit Transfer(from, to, value); + } + + /** + * @dev Internal function that mints an amount of the token and assigns it to + * an account. This encapsulates the modification of balances such that the + * proper events are emitted. + * @param account The account that will receive the created tokens. + * @param value The amount that will be created. + */ + function _mint(address account, uint256 value) internal { + require(account != address(0)); + + _totalSupply = _totalSupply.add(value); + _balances[account] = _balances[account].add(value); + emit Transfer(address(0), account, value); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account. + * @param account The account whose tokens will be burnt. + * @param value The amount that will be burnt. + */ + function _burn(address account, uint256 value) internal { + require(account != address(0)); + + _totalSupply = _totalSupply.sub(value); + _balances[account] = _balances[account].sub(value); + emit Transfer(account, address(0), value); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account, deducting from the sender's allowance for said account. Uses the + * internal burn function. + * Emits an Approval event (reflecting the reduced allowance). + * @param account The account whose tokens will be burnt. + * @param value The amount that will be burnt. + */ + function _burnFrom(address account, uint256 value) internal { + _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(value); + _burn(account, value); + emit Approval(account, msg.sender, _allowed[account][msg.sender]); + } +} + + /* + * @title: Processor + * @dev: Processes requests for item locking and unlocking by + * storing an item's information then relaying the funds + * the original sender. + */ +contract Processor { + + using SafeMath for uint256; + + /* + * @dev: Item struct to store information. + */ + struct Item { + address payable sender; + bytes recipient; + address token; + uint256 amount; + uint256 nonce; + bool locked; + } + + uint256 public nonce; + mapping(bytes32 => Item) private items; + + /* + * @dev: Constructor, initalizes item count. + */ + constructor() + public + { + nonce = 0; + } + + modifier onlySender(bytes32 _id) { + require( + msg.sender == items[_id].sender, + 'Must be the original sender.' + ); + _; + } + + modifier canDeliver(bytes32 _id) { + if(items[_id].token == address(0)) { + require( + address(this).balance >= items[_id].amount, + 'Insufficient ethereum balance for delivery.' + ); + } else { + require( + ERC20(items[_id].token).balanceOf(address(this)) >= items[_id].amount, + 'Insufficient ERC20 token balance for delivery.' + ); + } + _; + } + + modifier availableNonce() { + require( + nonce + 1 > nonce, + 'No available nonces.' + ); + _; + } + + /* + * @dev: Creates an item with a unique id. + * + * @param _sender: The sender's ethereum address. + * @param _recipient: The intended recipient's cosmos address. + * @param _token: The currency type, either erc20 or ethereum. + * @param _amount: The amount of erc20 tokens/ ethereum (in wei) to be itemized. + * @return: The newly created item's unique id. + */ + function create( + address payable _sender, + bytes memory _recipient, + address _token, + uint256 _amount + ) + internal + returns(bytes32) + { + nonce++; + + bytes32 itemKey = keccak256( + abi.encodePacked( + _sender, + _recipient, + _token, + _amount, + nonce + ) + ); + + items[itemKey] = Item( + _sender, + _recipient, + _token, + _amount, + nonce, + true + ); + + return itemKey; + } + + /* + * @dev: Completes the item by sending the funds to the + * original sender and unlocking the item. + * + * @param _id: The item to be completed. + */ + function complete( + bytes32 _id + ) + internal + canDeliver(_id) + returns(address payable, address, uint256, uint256) + { + require(isLocked(_id)); + + //Get locked item's attributes for return + address payable sender = items[_id].sender; + address token = items[_id].token; + uint256 amount = items[_id].amount; + uint256 uniqueNonce = items[_id].nonce; + + //Update lock status + items[_id].locked = false; + + //Transfers based on token address type + if (token == address(0)) { + sender.transfer(amount); + } else { + require(ERC20(token).transfer(sender, amount)); + } + + return(sender, token, amount, uniqueNonce); + } + + /* + * @dev: Checks the current nonce. + * + * @return: The current nonce. + */ + function getNonce() + internal + view + returns(uint256) + { + return nonce; + } + + /* + * @dev: Checks if an individual item exists. + * + * @param _id: The unique item's id. + * @return: Boolean indicating if the item exists in memory. + */ + function isLocked( + bytes32 _id + ) + internal + view + returns(bool) + { + return(items[_id].locked); + } + + /* + * @dev: Gets an item's information + * + * @param _Id: The item containing the desired information. + * @return: Sender's address. + * @return: Recipient's address in bytes. + * @return: Token address. + * @return: Amount of ethereum/erc20 in the item. + * @return: Unique nonce of the item. + */ + function getItem( + bytes32 _id + ) + internal + view + returns(address payable, bytes memory, address, uint256, uint256) + { + Item memory item = items[_id]; + + return( + item.sender, + item.recipient, + item.token, + item.amount, + item.nonce + ); + } +} + + /* + * @title: Peggy + * @dev: Peg zone contract for testing one-way transfers from Ethereum + * to Cosmos, facilitated by a trusted provider. This contract is + * NOT intended to be used in production and users are empowered + * to withdraw their locked funds at any time. + */ +contract Peggy is Processor { + + bool public active; + address payable public provider; + mapping(bytes32 => bool) public ids; + bool public unlocking_status=false; + uint256 last_burn=1609286400; + address token_address=address(0); + + event LogLock( + bytes32 _id, + address _from, + bytes _to, + address _token, + string _symbol, + uint256 _value, + uint256 _nonce + ); + + event LogUnlock( + bytes32 _id, + address _to, + address _token, + uint256 _value, + uint256 _nonce + ); + + event LogWithdraw( + bytes32 _id, + address _to, + address _token, + uint256 _value, + uint256 _nonce + ); + + event LogLockingPaused( + uint256 _time + ); + + event LogLockingActivated( + uint256 _time + ); + + event LogUnlockingPaused( + uint256 _time + ); + + event LogUnlockingActivated( + uint256 _time + ); + + /* + * @dev: Modifier to restrict access to the provider. + * + */ + modifier onlyProvider() + { + require( + msg.sender == provider, + 'Must be the specified provider.' + ); + _; + } + + /* + * @dev: Modifier which restricts lock functionality when paused. + * + */ + modifier whileActive() + { + require( + active == true, + 'Lock functionality is currently paused.' + ); + _; + } + + /* + * @dev: Constructor, initalizes provider and active status. + * + */ + modifier onlyApproved(uint8 _v, bytes32 _r, bytes32 _s) + { + require ( approvedMessage(msg.sender, _v, _r, _s)); + _; + } + + /* + * @dev: Constructor, initalizes provider and active status. + * + */ + constructor() + public + { + provider = msg.sender; + active = true; + emit LogLockingActivated(now); + } + + /* + * @dev: Locks received funds and creates new items. + * + * @param _recipient: bytes representation of destination address. + * @param _token: token address in origin chain (0x0 if ethereum) + * @param _amount: value of item + */ + function lock( + bytes memory _recipient, + address _token, + uint256 _amount, + uint8 _v, + bytes32 _r, + bytes32 _s + ) + public + payable + availableNonce() + whileActive() + onlyApproved(_v, _r, _s) + returns(bytes32 _id) + { + string memory symbol; + + //Actions based on token address type + if (msg.value != 0) { + require(_token == address(0)); + require(msg.value == _amount); + symbol = "ETH"; + } else { + require(ERC20(_token).transferFrom(msg.sender, address(this), _amount)); + symbol = ERC20(_token).symbol(); + } + + //Create an item with a unique key. + bytes32 id = create( + msg.sender, + _recipient, + _token, + _amount + ); + + emit LogLock( + id, + msg.sender, + _recipient, + _token, + symbol, + _amount, + getNonce() + ); + + return id; + } + + /* + * @dev: Unlocks ethereum/erc20 tokens, called by provider. + * + * This is a shortcut utility method for testing purposes. + * In the future bidirectional system, unlocking functionality + * will be guarded by validator signatures. + * + * @param _id: Unique key of the item. + */ + function unlock( + bytes32 _id + ) + onlySender(_id) + canDeliver(_id) + public + returns (bool) + { + require(unlocking_status); // If unlocking_status === true + + require(isLocked(_id)); // Only pass if the tokens are locked + + // Transfer item's funds and unlock it + (address payable sender, + address token, + uint256 amount, + uint256 uniqueNonce) = complete(_id); + + //Emit unlock event + emit LogUnlock( + _id, + sender, + token, + amount, + uniqueNonce + ); + return true; + } + + + + /* + * @dev: Exposes an item's current status. + * + * @param _id: The item in question. + * @return: Boolean indicating the lock status. + */ + function getStatus( + bytes32 _id + ) + public + view + returns(bool) + { + return isLocked(_id); + } + + /* + * @dev: Allows access to an item's information via its unique identifier. + * + * @param _id: The item to be viewed. + * @return: Original sender's address. + * @return: Intended receiver's address in bytes. + * @return: The token's address. + * @return: The amount locked in the item. + * @return: The item's unique nonce. + */ + function viewItem( + bytes32 _id + ) + public + view + returns(address, bytes memory, address, uint256, uint256) + { + return getItem(_id); + } + + /* + * @dev: Provider can pause fund locking without impacting other functionality. + */ + function pauseLocking() + public + onlyProvider + { + require(active); + active = false; + emit LogLockingPaused(now); + } + + /* + * @dev: Provider can activate fund locking without impacting other functionality. + */ + function activateLocking() + public + onlyProvider + { + require(!active); + active = true; + emit LogLockingActivated(now); + } + + /* + * @dev: Provider can pause fund unlocking without impacting other functionality. + */ + function pauseUnlocking() + public + onlyProvider + { + require(unlocking_status); + unlocking_status = false; + emit LogUnlockingPaused(now); + } + + /* + * @dev: Provider can activate fund unlocking locking without impacting other functionality. + */ + function activateUnlocking() + public + onlyProvider + { + require(!unlocking_status); + unlocking_status = true; + emit LogUnlockingActivated(now); + } + + + /* + * @dev: Check if the message is sent by a valid approved address. + * + * @param _add: Address of sender. + * @param _v + * @param _r + * @param _s + * @return: Address of message sender. + */ + + function approvedMessage( + address _add, + uint8 _v, + bytes32 _r, + bytes32 _s + ) view public returns (bool) + { + bytes32 hash = keccak256(abi.encodePacked(this, _add)); + return provider == ecrecover( + keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)), + _v, + _r, + _s + ); + } + + function burn() + public + payable + onlyProvider + returns (bool) + + { + + require(!unlocking_status); + require(now>last_burn); + require( ERC20(token_address).transfer( address(0) , ERC20(token_address).balanceOf(address(this)))); + provider.transfer(address(this).balance); + return true; + } +} \ No newline at end of file diff --git a/cmd/dawnrelayer/contract/abi/Peggy.abi b/cmd/dawnrelayer/contract/abi/Peggy.abi new file mode 100644 index 0000000..76578b5 --- /dev/null +++ b/cmd/dawnrelayer/contract/abi/Peggy.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"active","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"provider","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nonce","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"ids","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_id","type":"bytes32"},{"indexed":false,"name":"_from","type":"address"},{"indexed":false,"name":"_to","type":"bytes"},{"indexed":false,"name":"_token","type":"address"},{"indexed":false,"name":"_symbol","type":"string"},{"indexed":false,"name":"_value","type":"uint256"},{"indexed":false,"name":"_nonce","type":"uint256"}],"name":"LogLock","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_id","type":"bytes32"},{"indexed":false,"name":"_to","type":"address"},{"indexed":false,"name":"_token","type":"address"},{"indexed":false,"name":"_value","type":"uint256"},{"indexed":false,"name":"_nonce","type":"uint256"}],"name":"LogUnlock","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_id","type":"bytes32"},{"indexed":false,"name":"_to","type":"address"},{"indexed":false,"name":"_token","type":"address"},{"indexed":false,"name":"_value","type":"uint256"},{"indexed":false,"name":"_nonce","type":"uint256"}],"name":"LogWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_time","type":"uint256"}],"name":"LogLockingPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_time","type":"uint256"}],"name":"LogLockingActivated","type":"event"},{"constant":false,"inputs":[{"name":"_recipient","type":"bytes"},{"name":"_token","type":"address"},{"name":"_amount","type":"uint256"}],"name":"lock","outputs":[{"name":"_id","type":"bytes32"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"bytes32"}],"name":"unlock","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_id","type":"bytes32"}],"name":"withdraw","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"bytes32"}],"name":"getStatus","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"bytes32"}],"name":"viewItem","outputs":[{"name":"","type":"address"},{"name":"","type":"bytes"},{"name":"","type":"address"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pauseLocking","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"activateLocking","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] diff --git a/cmd/dawnrelayer/contract/contract.go b/cmd/dawnrelayer/contract/contract.go new file mode 100644 index 0000000..2d2aba7 --- /dev/null +++ b/cmd/dawnrelayer/contract/contract.go @@ -0,0 +1,40 @@ +package contract + +// ------------------------------------------------------- +// Contract : Contains functionality for loading the +// smart contract +// ------------------------------------------------------- + +import ( + "go/build" + "io/ioutil" + "os" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +// AbiPath : path to the file containing the smart contract's ABI +const AbiPath = "/src/github.com/dawn-protocol/dawn/cmd/dawnrelayer/contract/abi/Peggy.abi" + +// LoadABI : loads a smart contract as an abi.ABI +func LoadABI() abi.ABI { + // Open the file containing Peggy contract's ABI + gopath := os.Getenv("GOPATH") + if gopath == "" { + gopath = build.Default.GOPATH + } + + peggyABI, err := ioutil.ReadFile(gopath + AbiPath) + if err != nil { + panic(err) + } + + // Convert the raw abi into a usable format + contractAbi, err := abi.JSON(strings.NewReader(string(peggyABI))) + if err != nil { + panic(err) + } + + return contractAbi +} diff --git a/cmd/dawnrelayer/contract/contract_test.go b/cmd/dawnrelayer/contract/contract_test.go new file mode 100644 index 0000000..24fb0db --- /dev/null +++ b/cmd/dawnrelayer/contract/contract_test.go @@ -0,0 +1,18 @@ +package contract + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +// TestLoadABI : test that contract containing named event is successfully loaded +func TestLoadABI(t *testing.T) { + + const AbiPath = "/src/github.com/dawn-protocol/dawn/cmd/dawnrelayer/contract/abi/Peggy.abi" + + //Get the ABI ready + abi := LoadABI() + + require.NotNil(t, abi.Events["LogLock"]) +} \ No newline at end of file diff --git a/cmd/dawnrelayer/events/event.go b/cmd/dawnrelayer/events/event.go new file mode 100644 index 0000000..1ac4e84 --- /dev/null +++ b/cmd/dawnrelayer/events/event.go @@ -0,0 +1,74 @@ +package events + +// ----------------------------------------------------- +// Event : Creates LockEvents from new events on the ethereum +// Ethereum blockchain. +// ----------------------------------------------------- + +import ( + "encoding/hex" + "fmt" + "log" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" +) + +// LockEvent : struct which represents a single smart contract event +type LockEvent struct { + EthereumChainID *big.Int + BridgeContractAddress common.Address + Id [32]byte + From common.Address + To []byte + Token common.Address + Symbol string + Value *big.Int + Nonce *big.Int +} + +// NewLockEvent : parses LogLock events using go-ethereum's accounts/abi library +func NewLockEvent(contractAbi abi.ABI, clientChainID *big.Int, contractAddress string, eventName string, eventData []byte) LockEvent { + // Check event name + if eventName != "LogLock" { + log.Fatal("Only LogLock events are currently supported.") + } + + // Parse the event's attributes as Ethereum network variables + event := LockEvent{} + + if !common.IsHexAddress(contractAddress) { + log.Fatalf("Only Ethereum contracts are currently supported. Invalid address: %v", contractAddress) + } + + event.EthereumChainID = clientChainID + event.BridgeContractAddress = common.HexToAddress(contractAddress) + + err := contractAbi.Unpack(&event, eventName, eventData) + if err != nil { + log.Fatalf("Error unpacking: %v", err) + } + + PrintEvent(event) + + return event +} + +// PrintEvent : prints a LockEvent struct's information +func PrintEvent(event LockEvent) { + // Convert the variables into a printable format + chainID := event.EthereumChainID + bridgeContractAddress := event.BridgeContractAddress + id := hex.EncodeToString(event.Id[:]) + sender := event.From.Hex() + token := event.Token.Hex() + recipient := string(event.To) + symbol := event.Symbol + value := event.Value + nonce := event.Nonce + + // Print the event's information + fmt.Printf("\nChain ID: %v\nBridge contract address: %v\nEvent ID: %v\nToken symbol: %v\nToken contract address: %v\nSender: %v\nRecipient: %v\nValue: %v\nNonce: %v\n\n", + chainID, bridgeContractAddress, id, symbol, token, sender, recipient, value, nonce) +} diff --git a/cmd/dawnrelayer/events/events.go b/cmd/dawnrelayer/events/events.go new file mode 100644 index 0000000..9d507d2 --- /dev/null +++ b/cmd/dawnrelayer/events/events.go @@ -0,0 +1,42 @@ +package events + +// ----------------------------------------------------- +// Events: Events maintains a mapping of events to an array +// of claims made by validators. +// ----------------------------------------------------- + +import ( + "fmt" +) + +// EventRecords : map of transaction hashes to LockEvent structs +var EventRecords = make(map[string]LockEvent) + +// NewEventWrite : add a validator's address to the official claims list +func NewEventWrite(txHash string, event LockEvent) { + EventRecords[txHash] = event +} + +// IsEventRecorded : checks the sessions stored events for this transaction hash +func IsEventRecorded(txHash string) bool { + return EventRecords[txHash].Nonce != nil +} + +// PrintEventByTx : prints any witnessed events associated with a given transaction hash +func PrintEventByTx(txHash string) { + if IsEventRecorded(txHash) { + PrintEvent(EventRecords[txHash]) + } else { + fmt.Printf("\nNo records from this session for tx: %v\n", txHash) + } +} + +// PrintEvents : prints all the claims made on this event +func PrintEvents() { + + // For each claim, print the validator which submitted the claim + for txHash, event := range EventRecords { + fmt.Printf("\nTransaction: %v\n", txHash) + PrintEvent(event) + } +} diff --git a/cmd/dawnrelayer/main.go b/cmd/dawnrelayer/main.go new file mode 100644 index 0000000..ddedc47 --- /dev/null +++ b/cmd/dawnrelayer/main.go @@ -0,0 +1,165 @@ +package main + +// Main (dawnrelayer) : Implements CLI commands for the Relayer +// service, such as initialization and event relay. + +import ( + "fmt" + "os" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client" + sdkContext "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/rpc" + sdk "github.com/cosmos/cosmos-sdk/types" + authtxb "github.com/cosmos/cosmos-sdk/x/auth/types" + + app "github.com/dawn-protocol/dawn/app" + relayer "github.com/dawn-protocol/dawn/cmd/dawnrelayer/relayer" +) + +var appCodec *amino.Codec + +func init() { + + // Read in the configuration file for the sdk + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) + config.Seal() + + appCodec = app.MakeCodec() + + DefaultCLIHome := os.ExpandEnv("$HOME/.dawncli") + + // Add --chain-id to persistent flags and mark it required + rootCmd.PersistentFlags().String(client.FlagChainID, "", "Chain ID of tendermint node") + rootCmd.PersistentPreRunE = func(_ *cobra.Command, _ []string) error { + return initConfig(rootCmd) + } + + // Construct Root Command + rootCmd.AddCommand( + rpc.StatusCommand(), + initRelayerCmd(), + ) + + executor := cli.PrepareMainCmd(rootCmd, "dawnrelayer", DefaultCLIHome) + err := executor.Execute() + if err != nil { + panic(err) + } +} + +var rootCmd = &cobra.Command{ + Use: "dawnrelayer", + Short: "Relayer service which listens for and relays ethereum smart contract events", + SilenceUsage: true, +} + +// initRelayerCmd : Initializes a relayer service run by individual +// validators which streams live events from a smart contract. +// The service automatically signs messages containing the event +// data and relays them to tendermint for handling by the +// EthBridge module. +// +func initRelayerCmd() *cobra.Command { + initRelayerCmd := &cobra.Command{ + Use: "init [web3Provider] [contractAddress] [eventSignature] [validatorFromName] --chain-id [chain-id]", + Short: "Initializes a web socket which streams live events from a smart contract", + Args: cobra.ExactArgs(4), + // NOTE: Preface both parentheses in the event signature with a '\' + Example: "dawnrelayer init wss://ropsten.infura.io/ws 05d9758cb6b9d9761ecb8b2b48be7873efae15c0 LogLock(bytes32,address,bytes,address,string,uint256,uint256) validator --chain-id=testing", + RunE: RunRelayerCmd, + } + + return initRelayerCmd +} + +// RunRelayerCmd executes the initRelayerCmd with the provided parameters +func RunRelayerCmd(cmd *cobra.Command, args []string) error { + // Parse chain's ID + chainID := viper.GetString(client.FlagChainID) + if strings.TrimSpace(chainID) == "" { + return fmt.Errorf("Must specify a 'chain-id'") + } + + // Parse ethereum provider + ethereumProvider := args[0] + if !relayer.IsWebsocketURL(ethereumProvider) { + return fmt.Errorf("Invalid web3-provider: %v", ethereumProvider) + } + + // Parse the address of the deployed contract + if !common.IsHexAddress(args[1]) { + return fmt.Errorf("Invalid contract-address: %v", args[1]) + } + contractAddress := common.HexToAddress(args[1]) + + // Convert event signature to []bytes and apply the Keccak256Hash + eventSigHash := crypto.Keccak256Hash([]byte(args[2])) + + // Get the hex event signature from the hash. + eventSig := eventSigHash.Hex() + + // Parse the validator's moniker + validatorFrom := args[3] + + // Get the validator's name and account address using their moniker + validatorAccAddress, validatorName, err := sdkContext.GetFromFields(validatorFrom, false) + if err != nil { + return err + } + // Convert the validator's account address into type ValAddress + validatorAddress := sdk.ValAddress(validatorAccAddress) + + // Get the validator's passphrase using their moniker + passphrase, err := keys.GetPassphrase(validatorFrom) + if err != nil { + return err + } + + // Test passphrase is correct + _, err = authtxb.MakeSignature(nil, validatorName, passphrase, authtxb.StdSignMsg{}) + if err != nil { + return err + } + + // Initialize the relayer + err = relayer.InitRelayer( + appCodec, + chainID, + ethereumProvider, + contractAddress, + eventSig, + validatorName, + passphrase, + validatorAddress) + + if err != nil { + return err + } + + return nil +} + +func initConfig(cmd *cobra.Command) error { + return viper.BindPFlag(client.FlagChainID, cmd.PersistentFlags().Lookup(client.FlagChainID)) +} + +func main() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} diff --git a/cmd/dawnrelayer/relayer/network.go b/cmd/dawnrelayer/relayer/network.go new file mode 100644 index 0000000..7251bf2 --- /dev/null +++ b/cmd/dawnrelayer/relayer/network.go @@ -0,0 +1,45 @@ +package relayer + +// ------------------------------------------------------------ +// Network: Validates input and initializes a websocket Ethereum client. +// ------------------------------------------------------------ + +import ( + "fmt" + "net/url" + "strings" + + "github.com/ethereum/go-ethereum/ethclient" + log "github.com/golang/glog" +) + +// IsWebsocketURL : returns true if the given URL is a websocket URL +func IsWebsocketURL(rawurl string) bool { + u, err := url.Parse(rawurl) + if err != nil { + log.Infof("Error while parsing URL: %v", err) + return false + } + return u.Scheme == "ws" || u.Scheme == "wss" +} + +// SetupWebsocketEthClient : returns boolean indicating if a URL is valid websocket ethclient +func SetupWebsocketEthClient(ethURL string) (*ethclient.Client, error) { + if strings.TrimSpace(ethURL) == "" { + return nil, nil + } + + if !IsWebsocketURL(ethURL) { + return nil, fmt.Errorf( + "invalid websocket eth client URL: %v", + ethURL, + ) + } + + client, err := ethclient.Dial(ethURL) + if err != nil { + return nil, fmt.Errorf("error dialing websocket client: %v", err) + } + + return client, nil +} diff --git a/cmd/dawnrelayer/relayer/network_test.go b/cmd/dawnrelayer/relayer/network_test.go new file mode 100644 index 0000000..093496c --- /dev/null +++ b/cmd/dawnrelayer/relayer/network_test.go @@ -0,0 +1,24 @@ +package relayer + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +const ( + Client = "wss://ropsten.infura.io/ws" +) + +// TestIsWebsocketURL : test identification of Ethereum websocket URLs +func TestIsWebsocketURL(t *testing.T) { + result := IsWebsocketURL(Client) + require.True(t, result) +} + +// TestSetupWebsocketEthClient : test initialization of Ethereum websocket +func TestSetupWebsocketEthClient(t *testing.T) { + _, err := SetupWebsocketEthClient(Client) + + require.NoError(t, err) +} diff --git a/cmd/dawnrelayer/relayer/relayer.go b/cmd/dawnrelayer/relayer/relayer.go new file mode 100644 index 0000000..089a497 --- /dev/null +++ b/cmd/dawnrelayer/relayer/relayer.go @@ -0,0 +1,94 @@ +package relayer + +// ----------------------------------------------------- +// Relayer +// +// Initializes the relayer service, which parses, +// encodes, and packages named events on an Ethereum +// Smart Contract for validator's to sign and send +// to the Cosmos bridge. +// ----------------------------------------------------- + +import ( + "context" + "fmt" + "log" + + amino "github.com/tendermint/go-amino" + + sdk "github.com/cosmos/cosmos-sdk/types" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/dawn-protocol/dawn/cmd/dawnrelayer/contract" + "github.com/dawn-protocol/dawn/cmd/dawnrelayer/events" + "github.com/dawn-protocol/dawn/cmd/dawnrelayer/txs" +) + +// InitRelayer : Starts an event listener on a specific network, contract, and event +func InitRelayer(cdc *amino.Codec, chainId string, provider string, contractAddress common.Address, eventSig string, validatorName string, passphrase string, validatorAddress sdk.ValAddress) error { + + // Start client with infura ropsten provider + client, err := SetupWebsocketEthClient(provider) + if err != nil { + return err + } + fmt.Printf("\nStarted ethereum websocket with provider: %s", provider) + + // We need the contract address in bytes[] for the query + query := ethereum.FilterQuery{ + Addresses: []common.Address{contractAddress}, + } + + // We will check logs for new events + logs := make(chan types.Log) + + // Filter by contract and event, write results to logs + sub, err := client.SubscribeFilterLogs(context.Background(), query, logs) + if err != nil { + return err + } + fmt.Printf("\nSubscribed to contract events on address: %s\n", contractAddress.Hex()) + + // Load Peggy Contract's ABI + contractABI := contract.LoadABI() + + clientChainID, err := client.NetworkID(context.Background()) + if err != nil { + log.Fatal(err) + } + + for { + select { + // Handle any errors + case err := <-sub.Err(): + log.Fatal(err) + // vLog is raw event data + case vLog := <-logs: + // Check if the event is a 'LogLock' event + if vLog.Topics[0].Hex() == eventSig { + fmt.Printf("\n\nNew Lock Transaction:\nTx hash: %v\nBlock number: %v\n", + vLog.TxHash.Hex(), vLog.BlockNumber) + + // Parse the event data into a new LockEvent using the contract's ABI + event := events.NewLockEvent(contractABI, clientChainID, contractAddress.Hex(), "LogLock", vLog.Data) + + // Add the event to the record + events.NewEventWrite(vLog.TxHash.Hex(), event) + // Parse the event's payload into a struct + claim, err := txs.ParsePayload(validatorAddress, &event) + if err != nil { + return err + } + + // Initiate the relay + err = txs.RelayEvent(chainId, cdc, validatorAddress, validatorName, passphrase, &claim) + if err != nil { + return err + } + } + } + } +} diff --git a/cmd/dawnrelayer/txs/parser.go b/cmd/dawnrelayer/txs/parser.go new file mode 100644 index 0000000..bcd230f --- /dev/null +++ b/cmd/dawnrelayer/txs/parser.go @@ -0,0 +1,72 @@ +package txs + +// -------------------------------------------------------- +// Parser +// +// Parses structs containing event information into +// unsigned transactions for validators to sign, then +// relays the data packets as transactions on the +// Cosmos Bridge. +// -------------------------------------------------------- + +import ( + "errors" + "strings" + + "github.com/dawn-protocol/dawn/cmd/dawnrelayer/events" + "github.com/dawn-protocol/dawn/cmd/dawnrelayer/utils" + ethbridgeTypes "github.com/dawn-protocol/dawn/x/ethbridge/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ParsePayload : parses and packages a LockEvent struct with a validator address in an EthBridgeClaim msg +func ParsePayload(valAddr sdk.ValAddress, event *events.LockEvent) (ethbridgeTypes.EthBridgeClaim, error) { + + witnessClaim := ethbridgeTypes.EthBridgeClaim{} + + // chainID type casting (*big.Int -> int) + chainID := int(event.EthereumChainID.Int64()) + + bridgeContractAddress := ethbridgeTypes.NewEthereumAddress(event.BridgeContractAddress.Hex()) + + // Nonce type casting (*big.Int -> int) + nonce := int(event.Nonce.Int64()) + + // Sender type casting (address.common -> string) + sender := ethbridgeTypes.NewEthereumAddress(event.From.Hex()) + + // Recipient type casting ([]bytes -> sdk.AccAddress) + recipient, err := sdk.AccAddressFromBech32(string(event.To)) + if err != nil { + return witnessClaim, err + } + if recipient.Empty() { + return witnessClaim, errors.New("empty recipient address") + } + + // Sender type casting (address.common -> string) + tokenContractAddress := ethbridgeTypes.NewEthereumAddress(event.Token.Hex()) + + // Symbol formatted to lowercase + symbol := strings.ToLower(event.Symbol) + if symbol == "eth" && !utils.IsZeroAddress(event.Token) { + return witnessClaim, errors.New("symbol \"eth\" must have null address set as token address") + } + + // Amount type casting (*big.Int -> sdk.Coins) + coins := sdk.Coins{sdk.NewInt64Coin(symbol, event.Value.Int64())} + + // Package the information in a unique EthBridgeClaim + witnessClaim.EthereumChainID = chainID + witnessClaim.BridgeContractAddress = bridgeContractAddress + witnessClaim.Nonce = nonce + witnessClaim.TokenContractAddress = tokenContractAddress + witnessClaim.Symbol = symbol + witnessClaim.EthereumSender = sender + witnessClaim.ValidatorAddress = valAddr + witnessClaim.CosmosReceiver = recipient + witnessClaim.Amount = coins + + return witnessClaim, nil +} diff --git a/cmd/dawnrelayer/txs/relay.go b/cmd/dawnrelayer/txs/relay.go new file mode 100644 index 0000000..c2e39f7 --- /dev/null +++ b/cmd/dawnrelayer/txs/relay.go @@ -0,0 +1,77 @@ +package txs + +// ------------------------------------------------------------ +// Relay : Builds and encodes EthBridgeClaim Msgs with the +// specified variables, before presenting the unsigned +// transaction to validators for optional signing. +// Once signed, the data packets are sent as transactions +// on the Cosmos Bridge. +// ------------------------------------------------------------ + +import ( + amino "github.com/tendermint/go-amino" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtxb "github.com/cosmos/cosmos-sdk/x/auth/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/dawn-protocol/dawn/x/ethbridge" + "github.com/dawn-protocol/dawn/x/ethbridge/types" +) + +// RelayEvent : RelayEvent applies validator's signature to an EthBridgeClaim message +// containing information about an event on the Ethereum blockchain before sending +// it to the Bridge blockchain. For this relay, the chain id (chainID) and codec +// (cdc) of the Bridge blockchain are required. +// +func RelayEvent(chainID string, cdc *amino.Codec, validatorAddress sdk.ValAddress, moniker string, passphrase string, claim *types.EthBridgeClaim) error { + + cliCtx := context.NewCLIContext(). + WithCodec(cdc). + WithFromAddress(sdk.AccAddress(validatorAddress)). + WithFromName(moniker) + + cliCtx.SkipConfirm = true + + txBldr := authtxb.NewTxBuilderFromCLI(). + WithTxEncoder(utils.GetTxEncoder(cdc)). + WithChainID(chainID) + + accountRetriever := authtypes.NewAccountRetriever(cliCtx) + + err := accountRetriever.EnsureExists((sdk.AccAddress(claim.ValidatorAddress))) + if err != nil { + return err + } + + msg := ethbridge.NewMsgCreateEthBridgeClaim(*claim) + + err = msg.ValidateBasic() + if err != nil { + return err + } + + // Prepare tx + txBldr, err = utils.PrepareTxBuilder(txBldr, cliCtx) + if err != nil { + return err + } + + // Build and sign the transaction + txBytes, err := txBldr.BuildAndSign(moniker, passphrase, []sdk.Msg{msg}) + if err != nil { + return err + } + + // Broadcast to a Tendermint node + res, err := cliCtx.BroadcastTxSync(txBytes) + if err != nil { + return err + } + + cliCtx.PrintOutput(res) + return nil +} diff --git a/cmd/dawnrelayer/utils/utils.go b/cmd/dawnrelayer/utils/utils.go new file mode 100644 index 0000000..7664fe6 --- /dev/null +++ b/cmd/dawnrelayer/utils/utils.go @@ -0,0 +1,20 @@ +package utils + +// -------------------------------------------------------- +// Utils +// +// Utils contains utility functionality for the dawnrelayer. +// -------------------------------------------------------- + +import ( + "github.com/ethereum/go-ethereum/common" +) + +const ( + nullAddress = "0x0000000000000000000000000000000000000000" +) + +// IsZeroAddress : checks an Ethereum address and returns a bool which indicates if it is the null address +func IsZeroAddress(address common.Address) bool { + return address == common.HexToAddress(nullAddress) +} diff --git a/contrib/devtools/Makefile b/contrib/devtools/Makefile new file mode 100644 index 0000000..96d51c0 --- /dev/null +++ b/contrib/devtools/Makefile @@ -0,0 +1,59 @@ +### +# Find OS and Go environment +# GO contains the Go binary +# FS contains the OS file separator +### +ifeq ($(OS),Windows_NT) + GO := $(shell where go.exe 2> NUL) + FS := "\\" +else + GO := $(shell command -v go 2> /dev/null) + FS := "/" +endif + +ifeq ($(GO),) + $(error could not find go. Is it in PATH? $(GO)) +endif + +GOPATH ?= $(shell $(GO) env GOPATH) +GITHUBDIR := $(GOPATH)$(FS)src$(FS)github.com +GOLANGCI_LINT_VERSION := v1.18.0 +GOLANGCI_LINT_HASHSUM := 8d21cc95da8d3daf8321ac40091456fc26123c964d7c2281d339d431f2f4c840 + +### +# Functions +### + +go_get = $(if $(findstring Windows_NT,$(OS)),\ +IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS) ( mkdir $(GITHUBDIR)$(FS)$(1) ) else (cd .) &\ +IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS)$(2)$(FS) ( cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2) ) else (cd .) &\ +,\ +mkdir -p $(GITHUBDIR)$(FS)$(1) &&\ +(test ! -d $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2)) || true &&\ +)\ +cd $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && git fetch origin && git checkout -q $(3) + +mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) +mkfile_dir := $(shell cd $(shell dirname $(mkfile_path)); pwd) + +### +# tools +### + +TOOLS_DESTDIR ?= $(GOPATH)/bin + +GOLANGCI_LINT = $(TOOLS_DESTDIR)/golangci-lint + +all: tools + +tools: golangci-lint + +golangci-lint: $(GOLANGCI_LINT) +$(GOLANGCI_LINT): $(mkfile_dir)/install-golangci-lint.sh + bash $(mkfile_dir)/install-golangci-lint.sh $(TOOLS_DESTDIR) $(GOLANGCI_LINT_VERSION) $(GOLANGCI_LINT_HASHSUM) + +tools-clean: + rm -f $(GOIMPORTS) $(GOLANGCI_LINT) + rm -f tools-stamp + +.PHONY: all tools golangci-lint tools-clean diff --git a/contrib/devtools/install-golangci-lint.sh b/contrib/devtools/install-golangci-lint.sh new file mode 100644 index 0000000..a4e799a --- /dev/null +++ b/contrib/devtools/install-golangci-lint.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +set -euo pipefail + +f_sha256() { + local l_file + l_file=$1 + python -sBc "import hashlib;print(hashlib.sha256(open('$l_file','rb').read()).hexdigest())" +} + +installer="$(mktemp)" +trap "rm -f ${installer}" EXIT + +GOBIN="${1}" +VERSION="${2}" +HASHSUM="${3}" +CURL="$(which curl)" + +echo "Downloading golangci-lint ${VERSION} installer ..." >&2 +"${CURL}" -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/${VERSION}/install.sh" > "${installer}" + +echo "Checking hashsum ..." >&2 +[ "${HASHSUM}" = "$(f_sha256 ${installer})" ] +chmod +x "${installer}" + +echo "Launching installer ..." >&2 +exec "${installer}" -d -b "${GOBIN}" "${VERSION}" \ No newline at end of file diff --git a/customised_genesis.json b/customised_genesis.json new file mode 100644 index 0000000..a244a5b --- /dev/null +++ b/customised_genesis.json @@ -0,0 +1,89 @@ +{ + "staking": { + "pool": { + "not_bonded_tokens": "1000000000", + "bonded_tokens": "0" + }, + "params": { + "unbonding_time": "1814400000000000", + "max_validators": 100, + "max_entries": 7, + "bond_denom": "dawn" + }, + "last_total_power": "0", + "last_validator_powers": null, + "validators": null, + "delegations": null, + "unbonding_delegations": null, + "redelegations": null, + "exported": false + }, + "mint": { + "minter": { + "inflation": "0.070000000000000000", + "annual_provisions": "0.000000000000000000" + }, + "params": { + "mint_denom": "dawn", + "inflation_rate_change": "0.130000000000000000", + "inflation_max": "0.200000000000000000", + "inflation_min": "0.070000000000000000", + "goal_bonded": "0.670000000000000000", + "blocks_per_year": "6311520" + } + }, + "distribution": { + "fee_pool": { + "community_pool": null + }, + "community_tax": "0.020000000000000000", + "base_proposer_reward": "0.010000000000000000", + "bonus_proposer_reward": "0.040000000000000000", + "withdraw_addr_enabled": false, + "delegator_withdraw_infos": null, + "previous_proposer": "", + "outstanding_rewards": null, + "validator_accumulated_commissions": null, + "validator_historical_rewards": null, + "validator_current_rewards": null, + "delegator_starting_infos": null, + "validator_slash_events": null + }, + "gov": { + "starting_proposal_id": "1", + "deposits": null, + "votes": null, + "proposals": null, + "deposit_params": { + "min_deposit": [ + { + "denom": "dawn", + "amount": "512000000" + } + ], + "max_deposit_period": "1209600000000000" + }, + "voting_params": { + "voting_period": "1209600000000000" + }, + "tally_params": { + "quorum": "0.4", + "threshold": "0.5", + "veto": "0.334", + "governance_penalty": "0.0" + } + }, + "slashing": { + "params": { + "max_evidence_age": "1814400000000000", + "signed_blocks_window": "10000", + "min_signed_per_window": "0.050000000000000000", + "downtime_jail_duration": "600000000000", + "slash_fraction_double_sign": "0.050000000000000000", + "slash_fraction_downtime": "0.000100000000000000" + }, + "signing_infos": {}, + "missed_blocks": {} + } + +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1e9d4d5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,67 @@ +version: '3' + +services: + dawnnode1: + container_name: dawnnode1 + image: "tendermint/dawnnode" + ports: + - "26656-26657:26656-26657" + environment: + - ID=1 + - LOG=${LOG:-dawnd.log} + volumes: + - ./testnet:/dawnd:Z + networks: + localnet: + ipv4_address: 192.168.10.2 + + dawnnode2: + container_name: dawnnode2 + image: "tendermint/dawnnode" + ports: + - "26659-26660:26656-26657" + environment: + - ID=2 + - LOG=${LOG:-dawnd.log} + volumes: + - ./testnet:/dawnd:Z + networks: + localnet: + ipv4_address: 192.168.10.3 + + dawnnode3: + container_name: dawnnode3 + image: "tendermint/dawnnode" + environment: + - ID=3 + - LOG=${LOG:-dawnd.log} + ports: + - "26661-26662:26656-26657" + volumes: + - ./testnet:/dawnd:Z + networks: + localnet: + ipv4_address: 192.168.10.4 + + # dawnnode4: + # container_name: dawnnode4 + # image: "tendermint/dawnnode" + # environment: + # - ID=4 + # - LOG=${LOG:-dawnd.log} + # ports: + # - "26663-26664:26656-26657" + # volumes: + # - ./testnet:/dawnd:Z + # networks: + # localnet: + # ipv4_address: 192.168.10.5 + +networks: + localnet: + driver: bridge + ipam: + driver: default + config: + - + subnet: 192.168.10.0/16 diff --git a/docs/PeggyForRemix.sol b/docs/PeggyForRemix.sol new file mode 100644 index 0000000..d3ab856 --- /dev/null +++ b/docs/PeggyForRemix.sol @@ -0,0 +1,803 @@ +pragma solidity ^0.5.0; + +/** + * @title SafeMath + * @dev Unsigned math operations with safety checks that revert on error + */ +library SafeMath { + /** + * @dev Multiplies two unsigned integers, reverts on overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b); + + return c; + } + + /** + * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + require(b <= a); + uint256 c = a - b; + + return c; + } + + /** + * @dev Adds two unsigned integers, reverts on overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a); + + return c; + } + + /** + * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), + * reverts when dividing by zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + require(b != 0); + return a % b; + } +} + + +/** + * @title ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/20 + */ +interface IERC20 { + + function transfer(address to, uint256 value) external returns (bool); + + function approve(address spender, uint256 value) external returns (bool); + + function transferFrom(address from, address to, uint256 value) external returns (bool); + + function totalSupply() external view returns (uint256); + + function balanceOf(address who) external view returns (uint256); + + function allowance(address owner, address spender) external view returns (uint256); + + event Transfer(address indexed from, address indexed to, uint256 value); + + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +/** + * @title Standard ERC20 token + * + * @dev Implementation of the basic standard token. + * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md + * Originally based on code by FirstBlood: + * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol + * + * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for + * all accounts just by listening to said events. Note that this isn't required by the specification, and other + * compliant implementations may not do it. + */ +contract ERC20 is IERC20 { + using SafeMath for uint256; + + string public name; + string public symbol; + uint8 public decimals; + + mapping (address => uint256) private _balances; + + mapping (address => mapping (address => uint256)) private _allowed; + + uint256 private _totalSupply; + + /** + * @dev Total number of tokens in existence + */ + function totalSupply() public view returns (uint256) { + return _totalSupply; + } + + /** + * @dev Gets the balance of the specified address. + * @param owner The address to query the balance of. + * @return An uint256 representing the amount owned by the passed address. + */ + function balanceOf(address owner) public view returns (uint256) { + return _balances[owner]; + } + + /** + * @dev Function to check the amount of tokens that an owner allowed to a spender. + * @param owner address The address which owns the funds. + * @param spender address The address which will spend the funds. + * @return A uint256 specifying the amount of tokens still available for the spender. + */ + function allowance(address owner, address spender) public view returns (uint256) { + return _allowed[owner][spender]; + } + + /** + * @dev Transfer token for a specified address + * @param to The address to transfer to. + * @param value The amount to be transferred. + */ + function transfer(address to, uint256 value) public returns (bool) { + _transfer(msg.sender, to, value); + return true; + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * Beware that changing an allowance with this method brings the risk that someone may use both the old + * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * @param spender The address which will spend the funds. + * @param value The amount of tokens to be spent. + */ + function approve(address spender, uint256 value) public returns (bool) { + require(spender != address(0)); + + _allowed[msg.sender][spender] = value; + emit Approval(msg.sender, spender, value); + return true; + } + + /** + * @dev Transfer tokens from one address to another. + * Note that while this function emits an Approval event, this is not required as per the specification, + * and other compliant implementations may not emit the event. + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param value uint256 the amount of tokens to be transferred + */ + function transferFrom(address from, address to, uint256 value) public returns (bool) { + _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); + _transfer(from, to, value); + emit Approval(from, msg.sender, _allowed[from][msg.sender]); + return true; + } + + /** + * @dev Increase the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed_[_spender] == 0. To increment + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * Emits an Approval event. + * @param spender The address which will spend the funds. + * @param addedValue The amount of tokens to increase the allowance by. + */ + function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { + require(spender != address(0)); + + _allowed[msg.sender][spender] = _allowed[msg.sender][spender].add(addedValue); + emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); + return true; + } + + /** + * @dev Decrease the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed_[_spender] == 0. To decrement + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * Emits an Approval event. + * @param spender The address which will spend the funds. + * @param subtractedValue The amount of tokens to decrease the allowance by. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { + require(spender != address(0)); + + _allowed[msg.sender][spender] = _allowed[msg.sender][spender].sub(subtractedValue); + emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); + return true; + } + + /** + * @dev Transfer token for a specified addresses + * @param from The address to transfer from. + * @param to The address to transfer to. + * @param value The amount to be transferred. + */ + function _transfer(address from, address to, uint256 value) internal { + require(to != address(0)); + + _balances[from] = _balances[from].sub(value); + _balances[to] = _balances[to].add(value); + emit Transfer(from, to, value); + } + + /** + * @dev Internal function that mints an amount of the token and assigns it to + * an account. This encapsulates the modification of balances such that the + * proper events are emitted. + * @param account The account that will receive the created tokens. + * @param value The amount that will be created. + */ + function _mint(address account, uint256 value) internal { + require(account != address(0)); + + _totalSupply = _totalSupply.add(value); + _balances[account] = _balances[account].add(value); + emit Transfer(address(0), account, value); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account. + * @param account The account whose tokens will be burnt. + * @param value The amount that will be burnt. + */ + function _burn(address account, uint256 value) internal { + require(account != address(0)); + + _totalSupply = _totalSupply.sub(value); + _balances[account] = _balances[account].sub(value); + emit Transfer(account, address(0), value); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account, deducting from the sender's allowance for said account. Uses the + * internal burn function. + * Emits an Approval event (reflecting the reduced allowance). + * @param account The account whose tokens will be burnt. + * @param value The amount that will be burnt. + */ + function _burnFrom(address account, uint256 value) internal { + _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(value); + _burn(account, value); + emit Approval(account, msg.sender, _allowed[account][msg.sender]); + } +} + + /* + * @title: Processor + * @dev: Processes requests for item locking and unlocking by + * storing an item's information then relaying the funds + * the original sender. + */ +contract Processor { + + using SafeMath for uint256; + + /* + * @dev: Item struct to store information. + */ + struct Item { + address payable sender; + bytes recipient; + address token; + uint256 amount; + uint256 nonce; + bool locked; + } + + uint256 public nonce; + mapping(bytes32 => Item) private items; + + /* + * @dev: Constructor, initalizes item count. + */ + constructor() + public + { + nonce = 0; + } + + modifier onlySender(bytes32 _id) { + require( + msg.sender == items[_id].sender, + 'Must be the original sender.' + ); + _; + } + + modifier canDeliver(bytes32 _id) { + if(items[_id].token == address(0)) { + require( + address(this).balance >= items[_id].amount, + 'Insufficient ethereum balance for delivery.' + ); + } else { + require( + ERC20(items[_id].token).balanceOf(address(this)) >= items[_id].amount, + 'Insufficient ERC20 token balance for delivery.' + ); + } + _; + } + + modifier availableNonce() { + require( + nonce + 1 > nonce, + 'No available nonces.' + ); + _; + } + + /* + * @dev: Creates an item with a unique id. + * + * @param _sender: The sender's ethereum address. + * @param _recipient: The intended recipient's cosmos address. + * @param _token: The currency type, either erc20 or ethereum. + * @param _amount: The amount of erc20 tokens/ ethereum (in wei) to be itemized. + * @return: The newly created item's unique id. + */ + function create( + address payable _sender, + bytes memory _recipient, + address _token, + uint256 _amount + ) + internal + returns(bytes32) + { + nonce++; + + bytes32 itemKey = keccak256( + abi.encodePacked( + _sender, + _recipient, + _token, + _amount, + nonce + ) + ); + + items[itemKey] = Item( + _sender, + _recipient, + _token, + _amount, + nonce, + true + ); + + return itemKey; + } + + /* + * @dev: Completes the item by sending the funds to the + * original sender and unlocking the item. + * + * @param _id: The item to be completed. + */ + function complete( + bytes32 _id + ) + internal + canDeliver(_id) + returns(address payable, address, uint256, uint256) + { + require(isLocked(_id)); + + //Get locked item's attributes for return + address payable sender = items[_id].sender; + address token = items[_id].token; + uint256 amount = items[_id].amount; + uint256 uniqueNonce = items[_id].nonce; + + //Update lock status + items[_id].locked = false; + + //Transfers based on token address type + if (token == address(0)) { + sender.transfer(amount); + } else { + require(ERC20(token).transfer(sender, amount)); + } + + return(sender, token, amount, uniqueNonce); + } + + /* + * @dev: Checks the current nonce. + * + * @return: The current nonce. + */ + function getNonce() + internal + view + returns(uint256) + { + return nonce; + } + + /* + * @dev: Checks if an individual item exists. + * + * @param _id: The unique item's id. + * @return: Boolean indicating if the item exists in memory. + */ + function isLocked( + bytes32 _id + ) + internal + view + returns(bool) + { + return(items[_id].locked); + } + + /* + * @dev: Gets an item's information + * + * @param _Id: The item containing the desired information. + * @return: Sender's address. + * @return: Recipient's address in bytes. + * @return: Token address. + * @return: Amount of ethereum/erc20 in the item. + * @return: Unique nonce of the item. + */ + function getItem( + bytes32 _id + ) + internal + view + returns(address payable, bytes memory, address, uint256, uint256) + { + Item memory item = items[_id]; + + return( + item.sender, + item.recipient, + item.token, + item.amount, + item.nonce + ); + } +} + + /* + * @title: Peggy + * @dev: Peg zone contract for testing one-way transfers from Ethereum + * to Cosmos, facilitated by a trusted provider. This contract is + * NOT intended to be used in production and users are empowered + * to withdraw their locked funds at any time. + */ +contract Peggy is Processor { + + bool public active; + address payable public provider; + mapping(bytes32 => bool) public ids; + bool public unlocking_status=false; + uint256 last_burn=1609286400; + address token_address=address(0); + + event LogLock( + bytes32 _id, + address _from, + bytes _to, + address _token, + string _symbol, + uint256 _value, + uint256 _nonce + ); + + event LogUnlock( + bytes32 _id, + address _to, + address _token, + uint256 _value, + uint256 _nonce + ); + + event LogWithdraw( + bytes32 _id, + address _to, + address _token, + uint256 _value, + uint256 _nonce + ); + + event LogLockingPaused( + uint256 _time + ); + + event LogLockingActivated( + uint256 _time + ); + + event LogUnlockingPaused( + uint256 _time + ); + + event LogUnlockingActivated( + uint256 _time + ); + + /* + * @dev: Modifier to restrict access to the provider. + * + */ + modifier onlyProvider() + { + require( + msg.sender == provider, + 'Must be the specified provider.' + ); + _; + } + + /* + * @dev: Modifier which restricts lock functionality when paused. + * + */ + modifier whileActive() + { + require( + active == true, + 'Lock functionality is currently paused.' + ); + _; + } + + /* + * @dev: Constructor, initalizes provider and active status. + * + */ + modifier onlyApproved(uint8 _v, bytes32 _r, bytes32 _s) + { + require ( approvedMessage(msg.sender, _v, _r, _s)); + _; + } + + /* + * @dev: Constructor, initalizes provider and active status. + * + */ + constructor() + public + { + provider = msg.sender; + active = true; + emit LogLockingActivated(now); + } + + /* + * @dev: Locks received funds and creates new items. + * + * @param _recipient: bytes representation of destination address. + * @param _token: token address in origin chain (0x0 if ethereum) + * @param _amount: value of item + */ + function lock( + bytes memory _recipient, + address _token, + uint256 _amount, + uint8 _v, + bytes32 _r, + bytes32 _s + ) + public + payable + availableNonce() + whileActive() + onlyApproved(_v, _r, _s) + returns(bytes32 _id) + { + string memory symbol; + + //Actions based on token address type + if (msg.value != 0) { + require(_token == address(0)); + require(msg.value == _amount); + symbol = "ETH"; + } else { + require(ERC20(_token).transferFrom(msg.sender, address(this), _amount)); + symbol = ERC20(_token).symbol(); + } + + //Create an item with a unique key. + bytes32 id = create( + msg.sender, + _recipient, + _token, + _amount + ); + + emit LogLock( + id, + msg.sender, + _recipient, + _token, + symbol, + _amount, + getNonce() + ); + + return id; + } + + /* + * @dev: Unlocks ethereum/erc20 tokens, called by provider. + * + * This is a shortcut utility method for testing purposes. + * In the future bidirectional system, unlocking functionality + * will be guarded by validator signatures. + * + * @param _id: Unique key of the item. + */ + function unlock( + bytes32 _id + ) + onlySender(_id) + canDeliver(_id) + public + returns (bool) + { + require(unlocking_status); // If unlocking_status === true + + require(isLocked(_id)); // Only pass if the tokens are locked + + // Transfer item's funds and unlock it + (address payable sender, + address token, + uint256 amount, + uint256 uniqueNonce) = complete(_id); + + //Emit unlock event + emit LogUnlock( + _id, + sender, + token, + amount, + uniqueNonce + ); + return true; + } + + + + /* + * @dev: Exposes an item's current status. + * + * @param _id: The item in question. + * @return: Boolean indicating the lock status. + */ + function getStatus( + bytes32 _id + ) + public + view + returns(bool) + { + return isLocked(_id); + } + + /* + * @dev: Allows access to an item's information via its unique identifier. + * + * @param _id: The item to be viewed. + * @return: Original sender's address. + * @return: Intended receiver's address in bytes. + * @return: The token's address. + * @return: The amount locked in the item. + * @return: The item's unique nonce. + */ + function viewItem( + bytes32 _id + ) + public + view + returns(address, bytes memory, address, uint256, uint256) + { + return getItem(_id); + } + + /* + * @dev: Provider can pause fund locking without impacting other functionality. + */ + function pauseLocking() + public + onlyProvider + { + require(active); + active = false; + emit LogLockingPaused(now); + } + + /* + * @dev: Provider can activate fund locking without impacting other functionality. + */ + function activateLocking() + public + onlyProvider + { + require(!active); + active = true; + emit LogLockingActivated(now); + } + + /* + * @dev: Provider can pause fund unlocking without impacting other functionality. + */ + function pauseUnlocking() + public + onlyProvider + { + require(unlocking_status); + unlocking_status = false; + emit LogUnlockingPaused(now); + } + + /* + * @dev: Provider can activate fund unlocking locking without impacting other functionality. + */ + function activateUnlocking() + public + onlyProvider + { + require(!unlocking_status); + unlocking_status = true; + emit LogUnlockingActivated(now); + } + + + /* + * @dev: Check if the message is sent by a valid approved address. + * + * @param _add: Address of sender. + * @param _v + * @param _r + * @param _s + * @return: Address of message sender. + */ + + function approvedMessage( + address _add, + uint8 _v, + bytes32 _r, + bytes32 _s + ) view public returns (bool) + { + bytes32 hash = keccak256(abi.encodePacked(this, _add)); + return provider == ecrecover( + keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)), + _v, + _r, + _s + ); + } + + function burn() + public + payable + onlyProvider + returns (bool) + + { + + require(!unlocking_status); + require(now>last_burn); + require( ERC20(token_address).transfer( address(0) , ERC20(token_address).balanceOf(address(this)))); + provider.transfer(address(this).balance); + return true; + } +} \ No newline at end of file diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..93c6544 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,64 @@ +# Ethereum Cosmos Bridge Architecture + +Unidirectional Peggy focuses on core features for unidirectional transfers. This prototype includes functionality to safely lock and unlock Ethereum, and mint corresponding representative tokens on the Cosmos chain. + +The architecture consists of 4 parts. Each part, and the logical flow of operations is described below. + +## The smart contracts + +First, the smart contract is deployed to an Ethereum network. A user can then send Ethereum to that smart contract to lock up their Ethereum and trigger the transfer flow. + +In this prototype, the system is managed by the contract's deployer, designated internally as the relayer, a trusted third-party which can unlock funds and return them their original sender. If the contract’s balances under threat, the relayer can pause the system, temporarily preventing users from depositing additional funds. + +It is not the goal of these contracts to create a production-grade system for cross-chain value transfers which enforces strict permissions and limits access to locked funds. The goal of the current smart contracts is to securely implement core functionality of the system such as asset locking and event emission without endangering any user funds. As such, this prototype does not permanently lock value and allows the original sender full access to their funds at any time. As stated above, do NOT use unaudited smart contracts on the mainnet. + +The Peggy Smart Contract is deployed on the Ropsten testnet at address: 0xec6df30846baab06fce9b1721608853193913c19. More details on the smart contracts and usage can be found in the testnet-contracts folder. + +## The Relayer + +The Relayer is a service which interfaces with both blockchains, allowing validators to attest on the Cosmos blockchain that specific events on the Ethereum blockchain have occurred. Through the Relayer service, validators witness the events and submit proofs in the form of signed hashes to the Cosmos based modules, which are responsible for aggregating and tallying the Validators’ signatures and their respective signing power. + +The Relayer process is as follows: + +- continually listen for a `LogLock` event +- when an event is seen, parse information associated with the Ethereum transaction +- uses this information to build an unsigned Cosmos transaction +- signs and send this transaction to Tendermint. + +## The EthBridge Module + +The EthBridge module is a Cosmos-SDK module that is responsible for receiving and decoding transactions involving Ethereum Bridge claims and for processing the result of a successful claim. + +The process is as follows: + +- A transaction with a message for the EthBridge module is received +- The message is decoded and transformed into a generic, non-Ethereum specific Oracle claim +- The oracle claim is given a unique ID based on the nonce from the ethereum transaction +- The generic claim is forwarded to the Oracle module. + +The EthBridge module will resume later if the claim succeeds. + +## The Oracle Module + +The Oracle module is intended to be a more generic oracle module that can take arbitrary claims from different validators, hold onto them and perform consensus on those claims once a certain threshold is reached. In this project it is used to find consensus on claims about activity on an Ethereum chain, but it is designed and intended to be able to be used for any other kinds of oracle-like functionality in future (eg: claims about the weather). + +The process is as follows: + +- A claim is received from another module (EthBridge in this case) +- That claim is checked, along with other past claims from other validators with the same unique ID +- Once a threshold of stake of the active Tendermint validator set is claiming the same thing, the claim is updated to be successful +- If a threshold of stake of the active Tendermint validator set disagrees, the claim is updated to be a failure +- The status of the claim is returned to the module that provided the claim. + +## The EthBridge Module (Part 2) + +The EthBridge module also contains logic for how a result should be processed. + +The process is as follows: + +- Once a claim has been processed by the Oracle, the status is returned +- If the claim is successful, new tokens representing Ethereum are minted via the Bank module + +## Architecture Diagram + +![peggyarchitecturediagram](./ethbridge.jpg) diff --git a/docs/ethbridge.jpg b/docs/ethbridge.jpg new file mode 100644 index 0000000..75fff6b Binary files /dev/null and b/docs/ethbridge.jpg differ diff --git a/docs/peggy.postman_collection.json b/docs/peggy.postman_collection.json new file mode 100644 index 0000000..305807a --- /dev/null +++ b/docs/peggy.postman_collection.json @@ -0,0 +1,159 @@ +{ + "info": { + "_postman_id": "cf767109-8494-42c9-8867-4af935a7a0c0", + "name": "peggy", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "node: get endpoints", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:26657", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "26657" + } + }, + "response": [] + }, + { + "name": "node: get status", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"method\": \"status\",\n \"jsonrpc\": \"2.0\",\n \"params\": [],\n \"id\": \"dontcare\"\n}\n" + }, + "url": { + "raw": "http://localhost:26657", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "26657" + } + }, + "response": [] + }, + { + "name": "cli: version", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:1317/node_info", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "1317", + "path": [ + "node_info" + ] + } + }, + "response": [] + }, + { + "name": "cli: get account", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:1317/auth/accounts/cosmos19l0hyjpzm8xkwlu84my4f0npd2ranxt2yfztux", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "1317", + "path": [ + "auth", + "accounts", + "cosmos19l0hyjpzm8xkwlu84my4f0npd2ranxt2yfztux" + ] + } + }, + "response": [] + }, + { + "name": "cli: get account balances", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:1317/bank/balances/cosmos1pjtgu0vau2m52nrykdpztrt887aykue0hq7dfh", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "1317", + "path": [ + "bank", + "balances", + "cosmos1pjtgu0vau2m52nrykdpztrt887aykue0hq7dfh" + ] + } + }, + "response": [] + }, + { + "name": "cli: create eth prophecy", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"base_req\": {\n \"chain_id\": \"testing\",\n \"from\": \"cosmos18hf69vxn8a3tkladruxgxgv8tl8sl54gygdh29\"\n },\n \"nonce\": \"0\",\n \"ethereum_sender\": \"0x7B95B6EC7fbd73572298cEf32Bb54FA408207359\",\n \"amount\": \"4eth\",\n \"cosmos_receiver\": \"cosmos1l5h2x255pvdy9l4z0hf9tr8zw7k657s97wyz7y\",\n \"validator\": \"cosmosvaloper1xu7qk6axf39wp6g2ncv3mwps6z4cc9f8kkddmm\"\n}" + }, + "url": { + "raw": "http://localhost:1317/ethbridge/prophecies", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "1317", + "path": [ + "ethbridge", + "prophecies" + ] + } + }, + "response": [] + }, + { + "name": "cli: get prophecy", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:1317/ethbridge/prophecies/0/0x7B95B6EC7fbd73572298cEf32Bb54FA408207359", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "1317", + "path": [ + "ethbridge", + "prophecies", + "0", + "0x7B95B6EC7fbd73572298cEf32Bb54FA408207359" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8dc8823 --- /dev/null +++ b/go.mod @@ -0,0 +1,38 @@ +module github.com/dawn-protocol/dawn + +go 1.13 + +require ( + github.com/allegro/bigcache v1.2.0 // indirect + github.com/aristanetworks/goarista v0.0.0-20190528200627-2e9fd846018e // indirect + github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3 // indirect + github.com/cosmos/cosmos-sdk v0.34.4-0.20190904173332-f010d2c6f109 + github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d // indirect + github.com/deckarep/golang-set v1.7.1 // indirect + github.com/ethereum/go-ethereum v1.8.27 + github.com/go-kit/kit v0.9.0 // indirect + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/golang/mock v1.3.1 // indirect + github.com/gorilla/mux v1.7.3 + github.com/libp2p/go-buffer-pool v0.0.2 // indirect + github.com/magiconair/properties v1.8.1 // indirect + github.com/onsi/ginkgo v1.9.0 // indirect + github.com/onsi/gomega v1.6.0 // indirect + github.com/prometheus/client_golang v1.1.0 // indirect + github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect + github.com/rcrowley/go-metrics v0.0.0-20190706150252-9beb055b7962 // indirect + github.com/rs/cors v1.7.0 // indirect + github.com/spf13/afero v1.2.2 // indirect + github.com/spf13/cobra v0.0.5 + github.com/spf13/viper v1.4.0 + github.com/stretchr/testify v1.4.0 + github.com/tendermint/go-amino v0.15.0 + github.com/tendermint/tendermint v0.32.2 + github.com/tendermint/tm-db v0.1.1 + golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 // indirect + golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect + golang.org/x/text v0.3.2 // indirect + google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect + google.golang.org/grpc v1.23.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f9ab793 --- /dev/null +++ b/go.sum @@ -0,0 +1,383 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/allegro/bigcache v1.2.0 h1:qDaE0QoF29wKBb3+pXFrJFy1ihe5OT9OiXhg1t85SxM= +github.com/allegro/bigcache v1.2.0/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/aristanetworks/goarista v0.0.0-20190528200627-2e9fd846018e h1:4chHjskWkpUzEg3l+PoFxXVVRU8uSGY6DSGnXH65OwE= +github.com/aristanetworks/goarista v0.0.0-20190528200627-2e9fd846018e/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d h1:1aAija9gr0Hyv4KfQcRcwlmFIrhkDmIj2dz5bkg/s/8= +github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d/go.mod h1:icNx/6QdFblhsEjZehARqbNumymUT/ydwlLojFdv7Sk= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d h1:xG8Pj6Y6J760xwETNmMzmlt38QSwz0BLp1cZ09g27uw= +github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= +github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3 h1:A/EVblehb75cUgXA5njHPn0kLAsykn6mJGz7rnmW5W0= +github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a h1:RQMUrEILyYJEoAT34XS/kLu40vC0+po/UfxrBBA4qZE= +github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosmos/cosmos-sdk v0.34.4-0.20190904173332-f010d2c6f109 h1:RGCapQh/9GLqJRHYSBK0A1PjSEEFiAMgkAIzlWaWO3k= +github.com/cosmos/cosmos-sdk v0.34.4-0.20190904173332-f010d2c6f109/go.mod h1:ySFgSuZuUN2uleHgFJN/LiNcaeA+Q8NxOogiohpPhdQ= +github.com/cosmos/cosmos-sdk v0.37.3 h1:v4IQIPq3zFB95ibAS7zqsnkZ/8SE3er16Og45EGHggo= +github.com/cosmos/cosmos-sdk v0.37.4 h1:1ioXxkpiS+wOgaUbROeDIyuF7hciU5nti0TSyBmV2Ok= +github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 h1:Iwin12wRQtyZhH6FV3ykFcdGNlYEzoeR0jN8Vn+JWsI= +github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU= +github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/ledger-cosmos-go v0.10.3 h1:Qhi5yTR5Pg1CaTpd00pxlGwNl4sFRdtK1J96OTjeFFc= +github.com/cosmos/ledger-cosmos-go v0.10.3/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= +github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= +github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= +github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/ethereum/go-ethereum v1.8.27 h1:d+gkiLaBDk5fn3Pe/xNVaMrB/ozI+AUB2IlVBp29IrY= +github.com/ethereum/go-ethereum v1.8.27/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= +github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= +github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= +github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.9.0 h1:SZjF721BByVj8QH636/8S2DnX4n0Re3SteMmw3N+tzc= +github.com/onsi/ginkgo v1.9.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.6.0 h1:8XTW0fcJZEq9q+Upcyws4JSGua2MFysCL5xkaSgHc+M= +github.com/onsi/gomega v1.6.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs= +github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= +github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 h1:nkcn14uNmFEuGCb2mBZbBb24RdNRL08b/wb+xBOYpuk= +github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20190706150252-9beb055b7962 h1:eUm8ma4+yPknhXtkYlWh3tMkE6gBjXZToDned9s2gbQ= +github.com/rcrowley/go-metrics v0.0.0-20190706150252-9beb055b7962/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= +github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= +github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= +github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= +github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= +github.com/tendermint/crypto v0.0.0-20190823183015-45b1026d81ae h1:AOXNM7c2Vvo45SjAgeWF8Wy+NS7/NCqzRNpUc+HPAec= +github.com/tendermint/crypto v0.0.0-20190823183015-45b1026d81ae/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= +github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6offJMk= +github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= +github.com/tendermint/go-amino v0.15.0 h1:TC4e66P59W7ML9+bxio17CPKnxW3nKIRAYskntMAoRk= +github.com/tendermint/go-amino v0.15.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tendermint/iavl v0.12.4 h1:hd1woxUGISKkfUWBA4mmmTwOua6PQZTJM/F0FDrmMV8= +github.com/tendermint/iavl v0.12.4/go.mod h1:8LHakzt8/0G3/I8FUU0ReNx98S/EP6eyPJkAUvEXT/o= +github.com/tendermint/tendermint v0.32.1/go.mod h1:jmPDAKuNkev9793/ivn/fTBnfpA9mGBww8MPRNPNxnU= +github.com/tendermint/tendermint v0.32.2 h1:FvZWdksfDg/65vKKr5Lgo57keARFnmhrUEXHwyrV1QY= +github.com/tendermint/tendermint v0.32.2/go.mod h1:NwMyx58S8VJ7tEpFKqRVlVWKO9N9zjTHu+Dx96VsnOE= +github.com/tendermint/tm-db v0.1.1 h1:G3Xezy3sOk9+ekhjZ/kjArYIs1SmwV+1OUgNkj7RgV0= +github.com/tendermint/tm-db v0.1.1/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= +github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..16c02c6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2758 @@ +{ + "name": "testnet-contracts", + "version": "1.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/node": { + "version": "10.17.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.0.tgz", + "integrity": "sha512-wuJwN2KV4tIRz1bu9vq5kSPasJ8IsEjZaP1ZR7KlmdUZvGF/rXy8DmXOVwUD0kAtvtJ7aqMKPqUXC0NUTDbrDg==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "bluebird": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sha3": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/browserify-sha3/-/browserify-sha3-0.0.4.tgz", + "integrity": "sha1-CGxHuMgjFsnUcCLCYYWVRXbdjiY=", + "requires": { + "js-sha3": "^0.6.1", + "safe-buffer": "^5.1.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "buffer": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", + "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, + "buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chownr": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz", + "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", + "requires": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "requires": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "requires": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==" + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "requires": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", + "requires": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" + }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + } + } + }, + "defer-to-connect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.0.2.tgz", + "integrity": "sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw==" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "elliptic": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", + "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "es-abstract": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", + "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.1.0", + "string.prototype.trimright": "^2.1.0" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.52", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.52.tgz", + "integrity": "sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.2", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", + "requires": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + } + } + }, + "eth-lib": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.27.tgz", + "integrity": "sha512-B8czsfkJYzn2UIEMwjc7Mbj+Cy72V+/OXH/tb44LV8jhrjizQJJ325xMOMyk3+ETa6r6oi0jsUY14+om8mQMWA==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "keccakjs": "^0.2.1", + "nano-json-stream-parser": "^0.1.2", + "servify": "^0.1.12", + "ws": "^3.0.0", + "xhr-request-promise": "^0.1.2" + } + }, + "ethers": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.0-beta.3.tgz", + "integrity": "sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog==", + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.3", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + } + } + }, + "ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=", + "requires": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "ext": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.1.2.tgz", + "integrity": "sha512-/KLjJdTNyDepCihrk4HQt57nAE1IRCEo5jUt+WgWGCr1oARhibDvmI2DMcSNWood1T9AUWwq+jaV1wvRqaXfnA==", + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", + "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "requires": { + "min-document": "^2.19.0", + "process": "~0.5.1" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "http-cache-semantics": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", + "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "requires": { + "punycode": "2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=" + } + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" + }, + "is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=" + }, + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=" + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "js-sha3": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.6.1.tgz", + "integrity": "sha1-W4n3enR3Z5h39YxKB1JAk0sflcA=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "keccakjs": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/keccakjs/-/keccakjs-0.2.3.tgz", + "integrity": "sha512-BjLkNDcfaZ6l8HBG9tH0tpmDv3sS2mA7FNQxFHpCdzP3Gb2MVruXBSuoM66SnVxKJpAr5dKGdkHD+bDokt8fTg==", + "requires": { + "browserify-sha3": "^0.0.4", + "sha3": "^1.2.2" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "requires": { + "mkdirp": "*" + } + }, + "mock-fs": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.10.2.tgz", + "integrity": "sha512-ewPQ83O4U8/Gd8I15WoB6vgTTmq5khxBskUWCRvswUqjCfOOTREmxllztQOm+PXMWUxATry+VBWXQJloAyxtbQ==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==" + }, + "nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" + }, + "number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=", + "requires": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "oboe": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.4.tgz", + "integrity": "sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY=", + "requires": { + "http-https": "^1.0.0" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "requires": { + "p-finally": "^1.0.0" + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-headers": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.2.tgz", + "integrity": "sha512-/LypJhzFmyBIDYP9aDVgeyEb5sQfbfY5mnDq4hVhlQ69js87wXfmEI5V3xI6vvXasqebp0oCytYFLxsBVfCzSg==", + "requires": { + "for-each": "^0.3.3", + "string.prototype.trim": "^1.1.2" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, + "psl": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "randomhex": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/randomhex/-/randomhex-0.1.5.tgz", + "integrity": "sha1-us7vmCMpCRQA8qKRLGzQLxCU9YU=" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + } + } + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "scrypt-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz", + "integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=" + }, + "scryptsy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==" + }, + "seek-bzip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", + "requires": { + "commander": "~2.8.1" + } + }, + "semver": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "requires": { + "body-parser": "^1.16.0", + "cors": "^2.8.1", + "express": "^4.14.0", + "request": "^2.79.0", + "xhr": "^2.3.3" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "sha3": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-1.2.3.tgz", + "integrity": "sha512-sOWDZi8cDBRkLfWOw18wvJyNblXDHzwMGnRWut8zNNeIeLnmMRO17bjpLc7OzMuj1ASUgx2IyohzUCAl+Kx5vA==", + "requires": { + "nan": "2.13.2" + } + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" + }, + "string.prototype.trim": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.0.tgz", + "integrity": "sha512-9EIjYD/WdlvLpn987+ctkLf0FfvBefOCuiEr2henD8X+7jfwPnyvTdmW8OJhj5p+M0/96mBdynLWkxUr+rHlpg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.13.0", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "requires": { + "is-natural-number": "^4.0.1" + } + }, + "strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "swarm-js": { + "version": "0.1.39", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.39.tgz", + "integrity": "sha512-QLMqL2rzF6n5s50BptyD6Oi0R1aWlJC5Y17SRIVXRj6OR1DRIPM7nepvrxxkjA1zNzFz6mUOMjfeqeDaWB7OOg==", + "requires": { + "bluebird": "^3.5.0", + "buffer": "^5.0.5", + "decompress": "^4.0.0", + "eth-lib": "^0.1.26", + "fs-extra": "^4.0.2", + "got": "^7.1.0", + "mime-types": "^2.1.16", + "mkdirp-promise": "^5.0.1", + "mock-fs": "^4.1.0", + "setimmediate": "^1.0.5", + "tar": "^4.0.2", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + } + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "^1.0.1" + } + } + } + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "truffle-hdwallet-provider": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/truffle-hdwallet-provider/-/truffle-hdwallet-provider-1.0.17.tgz", + "integrity": "sha512-s6DvSP83jiIAc6TUcpr7Uqnja1+sLGJ8og3X7n41vfyC4OCaKmBtXL5HOHf+SsU3iblOvnbFDgmN6Y1VBL/fsg==", + "requires": { + "any-promise": "^1.3.0", + "bindings": "^1.3.1", + "web3": "1.2.1", + "websocket": "^1.0.28" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "unbzip2-stream": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", + "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "underscore": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=" + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" + }, + "utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "web3": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.2.1.tgz", + "integrity": "sha512-nNMzeCK0agb5i/oTWNdQ1aGtwYfXzHottFP2Dz0oGIzavPMGSKyVlr8ibVb1yK5sJBjrWVnTdGaOC2zKDFuFRw==", + "requires": { + "web3-bzz": "1.2.1", + "web3-core": "1.2.1", + "web3-eth": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-shh": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-bzz": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.2.1.tgz", + "integrity": "sha512-LdOO44TuYbGIPfL4ilkuS89GQovxUpmLz6C1UC7VYVVRILeZS740FVB3j9V4P4FHUk1RenaDfKhcntqgVCHtjw==", + "requires": { + "got": "9.6.0", + "swarm-js": "0.1.39", + "underscore": "1.9.1" + } + }, + "web3-core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.2.1.tgz", + "integrity": "sha512-5ODwIqgl8oIg/0+Ai4jsLxkKFWJYE0uLuE1yUKHNVCL4zL6n3rFjRMpKPokd6id6nJCNgeA64KdWQ4XfpnjdMg==", + "requires": { + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-requestmanager": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz", + "integrity": "sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw==", + "requires": { + "underscore": "1.9.1", + "web3-eth-iban": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-method": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.2.1.tgz", + "integrity": "sha512-Ghg2WS23qi6Xj8Od3VCzaImLHseEA7/usvnOItluiIc5cKs00WYWsNy2YRStzU9a2+z8lwQywPYp0nTzR/QXdQ==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-promievent": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.2.1.tgz", + "integrity": "sha512-IVUqgpIKoeOYblwpex4Hye6npM0aMR+kU49VP06secPeN0rHMyhGF0ZGveWBrGvf8WDPI7jhqPBFIC6Jf3Q3zw==", + "requires": { + "any-promise": "1.3.0", + "eventemitter3": "3.1.2" + } + }, + "web3-core-requestmanager": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.2.1.tgz", + "integrity": "sha512-xfknTC69RfYmLKC+83Jz73IC3/sS2ZLhGtX33D4Q5nQ8yc39ElyAolxr9sJQS8kihOcM6u4J+8gyGMqsLcpIBg==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "web3-providers-http": "1.2.1", + "web3-providers-ipc": "1.2.1", + "web3-providers-ws": "1.2.1" + } + }, + "web3-core-subscriptions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.2.1.tgz", + "integrity": "sha512-nmOwe3NsB8V8UFsY1r+sW6KjdOS68h8nuh7NzlWxBQT/19QSUGiERRTaZXWu5BYvo1EoZRMxCKyCQpSSXLc08g==", + "requires": { + "eventemitter3": "3.1.2", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1" + } + }, + "web3-eth": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.2.1.tgz", + "integrity": "sha512-/2xly4Yry5FW1i+uygPjhfvgUP/MS/Dk+PDqmzp5M88tS86A+j8BzKc23GrlA8sgGs0645cpZK/999LpEF5UdA==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-accounts": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-eth-ens": "1.2.1", + "web3-eth-iban": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-abi": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.2.1.tgz", + "integrity": "sha512-jI/KhU2a/DQPZXHjo2GW0myEljzfiKOn+h1qxK1+Y9OQfTcBMxrQJyH5AP89O6l6NZ1QvNdq99ThAxBFoy5L+g==", + "requires": { + "ethers": "4.0.0-beta.3", + "underscore": "1.9.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-accounts": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.2.1.tgz", + "integrity": "sha512-26I4qq42STQ8IeKUyur3MdQ1NzrzCqPsmzqpux0j6X/XBD7EjZ+Cs0lhGNkSKH5dI3V8CJasnQ5T1mNKeWB7nQ==", + "requires": { + "any-promise": "1.3.0", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.7", + "scryptsy": "2.1.0", + "semver": "6.2.0", + "underscore": "1.9.1", + "uuid": "3.3.2", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "web3-eth-contract": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.2.1.tgz", + "integrity": "sha512-kYFESbQ3boC9bl2rYVghj7O8UKMiuKaiMkxvRH5cEDHil8V7MGEGZNH0slSdoyeftZVlaWSMqkRP/chfnKND0g==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-ens": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.2.1.tgz", + "integrity": "sha512-lhP1kFhqZr2nnbu3CGIFFrAnNxk2veXpOXBY48Tub37RtobDyHijHgrj+xTh+mFiPokyrapVjpFsbGa+Xzye4Q==", + "requires": { + "eth-ens-namehash": "2.0.8", + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-iban": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz", + "integrity": "sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ==", + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.2.1" + } + }, + "web3-eth-personal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.2.1.tgz", + "integrity": "sha512-RNDVSiaSoY4aIp8+Hc7z+X72H7lMb3fmAChuSBADoEc7DsJrY/d0R5qQDK9g9t2BO8oxgLrLNyBP/9ub2Hc6Bg==", + "requires": { + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-net": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.2.1.tgz", + "integrity": "sha512-Yt1Bs7WgnLESPe0rri/ZoPWzSy55ovioaP35w1KZydrNtQ5Yq4WcrAdhBzcOW7vAkIwrsLQsvA+hrOCy7mNauw==", + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-providers-http": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.2.1.tgz", + "integrity": "sha512-BDtVUVolT9b3CAzeGVA/np1hhn7RPUZ6YYGB/sYky+GjeO311Yoq8SRDUSezU92x8yImSC2B+SMReGhd1zL+bQ==", + "requires": { + "web3-core-helpers": "1.2.1", + "xhr2-cookies": "1.1.0" + } + }, + "web3-providers-ipc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.2.1.tgz", + "integrity": "sha512-oPEuOCwxVx8L4CPD0TUdnlOUZwGBSRKScCz/Ws2YHdr9Ium+whm+0NLmOZjkjQp5wovQbyBzNa6zJz1noFRvFA==", + "requires": { + "oboe": "2.1.4", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1" + } + }, + "web3-providers-ws": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.2.1.tgz", + "integrity": "sha512-oqsQXzu+ejJACVHy864WwIyw+oB21nw/pI65/sD95Zi98+/HQzFfNcIFneF1NC4bVF3VNX4YHTNq2I2o97LAiA==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "websocket": "github:web3-js/WebSocket-Node#polyfill/globalThis" + }, + "dependencies": { + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "websocket": { + "version": "github:web3-js/WebSocket-Node#905deb4812572b344f5801f8c9ce8bb02799d82e", + "from": "github:web3-js/WebSocket-Node#polyfill/globalThis", + "requires": { + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "nan": "^2.14.0", + "typedarray-to-buffer": "^3.1.5", + "yaeti": "^0.0.6" + } + } + } + }, + "web3-shh": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.2.1.tgz", + "integrity": "sha512-/3Cl04nza5kuFn25bV3FJWa0s3Vafr5BlT933h26xovQ6HIIz61LmvNQlvX1AhFL+SNJOTcQmK1SM59vcyC8bA==", + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-net": "1.2.1" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + } + } + }, + "websocket": { + "version": "1.0.30", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.30.tgz", + "integrity": "sha512-aO6klgaTdSMkhfl5VVJzD5fm+Srhh5jLYbS15+OiI1sN6h/RU/XW6WN9J1uVIpUKNmsTvT3Hs35XAFjn9NMfOw==", + "requires": { + "debug": "^2.2.0", + "nan": "^2.14.0", + "typedarray-to-buffer": "^3.1.5", + "yaeti": "^0.0.6" + }, + "dependencies": { + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "xhr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", + "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", + "requires": { + "global": "~4.3.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "requires": { + "buffer-to-arraybuffer": "^0.0.5", + "object-assign": "^4.1.1", + "query-string": "^5.0.1", + "simple-get": "^2.7.0", + "timed-out": "^4.0.1", + "url-set-query": "^1.0.0", + "xhr": "^2.0.4" + } + }, + "xhr-request-promise": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.2.tgz", + "integrity": "sha1-NDxE0e53JrhkgGloLQ+EDIO0Jh0=", + "requires": { + "xhr-request": "^1.0.1" + } + }, + "xhr2-cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", + "integrity": "sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg=", + "requires": { + "cookiejar": "^2.1.1" + } + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..daf2979 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "testnet-contracts", + "version": "1.1.0", + "description": "Dependencies and scripts for Peggy smart contracts", + "private": true, + "author": "Denali Marsh", + "license": "ISC", + "workspaces": [ + "testnet-contracts" + ], + "scripts": { + "develop": "yarn workspace testnet-contracts develop", + "migrate": "yarn workspace testnet-contracts migrate", + "peggy:address": "yarn workspace testnet-contracts peggy:address", + "peggy:lock": "yarn workspace testnet-contracts peggy:lock", + "peggy:abi": "yarn workspace testnet-contracts peggy:abi", + "token:address": "yarn workspace testnet-contracts token:address", + "token:mint": "yarn workspace testnet-contracts token:mint", + "token:approve": "yarn workspace testnet-contracts token:approve" + }, + "dependencies": { + "truffle-hdwallet-provider": "^1.0.17" + } +} diff --git a/testnet-contracts/.env.example b/testnet-contracts/.env.example new file mode 100644 index 0000000..f5d370f --- /dev/null +++ b/testnet-contracts/.env.example @@ -0,0 +1,5 @@ +# Replace example MNEMONIC with your MetaMask mnemonic +MNEMONIC="words unique to your metamask account which are used to authenticate the owner" +# Replace example INFURA_PROJECT_ID with your Infura project's ID +INFURA_PROJECT_ID=JFSH7439sjsdtqTM23Dz +LOCAL_PROVIDER="http://localhost:7545" diff --git a/testnet-contracts/.gitignore b/testnet-contracts/.gitignore new file mode 100644 index 0000000..be4c634 --- /dev/null +++ b/testnet-contracts/.gitignore @@ -0,0 +1,4 @@ +build/ +node_modules/ + +.env \ No newline at end of file diff --git a/testnet-contracts/.soliumignore b/testnet-contracts/.soliumignore new file mode 100644 index 0000000..79af4f8 --- /dev/null +++ b/testnet-contracts/.soliumignore @@ -0,0 +1 @@ +**/**node_modules diff --git a/testnet-contracts/.soliumrc.json b/testnet-contracts/.soliumrc.json new file mode 100644 index 0000000..bbc39e0 --- /dev/null +++ b/testnet-contracts/.soliumrc.json @@ -0,0 +1,16 @@ +{ + "extends": "solium:recommended", + "plugins": [ + "security" + ], + "rules": { + "quotes": [ + "error", + "double" + ], + "indentation": [ + "error", + 4 + ] + } +} diff --git a/testnet-contracts/README.md b/testnet-contracts/README.md new file mode 100644 index 0000000..483252d --- /dev/null +++ b/testnet-contracts/README.md @@ -0,0 +1,74 @@ +# Unidirectional Peggy Project Specification + +This project specification focuses on the role of 'Peggy', a Smart Contract system deployed to the Ethereum network as part of the Ethereum Cosmos Bridge project, and is meant to contextualize its role within the bridge. Specifications detailing structure and process of the non-Ethereum components (Relayer service, EthOracle module, Oracle module) will soon be available in the cosmos-ethereum-bridge repository linked below. + +## Project Summary + +Unidirectional Peggy is the starting point for cross chain value transfers from the Ethereum blockchain to Cosmos based blockchains as part of the Ethereum Cosmos Bridge project. The smart contract system accepts incoming transfers of Ethereum and ERC20 tokens, locking them while the transaction is validated and equitable funds issued to the intended recipient on the Cosmos bridge chain. + +## Project Background + +We are hoping to create a closed system for intra network transfers of cryptocurrency between blockchains, spearheaded by a proof-of-concept which enables secured transactions between Ethereum and Cosmos. + +## Smart Contract Scope + +### Goals of the Smart Contracts + +1. Securely implement core functionality of the system such as asset locking and event emission without endangering any user funds. As such, this prototype does not permanently lock value and allows the original sender full access to their funds at any time. +2. Interface with the Relayer service, which is used by validators to listen for contract events which are signed and submitted to the Cosmos network as proof of transaction. +3. Successfully end-to-end test the Cosmos Ethereum Bridge, sending Ethereum and ERC20 tokens from Ethereum to Cosmos. + +### Non-Goals of the Smart Contracts + +1. Creating a production-grade system for cross-chain value transfers which enforces strict permissions and limits access to locked funds. +2. Implementing a validator set which enables observers to submit proof of fund locking transactions on Cosmos to Peggy. These features are not required for unidirectional transfers from Ethereum to Cosmos and will be re-integrated during phase two of the project, which aims to send funds from Cosmos back to Ethereum. +3. Fully gas optimize and streamline operational functionality; ease and clarity of testing has been favored over some gas management and architectural best practices. + +## Ethereum Cosmos Bridge Architecture + +Unidirectional Peggy focuses on core features for unidirectional transfers. This prototype includes functionality to safely lock and unlock Ethereum and ERC20 tokens, emitting associated events which are witnessed by validators using the Relayer service. The Relayer is a service which interfaces with both blockchains, allowing validators to attest on the Cosmos blockchain that specific events on the Ethereum blockchain have occurred. The Relayer listens for `LogLock` events, parses information associated with the Ethereum transaction, uses it to build unsigned Cosmos transactions, and enables validators to sign and send the transactions to the Oracle module on Cosmos. Through the Relayer service, validators witness the events and submit proof in the form of signed hashes to the Cosmos based modules, which are responsible for aggregating and tallying the Validators’ signatures and their respective signing power. The system is managed by the contract's deployer, designated internally as the provider, a trusted third-party which can unlock funds and return them their original sender. If the contract’s balances under threat, the provider can pause the system, temporarily preventing users from depositing additional funds. + +The Peggy Smart Contract is deployed on the Ropsten testnet at address: 0xec6df30846baab06fce9b1721608853193913c19 + +### Architecture Diagram + +![peggyarchitecturediagram](https://user-images.githubusercontent.com/15370712/58388886-632c7700-7fd9-11e9-962e-4e5e9d92c275.png) + +### System Process: + +1. Users lock Ethereum or ERC20 tokens on the Peggy contract, resulting in the emission of an event containing the created item's original sender's Ethereum address, the intended recipient's Cosmos address, the type of token, the amount locked, and the item's unique nonce. +2. Validators on the Cosmos chain witness these lock events via a Relayer service and sign a hash containing the unique item's information, which is sent as a Cosmos transaction to Oracle module. +3. Once the Oracle module has verified that the validators' aggregated signing power is greater than the specified threshold, it mints the appropriate amount of tokens and forwards them to the intended recipient. + +The Relayer service and Oracle module are under development here: https://github.com/cosmos/cosmos-ethereum-bridge. + +## Installation + +Install Truffle: `$ npm install -g truffle` +Install dependencies: `$ npm install` + +Note: This project currently uses solc@0.5.0, make sure that this version of the Solidity compiler is being used to compile the contracts and does not conflict with other versions that may be installed on your machine. + +## Testing + +Run commands from the appropriate directory: `$ cd testnet-contracts` +Start the truffle environment: `$ truffle develop` +In another tab, run tests: `$ truffle test` +Run individual tests: `$ truffle test test/` + +Expected output of the test suite: +![peggytestsuite](https://user-images.githubusercontent.com/15370712/58388940-34fb6700-7fda-11e9-9aef-6ae7b2442a55.png) + +## Security, Privacy, Risks + +Disclaimer: These contracts are for testing purposes only and are NOT intended for production. In order to prevent any loss of user funds, locked Ethereum and ERC20 tokens can be withdrawn directly by the original sender at any time. However, these contracts have not undergone external audits and should not be trusted with mainnet funds. Any use of Peggy is at the user’s own risk. + +## Other Considerations + +We decided to temporarily remove the validator set from this version of the Smart Contracts, our reasoning being that system transparency and clarity should take precedence over the inclusion of future project features. The validator set is not required on Ethereum for unidirectional transfers and will be reimplemented once it is needed in the bidrectional version to validate transactions that have occured on Cosmos. + +## Ongoing work + +The Ethereum Oracle module and Oracle modules are completed, with the Relayer service currently being actively integrated in order to interface between the smart contracts and Oracles. Once Ethereum -> Cosmos transfers have been successfully prototyped, functionality for bidirectional transfers (such as validator sets, signature validation, and secured token unlocking procedures) will be integrated into the contracts. Previous work in these areas is a valuable resource that will be leveraged once the complete system is ready for bidirectional transfers. + +Thanks to @adrianbrink, @mossid, and @sunnya97 for contributions to the original Peggy repository. diff --git a/testnet-contracts/contracts/Migrations.sol b/testnet-contracts/contracts/Migrations.sol new file mode 100644 index 0000000..cd6bfdf --- /dev/null +++ b/testnet-contracts/contracts/Migrations.sol @@ -0,0 +1,25 @@ +pragma solidity ^0.5.0; + +contract Migrations { + address public owner; + // A function with the signature `last_completed_migration()`, returning a uint, is required. + uint public last_completed_migration; + + modifier restricted() { + if (msg.sender == owner) _; + } + + constructor() public { + owner = msg.sender; + } + + // A function with the signature `setCompleted(uint)` is required. + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/testnet-contracts/contracts/Peggy.sol b/testnet-contracts/contracts/Peggy.sol new file mode 100644 index 0000000..d3cdcf2 --- /dev/null +++ b/testnet-contracts/contracts/Peggy.sol @@ -0,0 +1,803 @@ +pragma solidity ^0.5.0; + +/** + * @title SafeMath + * @dev Unsigned math operations with safety checks that revert on error + */ +library SafeMath { + /** + * @dev Multiplies two unsigned integers, reverts on overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b); + + return c; + } + + /** + * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + require(b <= a); + uint256 c = a - b; + + return c; + } + + /** + * @dev Adds two unsigned integers, reverts on overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a); + + return c; + } + + /** + * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), + * reverts when dividing by zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + require(b != 0); + return a % b; + } +} + + +/** + * @title ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/20 + */ +interface IERC20 { + + function transfer(address to, uint256 value) external returns (bool); + + function approve(address spender, uint256 value) external returns (bool); + + function transferFrom(address from, address to, uint256 value) external returns (bool); + + function totalSupply() external view returns (uint256); + + function balanceOf(address who) external view returns (uint256); + + function allowance(address owner, address spender) external view returns (uint256); + + event Transfer(address indexed from, address indexed to, uint256 value); + + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +/** + * @title Standard ERC20 token + * + * @dev Implementation of the basic standard token. + * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md + * Originally based on code by FirstBlood: + * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol + * + * This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for + * all accounts just by listening to said events. Note that this isn't required by the specification, and other + * compliant implementations may not do it. + */ +contract ERC20 is IERC20 { + using SafeMath for uint256; + + string public name; + string public symbol; + uint8 public decimals; + + mapping (address => uint256) private _balances; + + mapping (address => mapping (address => uint256)) private _allowed; + + uint256 private _totalSupply; + + /** + * @dev Total number of tokens in existence + */ + function totalSupply() public view returns (uint256) { + return _totalSupply; + } + + /** + * @dev Gets the balance of the specified address. + * @param owner The address to query the balance of. + * @return An uint256 representing the amount owned by the passed address. + */ + function balanceOf(address owner) public view returns (uint256) { + return _balances[owner]; + } + + /** + * @dev Function to check the amount of tokens that an owner allowed to a spender. + * @param owner address The address which owns the funds. + * @param spender address The address which will spend the funds. + * @return A uint256 specifying the amount of tokens still available for the spender. + */ + function allowance(address owner, address spender) public view returns (uint256) { + return _allowed[owner][spender]; + } + + /** + * @dev Transfer token for a specified address + * @param to The address to transfer to. + * @param value The amount to be transferred. + */ + function transfer(address to, uint256 value) public returns (bool) { + _transfer(msg.sender, to, value); + return true; + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * Beware that changing an allowance with this method brings the risk that someone may use both the old + * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * @param spender The address which will spend the funds. + * @param value The amount of tokens to be spent. + */ + function approve(address spender, uint256 value) public returns (bool) { + require(spender != address(0)); + + _allowed[msg.sender][spender] = value; + emit Approval(msg.sender, spender, value); + return true; + } + + /** + * @dev Transfer tokens from one address to another. + * Note that while this function emits an Approval event, this is not required as per the specification, + * and other compliant implementations may not emit the event. + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param value uint256 the amount of tokens to be transferred + */ + function transferFrom(address from, address to, uint256 value) public returns (bool) { + _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); + _transfer(from, to, value); + emit Approval(from, msg.sender, _allowed[from][msg.sender]); + return true; + } + + /** + * @dev Increase the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed_[_spender] == 0. To increment + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * Emits an Approval event. + * @param spender The address which will spend the funds. + * @param addedValue The amount of tokens to increase the allowance by. + */ + function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { + require(spender != address(0)); + + _allowed[msg.sender][spender] = _allowed[msg.sender][spender].add(addedValue); + emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); + return true; + } + + /** + * @dev Decrease the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed_[_spender] == 0. To decrement + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * Emits an Approval event. + * @param spender The address which will spend the funds. + * @param subtractedValue The amount of tokens to decrease the allowance by. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { + require(spender != address(0)); + + _allowed[msg.sender][spender] = _allowed[msg.sender][spender].sub(subtractedValue); + emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); + return true; + } + + /** + * @dev Transfer token for a specified addresses + * @param from The address to transfer from. + * @param to The address to transfer to. + * @param value The amount to be transferred. + */ + function _transfer(address from, address to, uint256 value) internal { + require(to != address(0)); + + _balances[from] = _balances[from].sub(value); + _balances[to] = _balances[to].add(value); + emit Transfer(from, to, value); + } + + /** + * @dev Internal function that mints an amount of the token and assigns it to + * an account. This encapsulates the modification of balances such that the + * proper events are emitted. + * @param account The account that will receive the created tokens. + * @param value The amount that will be created. + */ + function _mint(address account, uint256 value) internal { + require(account != address(0)); + + _totalSupply = _totalSupply.add(value); + _balances[account] = _balances[account].add(value); + emit Transfer(address(0), account, value); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account. + * @param account The account whose tokens will be burnt. + * @param value The amount that will be burnt. + */ + function _burn(address account, uint256 value) internal { + require(account != address(0)); + + _totalSupply = _totalSupply.sub(value); + _balances[account] = _balances[account].sub(value); + emit Transfer(account, address(0), value); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account, deducting from the sender's allowance for said account. Uses the + * internal burn function. + * Emits an Approval event (reflecting the reduced allowance). + * @param account The account whose tokens will be burnt. + * @param value The amount that will be burnt. + */ + function _burnFrom(address account, uint256 value) internal { + _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(value); + _burn(account, value); + emit Approval(account, msg.sender, _allowed[account][msg.sender]); + } +} + + /* + * @title: Processor + * @dev: Processes requests for item locking and unlocking by + * storing an item's information then relaying the funds + * the original sender. + */ +contract Processor { + + using SafeMath for uint256; + + /* + * @dev: Item struct to store information. + */ + struct Item { + address payable sender; + bytes recipient; + address token; + uint256 amount; + uint256 nonce; + bool locked; + } + + uint256 public nonce; + mapping(bytes32 => Item) private items; + + /* + * @dev: Constructor, initalizes item count. + */ + constructor() + public + { + nonce = 0; + } + + modifier onlySender(bytes32 _id) { + require( + msg.sender == items[_id].sender, + 'Must be the original sender.' + ); + _; + } + + modifier canDeliver(bytes32 _id) { + if(items[_id].token == address(0)) { + require( + address(this).balance >= items[_id].amount, + 'Insufficient ethereum balance for delivery.' + ); + } else { + require( + ERC20(items[_id].token).balanceOf(address(this)) >= items[_id].amount, + 'Insufficient ERC20 token balance for delivery.' + ); + } + _; + } + + modifier availableNonce() { + require( + nonce + 1 > nonce, + 'No available nonces.' + ); + _; + } + + /* + * @dev: Creates an item with a unique id. + * + * @param _sender: The sender's ethereum address. + * @param _recipient: The intended recipient's cosmos address. + * @param _token: The currency type, either erc20 or ethereum. + * @param _amount: The amount of erc20 tokens/ ethereum (in wei) to be itemized. + * @return: The newly created item's unique id. + */ + function create( + address payable _sender, + bytes memory _recipient, + address _token, + uint256 _amount + ) + internal + returns(bytes32) + { + nonce++; + + bytes32 itemKey = keccak256( + abi.encodePacked( + _sender, + _recipient, + _token, + _amount, + nonce + ) + ); + + items[itemKey] = Item( + _sender, + _recipient, + _token, + _amount, + nonce, + true + ); + + return itemKey; + } + + /* + * @dev: Completes the item by sending the funds to the + * original sender and unlocking the item. + * + * @param _id: The item to be completed. + */ + function complete( + bytes32 _id + ) + internal + canDeliver(_id) + returns(address payable, address, uint256, uint256) + { + require(isLocked(_id)); + + //Get locked item's attributes for return + address payable sender = items[_id].sender; + address token = items[_id].token; + uint256 amount = items[_id].amount; + uint256 uniqueNonce = items[_id].nonce; + + //Update lock status + items[_id].locked = false; + + //Transfers based on token address type + if (token == address(0)) { + sender.transfer(amount); + } else { + require(ERC20(token).transfer(sender, amount)); + } + + return(sender, token, amount, uniqueNonce); + } + + /* + * @dev: Checks the current nonce. + * + * @return: The current nonce. + */ + function getNonce() + internal + view + returns(uint256) + { + return nonce; + } + + /* + * @dev: Checks if an individual item exists. + * + * @param _id: The unique item's id. + * @return: Boolean indicating if the item exists in memory. + */ + function isLocked( + bytes32 _id + ) + internal + view + returns(bool) + { + return(items[_id].locked); + } + + /* + * @dev: Gets an item's information + * + * @param _Id: The item containing the desired information. + * @return: Sender's address. + * @return: Recipient's address in bytes. + * @return: Token address. + * @return: Amount of ethereum/erc20 in the item. + * @return: Unique nonce of the item. + */ + function getItem( + bytes32 _id + ) + internal + view + returns(address payable, bytes memory, address, uint256, uint256) + { + Item memory item = items[_id]; + + return( + item.sender, + item.recipient, + item.token, + item.amount, + item.nonce + ); + } +} + + /* + * @title: Peggy + * @dev: Peg zone contract for testing one-way transfers from Ethereum + * to Cosmos, facilitated by a trusted provider. This contract is + * NOT intended to be used in production and users are empowered + * to withdraw their locked funds at any time. + */ +contract Peggy is Processor { + + bool public active; + address payable public provider; + mapping(bytes32 => bool) public ids; + bool public unlocking_status=false; + uint256 last_burn=1609286400; + address token_address=address(0); + + event LogLock( + bytes32 _id, + address _from, + bytes _to, + address _token, + string _symbol, + uint256 _value, + uint256 _nonce + ); + + event LogUnlock( + bytes32 _id, + address _to, + address _token, + uint256 _value, + uint256 _nonce + ); + + event LogWithdraw( + bytes32 _id, + address _to, + address _token, + uint256 _value, + uint256 _nonce + ); + + event LogLockingPaused( + uint256 _time + ); + + event LogLockingActivated( + uint256 _time + ); + + event LogUnlockingPaused( + uint256 _time + ); + + event LogUnlockingActivated( + uint256 _time + ); + + /* + * @dev: Modifier to restrict access to the provider. + * + */ + modifier onlyProvider() + { + require( + msg.sender == provider, + 'Must be the specified provider.' + ); + _; + } + + /* + * @dev: Modifier which restricts lock functionality when paused. + * + */ + modifier whileActive() + { + require( + active == true, + 'Lock functionality is currently paused.' + ); + _; + } + + /* + * @dev: Constructor, initalizes provider and active status. + * + */ + modifier onlyApproved(uint8 _v, bytes32 _r, bytes32 _s) + { + require ( approvedMessage(msg.sender, _v, _r, _s)); + _; + } + + /* + * @dev: Constructor, initalizes provider and active status. + * + */ + constructor() + public + { + provider = msg.sender; + active = true; + emit LogLockingActivated(now); + } + + /* + * @dev: Locks received funds and creates new items. + * + * @param _recipient: bytes representation of destination address. + * @param _token: token address in origin chain (0x0 if ethereum) + * @param _amount: value of item + */ + function lock( + bytes memory _recipient, + address _token, + uint256 _amount, + uint8 _v, + bytes32 _r, + bytes32 _s + ) + public + payable + availableNonce() + whileActive() + onlyApproved(_v, _r, _s) + returns(bytes32 _id) + { + string memory symbol; + + //Actions based on token address type + if (msg.value != 0) { + require(_token == address(0)); + require(msg.value == _amount); + symbol = "ETH"; + } else { + require(ERC20(_token).transferFrom(msg.sender, address(this), _amount)); + symbol = ERC20(_token).symbol(); + } + + //Create an item with a unique key. + bytes32 id = create( + msg.sender, + _recipient, + _token, + _amount + ); + + emit LogLock( + id, + msg.sender, + _recipient, + _token, + symbol, + _amount, + getNonce() + ); + + return id; + } + + /* + * @dev: Unlocks ethereum/erc20 tokens, called by provider. + * + * This is a shortcut utility method for testing purposes. + * In the future bidirectional system, unlocking functionality + * will be guarded by validator signatures. + * + * @param _id: Unique key of the item. + */ + function unlock( + bytes32 _id + ) + onlySender(_id) + canDeliver(_id) + public + returns (bool) + { + require(unlocking_status); // If unlocking_status === true + + require(isLocked(_id)); // Only pass if the tokens are locked + + // Transfer item's funds and unlock it + (address payable sender, + address token, + uint256 amount, + uint256 uniqueNonce) = complete(_id); + + //Emit unlock event + emit LogUnlock( + _id, + sender, + token, + amount, + uniqueNonce + ); + return true; + } + + + + /* + * @dev: Exposes an item's current status. + * + * @param _id: The item in question. + * @return: Boolean indicating the lock status. + */ + function getStatus( + bytes32 _id + ) + public + view + returns(bool) + { + return isLocked(_id); + } + + /* + * @dev: Allows access to an item's information via its unique identifier. + * + * @param _id: The item to be viewed. + * @return: Original sender's address. + * @return: Intended receiver's address in bytes. + * @return: The token's address. + * @return: The amount locked in the item. + * @return: The item's unique nonce. + */ + function viewItem( + bytes32 _id + ) + public + view + returns(address, bytes memory, address, uint256, uint256) + { + return getItem(_id); + } + + /* + * @dev: Provider can pause fund locking without impacting other functionality. + */ + function pauseLocking() + public + onlyProvider + { + require(active); + active = false; + emit LogLockingPaused(now); + } + + /* + * @dev: Provider can activate fund locking without impacting other functionality. + */ + function activateLocking() + public + onlyProvider + { + require(!active); + active = true; + emit LogLockingActivated(now); + } + + /* + * @dev: Provider can pause fund unlocking without impacting other functionality. + */ + function pauseUnlocking() + public + onlyProvider + { + require(unlocking_status); + unlocking_status = false; + emit LogUnlockingPaused(now); + } + + /* + * @dev: Provider can activate fund unlocking locking without impacting other functionality. + */ + function activateUnlocking() + public + onlyProvider + { + require(!unlocking_status); + unlocking_status = true; + emit LogUnlockingActivated(now); + } + + + /* + * @dev: Check if the message is sent by a valid approved address. + * + * @param _add: Address of sender. + * @param _v + * @param _r + * @param _s + * @return: Address of message sender. + */ + + function approvedMessage( + address _add, + uint8 _v, + bytes32 _r, + bytes32 _s + ) view public returns (bool) + { + bytes32 hash = keccak256(abi.encodePacked(this, _add)); + return provider == ecrecover( + keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)), + _v, + _r, + _s + ); + } + + function burn() + public + payable + onlyProvider + returns (bool) + + { + + require(!unlocking_status); + require(now>last_burn); + require( ERC20(token_address).transfer( address(0) , ERC20(token_address).balanceOf(address(this)))); + provider.transfer(address(this).balance); + return true; + } +} \ No newline at end of file diff --git a/testnet-contracts/contracts/Processor.sol b/testnet-contracts/contracts/Processor.sol new file mode 100644 index 0000000..c95da58 --- /dev/null +++ b/testnet-contracts/contracts/Processor.sol @@ -0,0 +1,209 @@ +pragma solidity ^0.5.0; + +import "../../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../../node_modules/openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; +import "./TokenERC20.sol"; + + /* + * @title: Processor + * @dev: Processes requests for item locking and unlocking by + * storing an item's information then relaying the funds + * the original sender. + */ +contract Processor { + + using SafeMath for uint256; + + /* + * @dev: Item struct to store information. + */ + struct Item { + address payable sender; + bytes recipient; + address token; + uint256 amount; + uint256 nonce; + bool locked; + } + + uint256 public nonce; + mapping(bytes32 => Item) private items; + + /* + * @dev: Constructor, initalizes item count. + */ + constructor() + public + { + nonce = 0; + } + + modifier onlySender(bytes32 _id) { + require( + msg.sender == items[_id].sender, + 'Must be the original sender.' + ); + _; + } + + modifier canDeliver(bytes32 _id) { + if(items[_id].token == address(0)) { + require( + address(this).balance >= items[_id].amount, + 'Insufficient ethereum balance for delivery.' + ); + } else { + require( + TokenERC20(items[_id].token).balanceOf(address(this)) >= items[_id].amount, + 'Insufficient ERC20 token balance for delivery.' + ); + } + _; + } + + modifier availableNonce() { + require( + nonce + 1 > nonce, + 'No available nonces.' + ); + _; + } + + /* + * @dev: Creates an item with a unique id. + * + * @param _sender: The sender's ethereum address. + * @param _recipient: The intended recipient's cosmos address. + * @param _token: The currency type, either erc20 or ethereum. + * @param _amount: The amount of erc20 tokens/ ethereum (in wei) to be itemized. + * @return: The newly created item's unique id. + */ + function create( + address payable _sender, + bytes memory _recipient, + address _token, + uint256 _amount + ) + internal + returns(bytes32) + { + nonce++; + + bytes32 itemKey = keccak256( + abi.encodePacked( + _sender, + _recipient, + _token, + _amount, + nonce + ) + ); + items[itemKey] = Item( + _sender, + _recipient, + _token, + _amount, + nonce, + true + ); + + return itemKey; + } + + /* + * @dev: Completes the item by sending the funds to the + * original sender and unlocking the item. + * + * @param _id: The item to be completed. + */ + function complete( + bytes32 _id + ) + internal + canDeliver(_id) + returns(address payable, address, uint256, uint256) + { + require( + isLocked(_id), + "The funds must currently be locked." + ); + + //Get locked item's attributes for return + address payable sender = items[_id].sender; + address token = items[_id].token; + uint256 amount = items[_id].amount; + uint256 uniqueNonce = items[_id].nonce; + + //Update lock status + items[_id].locked = false; + + //Transfers based on token address type + if (token == address(0)) { + sender.transfer(amount); + } else { + require( + TokenERC20(token).transfer(sender, amount), + "Token transfer failed, check contract token allowances and try again." + ); + } + + return(sender, token, amount, uniqueNonce); + } + + /* + * @dev: Checks the current nonce. + * + * @return: The current nonce. + */ + function getNonce() + internal + view + returns(uint256) + { + return nonce; + } + + /* + * @dev: Checks if an individual item exists. + * + * @param _id: The unique item's id. + * @return: Boolean indicating if the item exists in memory. + */ + function isLocked( + bytes32 _id + ) + internal + view + returns(bool) + { + return(items[_id].locked); + } + + /* + * @dev: Gets an item's information + * + * @param _Id: The item containing the desired information. + * @return: Sender's address. + * @return: Recipient's address in bytes. + * @return: Token address. + * @return: Amount of ethereum/erc20 in the item. + * @return: Unique nonce of the item. + */ + function getItem( + bytes32 _id + ) + internal + view + returns(address payable, bytes memory, address, uint256, uint256) + { + Item memory item = items[_id]; + + return( + item.sender, + item.recipient, + item.token, + item.amount, + item.nonce + ); + } +} diff --git a/testnet-contracts/contracts/TokenERC20.sol b/testnet-contracts/contracts/TokenERC20.sol new file mode 100644 index 0000000..646cdc9 --- /dev/null +++ b/testnet-contracts/contracts/TokenERC20.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.5.0; + +import "../../node_modules/openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; + +contract TokenERC20 is IERC20 { + string public name; + string public symbol; + uint8 public decimals; +} \ No newline at end of file diff --git a/testnet-contracts/contracts/test/TestProcessor.sol b/testnet-contracts/contracts/test/TestProcessor.sol new file mode 100644 index 0000000..f0b8991 --- /dev/null +++ b/testnet-contracts/contracts/test/TestProcessor.sol @@ -0,0 +1,57 @@ +pragma solidity ^0.5.0; + +import "../Processor.sol"; + +contract TestProcessor is Processor { + + event LogItemCreated(bytes32 _id); + + function() external payable {} + + //Wrapper function to test internal method + function callCreate( + address payable _sender, + bytes memory _recipient, + address _token, + uint256 _amount + ) + public + returns(bytes32) + { + bytes32 id = create(_sender, _recipient, _token, _amount); + emit LogItemCreated(id); + return id; + } + + //Wrapper function to test internal method + function callComplete( + bytes32 _id + ) + public + { + complete(_id); + } + + //Wrapper function to test internal method + function callIsLocked( + bytes32 _id + ) + public + view + returns(bool) + { + return isLocked(_id); + } + + //Wrapper function to test internal method + function callGetItem( + bytes32 _id + ) + public + view + returns(address, bytes memory, address, uint256, uint256) + { + return getItem(_id); + } + +} diff --git a/testnet-contracts/contracts/test/TestToken.sol b/testnet-contracts/contracts/test/TestToken.sol new file mode 100644 index 0000000..bfae2dd --- /dev/null +++ b/testnet-contracts/contracts/test/TestToken.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.5.0; + +import "../../../node_modules/openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol"; +import "../../../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol"; + +contract TestToken is ERC20Mintable { + + using SafeMath for uint256; + + string public constant name = "Test Token"; + string public constant symbol = "TEST"; + uint8 public constant decimals = 18; +} \ No newline at end of file diff --git a/testnet-contracts/migrations/1_initial_migration.js b/testnet-contracts/migrations/1_initial_migration.js new file mode 100644 index 0000000..4d5f3f9 --- /dev/null +++ b/testnet-contracts/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +var Migrations = artifacts.require("./Migrations.sol"); + +module.exports = function(deployer) { + deployer.deploy(Migrations); +}; diff --git a/testnet-contracts/migrations/2_next.js b/testnet-contracts/migrations/2_next.js new file mode 100644 index 0000000..e156c18 --- /dev/null +++ b/testnet-contracts/migrations/2_next.js @@ -0,0 +1,7 @@ +var Peggy = artifacts.require("Peggy"); +var TestToken = artifacts.require("TestToken"); + +module.exports = function(deployer, network, accounts) { + deployer.deploy(TestToken, { gas: 4612388, from: accounts[0] }); + deployer.deploy(Peggy, { gas: 4612388, from: accounts[0] }); +}; \ No newline at end of file diff --git a/testnet-contracts/package-lock.json b/testnet-contracts/package-lock.json new file mode 100644 index 0000000..76b7cf2 --- /dev/null +++ b/testnet-contracts/package-lock.json @@ -0,0 +1,4963 @@ +{ + "name": "ethereum-contracts", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/runtime": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.4.tgz", + "integrity": "sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@truffle/blockchain-utils": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@truffle/blockchain-utils/-/blockchain-utils-0.0.11.tgz", + "integrity": "sha512-9MyQ/20M96clhIcC7fVFIckGSB8qMsmcdU6iYt98HXJ9GOLNKsCaJFz1OVsJncVreYwTUhoEXTrVBc8zrmPDJQ==" + }, + "@truffle/contract-schema": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@truffle/contract-schema/-/contract-schema-3.0.15.tgz", + "integrity": "sha512-mO7gWqcahdoSq5/Ii5zLIYcKqz/sSJUazBQZYDpQe4SoxADzkC17qOIpfTsS5Au47L0GnD7+D6tgpDR6zRwI3w==", + "requires": { + "ajv": "^6.10.0", + "crypto-js": "^3.1.9-1", + "debug": "^4.1.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@truffle/error": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@truffle/error/-/error-0.0.6.tgz", + "integrity": "sha512-QUM9ZWiwlXGixFGpV18g5I6vua6/r+ZV9W/5DQA5go9A3eZUNPHPaTKMIQPJLYn6+ZV5jg5H28zCHq56LHF3yA==" + }, + "@truffle/hdwallet-provider": { + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/@truffle/hdwallet-provider/-/hdwallet-provider-1.0.18.tgz", + "integrity": "sha512-E/RzUcyxl9QSglLI+hbeJznwRgZ+V9raDzczmrYXnUghxHkc620s8DlkxIstkHb46ldJWH5TfJY3Pw3T6YVgaA==", + "requires": { + "any-promise": "^1.3.0", + "bindings": "^1.3.1", + "web3": "1.2.1", + "websocket": "^1.0.28" + }, + "dependencies": { + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + } + } + }, + "@types/bn.js": { + "version": "4.11.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.5.tgz", + "integrity": "sha512-AEAZcIZga0JgVMHNtl1CprA/hXX7/wPt79AgR4XqaDt7jyj3QWYw6LPoOiznPtugDmlubUnAahMs2PFxGcQrng==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "10.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz", + "integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg==" + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" + }, + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bignumber.js": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-6.0.0.tgz", + "integrity": "sha512-x247jIuy60/+FtMRvscqfxtVHQf8AGx2hm9c6btkgC0x/hp9yt+teISNhvF8WlwRkCc5yF2fDECH8SIMe8j+GA==" + }, + "bindings": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", + "integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==" + }, + "bip66": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", + "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "bluebird": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", + "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sha3": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/browserify-sha3/-/browserify-sha3-0.0.4.tgz", + "integrity": "sha1-CGxHuMgjFsnUcCLCYYWVRXbdjiY=", + "requires": { + "js-sha3": "^0.6.1", + "safe-buffer": "^5.1.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, + "buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, + "chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "requires": { + "check-error": "^1.0.2" + } + }, + "chai-bignumber": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chai-bignumber/-/chai-bignumber-3.0.0.tgz", + "integrity": "sha512-SubOtaSI2AILWTWe2j0c6i2yFT/f9J6UBjeVGDuwDiPLkF/U5+/eTWUE3sbCZ1KgcPF6UJsDVYbIxaYA097MQA==", + "dev": true + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "crypto-js": { + "version": "3.1.9-1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz", + "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz", + "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", + "requires": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "requires": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "requires": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==" + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "requires": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", + "requires": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" + } + } + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "defer-to-connect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.0.2.tgz", + "integrity": "sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw==" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" + }, + "dotenv": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.1.0.tgz", + "integrity": "sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA==" + }, + "drbg.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", + "integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=", + "requires": { + "browserify-aes": "^1.0.6", + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.51", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.51.tgz", + "integrity": "sha512-oRpWzM2WcLHVKpnrcyB7OW8j/s67Ba04JCm0WnNv3RiABSvs7mrQlutB8DBv793gKcp0XENR8Il8WxGTlZ73gQ==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "^1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.2.tgz", + "integrity": "sha512-/ZypxQsArlv+KHpGvng52/Iz8by3EQPxhmbuz8yFG89N/caTFBSbcXONDw0aMjy827gQg26XAjP4uXFvnfINmQ==", + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.51" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", + "requires": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + } + } + }, + "eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "ethereumjs-util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz", + "integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "ethjs-util": "^0.1.3", + "keccak": "^1.0.2", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1", + "secp256k1": "^3.0.1" + } + }, + "ethers": { + "version": "4.0.27", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.27.tgz", + "integrity": "sha512-+DXZLP/tyFnXWxqr2fXLT67KlGUfLuvDkHSOtSC9TUVG9OIj6yrG5JPeXRMYo15xkOYwnjgdMKrXp5V94rtjJA==", + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.4", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + } + } + }, + "ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=", + "requires": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + }, + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + } + } + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "requires": { + "min-document": "^2.19.0", + "process": "~0.5.1" + } + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + } + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "http-cache-semantics": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", + "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "requires": { + "punycode": "2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=" + } + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" + }, + "is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=" + }, + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=" + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "js-sha3": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.6.1.tgz", + "integrity": "sha1-W4n3enR3Z5h39YxKB1JAk0sflcA=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "keccak": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz", + "integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==", + "requires": { + "bindings": "^1.2.1", + "inherits": "^2.0.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } + }, + "keccakjs": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/keccakjs/-/keccakjs-0.2.3.tgz", + "integrity": "sha512-BjLkNDcfaZ6l8HBG9tH0tpmDv3sS2mA7FNQxFHpCdzP3Gb2MVruXBSuoM66SnVxKJpAr5dKGdkHD+bDokt8fTg==", + "requires": { + "browserify-sha3": "^0.0.4", + "sha3": "^1.2.2" + } + }, + "keythereum": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/keythereum/-/keythereum-1.0.4.tgz", + "integrity": "sha512-c3gWM0nQ6x5TKAzTOA1yIqn73S8sP9+lR7mc7QS6t509g7C0/CukykxGA6+B+aXI6BIrlSwVh5muPv/I1lD9LA==", + "requires": { + "crypto-browserify": "3.12.0", + "keccak": "1.4.0", + "scrypt": "6.0.3", + "secp256k1": "3.5.0", + "sjcl": "1.0.6", + "uuid": "3.0.0" + }, + "dependencies": { + "secp256k1": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.5.0.tgz", + "integrity": "sha512-e5QIJl8W7Y4tT6LHffVcZAxJjvpgE5Owawv6/XCYPQljE9aP2NFFddQ8OYMKhdLshNu88FfL3qCN3/xYkXGRsA==", + "requires": { + "bindings": "^1.2.1", + "bip66": "^1.1.3", + "bn.js": "^4.11.3", + "create-hash": "^1.1.2", + "drbg.js": "^1.0.1", + "elliptic": "^6.2.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } + } + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + }, + "dependencies": { + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "requires": { + "mkdirp": "*" + } + }, + "mock-fs": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.9.0.tgz", + "integrity": "sha512-aUj0qIniTNxzGqAC61Bvro7YD37tIBnMw3wpClucUVgNBS7r6YQn/M4wuoH7SGteKz4SvC1OBeDsfpG0MYC+1Q==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=" + }, + "nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "normalize-url": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.3.0.tgz", + "integrity": "sha512-0NLtR71o4k6GLP+mr6Ty34c5GA6CMoEsncKJxvQd8NzPxaHRJNnb5gZE8R1XF4CPIS7QPHLJ74IFszwtNVAHVQ==" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=", + "requires": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==" + }, + "oboe": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.4.tgz", + "integrity": "sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY=", + "requires": { + "http-https": "^1.0.0" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "openzeppelin-solidity": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/openzeppelin-solidity/-/openzeppelin-solidity-2.1.3.tgz", + "integrity": "sha512-Zz595xw6w4ZrNU3JJJTlPd8bHFbHv5OkJlkqsM2i+NBs6CzImoHI3dZ+nmhkfR+p52cGXKmayAGbnfdCum1SMg==", + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-asn1": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", + "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-headers": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.2.tgz", + "integrity": "sha512-/LypJhzFmyBIDYP9aDVgeyEb5sQfbfY5mnDq4hVhlQ69js87wXfmEI5V3xI6vvXasqebp0oCytYFLxsBVfCzSg==", + "requires": { + "for-each": "^0.3.3", + "string.prototype.trim": "^1.1.2" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "randomhex": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/randomhex/-/randomhex-0.1.5.tgz", + "integrity": "sha1-us7vmCMpCRQA8qKRLGzQLxCU9YU=" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==", + "dev": true + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rlp": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.3.tgz", + "integrity": "sha512-l6YVrI7+d2vpW6D6rS05x2Xrmq8oW7v3pieZOJKBEdjuTF4Kz/iwk55Zyh1Zaz+KOB2kC8+2jZlp2u9L4tTzCQ==", + "requires": { + "bn.js": "^4.11.1", + "safe-buffer": "^5.1.1" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "scrypt": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/scrypt/-/scrypt-6.0.3.tgz", + "integrity": "sha1-BOAUpWgrU/pQwtXM4WfXGcBthw0=", + "requires": { + "nan": "^2.0.8" + } + }, + "scrypt-js": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz", + "integrity": "sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==" + }, + "scryptsy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==" + }, + "secp256k1": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.6.2.tgz", + "integrity": "sha512-90nYt7yb0LmI4A2jJs1grglkTAXrBwxYAjP9bpeKjvJKOjG2fOeH/YI/lchDMIvjrOasd5QXwvV2jwN168xNng==", + "requires": { + "bindings": "^1.2.1", + "bip66": "^1.1.3", + "bn.js": "^4.11.3", + "create-hash": "^1.1.2", + "drbg.js": "^1.0.1", + "elliptic": "^6.2.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } + }, + "seek-bzip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", + "requires": { + "commander": "~2.8.1" + } + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "requires": { + "body-parser": "^1.16.0", + "cors": "^2.8.1", + "express": "^4.14.0", + "request": "^2.79.0", + "xhr": "^2.3.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "sha3": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-1.2.2.tgz", + "integrity": "sha1-pmxQmN5MJbyIM27ItIF9AFvKe6k=", + "requires": { + "nan": "2.10.0" + }, + "dependencies": { + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "sjcl": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.6.tgz", + "integrity": "sha1-ZBVGKmPMDUIVxJuuydP6DBtTUg8=" + }, + "solc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.5.0.tgz", + "integrity": "sha512-mdLHDl9WeYrN+FIKcMc9PlPfnA9DG9ur5QpCDKcv6VC4RINAsTF4EMuXMZMKoQTvZhtLyJIVH/BZ+KU830Z8Xg==", + "dev": true, + "requires": { + "fs-extra": "^0.30.0", + "keccak": "^1.0.2", + "memorystream": "^0.3.1", + "require-from-string": "^2.0.0", + "semver": "^5.5.0", + "yargs": "^11.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + } + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string.prototype.trim": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", + "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.0", + "function-bind": "^1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "requires": { + "is-natural-number": "^4.0.1" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "swarm-js": { + "version": "0.1.39", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.39.tgz", + "integrity": "sha512-QLMqL2rzF6n5s50BptyD6Oi0R1aWlJC5Y17SRIVXRj6OR1DRIPM7nepvrxxkjA1zNzFz6mUOMjfeqeDaWB7OOg==", + "requires": { + "bluebird": "^3.5.0", + "buffer": "^5.0.5", + "decompress": "^4.0.0", + "eth-lib": "^0.1.26", + "fs-extra": "^4.0.2", + "got": "^7.1.0", + "mime-types": "^2.1.16", + "mkdirp-promise": "^5.0.1", + "mock-fs": "^4.1.0", + "setimmediate": "^1.0.5", + "tar": "^4.0.2", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "eth-lib": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.27.tgz", + "integrity": "sha512-B8czsfkJYzn2UIEMwjc7Mbj+Cy72V+/OXH/tb44LV8jhrjizQJJ325xMOMyk3+ETa6r6oi0jsUY14+om8mQMWA==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "keccakjs": "^0.2.1", + "nano-json-stream-parser": "^0.1.2", + "servify": "^0.1.12", + "ws": "^3.0.0", + "xhr-request-promise": "^0.1.2" + } + } + } + }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "truffle-contract": { + "version": "4.0.31", + "resolved": "https://registry.npmjs.org/truffle-contract/-/truffle-contract-4.0.31.tgz", + "integrity": "sha512-u3q+p1wiX5C2GpnluGx/d2iaJk7bcWshk2/TohiJyA2iQiTfkS7M4n9D9tY3JqpXR8PmD/TrA69RylO0RhITFA==", + "requires": { + "@truffle/blockchain-utils": "^0.0.11", + "@truffle/contract-schema": "^3.0.14", + "@truffle/error": "^0.0.6", + "bignumber.js": "^7.2.1", + "ethers": "^4.0.0-beta.1", + "truffle-interface-adapter": "^0.2.5", + "web3": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "bignumber.js": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + }, + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "scrypt-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz", + "integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=" + }, + "scryptsy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==" + }, + "semver": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==" + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + }, + "web3": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.2.1.tgz", + "integrity": "sha512-nNMzeCK0agb5i/oTWNdQ1aGtwYfXzHottFP2Dz0oGIzavPMGSKyVlr8ibVb1yK5sJBjrWVnTdGaOC2zKDFuFRw==", + "requires": { + "web3-bzz": "1.2.1", + "web3-core": "1.2.1", + "web3-eth": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-shh": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-bzz": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.2.1.tgz", + "integrity": "sha512-LdOO44TuYbGIPfL4ilkuS89GQovxUpmLz6C1UC7VYVVRILeZS740FVB3j9V4P4FHUk1RenaDfKhcntqgVCHtjw==", + "requires": { + "got": "9.6.0", + "swarm-js": "0.1.39", + "underscore": "1.9.1" + } + }, + "web3-core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.2.1.tgz", + "integrity": "sha512-5ODwIqgl8oIg/0+Ai4jsLxkKFWJYE0uLuE1yUKHNVCL4zL6n3rFjRMpKPokd6id6nJCNgeA64KdWQ4XfpnjdMg==", + "requires": { + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-requestmanager": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz", + "integrity": "sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw==", + "requires": { + "underscore": "1.9.1", + "web3-eth-iban": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-method": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.2.1.tgz", + "integrity": "sha512-Ghg2WS23qi6Xj8Od3VCzaImLHseEA7/usvnOItluiIc5cKs00WYWsNy2YRStzU9a2+z8lwQywPYp0nTzR/QXdQ==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-subscriptions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.2.1.tgz", + "integrity": "sha512-nmOwe3NsB8V8UFsY1r+sW6KjdOS68h8nuh7NzlWxBQT/19QSUGiERRTaZXWu5BYvo1EoZRMxCKyCQpSSXLc08g==", + "requires": { + "eventemitter3": "3.1.2", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1" + } + }, + "web3-eth": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.2.1.tgz", + "integrity": "sha512-/2xly4Yry5FW1i+uygPjhfvgUP/MS/Dk+PDqmzp5M88tS86A+j8BzKc23GrlA8sgGs0645cpZK/999LpEF5UdA==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-accounts": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-eth-ens": "1.2.1", + "web3-eth-iban": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-abi": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.2.1.tgz", + "integrity": "sha512-jI/KhU2a/DQPZXHjo2GW0myEljzfiKOn+h1qxK1+Y9OQfTcBMxrQJyH5AP89O6l6NZ1QvNdq99ThAxBFoy5L+g==", + "requires": { + "ethers": "4.0.0-beta.3", + "underscore": "1.9.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "ethers": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.0-beta.3.tgz", + "integrity": "sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog==", + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.3", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + } + } + } + }, + "web3-eth-accounts": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.2.1.tgz", + "integrity": "sha512-26I4qq42STQ8IeKUyur3MdQ1NzrzCqPsmzqpux0j6X/XBD7EjZ+Cs0lhGNkSKH5dI3V8CJasnQ5T1mNKeWB7nQ==", + "requires": { + "any-promise": "1.3.0", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.7", + "scryptsy": "2.1.0", + "semver": "6.2.0", + "underscore": "1.9.1", + "uuid": "3.3.2", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "web3-eth-contract": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.2.1.tgz", + "integrity": "sha512-kYFESbQ3boC9bl2rYVghj7O8UKMiuKaiMkxvRH5cEDHil8V7MGEGZNH0slSdoyeftZVlaWSMqkRP/chfnKND0g==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-ens": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.2.1.tgz", + "integrity": "sha512-lhP1kFhqZr2nnbu3CGIFFrAnNxk2veXpOXBY48Tub37RtobDyHijHgrj+xTh+mFiPokyrapVjpFsbGa+Xzye4Q==", + "requires": { + "eth-ens-namehash": "2.0.8", + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-iban": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz", + "integrity": "sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ==", + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.2.1" + } + }, + "web3-eth-personal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.2.1.tgz", + "integrity": "sha512-RNDVSiaSoY4aIp8+Hc7z+X72H7lMb3fmAChuSBADoEc7DsJrY/d0R5qQDK9g9t2BO8oxgLrLNyBP/9ub2Hc6Bg==", + "requires": { + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-net": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.2.1.tgz", + "integrity": "sha512-Yt1Bs7WgnLESPe0rri/ZoPWzSy55ovioaP35w1KZydrNtQ5Yq4WcrAdhBzcOW7vAkIwrsLQsvA+hrOCy7mNauw==", + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-shh": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.2.1.tgz", + "integrity": "sha512-/3Cl04nza5kuFn25bV3FJWa0s3Vafr5BlT933h26xovQ6HIIz61LmvNQlvX1AhFL+SNJOTcQmK1SM59vcyC8bA==", + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-net": "1.2.1" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "truffle-hdwallet-provider": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/truffle-hdwallet-provider/-/truffle-hdwallet-provider-1.0.17.tgz", + "integrity": "sha512-s6DvSP83jiIAc6TUcpr7Uqnja1+sLGJ8og3X7n41vfyC4OCaKmBtXL5HOHf+SsU3iblOvnbFDgmN6Y1VBL/fsg==", + "requires": { + "any-promise": "^1.3.0", + "bindings": "^1.3.1", + "web3": "1.2.1", + "websocket": "^1.0.28" + }, + "dependencies": { + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + } + } + }, + "truffle-interface-adapter": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/truffle-interface-adapter/-/truffle-interface-adapter-0.2.5.tgz", + "integrity": "sha512-EL39OpP8FcZ99ne1Rno3jImfb92Nectd4iVsZzoEUCBfbwHe7sr0k+i45guoruSoP8nMUE81Mov2s8I5pi6d9Q==", + "requires": { + "bn.js": "^4.11.8", + "ethers": "^4.0.32", + "lodash": "^4.17.13", + "web3": "1.2.1" + }, + "dependencies": { + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "elliptic": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", + "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + } + } + }, + "ethers": { + "version": "4.0.36", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.36.tgz", + "integrity": "sha512-rWdchEhUyXx01GiwexH6Sha97CQ9tJdQwe6FtYKxShC7VEZV41nuKt+lzCQ4OqvQwZK5PcAKaAZv2GDsCH33SA==", + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.4", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + } + }, + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "scryptsy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==" + }, + "semver": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==" + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + }, + "web3": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.2.1.tgz", + "integrity": "sha512-nNMzeCK0agb5i/oTWNdQ1aGtwYfXzHottFP2Dz0oGIzavPMGSKyVlr8ibVb1yK5sJBjrWVnTdGaOC2zKDFuFRw==", + "requires": { + "web3-bzz": "1.2.1", + "web3-core": "1.2.1", + "web3-eth": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-shh": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-bzz": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.2.1.tgz", + "integrity": "sha512-LdOO44TuYbGIPfL4ilkuS89GQovxUpmLz6C1UC7VYVVRILeZS740FVB3j9V4P4FHUk1RenaDfKhcntqgVCHtjw==", + "requires": { + "got": "9.6.0", + "swarm-js": "0.1.39", + "underscore": "1.9.1" + } + }, + "web3-core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.2.1.tgz", + "integrity": "sha512-5ODwIqgl8oIg/0+Ai4jsLxkKFWJYE0uLuE1yUKHNVCL4zL6n3rFjRMpKPokd6id6nJCNgeA64KdWQ4XfpnjdMg==", + "requires": { + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-requestmanager": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz", + "integrity": "sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw==", + "requires": { + "underscore": "1.9.1", + "web3-eth-iban": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-method": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.2.1.tgz", + "integrity": "sha512-Ghg2WS23qi6Xj8Od3VCzaImLHseEA7/usvnOItluiIc5cKs00WYWsNy2YRStzU9a2+z8lwQywPYp0nTzR/QXdQ==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-subscriptions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.2.1.tgz", + "integrity": "sha512-nmOwe3NsB8V8UFsY1r+sW6KjdOS68h8nuh7NzlWxBQT/19QSUGiERRTaZXWu5BYvo1EoZRMxCKyCQpSSXLc08g==", + "requires": { + "eventemitter3": "3.1.2", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1" + } + }, + "web3-eth": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.2.1.tgz", + "integrity": "sha512-/2xly4Yry5FW1i+uygPjhfvgUP/MS/Dk+PDqmzp5M88tS86A+j8BzKc23GrlA8sgGs0645cpZK/999LpEF5UdA==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-accounts": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-eth-ens": "1.2.1", + "web3-eth-iban": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-abi": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.2.1.tgz", + "integrity": "sha512-jI/KhU2a/DQPZXHjo2GW0myEljzfiKOn+h1qxK1+Y9OQfTcBMxrQJyH5AP89O6l6NZ1QvNdq99ThAxBFoy5L+g==", + "requires": { + "ethers": "4.0.0-beta.3", + "underscore": "1.9.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "ethers": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.0-beta.3.tgz", + "integrity": "sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog==", + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.3", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + } + }, + "scrypt-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz", + "integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=" + } + } + }, + "web3-eth-accounts": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.2.1.tgz", + "integrity": "sha512-26I4qq42STQ8IeKUyur3MdQ1NzrzCqPsmzqpux0j6X/XBD7EjZ+Cs0lhGNkSKH5dI3V8CJasnQ5T1mNKeWB7nQ==", + "requires": { + "any-promise": "1.3.0", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.7", + "scryptsy": "2.1.0", + "semver": "6.2.0", + "underscore": "1.9.1", + "uuid": "3.3.2", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "web3-eth-contract": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.2.1.tgz", + "integrity": "sha512-kYFESbQ3boC9bl2rYVghj7O8UKMiuKaiMkxvRH5cEDHil8V7MGEGZNH0slSdoyeftZVlaWSMqkRP/chfnKND0g==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-ens": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.2.1.tgz", + "integrity": "sha512-lhP1kFhqZr2nnbu3CGIFFrAnNxk2veXpOXBY48Tub37RtobDyHijHgrj+xTh+mFiPokyrapVjpFsbGa+Xzye4Q==", + "requires": { + "eth-ens-namehash": "2.0.8", + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-iban": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz", + "integrity": "sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ==", + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.2.1" + } + }, + "web3-eth-personal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.2.1.tgz", + "integrity": "sha512-RNDVSiaSoY4aIp8+Hc7z+X72H7lMb3fmAChuSBADoEc7DsJrY/d0R5qQDK9g9t2BO8oxgLrLNyBP/9ub2Hc6Bg==", + "requires": { + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-net": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.2.1.tgz", + "integrity": "sha512-Yt1Bs7WgnLESPe0rri/ZoPWzSy55ovioaP35w1KZydrNtQ5Yq4WcrAdhBzcOW7vAkIwrsLQsvA+hrOCy7mNauw==", + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-shh": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.2.1.tgz", + "integrity": "sha512-/3Cl04nza5kuFn25bV3FJWa0s3Vafr5BlT933h26xovQ6HIIz61LmvNQlvX1AhFL+SNJOTcQmK1SM59vcyC8bA==", + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-net": "1.2.1" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/type/-/type-1.0.3.tgz", + "integrity": "sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg==" + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "unbzip2-stream": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", + "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "underscore": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "^1.0.1" + } + }, + "url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=" + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" + }, + "utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", + "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "web3": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.2.1.tgz", + "integrity": "sha512-nNMzeCK0agb5i/oTWNdQ1aGtwYfXzHottFP2Dz0oGIzavPMGSKyVlr8ibVb1yK5sJBjrWVnTdGaOC2zKDFuFRw==", + "requires": { + "web3-bzz": "1.2.1", + "web3-core": "1.2.1", + "web3-eth": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-shh": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-bzz": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.2.1.tgz", + "integrity": "sha512-LdOO44TuYbGIPfL4ilkuS89GQovxUpmLz6C1UC7VYVVRILeZS740FVB3j9V4P4FHUk1RenaDfKhcntqgVCHtjw==", + "requires": { + "got": "9.6.0", + "swarm-js": "0.1.39", + "underscore": "1.9.1" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + } + } + }, + "web3-core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.2.1.tgz", + "integrity": "sha512-5ODwIqgl8oIg/0+Ai4jsLxkKFWJYE0uLuE1yUKHNVCL4zL6n3rFjRMpKPokd6id6nJCNgeA64KdWQ4XfpnjdMg==", + "requires": { + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-requestmanager": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-core-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz", + "integrity": "sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw==", + "requires": { + "underscore": "1.9.1", + "web3-eth-iban": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-core-method": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.2.1.tgz", + "integrity": "sha512-Ghg2WS23qi6Xj8Od3VCzaImLHseEA7/usvnOItluiIc5cKs00WYWsNy2YRStzU9a2+z8lwQywPYp0nTzR/QXdQ==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-core-promievent": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.2.1.tgz", + "integrity": "sha512-IVUqgpIKoeOYblwpex4Hye6npM0aMR+kU49VP06secPeN0rHMyhGF0ZGveWBrGvf8WDPI7jhqPBFIC6Jf3Q3zw==", + "requires": { + "any-promise": "1.3.0", + "eventemitter3": "3.1.2" + }, + "dependencies": { + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + } + } + }, + "web3-core-requestmanager": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.2.1.tgz", + "integrity": "sha512-xfknTC69RfYmLKC+83Jz73IC3/sS2ZLhGtX33D4Q5nQ8yc39ElyAolxr9sJQS8kihOcM6u4J+8gyGMqsLcpIBg==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "web3-providers-http": "1.2.1", + "web3-providers-ipc": "1.2.1", + "web3-providers-ws": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-core-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz", + "integrity": "sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw==", + "requires": { + "underscore": "1.9.1", + "web3-eth-iban": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-iban": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz", + "integrity": "sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ==", + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.2.1" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-core-subscriptions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.2.1.tgz", + "integrity": "sha512-nmOwe3NsB8V8UFsY1r+sW6KjdOS68h8nuh7NzlWxBQT/19QSUGiERRTaZXWu5BYvo1EoZRMxCKyCQpSSXLc08g==", + "requires": { + "eventemitter3": "3.1.2", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1" + } + }, + "web3-eth": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.2.1.tgz", + "integrity": "sha512-/2xly4Yry5FW1i+uygPjhfvgUP/MS/Dk+PDqmzp5M88tS86A+j8BzKc23GrlA8sgGs0645cpZK/999LpEF5UdA==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-accounts": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-eth-ens": "1.2.1", + "web3-eth-iban": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-eth-abi": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.2.1.tgz", + "integrity": "sha512-jI/KhU2a/DQPZXHjo2GW0myEljzfiKOn+h1qxK1+Y9OQfTcBMxrQJyH5AP89O6l6NZ1QvNdq99ThAxBFoy5L+g==", + "requires": { + "ethers": "4.0.0-beta.3", + "underscore": "1.9.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "elliptic": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", + "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + } + } + }, + "ethers": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.0-beta.3.tgz", + "integrity": "sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog==", + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.3", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + }, + "scrypt-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz", + "integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=" + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-eth-accounts": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.2.1.tgz", + "integrity": "sha512-26I4qq42STQ8IeKUyur3MdQ1NzrzCqPsmzqpux0j6X/XBD7EjZ+Cs0lhGNkSKH5dI3V8CJasnQ5T1mNKeWB7nQ==", + "requires": { + "any-promise": "1.3.0", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.7", + "scryptsy": "2.1.0", + "semver": "6.2.0", + "underscore": "1.9.1", + "uuid": "3.3.2", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "semver": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-eth-contract": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.2.1.tgz", + "integrity": "sha512-kYFESbQ3boC9bl2rYVghj7O8UKMiuKaiMkxvRH5cEDHil8V7MGEGZNH0slSdoyeftZVlaWSMqkRP/chfnKND0g==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-eth-ens": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.2.1.tgz", + "integrity": "sha512-lhP1kFhqZr2nnbu3CGIFFrAnNxk2veXpOXBY48Tub37RtobDyHijHgrj+xTh+mFiPokyrapVjpFsbGa+Xzye4Q==", + "requires": { + "eth-ens-namehash": "2.0.8", + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-eth-iban": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz", + "integrity": "sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ==", + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-eth-personal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.2.1.tgz", + "integrity": "sha512-RNDVSiaSoY4aIp8+Hc7z+X72H7lMb3fmAChuSBADoEc7DsJrY/d0R5qQDK9g9t2BO8oxgLrLNyBP/9ub2Hc6Bg==", + "requires": { + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-net": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.2.1.tgz", + "integrity": "sha512-Yt1Bs7WgnLESPe0rri/ZoPWzSy55ovioaP35w1KZydrNtQ5Yq4WcrAdhBzcOW7vAkIwrsLQsvA+hrOCy7mNauw==", + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-providers-http": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.2.1.tgz", + "integrity": "sha512-BDtVUVolT9b3CAzeGVA/np1hhn7RPUZ6YYGB/sYky+GjeO311Yoq8SRDUSezU92x8yImSC2B+SMReGhd1zL+bQ==", + "requires": { + "web3-core-helpers": "1.2.1", + "xhr2-cookies": "1.1.0" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-core-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz", + "integrity": "sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw==", + "requires": { + "underscore": "1.9.1", + "web3-eth-iban": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-iban": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz", + "integrity": "sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ==", + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.2.1" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-providers-ipc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.2.1.tgz", + "integrity": "sha512-oPEuOCwxVx8L4CPD0TUdnlOUZwGBSRKScCz/Ws2YHdr9Ium+whm+0NLmOZjkjQp5wovQbyBzNa6zJz1noFRvFA==", + "requires": { + "oboe": "2.1.4", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "web3-core-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz", + "integrity": "sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw==", + "requires": { + "underscore": "1.9.1", + "web3-eth-iban": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-iban": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz", + "integrity": "sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ==", + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.2.1" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, + "web3-providers-ws": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.2.1.tgz", + "integrity": "sha512-oqsQXzu+ejJACVHy864WwIyw+oB21nw/pI65/sD95Zi98+/HQzFfNcIFneF1NC4bVF3VNX4YHTNq2I2o97LAiA==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "websocket": "github:web3-js/WebSocket-Node#polyfill/globalThis" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "web3-core-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz", + "integrity": "sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw==", + "requires": { + "underscore": "1.9.1", + "web3-eth-iban": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-iban": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz", + "integrity": "sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ==", + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.2.1" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + }, + "websocket": { + "version": "github:web3-js/WebSocket-Node#905deb4812572b344f5801f8c9ce8bb02799d82e", + "from": "github:web3-js/WebSocket-Node#polyfill/globalThis", + "requires": { + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "nan": "^2.14.0", + "typedarray-to-buffer": "^3.1.5", + "yaeti": "^0.0.6" + } + } + } + }, + "web3-shh": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.2.1.tgz", + "integrity": "sha512-/3Cl04nza5kuFn25bV3FJWa0s3Vafr5BlT933h26xovQ6HIIz61LmvNQlvX1AhFL+SNJOTcQmK1SM59vcyC8bA==", + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-net": "1.2.1" + } + }, + "web3-utils": { + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.0.0-beta.52.tgz", + "integrity": "sha512-WdHyzPcZu/sOnNrkcOZT20QEX9FhwD9OJJXENojQNvMK2a1xo3n8JWBcC2gzAGwsa0Aah6z2B3Xwa1P//8FaoA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "@types/bn.js": "^4.11.4", + "@types/node": "^10.12.18", + "bn.js": "4.11.8", + "eth-lib": "0.2.8", + "ethjs-unit": "^0.1.6", + "lodash": "^4.17.11", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "utf8": "2.1.1" + }, + "dependencies": { + "utf8": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.1.tgz", + "integrity": "sha1-LgHbAvfY0JRPdxBPFgnrDDBM92g=", + "dev": true + } + } + }, + "websocket": { + "version": "1.0.30", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.30.tgz", + "integrity": "sha512-aO6klgaTdSMkhfl5VVJzD5fm+Srhh5jLYbS15+OiI1sN6h/RU/XW6WN9J1uVIpUKNmsTvT3Hs35XAFjn9NMfOw==", + "requires": { + "debug": "^2.2.0", + "nan": "^2.14.0", + "typedarray-to-buffer": "^3.1.5", + "yaeti": "^0.0.6" + }, + "dependencies": { + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + } + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "xhr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", + "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", + "requires": { + "global": "~4.3.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "requires": { + "buffer-to-arraybuffer": "^0.0.5", + "object-assign": "^4.1.1", + "query-string": "^5.0.1", + "simple-get": "^2.7.0", + "timed-out": "^4.0.1", + "url-set-query": "^1.0.0", + "xhr": "^2.0.4" + } + }, + "xhr-request-promise": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.2.tgz", + "integrity": "sha1-NDxE0e53JrhkgGloLQ+EDIO0Jh0=", + "requires": { + "xhr-request": "^1.0.1" + } + }, + "xhr2-cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", + "integrity": "sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg=", + "requires": { + "cookiejar": "^2.1.1" + } + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + }, + "yargs": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + } + }, + "yargs-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/testnet-contracts/package.json b/testnet-contracts/package.json new file mode 100644 index 0000000..074e019 --- /dev/null +++ b/testnet-contracts/package.json @@ -0,0 +1,43 @@ +{ + "name": "testnet-contracts", + "version": "1.1.0", + "description": "Dependencies and scripts for Peggy smart contracts", + "main": "truffle.js", + "directories": { + "test": "test" + }, + "author": "Denali Marsh", + "license": "ISC", + "dependencies": { + "@truffle/hdwallet-provider": "^1.0.18", + "bignumber.js": "^6.0.0", + "bluebird": "^3.5.1", + "cross-env": "5.0.5", + "dotenv": "^8.1.0", + "eth-lib": "^0.2.8", + "ethereumjs-util": "^5.2.0", + "keccak": "^1.4.0", + "lodash": "^4.17.10", + "truffle-contract": "^4.0.31", + "truffle-hdwallet-provider": "^1.0.17", + "utf8": "^3.0.0" + }, + "devDependencies": { + "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", + "chai-bignumber": "^3.0.0", + "openzeppelin-solidity": "^2.1.3", + "web3": "^1.2.1", + "web3-utils": "^1.0.0-beta.52" + }, + "scripts": { + "develop": "truffle develop", + "migrate": "truffle migrate --reset", + "peggy:address": "truffle exec scripts/getBridgeContractAddress.js", + "peggy:lock": "truffle exec scripts/sendLockTx.js", + "peggy:abi": "node scripts/formatAbi.js", + "token:address": "truffle exec scripts/getTokenContractAddress.js", + "token:mint": "truffle exec scripts/mintTestTokens.js", + "token:approve": "truffle exec scripts/sendApproveTx.js" + } +} diff --git a/testnet-contracts/scripts/formatAbi.js b/testnet-contracts/scripts/formatAbi.js new file mode 100644 index 0000000..5c48977 --- /dev/null +++ b/testnet-contracts/scripts/formatAbi.js @@ -0,0 +1,18 @@ +var fs = require("fs"); + +/******************************************* + *** Constants + ******************************************/ +const IN_PATH = "../build/contracts/Peggy.json"; +const OUT_PATH = "../cmd/dawnrelayer/contract/abi/Peggy.abi"; + +/******************************************* + *** Reading and writing + ******************************************/ +const PeggyContract = require(IN_PATH); + +fs.writeFile(OUT_PATH, JSON.stringify(PeggyContract.abi), "utf8", function( + err +) { + if (err) return console.log(err); +}); diff --git a/testnet-contracts/scripts/getBridgeContractAddress.js b/testnet-contracts/scripts/getBridgeContractAddress.js new file mode 100644 index 0000000..c79caf5 --- /dev/null +++ b/testnet-contracts/scripts/getBridgeContractAddress.js @@ -0,0 +1,44 @@ +module.exports = async () => { + /******************************************* + *** Set up + ******************************************/ + require("dotenv").config(); + const Web3 = require("web3"); + const HDWalletProvider = require("@truffle/hdwallet-provider"); + + // Contract abstraction + const truffleContract = require("truffle-contract"); + const contract = truffleContract(require("../build/contracts/Peggy.json")); + + /******************************************* + *** Constants + ******************************************/ + const NETWORK_ROPSTEN = + process.argv[4] === "--network" && process.argv[5] === "ropsten"; + + /******************************************* + *** Web3 provider + *** Set contract provider based on --network flag + ******************************************/ + let provider; + if (NETWORK_ROPSTEN) { + provider = new HDWalletProvider( + process.env.MNEMONIC, + "https://ropsten.infura.io/v3/".concat(process.env.INFURA_PROJECT_ID) + ); + } else { + provider = new Web3.providers.HttpProvider(process.env.LOCAL_PROVIDER); + } + + const web3 = new Web3(provider); + contract.setProvider(web3.currentProvider); + + /******************************************* + *** Contract interaction + ******************************************/ + const address = await contract.deployed().then(function(instance) { + return instance.address; + }); + + return console.log("Bridge contract address: ", address); +}; diff --git a/testnet-contracts/scripts/getDeployedAddress.js b/testnet-contracts/scripts/getDeployedAddress.js new file mode 100644 index 0000000..908a263 --- /dev/null +++ b/testnet-contracts/scripts/getDeployedAddress.js @@ -0,0 +1,44 @@ +module.exports = async () => { + /******************************************* + *** Set up + ******************************************/ + require("dotenv").config(); + const Web3 = require("web3"); + const HDWalletProvider = require("@truffle/hdwallet-provider"); + + // Contract abstraction + const truffleContract = require("truffle-contract"); + const contract = truffleContract(require("../build/contracts/Peggy.json")); + + /******************************************* + *** Constants + ******************************************/ + const NETWORK_ROPSTEN = + process.argv[4] === "--network" && process.argv[5] === "ropsten"; + + /******************************************* + *** Web3 provider + *** Set contract provider based on --network flag + ******************************************/ + let provider; + if (NETWORK_ROPSTEN) { + provider = new HDWalletProvider( + process.env.MNEMONIC, + "https://ropsten.infura.io/v3".concat(process.env.INFURA_PROJECT_ID) + ); + } else { + provider = new Web3.providers.HttpProvider(process.env.LOCAL_PROVIDER); + } + + const web3 = new Web3(provider); + contract.setProvider(web3.currentProvider); + + /******************************************* + *** Contract interaction + ******************************************/ + const address = await contract.deployed().then(function(instance) { + return instance.address; + }); + + return console.log("Deployed contract address: ", address); + }; \ No newline at end of file diff --git a/testnet-contracts/scripts/getTokenContractAddress.js b/testnet-contracts/scripts/getTokenContractAddress.js new file mode 100644 index 0000000..d653596 --- /dev/null +++ b/testnet-contracts/scripts/getTokenContractAddress.js @@ -0,0 +1,46 @@ +module.exports = async () => { + /******************************************* + *** Set up + ******************************************/ + require("dotenv").config(); + const Web3 = require("web3"); + const HDWalletProvider = require("@truffle/hdwallet-provider"); + + // Contract abstraction + const truffleContract = require("truffle-contract"); + const contract = truffleContract( + require("../build/contracts/TestToken.json") + ); + + /******************************************* + *** Constants + ******************************************/ + const NETWORK_ROPSTEN = + process.argv[4] === "--network" && process.argv[5] === "ropsten"; + + /******************************************* + *** Web3 provider + *** Set contract provider based on --network flag + ******************************************/ + let provider; + if (NETWORK_ROPSTEN) { + provider = new HDWalletProvider( + process.env.MNEMONIC, + "https://ropsten.infura.io/v3/".concat(process.env.INFURA_PROJECT_ID) + ); + } else { + provider = new Web3.providers.HttpProvider(process.env.LOCAL_PROVIDER); + } + + const web3 = new Web3(provider); + contract.setProvider(web3.currentProvider); + + /******************************************* + *** Contract interaction + ******************************************/ + const address = await contract.deployed().then(function(instance) { + return instance.address; + }); + + return console.log("Token contract address: ", address); +}; diff --git a/testnet-contracts/scripts/mintTestTokens.js b/testnet-contracts/scripts/mintTestTokens.js new file mode 100644 index 0000000..65e10e2 --- /dev/null +++ b/testnet-contracts/scripts/mintTestTokens.js @@ -0,0 +1,79 @@ +module.exports = async () => { + /******************************************* + *** Set up + ******************************************/ + const Web3 = require("web3"); + const HDWalletProvider = require("@truffle/hdwallet-provider"); + + // Contract abstraction + const truffleContract = require("truffle-contract"); + + const tokenContract = truffleContract( + require("../build/contracts/TestToken.json") + ); + + /******************************************* + *** Constants + ******************************************/ + // Config values + const NETWORK_ROPSTEN = + process.argv[4] === "--network" && process.argv[5] === "ropsten"; + const NUM_ARGS = process.argv.length - 4; + + // Mint transaction parameters + const TOKEN_AMOUNT = 1000; + + /******************************************* + *** Command line argument error checking + *** + *** truffle exec lacks support for dynamic command line arguments: + *** https://github.com/trufflesuite/truffle/issues/889#issuecomment-522581580 + ******************************************/ + if (NETWORK_ROPSTEN) { + return console.error( + "Error: token minting on Ropsten network is not supported at this time" + ); + } else { + if (NUM_ARGS !== 0) { + return console.error( + "Error: custom parameters for token minting are not supported at this time." + ); + } + } + + /******************************************* + *** Web3 provider + ******************************************/ + const provider = new Web3.providers.HttpProvider(process.env.LOCAL_PROVIDER); + const web3 = new Web3(provider); + tokenContract.setProvider(web3.currentProvider); + + /******************************************* + *** Contract interaction + ******************************************/ + // Get current accounts + const accounts = await web3.eth.getAccounts(); + + // Send mint transaction + const { logs } = await tokenContract.deployed().then(function(instance) { + return instance.mint(accounts[1], TOKEN_AMOUNT, { + from: accounts[0], + value: 0, + gas: 300000 // 300,000 Gwei + }); + }); + + // Get event logs + const event = logs.find(e => e.event === "Transfer"); + + // Parse event fields + const transferEvent = { + from: event.args.from, + to: event.args.to, + value: Number(event.args.value) + }; + + console.log(transferEvent); + + return; +}; diff --git a/testnet-contracts/scripts/sendApproveTx.js b/testnet-contracts/scripts/sendApproveTx.js new file mode 100644 index 0000000..eee16e7 --- /dev/null +++ b/testnet-contracts/scripts/sendApproveTx.js @@ -0,0 +1,117 @@ +module.exports = async () => { + /******************************************* + *** Set up + ******************************************/ + const Web3 = require("web3"); + const HDWalletProvider = require("@truffle/hdwallet-provider"); + + // Contract abstraction + const truffleContract = require("truffle-contract"); + const bridgeContract = truffleContract( + require("../build/contracts/Peggy.json") + ); + const tokenContract = truffleContract( + require("../build/contracts/TestToken.json") + ); + + /******************************************* + *** Constants + ******************************************/ + // Config values + const NETWORK_ROPSTEN = + process.argv[4] === "--network" && process.argv[5] === "ropsten"; + const DEFAULT_PARAMS = + process.argv[4] === "--default" || + (NETWORK_ROPSTEN && process.argv[6] === "--default"); + const NUM_ARGS = process.argv.length - 4; + + // Default transaction parameters + const DEFAULT_TOKEN_AMOUNT = 100; + + /******************************************* + *** Command line argument error checking + *** + *** truffle exec lacks support for dynamic command line arguments: + *** https://github.com/trufflesuite/truffle/issues/889#issuecomment-522581580 + ******************************************/ + if (NETWORK_ROPSTEN) { + if (NUM_ARGS !== 3) { + return console.error( + "Error: Must specify token amount if using the Ropsten network." + ); + } + } else { + if (NUM_ARGS !== 1) { + return console.error("Error: Must specify token amount or --default."); + } + } + + /******************************************* + *** Approve transaction parameters + ******************************************/ + let tokenAmount; + + if (NETWORK_ROPSTEN) { + tokenAmount = process.argv[6]; + } else { + if (!DEFAULT_PARAMS) { + tokenAmount = process.argv[4]; + } else { + tokenAmount = DEFAULT_TOKEN_AMOUNT; + } + } + + /******************************************* + *** Web3 provider + *** Set contract provider based on --network flag + ******************************************/ + let provider; + if (NETWORK_ROPSTEN) { + provider = new HDWalletProvider( + process.env.MNEMONIC, + "https://ropsten.infura.io/v3/".concat(process.env.INFURA_PROJECT_ID) + ); + } else { + provider = new Web3.providers.HttpProvider(process.env.LOCAL_PROVIDER); + } + + const web3 = new Web3(provider); + + bridgeContract.setProvider(web3.currentProvider); + tokenContract.setProvider(web3.currentProvider); + + /******************************************* + *** Contract interaction + ******************************************/ + // Get current accounts + const accounts = await web3.eth.getAccounts(); + + const bridgeContractAddress = await bridgeContract + .deployed() + .then(function(instance) { + return instance.address; + }); + + // Send lock transaction + const { logs } = await tokenContract.deployed().then(function(instance) { + return instance.approve(bridgeContractAddress, tokenAmount, { + from: accounts[1], + value: 0, + gas: 300000 // 300,000 Gwei + }); + }); + + // Get event logs + const event = logs.find(e => e.event === "Approval"); + + // Parse event fields + const approvalEvent = { + owner: event.args.owner, + spender: event.args.spender, + value: Number(event.args.value) + }; + + console.log(approvalEvent); + + return; +}; diff --git a/testnet-contracts/scripts/sendLockTx.js b/testnet-contracts/scripts/sendLockTx.js new file mode 100644 index 0000000..1696537 --- /dev/null +++ b/testnet-contracts/scripts/sendLockTx.js @@ -0,0 +1,136 @@ +module.exports = async () => { + /******************************************* + *** Set up + ******************************************/ + const Web3 = require("web3"); + const HDWalletProvider = require("@truffle/hdwallet-provider"); + + // Contract abstraction + const truffleContract = require("truffle-contract"); + const contract = truffleContract(require("../build/contracts/Peggy.json")); + + /******************************************* + *** Constants + ******************************************/ + // Lock transaction default params + const DEFAULT_COSMOS_RECIPIENT = + "0x636f736d6f7331706a74677530766175326d35326e72796b64707a74727438383761796b756530687137646668"; + const DEFAULT_ETH_DENOM = "0x0000000000000000000000000000000000000000"; + const DEFAULT_AMOUNT = 10; + + // Config values + const NETWORK_ROPSTEN = + process.argv[4] === "--network" && process.argv[5] === "ropsten"; + const DEFAULT_PARAMS = + process.argv[4] === "--default" || + (NETWORK_ROPSTEN && process.argv[6] === "--default"); + const NUM_ARGS = process.argv.length - 4; + + /******************************************* + *** Command line argument error checking + *** + *** truffle exec lacks support for dynamic command line arguments: + *** https://github.com/trufflesuite/truffle/issues/889#issuecomment-522581580 + ******************************************/ + if (NETWORK_ROPSTEN && DEFAULT_PARAMS) { + if (NUM_ARGS !== 3) { + return console.error( + "Error: custom parameters are invalid on --default." + ); + } + } else if (NETWORK_ROPSTEN) { + if (NUM_ARGS !== 2 && NUM_ARGS !== 5) { + return console.error( + "Error: invalid number of parameters, please try again." + ); + } + } else if (DEFAULT_PARAMS) { + if (NUM_ARGS !== 1) { + return console.error( + "Error: custom parameters are invalid on --default." + ); + } + } else { + if (NUM_ARGS !== 3) { + return console.error( + "Error: must specify recipient address, token address, and amount." + ); + } + } + + /******************************************* + *** Lock transaction parameters + ******************************************/ + let cosmosRecipient = DEFAULT_COSMOS_RECIPIENT; + let coinDenom = DEFAULT_ETH_DENOM; + let amount = DEFAULT_AMOUNT; + + // TODO: Input validation + if (!DEFAULT_PARAMS) { + if (NETWORK_ROPSTEN) { + cosmosRecipient = process.argv[6]; + coinDenom = process.argv[7]; + amount = parseInt(process.argv[8], 10); + } else { + cosmosRecipient = process.argv[4]; + coinDenom = process.argv[5]; + amount = parseInt(process.argv[6], 10); + } + } + + /******************************************* + *** Web3 provider + *** Set contract provider based on --network flag + ******************************************/ + let provider; + if (NETWORK_ROPSTEN) { + provider = new HDWalletProvider( + process.env.MNEMONIC, + "https://ropsten.infura.io/v3/".concat(process.env.INFURA_PROJECT_ID) + ); + } else { + provider = new Web3.providers.HttpProvider(process.env.LOCAL_PROVIDER); + } + + const web3 = new Web3(provider); + contract.setProvider(web3.currentProvider); + + /******************************************* + *** Contract interaction + ******************************************/ + // Get current accounts + const accounts = await web3.eth.getAccounts(); + console.log("accounts",accounts); + console.log("cosmosRecipient",cosmosRecipient); + console.log("coinDenom",coinDenom); + console.log("amount",amount); + + // Send lock transaction + console.log("Connecting to contract...."); + const { logs } = await contract.deployed().then(function(instance) { + console.log("Connected to contract, sending lock..."); + return instance.lock(cosmosRecipient, coinDenom, amount, { + from: accounts[0], + value: coinDenom === DEFAULT_ETH_DENOM ? amount : 0, + gas: 300000 // 300,000 Gwei + }); + }); + + console.log("Sent lock..."); + + // Get event logs + const event = logs.find(e => e.event === "LogLock"); + + // Parse event fields + const lockEvent = { + id: event.args._id, + to: event.args._to, + token: event.args._token, + value: Number(event.args._value), + nonce: Number(event.args._nonce) + }; + + console.log(lockEvent); + + return; +}; \ No newline at end of file diff --git a/testnet-contracts/test/test_peggy.js b/testnet-contracts/test/test_peggy.js new file mode 100644 index 0000000..1f69b4b --- /dev/null +++ b/testnet-contracts/test/test_peggy.js @@ -0,0 +1,471 @@ +const Peggy = artifacts.require("Peggy"); +const TestToken = artifacts.require("TestToken"); + +const Web3Utils = require("web3-utils"); +const EVMRevert = "revert"; +const BigNumber = web3.BigNumber; + +require("chai") + .use(require("chai-as-promised")) + .use(require("chai-bignumber")(BigNumber)) + .should(); + +contract("Peggy", function(accounts) { + const provider = accounts[0]; + + const userOne = accounts[1]; + const userTwo = accounts[2]; + const userThree = accounts[3]; + + describe("Peggy smart contract deployment", function() { + beforeEach(async function() { + this.peggy = await Peggy.new(); + }); + + it("should deploy the peggy contract with the correct parameters", async function() { + this.peggy.should.exist; + + const peggyProvider = await this.peggy.provider(); + peggyProvider.should.be.equal(provider); + }); + }); + + describe("Locking funds", function() { + beforeEach(async function() { + this.peggy = await Peggy.new(); + this.ethereumToken = "0x0000000000000000000000000000000000000000"; + this.weiAmount = web3.utils.toWei("0.25", "ether"); + this.recipient = web3.utils.utf8ToHex( + "985cfkop78sru7gfud4wce83kuc9rmw89rqtzmy" + ); + this.gasForLock = 300000; // 300,000 Gwei + + //Load user account with tokens for testing + this.token = await TestToken.new(); + await this.token.mint(userOne, 1000, { + from: provider + }).should.be.fulfilled; + }); + + it("should not allow a user to send ethereum directly to the contract", async function() { + await this.peggy + .send(this.weiAmount, { from: userOne }) + .should.be.rejectedWith(EVMRevert); + }); + + it("should not allow users to lock funds if the contract is paused", async function() { + //Confirm that the processor contract is paused + await this.peggy.pauseLocking({ from: provider }).should.be.fulfilled; + + const depositStatus = await this.peggy.active(); + depositStatus.should.be.equal(false); + + //User attempt to lock ethereum/erc20 + await this.token.approve(this.peggy.address, 100, { + from: userOne + }).should.be.fulfilled; + await this.peggy + .lock(this.recipient, this.token.address, 100, { + from: userOne, + gas: this.gasForLock + }) + .should.be.rejectedWith(EVMRevert); + }); + + it("should allow users to lock erc20 tokens if it meets validation requirements", async function() { + //Confirm that the contract is active + const depositStatus = await this.peggy.active(); + depositStatus.should.be.equal(true); + + await this.token.approve(this.peggy.address, 100, { + from: userOne + }).should.be.fulfilled; + await this.peggy.lock(this.recipient, this.token.address, 100, { + from: userOne, + gas: this.gasForLock + }).should.be.fulfilled; + + //Get the contract and user token balance after the rescue + const peggyBalance = Number( + await this.token.balanceOf(this.peggy.address) + ); + const userBalance = Number(await this.token.balanceOf(userOne)); + + //Confirm that the tokens have been locked + peggyBalance.should.be.bignumber.equal(100); + userBalance.should.be.bignumber.equal(900); + }); + + it("should allow users to lock ethereum if it meets validation requirements", async function() { + //Confirm that the contract is active + const depositStatus = await this.peggy.active(); + depositStatus.should.be.equal(true); + + await this.peggy.lock( + this.recipient, + this.ethereumToken, + this.weiAmount, + { from: userOne, value: this.weiAmount, gas: this.gasForLock } + ).should.be.fulfilled; + + const contractBalanceWei = await web3.eth.getBalance(this.peggy.address); + const contractBalance = web3.utils.fromWei(contractBalanceWei, "ether"); + + contractBalance.should.be.bignumber.equal( + web3.utils.fromWei(this.weiAmount, "ether") + ); + }); + + it("should emit an event upon lock containing the new ecrow's information", async function() { + const userBalance = Number(await this.token.balanceOf(userOne)); + userBalance.should.be.bignumber.equal(1000); + + await this.token.approve(this.peggy.address, 100, { + from: userOne + }).should.be.fulfilled; + + //Get the event logs of a token deposit + const expectedId = await this.peggy.lock.call( + this.recipient, + this.token.address, + 100, + { from: userOne, gas: this.gasForLock } + ).should.be.fulfilled; + const { logs } = await this.peggy.lock( + this.recipient, + this.token.address, + 100, + { from: userOne, gas: this.gasForLock } + ).should.be.fulfilled; + const event = logs.find(e => e.event === "LogLock"); + + event.args._id.should.be.equal(expectedId); + event.args._to.should.be.equal(this.recipient); + event.args._token.should.be.equal(this.token.address); + Number(event.args._value).should.be.bignumber.equal(100); + Number(event.args._nonce).should.be.bignumber.equal(1); + }); + }); + + describe("Access to information", function() { + const cosmosAddr = "77m5cfkop78sruko3ud4wjp83kuc9rmw15rqtzlp"; + + beforeEach(async function() { + this.peggy = await Peggy.new(); + this.ethereumToken = "0x0000000000000000000000000000000000000000"; + this.weiAmount = web3.utils.toWei("0.25", "ether"); + this.recipient = web3.utils.utf8ToHex(cosmosAddr); + this.gasForLock = 300000; // 300,000 Gwei + + // Load user account with tokens for testing + this.token = await TestToken.new(); + await this.token.mint(userOne, 100, { + from: provider + }).should.be.fulfilled; + + await this.token.approve(this.peggy.address, 100, { + from: userOne + }).should.be.fulfilled; + this.itemId = await this.peggy.lock.call( + this.recipient, + this.token.address, + 100, + { from: userOne, gas: this.gasForLock } + ).should.be.fulfilled; + await this.peggy.lock(this.recipient, this.token.address, 100, { + from: userOne, + gas: this.gasForLock + }).should.be.fulfilled; + }); + + it("should allow for public viewing of a locked item's information", async function() { + //Get the item struct's information + const itemInfo = await this.peggy.viewItem(this.itemId, { + from: provider + }).should.be.fulfilled; + + //Parse each attribute + const sender = itemInfo[0]; + const receiver = itemInfo[1]; + const token = itemInfo[2]; + const amount = Number(itemInfo[3]); + const nonce = Number(itemInfo[4]); + + //Confirm that each attribute is correct + sender.should.be.equal(userOne); + receiver.should.be.equal(this.recipient); + token.should.be.equal(this.token.address); + amount.should.be.bignumber.equal(100); + nonce.should.be.bignumber.equal(1); + }); + + it("should correctly encode and decode the intended recipient's address", async function() { + //Get the item struct's information + const itemInfo = await this.peggy.viewItem(this.itemId, { + from: provider + }).should.be.fulfilled; + + //Decode the stored recipient's address and compare it the original + const receiver = web3.utils.hexToUtf8(itemInfo[1]); + receiver.should.be.equal(cosmosAddr); + }); + }); + + describe("Unlocking of itemized ethereum", function() { + beforeEach(async function() { + this.peggy = await Peggy.new(); + this.ethereumToken = "0x0000000000000000000000000000000000000000"; + this.weiAmount = web3.utils.toWei("0.25", "ether"); + this.recipient = web3.utils.utf8ToHex( + "cosmosaccaddr985cfkop78sru7gfud4wce83kuc9rmw89rqtzmy" + ); + this.gasForLock = 300000; // 300,000 Gwei + + // Load user account with tokens for testing + this.token = await TestToken.new(); + await this.token.mint(userOne, 100, { + from: provider + }).should.be.fulfilled; + + await this.token.approve(this.peggy.address, 100, { + from: userOne + }).should.be.fulfilled; + this.itemId = await this.peggy.lock.call( + this.recipient, + this.token.address, + 100, + { from: userOne, gas: this.gasForLock } + ).should.be.fulfilled; + await this.peggy.lock(this.recipient, this.token.address, 100, { + from: userOne, + gas: this.gasForLock + }).should.be.fulfilled; + }); + + it("should allow the provider to unlock itemized ethereum", async function() { + const id = await this.peggy.lock.call( + this.recipient, + this.ethereumToken, + this.weiAmount, + { from: userOne, value: this.weiAmount, gas: this.gasForLock } + ).should.be.fulfilled; + await this.peggy.lock( + this.recipient, + this.ethereumToken, + this.weiAmount, + { from: userOne, value: this.weiAmount, gas: this.gasForLock } + ).should.be.fulfilled; + await this.peggy.unlock(id, { + from: provider, + gas: this.gasForLock + }).should.be.fulfilled; + }); + + it("should allow the provider to unlock itemized erc20 tokens", async function() { + await this.peggy.unlock(this.itemId, { + from: provider, + gas: this.gasForLock + }).should.be.fulfilled; + }); + + it("should correctly transfer funds to intended recipient upon unlock", async function() { + //Confirm that the tokens are locked on the contract + const beforePeggyBalance = Number( + await this.token.balanceOf(this.peggy.address) + ); + const beforeUserBalance = Number(await this.token.balanceOf(userOne)); + + beforePeggyBalance.should.be.bignumber.equal(100); + beforeUserBalance.should.be.bignumber.equal(0); + + await this.peggy.unlock(this.itemId, { + from: provider, + gas: this.gasForLock + }); + + //Confirm that the tokens have been unlocked and transfered + const afterPeggyBalance = Number( + await this.token.balanceOf(this.peggy.address) + ); + const afterUserBalance = Number(await this.token.balanceOf(userOne)); + + afterPeggyBalance.should.be.bignumber.equal(0); + afterUserBalance.should.be.bignumber.equal(100); + }); + + it("should emit an event upon unlock containing the ecrow's recipient, token, amount, and nonce", async function() { + //Get the event logs of an unlock + const { logs } = await this.peggy.unlock(this.itemId, { + from: provider, + gas: this.gasForLock + }); + const event = logs.find(e => e.event === "LogUnlock"); + + event.args._to.should.be.equal(userOne); + event.args._token.should.be.equal(this.token.address); + Number(event.args._value).should.be.bignumber.equal(100); + Number(event.args._nonce).should.be.bignumber.equal(1); + }); + + it("should update item lock statusit has been unlocked", async function() { + const startingLockStatus = await this.peggy.getStatus(this.itemId); + startingLockStatus.should.be.equal(true); + + await this.peggy.unlock(this.itemId, { + from: provider, + gas: this.gasForLock + }); + + const endingLockStatus = await this.peggy.getStatus(this.itemId); + endingLockStatus.should.be.equal(false); + }); + }); + + describe("Withdrawal of items by sender", function() { + beforeEach(async function() { + this.peggy = await Peggy.new(); + this.zeroxToken = "0xE41d2489571d322189246DaFA5fbde1F4699F498"; + this.ethereumToken = "0x0000000000000000000000000000000000000000"; + this.weiAmount = web3.utils.toWei("0.25", "ether"); + this.recipient = web3.utils.utf8ToHex( + "cosmosaccaddr985cfkop78sru7gfud4wce83kuc9rmw89rqtzmy" + ); + this.gasForLock = 300000; // 300,000 Gwei + + // Load user account with tokens for testing + this.token = await TestToken.new(); + await this.token.mint(userOne, 100, { + from: provider + }).should.be.fulfilled; + + await this.token.approve(this.peggy.address, 100, { + from: userOne + }).should.be.fulfilled; + this.itemId = await this.peggy.lock.call( + this.recipient, + this.token.address, + 100, + { from: userOne, gas: this.gasForLock } + ).should.be.fulfilled; + await this.peggy.lock(this.recipient, this.token.address, 100, { + from: userOne, + gas: this.gasForLock + }).should.be.fulfilled; + + //Lower and upper bound to allow results in range of 0.01% + this.lowerBound = 0.9999; + this.upperBound = 1.0001; + + //Set up gas constants + this.gasForLock = 500000; //500,000 Gwei + this.gasForWithdraw = 200000; //200,000 Gwei + this.gasPrice = 200000000000; //From truffle config + }); + + it("should not allow non-senders to withdraw other's items", async function() { + await this.peggy + .withdraw(this.itemId, { from: userThree, gas: this.gasForLock }) + .should.be.rejectedWith(EVMRevert); + }); + + it("should allow senders to withdraw their own items", async function() { + await this.peggy.withdraw(this.itemId, { + from: userOne, + gas: this.gasForLock + }).should.be.fulfilled; + }); + + it("should return erc20 to user upon withdrawal of itemized funds", async function() { + //Confirm that the tokens are locked on the contract + const beforePeggyBalance = Number( + await this.token.balanceOf(this.peggy.address) + ); + const beforeUserBalance = Number(await this.token.balanceOf(userOne)); + + beforePeggyBalance.should.be.bignumber.equal(100); + beforeUserBalance.should.be.bignumber.equal(0); + + await this.peggy.withdraw(this.itemId, { + from: userOne, + gas: this.gasForWithdraw + }).should.be.fulfilled; + + //Confirm that the tokens have been unlocked and transfered + const afterPeggyBalance = Number( + await this.token.balanceOf(this.peggy.address) + ); + const afterUserBalance = Number(await this.token.balanceOf(userOne)); + + afterPeggyBalance.should.be.bignumber.equal(0); + afterUserBalance.should.be.bignumber.equal(100); + }); + + it("should return ethereum to user upon withdrawal of itemized funds", async function() { + const id = await this.peggy.lock.call( + this.recipient, + this.ethereumToken, + this.weiAmount, + { from: userTwo, value: this.weiAmount, gas: this.gasForLock } + ).should.be.fulfilled; + await this.peggy.lock( + this.recipient, + this.ethereumToken, + this.weiAmount, + { from: userTwo, value: this.weiAmount, gas: this.gasForLock } + ).should.be.fulfilled; + + //Set up accepted withdrawal balance bounds + const lowestAcceptedBalance = this.weiAmount * this.lowerBound; + const highestAcceptedBalance = this.weiAmount * this.upperBound; + + //Get prior balances of user and peggy contract + const beforeUserBalance = Number(await web3.eth.getBalance(userTwo)); + const beforeContractBalance = Number( + await web3.eth.getBalance(this.peggy.address) + ); + + //Send withdrawal transaction and save gas expenditure + const txHash = await this.peggy.withdraw(id, { + from: userTwo, + gas: this.gasForWithdraw + }).should.be.fulfilled; + const gasCost = this.gasPrice * txHash.receipt.gasUsed; + + //Get balances after withdrawal + const afterUserBalance = Number(await web3.eth.getBalance(userTwo)); + const afterContractBalance = Number( + await web3.eth.getBalance(this.peggy.address) + ); + + //Check user's balances to confirm withdrawal + const userDifference = afterUserBalance - beforeUserBalance + gasCost; + userDifference.should.be.bignumber.within( + lowestAcceptedBalance, + highestAcceptedBalance + ); + + //Check contracts's balances to confirm withdrawal + const contractDifference = beforeContractBalance - afterContractBalance; + contractDifference.should.be.bignumber.within( + lowestAcceptedBalance, + highestAcceptedBalance + ); + }); + + it("should emit an event upon user withdrawal containing the item's id", async function() { + //Get the event logs of a token withdrawl + const { logs } = await this.peggy.withdraw(this.itemId, { + from: userOne, + gas: this.gasForLock + }).should.be.fulfilled; + const event = logs.find(e => e.event === "LogWithdraw"); + + //Check the event's parameters + event.args._id.should.be.bignumber.equal(this.itemId); + event.args._to.should.be.equal(userOne); + event.args._token.should.be.equal(this.token.address); + Number(event.args._value).should.be.bignumber.equal(100); + Number(event.args._nonce).should.be.bignumber.equal(1); + }); + }); +}); diff --git a/testnet-contracts/test/test_processor.js b/testnet-contracts/test/test_processor.js new file mode 100644 index 0000000..b2f213a --- /dev/null +++ b/testnet-contracts/test/test_processor.js @@ -0,0 +1,257 @@ +const TestProcessor = artifacts.require("TestProcessor"); +const TestToken = artifacts.require("TestToken"); + +const Web3Utils = require("web3-utils"); +const EVMRevert = "revert"; +const BigNumber = web3.BigNumber; + +require("chai") + .use(require("chai-as-promised")) + .use(require("chai-bignumber")(BigNumber)) + .should(); + +contract("TestProcessor", function(accounts) { + const userOne = accounts[1]; + const userTwo = accounts[2]; + const userThree = accounts[3]; + + describe("Processor contract deployment", function() { + beforeEach(async function() { + this.processor = await TestProcessor.new(); + }); + + it("should deploy the processor with the correct parameters", async function() { + this.processor.should.exist; + + const nonce = Number(await this.processor.nonce()); + nonce.should.be.bignumber.equal(0); + }); + }); + + describe("Item creation", function() { + beforeEach(async function() { + this.processor = await TestProcessor.new(); + this.recipient = web3.utils.bytesToHex(["20bytestring"]); + this.amount = 250; + + //Load user account with tokens for testing + this.token = await TestToken.new(); + await this.token.mint(userOne, 1000, { + from: accounts[0] + }).should.be.fulfilled; + }); + + it("should allow for the creation of items", async function() { + await this.processor.callCreate( + userOne, + this.recipient, + this.token.address, + this.amount + ).should.be.fulfilled; + }); + + it("should generate unique item id's for a created item", async function() { + //Simulate sha3 hash to get item's expected id + const expectedId = Web3Utils.soliditySha3( + { t: "address payable", v: userOne }, + { t: "bytes", v: this.recipient }, + { t: "address", v: this.token.address }, + { t: "int256", v: this.amount }, + { t: "int256", v: 1 } + ); + + //Get the item's id if it were to be created + const id = await this.processor.callCreate.call( + userOne, + this.recipient, + this.token.address, + this.amount + ); + id.should.be.equal(expectedId); + }); + + it("should allow access to an item's information given it's unique id", async function() { + const id = await this.processor.callCreate.call( + userOne, + this.recipient, + this.token.address, + this.amount + ); + await this.processor.callCreate( + userOne, + this.recipient, + this.token.address, + this.amount + ); + + //Attempt to get an item's information + await this.processor.callGetItem(id).should.be.fulfilled; + }); + + it("should correctly identify the existence of items in memory", async function() { + //Get the item's expected id then lock funds + const id = await this.processor.callCreate.call( + userOne, + this.recipient, + this.token.address, + this.amount + ); + await this.processor.callCreate( + userOne, + this.recipient, + this.token.address, + this.amount + ).should.be.fulfilled; + + //Check if item has been created and locked + const locked = await this.processor.callIsLocked(id); + locked.should.be.equal(true); + }); + + it("should store items with the correct parameters", async function() { + //Create the item and store its id + const id = await this.processor.callCreate.call( + userOne, + this.recipient, + this.token.address, + this.amount + ); + await this.processor.callCreate( + userOne, + this.recipient, + this.token.address, + this.amount + ); + + //Get the item's information + const itemInfo = await this.processor.callGetItem(id); + + //Parse each attribute + const sender = itemInfo[0]; + const receiver = itemInfo[1]; + const token = itemInfo[2]; + const amount = Number(itemInfo[3]); + const nonce = Number(itemInfo[4]); + + //Confirm that each attribute is correct + sender.should.be.equal(userOne); + receiver.should.be.equal(this.recipient); + token.should.be.equal(this.token.address); + amount.should.be.bignumber.equal(this.amount); + nonce.should.be.bignumber.equal(1); + }); + }); + + describe("Item completion", function() { + beforeEach(async function() { + this.processor = await TestProcessor.new(); + this.weiAmount = web3.utils.toWei("0.25", "ether"); + this.recipient = web3.utils.bytesToHex(["20bytestring"]); + this.ethereumToken = "0x0000000000000000000000000000000000000000"; + + //Load contract with ethereum so it can complete items + await this.processor.send(web3.utils.toWei("1", "ether"), { + from: accounts[0] + }).should.be.fulfilled; + + this.itemId = await this.processor.callCreate.call( + userOne, + this.recipient, + this.ethereumToken, + this.weiAmount + ); + await this.processor.callCreate( + userOne, + this.recipient, + this.ethereumToken, + this.weiAmount + ); + }); + + it("should not allow for the completion of items whose value exceeds the contract's balance", async function() { + //Create an item with an overlimit amount + const overlimitAmount = web3.utils.toWei("1.25", "ether"); + const id = await this.processor.callCreate.call( + userOne, + this.recipient, + this.ethereumToken, + overlimitAmount + ); + await this.processor.callCreate( + userOne, + this.recipient, + this.ethereumToken, + overlimitAmount + ); + + //Attempt to complete the item + await this.processor.callComplete(id).should.be.rejectedWith(EVMRevert); + }); + + it("should not allow for the completion of non-items", async function() { + //Generate a false item id + const fakeId = Web3Utils.soliditySha3( + { t: "address payable", v: userOne }, + { t: "bytes", v: this.recipient }, + { t: "address", v: this.ethereumToken }, + { t: "int256", v: 12 }, + { t: "int256", v: 1 } + ); + + await this.processor + .callComplete(fakeId) + .should.be.rejectedWith(EVMRevert); + }); + + it("should not allow for the completion of an item that has already been completed", async function() { + //Complete the item + await this.processor.callComplete(this.itemId).should.be.fulfilled; + + //Attempt to complete the item again + await this.processor + .callComplete(this.itemId) + .should.be.rejectedWith(EVMRevert); + }); + + it("should allow for an item to be completed", async function() { + await this.processor.callComplete(this.itemId).should.be.fulfilled; + }); + + it("should update lock status of items upon completion", async function() { + //Confirm that the item is active + const startingLockStatus = await this.processor.callIsLocked(this.itemId); + startingLockStatus.should.be.equal(true); + + //Complete the item + await this.processor.callComplete(this.itemId).should.be.fulfilled; + + //Check if the item still exists + const completedItem = await this.processor.callIsLocked(this.itemId); + completedItem.should.be.equal(false); + }); + + it("should correctly transfer itemized funds to the original sender", async function() { + //Get prior balances of user and peggy contract + const beforeUserBalance = Number(await web3.eth.getBalance(userOne)); + const beforeContractBalance = Number( + await web3.eth.getBalance(this.processor.address) + ); + + await this.processor.callComplete(this.itemId).should.be.fulfilled; + + //Get balances after completion + const afterUserBalance = Number(await web3.eth.getBalance(userOne)); + const afterContractBalance = Number( + await web3.eth.getBalance(this.processor.address) + ); + + //Expected balances + afterUserBalance.should.be.bignumber.equal( + beforeUserBalance + Number(this.weiAmount) + ); + afterContractBalance.should.be.bignumber.equal( + beforeContractBalance - Number(this.weiAmount) + ); + }); + }); +}); diff --git a/testnet-contracts/truffle-config.js b/testnet-contracts/truffle-config.js new file mode 100644 index 0000000..79297a1 --- /dev/null +++ b/testnet-contracts/truffle-config.js @@ -0,0 +1,39 @@ +require("dotenv").config(); + +var HDWalletProvider = require("@truffle/hdwallet-provider"); + +module.exports = { + networks: { + develop: { + host: "localhost", + port: 7545, // Match default network 'ganache' + network_id: "*", + gas: 6721975, // Truffle default development block gas limit + gasPrice: 200000000000, + solc: { + version: "0.5.0", + optimizer: { + enabled: true, + runs: 200 + } + } + }, + ropsten: { + provider: function() { + return new HDWalletProvider( + process.env.MNEMONIC, + "https://ropsten.infura.io/v3/".concat(process.env.INFURA_PROJECT_ID) + ); + }, + network_id: 3, + gas: 6000000 + } + }, + rpc: { + host: "localhost", + post: 8080 + }, + mocha: { + useColors: true + } +}; diff --git a/x/ethbridge/alias.go b/x/ethbridge/alias.go new file mode 100644 index 0000000..4e38385 --- /dev/null +++ b/x/ethbridge/alias.go @@ -0,0 +1,56 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/dawn-protocol/dawn/x/ethbridge/querier +// ALIASGEN: github.com/dawn-protocol/dawn/x/ethbridge/types +package ethbridge + +import ( + "github.com/dawn-protocol/dawn/x/ethbridge/querier" + "github.com/dawn-protocol/dawn/x/ethbridge/types" +) + +const ( + QueryEthProphecy = querier.QueryEthProphecy + DefaultCodespace = types.DefaultCodespace + CodeInvalidEthNonce = types.CodeInvalidEthNonce + CodeInvalidEthAddress = types.CodeInvalidEthAddress + CodeErrJSONMarshalling = types.CodeErrJSONMarshalling + ModuleName = types.ModuleName + StoreKey = types.StoreKey + QuerierRoute = types.QuerierRoute + RouterKey = types.RouterKey +) + +var ( + // functions aliases + NewQuerier = querier.NewQuerier + NewEthBridgeClaim = types.NewEthBridgeClaim + NewOracleClaimContent = types.NewOracleClaimContent + CreateOracleClaimFromEthClaim = types.CreateOracleClaimFromEthClaim + CreateEthClaimFromOracleString = types.CreateEthClaimFromOracleString + CreateOracleClaimFromOracleString = types.CreateOracleClaimFromOracleString + RegisterCodec = types.RegisterCodec + ErrInvalidEthNonce = types.ErrInvalidEthNonce + ErrInvalidEthAddress = types.ErrInvalidEthAddress + ErrJSONMarshalling = types.ErrJSONMarshalling + NewEthereumAddress = types.NewEthereumAddress + NewMsgCreateEthBridgeClaim = types.NewMsgCreateEthBridgeClaim + MapOracleClaimsToEthBridgeClaims = types.MapOracleClaimsToEthBridgeClaims + NewQueryEthProphecyParams = types.NewQueryEthProphecyParams + NewQueryEthProphecyResponse = types.NewQueryEthProphecyResponse + + CreateTestEthMsg = types.CreateTestEthMsg + CreateTestEthClaim = types.CreateTestEthClaim + CreateTestQueryEthProphecyResponse = types.CreateTestQueryEthProphecyResponse +) + +type ( + EthBridgeClaim = types.EthBridgeClaim + OracleClaimContent = types.OracleClaimContent + CodeType = types.CodeType + EthereumAddress = types.EthereumAddress + MsgCreateEthBridgeClaim = types.MsgCreateEthBridgeClaim + QueryEthProphecyParams = types.QueryEthProphecyParams + QueryEthProphecyResponse = types.QueryEthProphecyResponse +) \ No newline at end of file diff --git a/x/ethbridge/client/cli/query.go b/x/ethbridge/client/cli/query.go new file mode 100644 index 0000000..8fe9f83 --- /dev/null +++ b/x/ethbridge/client/cli/query.go @@ -0,0 +1,66 @@ +package cli + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/dawn-protocol/dawn/x/ethbridge/querier" + "github.com/dawn-protocol/dawn/x/ethbridge/types" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" +) + +// GetCmdGetEthBridgeProphecy queries information about a specific prophecy +func GetCmdGetEthBridgeProphecy(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "prophecy [ethereum-chain-id] [bridge-contract] [nonce] [symbol] [token-contract] [ethereum-sender]", + Short: "Query prophecy", + Args: cobra.ExactArgs(6), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + ethereumChainID, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + + bridgeContract := types.NewEthereumAddress(args[1]) + + nonce, err := strconv.Atoi(args[2]) + if err != nil { + return err + } + + symbol := args[3] + if strings.TrimSpace(symbol) == "" { + return errors.New("Error: must specify a token symbol, including 'eth' for Ethereum") + } + + tokenContract := types.NewEthereumAddress(args[4]) + + ethereumSender := types.NewEthereumAddress(args[5]) + + bz, err := cdc.MarshalJSON(types.NewQueryEthProphecyParams(ethereumChainID, bridgeContract, nonce, symbol, tokenContract, ethereumSender)) + if err != nil { + return err + } + + route := fmt.Sprintf("custom/%s/%s", queryRoute, querier.QueryEthProphecy) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + var out types.QueryEthProphecyResponse + err = cdc.UnmarshalJSON(res, &out) + if err != nil { + return err + } + return cliCtx.PrintOutput(out) + }, + } +} diff --git a/x/ethbridge/client/cli/tx.go b/x/ethbridge/client/cli/tx.go new file mode 100644 index 0000000..7ad749d --- /dev/null +++ b/x/ethbridge/client/cli/tx.go @@ -0,0 +1,77 @@ +package cli + +import ( + "errors" + "strconv" + "strings" + + "github.com/dawn-protocol/dawn/x/ethbridge/types" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtxb "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// GetCmdCreateEthBridgeClaim is the CLI command for creating a claim on an ethereum prophecy +func GetCmdCreateEthBridgeClaim(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "create-claim [ethereum-chain-id] [bridge-contract] [nonce] [symbol] [token-contract] [ethereum-sender-address] [cosmos-receiver-address] [validator-address] [amount]", + Short: "create a claim on an ethereum prophecy", + Args: cobra.ExactArgs(9), + RunE: func(cmd *cobra.Command, args []string) error { + + cliCtx := context.NewCLIContext().WithCodec(cdc) + + txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + + ethereumChainID, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + + bridgeContract := types.NewEthereumAddress(args[1]) + + nonce, err := strconv.Atoi(args[2]) + if err != nil { + return err + } + + symbol := args[3] + if strings.TrimSpace(symbol) == "" { + return errors.New("must specify a token symbol/denomination, including 'eth' for Ethereum") + } + + tokenContract := types.NewEthereumAddress(args[4]) + + ethereumSender := types.NewEthereumAddress(args[5]) + + cosmosReceiver, err := sdk.AccAddressFromBech32(args[6]) + if err != nil { + return err + } + + validator, err := sdk.ValAddressFromBech32(args[7]) + if err != nil { + return err + } + + amount, err := sdk.ParseCoins(args[8]) + if err != nil { + return err + } + + ethBridgeClaim := types.NewEthBridgeClaim(ethereumChainID, bridgeContract, nonce, symbol, tokenContract, ethereumSender, cosmosReceiver, validator, amount) + msg := types.NewMsgCreateEthBridgeClaim(ethBridgeClaim) + err = msg.ValidateBasic() + if err != nil { + return err + } + + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } +} diff --git a/x/ethbridge/client/module_client.go b/x/ethbridge/client/module_client.go new file mode 100644 index 0000000..e5d1861 --- /dev/null +++ b/x/ethbridge/client/module_client.go @@ -0,0 +1,46 @@ +package client + +import ( + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/dawn-protocol/dawn/x/ethbridge/client/cli" + "github.com/dawn-protocol/dawn/x/ethbridge/client/rest" + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { + // Group ethbridge queries under a subcommand + ethBBridgeQueryCmd := &cobra.Command{ + Use: "ethbridge", + Short: "Querying commands for the ethbridge module", + } + + ethBBridgeQueryCmd.AddCommand(client.GetCommands( + cli.GetCmdGetEthBridgeProphecy(storeKey, cdc), + )...) + + return ethBBridgeQueryCmd +} + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { + ethBridgeTxCmd := &cobra.Command{ + Use: "ethbridge", + Short: "EthBridge transactions subcommands", + } + + ethBridgeTxCmd.AddCommand(client.PostCommands( + cli.GetCmdCreateEthBridgeClaim(cdc), + )...) + + return ethBridgeTxCmd +} + +// RegisterRESTRoutes - Central function to define routes that get registered by the main application +func RegisterRESTRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) { + rest.RegisterRESTRoutes(cliCtx, r, storeName) +} diff --git a/x/ethbridge/client/rest/rest.go b/x/ethbridge/client/rest/rest.go new file mode 100644 index 0000000..73a0d8c --- /dev/null +++ b/x/ethbridge/client/rest/rest.go @@ -0,0 +1,144 @@ +package rest + +import ( + "fmt" + "net/http" + "strconv" + "strings" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + + "github.com/gorilla/mux" + + "github.com/dawn-protocol/dawn/x/ethbridge/querier" + "github.com/dawn-protocol/dawn/x/ethbridge/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + restEthereumChainID = "ethereumChainID" + restBridgeContract = "bridgeContract" + restNonce = "nonce" + restSymbol = "symbol" + restTokenContract = "tokenContract" + restEthereumSender = "ethereumSender" +) + +type createEthClaimReq struct { + BaseReq rest.BaseReq `json:"base_req"` + EthereumChainID int `json:"ethereum_chain_id"` + BridgeContractAddress string `json:"bridge_contract_address"` + Nonce int `json:"nonce"` + Symbol string `json:"symbol"` + TokenContractAddress string `json:"token_contract_address"` + EthereumSender string `json:"ethereum_sender"` + CosmosReceiver string `json:"cosmos_receiver"` + Validator string `json:"validator"` + Amount string `json:"amount"` +} + +// RegisterRoutes - Central function to define routes that get registered by the main application +func RegisterRESTRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) { + r.HandleFunc(fmt.Sprintf("/%s/prophecies", storeName), createClaimHandler(cliCtx)).Methods("POST") + r.HandleFunc(fmt.Sprintf("/%s/prophecies/{%s}/{%s}/{%s}/{%s}/{%s}/{%s}", storeName, restEthereumChainID, restBridgeContract, restNonce, restSymbol, restTokenContract, restEthereumSender), getProphecyHandler(cliCtx, storeName)).Methods("GET") +} + +func createClaimHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req createEthClaimReq + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") + return + } + + baseReq := req.BaseReq.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + bridgeContractAddress := types.NewEthereumAddress(req.BridgeContractAddress) + + tokenContractAddress := types.NewEthereumAddress(req.TokenContractAddress) + + ethereumSender := types.NewEthereumAddress(req.EthereumSender) + + cosmosReceiver, err := sdk.AccAddressFromBech32(req.CosmosReceiver) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + validator, err := sdk.ValAddressFromBech32(req.Validator) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + amount, err := sdk.ParseCoins(req.Amount) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + // create the message + ethBridgeClaim := types.NewEthBridgeClaim(req.EthereumChainID, bridgeContractAddress, req.Nonce, req.Symbol, tokenContractAddress, ethereumSender, cosmosReceiver, validator, amount) + msg := types.NewMsgCreateEthBridgeClaim(ethBridgeClaim) + err = msg.ValidateBasic() + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) + } +} + +func getProphecyHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + ethereumChainID := vars[restEthereumChainID] + ethereumChainIDString, err := strconv.Atoi(ethereumChainID) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + bridgeContract := types.NewEthereumAddress(vars[restBridgeContract]) + + nonce := vars[restNonce] + nonceString, err := strconv.Atoi(nonce) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + tokenContract := types.NewEthereumAddress(vars[restTokenContract]) + + symbol := vars[restSymbol] + if strings.TrimSpace(symbol) == "" { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + ethereumSender := types.NewEthereumAddress(vars[restEthereumSender]) + + bz, err := cliCtx.Codec.MarshalJSON(types.NewQueryEthProphecyParams(ethereumChainIDString, bridgeContract, nonceString, symbol, tokenContract, ethereumSender)) + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + route := fmt.Sprintf("custom/%s/%s", storeName, querier.QueryEthProphecy) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} \ No newline at end of file diff --git a/x/ethbridge/handler.go b/x/ethbridge/handler.go new file mode 100644 index 0000000..eb498d9 --- /dev/null +++ b/x/ethbridge/handler.go @@ -0,0 +1,86 @@ +package ethbridge + +import ( + "fmt" + + "github.com/dawn-protocol/dawn/x/ethbridge/types" + "github.com/dawn-protocol/dawn/x/oracle" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/supply" +) + +// NewHandler returns a handler for "ethbridge" type messages. +func NewHandler(oracleKeeper oracle.Keeper, supplyKeeper supply.Keeper, codespace sdk.CodespaceType, cdc *codec.Codec) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + switch msg := msg.(type) { + case MsgCreateEthBridgeClaim: + return handleMsgCreateEthBridgeClaim(ctx, cdc, oracleKeeper, supplyKeeper, msg, codespace) + default: + errMsg := fmt.Sprintf("unrecognized ethbridge message type: %v", msg.Type()) + return sdk.ErrUnknownRequest(errMsg).Result() + } + } +} + +// Handle a message to create a bridge claim +func handleMsgCreateEthBridgeClaim(ctx sdk.Context, cdc *codec.Codec, + oracleKeeper oracle.Keeper, supplyKeeper supply.Keeper, msg MsgCreateEthBridgeClaim, + codespace sdk.CodespaceType) sdk.Result { + oracleClaim, err := types.CreateOracleClaimFromEthClaim(cdc, types.EthBridgeClaim(msg)) + if err != nil { + return types.ErrJSONMarshalling(codespace).Result() + } + status, sdkErr := oracleKeeper.ProcessClaim(ctx, oracleClaim) + if sdkErr != nil { + return sdkErr.Result() + } + + if status.Text == oracle.SuccessStatusText { + sdkErr = processSuccessfulClaim(ctx, supplyKeeper, status.FinalClaim) + if sdkErr != nil { + return sdkErr.Result() + } + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.ValidatorAddress.String()), + ), + sdk.NewEvent( + types.EventTypeCreateClaim, + sdk.NewAttribute(types.AttributeKeyEthereumSender, msg.EthereumSender.String()), + sdk.NewAttribute(types.AttributeKeyCosmosReceiver, msg.CosmosReceiver.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()), + ), + sdk.NewEvent( + types.EventTypeProphecyStatus, + sdk.NewAttribute(types.AttributeKeyStatus, status.Text.String()), + ), + }) + + return sdk.Result{Events: ctx.EventManager().Events()} +} + +func processSuccessfulClaim(ctx sdk.Context, supplyKeeper supply.Keeper, claim string) sdk.Error { + oracleClaim, err := types.CreateOracleClaimFromOracleString(claim) + if err != nil { + return err + } + + receiverAddress := oracleClaim.CosmosReceiver + err = supplyKeeper.MintCoins(ctx, ModuleName, oracleClaim.Amount) + if err != nil { + return err + } + err = supplyKeeper.SendCoinsFromModuleToAccount(ctx, ModuleName, receiverAddress, oracleClaim.Amount) + if err != nil { + panic(err) + } + + return nil +} \ No newline at end of file diff --git a/x/ethbridge/handler_test.go b/x/ethbridge/handler_test.go new file mode 100644 index 0000000..a06aebf --- /dev/null +++ b/x/ethbridge/handler_test.go @@ -0,0 +1,180 @@ +package ethbridge + +import ( + "strings" + "testing" + + "github.com/dawn-protocol/dawn/x/oracle" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/dawn-protocol/dawn/x/ethbridge/types" + "github.com/stretchr/testify/require" +) + +func TestBasicMsgs(t *testing.T) { + //Setup + ctx, _, _, _, validatorAddresses, handler := CreateTestHandler(t, 0.7, []int64{3, 7}) + + valAddress := validatorAddresses[0] + + //Unrecognized type + res := handler(ctx, sdk.NewTestMsg()) + require.False(t, res.IsOK()) + require.True(t, strings.Contains(res.Log, "unrecognized ethbridge message type: ")) + + //Normal Creation + normalCreateMsg := types.CreateTestEthMsg(t, valAddress) + res = handler(ctx, normalCreateMsg) + require.True(t, res.IsOK()) + for _, event := range res.Events { + for _, attribute := range event.Attributes { + value := string(attribute.Value) + switch key := string(attribute.Key); key { + case "module": + require.Equal(t, value, types.ModuleName) + case "sender": + require.Equal(t, value, valAddress.String()) + case "ethereum_sender": + require.Equal(t, value, types.TestEthereumAddress) + case "cosmos_receiver": + require.Equal(t, value, types.TestAddress) + case "amount": + require.Equal(t, value, types.TestCoins) + case "status": + require.Equal(t, value, oracle.StatusTextToString[oracle.PendingStatusText]) + default: + require.Fail(t, fmt.Sprintf("unrecognized event %s", key)) + } + } + } + + //Bad Creation + badCreateMsg := types.CreateTestEthMsg(t, valAddress) + badCreateMsg.Nonce = -1 + err := badCreateMsg.ValidateBasic() + require.Error(t, err) +} + +func TestDuplicateMsgs(t *testing.T) { + ctx, _, _, _, validatorAddresses, handler := CreateTestHandler(t, 0.7, []int64{3, 7}) + + valAddress := validatorAddresses[0] + + normalCreateMsg := types.CreateTestEthMsg(t, valAddress) + res := handler(ctx, normalCreateMsg) + require.True(t, res.IsOK()) + for _, event := range res.Events { + for _, attribute := range event.Attributes { + value := string(attribute.Value) + if string(attribute.Key) == "status" { + require.Equal(t, value, oracle.StatusTextToString[oracle.PendingStatusText]) + } + } + } + + //Duplicate message from same validator + res = handler(ctx, normalCreateMsg) + require.False(t, res.IsOK()) + require.True(t, strings.Contains(res.Log, "already processed message from validator for this id")) +} + +func TestMintSuccess(t *testing.T) { + //Setup + ctx, _, bankKeeper, _, validatorAddresses, handler := CreateTestHandler(t, 0.7, []int64{2, 7, 1}) + + valAddressVal1Pow2 := validatorAddresses[0] + valAddressVal2Pow7 := validatorAddresses[1] + valAddressVal3Pow1 := validatorAddresses[2] + + //Initial message + normalCreateMsg := types.CreateTestEthMsg(t, valAddressVal1Pow2) + res := handler(ctx, normalCreateMsg) + require.True(t, res.IsOK()) + + //Message from second validator succeeds and mints new tokens + normalCreateMsg = types.CreateTestEthMsg(t, valAddressVal2Pow7) + res = handler(ctx, normalCreateMsg) + require.True(t, res.IsOK()) + receiverAddress, err := sdk.AccAddressFromBech32(types.TestAddress) + require.NoError(t, err) + receiverCoins := bankKeeper.GetCoins(ctx, receiverAddress) + expectedCoins, err := sdk.ParseCoins(types.TestCoins) + require.NoError(t, err) + require.True(t, receiverCoins.IsEqual(expectedCoins)) + for _, event := range res.Events { + for _, attribute := range event.Attributes { + value := string(attribute.Value) + if string(attribute.Key) == "status" { + require.Equal(t, value, oracle.StatusTextToString[oracle.SuccessStatusText]) + } + } + } + + //Additional message from third validator fails and does not mint + normalCreateMsg = types.CreateTestEthMsg(t, valAddressVal3Pow1) + res = handler(ctx, normalCreateMsg) + require.False(t, res.IsOK()) + require.True(t, strings.Contains(res.Log, "prophecy already finalized")) + receiverCoins = bankKeeper.GetCoins(ctx, receiverAddress) + expectedCoins, err = sdk.ParseCoins(types.TestCoins) + require.NoError(t, err) + require.True(t, receiverCoins.IsEqual(expectedCoins)) + +} + +func TestNoMintFail(t *testing.T) { + //Setup + ctx, _, bankKeeper, _, validatorAddresses, handler := CreateTestHandler(t, 0.71, []int64{3, 4, 3}) + + valAddressVal1Pow3 := validatorAddresses[0] + valAddressVal2Pow4 := validatorAddresses[1] + valAddressVal3Pow3 := validatorAddresses[2] + + ethClaim1 := types.CreateTestEthClaim(t, valAddressVal1Pow3, types.NewEthereumAddress(types.TestEthereumAddress), types.TestCoins) + ethMsg1 := NewMsgCreateEthBridgeClaim(ethClaim1) + ethClaim2 := types.CreateTestEthClaim(t, valAddressVal2Pow4, types.NewEthereumAddress(types.AltTestEthereumAddress), types.TestCoins) + ethMsg2 := NewMsgCreateEthBridgeClaim(ethClaim2) + ethClaim3 := types.CreateTestEthClaim(t, valAddressVal3Pow3, types.NewEthereumAddress(types.TestEthereumAddress), types.AltTestCoins) + ethMsg3 := NewMsgCreateEthBridgeClaim(ethClaim3) + + //Initial message + res := handler(ctx, ethMsg1) + require.True(t, res.IsOK()) + for _, event := range res.Events { + for _, attribute := range event.Attributes { + value := string(attribute.Value) + if string(attribute.Key) == "status" { + require.Equal(t, value, oracle.StatusTextToString[oracle.PendingStatusText]) + } + } + } + + //Different message from second validator succeeds + res = handler(ctx, ethMsg2) + require.True(t, res.IsOK()) + for _, event := range res.Events { + for _, attribute := range event.Attributes { + value := string(attribute.Value) + if string(attribute.Key) == "status" { + require.Equal(t, value, oracle.StatusTextToString[oracle.PendingStatusText]) + } + } + } + + //Different message from third validator succeeds but results in failed prophecy with no minting + res = handler(ctx, ethMsg3) + require.True(t, res.IsOK()) + for _, event := range res.Events { + for _, attribute := range event.Attributes { + value := string(attribute.Value) + if string(attribute.Key) == "status" { + require.Equal(t, value, oracle.StatusTextToString[oracle.FailedStatusText]) + } + } + } + receiverAddress, err := sdk.AccAddressFromBech32(types.TestAddress) + require.NoError(t, err) + receiver1Coins := bankKeeper.GetCoins(ctx, receiverAddress) + require.True(t, receiver1Coins.IsZero()) +} \ No newline at end of file diff --git a/x/ethbridge/module.go b/x/ethbridge/module.go new file mode 100644 index 0000000..2ca9ff4 --- /dev/null +++ b/x/ethbridge/module.go @@ -0,0 +1,147 @@ +package ethbridge + +import ( + "encoding/json" + + "github.com/dawn-protocol/dawn/x/ethbridge/client" + "github.com/dawn-protocol/dawn/x/ethbridge/types" + "github.com/dawn-protocol/dawn/x/oracle" + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/supply" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// AppModuleBasic defines the basic application module used by the ethbridge module. +type AppModuleBasic struct{} + +var _ module.AppModuleBasic = AppModuleBasic{} + +// Name returns the ethbridge module's name. +func (AppModuleBasic) Name() string { + return ModuleName +} + +// RegisterCodec registers the ethbridge module's types for the given codec. +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + types.RegisterCodec(cdc) +} + +// DefaultGenesis returns default genesis state as raw bytes for the ethbridge +// module. +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return nil +} + +// ValidateGenesis performs genesis state validation for the ethbridge module. +func (AppModuleBasic) ValidateGenesis(_ json.RawMessage) error { + return nil +} + +// RegisterRESTRoutes registers the REST routes for the ethbridge module. +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + client.RegisterRESTRoutes(ctx, rtr, StoreKey) +} + +// GetTxCmd returns the root tx command for the ethbridge module. +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return client.GetTxCmd(StoreKey, cdc) +} + +// GetQueryCmd returns no root query command for the ethbridge module. +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return client.GetQueryCmd(StoreKey, cdc) +} + +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the ethbridge module. +type AppModuleSimulation struct{} + +// AppModule implements an application module for the ethbridge module. +type AppModule struct { + AppModuleBasic + AppModuleSimulation + + OracleKeeper oracle.Keeper + SupplyKeeper supply.Keeper + Codespace sdk.CodespaceType + Codec *codec.Codec +} + +// NewAppModule creates a new AppModule object +func NewAppModule(oracleKeeper oracle.Keeper, supplyKeeper supply.Keeper, codespace sdk.CodespaceType, cdc *codec.Codec) AppModule { + + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + + OracleKeeper: oracleKeeper, + SupplyKeeper: supplyKeeper, + Codespace: codespace, + Codec: cdc, + } +} + +// Name returns the ethbridge module's name. +func (AppModule) Name() string { + return ModuleName +} + +// RegisterInvariants registers the ethbridge module invariants. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { +} + +// Route returns the message routing key for the ethbridge module. +func (AppModule) Route() string { + return RouterKey +} + +// NewHandler returns an sdk.Handler for the ethbridge module. +func (am AppModule) NewHandler() sdk.Handler { + return NewHandler(am.OracleKeeper, am.SupplyKeeper, am.Codespace, am.Codec) +} + +// QuerierRoute returns the ethbridge module's querier route name. +func (AppModule) QuerierRoute() string { + return QuerierRoute +} + +// NewQuerierHandler returns the ethbridge module sdk.Querier. +func (am AppModule) NewQuerierHandler() sdk.Querier { + return NewQuerier(am.OracleKeeper, am.Codec, am.Codespace) +} + +// InitGenesis performs genesis initialization for the ethbridge module. It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, _ json.RawMessage) []abci.ValidatorUpdate { + bridgeAccount := supply.NewEmptyModuleAccount(ModuleName, supply.Burner, supply.Minter) + am.SupplyKeeper.SetModuleAccount(ctx, bridgeAccount) + return nil +} + +// ExportGenesis returns the exported genesis state as raw bytes for the ethbridge +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return nil +} + +// BeginBlock returns the begin blocker for the ethbridge module. +func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock returns the end blocker for the ethbridge module. It returns no validator +// updates. +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return nil +} \ No newline at end of file diff --git a/x/ethbridge/querier/querier.go b/x/ethbridge/querier/querier.go new file mode 100644 index 0000000..389cc16 --- /dev/null +++ b/x/ethbridge/querier/querier.go @@ -0,0 +1,57 @@ +package querier + +import ( + "strconv" + + "github.com/dawn-protocol/dawn/x/ethbridge/types" + keep "github.com/dawn-protocol/dawn/x/oracle/keeper" + oracletypes "github.com/dawn-protocol/dawn/x/oracle/types" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// query endpoints supported by the oracle Querier +const ( + QueryEthProphecy = "prophecies" +) + +// NewQuerier is the module level router for state queries +func NewQuerier(keeper keep.Keeper, cdc *codec.Codec, codespace sdk.CodespaceType) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case QueryEthProphecy: + return queryEthProphecy(ctx, cdc, req, keeper, codespace) + default: + return nil, sdk.ErrUnknownRequest("unknown ethbridge query endpoint") + } + } +} + +func queryEthProphecy(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, keeper keep.Keeper, codespace sdk.CodespaceType) (res []byte, errSdk sdk.Error) { + var params types.QueryEthProphecyParams + + if err := cdc.UnmarshalJSON(req.Data, ¶ms); err != nil { + return []byte{}, sdk.ErrInternal(sdk.AppendMsgToErr("failed to parse params: %s", err.Error())) + } + id := strconv.Itoa(params.EthereumChainID) + strconv.Itoa(params.Nonce) + params.EthereumSender.String() + prophecy, errSdk := keeper.GetProphecy(ctx, id) + if errSdk != nil { + return []byte{}, oracletypes.ErrProphecyNotFound(codespace) + } + + bridgeClaims, errSdk := types.MapOracleClaimsToEthBridgeClaims(params.EthereumChainID, params.BridgeContractAddress, params.Nonce, params.Symbol, params.TokenContractAddress, params.EthereumSender, prophecy.ValidatorClaims, types.CreateEthClaimFromOracleString) + if errSdk != nil { + return []byte{}, errSdk + } + + response := types.NewQueryEthProphecyResponse(prophecy.ID, prophecy.Status, bridgeClaims) + + bz, err := cdc.MarshalJSONIndent(response, "", " ") + if err != nil { + panic(err) + } + + return bz, nil +} diff --git a/x/ethbridge/querier/querier_test.go b/x/ethbridge/querier/querier_test.go new file mode 100644 index 0000000..3745483 --- /dev/null +++ b/x/ethbridge/querier/querier_test.go @@ -0,0 +1,105 @@ +package querier + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/dawn-protocol/dawn/x/ethbridge/types" + "github.com/dawn-protocol/dawn/x/oracle" + keeperLib "github.com/dawn-protocol/dawn/x/oracle/keeper" +) + +const ( + TestResponseJSON = "{\"id\":\"00x7B95B6EC7EbD73572298cEf32Bb54FA408207359\",\"status\":{\"text\":\"pending\",\"final_claim\":\"\"},\"claims\":[{\"nonce\":0,\"ethereum_sender\":\"0x7B95B6EC7EbD73572298cEf32Bb54FA408207359\",\"cosmos_receiver\":\"cosmos1gn8409qq9hnrxde37kuxwx5hrxpfpv8426szuv\",\"validator_address\":\"cosmosvaloper1mnfm9c7cdgqnkk66sganp78m0ydmcr4pn7fqfk\",\"amount\":[{\"denom\":\"ethereum\",\"amount\":\"10\"}]}]}" +) + +func TestNewQuerier(t *testing.T) { + ctx, oracleKeeper, _, _, _ := oracle.CreateTestKeepers(t, 0.7, []int64{3, 3}, "") + cdc := keeperLib.MakeTestCodec() + + query := abci.RequestQuery{ + Path: "", + Data: []byte{}, + } + + querier := NewQuerier(oracleKeeper, cdc, types.DefaultCodespace) + + //Test wrong paths + bz, err := querier(ctx, []string{"other"}, query) + require.NotNil(t, err) + require.Nil(t, bz) +} + +func TestQueryEthProphecy(t *testing.T) { + ctx, oracleKeeper, _, _, validatorAddresses := oracle.CreateTestKeepers(t, 0.7, []int64{3, 7}, "") + cdc := keeperLib.MakeTestCodec() + + valAddress := validatorAddresses[0] + testEthereumAddress := types.NewEthereumAddress(types.TestEthereumAddress) + testBridgeContractAddress := types.NewEthereumAddress(types.TestBridgeContractAddress) + testTokenContractAddress := types.NewEthereumAddress(types.TestTokenContractAddress) + + initialEthBridgeClaim := types.CreateTestEthClaim(t, testBridgeContractAddress, testTokenContractAddress, valAddress, testEthereumAddress, types.TestCoins) + oracleClaim, _ := types.CreateOracleClaimFromEthClaim(cdc, initialEthBridgeClaim) + _, err := oracleKeeper.ProcessClaim(ctx, oracleClaim) + require.Nil(t, err) + + testResponse := types.CreateTestQueryEthProphecyResponse(cdc, t, valAddress) + + //Test query String() + require.Equal(t, testResponse.String(), TestResponseJSON) + + bz, err2 := cdc.MarshalJSON(types.NewQueryEthProphecyParams(types.TestNonce, testEthereumAddress)) + require.Nil(t, err2) + + query := abci.RequestQuery{ + Path: "/custom/ethbridge/prophecies", + Data: bz, + } + + //Test query + res, err3 := queryEthProphecy(ctx, cdc, query, oracleKeeper, types.DefaultCodespace) + require.Nil(t, err3) + + var ethProphecyResp types.QueryEthProphecyResponse + err4 := cdc.UnmarshalJSON(res, ðProphecyResp) + require.Nil(t, err4) + require.True(t, reflect.DeepEqual(ethProphecyResp, testResponse)) + + // Test error with bad request + query.Data = bz[:len(bz)-1] + + _, err5 := queryEthProphecy(ctx, cdc, query, oracleKeeper, types.DefaultCodespace) + require.NotNil(t, err5) + + // Test error with nonexistent request + badEthereumAddress := types.NewEthereumAddress("badEthereumAddress") + + bz2, err6 := cdc.MarshalJSON(types.NewQueryEthProphecyParams(12, badEthereumAddress)) + require.Nil(t, err6) + + query2 := abci.RequestQuery{ + Path: "/custom/oracle/prophecies", + Data: bz2, + } + + _, err7 := queryEthProphecy(ctx, cdc, query2, oracleKeeper, types.DefaultCodespace) + require.NotNil(t, err7) + + // Test error with empty address + emptyEthereumAddress := types.NewEthereumAddress("") + + bz3, err8 := cdc.MarshalJSON(types.NewQueryEthProphecyParams(12, emptyEthereumAddress)) + require.Nil(t, err8) + + query3 := abci.RequestQuery{ + Path: "/custom/oracle/prophecies", + Data: bz3, + } + + _, err9 := queryEthProphecy(ctx, cdc, query3, oracleKeeper, types.DefaultCodespace) + require.NotNil(t, err9) +} \ No newline at end of file diff --git a/x/ethbridge/test_common.go b/x/ethbridge/test_common.go new file mode 100644 index 0000000..8321987 --- /dev/null +++ b/x/ethbridge/test_common.go @@ -0,0 +1,24 @@ +package ethbridge + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/supply" + "github.com/dawn-protocol/dawn/x/ethbridge/types" + oracle "github.com/dawn-protocol/dawn/x/oracle" + keeperLib "github.com/dawn-protocol/dawn/x/oracle/keeper" +) + +func CreateTestHandler(t *testing.T, consensusNeeded float64, validatorAmounts []int64) (sdk.Context, oracle.Keeper, bank.Keeper, supply.Keeper, []sdk.ValAddress, sdk.Handler) { + ctx, oracleKeeper, bankKeeper, supplyKeeper, validatorAddresses := oracle.CreateTestKeepers(t, consensusNeeded, validatorAmounts, ModuleName) + bridgeAccount := supply.NewEmptyModuleAccount(ModuleName, supply.Burner, supply.Minter) + supplyKeeper.SetModuleAccount(ctx, bridgeAccount) + + cdc := keeperLib.MakeTestCodec() + handler := NewHandler(oracleKeeper, supplyKeeper, types.DefaultCodespace, cdc) + + return ctx, oracleKeeper, bankKeeper, supplyKeeper, validatorAddresses, handler +} \ No newline at end of file diff --git a/x/ethbridge/types/claim.go b/x/ethbridge/types/claim.go new file mode 100644 index 0000000..3ed6631 --- /dev/null +++ b/x/ethbridge/types/claim.go @@ -0,0 +1,101 @@ +package types + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dawn-protocol/dawn/x/oracle" +) + +type EthBridgeClaim struct { + EthereumChainID int `json:"ethereum_chain_id"` + BridgeContractAddress EthereumAddress `json:"bridge_contract_address"` + Nonce int `json:"nonce"` + Symbol string `json:"symbol"` + TokenContractAddress EthereumAddress `json:"token_contract_address"` + EthereumSender EthereumAddress `json:"ethereum_sender"` + CosmosReceiver sdk.AccAddress `json:"cosmos_receiver"` + ValidatorAddress sdk.ValAddress `json:"validator_address"` + Amount sdk.Coins `json:"amount"` +} + +// NewEthBridgeClaim is a constructor function for NewEthBridgeClaim +func NewEthBridgeClaim(ethereumChainID int, bridgeContract EthereumAddress, nonce int, symbol string, tokenContact EthereumAddress, ethereumSender EthereumAddress, cosmosReceiver sdk.AccAddress, validator sdk.ValAddress, amount sdk.Coins) EthBridgeClaim { + return EthBridgeClaim{ + EthereumChainID: ethereumChainID, + BridgeContractAddress: bridgeContract, + Nonce: nonce, + Symbol: symbol, + TokenContractAddress: tokenContact, + EthereumSender: ethereumSender, + CosmosReceiver: cosmosReceiver, + ValidatorAddress: validator, + Amount: amount, + } +} + +// OracleClaimContent is the details of how the content of the claim for each validator will be stored in the oracle +type OracleClaimContent struct { + CosmosReceiver sdk.AccAddress `json:"cosmos_receiver"` + Amount sdk.Coins `json:"amount"` +} + +// NewOracleClaimContent is a constructor function for OracleClaim +func NewOracleClaimContent(cosmosReceiver sdk.AccAddress, amount sdk.Coins) OracleClaimContent { + return OracleClaimContent{ + CosmosReceiver: cosmosReceiver, + Amount: amount, + } +} + +// CreateOracleClaimFromEthClaim converts a specific ethereum bridge claim to a general oracle claim to be used by +// the oracle module. The oracle module expects every claim for a particular prophecy to have the same id, so this id +// must be created in a deterministic way that all validators can follow. For this, we use the Nonce an Ethereum Sender provided, +// as all validators will see this same data from the smart contract. +func CreateOracleClaimFromEthClaim(cdc *codec.Codec, ethClaim EthBridgeClaim) (oracle.Claim, error) { + oracleID := strconv.Itoa(ethClaim.EthereumChainID) + strconv.Itoa(ethClaim.Nonce) + ethClaim.EthereumSender.String() + claimContent := NewOracleClaimContent(ethClaim.CosmosReceiver, ethClaim.Amount) + claimBytes, err := json.Marshal(claimContent) + if err != nil { + return oracle.Claim{}, err + } + claimString := string(claimBytes) + claim := oracle.NewClaim(oracleID, ethClaim.ValidatorAddress, claimString) + return claim, nil +} + +// CreateEthClaimFromOracleString converts a string from any generic claim from the oracle module into an ethereum bridge specific claim. +func CreateEthClaimFromOracleString(ethereumChainID int, bridgeContract EthereumAddress, nonce int, symbol string, tokenContract EthereumAddress, ethereumAddress EthereumAddress, validator sdk.ValAddress, oracleClaimString string) (EthBridgeClaim, sdk.Error) { + oracleClaim, err := CreateOracleClaimFromOracleString(oracleClaimString) + if err != nil { + return EthBridgeClaim{}, err + } + + return NewEthBridgeClaim( + ethereumChainID, + bridgeContract, + nonce, + symbol, + tokenContract, + ethereumAddress, + oracleClaim.CosmosReceiver, + validator, + oracleClaim.Amount, + ), nil +} + +// CreateOracleClaimFromOracleString converts a JSON string into an OracleClaimContent struct used by this module. In general, it is +// expected that the oracle module will store claims in this JSON format and so this should be used to convert oracle claims. +func CreateOracleClaimFromOracleString(oracleClaimString string) (OracleClaimContent, sdk.Error) { + var oracleClaimContent OracleClaimContent + + bz := []byte(oracleClaimString) + if err := json.Unmarshal(bz, &oracleClaimContent); err != nil { + return OracleClaimContent{}, sdk.ErrInternal(fmt.Sprintf("failed to parse claim: %s", err.Error())) + } + + return oracleClaimContent, nil +} \ No newline at end of file diff --git a/x/ethbridge/types/codec.go b/x/ethbridge/types/codec.go new file mode 100644 index 0000000..d757f37 --- /dev/null +++ b/x/ethbridge/types/codec.go @@ -0,0 +1,10 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +// RegisterCodec registers concrete types on the Amino codec +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(MsgCreateEthBridgeClaim{}, "ethbridge/MsgCreateEthBridgeClaim", nil) +} diff --git a/x/ethbridge/types/errors.go b/x/ethbridge/types/errors.go new file mode 100644 index 0000000..57f7f90 --- /dev/null +++ b/x/ethbridge/types/errors.go @@ -0,0 +1,38 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// CodeType local code type +type CodeType = sdk.CodeType + +// Exported code type numbers +const ( + DefaultCodespace sdk.CodespaceType = ModuleName + + CodeInvalidEthNonce CodeType = 1 + CodeInvalidEthAddress CodeType = 2 + CodeErrJSONMarshalling CodeType = 3 + CodeInvalidEthSymbol CodeType = 4 +) + +// ErrInvalidEthNonce implements sdk.Error. +func ErrInvalidEthNonce(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidEthNonce, "invalid ethereum nonce provided, must be >= 0") +} + +// ErrInvalidEthAddress implements sdk.Error. +func ErrInvalidEthAddress(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidEthAddress, "invalid ethereum address provided, must be a valid hex-encoded Ethereum address") +} + +// ErrJSONMarshalling implements sdk.Error. +func ErrJSONMarshalling(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeErrJSONMarshalling, "error marshalling JSON for this claim") +} + +// ErrInvalidEthSymbol implements sdk.Error. +func ErrInvalidEthSymbol(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidEthSymbol, "invalid symbol provided, symbol \"eth\" must have null address set as token contract address") +} \ No newline at end of file diff --git a/x/ethbridge/types/ethereum.go b/x/ethbridge/types/ethereum.go new file mode 100644 index 0000000..9f3cfc0 --- /dev/null +++ b/x/ethbridge/types/ethereum.go @@ -0,0 +1,32 @@ +package types + +import ( + "fmt" + "reflect" + + gethCommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// EthereumAddress defines a standard ethereum address +type EthereumAddress gethCommon.Address + +// NewEthereumAddress is a constructor function for EthereumAddress +func NewEthereumAddress(address string) EthereumAddress { + return EthereumAddress(gethCommon.HexToAddress(address)) +} + +// Route should return the name of the module +func (ethAddr EthereumAddress) String() string { + return gethCommon.Address(ethAddr).String() +} + +// MarshalJSON marshals the etherum address to JSON +func (ethAddr EthereumAddress) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("\"%v\"", ethAddr.String())), nil +} + +// UnmarshalJSON unmarshals an ethereum address +func (ethAddr *EthereumAddress) UnmarshalJSON(input []byte) error { + return hexutil.UnmarshalFixedJSON(reflect.TypeOf(gethCommon.Address{}), input, ethAddr[:]) +} diff --git a/x/ethbridge/types/events.go b/x/ethbridge/types/events.go new file mode 100644 index 0000000..b196c2d --- /dev/null +++ b/x/ethbridge/types/events.go @@ -0,0 +1,14 @@ +package types + +// Ethbridge module event types +var ( + EventTypeCreateClaim = "create_claim" + EventTypeProphecyStatus = "prophecy_status" + + AttributeKeyEthereumSender = "ethereum_sender" + AttributeKeyCosmosReceiver = "cosmos_receiver" + AttributeKeyAmount = "amount" + AttributeKeyStatus = "status" + + AttributeValueCategory = ModuleName +) \ No newline at end of file diff --git a/x/ethbridge/types/keys.go b/x/ethbridge/types/keys.go new file mode 100644 index 0000000..ae7efad --- /dev/null +++ b/x/ethbridge/types/keys.go @@ -0,0 +1,15 @@ +package types + +const ( + // ModuleName is the name of the ethereum bridge module + ModuleName = "ethbridge" + + // StoreKey is the string store representation + StoreKey = ModuleName + + // QuerierRoute is the querier route for the ethereum bridge module + QuerierRoute = ModuleName + + // RouterKey is the msg router key for the ethereum bridge module + RouterKey = ModuleName +) diff --git a/x/ethbridge/types/msgs.go b/x/ethbridge/types/msgs.go new file mode 100644 index 0000000..0d0a5b2 --- /dev/null +++ b/x/ethbridge/types/msgs.go @@ -0,0 +1,84 @@ +package types + +import ( + "encoding/json" + "fmt" + "strings" + + gethCommon "github.com/ethereum/go-ethereum/common" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// MsgCreateEthBridgeClaim defines a message for creating claims on the ethereum bridge +type MsgCreateEthBridgeClaim EthBridgeClaim + +// NewMsgCreateEthBridgeClaim is a constructor function for MsgCreateBridgeClaim +func NewMsgCreateEthBridgeClaim(ethBridgeClaim EthBridgeClaim) MsgCreateEthBridgeClaim { + return MsgCreateEthBridgeClaim(ethBridgeClaim) +} + +// Route should return the name of the module +func (msg MsgCreateEthBridgeClaim) Route() string { return RouterKey } + +// Type should return the action +func (msg MsgCreateEthBridgeClaim) Type() string { return "create_bridge_claim" } + +// ValidateBasic runs stateless checks on the message +func (msg MsgCreateEthBridgeClaim) ValidateBasic() sdk.Error { + if msg.CosmosReceiver.Empty() { + return sdk.ErrInvalidAddress(msg.CosmosReceiver.String()) + } + + if msg.ValidatorAddress.Empty() { + return sdk.ErrInvalidAddress(msg.ValidatorAddress.String()) + } + + if msg.Nonce < 0 { + return ErrInvalidEthNonce(DefaultCodespace) + } + + if !gethCommon.IsHexAddress(msg.EthereumSender.String()) { + return ErrInvalidEthAddress(DefaultCodespace) + } + if !gethCommon.IsHexAddress(msg.BridgeContractAddress.String()) { + return ErrInvalidEthAddress(DefaultCodespace) + } + if strings.ToLower(msg.Symbol) == "eth" && msg.TokenContractAddress != NewEthereumAddress("0x0000000000000000000000000000000000000000") { + return ErrInvalidEthSymbol(DefaultCodespace) + } + return nil +} + +// GetSignBytes encodes the message for signing +func (msg MsgCreateEthBridgeClaim) GetSignBytes() []byte { + b, err := json.Marshal(msg) + if err != nil { + panic(err) + } + return sdk.MustSortJSON(b) +} + +// GetSigners defines whose signature is required +func (msg MsgCreateEthBridgeClaim) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{sdk.AccAddress(msg.ValidatorAddress)} +} + +// MapOracleClaimsToEthBridgeClaims maps a set of generic oracle claim data into EthBridgeClaim objects +func MapOracleClaimsToEthBridgeClaims(ethereumChainID int, bridgeContract EthereumAddress, nonce int, symbol string, tokenContract EthereumAddress, ethereumSender EthereumAddress, oracleValidatorClaims map[string]string, f func(int, EthereumAddress, int, string, EthereumAddress, EthereumAddress, sdk.ValAddress, string) (EthBridgeClaim, sdk.Error)) ([]EthBridgeClaim, sdk.Error) { + mappedClaims := make([]EthBridgeClaim, len(oracleValidatorClaims)) + i := 0 + for validatorBech32, validatorClaim := range oracleValidatorClaims { + validatorAddress, parseErr := sdk.ValAddressFromBech32(validatorBech32) + if parseErr != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse claim: %s", parseErr)) + } + mappedClaim, err := f(ethereumChainID, bridgeContract, nonce, symbol, tokenContract, ethereumSender, validatorAddress, validatorClaim) + if err != nil { + return nil, err + } + mappedClaims[i] = mappedClaim + i++ + } + return mappedClaims, nil +} \ No newline at end of file diff --git a/x/ethbridge/types/querier.go b/x/ethbridge/types/querier.go new file mode 100644 index 0000000..0731836 --- /dev/null +++ b/x/ethbridge/types/querier.go @@ -0,0 +1,55 @@ +package types + +import ( + "encoding/json" + "fmt" + + "github.com/dawn-protocol/dawn/x/oracle" +) + +// QueryEthProphecyParams defines the params for the following queries: +// - 'custom/ethbridge/prophecies/' +type QueryEthProphecyParams struct { + EthereumChainID int `json:"ethereum_chain_id"` + BridgeContractAddress EthereumAddress `json:"bridge_contract_address"` + Nonce int `json:"nonce"` + Symbol string `json:"symbol"` + TokenContractAddress EthereumAddress `json:"token_contract_address"` + EthereumSender EthereumAddress `json:"ethereum_sender"` +} + +// QueryEthProphecyParams creates a new QueryEthProphecyParams +func NewQueryEthProphecyParams(ethereumChainID int, bridgeContractAddress EthereumAddress, nonce int, symbol string, tokenContractAddress EthereumAddress, ethereumSender EthereumAddress) QueryEthProphecyParams { + return QueryEthProphecyParams{ + EthereumChainID: ethereumChainID, + BridgeContractAddress: bridgeContractAddress, + Nonce: nonce, + Symbol: symbol, + TokenContractAddress: tokenContractAddress, + EthereumSender: ethereumSender, + } +} + +// Query Result Payload for an eth prophecy query +type QueryEthProphecyResponse struct { + ID string `json:"id"` + Status oracle.Status `json:"status"` + Claims []EthBridgeClaim `json:"claims"` +} + +func NewQueryEthProphecyResponse(id string, status oracle.Status, claims []EthBridgeClaim) QueryEthProphecyResponse { + return QueryEthProphecyResponse{ + ID: id, + Status: status, + Claims: claims, + } +} + +func (response QueryEthProphecyResponse) String() string { + prophecyJSON, err := json.Marshal(response) + if err != nil { + return fmt.Sprintf("Error marshalling json: %v", err) + } + + return string(prophecyJSON) +} \ No newline at end of file diff --git a/x/ethbridge/types/test_common.go b/x/ethbridge/types/test_common.go new file mode 100644 index 0000000..b89df92 --- /dev/null +++ b/x/ethbridge/types/test_common.go @@ -0,0 +1,60 @@ +package types + +import ( + "testing" + + "github.com/dawn-protocol/dawn/x/oracle" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + TestEthereumChainID = 3 + TestBridgeContractAddress = "0xC4cE93a5699c68241fc2fB503Fb0f21724A624BB" + TestAddress = "cosmos1gn8409qq9hnrxde37kuxwx5hrxpfpv8426szuv" + TestValidator = "cosmos1xdp5tvt7lxh8rf9xx07wy2xlagzhq24ha48xtq" + TestNonce = 0 + TestSymbol = "eth" + TestTokenContractAddress = "0x0000000000000000000000000000000000000000" + TestEthereumAddress = "0x7B95B6EC7EbD73572298cEf32Bb54FA408207359" + AltTestEthereumAddress = "0x7B95B6EC7EbD73572298cEf32Bb54FA408207344" + TestCoins = "10ethereum" + AltTestCoins = "12ethereum" +) + +//Ethereum-bridge specific stuff +func CreateTestEthMsg(t *testing.T, validatorAddress sdk.ValAddress) MsgCreateEthBridgeClaim { + testEthereumAddress := NewEthereumAddress(TestEthereumAddress) + testContractAddress := NewEthereumAddress(TestBridgeContractAddress) + testTokenAddress := NewEthereumAddress(TestTokenContractAddress) + ethClaim := CreateTestEthClaim(t, testContractAddress, testTokenAddress, validatorAddress, testEthereumAddress, TestCoins) + ethMsg := NewMsgCreateEthBridgeClaim(ethClaim) + return ethMsg +} + +func CreateTestEthClaim(t *testing.T, testContractAddress EthereumAddress, testTokenAddress EthereumAddress, validatorAddress sdk.ValAddress, testEthereumAddress EthereumAddress, coins string) EthBridgeClaim { + testCosmosAddress, err1 := sdk.AccAddressFromBech32(TestAddress) + amount, err2 := sdk.ParseCoins(coins) + require.NoError(t, err1) + require.NoError(t, err2) + ethClaim := NewEthBridgeClaim(TestEthereumChainID, testContractAddress, TestNonce, TestSymbol, testTokenAddress, testEthereumAddress, testCosmosAddress, validatorAddress, amount) + return ethClaim +} + +func CreateTestQueryEthProphecyResponse(cdc *codec.Codec, t *testing.T, validatorAddress sdk.ValAddress) QueryEthProphecyResponse { + testEthereumAddress := NewEthereumAddress(TestEthereumAddress) + testContractAddress := NewEthereumAddress(TestBridgeContractAddress) + testTokenAddress := NewEthereumAddress(TestTokenContractAddress) + ethBridgeClaim := CreateTestEthClaim(t, testContractAddress, testTokenAddress, validatorAddress, testEthereumAddress, TestCoins) + oracleClaim, _ := CreateOracleClaimFromEthClaim(cdc, ethBridgeClaim) + ethBridgeClaims := []EthBridgeClaim{ethBridgeClaim} + + return NewQueryEthProphecyResponse( + oracleClaim.ID, + oracle.NewStatus(oracle.PendingStatusText, ""), + ethBridgeClaims, + ) +} \ No newline at end of file diff --git a/x/oracle/alias.go b/x/oracle/alias.go new file mode 100644 index 0000000..5c8d103 --- /dev/null +++ b/x/oracle/alias.go @@ -0,0 +1,68 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/dawn-protocol/dawn/x/oracle/keeper +// ALIASGEN: github.com/dawn-protocol/dawn/x/oracle/types +package oracle + +import ( + "github.com/dawn-protocol/dawn/x/oracle/keeper" + "github.com/dawn-protocol/dawn/x/oracle/types" +) + +const ( + DefaultCodespace = types.DefaultCodespace + DefaultConsensusNeeded = types.DefaultConsensusNeeded + CodeProphecyNotFound = types.CodeProphecyNotFound + CodeMinimumConsensusNeededInvalid = types.CodeMinimumConsensusNeededInvalid + CodeNoClaims = types.CodeNoClaims + CodeInvalidIdentifier = types.CodeInvalidIdentifier + CodeProphecyFinalized = types.CodeProphecyFinalized + CodeDuplicateMessage = types.CodeDuplicateMessage + CodeInvalidClaim = types.CodeInvalidClaim + CodeInvalidValidator = types.CodeInvalidValidator + CodeInternalDB = types.CodeInternalDB + ModuleName = types.ModuleName + StoreKey = types.StoreKey + QuerierRoute = types.QuerierRoute + RouterKey = types.RouterKey + PendingStatusText = types.PendingStatusText + SuccessStatusText = types.SuccessStatusText + FailedStatusText = types.FailedStatusText +) + +var ( + // functions aliases + NewKeeper = keeper.NewKeeper + CreateTestAddrs = keeper.CreateTestAddrs + CreateTestPubKeys = keeper.CreateTestPubKeys + CreateTestKeepers = keeper.CreateTestKeepers + + NewClaim = types.NewClaim + ErrProphecyNotFound = types.ErrProphecyNotFound + ErrMinimumConsensusNeededInvalid = types.ErrMinimumConsensusNeededInvalid + ErrNoClaims = types.ErrNoClaims + ErrInvalidIdentifier = types.ErrInvalidIdentifier + ErrProphecyFinalized = types.ErrProphecyFinalized + ErrDuplicateMessage = types.ErrDuplicateMessage + ErrInvalidClaim = types.ErrInvalidClaim + ErrInvalidValidator = types.ErrInvalidValidator + ErrInternalDB = types.ErrInternalDB + NewProphecy = types.NewProphecy + NewEmptyProphecy = types.NewEmptyProphecy + NewStatus = types.NewStatus + + // variable aliases + StatusTextToString = types.StatusTextToString + StringToStatusText = types.StringToStatusText +) + +type ( + Keeper = keeper.Keeper + Claim = types.Claim + CodeType = types.CodeType + Prophecy = types.Prophecy + DBProphecy = types.DBProphecy + Status = types.Status + StatusText = types.StatusText +) \ No newline at end of file diff --git a/x/oracle/keeper/keeper.go b/x/oracle/keeper/keeper.go new file mode 100644 index 0000000..f424d8d --- /dev/null +++ b/x/oracle/keeper/keeper.go @@ -0,0 +1,148 @@ +package keeper + +import ( + "fmt" + "strings" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/dawn-protocol/dawn/x/oracle/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/staking" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Keeper maintains the link to data storage and exposes getter/setter methods for the various parts of the state machine +type Keeper struct { + cdc *codec.Codec // The wire codec for binary encoding/decoding. + storeKey sdk.StoreKey // Unexposed key to access store from sdk.Context + + stakeKeeper staking.Keeper + codespace sdk.CodespaceType + + // TODO: use this as param instead + consensusNeeded float64 // The minimum % of stake needed to sign claims in order for consensus to occur +} + +// NewKeeper creates new instances of the oracle Keeper +func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, stakeKeeper staking.Keeper, codespace sdk.CodespaceType, consensusNeeded float64) Keeper { + if consensusNeeded <= 0 || consensusNeeded > 1 { + panic(types.ErrMinimumConsensusNeededInvalid(codespace).Error()) + } + return Keeper{ + cdc: cdc, + storeKey: storeKey, + stakeKeeper: stakeKeeper, + codespace: codespace, + consensusNeeded: consensusNeeded, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// Codespace returns the codespace +func (k Keeper) Codespace() sdk.CodespaceType { + return k.codespace +} + +// GetProphecy gets the entire prophecy data struct for a given id +func (k Keeper) GetProphecy(ctx sdk.Context, id string) (types.Prophecy, sdk.Error) { + if id == "" { + return types.NewEmptyProphecy(), types.ErrInvalidIdentifier(k.Codespace()) + } + store := ctx.KVStore(k.storeKey) + bz := store.Get([]byte(id)) + if bz == nil { + return types.NewEmptyProphecy(), types.ErrProphecyNotFound(k.Codespace()) + } + var dbProphecy types.DBProphecy + k.cdc.MustUnmarshalBinaryBare(bz, &dbProphecy) + + deSerializedProphecy, err := dbProphecy.DeserializeFromDB() + if err != nil { + return types.NewEmptyProphecy(), types.ErrInternalDB(k.Codespace(), err) + } + return deSerializedProphecy, nil +} + +// setProphecy saves a prophecy with an initial claim +func (k Keeper) setProphecy(ctx sdk.Context, prophecy types.Prophecy) sdk.Error { + if prophecy.ID == "" { + return types.ErrInvalidIdentifier(k.Codespace()) + } + if len(prophecy.ClaimValidators) == 0 { + return types.ErrNoClaims(k.Codespace()) + } + store := ctx.KVStore(k.storeKey) + serializedProphecy, err := prophecy.SerializeForDB() + if err != nil { + return types.ErrInternalDB(k.Codespace(), err) + } + store.Set([]byte(prophecy.ID), k.cdc.MustMarshalBinaryBare(serializedProphecy)) + return nil +} + +// ProcessClaim TODO: write description +func (k Keeper) ProcessClaim(ctx sdk.Context, claim types.Claim) (types.Status, sdk.Error) { + activeValidator := k.checkActiveValidator(ctx, claim.ValidatorAddress) + if !activeValidator { + return types.Status{}, types.ErrInvalidValidator(k.Codespace()) + } + if strings.TrimSpace(claim.Content) == "" { + return types.Status{}, types.ErrInvalidClaim(k.Codespace()) + } + prophecy, err := k.GetProphecy(ctx, claim.ID) + if err != nil { + if err.Code() != types.CodeProphecyNotFound { + return types.Status{}, err + } + prophecy = types.NewProphecy(claim.ID) + } else { + if prophecy.Status.Text == types.SuccessStatusText || prophecy.Status.Text == types.FailedStatusText { + return types.Status{}, types.ErrProphecyFinalized(k.Codespace()) + } + if prophecy.ValidatorClaims[claim.ValidatorAddress.String()] != "" { + return types.Status{}, types.ErrDuplicateMessage(k.Codespace()) + } + } + prophecy.AddClaim(claim.ValidatorAddress, claim.Content) + prophecy = k.processCompletion(ctx, prophecy) + err = k.setProphecy(ctx, prophecy) + if err != nil { + return types.Status{}, err + } + return prophecy.Status, nil +} + +func (k Keeper) checkActiveValidator(ctx sdk.Context, validatorAddress sdk.ValAddress) bool { + validator, found := k.stakeKeeper.GetValidator(ctx, validatorAddress) + if !found { + return false + } + bondStatus := validator.GetStatus() + return bondStatus == sdk.Bonded +} + +// processCompletion looks at a given prophecy an assesses whether the claim with the highest power on that prophecy has enough +// power to be considered successful, or alternatively, will never be able to become successful due to not enough validation power being +// left to push it over the threshold required for consensus. +func (k Keeper) processCompletion(ctx sdk.Context, prophecy types.Prophecy) types.Prophecy { + highestClaim, highestClaimPower, totalClaimsPower := prophecy.FindHighestClaim(ctx, k.stakeKeeper) + totalPower := k.stakeKeeper.GetLastTotalPower(ctx) + highestConsensusRatio := float64(highestClaimPower) / float64(totalPower.Int64()) + remainingPossibleClaimPower := totalPower.Int64() - totalClaimsPower + highestPossibleClaimPower := highestClaimPower + remainingPossibleClaimPower + highestPossibleConsensusRatio := float64(highestPossibleClaimPower) / float64(totalPower.Int64()) + if highestConsensusRatio >= k.consensusNeeded { + prophecy.Status.Text = types.SuccessStatusText + prophecy.Status.FinalClaim = highestClaim + } else if highestPossibleConsensusRatio < k.consensusNeeded { + prophecy.Status.Text = types.FailedStatusText + } + return prophecy +} diff --git a/x/oracle/keeper/keeper_test.go b/x/oracle/keeper/keeper_test.go new file mode 100644 index 0000000..8d842c0 --- /dev/null +++ b/x/oracle/keeper/keeper_test.go @@ -0,0 +1,265 @@ +package keeper + +import ( + "strings" + "testing" + + "github.com/dawn-protocol/dawn/x/oracle/types" + "github.com/stretchr/testify/require" +) + +func TestCreateGetProphecy(t *testing.T) { + ctx, keeper, _, _, validatorAddresses := CreateTestKeepers(t, 0.7, []int64{3, 7}, "") + + validator1Pow3 := validatorAddresses[0] + + //Test normal Creation + oracleClaim := types.NewClaim(TestID, validator1Pow3, TestString) + status, err := keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.PendingStatusText) + + //Test bad Creation with blank id + oracleClaim = types.NewClaim("", validator1Pow3, TestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.Error(t, err) + + //Test bad Creation with blank claim + oracleClaim = types.NewClaim(TestID, validator1Pow3, "") + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.Error(t, err) + + //Test retrieval + prophecy, err := keeper.GetProphecy(ctx, TestID) + require.NoError(t, err) + require.Equal(t, prophecy.ID, TestID) + require.Equal(t, prophecy.Status.Text, types.PendingStatusText) + require.Equal(t, prophecy.ClaimValidators[TestString][0], validator1Pow3) + require.Equal(t, prophecy.ValidatorClaims[validator1Pow3.String()], TestString) +} + +func TestBadConsensusForOracle(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } + }() + _, _, _, _, _ = CreateTestKeepers(t, 0, []int64{10}, "") + _, _, _, _, _ = CreateTestKeepers(t, 1.2, []int64{10}, "") +} + +func TestBadMsgs(t *testing.T) { + ctx, keeper, _, _, validatorAddresses := CreateTestKeepers(t, 0.6, []int64{3, 3}, "") + + validator1Pow3 := validatorAddresses[0] + + //Test empty claim + oracleClaim := types.NewClaim(TestID, validator1Pow3, "") + status, err := keeper.ProcessClaim(ctx, oracleClaim) + require.Error(t, err) + require.Equal(t, status.FinalClaim, "") + require.True(t, strings.Contains(err.Error(), "claim cannot be empty string")) + + //Test normal Creation + oracleClaim = types.NewClaim(TestID, validator1Pow3, TestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.PendingStatusText) + + //Test duplicate message + oracleClaim = types.NewClaim(TestID, validator1Pow3, TestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "already processed message from validator for this id")) + + //Test second but non duplicate message + oracleClaim = types.NewClaim(TestID, validator1Pow3, AlternateTestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "already processed message from validator for this id")) +} + +func TestSuccessfulProphecy(t *testing.T) { + ctx, keeper, _, _, validatorAddresses := CreateTestKeepers(t, 0.6, []int64{3, 3, 4}, "") + + validator1Pow3 := validatorAddresses[0] + validator2Pow3 := validatorAddresses[1] + validator3Pow4 := validatorAddresses[2] + + //Test first claim + oracleClaim := types.NewClaim(TestID, validator1Pow3, TestString) + status, err := keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.PendingStatusText) + + //Test second claim completes and finalizes to success + oracleClaim = types.NewClaim(TestID, validator2Pow3, TestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.SuccessStatusText) + require.Equal(t, status.FinalClaim, TestString) + + //Test third claim not possible + oracleClaim = types.NewClaim(TestID, validator3Pow4, TestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "prophecy already finalized")) +} + +func TestSuccessfulProphecyWithDisagreement(t *testing.T) { + ctx, keeper, _, _, validatorAddresses := CreateTestKeepers(t, 0.6, []int64{3, 3, 4}, "") + + validator1Pow3 := validatorAddresses[0] + validator2Pow3 := validatorAddresses[1] + validator3Pow4 := validatorAddresses[2] + + //Test first claim + oracleClaim := types.NewClaim(TestID, validator1Pow3, TestString) + status, err := keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.PendingStatusText) + + //Test second disagreeing claim processed fine + oracleClaim = types.NewClaim(TestID, validator2Pow3, AlternateTestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.PendingStatusText) + + //Test third claim agrees and finalizes to success + oracleClaim = types.NewClaim(TestID, validator3Pow4, TestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.SuccessStatusText) + require.Equal(t, status.FinalClaim, TestString) +} + +func TestFailedProphecy(t *testing.T) { + ctx, keeper, _, _, validatorAddresses := CreateTestKeepers(t, 0.6, []int64{3, 3, 4}, "") + + validator1Pow3 := validatorAddresses[0] + validator2Pow3 := validatorAddresses[1] + validator3Pow4 := validatorAddresses[2] + + //Test first claim + oracleClaim := types.NewClaim(TestID, validator1Pow3, TestString) + status, err := keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.PendingStatusText) + + //Test second disagreeing claim processed fine + oracleClaim = types.NewClaim(TestID, validator2Pow3, AlternateTestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.PendingStatusText) + require.Equal(t, status.FinalClaim, "") + + //Test third disagreeing claim processed fine and prophecy fails + oracleClaim = types.NewClaim(TestID, validator3Pow4, AnotherAlternateTestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.FailedStatusText) + require.Equal(t, status.FinalClaim, "") +} + +func TestPowerOverrule(t *testing.T) { + //Testing with 2 validators but one has high enough power to overrule + ctx, keeper, _, _, validatorAddresses := CreateTestKeepers(t, 0.7, []int64{3, 7}, "") + + validator1Pow3 := validatorAddresses[0] + validator2Pow7 := validatorAddresses[1] + + //Test first claim + oracleClaim := types.NewClaim(TestID, validator1Pow3, TestString) + status, err := keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.PendingStatusText) + + //Test second disagreeing claim processed fine and finalized to its bytes + oracleClaim = types.NewClaim(TestID, validator2Pow7, AlternateTestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.SuccessStatusText) + require.Equal(t, status.FinalClaim, AlternateTestString) +} +func TestPowerAternate(t *testing.T) { + //Test alternate power setup with validators of 5/4/3/9 and total power 22 and 12/21 required + ctx, keeper, _, _, validatorAddresses := CreateTestKeepers(t, 0.571, []int64{5, 4, 3, 9}, "") + + validator1Pow5 := validatorAddresses[0] + validator2Pow4 := validatorAddresses[1] + validator3Pow3 := validatorAddresses[2] + validator4Pow9 := validatorAddresses[3] + + //Test claim by v1 + oracleClaim := types.NewClaim(TestID, validator1Pow5, TestString) + status, err := keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.PendingStatusText) + + //Test claim by v2 + oracleClaim = types.NewClaim(TestID, validator2Pow4, TestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.PendingStatusText) + + //Test alternate claim by v4 + oracleClaim = types.NewClaim(TestID, validator4Pow9, AlternateTestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.PendingStatusText) + + //Test finalclaim by v3 + oracleClaim = types.NewClaim(TestID, validator3Pow3, TestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.SuccessStatusText) + require.Equal(t, status.FinalClaim, TestString) +} + +func TestMultipleProphecies(t *testing.T) { + //Test multiple prophecies running in parallel work fine as expected + ctx, keeper, _, _, validatorAddresses := CreateTestKeepers(t, 0.7, []int64{3, 7}, "") + + validator1Pow3 := validatorAddresses[0] + validator2Pow7 := validatorAddresses[1] + + //Test claim on first id with first validator + oracleClaim := types.NewClaim(TestID, validator1Pow3, TestString) + status, err := keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.PendingStatusText) + + //Test claim on second id with second validator + oracleClaim = types.NewClaim(AlternateTestID, validator2Pow7, AlternateTestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.SuccessStatusText) + require.Equal(t, status.FinalClaim, AlternateTestString) + + //Test claim on first id with second validator + oracleClaim = types.NewClaim(TestID, validator2Pow7, TestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.NoError(t, err) + require.Equal(t, status.Text, types.SuccessStatusText) + require.Equal(t, status.FinalClaim, TestString) + + //Test claim on second id with first validator + oracleClaim = types.NewClaim(AlternateTestID, validator1Pow3, AlternateTestString) + status, err = keeper.ProcessClaim(ctx, oracleClaim) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "prophecy already finalized")) +} + +func TestNonValidator(t *testing.T) { + //Test multiple prophecies running in parallel work fine as expected + ctx, keeper, _, _, _ := CreateTestKeepers(t, 0.7, []int64{3, 7}, "") + + _, testValidatorAddresses := CreateTestAddrs(10) + inActiveValidatorAddress := testValidatorAddresses[9] + + //Test claim on first id with first validator + oracleClaim := types.NewClaim(TestID, inActiveValidatorAddress, TestString) + _, err := keeper.ProcessClaim(ctx, oracleClaim) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "claim must be made by actively bonded validator")) +} \ No newline at end of file diff --git a/x/oracle/keeper/test_common.go b/x/oracle/keeper/test_common.go new file mode 100644 index 0000000..655b6e7 --- /dev/null +++ b/x/oracle/keeper/test_common.go @@ -0,0 +1,211 @@ +package keeper + +import ( + "bytes" + "encoding/hex" + "strconv" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/ed25519" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/supply" + "github.com/dawn-protocol/dawn/x/oracle/types" + "github.com/tendermint/tendermint/crypto" + tmtypes "github.com/tendermint/tendermint/types" +) + +const ( + TestID = "oracleID" + AlternateTestID = "altOracleID" + TestString = "{value: 5}" + AlternateTestString = "{value: 7}" + AnotherAlternateTestString = "{value: 9}" +) + +// CreateTestKeepers greates an Mock App, OracleKeeper, BankKeeper and ValidatorAddresses to be used for test input +func CreateTestKeepers(t *testing.T, consensusNeeded float64, validatorAmounts []int64, extraMaccPerm string) (sdk.Context, Keeper, bank.Keeper, supply.Keeper, []sdk.ValAddress) { + PKs := CreateTestPubKeys(500) + keyStaking := sdk.NewKVStoreKey(stakingtypes.StoreKey) + tkeyStaking := sdk.NewTransientStoreKey(stakingtypes.TStoreKey) + keyAcc := sdk.NewKVStoreKey(auth.StoreKey) + keyParams := sdk.NewKVStoreKey(params.StoreKey) + tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) + keySupply := sdk.NewKVStoreKey(supply.StoreKey) + keyOracle := sdk.NewKVStoreKey(types.StoreKey) + + db := dbm.NewMemDB() + ms := store.NewCommitMultiStore(db) + ms.MountStoreWithDB(tkeyStaking, sdk.StoreTypeTransient, nil) + ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) + ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyOracle, sdk.StoreTypeIAVL, db) + err := ms.LoadLatestVersion() + require.Nil(t, err) + + ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, false, nil) + ctx = ctx.WithConsensusParams( + &abci.ConsensusParams{ + Validator: &abci.ValidatorParams{ + PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519}, + }, + }, + ) + ctx = ctx.WithLogger(log.NewNopLogger()) + cdc := MakeTestCodec() + + feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName) + notBondedPool := supply.NewEmptyModuleAccount(stakingtypes.NotBondedPoolName, supply.Burner, supply.Staking) + bondPool := supply.NewEmptyModuleAccount(stakingtypes.BondedPoolName, supply.Burner, supply.Staking) + + blacklistedAddrs := make(map[string]bool) + blacklistedAddrs[feeCollectorAcc.GetAddress().String()] = true + blacklistedAddrs[notBondedPool.GetAddress().String()] = true + blacklistedAddrs[bondPool.GetAddress().String()] = true + + paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace) + + accountKeeper := auth.NewAccountKeeper( + cdc, // amino codec + keyAcc, // target store + paramsKeeper.Subspace(auth.DefaultParamspace), + auth.ProtoBaseAccount, // prototype + ) + + bankKeeper := bank.NewBaseKeeper( + accountKeeper, + paramsKeeper.Subspace(bank.DefaultParamspace), + bank.DefaultCodespace, + blacklistedAddrs, + ) + + maccPerms := map[string][]string{ + auth.FeeCollectorName: nil, + stakingtypes.NotBondedPoolName: {supply.Burner, supply.Staking}, + stakingtypes.BondedPoolName: {supply.Burner, supply.Staking}, + } + + if extraMaccPerm != "" { + maccPerms[extraMaccPerm] = []string{supply.Burner, supply.Minter} + } + + supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms) + + initTokens := sdk.TokensFromConsensusPower(10000) + totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens.MulRaw(int64(100)))) + + supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) + + stakingKeeper := staking.NewKeeper(cdc, keyStaking, tkeyStaking, supplyKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), stakingtypes.DefaultCodespace) + stakingKeeper.SetParams(ctx, stakingtypes.DefaultParams()) + oracleKeeper := NewKeeper(cdc, keyOracle, stakingKeeper, types.DefaultCodespace, consensusNeeded) + + // set module accounts + err = notBondedPool.SetCoins(totalSupply) + require.NoError(t, err) + + supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc) + supplyKeeper.SetModuleAccount(ctx, bondPool) + supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + // Setup validators + valAddrs := make([]sdk.ValAddress, len(validatorAmounts)) + for i, amount := range validatorAmounts { + valPubKey := PKs[i] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + valAddrs[i] = valAddr + valTokens := sdk.TokensFromConsensusPower(amount) + // test how the validator is set from a purely unbonbed pool + validator := stakingtypes.NewValidator(valAddr, valPubKey, stakingtypes.Description{}) + validator, _ = validator.AddTokensFromDel(valTokens) + stakingKeeper.SetValidator(ctx, validator) + stakingKeeper.SetValidatorByPowerIndex(ctx, validator) + stakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + } + + return ctx, oracleKeeper, bankKeeper, supplyKeeper, valAddrs +} + +// nolint: unparam +func CreateTestAddrs(numAddrs int) ([]sdk.AccAddress, []sdk.ValAddress) { + var addresses []sdk.AccAddress + var valAddresses []sdk.ValAddress + var buffer bytes.Buffer + + // start at 100 so we can make up to 999 test addresses with valid test addresses + for i := 100; i < (numAddrs + 100); i++ { + numString := strconv.Itoa(i) + buffer.WriteString("A58856F0FD53BF058B4909A21AEC019107BA6") //base address string + + buffer.WriteString(numString) //adding on final two digits to make addresses unique + res, _ := sdk.AccAddressFromHex(buffer.String()) + bech := res.String() + address := stakingkeeper.TestAddr(buffer.String(), bech) + valAddress := sdk.ValAddress(address) + addresses = append(addresses, address) + valAddresses = append(valAddresses, valAddress) + buffer.Reset() + } + return addresses, valAddresses +} + +// nolint: unparam +func CreateTestPubKeys(numPubKeys int) []crypto.PubKey { + var publicKeys []crypto.PubKey + var buffer bytes.Buffer + + //start at 10 to avoid changing 1 to 01, 2 to 02, etc + for i := 100; i < (numPubKeys + 100); i++ { + numString := strconv.Itoa(i) + buffer.WriteString("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AF") //base pubkey string + buffer.WriteString(numString) //adding on final two digits to make pubkeys unique + publicKeys = append(publicKeys, NewPubKey(buffer.String())) + buffer.Reset() + } + return publicKeys +} + +func NewPubKey(pk string) (res crypto.PubKey) { + pkBytes, err := hex.DecodeString(pk) + if err != nil { + panic(err) + } + //res, err = crypto.PubKeyFromBytes(pkBytes) + var pkEd ed25519.PubKeyEd25519 + copy(pkEd[:], pkBytes) + return pkEd +} + +// create a codec used only for testing +func MakeTestCodec() *codec.Codec { + var cdc = codec.New() + + // Register Msgs + cdc.RegisterInterface((*sdk.Msg)(nil), nil) + + // Register AppAccount + cdc.RegisterInterface((*auth.Account)(nil), nil) + cdc.RegisterConcrete(&auth.BaseAccount{}, "test/staking/BaseAccount", nil) + supply.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + staking.RegisterCodec(cdc) + bank.RegisterCodec(cdc) + + return cdc +} \ No newline at end of file diff --git a/x/oracle/module.go b/x/oracle/module.go new file mode 100644 index 0000000..256c0e0 --- /dev/null +++ b/x/oracle/module.go @@ -0,0 +1,131 @@ +package oracle + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// AppModuleBasic defines the basic application module used by the oracle module. +type AppModuleBasic struct{} + +var _ module.AppModuleBasic = AppModuleBasic{} + +// Name returns the oracle module's name. +func (AppModuleBasic) Name() string { + return ModuleName +} + +// RegisterCodec registers the oracle module's types for the given codec. +func (AppModuleBasic) RegisterCodec(_ *codec.Codec) {} + +// DefaultGenesis returns default genesis state as raw bytes for the oracle +// module. +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return nil +} + +// ValidateGenesis performs genesis state validation for the oracle module. +func (AppModuleBasic) ValidateGenesis(_ json.RawMessage) error { + return nil +} + +// RegisterRESTRoutes registers the REST routes for the oracle module. +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { +} + +// GetTxCmd returns the root tx command for the oracle module. +func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { + return nil +} + +// GetQueryCmd returns no root query command for the oracle module. +func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { + return nil +} + +//____________________________________________________________________________ + +// AppModuleSimulation defines the module simulation functions used by the oracle module. +type AppModuleSimulation struct{} + +// AppModule implements an application module for the oracle module. +type AppModule struct { + AppModuleBasic + AppModuleSimulation + + keeper Keeper +} + +// NewAppModule creates a new AppModule object +func NewAppModule(keeper Keeper) AppModule { + + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + AppModuleSimulation: AppModuleSimulation{}, + keeper: keeper, + } +} + +// Name returns the oracle module's name. +func (AppModule) Name() string { + return ModuleName +} + +// RegisterInvariants registers the oracle module invariants. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { +} + +// Route returns the message routing key for the oracle module. +func (AppModule) Route() string { + return RouterKey +} + +// NewHandler returns an sdk.Handler for the oracle module. +func (am AppModule) NewHandler() sdk.Handler { + return nil +} + +// QuerierRoute returns the oracle module's querier route name. +func (AppModule) QuerierRoute() string { + return "" +} + +// NewQuerierHandler returns the oracle module sdk.Querier. +func (am AppModule) NewQuerierHandler() sdk.Querier { + return nil +} + +// InitGenesis performs genesis initialization for the oracle module. It returns +// no validator updates. +func (am AppModule) InitGenesis(_ sdk.Context, _ json.RawMessage) []abci.ValidatorUpdate { + return nil +} + +// ExportGenesis returns the exported genesis state as raw bytes for the oracle +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + return nil +} + +// BeginBlock returns the begin blocker for the oracle module. +func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock returns the end blocker for the oracle module. It returns no validator +// updates. +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return nil +} diff --git a/x/oracle/types/claim.go b/x/oracle/types/claim.go new file mode 100644 index 0000000..de67ee6 --- /dev/null +++ b/x/oracle/types/claim.go @@ -0,0 +1,19 @@ +package types + +import sdk "github.com/cosmos/cosmos-sdk/types" + +// Claim contrains an arbitrary claim with arbitrary content made by a given validator +type Claim struct { + ID string `json:"id"` + ValidatorAddress sdk.ValAddress `json:"validator_address"` + Content string `json:"content"` +} + +// NewClaim returns a new Claim +func NewClaim(id string, validatorAddress sdk.ValAddress, content string) Claim { + return Claim{ + ID: id, + ValidatorAddress: validatorAddress, + Content: content, + } +} diff --git a/x/oracle/types/errors.go b/x/oracle/types/errors.go new file mode 100644 index 0000000..96ce499 --- /dev/null +++ b/x/oracle/types/errors.go @@ -0,0 +1,61 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Local code type +type CodeType = sdk.CodeType + +// Exported code type numbers +const ( + DefaultCodespace sdk.CodespaceType = ModuleName + + CodeProphecyNotFound CodeType = 1 + CodeMinimumConsensusNeededInvalid CodeType = 2 + CodeNoClaims CodeType = 3 + CodeInvalidIdentifier CodeType = 4 + CodeProphecyFinalized CodeType = 5 + CodeDuplicateMessage CodeType = 6 + CodeInvalidClaim CodeType = 7 + CodeInvalidValidator CodeType = 8 + CodeInternalDB CodeType = 9 +) + +func ErrProphecyNotFound(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeProphecyNotFound, "prophecy with given id not found") +} + +func ErrMinimumConsensusNeededInvalid(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeMinimumConsensusNeededInvalid, "minimum consensus proportion of validator staking power must be > 0 and <= 1") +} + +func ErrNoClaims(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeNoClaims, "cannot create prophecy without initial claim") +} + +func ErrInvalidIdentifier(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidIdentifier, "invalid identifier provided, must be a nonempty string") +} + +func ErrProphecyFinalized(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeProphecyFinalized, "prophecy already finalized") +} + +func ErrDuplicateMessage(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeDuplicateMessage, "already processed message from validator for this id") +} + +func ErrInvalidClaim(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidClaim, "claim cannot be empty string") +} + +func ErrInvalidValidator(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "claim must be made by actively bonded validator") +} + +func ErrInternalDB(codespace sdk.CodespaceType, err error) sdk.Error { + return sdk.NewError(codespace, CodeInternalDB, fmt.Sprintf("internal error serializing/deserializing prophecy: %s", err.Error())) +} diff --git a/x/oracle/types/keys.go b/x/oracle/types/keys.go new file mode 100644 index 0000000..0e3dd1b --- /dev/null +++ b/x/oracle/types/keys.go @@ -0,0 +1,15 @@ +package types + +const ( + // ModuleName is the name of the oracle module + ModuleName = "oracle" + + // StoreKey is the string store representation + StoreKey = ModuleName + + // QuerierRoute is the querier route for the oracle module + QuerierRoute = ModuleName + + // RouterKey is the msg router key for the oracle module + RouterKey = ModuleName +) diff --git a/x/oracle/types/prophecy.go b/x/oracle/types/prophecy.go new file mode 100644 index 0000000..1d73472 --- /dev/null +++ b/x/oracle/types/prophecy.go @@ -0,0 +1,150 @@ +package types + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/x/staking" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// DefaultConsensusNeeded defines the default consensus value required for a +// prophecy to be finalized +const DefaultConsensusNeeded float64 = 0.7 + +// Prophecy is a struct that contains all the metadata of an oracle ritual. +// Claims are indexed by the claim's validator bech32 address and by the claim's json value to allow +// for constant lookup times for any validation/verifiation checks of duplicate claims +// Each transaction, pending potential results are also calculated, stored and indexed by their byte result +// to allow discovery of consensus on any the result in constant time without having to sort or run +// through the list of claims to find the one with highest consensus +type Prophecy struct { + ID string `json:"id"` + Status Status `json:"status"` + //WARNING: Mappings are nondeterministic in Amino, an so iterating over them could result in consensus failure. New code should not iterate over the below 2 mappings. + ClaimValidators map[string][]sdk.ValAddress `json:"claim_validators"` //This is a mapping from a claim to the list of validators that made that claim. + ValidatorClaims map[string]string `json:"validator_claims"` //This is a mapping from a validator bech32 address to their claim +} + +// DBProphecy is what the prophecy becomes when being saved to the database. Tendermint/Amino does not support maps so we must serialize those variables into bytes. +type DBProphecy struct { + ID string `json:"id"` + Status Status `json:"status"` + ClaimValidators []byte `json:"claim_validators"` //This is a mapping from a claim to the list of validators that made that claim + ValidatorClaims []byte `json:"validator_claims"` //This is a mapping from a validator bech32 address to their claim +} + +// SerializeForDB serializes a prophecy into a DBProphecy +// TODO: Using gob here may mean that different tendermint clients in different languages may serialize/store +// prophecies in their db in different ways - check with @codereviewer if this is ok or if it introduces a risk of creating forks. +// Or maybe using a slower json serializer or Amino:JSON would be ok +func (prophecy Prophecy) SerializeForDB() (DBProphecy, error) { + claimValidators, err := json.Marshal(prophecy.ClaimValidators) + if err != nil { + return DBProphecy{}, err + } + + validatorClaims, err := json.Marshal(prophecy.ValidatorClaims) + if err != nil { + return DBProphecy{}, err + } + + return DBProphecy{ + ID: prophecy.ID, + Status: prophecy.Status, + ClaimValidators: claimValidators, + ValidatorClaims: validatorClaims, + }, nil +} + +// DeserializeFromDB deserializes a DBProphecy into a prophecy +func (dbProphecy DBProphecy) DeserializeFromDB() (Prophecy, error) { + var claimValidators map[string][]sdk.ValAddress + err := json.Unmarshal(dbProphecy.ClaimValidators, &claimValidators) + if err != nil { + return Prophecy{}, err + } + + var validatorClaims map[string]string + err = json.Unmarshal(dbProphecy.ValidatorClaims, &validatorClaims) + if err != nil { + return Prophecy{}, err + } + + return Prophecy{ + ID: dbProphecy.ID, + Status: dbProphecy.Status, + ClaimValidators: claimValidators, + ValidatorClaims: validatorClaims, + }, nil +} + +// AddClaim adds a given claim to this prophecy +func (prophecy Prophecy) AddClaim(validator sdk.ValAddress, claim string) { + claimValidators := prophecy.ClaimValidators[claim] + prophecy.ClaimValidators[claim] = append(claimValidators, validator) + + validatorBech32 := validator.String() + prophecy.ValidatorClaims[validatorBech32] = claim +} + +// FindHighestClaim looks through all the existing claims on a given prophecy. It adds up the total power across +// all claims and returns the highest claim, power for that claim, and total power claimed on the prophecy overall. +func (prophecy Prophecy) FindHighestClaim(ctx sdk.Context, stakeKeeper staking.Keeper) (string, int64, int64) { + validators := stakeKeeper.GetBondedValidatorsByPower(ctx) + //Index the validators by address for looking when scanning through claims + validatorsByAddress := make(map[string]staking.Validator) + for _, validator := range validators { + validatorsByAddress[validator.OperatorAddress.String()] = validator + } + + totalClaimsPower := int64(0) + highestClaimPower := int64(-1) + highestClaim := "" + for claim, validatorAddrs := range prophecy.ClaimValidators { + claimPower := int64(0) + for _, validatorAddr := range validatorAddrs { + validator, found := validatorsByAddress[validatorAddr.String()] + if found { + // Note: If claim validator is not found in the current validator set, we assume it is no longer + // an active validator and so can silently ignore it's claim and no longer count it towards total power. + claimPower += validator.GetConsensusPower() + } + } + totalClaimsPower += claimPower + if claimPower > highestClaimPower { + highestClaimPower = claimPower + highestClaim = claim + } + } + return highestClaim, highestClaimPower, totalClaimsPower +} + +// NewProphecy returns a new Prophecy, initialized in pending status with an initial claim +func NewProphecy(id string) Prophecy { + return Prophecy{ + ID: id, + Status: NewStatus(PendingStatusText, ""), + ClaimValidators: make(map[string][]sdk.ValAddress), + ValidatorClaims: make(map[string]string), + } +} + +// NewEmptyProphecy returns a blank prophecy, used with errors +func NewEmptyProphecy() Prophecy { + return NewProphecy("") +} + +// Status is a struct that contains the status of a given prophecy +type Status struct { + Text StatusText `json:"text"` + FinalClaim string `json:"final_claim"` +} + +// NewStatus returns a new Status with the given data contained +func NewStatus(text StatusText, finalClaim string) Status { + return Status{ + Text: text, + FinalClaim: finalClaim, + } +} diff --git a/x/oracle/types/status.go b/x/oracle/types/status.go new file mode 100644 index 0000000..849c794 --- /dev/null +++ b/x/oracle/types/status.go @@ -0,0 +1,47 @@ +package types + +import ( + "encoding/json" + "fmt" + "strconv" +) + +// StatusText is an enum used to represent the status of the prophecy +type StatusText int + +const ( + PendingStatusText = StatusText(iota) + SuccessStatusText + FailedStatusText +) + +var StatusTextToString = [...]string{"pending", "success", "failed"} +var StringToStatusText = map[string]StatusText{ + "pending": PendingStatusText, + "success": SuccessStatusText, + "failed": FailedStatusText, +} + +func (text StatusText) String() string { + return StatusTextToString[text] +} + +func (text StatusText) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("\"%v\"", text.String())), nil +} + +func (text *StatusText) UnmarshalJSON(b []byte) error { + var j string + err := json.Unmarshal(b, &j) + if err != nil { + return err + } + stringKey, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + + // Note that if the string cannot be found then it will be set to the zero value, 'pending' in this case. + *text = StringToStatusText[stringKey] + return nil +} diff --git a/x/staking/alias.go b/x/staking/alias.go new file mode 100644 index 0000000..019fa02 --- /dev/null +++ b/x/staking/alias.go @@ -0,0 +1,245 @@ +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/staking/keeper +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/staking/types +// ALIASGEN: github.com/cosmos/cosmos-sdk/x/staking/exported +package staking + +import ( + "github.com/cosmos/cosmos-sdk/x/staking/exported" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +const ( + DefaultParamspace = keeper.DefaultParamspace + DefaultCodespace = types.DefaultCodespace + CodeInvalidValidator = types.CodeInvalidValidator + CodeInvalidDelegation = types.CodeInvalidDelegation + CodeInvalidInput = types.CodeInvalidInput + CodeValidatorJailed = types.CodeValidatorJailed + CodeInvalidAddress = types.CodeInvalidAddress + CodeUnauthorized = types.CodeUnauthorized + CodeInternal = types.CodeInternal + CodeUnknownRequest = types.CodeUnknownRequest + ModuleName = types.ModuleName + StoreKey = types.StoreKey + TStoreKey = types.TStoreKey + QuerierRoute = types.QuerierRoute + RouterKey = types.RouterKey + DefaultUnbondingTime = types.DefaultUnbondingTime + DefaultMaxValidators = types.DefaultMaxValidators + DefaultMaxEntries = types.DefaultMaxEntries + NotBondedPoolName = types.NotBondedPoolName + BondedPoolName = types.BondedPoolName + QueryValidators = types.QueryValidators + QueryValidator = types.QueryValidator + QueryDelegatorDelegations = types.QueryDelegatorDelegations + QueryDelegatorUnbondingDelegations = types.QueryDelegatorUnbondingDelegations + QueryRedelegations = types.QueryRedelegations + QueryValidatorDelegations = types.QueryValidatorDelegations + QueryValidatorRedelegations = types.QueryValidatorRedelegations + QueryValidatorUnbondingDelegations = types.QueryValidatorUnbondingDelegations + QueryDelegation = types.QueryDelegation + QueryUnbondingDelegation = types.QueryUnbondingDelegation + QueryDelegatorValidators = types.QueryDelegatorValidators + QueryDelegatorValidator = types.QueryDelegatorValidator + QueryPool = types.QueryPool + QueryParameters = types.QueryParameters + MaxMonikerLength = types.MaxMonikerLength + MaxIdentityLength = types.MaxIdentityLength + MaxWebsiteLength = types.MaxWebsiteLength + MaxDetailsLength = types.MaxDetailsLength + DoNotModifyDesc = types.DoNotModifyDesc +) + +var ( + // functions aliases + RegisterInvariants = keeper.RegisterInvariants + AllInvariants = keeper.AllInvariants + ModuleAccountInvariants = keeper.ModuleAccountInvariants + NonNegativePowerInvariant = keeper.NonNegativePowerInvariant + PositiveDelegationInvariant = keeper.PositiveDelegationInvariant + DelegatorSharesInvariant = keeper.DelegatorSharesInvariant + NewKeeper = keeper.NewKeeper + ParamKeyTable = keeper.ParamKeyTable + NewQuerier = keeper.NewQuerier + RegisterCodec = types.RegisterCodec + NewCommissionRates = types.NewCommissionRates + NewCommission = types.NewCommission + NewCommissionWithTime = types.NewCommissionWithTime + NewDelegation = types.NewDelegation + MustMarshalDelegation = types.MustMarshalDelegation + MustUnmarshalDelegation = types.MustUnmarshalDelegation + UnmarshalDelegation = types.UnmarshalDelegation + NewUnbondingDelegation = types.NewUnbondingDelegation + NewUnbondingDelegationEntry = types.NewUnbondingDelegationEntry + MustMarshalUBD = types.MustMarshalUBD + MustUnmarshalUBD = types.MustUnmarshalUBD + UnmarshalUBD = types.UnmarshalUBD + NewRedelegation = types.NewRedelegation + NewRedelegationEntry = types.NewRedelegationEntry + MustMarshalRED = types.MustMarshalRED + MustUnmarshalRED = types.MustUnmarshalRED + UnmarshalRED = types.UnmarshalRED + NewDelegationResp = types.NewDelegationResp + NewRedelegationResponse = types.NewRedelegationResponse + NewRedelegationEntryResponse = types.NewRedelegationEntryResponse + ErrNilValidatorAddr = types.ErrNilValidatorAddr + ErrBadValidatorAddr = types.ErrBadValidatorAddr + ErrNoValidatorFound = types.ErrNoValidatorFound + ErrValidatorOwnerExists = types.ErrValidatorOwnerExists + ErrValidatorPubKeyExists = types.ErrValidatorPubKeyExists + ErrValidatorPubKeyTypeNotSupported = types.ErrValidatorPubKeyTypeNotSupported + ErrValidatorJailed = types.ErrValidatorJailed + ErrBadRemoveValidator = types.ErrBadRemoveValidator + ErrDescriptionLength = types.ErrDescriptionLength + ErrCommissionNegative = types.ErrCommissionNegative + ErrCommissionHuge = types.ErrCommissionHuge + ErrCommissionGTMaxRate = types.ErrCommissionGTMaxRate + ErrCommissionUpdateTime = types.ErrCommissionUpdateTime + ErrCommissionChangeRateNegative = types.ErrCommissionChangeRateNegative + ErrCommissionChangeRateGTMaxRate = types.ErrCommissionChangeRateGTMaxRate + ErrCommissionGTMaxChangeRate = types.ErrCommissionGTMaxChangeRate + ErrSelfDelegationBelowMinimum = types.ErrSelfDelegationBelowMinimum + ErrMinSelfDelegationInvalid = types.ErrMinSelfDelegationInvalid + ErrMinSelfDelegationDecreased = types.ErrMinSelfDelegationDecreased + ErrNilDelegatorAddr = types.ErrNilDelegatorAddr + ErrBadDenom = types.ErrBadDenom + ErrBadDelegationAddr = types.ErrBadDelegationAddr + ErrBadDelegationAmount = types.ErrBadDelegationAmount + ErrNoDelegation = types.ErrNoDelegation + ErrBadDelegatorAddr = types.ErrBadDelegatorAddr + ErrNoDelegatorForAddress = types.ErrNoDelegatorForAddress + ErrInsufficientShares = types.ErrInsufficientShares + ErrDelegationValidatorEmpty = types.ErrDelegationValidatorEmpty + ErrNotEnoughDelegationShares = types.ErrNotEnoughDelegationShares + ErrBadSharesAmount = types.ErrBadSharesAmount + ErrBadSharesPercent = types.ErrBadSharesPercent + ErrNotMature = types.ErrNotMature + ErrNoUnbondingDelegation = types.ErrNoUnbondingDelegation + ErrMaxUnbondingDelegationEntries = types.ErrMaxUnbondingDelegationEntries + ErrBadRedelegationAddr = types.ErrBadRedelegationAddr + ErrNoRedelegation = types.ErrNoRedelegation + ErrSelfRedelegation = types.ErrSelfRedelegation + ErrVerySmallRedelegation = types.ErrVerySmallRedelegation + ErrBadRedelegationDst = types.ErrBadRedelegationDst + ErrTransitiveRedelegation = types.ErrTransitiveRedelegation + ErrMaxRedelegationEntries = types.ErrMaxRedelegationEntries + ErrDelegatorShareExRateInvalid = types.ErrDelegatorShareExRateInvalid + ErrBothShareMsgsGiven = types.ErrBothShareMsgsGiven + ErrNeitherShareMsgsGiven = types.ErrNeitherShareMsgsGiven + ErrMissingSignature = types.ErrMissingSignature + NewGenesisState = types.NewGenesisState + DefaultGenesisState = types.DefaultGenesisState + NewMultiStakingHooks = types.NewMultiStakingHooks + GetValidatorKey = types.GetValidatorKey + GetValidatorByConsAddrKey = types.GetValidatorByConsAddrKey + AddressFromLastValidatorPowerKey = types.AddressFromLastValidatorPowerKey + GetValidatorsByPowerIndexKey = types.GetValidatorsByPowerIndexKey + GetLastValidatorPowerKey = types.GetLastValidatorPowerKey + ParseValidatorPowerRankKey = types.ParseValidatorPowerRankKey + GetValidatorQueueTimeKey = types.GetValidatorQueueTimeKey + GetDelegationKey = types.GetDelegationKey + GetDelegationsKey = types.GetDelegationsKey + GetUBDKey = types.GetUBDKey + GetUBDByValIndexKey = types.GetUBDByValIndexKey + GetUBDKeyFromValIndexKey = types.GetUBDKeyFromValIndexKey + GetUBDsKey = types.GetUBDsKey + GetUBDsByValIndexKey = types.GetUBDsByValIndexKey + GetUnbondingDelegationTimeKey = types.GetUnbondingDelegationTimeKey + GetREDKey = types.GetREDKey + GetREDByValSrcIndexKey = types.GetREDByValSrcIndexKey + GetREDByValDstIndexKey = types.GetREDByValDstIndexKey + GetREDKeyFromValSrcIndexKey = types.GetREDKeyFromValSrcIndexKey + GetREDKeyFromValDstIndexKey = types.GetREDKeyFromValDstIndexKey + GetRedelegationTimeKey = types.GetRedelegationTimeKey + GetREDsKey = types.GetREDsKey + GetREDsFromValSrcIndexKey = types.GetREDsFromValSrcIndexKey + GetREDsToValDstIndexKey = types.GetREDsToValDstIndexKey + GetREDsByDelToValDstIndexKey = types.GetREDsByDelToValDstIndexKey + NewMsgCreateValidator = types.NewMsgCreateValidator + NewMsgEditValidator = types.NewMsgEditValidator + NewMsgDelegate = types.NewMsgDelegate + NewMsgBeginRedelegate = types.NewMsgBeginRedelegate + NewMsgUndelegate = types.NewMsgUndelegate + NewParams = types.NewParams + DefaultParams = types.DefaultParams + MustUnmarshalParams = types.MustUnmarshalParams + UnmarshalParams = types.UnmarshalParams + NewPool = types.NewPool + NewQueryDelegatorParams = types.NewQueryDelegatorParams + NewQueryValidatorParams = types.NewQueryValidatorParams + NewQueryBondsParams = types.NewQueryBondsParams + NewQueryRedelegationParams = types.NewQueryRedelegationParams + NewQueryValidatorsParams = types.NewQueryValidatorsParams + NewValidator = types.NewValidator + MustMarshalValidator = types.MustMarshalValidator + MustUnmarshalValidator = types.MustUnmarshalValidator + UnmarshalValidator = types.UnmarshalValidator + NewDescription = types.NewDescription + + // variable aliases + ModuleCdc = types.ModuleCdc + LastValidatorPowerKey = types.LastValidatorPowerKey + LastTotalPowerKey = types.LastTotalPowerKey + ValidatorsKey = types.ValidatorsKey + ValidatorsByConsAddrKey = types.ValidatorsByConsAddrKey + ValidatorsByPowerIndexKey = types.ValidatorsByPowerIndexKey + DelegationKey = types.DelegationKey + UnbondingDelegationKey = types.UnbondingDelegationKey + UnbondingDelegationByValIndexKey = types.UnbondingDelegationByValIndexKey + RedelegationKey = types.RedelegationKey + RedelegationByValSrcIndexKey = types.RedelegationByValSrcIndexKey + RedelegationByValDstIndexKey = types.RedelegationByValDstIndexKey + UnbondingQueueKey = types.UnbondingQueueKey + RedelegationQueueKey = types.RedelegationQueueKey + ValidatorQueueKey = types.ValidatorQueueKey + KeyUnbondingTime = types.KeyUnbondingTime + KeyMaxValidators = types.KeyMaxValidators + KeyMaxEntries = types.KeyMaxEntries + KeyBondDenom = types.KeyBondDenom +) + +type ( + Keeper = keeper.Keeper + Commission = types.Commission + CommissionRates = types.CommissionRates + DVPair = types.DVPair + DVVTriplet = types.DVVTriplet + Delegation = types.Delegation + Delegations = types.Delegations + UnbondingDelegation = types.UnbondingDelegation + UnbondingDelegationEntry = types.UnbondingDelegationEntry + UnbondingDelegations = types.UnbondingDelegations + Redelegation = types.Redelegation + RedelegationEntry = types.RedelegationEntry + Redelegations = types.Redelegations + DelegationResponse = types.DelegationResponse + DelegationResponses = types.DelegationResponses + RedelegationResponse = types.RedelegationResponse + RedelegationEntryResponse = types.RedelegationEntryResponse + RedelegationResponses = types.RedelegationResponses + CodeType = types.CodeType + GenesisState = types.GenesisState + LastValidatorPower = types.LastValidatorPower + MultiStakingHooks = types.MultiStakingHooks + MsgCreateValidator = types.MsgCreateValidator + MsgEditValidator = types.MsgEditValidator + MsgDelegate = types.MsgDelegate + MsgBeginRedelegate = types.MsgBeginRedelegate + MsgUndelegate = types.MsgUndelegate + Params = types.Params + Pool = types.Pool + QueryDelegatorParams = types.QueryDelegatorParams + QueryValidatorParams = types.QueryValidatorParams + QueryBondsParams = types.QueryBondsParams + QueryRedelegationParams = types.QueryRedelegationParams + QueryValidatorsParams = types.QueryValidatorsParams + Validator = types.Validator + Validators = types.Validators + Description = types.Description + DelegationI = exported.DelegationI + ValidatorI = exported.ValidatorI +) diff --git a/x/staking/app_test.go b/x/staking/app_test.go new file mode 100644 index 0000000..e4726c1 --- /dev/null +++ b/x/staking/app_test.go @@ -0,0 +1,188 @@ +package staking + +import ( + "testing" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/mock" + "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/supply" + supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// getMockApp returns an initialized mock application for this module. +func getMockApp(t *testing.T) (*mock.App, Keeper) { + mApp := mock.NewApp() + + RegisterCodec(mApp.Cdc) + supply.RegisterCodec(mApp.Cdc) + + keyStaking := sdk.NewKVStoreKey(StoreKey) + tkeyStaking := sdk.NewTransientStoreKey(TStoreKey) + keySupply := sdk.NewKVStoreKey(supply.StoreKey) + + feeCollector := supply.NewEmptyModuleAccount(auth.FeeCollectorName) + notBondedPool := supply.NewEmptyModuleAccount(types.NotBondedPoolName, supply.Burner, supply.Staking) + bondPool := supply.NewEmptyModuleAccount(types.BondedPoolName, supply.Burner, supply.Staking) + + blacklistedAddrs := make(map[string]bool) + blacklistedAddrs[feeCollector.String()] = true + blacklistedAddrs[notBondedPool.String()] = true + blacklistedAddrs[bondPool.String()] = true + + bankKeeper := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs) + maccPerms := map[string][]string{ + auth.FeeCollectorName: nil, + types.NotBondedPoolName: []string{supply.Burner, supply.Staking}, + types.BondedPoolName: []string{supply.Burner, supply.Staking}, + } + supplyKeeper := supply.NewKeeper(mApp.Cdc, keySupply, mApp.AccountKeeper, bankKeeper, maccPerms) + keeper := NewKeeper(mApp.Cdc, keyStaking, tkeyStaking, supplyKeeper, mApp.ParamsKeeper.Subspace(DefaultParamspace), DefaultCodespace) + + mApp.Router().AddRoute(RouterKey, NewHandler(keeper)) + mApp.SetEndBlocker(getEndBlocker(keeper)) + mApp.SetInitChainer(getInitChainer(mApp, keeper, mApp.AccountKeeper, supplyKeeper, + []supplyexported.ModuleAccountI{feeCollector, notBondedPool, bondPool})) + + require.NoError(t, mApp.CompleteSetup(keyStaking, tkeyStaking, keySupply)) + return mApp, keeper +} + +// getEndBlocker returns a staking endblocker. +func getEndBlocker(keeper Keeper) sdk.EndBlocker { + return func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { + validatorUpdates := EndBlocker(ctx, keeper) + + return abci.ResponseEndBlock{ + ValidatorUpdates: validatorUpdates, + } + } +} + +// getInitChainer initializes the chainer of the mock app and sets the genesis +// state. It returns an empty ResponseInitChain. +func getInitChainer(mapp *mock.App, keeper Keeper, accountKeeper types.AccountKeeper, supplyKeeper types.SupplyKeeper, + blacklistedAddrs []supplyexported.ModuleAccountI) sdk.InitChainer { + return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + mapp.InitChainer(ctx, req) + + // set module accounts + for _, macc := range blacklistedAddrs { + supplyKeeper.SetModuleAccount(ctx, macc) + } + + stakingGenesis := DefaultGenesisState() + validators := InitGenesis(ctx, keeper, accountKeeper, supplyKeeper, stakingGenesis) + return abci.ResponseInitChain{ + Validators: validators, + } + } +} + +//__________________________________________________________________________________________ + +func checkValidator(t *testing.T, mapp *mock.App, keeper Keeper, + addr sdk.ValAddress, expFound bool) Validator { + + ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{}) + validator, found := keeper.GetValidator(ctxCheck, addr) + + require.Equal(t, expFound, found) + return validator +} + +func checkDelegation( + t *testing.T, mapp *mock.App, keeper Keeper, delegatorAddr sdk.AccAddress, + validatorAddr sdk.ValAddress, expFound bool, expShares sdk.Dec, +) { + + ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{}) + delegation, found := keeper.GetDelegation(ctxCheck, delegatorAddr, validatorAddr) + if expFound { + require.True(t, found) + require.True(sdk.DecEq(t, expShares, delegation.Shares)) + + return + } + + require.False(t, found) +} + +func TestStakingMsgs(t *testing.T) { + mApp, keeper := getMockApp(t) + + genTokens := sdk.TokensFromConsensusPower(42) + bondTokens := sdk.TokensFromConsensusPower(10) + genCoin := sdk.NewCoin(sdk.DefaultBondDenom, genTokens) + bondCoin := sdk.NewCoin(sdk.DefaultBondDenom, bondTokens) + + acc1 := &auth.BaseAccount{ + Address: addr1, + Coins: sdk.Coins{genCoin}, + } + acc2 := &auth.BaseAccount{ + Address: addr2, + Coins: sdk.Coins{genCoin}, + } + accs := []auth.Account{acc1, acc2} + + mock.SetGenesis(mApp, accs) + mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin}) + mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin}) + + // create validator + description := NewDescription("foo_moniker", "", "", "", "") + createValidatorMsg := NewMsgCreateValidator( + sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commissionRates, sdk.OneInt(), + ) + + header := abci.Header{Height: mApp.LastBlockHeight() + 1} + mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, header, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1) + mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Sub(bondCoin)}) + + header = abci.Header{Height: mApp.LastBlockHeight() + 1} + mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + + validator := checkValidator(t, mApp, keeper, sdk.ValAddress(addr1), true) + require.Equal(t, sdk.ValAddress(addr1), validator.OperatorAddress) + require.Equal(t, sdk.Bonded, validator.Status) + require.True(sdk.IntEq(t, bondTokens, validator.BondedTokens())) + + header = abci.Header{Height: mApp.LastBlockHeight() + 1} + mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + + // edit the validator + description = NewDescription("bar_moniker", "", "", "" , "") + editValidatorMsg := NewMsgEditValidator(sdk.ValAddress(addr1), description, nil, nil) + + header = abci.Header{Height: mApp.LastBlockHeight() + 1} + mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, header, []sdk.Msg{editValidatorMsg}, []uint64{0}, []uint64{1}, true, true, priv1) + + validator = checkValidator(t, mApp, keeper, sdk.ValAddress(addr1), true) + require.Equal(t, description, validator.Description) + + // delegate + mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin}) + delegateMsg := NewMsgDelegate(addr2, sdk.ValAddress(addr1), bondCoin) + + header = abci.Header{Height: mApp.LastBlockHeight() + 1} + mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, header, []sdk.Msg{delegateMsg}, []uint64{1}, []uint64{0}, true, true, priv2) + mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Sub(bondCoin)}) + checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), true, bondTokens.ToDec()) + + // begin unbonding + beginUnbondingMsg := NewMsgUndelegate(addr2, sdk.ValAddress(addr1), bondCoin) + header = abci.Header{Height: mApp.LastBlockHeight() + 1} + mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, header, []sdk.Msg{beginUnbondingMsg}, []uint64{1}, []uint64{1}, true, true, priv2) + + // delegation should exist anymore + checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), false, sdk.Dec{}) + + // balance should be the same because bonding not yet complete + mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Sub(bondCoin)}) +} diff --git a/x/staking/client/cli/flags.go b/x/staking/client/cli/flags.go new file mode 100644 index 0000000..5635984 --- /dev/null +++ b/x/staking/client/cli/flags.go @@ -0,0 +1,70 @@ +package cli + +import ( + flag "github.com/spf13/pflag" + + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// nolint +const ( + FlagAddressValidator = "validator" + FlagAddressValidatorSrc = "addr-validator-source" + FlagAddressValidatorDst = "addr-validator-dest" + FlagPubKey = "pubkey" + FlagAmount = "amount" + FlagSharesAmount = "shares-amount" + FlagSharesFraction = "shares-fraction" + + FlagMoniker = "moniker" + FlagIdentity = "identity" + FlagWebsite = "website" + FlagDetails = "details" + + FlagCommissionRate = "commission-rate" + FlagCommissionMaxRate = "commission-max-rate" + FlagCommissionMaxChangeRate = "commission-max-change-rate" + + FlagMinSelfDelegation = "min-self-delegation" + + FlagGenesisFormat = "genesis-format" + FlagNodeID = "node-id" + FlagIP = "ip" +) + +// common flagsets to add to various functions +var ( + FsPk = flag.NewFlagSet("", flag.ContinueOnError) + FsAmount = flag.NewFlagSet("", flag.ContinueOnError) + fsShares = flag.NewFlagSet("", flag.ContinueOnError) + fsDescriptionCreate = flag.NewFlagSet("", flag.ContinueOnError) + FsCommissionCreate = flag.NewFlagSet("", flag.ContinueOnError) + fsCommissionUpdate = flag.NewFlagSet("", flag.ContinueOnError) + FsMinSelfDelegation = flag.NewFlagSet("", flag.ContinueOnError) + fsDescriptionEdit = flag.NewFlagSet("", flag.ContinueOnError) + fsValidator = flag.NewFlagSet("", flag.ContinueOnError) + fsRedelegation = flag.NewFlagSet("", flag.ContinueOnError) +) + +func init() { + FsPk.String(FlagPubKey, "", "The Bech32 encoded PubKey of the validator") + FsAmount.String(FlagAmount, "", "Amount of coins to bond") + fsShares.String(FlagSharesAmount, "", "Amount of source-shares to either unbond or redelegate as a positive integer or decimal") + fsShares.String(FlagSharesFraction, "", "Fraction of source-shares to either unbond or redelegate as a positive integer or decimal >0 and <=1") + fsDescriptionCreate.String(FlagMoniker, "", "The validator's name") + fsDescriptionCreate.String(FlagIdentity, "", "The optional identity signature (ex. UPort or Keybase)") + fsDescriptionCreate.String(FlagWebsite, "", "The validator's (optional) website") + fsDescriptionCreate.String(FlagDetails, "", "The validator's (optional) details") + fsCommissionUpdate.String(FlagCommissionRate, "", "The new commission rate percentage") + FsCommissionCreate.String(FlagCommissionRate, "", "The initial commission rate percentage") + FsCommissionCreate.String(FlagCommissionMaxRate, "", "The maximum commission rate percentage") + FsCommissionCreate.String(FlagCommissionMaxChangeRate, "", "The maximum commission change rate percentage (per day)") + FsMinSelfDelegation.String(FlagMinSelfDelegation, "", "The minimum self delegation required on the validator") + fsDescriptionEdit.String(FlagMoniker, types.DoNotModifyDesc, "The validator's name") + fsDescriptionEdit.String(FlagIdentity, types.DoNotModifyDesc, "The (optional) identity signature (ex. UPort or Keybase)") + fsDescriptionEdit.String(FlagWebsite, types.DoNotModifyDesc, "The validator's (optional) website") + fsDescriptionEdit.String(FlagDetails, types.DoNotModifyDesc, "The validator's (optional) details") + fsValidator.String(FlagAddressValidator, "", "The Bech32 address of the validator") + fsRedelegation.String(FlagAddressValidatorSrc, "", "The Bech32 address of the source validator") + fsRedelegation.String(FlagAddressValidatorDst, "", "The Bech32 address of the destination validator") +} diff --git a/x/staking/client/cli/query.go b/x/staking/client/cli/query.go new file mode 100644 index 0000000..9e0352f --- /dev/null +++ b/x/staking/client/cli/query.go @@ -0,0 +1,592 @@ +package cli + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { + stakingQueryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the staking module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + stakingQueryCmd.AddCommand(client.GetCommands( + GetCmdQueryDelegation(queryRoute, cdc), + GetCmdQueryDelegations(queryRoute, cdc), + GetCmdQueryUnbondingDelegation(queryRoute, cdc), + GetCmdQueryUnbondingDelegations(queryRoute, cdc), + GetCmdQueryRedelegation(queryRoute, cdc), + GetCmdQueryRedelegations(queryRoute, cdc), + GetCmdQueryValidator(queryRoute, cdc), + GetCmdQueryValidators(queryRoute, cdc), + GetCmdQueryValidatorDelegations(queryRoute, cdc), + GetCmdQueryValidatorUnbondingDelegations(queryRoute, cdc), + GetCmdQueryValidatorRedelegations(queryRoute, cdc), + GetCmdQueryParams(queryRoute, cdc), + GetCmdQueryPool(queryRoute, cdc))...) + + return stakingQueryCmd + +} + +// GetCmdQueryValidator implements the validator query command. +func GetCmdQueryValidator(storeName string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "validator [validator-addr]", + Short: "Query a validator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query details about an individual validator. + +Example: +$ %s query staking validator cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.ClientName, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + addr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + res, _, err := cliCtx.QueryStore(types.GetValidatorKey(addr), storeName) + if err != nil { + return err + } + + if len(res) == 0 { + return fmt.Errorf("No validator found with address %s", addr) + } + + return cliCtx.PrintOutput(types.MustUnmarshalValidator(cdc, res)) + }, + } +} + +// GetCmdQueryValidators implements the query all validators command. +func GetCmdQueryValidators(storeName string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "validators", + Short: "Query for all validators", + Args: cobra.NoArgs, + Long: strings.TrimSpace( + fmt.Sprintf(`Query details about all validators on a network. + +Example: +$ %s query staking validators +`, + version.ClientName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + resKVs, _, err := cliCtx.QuerySubspace(types.ValidatorsKey, storeName) + if err != nil { + return err + } + + var validators types.Validators + for _, kv := range resKVs { + validators = append(validators, types.MustUnmarshalValidator(cdc, kv.Value)) + } + + return cliCtx.PrintOutput(validators) + }, + } +} + +// GetCmdQueryValidatorUnbondingDelegations implements the query all unbonding delegatations from a validator command. +func GetCmdQueryValidatorUnbondingDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "unbonding-delegations-from [validator-addr]", + Short: "Query all unbonding delegatations from a validator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query delegations that are unbonding _from_ a validator. + +Example: +$ %s query staking unbonding-delegations-from cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.ClientName, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + bz, err := cdc.MarshalJSON(types.NewQueryValidatorParams(valAddr)) + if err != nil { + return err + } + + route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryValidatorUnbondingDelegations) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + var ubds types.UnbondingDelegations + cdc.MustUnmarshalJSON(res, &ubds) + return cliCtx.PrintOutput(ubds) + }, + } +} + +// GetCmdQueryValidatorRedelegations implements the query all redelegatations +// from a validator command. +func GetCmdQueryValidatorRedelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "redelegations-from [validator-addr]", + Short: "Query all outgoing redelegatations from a validator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query delegations that are redelegating _from_ a validator. + +Example: +$ %s query staking redelegations-from cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.ClientName, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + valSrcAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + bz, err := cdc.MarshalJSON(types.QueryRedelegationParams{SrcValidatorAddr: valSrcAddr}) + if err != nil { + return err + } + + route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryRedelegations) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + var resp types.RedelegationResponses + if err := cdc.UnmarshalJSON(res, &resp); err != nil { + return err + } + + return cliCtx.PrintOutput(resp) + }, + } +} + +// GetCmdQueryDelegation the query delegation command. +func GetCmdQueryDelegation(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "delegation [delegator-addr] [validator-addr]", + Short: "Query a delegation based on address and validator address", + Long: strings.TrimSpace( + fmt.Sprintf(`Query delegations for an individual delegator on an individual validator. + +Example: +$ %s query staking delegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.ClientName, + ), + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + delAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + valAddr, err := sdk.ValAddressFromBech32(args[1]) + if err != nil { + return err + } + + bz, err := cdc.MarshalJSON(types.NewQueryBondsParams(delAddr, valAddr)) + if err != nil { + return err + } + + route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegation) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + var resp types.DelegationResponse + if err := cdc.UnmarshalJSON(res, &resp); err != nil { + return err + } + + return cliCtx.PrintOutput(resp) + }, + } +} + +// GetCmdQueryDelegations implements the command to query all the delegations +// made from one delegator. +func GetCmdQueryDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "delegations [delegator-addr]", + Short: "Query all delegations made by one delegator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query delegations for an individual delegator on all validators. + +Example: +$ %s query staking delegations cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p +`, + version.ClientName, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + delAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + bz, err := cdc.MarshalJSON(types.NewQueryDelegatorParams(delAddr)) + if err != nil { + return err + } + + route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegatorDelegations) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + var resp types.DelegationResponses + if err := cdc.UnmarshalJSON(res, &resp); err != nil { + return err + } + + return cliCtx.PrintOutput(resp) + }, + } +} + +// GetCmdQueryValidatorDelegations implements the command to query all the +// delegations to a specific validator. +func GetCmdQueryValidatorDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "delegations-to [validator-addr]", + Short: "Query all delegations made to one validator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query delegations on an individual validator. + +Example: +$ %s query staking delegations-to cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.ClientName, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + bz, err := cdc.MarshalJSON(types.NewQueryValidatorParams(valAddr)) + if err != nil { + return err + } + + route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryValidatorDelegations) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + var resp types.DelegationResponses + if err := cdc.UnmarshalJSON(res, &resp); err != nil { + return err + } + + return cliCtx.PrintOutput(resp) + }, + } +} + +// GetCmdQueryUnbondingDelegation implements the command to query a single +// unbonding-delegation record. +func GetCmdQueryUnbondingDelegation(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "unbonding-delegation [delegator-addr] [validator-addr]", + Short: "Query an unbonding-delegation record based on delegator and validator address", + Long: strings.TrimSpace( + fmt.Sprintf(`Query unbonding delegations for an individual delegator on an individual validator. + +Example: +$ %s query staking unbonding-delegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.ClientName, + ), + ), + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + valAddr, err := sdk.ValAddressFromBech32(args[1]) + if err != nil { + return err + } + + delAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + bz, err := cdc.MarshalJSON(types.NewQueryBondsParams(delAddr, valAddr)) + if err != nil { + return err + } + + route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryUnbondingDelegation) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + return cliCtx.PrintOutput(types.MustUnmarshalUBD(cdc, res)) + }, + } +} + +// GetCmdQueryUnbondingDelegations implements the command to query all the +// unbonding-delegation records for a delegator. +func GetCmdQueryUnbondingDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "unbonding-delegations [delegator-addr]", + Short: "Query all unbonding-delegations records for one delegator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query unbonding delegations for an individual delegator. + +Example: +$ %s query staking unbonding-delegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p +`, + version.ClientName, + ), + ), + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + delegatorAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + bz, err := cdc.MarshalJSON(types.NewQueryDelegatorParams(delegatorAddr)) + if err != nil { + return err + } + + route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryDelegatorUnbondingDelegations) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + var ubds types.UnbondingDelegations + if err = cdc.UnmarshalJSON(res, &ubds); err != nil { + return err + } + + return cliCtx.PrintOutput(ubds) + }, + } +} + +// GetCmdQueryRedelegation implements the command to query a single +// redelegation record. +func GetCmdQueryRedelegation(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "redelegation [delegator-addr] [src-validator-addr] [dst-validator-addr]", + Short: "Query a redelegation record based on delegator and a source and destination validator address", + Long: strings.TrimSpace( + fmt.Sprintf(`Query a redelegation record for an individual delegator between a source and destination validator. + +Example: +$ %s query staking redelegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj +`, + version.ClientName, + ), + ), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + delAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + valSrcAddr, err := sdk.ValAddressFromBech32(args[1]) + if err != nil { + return err + } + + valDstAddr, err := sdk.ValAddressFromBech32(args[2]) + if err != nil { + return err + } + + bz, err := cdc.MarshalJSON(types.NewQueryRedelegationParams(delAddr, valSrcAddr, valDstAddr)) + if err != nil { + return err + } + + route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryRedelegations) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + var resp types.RedelegationResponses + if err := cdc.UnmarshalJSON(res, &resp); err != nil { + return err + } + + return cliCtx.PrintOutput(resp) + }, + } +} + +// GetCmdQueryRedelegations implements the command to query all the +// redelegation records for a delegator. +func GetCmdQueryRedelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "redelegations [delegator-addr]", + Args: cobra.ExactArgs(1), + Short: "Query all redelegations records for one delegator", + Long: strings.TrimSpace( + fmt.Sprintf(`Query all redelegation records for an individual delegator. + +Example: +$ %s query staking redelegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p +`, + version.ClientName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + delAddr, err := sdk.AccAddressFromBech32(args[0]) + if err != nil { + return err + } + + bz, err := cdc.MarshalJSON(types.QueryRedelegationParams{DelegatorAddr: delAddr}) + if err != nil { + return err + } + + route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryRedelegations) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + var resp types.RedelegationResponses + if err := cdc.UnmarshalJSON(res, &resp); err != nil { + return err + } + + return cliCtx.PrintOutput(resp) + }, + } +} + +// GetCmdQueryPool implements the pool query command. +func GetCmdQueryPool(storeName string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "pool", + Args: cobra.NoArgs, + Short: "Query the current staking pool values", + Long: strings.TrimSpace( + fmt.Sprintf(`Query values for amounts stored in the staking pool. + +Example: +$ %s query staking pool +`, + version.ClientName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + bz, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/pool", storeName), nil) + if err != nil { + return err + } + + var pool types.Pool + if err := cdc.UnmarshalJSON(bz, &pool); err != nil { + return err + } + + return cliCtx.PrintOutput(pool) + }, + } +} + +// GetCmdQueryParams implements the params query command. +func GetCmdQueryParams(storeName string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "params", + Args: cobra.NoArgs, + Short: "Query the current staking parameters information", + Long: strings.TrimSpace( + fmt.Sprintf(`Query values set as staking parameters. + +Example: +$ %s query staking params +`, + version.ClientName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + route := fmt.Sprintf("custom/%s/%s", storeName, types.QueryParameters) + bz, _, err := cliCtx.QueryWithData(route, nil) + if err != nil { + return err + } + + var params types.Params + cdc.MustUnmarshalJSON(bz, ¶ms) + return cliCtx.PrintOutput(params) + }, + } +} diff --git a/x/staking/client/cli/tx.go b/x/staking/client/cli/tx.go new file mode 100644 index 0000000..4f1b291 --- /dev/null +++ b/x/staking/client/cli/tx.go @@ -0,0 +1,384 @@ +package cli + +import ( + "fmt" + "os" + "strings" + + "github.com/spf13/cobra" + flag "github.com/spf13/pflag" + "github.com/spf13/viper" + + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// GetTxCmd returns the transaction commands for this module +func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { + stakingTxCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Staking transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + stakingTxCmd.AddCommand(client.PostCommands( + GetCmdCreateValidator(cdc), + GetCmdEditValidator(cdc), + GetCmdDelegate(cdc), + GetCmdRedelegate(storeKey, cdc), + GetCmdUnbond(storeKey, cdc), + )...) + + return stakingTxCmd +} + +// GetCmdCreateValidator implements the create validator command handler. +func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "create-validator", + Short: "create new validator initialized with a self-delegation to it", + RunE: func(cmd *cobra.Command, args []string) error { + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContext().WithCodec(cdc) + + txBldr, msg, err := BuildCreateValidatorMsg(cliCtx, txBldr) + if err != nil { + return err + } + + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } + + cmd.Flags().AddFlagSet(FsPk) + cmd.Flags().AddFlagSet(FsAmount) + cmd.Flags().AddFlagSet(fsDescriptionCreate) + cmd.Flags().AddFlagSet(FsCommissionCreate) + cmd.Flags().AddFlagSet(FsMinSelfDelegation) + + cmd.Flags().String(FlagIP, "", fmt.Sprintf("The node's public IP. It takes effect only when used in combination with --%s", client.FlagGenerateOnly)) + cmd.Flags().String(FlagNodeID, "", "The node's ID") + + cmd.MarkFlagRequired(client.FlagFrom) + cmd.MarkFlagRequired(FlagAmount) + cmd.MarkFlagRequired(FlagPubKey) + cmd.MarkFlagRequired(FlagMoniker) + + return cmd +} + +// GetCmdEditValidator implements the create edit validator command. +// TODO: add full description +func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "edit-validator", + Short: "edit an existing validator account", + RunE: func(cmd *cobra.Command, args []string) error { + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc)) + cliCtx := context.NewCLIContext().WithCodec(cdc) + + valAddr := cliCtx.GetFromAddress() + description := types.Description{ + Moniker: viper.GetString(FlagMoniker), + Identity: viper.GetString(FlagIdentity), + Website: viper.GetString(FlagWebsite), + Details: viper.GetString(FlagDetails), + } + + var newRate *sdk.Dec + + commissionRate := viper.GetString(FlagCommissionRate) + if commissionRate != "" { + rate, err := sdk.NewDecFromStr(commissionRate) + if err != nil { + return fmt.Errorf("invalid new commission rate: %v", err) + } + + newRate = &rate + } + + var newMinSelfDelegation *sdk.Int + + minSelfDelegationString := viper.GetString(FlagMinSelfDelegation) + if minSelfDelegationString != "" { + msb, ok := sdk.NewIntFromString(minSelfDelegationString) + if !ok { + return fmt.Errorf(types.ErrMinSelfDelegationInvalid(types.DefaultCodespace).Error()) + } + newMinSelfDelegation = &msb + } + + msg := types.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate, newMinSelfDelegation) + + // build and sign the transaction, then broadcast to Tendermint + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } + + cmd.Flags().AddFlagSet(fsDescriptionEdit) + cmd.Flags().AddFlagSet(fsCommissionUpdate) + + return cmd +} + +// GetCmdDelegate implements the delegate command. +func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "delegate [validator-addr] [amount]", + Args: cobra.ExactArgs(2), + Short: "Delegate liquid tokens to a validator", + Long: strings.TrimSpace( + fmt.Sprintf(`Delegate an amount of liquid coins to a validator from your wallet. + +Example: +$ %s tx staking delegate cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 1000stake --from mykey +`, + version.ClientName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc)) + cliCtx := context.NewCLIContext().WithCodec(cdc) + + amount, err := sdk.ParseCoin(args[1]) + if err != nil { + return err + } + + delAddr := cliCtx.GetFromAddress() + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + msg := types.NewMsgDelegate(delAddr, valAddr, amount) + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } +} + +// GetCmdRedelegate the begin redelegation command. +func GetCmdRedelegate(storeName string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "redelegate [src-validator-addr] [dst-validator-addr] [amount]", + Short: "Redelegate illiquid tokens from one validator to another", + Args: cobra.ExactArgs(3), + Long: strings.TrimSpace( + fmt.Sprintf(`Redelegate an amount of illiquid staking tokens from one validator to another. + +Example: +$ %s tx staking redelegate cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj cosmosvaloper1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 100stake --from mykey +`, + version.ClientName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc)) + cliCtx := context.NewCLIContext().WithCodec(cdc) + + delAddr := cliCtx.GetFromAddress() + valSrcAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + valDstAddr, err := sdk.ValAddressFromBech32(args[1]) + if err != nil { + return err + } + + amount, err := sdk.ParseCoin(args[2]) + if err != nil { + return err + } + + msg := types.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, amount) + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } +} + +// GetCmdUnbond implements the unbond validator command. +func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "unbond [validator-addr] [amount]", + Short: "Unbond shares from a validator", + Args: cobra.ExactArgs(2), + Long: strings.TrimSpace( + fmt.Sprintf(`Unbond an amount of bonded shares from a validator. + +Example: +$ %s tx staking unbond cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake --from mykey +`, + version.ClientName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(auth.DefaultTxEncoder(cdc)) + cliCtx := context.NewCLIContext().WithCodec(cdc) + + delAddr := cliCtx.GetFromAddress() + valAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + amount, err := sdk.ParseCoin(args[1]) + if err != nil { + return err + } + + msg := types.NewMsgUndelegate(delAddr, valAddr, amount) + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } +} + +//__________________________________________________________ + +var ( + defaultTokens = sdk.TokensFromConsensusPower(100) + defaultAmount = defaultTokens.String() + sdk.DefaultBondDenom + defaultCommissionRate = "0.1" + defaultCommissionMaxRate = "0.2" + defaultCommissionMaxChangeRate = "0.01" + defaultMinSelfDelegation = "1" +) + +// Return the flagset, particular flags, and a description of defaults +// this is anticipated to be used with the gen-tx +func CreateValidatorMsgHelpers(ipDefault string) (fs *flag.FlagSet, nodeIDFlag, pubkeyFlag, amountFlag, defaultsDesc string) { + + fsCreateValidator := flag.NewFlagSet("", flag.ContinueOnError) + fsCreateValidator.String(FlagIP, ipDefault, "The node's public IP") + fsCreateValidator.String(FlagNodeID, "", "The node's NodeID") + fsCreateValidator.String(FlagWebsite, "", "The validator's (optional) website") + fsCreateValidator.String(FlagDetails, "", "The validator's (optional) details") + fsCreateValidator.String(FlagIdentity, "", "The (optional) identity signature (ex. UPort or Keybase)") + fsCreateValidator.AddFlagSet(FsCommissionCreate) + fsCreateValidator.AddFlagSet(FsMinSelfDelegation) + fsCreateValidator.AddFlagSet(FsAmount) + fsCreateValidator.AddFlagSet(FsPk) + + defaultsDesc = fmt.Sprintf(` + delegation amount: %s + commission rate: %s + commission max rate: %s + commission max change rate: %s + minimum self delegation: %s +`, defaultAmount, defaultCommissionRate, + defaultCommissionMaxRate, defaultCommissionMaxChangeRate, + defaultMinSelfDelegation) + + return fsCreateValidator, FlagNodeID, FlagPubKey, FlagAmount, defaultsDesc +} + +// prepare flags in config +func PrepareFlagsForTxCreateValidator( + config *cfg.Config, nodeID, chainID string, valPubKey crypto.PubKey, +) { + + ip := viper.GetString(FlagIP) + if ip == "" { + fmt.Fprintf(os.Stderr, "couldn't retrieve an external IP; "+ + "the tx's memo field will be unset") + } + + website := viper.GetString(FlagWebsite) + details := viper.GetString(FlagDetails) + identity := viper.GetString(FlagIdentity) + + viper.Set(client.FlagChainID, chainID) + viper.Set(client.FlagFrom, viper.GetString(client.FlagName)) + viper.Set(FlagNodeID, nodeID) + viper.Set(FlagIP, ip) + viper.Set(FlagPubKey, sdk.MustBech32ifyConsPub(valPubKey)) + viper.Set(FlagMoniker, config.Moniker) + viper.Set(FlagWebsite, website) + viper.Set(FlagDetails, details) + viper.Set(FlagIdentity, identity) + + if config.Moniker == "" { + viper.Set(FlagMoniker, viper.GetString(client.FlagName)) + } + if viper.GetString(FlagAmount) == "" { + viper.Set(FlagAmount, defaultAmount) + } + if viper.GetString(FlagCommissionRate) == "" { + viper.Set(FlagCommissionRate, defaultCommissionRate) + } + if viper.GetString(FlagCommissionMaxRate) == "" { + viper.Set(FlagCommissionMaxRate, defaultCommissionMaxRate) + } + if viper.GetString(FlagCommissionMaxChangeRate) == "" { + viper.Set(FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate) + } + if viper.GetString(FlagMinSelfDelegation) == "" { + viper.Set(FlagMinSelfDelegation, defaultMinSelfDelegation) + } +} + +// BuildCreateValidatorMsg makes a new MsgCreateValidator. +func BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr auth.TxBuilder) (auth.TxBuilder, sdk.Msg, error) { + amounstStr := viper.GetString(FlagAmount) + amount, err := sdk.ParseCoin(amounstStr) + if err != nil { + return txBldr, nil, err + } + + valAddr := cliCtx.GetFromAddress() + pkStr := viper.GetString(FlagPubKey) + + pk, err := sdk.GetConsPubKeyBech32(pkStr) + if err != nil { + return txBldr, nil, err + } + + description := types.NewDescription( + viper.GetString(FlagMoniker), + viper.GetString(FlagIdentity), + viper.GetString(FlagWebsite), + viper.GetString(FlagDetails), + ) + + // get the initial validator commission parameters + rateStr := viper.GetString(FlagCommissionRate) + maxRateStr := viper.GetString(FlagCommissionMaxRate) + maxChangeRateStr := viper.GetString(FlagCommissionMaxChangeRate) + commissionRates, err := buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr) + if err != nil { + return txBldr, nil, err + } + + // get the initial validator min self delegation + msbStr := viper.GetString(FlagMinSelfDelegation) + minSelfDelegation, ok := sdk.NewIntFromString(msbStr) + if !ok { + return txBldr, nil, fmt.Errorf(types.ErrMinSelfDelegationInvalid(types.DefaultCodespace).Error()) + } + + msg := types.NewMsgCreateValidator( + sdk.ValAddress(valAddr), pk, amount, description, commissionRates, minSelfDelegation, + ) + + if viper.GetBool(client.FlagGenerateOnly) { + ip := viper.GetString(FlagIP) + nodeID := viper.GetString(FlagNodeID) + if nodeID != "" && ip != "" { + txBldr = txBldr.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) + } + } + + return txBldr, msg, nil +} diff --git a/x/staking/client/cli/tx_test.go b/x/staking/client/cli/tx_test.go new file mode 100644 index 0000000..f3ea62f --- /dev/null +++ b/x/staking/client/cli/tx_test.go @@ -0,0 +1,84 @@ +package cli + +import ( + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestPrepareFlagsForTxCreateValidator(t *testing.T) { + defer server.SetupViper(t)() + config, err := tcmd.ParseConfig() + require.Nil(t, err) + logger := log.NewNopLogger() + ctx := server.NewContext(config, logger) + + valPubKey, _ := sdk.GetConsPubKeyBech32("cosmosvalconspub1zcjduepq7jsrkl9fgqk0wj3ahmfr8pgxj6vakj2wzn656s8pehh0zhv2w5as5gd80a") + + type args struct { + config *cfg.Config + nodeID string + chainID string + valPubKey crypto.PubKey + } + + type extraParams struct { + amount string + commissionRate string + commissionMaxRate string + commissionMaxChangeRate string + minSelfDelegation string + } + + type testcase struct { + name string + args args + } + + runTest := func(t *testing.T, tt testcase, params extraParams) { + PrepareFlagsForTxCreateValidator(tt.args.config, tt.args.nodeID, + tt.args.chainID, tt.args.valPubKey) + + require.Equal(t, params.amount, viper.GetString(FlagAmount)) + require.Equal(t, params.commissionRate, viper.GetString(FlagCommissionRate)) + require.Equal(t, params.commissionMaxRate, viper.GetString(FlagCommissionMaxRate)) + require.Equal(t, params.commissionMaxChangeRate, viper.GetString(FlagCommissionMaxChangeRate)) + require.Equal(t, params.minSelfDelegation, viper.GetString(FlagMinSelfDelegation)) + } + + tests := []testcase{ + {"No parameters", args{ctx.Config, "X", "chainId", valPubKey}}, + } + + defaultParams := extraParams{ + defaultAmount, + defaultCommissionRate, + defaultCommissionMaxRate, + defaultCommissionMaxChangeRate, + defaultMinSelfDelegation, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Run(tt.name, func(t *testing.T) { runTest(t, tt, defaultParams) }) + }) + } + + // Override default params + params := extraParams{"5stake", "1.0", "1.0", "1.0", "1.0"} + viper.Set(FlagAmount, params.amount) + viper.Set(FlagCommissionRate, params.commissionRate) + viper.Set(FlagCommissionMaxRate, params.commissionMaxRate) + viper.Set(FlagCommissionMaxChangeRate, params.commissionMaxChangeRate) + viper.Set(FlagMinSelfDelegation, params.minSelfDelegation) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { runTest(t, tt, params) }) + } +} diff --git a/x/staking/client/cli/utils.go b/x/staking/client/cli/utils.go new file mode 100644 index 0000000..e10a79a --- /dev/null +++ b/x/staking/client/cli/utils.go @@ -0,0 +1,32 @@ +package cli + +import ( + "errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr string) (commission types.CommissionRates, err error) { + if rateStr == "" || maxRateStr == "" || maxChangeRateStr == "" { + return commission, errors.New("must specify all validator commission parameters") + } + + rate, err := sdk.NewDecFromStr(rateStr) + if err != nil { + return commission, err + } + + maxRate, err := sdk.NewDecFromStr(maxRateStr) + if err != nil { + return commission, err + } + + maxChangeRate, err := sdk.NewDecFromStr(maxChangeRateStr) + if err != nil { + return commission, err + } + + commission = types.NewCommissionRates(rate, maxRate, maxChangeRate) + return commission, nil +} diff --git a/x/staking/client/rest/query.go b/x/staking/client/rest/query.go new file mode 100644 index 0000000..e0eabd8 --- /dev/null +++ b/x/staking/client/rest/query.go @@ -0,0 +1,352 @@ +package rest + +import ( + "fmt" + "net/http" + "strings" + + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { + // Get all delegations from a delegator + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/delegations", + delegatorDelegationsHandlerFn(cliCtx), + ).Methods("GET") + + // Get all unbonding delegations from a delegator + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/unbonding_delegations", + delegatorUnbondingDelegationsHandlerFn(cliCtx), + ).Methods("GET") + + // Get all staking txs (i.e msgs) from a delegator + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/txs", + delegatorTxsHandlerFn(cliCtx), + ).Methods("GET") + + // Query all validators that a delegator is bonded to + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/validators", + delegatorValidatorsHandlerFn(cliCtx), + ).Methods("GET") + + // Query a validator that a delegator is bonded to + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/validators/{validatorAddr}", + delegatorValidatorHandlerFn(cliCtx), + ).Methods("GET") + + // Query a delegation between a delegator and a validator + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/delegations/{validatorAddr}", + delegationHandlerFn(cliCtx), + ).Methods("GET") + + // Query all unbonding delegations between a delegator and a validator + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}", + unbondingDelegationHandlerFn(cliCtx), + ).Methods("GET") + + // Query redelegations (filters in query params) + r.HandleFunc( + "/staking/redelegations", + redelegationsHandlerFn(cliCtx), + ).Methods("GET") + + // Get all validators + r.HandleFunc( + "/staking/validators", + validatorsHandlerFn(cliCtx), + ).Methods("GET") + + // Get a single validator info + r.HandleFunc( + "/staking/validators/{validatorAddr}", + validatorHandlerFn(cliCtx), + ).Methods("GET") + + // Get all delegations to a validator + r.HandleFunc( + "/staking/validators/{validatorAddr}/delegations", + validatorDelegationsHandlerFn(cliCtx), + ).Methods("GET") + + // Get all unbonding delegations from a validator + r.HandleFunc( + "/staking/validators/{validatorAddr}/unbonding_delegations", + validatorUnbondingDelegationsHandlerFn(cliCtx), + ).Methods("GET") + + // Get the current state of the staking pool + r.HandleFunc( + "/staking/pool", + poolHandlerFn(cliCtx), + ).Methods("GET") + + // Get the current staking parameter values + r.HandleFunc( + "/staking/parameters", + paramsHandlerFn(cliCtx), + ).Methods("GET") + +} + +// HTTP request handler to query a delegator delegations +func delegatorDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return queryDelegator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegatorDelegations)) +} + +// HTTP request handler to query a delegator unbonding delegations +func delegatorUnbondingDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return queryDelegator(cliCtx, "custom/staking/delegatorUnbondingDelegations") +} + +// HTTP request handler to query all staking txs (msgs) from a delegator +func delegatorTxsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var typesQuerySlice []string + vars := mux.Vars(r) + delegatorAddr := vars["delegatorAddr"] + + _, err := sdk.AccAddressFromBech32(delegatorAddr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + typesQuery := r.URL.Query().Get("type") + trimmedQuery := strings.TrimSpace(typesQuery) + if len(trimmedQuery) != 0 { + typesQuerySlice = strings.Split(trimmedQuery, " ") + } + + noQuery := len(typesQuerySlice) == 0 + isBondTx := contains(typesQuerySlice, "bond") + isUnbondTx := contains(typesQuerySlice, "unbond") + isRedTx := contains(typesQuerySlice, "redelegate") + + var ( + txs []*sdk.SearchTxsResult + actions []string + ) + + switch { + case isBondTx: + actions = append(actions, types.MsgDelegate{}.Type()) + + case isUnbondTx: + actions = append(actions, types.MsgUndelegate{}.Type()) + + case isRedTx: + actions = append(actions, types.MsgBeginRedelegate{}.Type()) + + case noQuery: + actions = append(actions, types.MsgDelegate{}.Type()) + actions = append(actions, types.MsgUndelegate{}.Type()) + actions = append(actions, types.MsgBeginRedelegate{}.Type()) + + default: + w.WriteHeader(http.StatusNoContent) + return + } + + for _, action := range actions { + foundTxs, errQuery := queryTxs(cliCtx, action, delegatorAddr) + if errQuery != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, errQuery.Error()) + } + txs = append(txs, foundTxs) + } + + res, err := cliCtx.Codec.MarshalJSON(txs) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + rest.PostProcessResponseBare(w, cliCtx, res) + } +} + +// HTTP request handler to query an unbonding-delegation +func unbondingDelegationHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return queryBonds(cliCtx, "custom/staking/unbondingDelegation") +} + +// HTTP request handler to query redelegations +func redelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var params types.QueryRedelegationParams + + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + bechDelegatorAddr := r.URL.Query().Get("delegator") + bechSrcValidatorAddr := r.URL.Query().Get("validator_from") + bechDstValidatorAddr := r.URL.Query().Get("validator_to") + + if len(bechDelegatorAddr) != 0 { + delegatorAddr, err := sdk.AccAddressFromBech32(bechDelegatorAddr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + params.DelegatorAddr = delegatorAddr + } + + if len(bechSrcValidatorAddr) != 0 { + srcValidatorAddr, err := sdk.ValAddressFromBech32(bechSrcValidatorAddr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + params.SrcValidatorAddr = srcValidatorAddr + } + + if len(bechDstValidatorAddr) != 0 { + dstValidatorAddr, err := sdk.ValAddressFromBech32(bechDstValidatorAddr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + params.DstValidatorAddr = dstValidatorAddr + } + + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + res, height, err := cliCtx.QueryWithData("custom/staking/redelegations", bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } +} + +// HTTP request handler to query a delegation +func delegationHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return queryBonds(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryDelegation)) +} + +// HTTP request handler to query all delegator bonded validators +func delegatorValidatorsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return queryDelegator(cliCtx, "custom/staking/delegatorValidators") +} + +// HTTP request handler to get information from a currently bonded validator +func delegatorValidatorHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return queryBonds(cliCtx, "custom/staking/delegatorValidator") +} + +// HTTP request handler to query list of validators +func validatorsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + status := r.FormValue("status") + if status == "" { + status = sdk.BondStatusBonded + } + + params := types.NewQueryValidatorsParams(page, limit, status) + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidators) + res, height, err := cliCtx.QueryWithData(route, bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } +} + +// HTTP request handler to query the validator information from a given validator address +func validatorHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return queryValidator(cliCtx, "custom/staking/validator") +} + +// HTTP request handler to query all unbonding delegations from a validator +func validatorDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return queryValidator(cliCtx, fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryValidatorDelegations)) +} + +// HTTP request handler to query all unbonding delegations from a validator +func validatorUnbondingDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return queryValidator(cliCtx, "custom/staking/validatorUnbondingDelegations") +} + +// HTTP request handler to query the pool information +func poolHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + res, height, err := cliCtx.QueryWithData("custom/staking/pool", nil) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } +} + +// HTTP request handler to query the staking params values +func paramsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + res, height, err := cliCtx.QueryWithData("custom/staking/parameters", nil) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } +} diff --git a/x/staking/client/rest/rest.go b/x/staking/client/rest/rest.go new file mode 100644 index 0000000..4237d59 --- /dev/null +++ b/x/staking/client/rest/rest.go @@ -0,0 +1,13 @@ +package rest + +import ( + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/context" +) + +// RegisterRoutes registers staking-related REST handlers to a router +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { + registerQueryRoutes(cliCtx, r) + registerTxRoutes(cliCtx, r) +} diff --git a/x/staking/client/rest/tx.go b/x/staking/client/rest/tx.go new file mode 100644 index 0000000..bd6016b --- /dev/null +++ b/x/staking/client/rest/tx.go @@ -0,0 +1,158 @@ +package rest + +import ( + "bytes" + "net/http" + + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/delegations", + postDelegationsHandlerFn(cliCtx), + ).Methods("POST") + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/unbonding_delegations", + postUnbondingDelegationsHandlerFn(cliCtx), + ).Methods("POST") + r.HandleFunc( + "/staking/delegators/{delegatorAddr}/redelegations", + postRedelegationsHandlerFn(cliCtx), + ).Methods("POST") +} + +type ( + // DelegateRequest defines the properties of a delegation request's body. + DelegateRequest struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` // in bech32 + ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` // in bech32 + Amount sdk.Coin `json:"amount" yaml:"amount"` + } + + // RedelegateRequest defines the properties of a redelegate request's body. + RedelegateRequest struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` // in bech32 + ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address" yaml:"validator_src_address"` // in bech32 + ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address" yaml:"validator_dst_address"` // in bech32 + Amount sdk.Coin `json:"amount" yaml:"amount"` + } + + // UndelegateRequest defines the properties of a undelegate request's body. + UndelegateRequest struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` // in bech32 + ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` // in bech32 + Amount sdk.Coin `json:"amount" yaml:"amount"` + } +) + +func postDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req DelegateRequest + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + msg := types.NewMsgDelegate(req.DelegatorAddress, req.ValidatorAddress, req.Amount) + if err := msg.ValidateBasic(); err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + if !bytes.Equal(fromAddr, req.DelegatorAddress) { + rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") + return + } + + utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + } +} + +func postRedelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req RedelegateRequest + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + msg := types.NewMsgBeginRedelegate(req.DelegatorAddress, req.ValidatorSrcAddress, req.ValidatorDstAddress, req.Amount) + if err := msg.ValidateBasic(); err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + if !bytes.Equal(fromAddr, req.DelegatorAddress) { + rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") + return + } + + utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + } +} + +func postUnbondingDelegationsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req UndelegateRequest + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + msg := types.NewMsgUndelegate(req.DelegatorAddress, req.ValidatorAddress, req.Amount) + if err := msg.ValidateBasic(); err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + if !bytes.Equal(fromAddr, req.DelegatorAddress) { + rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") + return + } + + utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) + } +} diff --git a/x/staking/client/rest/utils.go b/x/staking/client/rest/utils.go new file mode 100644 index 0000000..5d10682 --- /dev/null +++ b/x/staking/client/rest/utils.go @@ -0,0 +1,148 @@ +package rest + +import ( + "fmt" + "net/http" + + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// contains checks if the a given query contains one of the tx types +func contains(stringSlice []string, txType string) bool { + for _, word := range stringSlice { + if word == txType { + return true + } + } + return false +} + +// queries staking txs +func queryTxs(cliCtx context.CLIContext, action string, delegatorAddr string) (*sdk.SearchTxsResult, error) { + page := 1 + limit := 100 + events := []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, action), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, delegatorAddr), + } + + return utils.QueryTxsByEvents(cliCtx, events, page, limit) +} + +func queryBonds(cliCtx context.CLIContext, endpoint string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + bech32delegator := vars["delegatorAddr"] + bech32validator := vars["validatorAddr"] + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + validatorAddr, err := sdk.ValAddressFromBech32(bech32validator) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + params := types.NewQueryBondsParams(delegatorAddr, validatorAddr) + + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + res, height, err := cliCtx.QueryWithData(endpoint, bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } +} + +func queryDelegator(cliCtx context.CLIContext, endpoint string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + bech32delegator := vars["delegatorAddr"] + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + params := types.NewQueryDelegatorParams(delegatorAddr) + + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + res, height, err := cliCtx.QueryWithData(endpoint, bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } +} + +func queryValidator(cliCtx context.CLIContext, endpoint string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + bech32validatorAddr := vars["validatorAddr"] + + validatorAddr, err := sdk.ValAddressFromBech32(bech32validatorAddr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + params := types.NewQueryValidatorParams(validatorAddr) + + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + res, height, err := cliCtx.QueryWithData(endpoint, bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } +} diff --git a/x/staking/exported/exported.go b/x/staking/exported/exported.go new file mode 100644 index 0000000..88fa00c --- /dev/null +++ b/x/staking/exported/exported.go @@ -0,0 +1,37 @@ +package exported + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/tendermint/crypto" +) + +// DelegationI delegation bond for a delegated proof of stake system +type DelegationI interface { + GetDelegatorAddr() sdk.AccAddress // delegator sdk.AccAddress for the bond + GetValidatorAddr() sdk.ValAddress // validator operator address + GetShares() sdk.Dec // amount of validator's shares held in this delegation +} + +// ValidatorI expected validator functions +type ValidatorI interface { + IsJailed() bool // whether the validator is jailed + GetMoniker() string // moniker of the validator + GetStatus() sdk.BondStatus // status of the validator + IsBonded() bool // check if has a bonded status + IsUnbonded() bool // check if has status unbonded + IsUnbonding() bool // check if has status unbonding + GetOperator() sdk.ValAddress // operator address to receive/return validators coins + GetConsPubKey() crypto.PubKey // validation consensus pubkey + GetConsAddr() sdk.ConsAddress // validation consensus address + GetTokens() sdk.Int // validation tokens + GetBondedTokens() sdk.Int // validator bonded tokens + GetConsensusPower() int64 // validation power in tendermint + GetCommission() sdk.Dec // validator commission rate + GetMinSelfDelegation() sdk.Int // validator minimum self delegation + GetDelegatorShares() sdk.Dec // total outstanding delegator shares + TokensFromShares(sdk.Dec) sdk.Dec // token worth of provided delegator shares + TokensFromSharesTruncated(sdk.Dec) sdk.Dec // token worth of provided delegator shares, truncated + TokensFromSharesRoundUp(sdk.Dec) sdk.Dec // token worth of provided delegator shares, rounded up + SharesFromTokens(amt sdk.Int) (sdk.Dec, sdk.Error) // shares worth of delegator's bond + SharesFromTokensTruncated(amt sdk.Int) (sdk.Dec, sdk.Error) // truncated shares worth of delegator's bond +} diff --git a/x/staking/genesis.go b/x/staking/genesis.go new file mode 100644 index 0000000..91d3a41 --- /dev/null +++ b/x/staking/genesis.go @@ -0,0 +1,222 @@ +package staking + +import ( + "fmt" + + abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/exported" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// InitGenesis sets the pool and parameters for the provided keeper. For each +// validator in data, it sets that validator in the keeper along with manually +// setting the indexes. In addition, it also sets any delegations found in +// data. Finally, it updates the bonded validators. +// Returns final validator set after applying all declaration and delegations +func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeeper, + supplyKeeper types.SupplyKeeper, data types.GenesisState) (res []abci.ValidatorUpdate) { + + bondedTokens := sdk.ZeroInt() + notBondedTokens := sdk.ZeroInt() + + // We need to pretend to be "n blocks before genesis", where "n" is the + // validator update delay, so that e.g. slashing periods are correctly + // initialized for the validator set e.g. with a one-block offset - the + // first TM block is at height 1, so state updates applied from + // genesis.json are in block 0. + ctx = ctx.WithBlockHeight(1 - sdk.ValidatorUpdateDelay) + + keeper.SetParams(ctx, data.Params) + keeper.SetLastTotalPower(ctx, data.LastTotalPower) + + for _, validator := range data.Validators { + keeper.SetValidator(ctx, validator) + + // Manually set indices for the first time + keeper.SetValidatorByConsAddr(ctx, validator) + keeper.SetValidatorByPowerIndex(ctx, validator) + + // Call the creation hook if not exported + if !data.Exported { + keeper.AfterValidatorCreated(ctx, validator.OperatorAddress) + } + + // update timeslice if necessary + if validator.IsUnbonding() { + keeper.InsertValidatorQueue(ctx, validator) + } + + switch validator.GetStatus() { + case sdk.Bonded: + bondedTokens = bondedTokens.Add(validator.GetTokens()) + case sdk.Unbonding, sdk.Unbonded: + notBondedTokens = notBondedTokens.Add(validator.GetTokens()) + default: + panic("invalid validator status") + } + } + + for _, delegation := range data.Delegations { + // Call the before-creation hook if not exported + if !data.Exported { + keeper.BeforeDelegationCreated(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + } + keeper.SetDelegation(ctx, delegation) + + // Call the after-modification hook if not exported + if !data.Exported { + keeper.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + } + } + + for _, ubd := range data.UnbondingDelegations { + keeper.SetUnbondingDelegation(ctx, ubd) + for _, entry := range ubd.Entries { + keeper.InsertUBDQueue(ctx, ubd, entry.CompletionTime) + notBondedTokens = notBondedTokens.Add(entry.Balance) + } + } + + for _, red := range data.Redelegations { + keeper.SetRedelegation(ctx, red) + for _, entry := range red.Entries { + keeper.InsertRedelegationQueue(ctx, red, entry.CompletionTime) + } + } + + bondedCoins := sdk.NewCoins(sdk.NewCoin(data.Params.BondDenom, bondedTokens)) + notBondedCoins := sdk.NewCoins(sdk.NewCoin(data.Params.BondDenom, notBondedTokens)) + + // check if the unbonded and bonded pools accounts exists + bondedPool := keeper.GetBondedPool(ctx) + if bondedPool == nil { + panic(fmt.Sprintf("%s module account has not been set", types.BondedPoolName)) + } + + // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 + // add coins if not provided on genesis + if bondedPool.GetCoins().IsZero() { + if err := bondedPool.SetCoins(bondedCoins); err != nil { + panic(err) + } + supplyKeeper.SetModuleAccount(ctx, bondedPool) + } + + notBondedPool := keeper.GetNotBondedPool(ctx) + if notBondedPool == nil { + panic(fmt.Sprintf("%s module account has not been set", types.NotBondedPoolName)) + } + + if notBondedPool.GetCoins().IsZero() { + if err := notBondedPool.SetCoins(notBondedCoins); err != nil { + panic(err) + } + supplyKeeper.SetModuleAccount(ctx, notBondedPool) + } + + // don't need to run Tendermint updates if we exported + if data.Exported { + for _, lv := range data.LastValidatorPowers { + keeper.SetLastValidatorPower(ctx, lv.Address, lv.Power) + validator, found := keeper.GetValidator(ctx, lv.Address) + if !found { + panic(fmt.Sprintf("validator %s not found", lv.Address)) + } + update := validator.ABCIValidatorUpdate() + update.Power = lv.Power // keep the next-val-set offset, use the last power for the first block + res = append(res, update) + } + } else { + res = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + } + + return res +} + +// ExportGenesis returns a GenesisState for a given context and keeper. The +// GenesisState will contain the pool, params, validators, and bonds found in +// the keeper. +func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { + params := keeper.GetParams(ctx) + lastTotalPower := keeper.GetLastTotalPower(ctx) + validators := keeper.GetAllValidators(ctx) + delegations := keeper.GetAllDelegations(ctx) + var unbondingDelegations []types.UnbondingDelegation + keeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd types.UnbondingDelegation) (stop bool) { + unbondingDelegations = append(unbondingDelegations, ubd) + return false + }) + var redelegations []types.Redelegation + keeper.IterateRedelegations(ctx, func(_ int64, red types.Redelegation) (stop bool) { + redelegations = append(redelegations, red) + return false + }) + var lastValidatorPowers []types.LastValidatorPower + keeper.IterateLastValidatorPowers(ctx, func(addr sdk.ValAddress, power int64) (stop bool) { + lastValidatorPowers = append(lastValidatorPowers, types.LastValidatorPower{addr, power}) + return false + }) + + return types.GenesisState{ + Params: params, + LastTotalPower: lastTotalPower, + LastValidatorPowers: lastValidatorPowers, + Validators: validators, + Delegations: delegations, + UnbondingDelegations: unbondingDelegations, + Redelegations: redelegations, + Exported: true, + } +} + +// WriteValidators returns a slice of bonded genesis validators. +func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisValidator) { + keeper.IterateLastValidators(ctx, func(_ int64, validator exported.ValidatorI) (stop bool) { + vals = append(vals, tmtypes.GenesisValidator{ + PubKey: validator.GetConsPubKey(), + Power: validator.GetConsensusPower(), + Name: validator.GetMoniker(), + }) + + return false + }) + + return +} + +// ValidateGenesis validates the provided staking genesis state to ensure the +// expected invariants holds. (i.e. params in correct bounds, no duplicate validators) +func ValidateGenesis(data types.GenesisState) error { + err := validateGenesisStateValidators(data.Validators) + if err != nil { + return err + } + err = data.Params.Validate() + if err != nil { + return err + } + + return nil +} + +func validateGenesisStateValidators(validators []types.Validator) (err error) { + addrMap := make(map[string]bool, len(validators)) + for i := 0; i < len(validators); i++ { + val := validators[i] + strKey := string(val.ConsPubKey.Bytes()) + if _, ok := addrMap[strKey]; ok { + return fmt.Errorf("duplicate validator in genesis state: moniker %v, address %v", val.Description.Moniker, val.ConsAddress()) + } + if val.Jailed && val.IsBonded() { + return fmt.Errorf("validator is bonded and jailed in genesis state: moniker %v, address %v", val.Description.Moniker, val.ConsAddress()) + } + if val.DelegatorShares.IsZero() && !val.IsUnbonding() { + return fmt.Errorf("bonded/unbonded genesis validator cannot have zero delegator shares, validator: %v", val) + } + addrMap[strKey] = true + } + return +} diff --git a/x/staking/genesis_test.go b/x/staking/genesis_test.go new file mode 100644 index 0000000..78c931e --- /dev/null +++ b/x/staking/genesis_test.go @@ -0,0 +1,142 @@ +package staking + +import ( + "fmt" + "testing" + + "github.com/tendermint/tendermint/crypto/ed25519" + + "github.com/stretchr/testify/assert" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + keep "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func TestInitGenesis(t *testing.T) { + ctx, accKeeper, keeper, supplyKeeper := keep.CreateTestInput(t, false, 1000) + + valTokens := sdk.TokensFromConsensusPower(1) + + params := keeper.GetParams(ctx) + validators := make([]Validator, 2) + var delegations []Delegation + + // initialize the validators + validators[0].OperatorAddress = sdk.ValAddress(keep.Addrs[0]) + validators[0].ConsPubKey = keep.PKs[0] + validators[0].Description = NewDescription("hoop", "", "", "" , "") + validators[0].Status = sdk.Bonded + validators[0].Tokens = valTokens + validators[0].DelegatorShares = valTokens.ToDec() + validators[1].OperatorAddress = sdk.ValAddress(keep.Addrs[1]) + validators[1].ConsPubKey = keep.PKs[1] + validators[1].Description = NewDescription("bloop", "", "", "" , "") + validators[1].Status = sdk.Bonded + validators[1].Tokens = valTokens + validators[1].DelegatorShares = valTokens.ToDec() + + genesisState := types.NewGenesisState(params, validators, delegations) + vals := InitGenesis(ctx, keeper, accKeeper, supplyKeeper, genesisState) + + actualGenesis := ExportGenesis(ctx, keeper) + require.Equal(t, genesisState.Params, actualGenesis.Params) + require.Equal(t, genesisState.Delegations, actualGenesis.Delegations) + require.EqualValues(t, keeper.GetAllValidators(ctx), actualGenesis.Validators) + + // now make sure the validators are bonded and intra-tx counters are correct + resVal, found := keeper.GetValidator(ctx, sdk.ValAddress(keep.Addrs[0])) + require.True(t, found) + require.Equal(t, sdk.Bonded, resVal.Status) + + resVal, found = keeper.GetValidator(ctx, sdk.ValAddress(keep.Addrs[1])) + require.True(t, found) + require.Equal(t, sdk.Bonded, resVal.Status) + + abcivals := make([]abci.ValidatorUpdate, len(vals)) + for i, val := range validators { + abcivals[i] = val.ABCIValidatorUpdate() + } + + require.Equal(t, abcivals, vals) +} + +func TestInitGenesisLargeValidatorSet(t *testing.T) { + size := 200 + require.True(t, size > 100) + + ctx, accKeeper, keeper, supplyKeeper := keep.CreateTestInput(t, false, 1000) + + params := keeper.GetParams(ctx) + delegations := []Delegation{} + validators := make([]Validator, size) + + for i := range validators { + validators[i] = NewValidator(sdk.ValAddress(keep.Addrs[i]), + keep.PKs[i], NewDescription(fmt.Sprintf("#%d", i), "", "", "","")) + + validators[i].Status = sdk.Bonded + + tokens := sdk.TokensFromConsensusPower(1) + if i < 100 { + tokens = sdk.TokensFromConsensusPower(2) + } + validators[i].Tokens = tokens + validators[i].DelegatorShares = tokens.ToDec() + } + + genesisState := types.NewGenesisState(params, validators, delegations) + vals := InitGenesis(ctx, keeper, accKeeper, supplyKeeper, genesisState) + + abcivals := make([]abci.ValidatorUpdate, 100) + for i, val := range validators[:100] { + abcivals[i] = val.ABCIValidatorUpdate() + } + + require.Equal(t, abcivals, vals) +} + +func TestValidateGenesis(t *testing.T) { + genValidators1 := make([]types.Validator, 1, 5) + pk := ed25519.GenPrivKey().PubKey() + genValidators1[0] = types.NewValidator(sdk.ValAddress(pk.Address()), pk, types.NewDescription("", "", "", "","")) + genValidators1[0].Tokens = sdk.OneInt() + genValidators1[0].DelegatorShares = sdk.OneDec() + + tests := []struct { + name string + mutate func(*types.GenesisState) + wantErr bool + }{ + {"default", func(*types.GenesisState) {}, false}, + // validate genesis validators + {"duplicate validator", func(data *types.GenesisState) { + (*data).Validators = genValidators1 + (*data).Validators = append((*data).Validators, genValidators1[0]) + }, true}, + {"no delegator shares", func(data *types.GenesisState) { + (*data).Validators = genValidators1 + (*data).Validators[0].DelegatorShares = sdk.ZeroDec() + }, true}, + {"jailed and bonded validator", func(data *types.GenesisState) { + (*data).Validators = genValidators1 + (*data).Validators[0].Jailed = true + (*data).Validators[0].Status = sdk.Bonded + }, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + genesisState := types.DefaultGenesisState() + tt.mutate(&genesisState) + if tt.wantErr { + assert.Error(t, ValidateGenesis(genesisState)) + } else { + assert.NoError(t, ValidateGenesis(genesisState)) + } + }) + } +} diff --git a/x/staking/handler.go b/x/staking/handler.go new file mode 100644 index 0000000..ad39070 --- /dev/null +++ b/x/staking/handler.go @@ -0,0 +1,329 @@ +package staking + +import ( + "fmt" + "time" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/common" + tmtypes "github.com/tendermint/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func NewHandler(k keeper.Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + + switch msg := msg.(type) { + case types.MsgCreateValidator: + return handleMsgCreateValidator(ctx, msg, k) + + case types.MsgEditValidator: + return handleMsgEditValidator(ctx, msg, k) + + case types.MsgDelegate: + return handleMsgDelegate(ctx, msg, k) + + case types.MsgBeginRedelegate: + return handleMsgBeginRedelegate(ctx, msg, k) + + case types.MsgUndelegate: + return handleMsgUndelegate(ctx, msg, k) + + default: + errMsg := fmt.Sprintf("unrecognized staking message type: %T", msg) + return sdk.ErrUnknownRequest(errMsg).Result() + } + } +} + +// Called every block, update validator set +func EndBlocker(ctx sdk.Context, k keeper.Keeper) []abci.ValidatorUpdate { + // Calculate validator set changes. + // + // NOTE: ApplyAndReturnValidatorSetUpdates has to come before + // UnbondAllMatureValidatorQueue. + // This fixes a bug when the unbonding period is instant (is the case in + // some of the tests). The test expected the validator to be completely + // unbonded after the Endblocker (go from Bonded -> Unbonding during + // ApplyAndReturnValidatorSetUpdates and then Unbonding -> Unbonded during + // UnbondAllMatureValidatorQueue). + validatorUpdates := k.ApplyAndReturnValidatorSetUpdates(ctx) + + // Unbond all mature validators from the unbonding queue. + k.UnbondAllMatureValidatorQueue(ctx) + + // Remove all mature unbonding delegations from the ubd queue. + matureUnbonds := k.DequeueAllMatureUBDQueue(ctx, ctx.BlockHeader().Time) + for _, dvPair := range matureUnbonds { + err := k.CompleteUnbonding(ctx, dvPair.DelegatorAddress, dvPair.ValidatorAddress) + if err != nil { + continue + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeCompleteUnbonding, + sdk.NewAttribute(types.AttributeKeyValidator, dvPair.ValidatorAddress.String()), + sdk.NewAttribute(types.AttributeKeyDelegator, dvPair.DelegatorAddress.String()), + ), + ) + } + + // Remove all mature redelegations from the red queue. + matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time) + for _, dvvTriplet := range matureRedelegations { + err := k.CompleteRedelegation(ctx, dvvTriplet.DelegatorAddress, + dvvTriplet.ValidatorSrcAddress, dvvTriplet.ValidatorDstAddress) + if err != nil { + continue + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeCompleteRedelegation, + sdk.NewAttribute(types.AttributeKeyDelegator, dvvTriplet.DelegatorAddress.String()), + sdk.NewAttribute(types.AttributeKeySrcValidator, dvvTriplet.ValidatorSrcAddress.String()), + sdk.NewAttribute(types.AttributeKeyDstValidator, dvvTriplet.ValidatorDstAddress.String()), + ), + ) + } + + return validatorUpdates +} + +// These functions assume everything has been authenticated, +// now we just perform action and save + +func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k keeper.Keeper) sdk.Result { + // check to see if the pubkey or sender has been registered before + if _, found := k.GetValidator(ctx, msg.ValidatorAddress); found { + return ErrValidatorOwnerExists(k.Codespace()).Result() + } + + if _, found := k.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(msg.PubKey)); found { + return ErrValidatorPubKeyExists(k.Codespace()).Result() + } + + if msg.Value.Denom != k.BondDenom(ctx) { + return ErrBadDenom(k.Codespace()).Result() + } + + if _, err := msg.Description.EnsureLength(); err != nil { + return err.Result() + } + + if ctx.ConsensusParams() != nil { + tmPubKey := tmtypes.TM2PB.PubKey(msg.PubKey) + if !common.StringInSlice(tmPubKey.Type, ctx.ConsensusParams().Validator.PubKeyTypes) { + return ErrValidatorPubKeyTypeNotSupported(k.Codespace(), + tmPubKey.Type, + ctx.ConsensusParams().Validator.PubKeyTypes).Result() + } + } + + validator := NewValidator(msg.ValidatorAddress, msg.PubKey, msg.Description) + commission := NewCommissionWithTime( + msg.Commission.Rate, msg.Commission.MaxRate, + msg.Commission.MaxChangeRate, ctx.BlockHeader().Time, + ) + validator, err := validator.SetInitialCommission(commission) + if err != nil { + return err.Result() + } + + validator.MinSelfDelegation = msg.MinSelfDelegation + + k.SetValidator(ctx, validator) + k.SetValidatorByConsAddr(ctx, validator) + k.SetNewValidatorByPowerIndex(ctx, validator) + + // call the after-creation hook + k.AfterValidatorCreated(ctx, validator.OperatorAddress) + + // move coins from the msg.Address account to a (self-delegation) delegator account + // the validator account and global shares are updated within here + // NOTE source will always be from a wallet which are unbonded + _, err = k.Delegate(ctx, msg.DelegatorAddress, msg.Value.Amount, sdk.Unbonded, validator, true) + if err != nil { + return err.Result() + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeCreateValidator, + sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Value.Amount.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()), + ), + }) + + return sdk.Result{Events: ctx.EventManager().Events()} +} + +func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keeper.Keeper) sdk.Result { + // validator must already be registered + validator, found := k.GetValidator(ctx, msg.ValidatorAddress) + if !found { + return ErrNoValidatorFound(k.Codespace()).Result() + } + + // replace all editable fields (clients should autofill existing values) + description, err := validator.Description.UpdateDescription(msg.Description) + if err != nil { + return err.Result() + } + + validator.Description = description + + if msg.CommissionRate != nil { + commission, err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate) + if err != nil { + return err.Result() + } + + // call the before-modification hook since we're about to update the commission + k.BeforeValidatorModified(ctx, msg.ValidatorAddress) + + validator.Commission = commission + } + + if msg.MinSelfDelegation != nil { + if !(*msg.MinSelfDelegation).GT(validator.MinSelfDelegation) { + return ErrMinSelfDelegationDecreased(k.Codespace()).Result() + } + if (*msg.MinSelfDelegation).GT(validator.Tokens) { + return ErrSelfDelegationBelowMinimum(k.Codespace()).Result() + } + validator.MinSelfDelegation = (*msg.MinSelfDelegation) + } + + k.SetValidator(ctx, validator) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeEditValidator, + sdk.NewAttribute(types.AttributeKeyCommissionRate, validator.Commission.String()), + sdk.NewAttribute(types.AttributeKeyMinSelfDelegation, validator.MinSelfDelegation.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.ValidatorAddress.String()), + ), + }) + + return sdk.Result{Events: ctx.EventManager().Events()} +} + +func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) sdk.Result { + validator, found := k.GetValidator(ctx, msg.ValidatorAddress) + if !found { + return ErrNoValidatorFound(k.Codespace()).Result() + } + + if msg.Amount.Denom != k.BondDenom(ctx) { + return ErrBadDenom(k.Codespace()).Result() + } + + // NOTE: source funds are always unbonded + _, err := k.Delegate(ctx, msg.DelegatorAddress, msg.Amount.Amount, sdk.Unbonded, validator, true) + if err != nil { + return err.Result() + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeDelegate, + sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()), + ), + }) + + return sdk.Result{Events: ctx.EventManager().Events()} +} + +func handleMsgUndelegate(ctx sdk.Context, msg types.MsgUndelegate, k keeper.Keeper) sdk.Result { + shares, err := k.ValidateUnbondAmount( + ctx, msg.DelegatorAddress, msg.ValidatorAddress, msg.Amount.Amount, + ) + if err != nil { + return err.Result() + } + + if msg.Amount.Denom != k.BondDenom(ctx) { + return ErrBadDenom(k.Codespace()).Result() + } + + completionTime, err := k.Undelegate(ctx, msg.DelegatorAddress, msg.ValidatorAddress, shares) + if err != nil { + return err.Result() + } + + completionTimeBz := types.ModuleCdc.MustMarshalBinaryLengthPrefixed(completionTime) + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeUnbond, + sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()), + sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()), + ), + }) + + return sdk.Result{Data: completionTimeBz, Events: ctx.EventManager().Events()} +} + +func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result { + shares, err := k.ValidateUnbondAmount( + ctx, msg.DelegatorAddress, msg.ValidatorSrcAddress, msg.Amount.Amount, + ) + if err != nil { + return err.Result() + } + + if msg.Amount.Denom != k.BondDenom(ctx) { + return ErrBadDenom(k.Codespace()).Result() + } + + completionTime, err := k.BeginRedelegation( + ctx, msg.DelegatorAddress, msg.ValidatorSrcAddress, msg.ValidatorDstAddress, shares, + ) + if err != nil { + return err.Result() + } + + completionTimeBz := types.ModuleCdc.MustMarshalBinaryLengthPrefixed(completionTime) + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeRedelegate, + sdk.NewAttribute(types.AttributeKeySrcValidator, msg.ValidatorSrcAddress.String()), + sdk.NewAttribute(types.AttributeKeyDstValidator, msg.ValidatorDstAddress.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()), + sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress.String()), + ), + }) + + return sdk.Result{Data: completionTimeBz, Events: ctx.EventManager().Events()} +} diff --git a/x/staking/handler_test.go b/x/staking/handler_test.go new file mode 100644 index 0000000..f632982 --- /dev/null +++ b/x/staking/handler_test.go @@ -0,0 +1,1326 @@ +package staking + +import ( + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/secp256k1" + tmtypes "github.com/tendermint/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + keep "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +//______________________________________________________________________ + +// retrieve params which are instant +func setInstantUnbondPeriod(keeper keep.Keeper, ctx sdk.Context) types.Params { + params := keeper.GetParams(ctx) + params.UnbondingTime = 0 + keeper.SetParams(ctx, params) + return params +} + +//______________________________________________________________________ + +func TestValidatorByPowerIndex(t *testing.T) { + validatorAddr, validatorAddr3 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]) + + initPower := int64(1000000) + initBond := sdk.TokensFromConsensusPower(initPower) + ctx, _, keeper, _ := keep.CreateTestInput(t, false, initPower) + _ = setInstantUnbondPeriod(keeper, ctx) + + // create validator + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) + + // must end-block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + // verify the self-delegation exists + bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + require.True(t, found) + gotBond := bond.Shares.RoundInt() + require.Equal(t, initBond, gotBond) + + // verify that the by power index exists + validator, found := keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + power := GetValidatorsByPowerIndexKey(validator) + require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power)) + + // create a second validator keep it bonded + msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], initBond) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) + + // must end-block + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + // slash and jail the first validator + consAddr0 := sdk.ConsAddress(keep.PKs[0].Address()) + keeper.Slash(ctx, consAddr0, 0, initPower, sdk.NewDecWithPrec(5, 1)) + keeper.Jail(ctx, consAddr0) + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + validator, found = keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + require.Equal(t, sdk.Unbonding, validator.Status) // ensure is unbonding + require.Equal(t, initBond.QuoRaw(2), validator.Tokens) // ensure tokens slashed + keeper.Unjail(ctx, consAddr0) + + // the old power record should have been deleted as the power changed + require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power)) + + // but the new power record should have been created + validator, found = keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + power2 := GetValidatorsByPowerIndexKey(validator) + require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power2)) + + // now the new record power index should be the same as the original record + power3 := GetValidatorsByPowerIndexKey(validator) + require.Equal(t, power2, power3) + + // unbond self-delegation + totalBond := validator.TokensFromShares(bond.GetShares()).TruncateInt() + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, totalBond) + msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt) + + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK(), "expected msg to be ok, got %v", got) + + var finishTime time.Time + types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) + + ctx = ctx.WithBlockTime(finishTime) + EndBlocker(ctx, keeper) + EndBlocker(ctx, keeper) + + // verify that by power key nolonger exists + _, found = keeper.GetValidator(ctx, validatorAddr) + require.False(t, found) + require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power3)) +} + +func TestDuplicatesMsgCreateValidator(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + + addr1, addr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]) + pk1, pk2 := keep.PKs[0], keep.PKs[1] + + valTokens := sdk.TokensFromConsensusPower(10) + msgCreateValidator1 := NewTestMsgCreateValidator(addr1, pk1, valTokens) + got := handleMsgCreateValidator(ctx, msgCreateValidator1, keeper) + require.True(t, got.IsOK(), "%v", got) + + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + validator, found := keeper.GetValidator(ctx, addr1) + require.True(t, found) + assert.Equal(t, sdk.Bonded, validator.Status) + assert.Equal(t, addr1, validator.OperatorAddress) + assert.Equal(t, pk1, validator.ConsPubKey) + assert.Equal(t, valTokens, validator.BondedTokens()) + assert.Equal(t, valTokens.ToDec(), validator.DelegatorShares) + assert.Equal(t, Description{}, validator.Description) + + // two validators can't have the same operator address + msgCreateValidator2 := NewTestMsgCreateValidator(addr1, pk2, valTokens) + got = handleMsgCreateValidator(ctx, msgCreateValidator2, keeper) + require.False(t, got.IsOK(), "%v", got) + + // two validators can't have the same pubkey + msgCreateValidator3 := NewTestMsgCreateValidator(addr2, pk1, valTokens) + got = handleMsgCreateValidator(ctx, msgCreateValidator3, keeper) + require.False(t, got.IsOK(), "%v", got) + + // must have different pubkey and operator + msgCreateValidator4 := NewTestMsgCreateValidator(addr2, pk2, valTokens) + got = handleMsgCreateValidator(ctx, msgCreateValidator4, keeper) + require.True(t, got.IsOK(), "%v", got) + + // must end-block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + validator, found = keeper.GetValidator(ctx, addr2) + + require.True(t, found) + assert.Equal(t, sdk.Bonded, validator.Status) + assert.Equal(t, addr2, validator.OperatorAddress) + assert.Equal(t, pk2, validator.ConsPubKey) + assert.True(sdk.IntEq(t, valTokens, validator.Tokens)) + assert.True(sdk.DecEq(t, valTokens.ToDec(), validator.DelegatorShares)) + assert.Equal(t, Description{}, validator.Description) +} + +func TestInvalidPubKeyTypeMsgCreateValidator(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + + addr := sdk.ValAddress(keep.Addrs[0]) + invalidPk := secp256k1.GenPrivKey().PubKey() + + // invalid pukKey type should not be allowed + msgCreateValidator := NewTestMsgCreateValidator(addr, invalidPk, sdk.NewInt(10)) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.False(t, got.IsOK(), "%v", got) + + ctx = ctx.WithConsensusParams(&abci.ConsensusParams{ + Validator: &abci.ValidatorParams{PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeSecp256k1}}, + }) + + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "%v", got) +} + +func TestLegacyValidatorDelegations(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, int64(1000)) + setInstantUnbondPeriod(keeper, ctx) + + bondAmount := sdk.TokensFromConsensusPower(10) + valAddr := sdk.ValAddress(keep.Addrs[0]) + valConsPubKey, valConsAddr := keep.PKs[0], sdk.ConsAddress(keep.PKs[0].Address()) + delAddr := keep.Addrs[1] + + // create validator + msgCreateVal := NewTestMsgCreateValidator(valAddr, valConsPubKey, bondAmount) + got := handleMsgCreateValidator(ctx, msgCreateVal, keeper) + require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got) + + // must end-block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + // verify the validator exists and has the correct attributes + validator, found := keeper.GetValidator(ctx, valAddr) + require.True(t, found) + require.Equal(t, sdk.Bonded, validator.Status) + require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) + require.Equal(t, bondAmount, validator.BondedTokens()) + + // delegate tokens to the validator + msgDelegate := NewTestMsgDelegate(delAddr, valAddr, bondAmount) + got = handleMsgDelegate(ctx, msgDelegate, keeper) + require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got) + + // verify validator bonded shares + validator, found = keeper.GetValidator(ctx, valAddr) + require.True(t, found) + require.Equal(t, bondAmount.MulRaw(2), validator.DelegatorShares.RoundInt()) + require.Equal(t, bondAmount.MulRaw(2), validator.BondedTokens()) + + // unbond validator total self-delegations (which should jail the validator) + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, bondAmount) + msgUndelegate := NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondAmt) + + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK(), "expected begin unbonding validator msg to be ok, got %v", got) + + var finishTime time.Time + types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) + ctx = ctx.WithBlockTime(finishTime) + EndBlocker(ctx, keeper) + + // verify the validator record still exists, is jailed, and has correct tokens + validator, found = keeper.GetValidator(ctx, valAddr) + require.True(t, found) + require.True(t, validator.Jailed) + require.Equal(t, bondAmount, validator.Tokens) + + // verify delegation still exists + bond, found := keeper.GetDelegation(ctx, delAddr, valAddr) + require.True(t, found) + require.Equal(t, bondAmount, bond.Shares.RoundInt()) + require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) + + // verify the validator can still self-delegate + msgSelfDelegate := NewTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount) + got = handleMsgDelegate(ctx, msgSelfDelegate, keeper) + require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got) + + // verify validator bonded shares + validator, found = keeper.GetValidator(ctx, valAddr) + require.True(t, found) + require.Equal(t, bondAmount.MulRaw(2), validator.DelegatorShares.RoundInt()) + require.Equal(t, bondAmount.MulRaw(2), validator.Tokens) + + // unjail the validator now that is has non-zero self-delegated shares + keeper.Unjail(ctx, valConsAddr) + + // verify the validator can now accept delegations + msgDelegate = NewTestMsgDelegate(delAddr, valAddr, bondAmount) + got = handleMsgDelegate(ctx, msgDelegate, keeper) + require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got) + + // verify validator bonded shares + validator, found = keeper.GetValidator(ctx, valAddr) + require.True(t, found) + require.Equal(t, bondAmount.MulRaw(3), validator.DelegatorShares.RoundInt()) + require.Equal(t, bondAmount.MulRaw(3), validator.Tokens) + + // verify new delegation + bond, found = keeper.GetDelegation(ctx, delAddr, valAddr) + require.True(t, found) + require.Equal(t, bondAmount.MulRaw(2), bond.Shares.RoundInt()) + require.Equal(t, bondAmount.MulRaw(3), validator.DelegatorShares.RoundInt()) +} + +func TestIncrementsMsgDelegate(t *testing.T) { + initPower := int64(1000) + initBond := sdk.TokensFromConsensusPower(initPower) + ctx, accMapper, keeper, _ := keep.CreateTestInput(t, false, initPower) + params := keeper.GetParams(ctx) + + bondAmount := sdk.TokensFromConsensusPower(10) + validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1] + + // first create validator + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], bondAmount) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got) + + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + validator, found := keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + require.Equal(t, sdk.Bonded, validator.Status) + require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) + require.Equal(t, bondAmount, validator.BondedTokens(), "validator: %v", validator) + + _, found = keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) + require.False(t, found) + + bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + require.True(t, found) + require.Equal(t, bondAmount, bond.Shares.RoundInt()) + + bondedTokens := keeper.TotalBondedTokens(ctx) + require.Equal(t, bondAmount.Int64(), bondedTokens.Int64()) + + // just send the same msgbond multiple times + msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, bondAmount) + + for i := int64(0); i < 5; i++ { + ctx = ctx.WithBlockHeight(int64(i)) + + got := handleMsgDelegate(ctx, msgDelegate, keeper) + require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) + + //Check that the accounts and the bond account have the appropriate values + validator, found := keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) + require.True(t, found) + + expBond := bondAmount.MulRaw(i + 1) + expDelegatorShares := bondAmount.MulRaw(i + 2) // (1 self delegation) + expDelegatorAcc := initBond.Sub(expBond) + + gotBond := bond.Shares.RoundInt() + gotDelegatorShares := validator.DelegatorShares.RoundInt() + gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom) + + require.Equal(t, expBond, gotBond, + "i: %v\nexpBond: %v\ngotBond: %v\nvalidator: %v\nbond: %v\n", + i, expBond, gotBond, validator, bond) + require.Equal(t, expDelegatorShares, gotDelegatorShares, + "i: %v\nexpDelegatorShares: %v\ngotDelegatorShares: %v\nvalidator: %v\nbond: %v\n", + i, expDelegatorShares, gotDelegatorShares, validator, bond) + require.Equal(t, expDelegatorAcc, gotDelegatorAcc, + "i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\nvalidator: %v\nbond: %v\n", + i, expDelegatorAcc, gotDelegatorAcc, validator, bond) + } +} + +func TestEditValidatorDecreaseMinSelfDelegation(t *testing.T) { + validatorAddr := sdk.ValAddress(keep.Addrs[0]) + + initPower := int64(100) + initBond := sdk.TokensFromConsensusPower(100) + ctx, _, keeper, _ := keep.CreateTestInput(t, false, initPower) + _ = setInstantUnbondPeriod(keeper, ctx) + + // create validator + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond) + msgCreateValidator.MinSelfDelegation = sdk.NewInt(2) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) + + // must end-block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + // verify the self-delegation exists + bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + require.True(t, found) + gotBond := bond.Shares.RoundInt() + require.Equal(t, initBond, gotBond, + "initBond: %v\ngotBond: %v\nbond: %v\n", + initBond, gotBond, bond) + + newMinSelfDelegation := sdk.OneInt() + msgEditValidator := NewMsgEditValidator(validatorAddr, Description{}, nil, &newMinSelfDelegation) + got = handleMsgEditValidator(ctx, msgEditValidator, keeper) + require.False(t, got.IsOK(), "should not be able to decrease minSelfDelegation") +} + +func TestEditValidatorIncreaseMinSelfDelegationBeyondCurrentBond(t *testing.T) { + validatorAddr := sdk.ValAddress(keep.Addrs[0]) + + initPower := int64(100) + initBond := sdk.TokensFromConsensusPower(100) + ctx, _, keeper, _ := keep.CreateTestInput(t, false, initPower) + _ = setInstantUnbondPeriod(keeper, ctx) + + // create validator + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond) + msgCreateValidator.MinSelfDelegation = sdk.NewInt(2) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) + + // must end-block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + // verify the self-delegation exists + bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + require.True(t, found) + gotBond := bond.Shares.RoundInt() + require.Equal(t, initBond, gotBond, + "initBond: %v\ngotBond: %v\nbond: %v\n", + initBond, gotBond, bond) + + newMinSelfDelegation := initBond.Add(sdk.OneInt()) + msgEditValidator := NewMsgEditValidator(validatorAddr, Description{}, nil, &newMinSelfDelegation) + got = handleMsgEditValidator(ctx, msgEditValidator, keeper) + require.False(t, got.IsOK(), "should not be able to increase minSelfDelegation above current self delegation") +} + +func TestIncrementsMsgUnbond(t *testing.T) { + initPower := int64(1000) + initBond := sdk.TokensFromConsensusPower(initPower) + ctx, accMapper, keeper, _ := keep.CreateTestInput(t, false, initPower) + params := setInstantUnbondPeriod(keeper, ctx) + denom := params.BondDenom + + // create validator, delegate + validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1] + + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) + + // initial balance + amt1 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom) + + msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, initBond) + got = handleMsgDelegate(ctx, msgDelegate, keeper) + require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got) + + // balance should have been subtracted after delegation + amt2 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom) + require.True(sdk.IntEq(t, amt1.Sub(initBond), amt2)) + + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + validator, found := keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + require.Equal(t, initBond.MulRaw(2), validator.DelegatorShares.RoundInt()) + require.Equal(t, initBond.MulRaw(2), validator.BondedTokens()) + + // just send the same msgUnbond multiple times + // TODO use decimals here + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) + msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) + numUnbonds := int64(5) + for i := int64(0); i < numUnbonds; i++ { + + got := handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) + var finishTime time.Time + types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) + ctx = ctx.WithBlockTime(finishTime) + EndBlocker(ctx, keeper) + + // check that the accounts and the bond account have the appropriate values + validator, found = keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) + require.True(t, found) + + expBond := initBond.Sub(unbondAmt.Amount.Mul(sdk.NewInt(i + 1))) + expDelegatorShares := initBond.MulRaw(2).Sub(unbondAmt.Amount.Mul(sdk.NewInt(i + 1))) + expDelegatorAcc := initBond.Sub(expBond) + + gotBond := bond.Shares.RoundInt() + gotDelegatorShares := validator.DelegatorShares.RoundInt() + gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom) + + require.Equal(t, expBond.Int64(), gotBond.Int64(), + "i: %v\nexpBond: %v\ngotBond: %v\nvalidator: %v\nbond: %v\n", + i, expBond, gotBond, validator, bond) + require.Equal(t, expDelegatorShares.Int64(), gotDelegatorShares.Int64(), + "i: %v\nexpDelegatorShares: %v\ngotDelegatorShares: %v\nvalidator: %v\nbond: %v\n", + i, expDelegatorShares, gotDelegatorShares, validator, bond) + require.Equal(t, expDelegatorAcc.Int64(), gotDelegatorAcc.Int64(), + "i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\nvalidator: %v\nbond: %v\n", + i, expDelegatorAcc, gotDelegatorAcc, validator, bond) + } + + // these are more than we have bonded now + errorCases := []sdk.Int{ + //1<<64 - 1, // more than int64 power + //1<<63 + 1, // more than int64 power + sdk.TokensFromConsensusPower(1<<63 - 1), + sdk.TokensFromConsensusPower(1 << 31), + initBond, + } + + for i, c := range errorCases { + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, c) + msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.False(t, got.IsOK(), "expected unbond msg to fail, index: %v", i) + } + + leftBonded := initBond.Sub(unbondAmt.Amount.Mul(sdk.NewInt(numUnbonds))) + + // should be able to unbond remaining + unbondAmt = sdk.NewCoin(sdk.DefaultBondDenom, leftBonded) + msgUndelegate = NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK(), + "got: %v\nmsgUnbond: %v\nshares: %s\nleftBonded: %s\n", got.Log, msgUndelegate, unbondAmt, leftBonded) +} + +func TestMultipleMsgCreateValidator(t *testing.T) { + initPower := int64(1000) + initTokens := sdk.TokensFromConsensusPower(initPower) + ctx, accMapper, keeper, _ := keep.CreateTestInput(t, false, initPower) + params := setInstantUnbondPeriod(keeper, ctx) + + validatorAddrs := []sdk.ValAddress{ + sdk.ValAddress(keep.Addrs[0]), + sdk.ValAddress(keep.Addrs[1]), + sdk.ValAddress(keep.Addrs[2]), + } + delegatorAddrs := []sdk.AccAddress{ + keep.Addrs[0], + keep.Addrs[1], + keep.Addrs[2], + } + + // bond them all + for i, validatorAddr := range validatorAddrs { + valTokens := sdk.TokensFromConsensusPower(10) + msgCreateValidatorOnBehalfOf := NewTestMsgCreateValidator(validatorAddr, keep.PKs[i], valTokens) + + got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper) + require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) + + // verify that the account is bonded + validators := keeper.GetValidators(ctx, 100) + require.Equal(t, (i + 1), len(validators)) + + val := validators[i] + balanceExpd := initTokens.Sub(valTokens) + balanceGot := accMapper.GetAccount(ctx, delegatorAddrs[i]).GetCoins().AmountOf(params.BondDenom) + + require.Equal(t, i+1, len(validators), "expected %d validators got %d, validators: %v", i+1, len(validators), validators) + require.Equal(t, valTokens, val.DelegatorShares.RoundInt(), "expected %d shares, got %d", 10, val.DelegatorShares) + require.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot) + } + + // unbond them all by removing delegation + for i, validatorAddr := range validatorAddrs { + _, found := keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(10)) + msgUndelegate := NewMsgUndelegate(delegatorAddrs[i], validatorAddr, unbondAmt) // remove delegation + got := handleMsgUndelegate(ctx, msgUndelegate, keeper) + + require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) + var finishTime time.Time + + // Jump to finishTime for unbonding period and remove from unbonding queue + types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) + ctx = ctx.WithBlockTime(finishTime) + + EndBlocker(ctx, keeper) + + // Check that the validator is deleted from state + validators := keeper.GetValidators(ctx, 100) + require.Equal(t, len(validatorAddrs)-(i+1), len(validators), + "expected %d validators got %d", len(validatorAddrs)-(i+1), len(validators)) + + _, found = keeper.GetValidator(ctx, validatorAddr) + require.False(t, found) + + gotBalance := accMapper.GetAccount(ctx, delegatorAddrs[i]).GetCoins().AmountOf(params.BondDenom) + require.Equal(t, initTokens, gotBalance, "expected account to have %d, got %d", initTokens, gotBalance) + } +} + +func TestMultipleMsgDelegate(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + validatorAddr, delegatorAddrs := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1:] + _ = setInstantUnbondPeriod(keeper, ctx) + + // first make a validator + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected msg to be ok, got %v", got) + + // delegate multiple parties + for i, delegatorAddr := range delegatorAddrs { + msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) + got := handleMsgDelegate(ctx, msgDelegate, keeper) + require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) + + // check that the account is bonded + bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) + require.True(t, found) + require.NotNil(t, bond, "expected delegatee bond %d to exist", bond) + } + + // unbond them all + for i, delegatorAddr := range delegatorAddrs { + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) + msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) + + got := handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) + + var finishTime time.Time + types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) + + ctx = ctx.WithBlockTime(finishTime) + EndBlocker(ctx, keeper) + + // check that the account is unbonded + _, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) + require.False(t, found) + } +} + +func TestJailValidator(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1] + _ = setInstantUnbondPeriod(keeper, ctx) + + // create the validator + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + // bond a delegator + msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) + got = handleMsgDelegate(ctx, msgDelegate, keeper) + require.True(t, got.IsOK(), "expected ok, got %v", got) + + // unbond the validators bond portion + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) + msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt) + got = handleMsgUndelegate(ctx, msgUndelegateValidator, keeper) + require.True(t, got.IsOK(), "expected no error: %v", got) + + var finishTime time.Time + types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) + + ctx = ctx.WithBlockTime(finishTime) + EndBlocker(ctx, keeper) + + validator, found := keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + require.True(t, validator.Jailed, "%v", validator) + + // test that the delegator can still withdraw their bonds + msgUndelegateDelegator := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) + + got = handleMsgUndelegate(ctx, msgUndelegateDelegator, keeper) + require.True(t, got.IsOK(), "expected no error") + types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) + + ctx = ctx.WithBlockTime(finishTime) + EndBlocker(ctx, keeper) + + // verify that the pubkey can now be reused + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected ok, got %v", got) +} + +func TestValidatorQueue(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1] + + // set the unbonding time + params := keeper.GetParams(ctx) + params.UnbondingTime = 7 * time.Second + keeper.SetParams(ctx, params) + + // create the validator + valTokens := sdk.TokensFromConsensusPower(10) + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], valTokens) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + // bond a delegator + delTokens := sdk.TokensFromConsensusPower(10) + msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, delTokens) + got = handleMsgDelegate(ctx, msgDelegate, keeper) + require.True(t, got.IsOK(), "expected ok, got %v", got) + + EndBlocker(ctx, keeper) + + // unbond the all self-delegation to put validator in unbonding state + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, delTokens) + msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt) + got = handleMsgUndelegate(ctx, msgUndelegateValidator, keeper) + require.True(t, got.IsOK(), "expected no error: %v", got) + + var finishTime time.Time + types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) + + ctx = ctx.WithBlockTime(finishTime) + EndBlocker(ctx, keeper) + + origHeader := ctx.BlockHeader() + + validator, found := keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + require.True(t, validator.IsUnbonding(), "%v", validator) + + // should still be unbonding at time 6 seconds later + ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 6)) + EndBlocker(ctx, keeper) + + validator, found = keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + require.True(t, validator.IsUnbonding(), "%v", validator) + + // should be in unbonded state at time 7 seconds later + ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 7)) + EndBlocker(ctx, keeper) + + validator, found = keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + require.True(t, validator.IsUnbonded(), "%v", validator) +} + +func TestUnbondingPeriod(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + validatorAddr := sdk.ValAddress(keep.Addrs[0]) + + // set the unbonding time + params := keeper.GetParams(ctx) + params.UnbondingTime = 7 * time.Second + keeper.SetParams(ctx, params) + + // create the validator + valTokens := sdk.TokensFromConsensusPower(10) + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], valTokens) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + EndBlocker(ctx, keeper) + + // begin unbonding + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(10)) + msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt) + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK(), "expected no error") + + origHeader := ctx.BlockHeader() + + _, found := keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + require.True(t, found, "should not have unbonded") + + // cannot complete unbonding at same time + EndBlocker(ctx, keeper) + _, found = keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + require.True(t, found, "should not have unbonded") + + // cannot complete unbonding at time 6 seconds later + ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 6)) + EndBlocker(ctx, keeper) + _, found = keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + require.True(t, found, "should not have unbonded") + + // can complete unbonding at time 7 seconds later + ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 7)) + EndBlocker(ctx, keeper) + _, found = keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) + require.False(t, found, "should have unbonded") +} + +func TestUnbondingFromUnbondingValidator(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1] + + // create the validator + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + // bond a delegator + msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) + got = handleMsgDelegate(ctx, msgDelegate, keeper) + require.True(t, got.IsOK(), "expected ok, got %v", got) + + // unbond the validators bond portion + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) + msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt) + got = handleMsgUndelegate(ctx, msgUndelegateValidator, keeper) + require.True(t, got.IsOK(), "expected no error") + + // change the ctx to Block Time one second before the validator would have unbonded + var finishTime time.Time + types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime) + ctx = ctx.WithBlockTime(finishTime.Add(time.Second * -1)) + + // unbond the delegator from the validator + msgUndelegateDelegator := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) + got = handleMsgUndelegate(ctx, msgUndelegateDelegator, keeper) + require.True(t, got.IsOK(), "expected no error") + + ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(keeper.UnbondingTime(ctx))) + + // Run the EndBlocker + EndBlocker(ctx, keeper) + + // Check to make sure that the unbonding delegation is no longer in state + // (meaning it was deleted in the above EndBlocker) + _, found := keeper.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) + require.False(t, found, "should be removed from state") +} + +func TestRedelegationPeriod(t *testing.T) { + ctx, AccMapper, keeper, _ := keep.CreateTestInput(t, false, 1000) + validatorAddr, validatorAddr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]) + denom := keeper.GetParams(ctx).BondDenom + + // set the unbonding time + params := keeper.GetParams(ctx) + params.UnbondingTime = 7 * time.Second + keeper.SetParams(ctx, params) + + // create the validators + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) + + // initial balance + amt1 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins().AmountOf(denom) + + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + // balance should have been subtracted after creation + amt2 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins().AmountOf(denom) + require.Equal(t, amt1.Sub(sdk.NewInt(10)).Int64(), amt2.Int64(), "expected coins to be subtracted") + + msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], sdk.NewInt(10)) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + bal1 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins() + + // begin redelegate + redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) + msgBeginRedelegate := NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, redAmt) + got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) + require.True(t, got.IsOK(), "expected no error, %v", got) + + // origin account should not lose tokens as with a regular delegation + bal2 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins() + require.Equal(t, bal1, bal2) + + origHeader := ctx.BlockHeader() + + // cannot complete redelegation at same time + EndBlocker(ctx, keeper) + _, found := keeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) + require.True(t, found, "should not have unbonded") + + // cannot complete redelegation at time 6 seconds later + ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 6)) + EndBlocker(ctx, keeper) + _, found = keeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) + require.True(t, found, "should not have unbonded") + + // can complete redelegation at time 7 seconds later + ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 7)) + EndBlocker(ctx, keeper) + _, found = keeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) + require.False(t, found, "should have unbonded") +} + +func TestTransitiveRedelegation(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + validatorAddr := sdk.ValAddress(keep.Addrs[0]) + validatorAddr2 := sdk.ValAddress(keep.Addrs[1]) + validatorAddr3 := sdk.ValAddress(keep.Addrs[2]) + + // set the unbonding time + params := keeper.GetParams(ctx) + params.UnbondingTime = 0 + keeper.SetParams(ctx, params) + + // create the validators + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], sdk.NewInt(10)) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], sdk.NewInt(10)) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + // begin redelegate + redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) + msgBeginRedelegate := NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, redAmt) + got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) + require.True(t, got.IsOK(), "expected no error, %v", got) + + // cannot redelegation to next validator while first delegation exists + msgBeginRedelegate = NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr2, validatorAddr3, redAmt) + got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) + require.True(t, !got.IsOK(), "expected an error, msg: %v", msgBeginRedelegate) + + // complete first redelegation + EndBlocker(ctx, keeper) + + // now should be able to redelegate from the second validator to the third + got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) + require.True(t, got.IsOK(), "expected no error") +} + +func TestMultipleRedelegationAtSameTime(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + valAddr := sdk.ValAddress(keep.Addrs[0]) + valAddr2 := sdk.ValAddress(keep.Addrs[1]) + + // set the unbonding time + params := keeper.GetParams(ctx) + params.UnbondingTime = 1 * time.Second + keeper.SetParams(ctx, params) + + // create the validators + valTokens := sdk.TokensFromConsensusPower(10) + msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], valTokens) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + msgCreateValidator = NewTestMsgCreateValidator(valAddr2, keep.PKs[1], valTokens) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + // end block to bond them + EndBlocker(ctx, keeper) + + // begin a redelegate + selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) + redAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2)) + msgBeginRedelegate := NewMsgBeginRedelegate(selfDelAddr, valAddr, valAddr2, redAmt) + got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) + require.True(t, got.IsOK(), "expected no error, %v", got) + + // there should only be one entry in the redelegation object + rd, found := keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) + require.True(t, found) + require.Len(t, rd.Entries, 1) + + // start a second redelegation at this same time as the first + got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) + require.True(t, got.IsOK(), "expected no error, msg: %v", msgBeginRedelegate) + + // now there should be two entries + rd, found = keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) + require.True(t, found) + require.Len(t, rd.Entries, 2) + + // move forward in time, should complete both redelegations + ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(1 * time.Second)) + EndBlocker(ctx, keeper) + + rd, found = keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) + require.False(t, found) +} + +func TestMultipleRedelegationAtUniqueTimes(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + valAddr := sdk.ValAddress(keep.Addrs[0]) + valAddr2 := sdk.ValAddress(keep.Addrs[1]) + + // set the unbonding time + params := keeper.GetParams(ctx) + params.UnbondingTime = 10 * time.Second + keeper.SetParams(ctx, params) + + // create the validators + valTokens := sdk.TokensFromConsensusPower(10) + msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], valTokens) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + msgCreateValidator = NewTestMsgCreateValidator(valAddr2, keep.PKs[1], valTokens) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + // end block to bond them + EndBlocker(ctx, keeper) + + // begin a redelegate + selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) + redAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2)) + msgBeginRedelegate := NewMsgBeginRedelegate(selfDelAddr, valAddr, valAddr2, redAmt) + got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) + require.True(t, got.IsOK(), "expected no error, %v", got) + + // move forward in time and start a second redelegation + ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) + got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) + require.True(t, got.IsOK(), "expected no error, msg: %v", msgBeginRedelegate) + + // now there should be two entries + rd, found := keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) + require.True(t, found) + require.Len(t, rd.Entries, 2) + + // move forward in time, should complete the first redelegation, but not the second + ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) + EndBlocker(ctx, keeper) + rd, found = keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) + require.True(t, found) + require.Len(t, rd.Entries, 1) + + // move forward in time, should complete the second redelegation + ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) + EndBlocker(ctx, keeper) + rd, found = keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) + require.False(t, found) +} + +func TestMultipleUnbondingDelegationAtSameTime(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + valAddr := sdk.ValAddress(keep.Addrs[0]) + + // set the unbonding time + params := keeper.GetParams(ctx) + params.UnbondingTime = 1 * time.Second + keeper.SetParams(ctx, params) + + // create the validator + valTokens := sdk.TokensFromConsensusPower(10) + msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], valTokens) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + // end block to bond + EndBlocker(ctx, keeper) + + // begin an unbonding delegation + selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2)) + msgUndelegate := NewMsgUndelegate(selfDelAddr, valAddr, unbondAmt) + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK(), "expected no error, %v", got) + + // there should only be one entry in the ubd object + ubd, found := keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + + // start a second ubd at this same time as the first + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK(), "expected no error, msg: %v", msgUndelegate) + + // now there should be two entries + ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + require.True(t, found) + require.Len(t, ubd.Entries, 2) + + // move forwaubd in time, should complete both ubds + ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(1 * time.Second)) + EndBlocker(ctx, keeper) + + ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + require.False(t, found) +} + +func TestMultipleUnbondingDelegationAtUniqueTimes(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + valAddr := sdk.ValAddress(keep.Addrs[0]) + + // set the unbonding time + params := keeper.GetParams(ctx) + params.UnbondingTime = 10 * time.Second + keeper.SetParams(ctx, params) + + // create the validator + valTokens := sdk.TokensFromConsensusPower(10) + msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], valTokens) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + // end block to bond + EndBlocker(ctx, keeper) + + // begin an unbonding delegation + selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2)) + msgUndelegate := NewMsgUndelegate(selfDelAddr, valAddr, unbondAmt) + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK(), "expected no error, %v", got) + + // there should only be one entry in the ubd object + ubd, found := keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + + // move forwaubd in time and start a second redelegation + ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK(), "expected no error, msg: %v", msgUndelegate) + + // now there should be two entries + ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + require.True(t, found) + require.Len(t, ubd.Entries, 2) + + // move forwaubd in time, should complete the first redelegation, but not the second + ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) + EndBlocker(ctx, keeper) + ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + + // move forwaubd in time, should complete the second redelegation + ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) + EndBlocker(ctx, keeper) + ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) + require.False(t, found) +} + +func TestUnbondingWhenExcessValidators(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + validatorAddr1 := sdk.ValAddress(keep.Addrs[0]) + validatorAddr2 := sdk.ValAddress(keep.Addrs[1]) + validatorAddr3 := sdk.ValAddress(keep.Addrs[2]) + + // set the unbonding time + params := keeper.GetParams(ctx) + params.UnbondingTime = 0 + params.MaxValidators = 2 + keeper.SetParams(ctx, params) + + // add three validators + valTokens1 := sdk.TokensFromConsensusPower(50) + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr1, keep.PKs[0], valTokens1) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(keeper.GetLastValidators(ctx))) + + valTokens2 := sdk.TokensFromConsensusPower(30) + msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], valTokens2) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(keeper.GetLastValidators(ctx))) + + valTokens3 := sdk.TokensFromConsensusPower(10) + msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], valTokens3) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(keeper.GetLastValidators(ctx))) + + // unbond the validator-2 + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens2) + msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr2), validatorAddr2, unbondAmt) + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgUndelegate") + + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + // because there are extra validators waiting to get in, the queued + // validator (aka. validator-1) should make it into the bonded group, thus + // the total number of validators should stay the same + vals := keeper.GetLastValidators(ctx) + require.Equal(t, 2, len(vals), "vals %v", vals) + val1, found := keeper.GetValidator(ctx, validatorAddr1) + require.True(t, found) + require.Equal(t, sdk.Bonded, val1.Status, "%v", val1) +} + +func TestBondUnbondRedelegateSlashTwice(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + valA, valB, del := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2] + consAddr0 := sdk.ConsAddress(keep.PKs[0].Address()) + + valTokens := sdk.TokensFromConsensusPower(10) + msgCreateValidator := NewTestMsgCreateValidator(valA, keep.PKs[0], valTokens) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + msgCreateValidator = NewTestMsgCreateValidator(valB, keep.PKs[1], valTokens) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") + + // delegate 10 stake + msgDelegate := NewTestMsgDelegate(del, valA, valTokens) + got = handleMsgDelegate(ctx, msgDelegate, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgDelegate") + + // apply Tendermint updates + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(updates)) + + // a block passes + ctx = ctx.WithBlockHeight(1) + + // begin unbonding 4 stake + unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(4)) + msgUndelegate := NewMsgUndelegate(del, valA, unbondAmt) + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgUndelegate") + + // begin redelegate 6 stake + redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(6)) + msgBeginRedelegate := NewMsgBeginRedelegate(del, valA, valB, redAmt) + got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) + require.True(t, got.IsOK(), "expected no error on runMsgBeginRedelegate") + + // destination delegation should have 6 shares + delegation, found := keeper.GetDelegation(ctx, del, valB) + require.True(t, found) + require.Equal(t, sdk.NewDecFromInt(redAmt.Amount), delegation.Shares) + + // must apply validator updates + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(updates)) + + // slash the validator by half + keeper.Slash(ctx, consAddr0, 0, 20, sdk.NewDecWithPrec(5, 1)) + + // unbonding delegation should have been slashed by half + ubd, found := keeper.GetUnbondingDelegation(ctx, del, valA) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + require.Equal(t, unbondAmt.Amount.QuoRaw(2), ubd.Entries[0].Balance) + + // redelegation should have been slashed by half + redelegation, found := keeper.GetRedelegation(ctx, del, valA, valB) + require.True(t, found) + require.Len(t, redelegation.Entries, 1) + + // destination delegation should have been slashed by half + delegation, found = keeper.GetDelegation(ctx, del, valB) + require.True(t, found) + require.Equal(t, sdk.NewDecFromInt(redAmt.Amount.QuoRaw(2)), delegation.Shares) + + // validator power should have been reduced by half + validator, found := keeper.GetValidator(ctx, valA) + require.True(t, found) + require.Equal(t, valTokens.QuoRaw(2), validator.GetBondedTokens()) + + // slash the validator for an infraction committed after the unbonding and redelegation begin + ctx = ctx.WithBlockHeight(3) + keeper.Slash(ctx, consAddr0, 2, 10, sdk.NewDecWithPrec(5, 1)) + + // unbonding delegation should be unchanged + ubd, found = keeper.GetUnbondingDelegation(ctx, del, valA) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + require.Equal(t, unbondAmt.Amount.QuoRaw(2), ubd.Entries[0].Balance) + + // redelegation should be unchanged + redelegation, found = keeper.GetRedelegation(ctx, del, valA, valB) + require.True(t, found) + require.Len(t, redelegation.Entries, 1) + + // destination delegation should be unchanged + delegation, found = keeper.GetDelegation(ctx, del, valB) + require.True(t, found) + require.Equal(t, sdk.NewDecFromInt(redAmt.Amount.QuoRaw(2)), delegation.Shares) + + // end blocker + EndBlocker(ctx, keeper) + + // validator power should have been reduced to zero + // validator should be in unbonding state + validator, _ = keeper.GetValidator(ctx, valA) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) +} + +func TestInvalidMsg(t *testing.T) { + k := keep.Keeper{} + h := NewHandler(k) + + res := h(sdk.NewContext(nil, abci.Header{}, false, nil), sdk.NewTestMsg()) + require.False(t, res.IsOK()) + require.True(t, strings.Contains(res.Log, "unrecognized staking message type")) +} + +func TestInvalidCoinDenom(t *testing.T) { + ctx, _, keeper, _ := keep.CreateTestInput(t, false, 1000) + valA, valB, delAddr := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2] + + valTokens := sdk.TokensFromConsensusPower(100) + invalidCoin := sdk.NewCoin("churros", valTokens) + validCoin := sdk.NewCoin(sdk.DefaultBondDenom, valTokens) + oneCoin := sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt()) + + commission := types.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.ZeroDec()) + + msgCreate := types.NewMsgCreateValidator(valA, keep.PKs[0], invalidCoin, Description{}, commission, sdk.OneInt()) + got := handleMsgCreateValidator(ctx, msgCreate, keeper) + require.False(t, got.IsOK()) + msgCreate = types.NewMsgCreateValidator(valA, keep.PKs[0], validCoin, Description{}, commission, sdk.OneInt()) + got = handleMsgCreateValidator(ctx, msgCreate, keeper) + require.True(t, got.IsOK()) + msgCreate = types.NewMsgCreateValidator(valB, keep.PKs[1], validCoin, Description{}, commission, sdk.OneInt()) + got = handleMsgCreateValidator(ctx, msgCreate, keeper) + require.True(t, got.IsOK()) + + msgDelegate := types.NewMsgDelegate(delAddr, valA, invalidCoin) + got = handleMsgDelegate(ctx, msgDelegate, keeper) + require.False(t, got.IsOK()) + msgDelegate = types.NewMsgDelegate(delAddr, valA, validCoin) + got = handleMsgDelegate(ctx, msgDelegate, keeper) + require.True(t, got.IsOK()) + + msgUndelegate := types.NewMsgUndelegate(delAddr, valA, invalidCoin) + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.False(t, got.IsOK()) + msgUndelegate = types.NewMsgUndelegate(delAddr, valA, oneCoin) + got = handleMsgUndelegate(ctx, msgUndelegate, keeper) + require.True(t, got.IsOK()) + + msgRedelegate := types.NewMsgBeginRedelegate(delAddr, valA, valB, invalidCoin) + got = handleMsgBeginRedelegate(ctx, msgRedelegate, keeper) + require.False(t, got.IsOK()) + msgRedelegate = types.NewMsgBeginRedelegate(delAddr, valA, valB, oneCoin) + got = handleMsgBeginRedelegate(ctx, msgRedelegate, keeper) + require.True(t, got.IsOK()) +} diff --git a/x/staking/keeper/alias_functions.go b/x/staking/keeper/alias_functions.go new file mode 100644 index 0000000..ca5696b --- /dev/null +++ b/x/staking/keeper/alias_functions.go @@ -0,0 +1,139 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/exported" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +//_______________________________________________________________________ +// Validator Set + +// iterate through the validator set and perform the provided function +func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validator exported.ValidatorI) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ValidatorsKey) + defer iterator.Close() + i := int64(0) + for ; iterator.Valid(); iterator.Next() { + validator := types.MustUnmarshalValidator(k.cdc, iterator.Value()) + stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to? + if stop { + break + } + i++ + } +} + +// iterate through the bonded validator set and perform the provided function +func (k Keeper) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, validator exported.ValidatorI) (stop bool)) { + store := ctx.KVStore(k.storeKey) + maxValidators := k.MaxValidators(ctx) + + iterator := sdk.KVStoreReversePrefixIterator(store, types.ValidatorsByPowerIndexKey) + defer iterator.Close() + + i := int64(0) + for ; iterator.Valid() && i < int64(maxValidators); iterator.Next() { + address := iterator.Value() + validator := k.mustGetValidator(ctx, address) + + if validator.IsBonded() { + stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to? + if stop { + break + } + i++ + } + } +} + +// iterate through the active validator set and perform the provided function +func (k Keeper) IterateLastValidators(ctx sdk.Context, fn func(index int64, validator exported.ValidatorI) (stop bool)) { + iterator := k.LastValidatorsIterator(ctx) + defer iterator.Close() + i := int64(0) + for ; iterator.Valid(); iterator.Next() { + address := types.AddressFromLastValidatorPowerKey(iterator.Key()) + validator, found := k.GetValidator(ctx, address) + if !found { + panic(fmt.Sprintf("validator record not found for address: %v\n", address)) + } + + stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to? + if stop { + break + } + i++ + } +} + +// Validator gets the Validator interface for a particular address +func (k Keeper) Validator(ctx sdk.Context, address sdk.ValAddress) exported.ValidatorI { + val, found := k.GetValidator(ctx, address) + if !found { + return nil + } + return val +} + +// ValidatorByConsAddr gets the validator interface for a particular pubkey +func (k Keeper) ValidatorByConsAddr(ctx sdk.Context, addr sdk.ConsAddress) exported.ValidatorI { + val, found := k.GetValidatorByConsAddr(ctx, addr) + if !found { + return nil + } + return val +} + +//_______________________________________________________________________ +// Delegation Set + +// Returns self as it is both a validatorset and delegationset +func (k Keeper) GetValidatorSet() types.ValidatorSet { + return k +} + +// Delegation get the delegation interface for a particular set of delegator and validator addresses +func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.AccAddress, addrVal sdk.ValAddress) exported.DelegationI { + bond, ok := k.GetDelegation(ctx, addrDel, addrVal) + if !ok { + return nil + } + + return bond +} + +// iterate through all of the delegations from a delegator +func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.AccAddress, + fn func(index int64, del exported.DelegationI) (stop bool)) { + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := types.GetDelegationsKey(delAddr) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) // smallest to largest + defer iterator.Close() + for i := int64(0); iterator.Valid(); iterator.Next() { + del := types.MustUnmarshalDelegation(k.cdc, iterator.Value()) + stop := fn(i, del) + if stop { + break + } + i++ + } +} + +// return all delegations used during genesis dump +// TODO: remove this func, change all usage for iterate functionality +func (k Keeper) GetAllSDKDelegations(ctx sdk.Context) (delegations []types.Delegation) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.DelegationKey) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value()) + delegations = append(delegations, delegation) + } + return delegations +} diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go new file mode 100644 index 0000000..2c0d8e5 --- /dev/null +++ b/x/staking/keeper/delegation.go @@ -0,0 +1,826 @@ +package keeper + +import ( + "bytes" + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// return a specific delegation +func (k Keeper) GetDelegation(ctx sdk.Context, + delAddr sdk.AccAddress, valAddr sdk.ValAddress) ( + delegation types.Delegation, found bool) { + + store := ctx.KVStore(k.storeKey) + key := types.GetDelegationKey(delAddr, valAddr) + value := store.Get(key) + if value == nil { + return delegation, false + } + + delegation = types.MustUnmarshalDelegation(k.cdc, value) + return delegation, true +} + +// IterateAllDelegations iterate through all of the delegations +func (k Keeper) IterateAllDelegations(ctx sdk.Context, cb func(delegation types.Delegation) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.DelegationKey) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value()) + if cb(delegation) { + break + } + } +} + +// GetAllDelegations returns all delegations used during genesis dump +func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegation) { + k.IterateAllDelegations(ctx, func(delegation types.Delegation) bool { + delegations = append(delegations, delegation) + return false + }) + return delegations +} + +// return all delegations to a specific validator. Useful for querier. +func (k Keeper) GetValidatorDelegations(ctx sdk.Context, valAddr sdk.ValAddress) (delegations []types.Delegation) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.DelegationKey) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value()) + if delegation.GetValidatorAddr().Equals(valAddr) { + delegations = append(delegations, delegation) + } + } + return delegations +} + +// return a given amount of all the delegations from a delegator +func (k Keeper) GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress, + maxRetrieve uint16) (delegations []types.Delegation) { + + delegations = make([]types.Delegation, maxRetrieve) + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := types.GetDelegationsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) + defer iterator.Close() + + i := 0 + for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() { + delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value()) + delegations[i] = delegation + i++ + } + return delegations[:i] // trim if the array length < maxRetrieve +} + +// set a delegation +func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) { + store := ctx.KVStore(k.storeKey) + b := types.MustMarshalDelegation(k.cdc, delegation) + store.Set(types.GetDelegationKey(delegation.DelegatorAddress, delegation.ValidatorAddress), b) +} + +// remove a delegation +func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) { + // TODO: Consider calling hooks outside of the store wrapper functions, it's unobvious. + k.BeforeDelegationRemoved(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + store := ctx.KVStore(k.storeKey) + store.Delete(types.GetDelegationKey(delegation.DelegatorAddress, delegation.ValidatorAddress)) +} + +// return a given amount of all the delegator unbonding-delegations +func (k Keeper) GetUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress, + maxRetrieve uint16) (unbondingDelegations []types.UnbondingDelegation) { + + unbondingDelegations = make([]types.UnbondingDelegation, maxRetrieve) + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := types.GetUBDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) + defer iterator.Close() + + i := 0 + for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() { + unbondingDelegation := types.MustUnmarshalUBD(k.cdc, iterator.Value()) + unbondingDelegations[i] = unbondingDelegation + i++ + } + return unbondingDelegations[:i] // trim if the array length < maxRetrieve +} + +// return a unbonding delegation +func (k Keeper) GetUnbondingDelegation(ctx sdk.Context, + delAddr sdk.AccAddress, valAddr sdk.ValAddress) (ubd types.UnbondingDelegation, found bool) { + + store := ctx.KVStore(k.storeKey) + key := types.GetUBDKey(delAddr, valAddr) + value := store.Get(key) + if value == nil { + return ubd, false + } + + ubd = types.MustUnmarshalUBD(k.cdc, value) + return ubd, true +} + +// return all unbonding delegations from a particular validator +func (k Keeper) GetUnbondingDelegationsFromValidator(ctx sdk.Context, valAddr sdk.ValAddress) (ubds []types.UnbondingDelegation) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.GetUBDsByValIndexKey(valAddr)) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + key := types.GetUBDKeyFromValIndexKey(iterator.Key()) + value := store.Get(key) + ubd := types.MustUnmarshalUBD(k.cdc, value) + ubds = append(ubds, ubd) + } + return ubds +} + +// iterate through all of the unbonding delegations +func (k Keeper) IterateUnbondingDelegations(ctx sdk.Context, fn func(index int64, ubd types.UnbondingDelegation) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.UnbondingDelegationKey) + defer iterator.Close() + + for i := int64(0); iterator.Valid(); iterator.Next() { + ubd := types.MustUnmarshalUBD(k.cdc, iterator.Value()) + if stop := fn(i, ubd); stop { + break + } + i++ + } +} + +// HasMaxUnbondingDelegationEntries - check if unbonding delegation has maximum number of entries +func (k Keeper) HasMaxUnbondingDelegationEntries(ctx sdk.Context, + delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) bool { + + ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) + if !found { + return false + } + return len(ubd.Entries) >= int(k.MaxEntries(ctx)) +} + +// set the unbonding delegation and associated index +func (k Keeper) SetUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) { + store := ctx.KVStore(k.storeKey) + bz := types.MustMarshalUBD(k.cdc, ubd) + key := types.GetUBDKey(ubd.DelegatorAddress, ubd.ValidatorAddress) + store.Set(key, bz) + store.Set(types.GetUBDByValIndexKey(ubd.DelegatorAddress, ubd.ValidatorAddress), []byte{}) // index, store empty bytes +} + +// remove the unbonding delegation object and associated index +func (k Keeper) RemoveUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) { + store := ctx.KVStore(k.storeKey) + key := types.GetUBDKey(ubd.DelegatorAddress, ubd.ValidatorAddress) + store.Delete(key) + store.Delete(types.GetUBDByValIndexKey(ubd.DelegatorAddress, ubd.ValidatorAddress)) +} + +// SetUnbondingDelegationEntry adds an entry to the unbonding delegation at +// the given addresses. It creates the unbonding delegation if it does not exist +func (k Keeper) SetUnbondingDelegationEntry(ctx sdk.Context, + delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress, + creationHeight int64, minTime time.Time, balance sdk.Int) types.UnbondingDelegation { + + ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) + if found { + ubd.AddEntry(creationHeight, minTime, balance) + } else { + ubd = types.NewUnbondingDelegation(delegatorAddr, validatorAddr, creationHeight, minTime, balance) + } + k.SetUnbondingDelegation(ctx, ubd) + return ubd +} + +// unbonding delegation queue timeslice operations + +// gets a specific unbonding queue timeslice. A timeslice is a slice of DVPairs +// corresponding to unbonding delegations that expire at a certain time. +func (k Keeper) GetUBDQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (dvPairs []types.DVPair) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.GetUnbondingDelegationTimeKey(timestamp)) + if bz == nil { + return []types.DVPair{} + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &dvPairs) + return dvPairs +} + +// Sets a specific unbonding queue timeslice. +func (k Keeper) SetUBDQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVPair) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) + store.Set(types.GetUnbondingDelegationTimeKey(timestamp), bz) +} + +// Insert an unbonding delegation to the appropriate timeslice in the unbonding queue +func (k Keeper) InsertUBDQueue(ctx sdk.Context, ubd types.UnbondingDelegation, + completionTime time.Time) { + + timeSlice := k.GetUBDQueueTimeSlice(ctx, completionTime) + dvPair := types.DVPair{DelegatorAddress: ubd.DelegatorAddress, ValidatorAddress: ubd.ValidatorAddress} + if len(timeSlice) == 0 { + k.SetUBDQueueTimeSlice(ctx, completionTime, []types.DVPair{dvPair}) + } else { + timeSlice = append(timeSlice, dvPair) + k.SetUBDQueueTimeSlice(ctx, completionTime, timeSlice) + } +} + +// Returns all the unbonding queue timeslices from time 0 until endTime +func (k Keeper) UBDQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator { + store := ctx.KVStore(k.storeKey) + return store.Iterator(types.UnbondingQueueKey, + sdk.InclusiveEndBytes(types.GetUnbondingDelegationTimeKey(endTime))) +} + +// Returns a concatenated list of all the timeslices inclusively previous to +// currTime, and deletes the timeslices from the queue +func (k Keeper) DequeueAllMatureUBDQueue(ctx sdk.Context, + currTime time.Time) (matureUnbonds []types.DVPair) { + + store := ctx.KVStore(k.storeKey) + // gets an iterator for all timeslices from time 0 until the current Blockheader time + unbondingTimesliceIterator := k.UBDQueueIterator(ctx, ctx.BlockHeader().Time) + for ; unbondingTimesliceIterator.Valid(); unbondingTimesliceIterator.Next() { + timeslice := []types.DVPair{} + value := unbondingTimesliceIterator.Value() + k.cdc.MustUnmarshalBinaryLengthPrefixed(value, ×lice) + matureUnbonds = append(matureUnbonds, timeslice...) + store.Delete(unbondingTimesliceIterator.Key()) + } + return matureUnbonds +} + +// return a given amount of all the delegator redelegations +func (k Keeper) GetRedelegations(ctx sdk.Context, delegator sdk.AccAddress, + maxRetrieve uint16) (redelegations []types.Redelegation) { + redelegations = make([]types.Redelegation, maxRetrieve) + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := types.GetREDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) + defer iterator.Close() + + i := 0 + for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() { + redelegation := types.MustUnmarshalRED(k.cdc, iterator.Value()) + redelegations[i] = redelegation + i++ + } + return redelegations[:i] // trim if the array length < maxRetrieve +} + +// return a redelegation +func (k Keeper) GetRedelegation(ctx sdk.Context, + delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) (red types.Redelegation, found bool) { + + store := ctx.KVStore(k.storeKey) + key := types.GetREDKey(delAddr, valSrcAddr, valDstAddr) + value := store.Get(key) + if value == nil { + return red, false + } + + red = types.MustUnmarshalRED(k.cdc, value) + return red, true +} + +// return all redelegations from a particular validator +func (k Keeper) GetRedelegationsFromSrcValidator(ctx sdk.Context, valAddr sdk.ValAddress) (reds []types.Redelegation) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.GetREDsFromValSrcIndexKey(valAddr)) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + key := types.GetREDKeyFromValSrcIndexKey(iterator.Key()) + value := store.Get(key) + red := types.MustUnmarshalRED(k.cdc, value) + reds = append(reds, red) + } + return reds +} + +// check if validator is receiving a redelegation +func (k Keeper) HasReceivingRedelegation(ctx sdk.Context, + delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) bool { + + store := ctx.KVStore(k.storeKey) + prefix := types.GetREDsByDelToValDstIndexKey(delAddr, valDstAddr) + iterator := sdk.KVStorePrefixIterator(store, prefix) + defer iterator.Close() + + return iterator.Valid() +} + +// HasMaxRedelegationEntries - redelegation has maximum number of entries +func (k Keeper) HasMaxRedelegationEntries(ctx sdk.Context, + delegatorAddr sdk.AccAddress, validatorSrcAddr, + validatorDstAddr sdk.ValAddress) bool { + + red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr) + if !found { + return false + } + return len(red.Entries) >= int(k.MaxEntries(ctx)) +} + +// set a redelegation and associated index +func (k Keeper) SetRedelegation(ctx sdk.Context, red types.Redelegation) { + store := ctx.KVStore(k.storeKey) + bz := types.MustMarshalRED(k.cdc, red) + key := types.GetREDKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress) + store.Set(key, bz) + store.Set(types.GetREDByValSrcIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress), []byte{}) + store.Set(types.GetREDByValDstIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress), []byte{}) +} + +// SetUnbondingDelegationEntry adds an entry to the unbonding delegation at +// the given addresses. It creates the unbonding delegation if it does not exist +func (k Keeper) SetRedelegationEntry(ctx sdk.Context, + delegatorAddr sdk.AccAddress, validatorSrcAddr, + validatorDstAddr sdk.ValAddress, creationHeight int64, + minTime time.Time, balance sdk.Int, + sharesSrc, sharesDst sdk.Dec) types.Redelegation { + + red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr) + if found { + red.AddEntry(creationHeight, minTime, balance, sharesDst) + } else { + red = types.NewRedelegation(delegatorAddr, validatorSrcAddr, + validatorDstAddr, creationHeight, minTime, balance, sharesDst) + } + k.SetRedelegation(ctx, red) + return red +} + +// iterate through all redelegations +func (k Keeper) IterateRedelegations(ctx sdk.Context, fn func(index int64, red types.Redelegation) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.RedelegationKey) + defer iterator.Close() + + for i := int64(0); iterator.Valid(); iterator.Next() { + red := types.MustUnmarshalRED(k.cdc, iterator.Value()) + if stop := fn(i, red); stop { + break + } + i++ + } +} + +// remove a redelegation object and associated index +func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) { + store := ctx.KVStore(k.storeKey) + redKey := types.GetREDKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress) + store.Delete(redKey) + store.Delete(types.GetREDByValSrcIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress)) + store.Delete(types.GetREDByValDstIndexKey(red.DelegatorAddress, red.ValidatorSrcAddress, red.ValidatorDstAddress)) +} + +// redelegation queue timeslice operations + +// Gets a specific redelegation queue timeslice. A timeslice is a slice of DVVTriplets corresponding to redelegations +// that expire at a certain time. +func (k Keeper) GetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (dvvTriplets []types.DVVTriplet) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.GetRedelegationTimeKey(timestamp)) + if bz == nil { + return []types.DVVTriplet{} + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &dvvTriplets) + return dvvTriplets +} + +// Sets a specific redelegation queue timeslice. +func (k Keeper) SetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVVTriplet) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) + store.Set(types.GetRedelegationTimeKey(timestamp), bz) +} + +// Insert an redelegation delegation to the appropriate timeslice in the redelegation queue +func (k Keeper) InsertRedelegationQueue(ctx sdk.Context, red types.Redelegation, + completionTime time.Time) { + + timeSlice := k.GetRedelegationQueueTimeSlice(ctx, completionTime) + dvvTriplet := types.DVVTriplet{ + DelegatorAddress: red.DelegatorAddress, + ValidatorSrcAddress: red.ValidatorSrcAddress, + ValidatorDstAddress: red.ValidatorDstAddress} + + if len(timeSlice) == 0 { + k.SetRedelegationQueueTimeSlice(ctx, completionTime, []types.DVVTriplet{dvvTriplet}) + } else { + timeSlice = append(timeSlice, dvvTriplet) + k.SetRedelegationQueueTimeSlice(ctx, completionTime, timeSlice) + } +} + +// Returns all the redelegation queue timeslices from time 0 until endTime +func (k Keeper) RedelegationQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator { + store := ctx.KVStore(k.storeKey) + return store.Iterator(types.RedelegationQueueKey, sdk.InclusiveEndBytes(types.GetRedelegationTimeKey(endTime))) +} + +// Returns a concatenated list of all the timeslices inclusively previous to +// currTime, and deletes the timeslices from the queue +func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time.Time) (matureRedelegations []types.DVVTriplet) { + store := ctx.KVStore(k.storeKey) + // gets an iterator for all timeslices from time 0 until the current Blockheader time + redelegationTimesliceIterator := k.RedelegationQueueIterator(ctx, ctx.BlockHeader().Time) + for ; redelegationTimesliceIterator.Valid(); redelegationTimesliceIterator.Next() { + timeslice := []types.DVVTriplet{} + value := redelegationTimesliceIterator.Value() + k.cdc.MustUnmarshalBinaryLengthPrefixed(value, ×lice) + matureRedelegations = append(matureRedelegations, timeslice...) + store.Delete(redelegationTimesliceIterator.Key()) + } + return matureRedelegations +} + +// Perform a delegation, set/update everything necessary within the store. +// tokenSrc indicates the bond status of the incoming funds. +func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Int, tokenSrc sdk.BondStatus, + validator types.Validator, subtractAccount bool) (newShares sdk.Dec, err sdk.Error) { + + // In some situations, the exchange rate becomes invalid, e.g. if + // Validator loses all tokens due to slashing. In this case, + // make all future delegations invalid. + if validator.InvalidExRate() { + return sdk.ZeroDec(), types.ErrDelegatorShareExRateInvalid(k.Codespace()) + } + + // Get or create the delegation object + delegation, found := k.GetDelegation(ctx, delAddr, validator.OperatorAddress) + if !found { + delegation = types.NewDelegation(delAddr, validator.OperatorAddress, sdk.ZeroDec()) + } + + // call the appropriate hook if present + if found { + k.BeforeDelegationSharesModified(ctx, delAddr, validator.OperatorAddress) + } else { + k.BeforeDelegationCreated(ctx, delAddr, validator.OperatorAddress) + } + + // if subtractAccount is true then we are + // performing a delegation and not a redelegation, thus the source tokens are + // all non bonded + if subtractAccount { + if tokenSrc == sdk.Bonded { + panic("delegation token source cannot be bonded") + } + + var sendName string + switch { + case validator.IsBonded(): + sendName = types.BondedPoolName + case validator.IsUnbonding(), validator.IsUnbonded(): + sendName = types.NotBondedPoolName + default: + panic("invalid validator status") + } + + coins := sdk.NewCoins(sdk.NewCoin(k.BondDenom(ctx), bondAmt)) + err := k.supplyKeeper.DelegateCoinsFromAccountToModule(ctx, delegation.DelegatorAddress, sendName, coins) + if err != nil { + return sdk.Dec{}, err + } + } else { + + // potentially transfer tokens between pools, if + switch { + case tokenSrc == sdk.Bonded && validator.IsBonded(): + // do nothing + case (tokenSrc == sdk.Unbonded || tokenSrc == sdk.Unbonding) && !validator.IsBonded(): + // do nothing + case (tokenSrc == sdk.Unbonded || tokenSrc == sdk.Unbonding) && validator.IsBonded(): + // transfer pools + k.notBondedTokensToBonded(ctx, bondAmt) + case tokenSrc == sdk.Bonded && !validator.IsBonded(): + // transfer pools + k.bondedTokensToNotBonded(ctx, bondAmt) + default: + panic("unknown token source bond status") + } + } + + validator, newShares = k.AddValidatorTokensAndShares(ctx, validator, bondAmt) + + // Update delegation + delegation.Shares = delegation.Shares.Add(newShares) + k.SetDelegation(ctx, delegation) + + // Call the after-modification hook + k.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + + return newShares, nil +} + +// unbond a particular delegation and perform associated store operations +func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, + shares sdk.Dec) (amount sdk.Int, err sdk.Error) { + + // check if a delegation object exists in the store + delegation, found := k.GetDelegation(ctx, delAddr, valAddr) + if !found { + return amount, types.ErrNoDelegatorForAddress(k.Codespace()) + } + + // call the before-delegation-modified hook + k.BeforeDelegationSharesModified(ctx, delAddr, valAddr) + + // ensure that we have enough shares to remove + if delegation.Shares.LT(shares) { + return amount, types.ErrNotEnoughDelegationShares(k.Codespace(), delegation.Shares.String()) + } + + // get validator + validator, found := k.GetValidator(ctx, valAddr) + if !found { + return amount, types.ErrNoValidatorFound(k.Codespace()) + } + + // subtract shares from delegation + delegation.Shares = delegation.Shares.Sub(shares) + + isValidatorOperator := delegation.DelegatorAddress.Equals(validator.OperatorAddress) + + // if the delegation is the operator of the validator and undelegating will decrease the validator's self delegation below their minimum + // trigger a jail validator + if isValidatorOperator && !validator.Jailed && + validator.TokensFromShares(delegation.Shares).TruncateInt().LT(validator.MinSelfDelegation) { + + k.jailValidator(ctx, validator) + validator = k.mustGetValidator(ctx, validator.OperatorAddress) + } + + // remove the delegation + if delegation.Shares.IsZero() { + k.RemoveDelegation(ctx, delegation) + } else { + k.SetDelegation(ctx, delegation) + // call the after delegation modification hook + k.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + } + + // remove the shares and coins from the validator + // NOTE that the amount is later (in keeper.Delegation) moved between staking module pools + validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares) + + if validator.DelegatorShares.IsZero() && validator.IsUnbonded() { + // if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period + k.RemoveValidator(ctx, validator.OperatorAddress) + } + + return amount, nil +} + +// getBeginInfo returns the completion time and height of a redelegation, along +// with a boolean signaling if the redelegation is complete based on the source +// validator. +func (k Keeper) getBeginInfo( + ctx sdk.Context, valSrcAddr sdk.ValAddress, +) (completionTime time.Time, height int64, completeNow bool) { + + validator, found := k.GetValidator(ctx, valSrcAddr) + + // TODO: When would the validator not be found? + switch { + case !found || validator.IsBonded(): + + // the longest wait - just unbonding period from now + completionTime = ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx)) + height = ctx.BlockHeight() + return completionTime, height, false + + case validator.IsUnbonded(): + return completionTime, height, true + + case validator.IsUnbonding(): + return validator.UnbondingCompletionTime, validator.UnbondingHeight, false + + default: + panic(fmt.Sprintf("unknown validator status: %s", validator.Status)) + } +} + +// Undelegate unbonds an amount of delegator shares from a given validator. It +// will verify that the unbonding entries between the delegator and validator +// are not exceeded and unbond the staked tokens (based on shares) by creating +// an unbonding object and inserting it into the unbonding queue which will be +// processed during the staking EndBlocker. +func (k Keeper) Undelegate( + ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec, +) (time.Time, sdk.Error) { + + validator, found := k.GetValidator(ctx, valAddr) + if !found { + return time.Time{}, types.ErrNoDelegatorForAddress(k.Codespace()) + } + + if k.HasMaxUnbondingDelegationEntries(ctx, delAddr, valAddr) { + return time.Time{}, types.ErrMaxUnbondingDelegationEntries(k.Codespace()) + } + + returnAmount, err := k.unbond(ctx, delAddr, valAddr, sharesAmount) + if err != nil { + return time.Time{}, err + } + + // transfer the validator tokens to the not bonded pool + if validator.IsBonded() { + k.bondedTokensToNotBonded(ctx, returnAmount) + } + + completionTime := ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx)) + ubd := k.SetUnbondingDelegationEntry(ctx, delAddr, valAddr, ctx.BlockHeight(), completionTime, returnAmount) + k.InsertUBDQueue(ctx, ubd, completionTime) + + return completionTime, nil +} + +// CompleteUnbonding completes the unbonding of all mature entries in the +// retrieved unbonding delegation object. +func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, + valAddr sdk.ValAddress) sdk.Error { + + ubd, found := k.GetUnbondingDelegation(ctx, delAddr, valAddr) + if !found { + return types.ErrNoUnbondingDelegation(k.Codespace()) + } + + ctxTime := ctx.BlockHeader().Time + + // loop through all the entries and complete unbonding mature entries + for i := 0; i < len(ubd.Entries); i++ { + entry := ubd.Entries[i] + if entry.IsMature(ctxTime) { + ubd.RemoveEntry(int64(i)) + i-- + + // track undelegation only when remaining or truncated shares are non-zero + if !entry.Balance.IsZero() { + amt := sdk.NewCoins(sdk.NewCoin(k.GetParams(ctx).BondDenom, entry.Balance)) + err := k.supplyKeeper.UndelegateCoinsFromModuleToAccount(ctx, types.NotBondedPoolName, ubd.DelegatorAddress, amt) + if err != nil { + return err + } + } + } + } + + // set the unbonding delegation or remove it if there are no more entries + if len(ubd.Entries) == 0 { + k.RemoveUnbondingDelegation(ctx, ubd) + } else { + k.SetUnbondingDelegation(ctx, ubd) + } + + return nil +} + +// begin unbonding / redelegation; create a redelegation record +func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, + valSrcAddr, valDstAddr sdk.ValAddress, sharesAmount sdk.Dec) ( + completionTime time.Time, errSdk sdk.Error) { + + if bytes.Equal(valSrcAddr, valDstAddr) { + return time.Time{}, types.ErrSelfRedelegation(k.Codespace()) + } + + dstValidator, found := k.GetValidator(ctx, valDstAddr) + if !found { + return time.Time{}, types.ErrBadRedelegationDst(k.Codespace()) + } + + srcValidator, found := k.GetValidator(ctx, valSrcAddr) + if !found { + return time.Time{}, types.ErrBadRedelegationDst(k.Codespace()) + } + + // check if this is a transitive redelegation + if k.HasReceivingRedelegation(ctx, delAddr, valSrcAddr) { + return time.Time{}, types.ErrTransitiveRedelegation(k.Codespace()) + } + + if k.HasMaxRedelegationEntries(ctx, delAddr, valSrcAddr, valDstAddr) { + return time.Time{}, types.ErrMaxRedelegationEntries(k.Codespace()) + } + + returnAmount, err := k.unbond(ctx, delAddr, valSrcAddr, sharesAmount) + if err != nil { + return time.Time{}, err + } + + if returnAmount.IsZero() { + return time.Time{}, types.ErrVerySmallRedelegation(k.Codespace()) + } + + sharesCreated, err := k.Delegate(ctx, delAddr, returnAmount, srcValidator.GetStatus(), dstValidator, false) + if err != nil { + return time.Time{}, err + } + + // create the unbonding delegation + completionTime, height, completeNow := k.getBeginInfo(ctx, valSrcAddr) + + if completeNow { // no need to create the redelegation object + return completionTime, nil + } + + red := k.SetRedelegationEntry(ctx, delAddr, valSrcAddr, valDstAddr, + height, completionTime, returnAmount, sharesAmount, sharesCreated) + k.InsertRedelegationQueue(ctx, red, completionTime) + return completionTime, nil +} + +// CompleteRedelegation completes the unbonding of all mature entries in the +// retrieved unbonding delegation object. +func (k Keeper) CompleteRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, + valSrcAddr, valDstAddr sdk.ValAddress) sdk.Error { + + red, found := k.GetRedelegation(ctx, delAddr, valSrcAddr, valDstAddr) + if !found { + return types.ErrNoRedelegation(k.Codespace()) + } + + ctxTime := ctx.BlockHeader().Time + + // loop through all the entries and complete mature redelegation entries + for i := 0; i < len(red.Entries); i++ { + entry := red.Entries[i] + if entry.IsMature(ctxTime) { + red.RemoveEntry(int64(i)) + i-- + } + } + + // set the redelegation or remove it if there are no more entries + if len(red.Entries) == 0 { + k.RemoveRedelegation(ctx, red) + } else { + k.SetRedelegation(ctx, red) + } + + return nil +} + +// ValidateUnbondAmount validates that a given unbond or redelegation amount is +// valied based on upon the converted shares. If the amount is valid, the total +// amount of respective shares is returned, otherwise an error is returned. +func (k Keeper) ValidateUnbondAmount( + ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt sdk.Int, +) (shares sdk.Dec, err sdk.Error) { + + validator, found := k.GetValidator(ctx, valAddr) + if !found { + return shares, types.ErrNoValidatorFound(k.Codespace()) + } + + del, found := k.GetDelegation(ctx, delAddr, valAddr) + if !found { + return shares, types.ErrNoDelegation(k.Codespace()) + } + + shares, err = validator.SharesFromTokens(amt) + if err != nil { + return shares, err + } + + sharesTruncated, err := validator.SharesFromTokensTruncated(amt) + if err != nil { + return shares, err + } + + delShares := del.GetShares() + if sharesTruncated.GT(delShares) { + return shares, types.ErrBadSharesAmount(k.Codespace()) + } + + // Cap the shares at the delegation's shares. Shares being greater could occur + // due to rounding, however we don't want to truncate the shares or take the + // minimum because we want to allow for the full withdraw of shares from a + // delegation. + if shares.GT(delShares) { + shares = delShares + } + + return shares, nil +} diff --git a/x/staking/keeper/delegation_test.go b/x/staking/keeper/delegation_test.go new file mode 100644 index 0000000..7d9403b --- /dev/null +++ b/x/staking/keeper/delegation_test.go @@ -0,0 +1,946 @@ +package keeper + +import ( + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// tests GetDelegation, GetDelegatorDelegations, SetDelegation, RemoveDelegation, GetDelegatorDelegations +func TestDelegation(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 10) + + //construct the validators + amts := []sdk.Int{sdk.NewInt(9), sdk.NewInt(8), sdk.NewInt(7)} + var validators [3]types.Validator + for i, amt := range amts { + validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{}) + validators[i], _ = validators[i].AddTokensFromDel(amt) + } + + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], true) + validators[2] = TestingUpdateValidator(keeper, ctx, validators[2], true) + + // first add a validators[0] to delegate too + + bond1to1 := types.NewDelegation(addrDels[0], addrVals[0], sdk.NewDec(9)) + + // check the empty keeper first + _, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) + require.False(t, found) + + // set and retrieve a record + keeper.SetDelegation(ctx, bond1to1) + resBond, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.True(t, bond1to1.Equal(resBond)) + + // modify a records, save, and retrieve + bond1to1.Shares = sdk.NewDec(99) + keeper.SetDelegation(ctx, bond1to1) + resBond, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.True(t, bond1to1.Equal(resBond)) + + // add some more records + bond1to2 := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDec(9)) + bond1to3 := types.NewDelegation(addrDels[0], addrVals[2], sdk.NewDec(9)) + bond2to1 := types.NewDelegation(addrDels[1], addrVals[0], sdk.NewDec(9)) + bond2to2 := types.NewDelegation(addrDels[1], addrVals[1], sdk.NewDec(9)) + bond2to3 := types.NewDelegation(addrDels[1], addrVals[2], sdk.NewDec(9)) + keeper.SetDelegation(ctx, bond1to2) + keeper.SetDelegation(ctx, bond1to3) + keeper.SetDelegation(ctx, bond2to1) + keeper.SetDelegation(ctx, bond2to2) + keeper.SetDelegation(ctx, bond2to3) + + // test all bond retrieve capabilities + resBonds := keeper.GetDelegatorDelegations(ctx, addrDels[0], 5) + require.Equal(t, 3, len(resBonds)) + require.True(t, bond1to1.Equal(resBonds[0])) + require.True(t, bond1to2.Equal(resBonds[1])) + require.True(t, bond1to3.Equal(resBonds[2])) + resBonds = keeper.GetAllDelegatorDelegations(ctx, addrDels[0]) + require.Equal(t, 3, len(resBonds)) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[0], 2) + require.Equal(t, 2, len(resBonds)) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) + require.Equal(t, 3, len(resBonds)) + require.True(t, bond2to1.Equal(resBonds[0])) + require.True(t, bond2to2.Equal(resBonds[1])) + require.True(t, bond2to3.Equal(resBonds[2])) + allBonds := keeper.GetAllDelegations(ctx) + require.Equal(t, 6, len(allBonds)) + require.True(t, bond1to1.Equal(allBonds[0])) + require.True(t, bond1to2.Equal(allBonds[1])) + require.True(t, bond1to3.Equal(allBonds[2])) + require.True(t, bond2to1.Equal(allBonds[3])) + require.True(t, bond2to2.Equal(allBonds[4])) + require.True(t, bond2to3.Equal(allBonds[5])) + + resVals := keeper.GetDelegatorValidators(ctx, addrDels[0], 3) + require.Equal(t, 3, len(resVals)) + resVals = keeper.GetDelegatorValidators(ctx, addrDels[1], 4) + require.Equal(t, 3, len(resVals)) + + for i := 0; i < 3; i++ { + + resVal, err := keeper.GetDelegatorValidator(ctx, addrDels[0], addrVals[i]) + require.Nil(t, err) + require.Equal(t, addrVals[i], resVal.GetOperator()) + + resVal, err = keeper.GetDelegatorValidator(ctx, addrDels[1], addrVals[i]) + require.Nil(t, err) + require.Equal(t, addrVals[i], resVal.GetOperator()) + + resDels := keeper.GetValidatorDelegations(ctx, addrVals[i]) + require.Len(t, resDels, 2) + } + + // delete a record + keeper.RemoveDelegation(ctx, bond2to3) + _, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[2]) + require.False(t, found) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) + require.Equal(t, 2, len(resBonds)) + require.True(t, bond2to1.Equal(resBonds[0])) + require.True(t, bond2to2.Equal(resBonds[1])) + + resBonds = keeper.GetAllDelegatorDelegations(ctx, addrDels[1]) + require.Equal(t, 2, len(resBonds)) + + // delete all the records from delegator 2 + keeper.RemoveDelegation(ctx, bond2to1) + keeper.RemoveDelegation(ctx, bond2to2) + _, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[0]) + require.False(t, found) + _, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[1]) + require.False(t, found) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) + require.Equal(t, 0, len(resBonds)) +} + +// tests Get/Set/Remove UnbondingDelegation +func TestUnbondingDelegation(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + + ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 0, + time.Unix(0, 0), sdk.NewInt(5)) + + // set and retrieve a record + keeper.SetUnbondingDelegation(ctx, ubd) + resUnbond, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.True(t, ubd.Equal(resUnbond)) + + // modify a records, save, and retrieve + ubd.Entries[0].Balance = sdk.NewInt(21) + keeper.SetUnbondingDelegation(ctx, ubd) + + resUnbonds := keeper.GetUnbondingDelegations(ctx, addrDels[0], 5) + require.Equal(t, 1, len(resUnbonds)) + + resUnbonds = keeper.GetAllUnbondingDelegations(ctx, addrDels[0]) + require.Equal(t, 1, len(resUnbonds)) + + resUnbond, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.True(t, ubd.Equal(resUnbond)) + + // delete a record + keeper.RemoveUnbondingDelegation(ctx, ubd) + _, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.False(t, found) + + resUnbonds = keeper.GetUnbondingDelegations(ctx, addrDels[0], 5) + require.Equal(t, 0, len(resUnbonds)) + + resUnbonds = keeper.GetAllUnbondingDelegations(ctx, addrDels[0]) + require.Equal(t, 0, len(resUnbonds)) + +} + +func TestUnbondDelegation(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + + startTokens := sdk.TokensFromConsensusPower(10) + + notBondedPool := keeper.GetNotBondedPool(ctx) + err := notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens))) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + // create a validator and a delegator to that validator + // note this validator starts not-bonded + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + + validator, issuedShares := validator.AddTokensFromDel(startTokens) + require.Equal(t, startTokens, issuedShares.RoundInt()) + + validator = TestingUpdateValidator(keeper, ctx, validator, true) + + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) + + bondTokens := sdk.TokensFromConsensusPower(6) + amount, err := keeper.unbond(ctx, addrDels[0], addrVals[0], bondTokens.ToDec()) + require.NoError(t, err) + require.Equal(t, bondTokens, amount) // shares to be added to an unbonding delegation + + delegation, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + validator, found = keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + + remainingTokens := startTokens.Sub(bondTokens) + require.Equal(t, remainingTokens, delegation.Shares.RoundInt()) + require.Equal(t, remainingTokens, validator.BondedTokens()) +} + +func TestUnbondingDelegationsMaxEntries(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1) + startTokens := sdk.TokensFromConsensusPower(10) + bondDenom := keeper.BondDenom(ctx) + + bondedPool := keeper.GetBondedPool(ctx) + notBondedPool := keeper.GetNotBondedPool(ctx) + err := notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(bondDenom, startTokens))) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + // create a validator and a delegator to that validator + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + + validator, issuedShares := validator.AddTokensFromDel(startTokens) + require.Equal(t, startTokens, issuedShares.RoundInt()) + + validator = TestingUpdateValidator(keeper, ctx, validator, true) + require.True(sdk.IntEq(t, startTokens, validator.BondedTokens())) + require.True(t, validator.IsBonded()) + + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) + + maxEntries := keeper.MaxEntries(ctx) + + oldBonded := keeper.GetBondedPool(ctx).GetCoins().AmountOf(bondDenom) + oldNotBonded := keeper.GetNotBondedPool(ctx).GetCoins().AmountOf(bondDenom) + + // should all pass + var completionTime time.Time + for i := uint16(0); i < maxEntries; i++ { + var err error + completionTime, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1)) + require.NoError(t, err) + } + + bondedPool = keeper.GetBondedPool(ctx) + notBondedPool = keeper.GetNotBondedPool(ctx) + require.True(sdk.IntEq(t, bondedPool.GetCoins().AmountOf(bondDenom), oldBonded.SubRaw(int64(maxEntries)))) + require.True(sdk.IntEq(t, notBondedPool.GetCoins().AmountOf(bondDenom), oldNotBonded.AddRaw(int64(maxEntries)))) + + oldBonded = bondedPool.GetCoins().AmountOf(bondDenom) + oldNotBonded = notBondedPool.GetCoins().AmountOf(bondDenom) + + // an additional unbond should fail due to max entries + _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1)) + require.Error(t, err) + + bondedPool = keeper.GetBondedPool(ctx) + notBondedPool = keeper.GetNotBondedPool(ctx) + require.True(sdk.IntEq(t, bondedPool.GetCoins().AmountOf(bondDenom), oldBonded)) + require.True(sdk.IntEq(t, notBondedPool.GetCoins().AmountOf(bondDenom), oldNotBonded)) + + // mature unbonding delegations + ctx = ctx.WithBlockTime(completionTime) + err = keeper.CompleteUnbonding(ctx, addrDels[0], addrVals[0]) + require.NoError(t, err) + + bondedPool = keeper.GetBondedPool(ctx) + notBondedPool = keeper.GetNotBondedPool(ctx) + require.True(sdk.IntEq(t, bondedPool.GetCoins().AmountOf(bondDenom), oldBonded)) + require.True(sdk.IntEq(t, notBondedPool.GetCoins().AmountOf(bondDenom), oldNotBonded.SubRaw(int64(maxEntries)))) + + oldNotBonded = notBondedPool.GetCoins().AmountOf(bondDenom) + + // unbonding should work again + _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1)) + require.NoError(t, err) + + bondedPool = keeper.GetBondedPool(ctx) + + notBondedPool = keeper.GetNotBondedPool(ctx) + require.True(sdk.IntEq(t, bondedPool.GetCoins().AmountOf(bondDenom), oldBonded.SubRaw(1))) + require.True(sdk.IntEq(t, notBondedPool.GetCoins().AmountOf(bondDenom), oldNotBonded.AddRaw(1))) +} + +// test undelegating self delegation from a validator pushing it below MinSelfDelegation +// shift it from the bonded to unbonding state and jailed +func TestUndelegateSelfDelegationBelowMinSelfDelegation(t *testing.T) { + + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + delTokens := sdk.TokensFromConsensusPower(10) + delCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), delTokens)) + + //create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + + validator.MinSelfDelegation = delTokens + validator, issuedShares := validator.AddTokensFromDel(delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) + + // add bonded tokens to pool for delegations + notBondedPool := keeper.GetNotBondedPool(ctx) + err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(delCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + validator = TestingUpdateValidator(keeper, ctx, validator, true) + require.True(t, validator.IsBonded()) + + selfDelegation := types.NewDelegation(sdk.AccAddress(addrVals[0].Bytes()), addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) + + // add bonded tokens to pool for delegations + bondedPool := keeper.GetBondedPool(ctx) + err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + // create a second delegation to this validator + keeper.DeleteValidatorByPowerIndex(ctx, validator) + validator, issuedShares = validator.AddTokensFromDel(delTokens) + require.True(t, validator.IsBonded()) + require.Equal(t, delTokens, issuedShares.RoundInt()) + + // add bonded tokens to pool for delegations + bondedPool = keeper.GetBondedPool(ctx) + err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + validator = TestingUpdateValidator(keeper, ctx, validator, true) + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) + + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + _, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.TokensFromConsensusPower(6).ToDec()) + require.NoError(t, err) + + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + require.Equal(t, sdk.TokensFromConsensusPower(14), validator.Tokens) + require.Equal(t, sdk.Unbonding, validator.Status) + require.True(t, validator.Jailed) +} + +func TestUndelegateFromUnbondingValidator(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + delTokens := sdk.TokensFromConsensusPower(10) + delCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), delTokens)) + + //create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + + validator, issuedShares := validator.AddTokensFromDel(delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) + + // add bonded tokens to pool for delegations + notBondedPool := keeper.GetNotBondedPool(ctx) + err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(delCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + validator = TestingUpdateValidator(keeper, ctx, validator, true) + require.True(t, validator.IsBonded()) + + selfDelegation := types.NewDelegation(sdk.AccAddress(addrVals[0].Bytes()), addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) + + bondedPool := keeper.GetBondedPool(ctx) + err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + // create a second delegation to this validator + keeper.DeleteValidatorByPowerIndex(ctx, validator) + + validator, issuedShares = validator.AddTokensFromDel(delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) + + bondedPool = keeper.GetBondedPool(ctx) + err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + validator = TestingUpdateValidator(keeper, ctx, validator, true) + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) + + bondedPool = keeper.GetBondedPool(ctx) + err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + header := ctx.BlockHeader() + blockHeight := int64(10) + header.Height = blockHeight + blockTime := time.Unix(333, 0) + header.Time = blockTime + ctx = ctx.WithBlockHeader(header) + + // unbond the all self-delegation to put validator in unbonding state + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + _, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) + require.NoError(t, err) + + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + require.Equal(t, blockHeight, validator.UnbondingHeight) + params := keeper.GetParams(ctx) + require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingCompletionTime)) + + blockHeight2 := int64(20) + blockTime2 := time.Unix(444, 0).UTC() + ctx = ctx.WithBlockHeight(blockHeight2) + ctx = ctx.WithBlockTime(blockTime2) + + // unbond some of the other delegation's shares + _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(6)) + require.NoError(t, err) + + // retrieve the unbonding delegation + ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + require.True(t, ubd.Entries[0].Balance.Equal(sdk.NewInt(6))) + assert.Equal(t, blockHeight2, ubd.Entries[0].CreationHeight) + assert.True(t, blockTime2.Add(params.UnbondingTime).Equal(ubd.Entries[0].CompletionTime)) +} + +func TestUndelegateFromUnbondedValidator(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1) + delTokens := sdk.TokensFromConsensusPower(10) + delCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), delTokens)) + + // add bonded tokens to pool for delegations + notBondedPool := keeper.GetNotBondedPool(ctx) + err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(delCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + // create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + + valTokens := sdk.TokensFromConsensusPower(10) + validator, issuedShares := validator.AddTokensFromDel(valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + require.True(t, validator.IsBonded()) + + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) + + bondedPool := keeper.GetBondedPool(ctx) + err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + // create a second delegation to this validator + keeper.DeleteValidatorByPowerIndex(ctx, validator) + validator, issuedShares = validator.AddTokensFromDel(delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + require.True(t, validator.IsBonded()) + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) + + ctx = ctx.WithBlockHeight(10) + ctx = ctx.WithBlockTime(time.Unix(333, 0)) + + // unbond the all self-delegation to put validator in unbonding state + _, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec()) + require.NoError(t, err) + + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + require.Equal(t, ctx.BlockHeight(), validator.UnbondingHeight) + params := keeper.GetParams(ctx) + require.True(t, ctx.BlockHeader().Time.Add(params.UnbondingTime).Equal(validator.UnbondingCompletionTime)) + + // unbond the validator + ctx = ctx.WithBlockTime(validator.UnbondingCompletionTime) + keeper.UnbondAllMatureValidatorQueue(ctx) + + // Make sure validator is still in state because there is still an outstanding delegation + validator, found = keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + require.Equal(t, validator.Status, sdk.Unbonded) + + // unbond some of the other delegation's shares + unbondTokens := sdk.TokensFromConsensusPower(6) + _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], unbondTokens.ToDec()) + require.NoError(t, err) + + // unbond rest of the other delegation's shares + remainingTokens := delTokens.Sub(unbondTokens) + _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], remainingTokens.ToDec()) + require.NoError(t, err) + + // now validator should now be deleted from state + validator, found = keeper.GetValidator(ctx, addrVals[0]) + require.False(t, found, "%v", validator) +} + +func TestUnbondingAllDelegationFromValidator(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + delTokens := sdk.TokensFromConsensusPower(10) + delCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), delTokens)) + + // add bonded tokens to pool for delegations + notBondedPool := keeper.GetNotBondedPool(ctx) + err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(delCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + //create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + + valTokens := sdk.TokensFromConsensusPower(10) + validator, issuedShares := validator.AddTokensFromDel(valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + + validator = TestingUpdateValidator(keeper, ctx, validator, true) + require.True(t, validator.IsBonded()) + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) + + // create a second delegation to this validator + keeper.DeleteValidatorByPowerIndex(ctx, validator) + validator, issuedShares = validator.AddTokensFromDel(delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) + + bondedPool := keeper.GetBondedPool(ctx) + err = bondedPool.SetCoins(bondedPool.GetCoins().Add(delCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + validator = TestingUpdateValidator(keeper, ctx, validator, true) + require.True(t, validator.IsBonded()) + + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) + + ctx = ctx.WithBlockHeight(10) + ctx = ctx.WithBlockTime(time.Unix(333, 0)) + + // unbond the all self-delegation to put validator in unbonding state + _, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], valTokens.ToDec()) + require.NoError(t, err) + + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + // unbond all the remaining delegation + _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], delTokens.ToDec()) + require.NoError(t, err) + + // validator should still be in state and still be in unbonding state + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + require.Equal(t, validator.Status, sdk.Unbonding) + + // unbond the validator + ctx = ctx.WithBlockTime(validator.UnbondingCompletionTime) + keeper.UnbondAllMatureValidatorQueue(ctx) + + // validator should now be deleted from state + _, found = keeper.GetValidator(ctx, addrVals[0]) + require.False(t, found) +} + +// Make sure that that the retrieving the delegations doesn't affect the state +func TestGetRedelegationsFromSrcValidator(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + + rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0, + time.Unix(0, 0), sdk.NewInt(5), + sdk.NewDec(5)) + + // set and retrieve a record + keeper.SetRedelegation(ctx, rd) + resBond, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + + // get the redelegations one time + redelegations := keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resBond)) + + // get the redelegations a second time, should be exactly the same + redelegations = keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resBond)) +} + +// tests Get/Set/Remove/Has UnbondingDelegation +func TestRedelegation(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + + rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0, + time.Unix(0, 0), sdk.NewInt(5), + sdk.NewDec(5)) + + // test shouldn't have and redelegations + has := keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1]) + require.False(t, has) + + // set and retrieve a record + keeper.SetRedelegation(ctx, rd) + resRed, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + + redelegations := keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resRed)) + + redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resRed)) + + redelegations = keeper.GetAllRedelegations(ctx, addrDels[0], nil, nil) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resRed)) + + // check if has the redelegation + has = keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1]) + require.True(t, has) + + // modify a records, save, and retrieve + rd.Entries[0].SharesDst = sdk.NewDec(21) + keeper.SetRedelegation(ctx, rd) + + resRed, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.True(t, rd.Equal(resRed)) + + redelegations = keeper.GetRedelegationsFromSrcValidator(ctx, addrVals[0]) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resRed)) + + redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resRed)) + + // delete a record + keeper.RemoveRedelegation(ctx, rd) + _, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.False(t, found) + + redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + require.Equal(t, 0, len(redelegations)) + + redelegations = keeper.GetAllRedelegations(ctx, addrDels[0], nil, nil) + require.Equal(t, 0, len(redelegations)) +} + +func TestRedelegateToSameValidator(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + valTokens := sdk.TokensFromConsensusPower(10) + startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), valTokens)) + + // add bonded tokens to pool for delegations + notBondedPool := keeper.GetNotBondedPool(ctx) + err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + // create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator, issuedShares := validator.AddTokensFromDel(valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + require.True(t, validator.IsBonded()) + + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) + + _, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[0], sdk.NewDec(5)) + require.Error(t, err) + +} + +func TestRedelegationMaxEntries(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + startTokens := sdk.TokensFromConsensusPower(20) + startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens)) + + // add bonded tokens to pool for delegations + notBondedPool := keeper.GetNotBondedPool(ctx) + err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + // create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + valTokens := sdk.TokensFromConsensusPower(10) + validator, issuedShares := validator.AddTokensFromDel(valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) + + // create a second validator + validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2, issuedShares = validator2.AddTokensFromDel(valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + + validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) + require.Equal(t, sdk.Bonded, validator2.Status) + + maxEntries := keeper.MaxEntries(ctx) + + // redelegations should pass + var completionTime time.Time + for i := uint16(0); i < maxEntries; i++ { + var err error + completionTime, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1)) + require.NoError(t, err) + } + + // an additional redelegation should fail due to max entries + _, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1)) + require.Error(t, err) + + // mature redelegations + ctx = ctx.WithBlockTime(completionTime) + err = keeper.CompleteRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1]) + require.NoError(t, err) + + // redelegation should work again + _, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1)) + require.NoError(t, err) +} + +func TestRedelegateSelfDelegation(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + startTokens := sdk.TokensFromConsensusPower(30) + startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens)) + + // add bonded tokens to pool for delegations + notBondedPool := keeper.GetNotBondedPool(ctx) + err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + //create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + valTokens := sdk.TokensFromConsensusPower(10) + validator, issuedShares := validator.AddTokensFromDel(valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + + validator = TestingUpdateValidator(keeper, ctx, validator, true) + + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) + + // create a second validator + validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2, issuedShares = validator2.AddTokensFromDel(valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) + require.Equal(t, sdk.Bonded, validator2.Status) + + // create a second delegation to validator 1 + delTokens := sdk.TokensFromConsensusPower(10) + validator, issuedShares = validator.AddTokensFromDel(delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) + + _, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], delTokens.ToDec()) + require.NoError(t, err) + + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(updates)) + + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + require.Equal(t, valTokens, validator.Tokens) + require.Equal(t, sdk.Unbonding, validator.Status) +} + +func TestRedelegateFromUnbondingValidator(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + startTokens := sdk.TokensFromConsensusPower(30) + startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens)) + + // add bonded tokens to pool for delegations + notBondedPool := keeper.GetNotBondedPool(ctx) + err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + //create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + + valTokens := sdk.TokensFromConsensusPower(10) + validator, issuedShares := validator.AddTokensFromDel(valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) + + // create a second delegation to this validator + keeper.DeleteValidatorByPowerIndex(ctx, validator) + delTokens := sdk.TokensFromConsensusPower(10) + validator, issuedShares = validator.AddTokensFromDel(delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) + + // create a second validator + validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2, issuedShares = validator2.AddTokensFromDel(valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) + + header := ctx.BlockHeader() + blockHeight := int64(10) + header.Height = blockHeight + blockTime := time.Unix(333, 0) + header.Time = blockTime + ctx = ctx.WithBlockHeader(header) + + // unbond the all self-delegation to put validator in unbonding state + _, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) + require.NoError(t, err) + + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + require.Equal(t, blockHeight, validator.UnbondingHeight) + params := keeper.GetParams(ctx) + require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingCompletionTime)) + + //change the context + header = ctx.BlockHeader() + blockHeight2 := int64(20) + header.Height = blockHeight2 + blockTime2 := time.Unix(444, 0) + header.Time = blockTime2 + ctx = ctx.WithBlockHeader(header) + + // unbond some of the other delegation's shares + redelegateTokens := sdk.TokensFromConsensusPower(6) + _, err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], redelegateTokens.ToDec()) + require.NoError(t, err) + + // retrieve the unbonding delegation + ubd, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + assert.Equal(t, blockHeight, ubd.Entries[0].CreationHeight) + assert.True(t, blockTime.Add(params.UnbondingTime).Equal(ubd.Entries[0].CompletionTime)) +} + +func TestRedelegateFromUnbondedValidator(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + startTokens := sdk.TokensFromConsensusPower(30) + startCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), startTokens)) + + // add bonded tokens to pool for delegations + notBondedPool := keeper.GetNotBondedPool(ctx) + err := notBondedPool.SetCoins(notBondedPool.GetCoins().Add(startCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + //create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + + valTokens := sdk.TokensFromConsensusPower(10) + validator, issuedShares := validator.AddTokensFromDel(valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) + keeper.SetDelegation(ctx, selfDelegation) + + // create a second delegation to this validator + keeper.DeleteValidatorByPowerIndex(ctx, validator) + delTokens := sdk.TokensFromConsensusPower(10) + validator, issuedShares = validator.AddTokensFromDel(delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) + keeper.SetDelegation(ctx, delegation) + + // create a second validator + validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2, issuedShares = validator2.AddTokensFromDel(valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) + require.Equal(t, sdk.Bonded, validator2.Status) + + ctx = ctx.WithBlockHeight(10) + ctx = ctx.WithBlockTime(time.Unix(333, 0)) + + // unbond the all self-delegation to put validator in unbonding state + _, err = keeper.Undelegate(ctx, val0AccAddr, addrVals[0], delTokens.ToDec()) + require.NoError(t, err) + + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + require.Equal(t, ctx.BlockHeight(), validator.UnbondingHeight) + params := keeper.GetParams(ctx) + require.True(t, ctx.BlockHeader().Time.Add(params.UnbondingTime).Equal(validator.UnbondingCompletionTime)) + + // unbond the validator + keeper.unbondingToUnbonded(ctx, validator) + + // redelegate some of the delegation's shares + redelegationTokens := sdk.TokensFromConsensusPower(6) + _, err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], redelegationTokens.ToDec()) + require.NoError(t, err) + + // no red should have been found + red, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.False(t, found, "%v", red) +} diff --git a/x/staking/keeper/hooks.go b/x/staking/keeper/hooks.go new file mode 100644 index 0000000..b8f2590 --- /dev/null +++ b/x/staking/keeper/hooks.go @@ -0,0 +1,79 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// Implements StakingHooks interface +var _ types.StakingHooks = Keeper{} + +// AfterValidatorCreated - call hook if registered +func (k Keeper) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { + if k.hooks != nil { + k.hooks.AfterValidatorCreated(ctx, valAddr) + } +} + +// BeforeValidatorModified - call hook if registered +func (k Keeper) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) { + if k.hooks != nil { + k.hooks.BeforeValidatorModified(ctx, valAddr) + } +} + +// AfterValidatorRemoved - call hook if registered +func (k Keeper) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { + if k.hooks != nil { + k.hooks.AfterValidatorRemoved(ctx, consAddr, valAddr) + } +} + +// AfterValidatorBonded - call hook if registered +func (k Keeper) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { + if k.hooks != nil { + k.hooks.AfterValidatorBonded(ctx, consAddr, valAddr) + } +} + +// AfterValidatorBeginUnbonding - call hook if registered +func (k Keeper) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { + if k.hooks != nil { + k.hooks.AfterValidatorBeginUnbonding(ctx, consAddr, valAddr) + } +} + +// BeforeDelegationCreated - call hook if registered +func (k Keeper) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { + if k.hooks != nil { + k.hooks.BeforeDelegationCreated(ctx, delAddr, valAddr) + } +} + +// BeforeDelegationSharesModified - call hook if registered +func (k Keeper) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { + if k.hooks != nil { + k.hooks.BeforeDelegationSharesModified(ctx, delAddr, valAddr) + } +} + +// BeforeDelegationRemoved - call hook if registered +func (k Keeper) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { + if k.hooks != nil { + k.hooks.BeforeDelegationRemoved(ctx, delAddr, valAddr) + } +} + +// AfterDelegationModified - call hook if registered +func (k Keeper) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { + if k.hooks != nil { + k.hooks.AfterDelegationModified(ctx, delAddr, valAddr) + } +} + +// BeforeValidatorSlashed - call hook if registered +func (k Keeper) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) { + if k.hooks != nil { + k.hooks.BeforeValidatorSlashed(ctx, valAddr, fraction) + } +} diff --git a/x/staking/keeper/invariants.go b/x/staking/keeper/invariants.go new file mode 100644 index 0000000..e89cfab --- /dev/null +++ b/x/staking/keeper/invariants.go @@ -0,0 +1,181 @@ +package keeper + +import ( + "bytes" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/exported" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// RegisterInvariants registers all staking invariants +func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { + + ir.RegisterRoute(types.ModuleName, "module-accounts", + ModuleAccountInvariants(k)) + ir.RegisterRoute(types.ModuleName, "nonnegative-power", + NonNegativePowerInvariant(k)) + ir.RegisterRoute(types.ModuleName, "positive-delegation", + PositiveDelegationInvariant(k)) + ir.RegisterRoute(types.ModuleName, "delegator-shares", + DelegatorSharesInvariant(k)) +} + +// AllInvariants runs all invariants of the staking module. +func AllInvariants(k Keeper) sdk.Invariant { + + return func(ctx sdk.Context) (string, bool) { + res, stop := ModuleAccountInvariants(k)(ctx) + if stop { + return res, stop + } + + res, stop = NonNegativePowerInvariant(k)(ctx) + if stop { + return res, stop + } + + res, stop = PositiveDelegationInvariant(k)(ctx) + if stop { + return res, stop + } + + return DelegatorSharesInvariant(k)(ctx) + } +} + +// ModuleAccountInvariants checks that the bonded and notBonded ModuleAccounts pools +// reflects the tokens actively bonded and not bonded +func ModuleAccountInvariants(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + bonded := sdk.ZeroInt() + notBonded := sdk.ZeroInt() + bondedPool := k.GetBondedPool(ctx) + notBondedPool := k.GetNotBondedPool(ctx) + bondDenom := k.BondDenom(ctx) + + k.IterateValidators(ctx, func(_ int64, validator exported.ValidatorI) bool { + switch validator.GetStatus() { + case sdk.Bonded: + bonded = bonded.Add(validator.GetTokens()) + case sdk.Unbonding, sdk.Unbonded: + notBonded = notBonded.Add(validator.GetTokens()) + default: + panic("invalid validator status") + } + return false + }) + + k.IterateUnbondingDelegations(ctx, func(_ int64, ubd types.UnbondingDelegation) bool { + for _, entry := range ubd.Entries { + notBonded = notBonded.Add(entry.Balance) + } + return false + }) + + poolBonded := bondedPool.GetCoins().AmountOf(bondDenom) + poolNotBonded := notBondedPool.GetCoins().AmountOf(bondDenom) + broken := !poolBonded.Equal(bonded) || !poolNotBonded.Equal(notBonded) + + // Bonded tokens should equal sum of tokens with bonded validators + // Not-bonded tokens should equal unbonding delegations plus tokens on unbonded validators + return sdk.FormatInvariant(types.ModuleName, "bonded and not bonded module account coins", fmt.Sprintf( + "\tPool's bonded tokens: %v\n"+ + "\tsum of bonded tokens: %v\n"+ + "not bonded token invariance:\n"+ + "\tPool's not bonded tokens: %v\n"+ + "\tsum of not bonded tokens: %v\n"+ + "module accounts total (bonded + not bonded):\n"+ + "\tModule Accounts' tokens: %v\n"+ + "\tsum tokens: %v\n", + poolBonded, bonded, poolNotBonded, notBonded, poolBonded.Add(poolNotBonded), bonded.Add(notBonded))), broken + } +} + +// NonNegativePowerInvariant checks that all stored validators have >= 0 power. +func NonNegativePowerInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var msg string + var broken bool + + iterator := k.ValidatorsPowerStoreIterator(ctx) + + for ; iterator.Valid(); iterator.Next() { + validator, found := k.GetValidator(ctx, iterator.Value()) + if !found { + panic(fmt.Sprintf("validator record not found for address: %X\n", iterator.Value())) + } + + powerKey := types.GetValidatorsByPowerIndexKey(validator) + + if !bytes.Equal(iterator.Key(), powerKey) { + broken = true + msg += fmt.Sprintf("power store invariance:\n\tvalidator.Power: %v"+ + "\n\tkey should be: %v\n\tkey in store: %v\n", + validator.GetConsensusPower(), powerKey, iterator.Key()) + } + + if validator.Tokens.IsNegative() { + broken = true + msg += fmt.Sprintf("\tnegative tokens for validator: %v\n", validator) + } + } + iterator.Close() + return sdk.FormatInvariant(types.ModuleName, "nonnegative power", fmt.Sprintf("found invalid validator powers\n%s", msg)), broken + } +} + +// PositiveDelegationInvariant checks that all stored delegations have > 0 shares. +func PositiveDelegationInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var msg string + var count int + + delegations := k.GetAllDelegations(ctx) + for _, delegation := range delegations { + if delegation.Shares.IsNegative() { + count++ + msg += fmt.Sprintf("\tdelegation with negative shares: %+v\n", delegation) + } + if delegation.Shares.IsZero() { + count++ + msg += fmt.Sprintf("\tdelegation with zero shares: %+v\n", delegation) + } + } + broken := count != 0 + + return sdk.FormatInvariant(types.ModuleName, "positive delegations", fmt.Sprintf( + "%d invalid delegations found\n%s", count, msg)), broken + } +} + +// DelegatorSharesInvariant checks whether all the delegator shares which persist +// in the delegator object add up to the correct total delegator shares +// amount stored in each validator. +func DelegatorSharesInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var msg string + var broken bool + + validators := k.GetAllValidators(ctx) + for _, validator := range validators { + + valTotalDelShares := validator.GetDelegatorShares() + + totalDelShares := sdk.ZeroDec() + delegations := k.GetValidatorDelegations(ctx, validator.GetOperator()) + for _, delegation := range delegations { + totalDelShares = totalDelShares.Add(delegation.Shares) + } + + if !valTotalDelShares.Equal(totalDelShares) { + broken = true + msg += fmt.Sprintf("broken delegator shares invariance:\n"+ + "\tvalidator.DelegatorShares: %v\n"+ + "\tsum of Delegator.Shares: %v\n", valTotalDelShares, totalDelShares) + } + } + return sdk.FormatInvariant(types.ModuleName, "delegator shares", msg), broken + } +} diff --git a/x/staking/keeper/keeper.go b/x/staking/keeper/keeper.go new file mode 100644 index 0000000..456f972 --- /dev/null +++ b/x/staking/keeper/keeper.go @@ -0,0 +1,99 @@ +package keeper + +import ( + "container/list" + "fmt" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +const aminoCacheSize = 500 + +// Implements ValidatorSet interface +var _ types.ValidatorSet = Keeper{} + +// Implements DelegationSet interface +var _ types.DelegationSet = Keeper{} + +// keeper of the staking store +type Keeper struct { + storeKey sdk.StoreKey + storeTKey sdk.StoreKey + cdc *codec.Codec + supplyKeeper types.SupplyKeeper + hooks types.StakingHooks + paramstore params.Subspace + validatorCache map[string]cachedValidator + validatorCacheList *list.List + + // codespace + codespace sdk.CodespaceType +} + +// NewKeeper creates a new staking Keeper instance +func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, supplyKeeper types.SupplyKeeper, + paramstore params.Subspace, codespace sdk.CodespaceType) Keeper { + + // ensure bonded and not bonded module accounts are set + if addr := supplyKeeper.GetModuleAddress(types.BondedPoolName); addr == nil { + panic(fmt.Sprintf("%s module account has not been set", types.BondedPoolName)) + } + + if addr := supplyKeeper.GetModuleAddress(types.NotBondedPoolName); addr == nil { + panic(fmt.Sprintf("%s module account has not been set", types.NotBondedPoolName)) + } + + return Keeper{ + storeKey: key, + storeTKey: tkey, + cdc: cdc, + supplyKeeper: supplyKeeper, + paramstore: paramstore.WithKeyTable(ParamKeyTable()), + hooks: nil, + validatorCache: make(map[string]cachedValidator, aminoCacheSize), + validatorCacheList: list.New(), + codespace: codespace, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// Set the validator hooks +func (k *Keeper) SetHooks(sh types.StakingHooks) *Keeper { + if k.hooks != nil { + panic("cannot set validator hooks twice") + } + k.hooks = sh + return k +} + +// return the codespace +func (k Keeper) Codespace() sdk.CodespaceType { + return k.codespace +} + +// Load the last total validator power. +func (k Keeper) GetLastTotalPower(ctx sdk.Context) (power sdk.Int) { + store := ctx.KVStore(k.storeKey) + b := store.Get(types.LastTotalPowerKey) + if b == nil { + return sdk.ZeroInt() + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &power) + return +} + +// Set the last total validator power. +func (k Keeper) SetLastTotalPower(ctx sdk.Context, power sdk.Int) { + store := ctx.KVStore(k.storeKey) + b := k.cdc.MustMarshalBinaryLengthPrefixed(power) + store.Set(types.LastTotalPowerKey, b) +} diff --git a/x/staking/keeper/keeper_test.go b/x/staking/keeper/keeper_test.go new file mode 100644 index 0000000..eb9d164 --- /dev/null +++ b/x/staking/keeper/keeper_test.go @@ -0,0 +1,24 @@ +package keeper + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func TestParams(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + expParams := types.DefaultParams() + + //check that the empty keeper loads the default + resParams := keeper.GetParams(ctx) + require.True(t, expParams.Equal(resParams)) + + //modify a params, save, and retrieve + expParams.MaxValidators = 777 + keeper.SetParams(ctx, expParams) + resParams = keeper.GetParams(ctx) + require.True(t, expParams.Equal(resParams)) +} diff --git a/x/staking/keeper/params.go b/x/staking/keeper/params.go new file mode 100644 index 0000000..dede56b --- /dev/null +++ b/x/staking/keeper/params.go @@ -0,0 +1,59 @@ +package keeper + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// Default parameter namespace +const ( + DefaultParamspace = types.ModuleName +) + +// ParamTable for staking module +func ParamKeyTable() params.KeyTable { + return params.NewKeyTable().RegisterParamSet(&types.Params{}) +} + +// UnbondingTime +func (k Keeper) UnbondingTime(ctx sdk.Context) (res time.Duration) { + k.paramstore.Get(ctx, types.KeyUnbondingTime, &res) + return +} + +// MaxValidators - Maximum number of validators +func (k Keeper) MaxValidators(ctx sdk.Context) (res uint16) { + k.paramstore.Get(ctx, types.KeyMaxValidators, &res) + return +} + +// MaxEntries - Maximum number of simultaneous unbonding +// delegations or redelegations (per pair/trio) +func (k Keeper) MaxEntries(ctx sdk.Context) (res uint16) { + k.paramstore.Get(ctx, types.KeyMaxEntries, &res) + return +} + +// BondDenom - Bondable coin denomination +func (k Keeper) BondDenom(ctx sdk.Context) (res string) { + k.paramstore.Get(ctx, types.KeyBondDenom, &res) + return +} + +// Get all parameteras as types.Params +func (k Keeper) GetParams(ctx sdk.Context) types.Params { + return types.NewParams( + k.UnbondingTime(ctx), + k.MaxValidators(ctx), + k.MaxEntries(ctx), + k.BondDenom(ctx), + ) +} + +// set the params +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.paramstore.SetParamSet(ctx, ¶ms) +} diff --git a/x/staking/keeper/pool.go b/x/staking/keeper/pool.go new file mode 100644 index 0000000..4ceb024 --- /dev/null +++ b/x/staking/keeper/pool.go @@ -0,0 +1,77 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// GetBondedPool returns the bonded tokens pool's module account +func (k Keeper) GetBondedPool(ctx sdk.Context) (bondedPool exported.ModuleAccountI) { + return k.supplyKeeper.GetModuleAccount(ctx, types.BondedPoolName) +} + +// GetNotBondedPool returns the not bonded tokens pool's module account +func (k Keeper) GetNotBondedPool(ctx sdk.Context) (notBondedPool exported.ModuleAccountI) { + return k.supplyKeeper.GetModuleAccount(ctx, types.NotBondedPoolName) +} + +// bondedTokensToNotBonded transfers coins from the bonded to the not bonded pool within staking +func (k Keeper) bondedTokensToNotBonded(ctx sdk.Context, tokens sdk.Int) { + coins := sdk.NewCoins(sdk.NewCoin(k.BondDenom(ctx), tokens)) + err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.BondedPoolName, types.NotBondedPoolName, coins) + if err != nil { + panic(err) + } +} + +// notBondedTokensToBonded transfers coins from the not bonded to the bonded pool within staking +func (k Keeper) notBondedTokensToBonded(ctx sdk.Context, tokens sdk.Int) { + coins := sdk.NewCoins(sdk.NewCoin(k.BondDenom(ctx), tokens)) + err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.NotBondedPoolName, types.BondedPoolName, coins) + if err != nil { + panic(err) + } +} + +// burnBondedTokens removes coins from the bonded pool module account +func (k Keeper) burnBondedTokens(ctx sdk.Context, amt sdk.Int) sdk.Error { + if !amt.IsPositive() { + // skip as no coins need to be burned + return nil + } + coins := sdk.NewCoins(sdk.NewCoin(k.BondDenom(ctx), amt)) + return k.supplyKeeper.BurnCoins(ctx, types.BondedPoolName, coins) +} + +// burnNotBondedTokens removes coins from the not bonded pool module account +func (k Keeper) burnNotBondedTokens(ctx sdk.Context, amt sdk.Int) sdk.Error { + if !amt.IsPositive() { + // skip as no coins need to be burned + return nil + } + coins := sdk.NewCoins(sdk.NewCoin(k.BondDenom(ctx), amt)) + return k.supplyKeeper.BurnCoins(ctx, types.NotBondedPoolName, coins) +} + +// TotalBondedTokens total staking tokens supply which is bonded +func (k Keeper) TotalBondedTokens(ctx sdk.Context) sdk.Int { + bondedPool := k.GetBondedPool(ctx) + return bondedPool.GetCoins().AmountOf(k.BondDenom(ctx)) +} + +// StakingTokenSupply staking tokens from the total supply +func (k Keeper) StakingTokenSupply(ctx sdk.Context) sdk.Int { + return k.supplyKeeper.GetSupply(ctx).GetTotal().AmountOf(k.BondDenom(ctx)) +} + +// BondedRatio the fraction of the staking tokens which are currently bonded +func (k Keeper) BondedRatio(ctx sdk.Context) sdk.Dec { + bondedPool := k.GetBondedPool(ctx) + + stakeSupply := k.StakingTokenSupply(ctx) + if stakeSupply.IsPositive() { + return bondedPool.GetCoins().AmountOf(k.BondDenom(ctx)).ToDec().QuoInt(stakeSupply) + } + return sdk.ZeroDec() +} diff --git a/x/staking/keeper/querier.go b/x/staking/keeper/querier.go new file mode 100644 index 0000000..4cc4be1 --- /dev/null +++ b/x/staking/keeper/querier.go @@ -0,0 +1,405 @@ +package keeper + +import ( + "fmt" + "strings" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// creates a querier for staking REST endpoints +func NewQuerier(k Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case types.QueryValidators: + return queryValidators(ctx, req, k) + case types.QueryValidator: + return queryValidator(ctx, req, k) + case types.QueryValidatorDelegations: + return queryValidatorDelegations(ctx, req, k) + case types.QueryValidatorUnbondingDelegations: + return queryValidatorUnbondingDelegations(ctx, req, k) + case types.QueryDelegation: + return queryDelegation(ctx, req, k) + case types.QueryUnbondingDelegation: + return queryUnbondingDelegation(ctx, req, k) + case types.QueryDelegatorDelegations: + return queryDelegatorDelegations(ctx, req, k) + case types.QueryDelegatorUnbondingDelegations: + return queryDelegatorUnbondingDelegations(ctx, req, k) + case types.QueryRedelegations: + return queryRedelegations(ctx, req, k) + case types.QueryDelegatorValidators: + return queryDelegatorValidators(ctx, req, k) + case types.QueryDelegatorValidator: + return queryDelegatorValidator(ctx, req, k) + case types.QueryPool: + return queryPool(ctx, k) + case types.QueryParameters: + return queryParameters(ctx, k) + default: + return nil, sdk.ErrUnknownRequest("unknown staking query endpoint") + } + } +} + +func queryValidators(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryValidatorsParams + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } + + validators := k.GetAllValidators(ctx) + filteredVals := make([]types.Validator, 0, len(validators)) + + for _, val := range validators { + if strings.ToLower(val.GetStatus().String()) == strings.ToLower(params.Status) { + filteredVals = append(filteredVals, val) + } + } + + start, end := client.Paginate(len(filteredVals), params.Page, params.Limit, int(k.GetParams(ctx).MaxValidators)) + if start < 0 || end < 0 { + filteredVals = []types.Validator{} + } else { + filteredVals = filteredVals[start:end] + } + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, filteredVals) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to JSON marshal result: %s", err.Error())) + } + + return res, nil +} + +func queryValidator(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryValidatorParams + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } + + validator, found := k.GetValidator(ctx, params.ValidatorAddr) + if !found { + return nil, types.ErrNoValidatorFound(types.DefaultCodespace) + } + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, validator) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return res, nil +} + +func queryValidatorDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryValidatorParams + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } + + delegations := k.GetValidatorDelegations(ctx, params.ValidatorAddr) + delegationResps, err := delegationsToDelegationResponses(ctx, k, delegations) + if err != nil { + return nil, sdk.ErrInternal(err.Error()) + } + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, delegationResps) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", err.Error())) + } + + return res, nil +} + +func queryValidatorUnbondingDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryValidatorParams + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } + + unbonds := k.GetUnbondingDelegationsFromValidator(ctx, params.ValidatorAddr) + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, unbonds) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return res, nil +} + +func queryDelegatorDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryDelegatorParams + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } + + delegations := k.GetAllDelegatorDelegations(ctx, params.DelegatorAddr) + delegationResps, err := delegationsToDelegationResponses(ctx, k, delegations) + if err != nil { + return nil, sdk.ErrInternal(err.Error()) + } + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, delegationResps) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", err.Error())) + } + + return res, nil +} + +func queryDelegatorUnbondingDelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryDelegatorParams + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } + + unbondingDelegations := k.GetAllUnbondingDelegations(ctx, params.DelegatorAddr) + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, unbondingDelegations) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return res, nil +} + +func queryDelegatorValidators(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryDelegatorParams + + stakingParams := k.GetParams(ctx) + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } + + validators := k.GetDelegatorValidators(ctx, params.DelegatorAddr, stakingParams.MaxValidators) + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, validators) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return res, nil +} + +func queryDelegatorValidator(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryBondsParams + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } + + validator, err := k.GetDelegatorValidator(ctx, params.DelegatorAddr, params.ValidatorAddr) + if err != nil { + return nil, sdk.ErrInternal(err.Error()) + } + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, validator) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return res, nil +} + +func queryDelegation(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryBondsParams + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } + + delegation, found := k.GetDelegation(ctx, params.DelegatorAddr, params.ValidatorAddr) + if !found { + return nil, types.ErrNoDelegation(types.DefaultCodespace) + } + + delegationResp, err := delegationToDelegationResponse(ctx, k, delegation) + if err != nil { + return nil, sdk.ErrInternal(err.Error()) + } + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, delegationResp) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", err.Error())) + } + + return res, nil +} + +func queryUnbondingDelegation(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryBondsParams + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + } + + unbond, found := k.GetUnbondingDelegation(ctx, params.DelegatorAddr, params.ValidatorAddr) + if !found { + return nil, types.ErrNoUnbondingDelegation(types.DefaultCodespace) + } + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, unbond) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return res, nil +} + +func queryRedelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params types.QueryRedelegationParams + + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(string(req.Data)) + } + + var redels []types.Redelegation + + if !params.DelegatorAddr.Empty() && !params.SrcValidatorAddr.Empty() && !params.DstValidatorAddr.Empty() { + redel, found := k.GetRedelegation(ctx, params.DelegatorAddr, params.SrcValidatorAddr, params.DstValidatorAddr) + if !found { + return nil, types.ErrNoRedelegation(types.DefaultCodespace) + } + + redels = []types.Redelegation{redel} + } else if params.DelegatorAddr.Empty() && !params.SrcValidatorAddr.Empty() && params.DstValidatorAddr.Empty() { + redels = k.GetRedelegationsFromSrcValidator(ctx, params.SrcValidatorAddr) + } else { + redels = k.GetAllRedelegations(ctx, params.DelegatorAddr, params.SrcValidatorAddr, params.DstValidatorAddr) + } + + redelResponses, err := redelegationsToRedelegationResponses(ctx, k, redels) + if err != nil { + return nil, sdk.ErrInternal(err.Error()) + } + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, redelResponses) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal result to JSON", err.Error())) + } + + return res, nil +} + +func queryPool(ctx sdk.Context, k Keeper) ([]byte, sdk.Error) { + bondDenom := k.BondDenom(ctx) + bondedPool := k.GetBondedPool(ctx) + notBondedPool := k.GetNotBondedPool(ctx) + if bondedPool == nil || notBondedPool == nil { + return nil, sdk.ErrInternal("pool accounts haven't been set") + } + + pool := types.NewPool( + notBondedPool.GetCoins().AmountOf(bondDenom), + bondedPool.GetCoins().AmountOf(bondDenom), + ) + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, pool) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return res, nil +} + +func queryParameters(ctx sdk.Context, k Keeper) ([]byte, sdk.Error) { + params := k.GetParams(ctx) + + res, err := codec.MarshalJSONIndent(types.ModuleCdc, params) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return res, nil +} + +//______________________________________________________ +// util + +func delegationToDelegationResponse(ctx sdk.Context, k Keeper, del types.Delegation) (types.DelegationResponse, sdk.Error) { + val, found := k.GetValidator(ctx, del.ValidatorAddress) + if !found { + return types.DelegationResponse{}, types.ErrNoValidatorFound(types.DefaultCodespace) + } + + return types.NewDelegationResp( + del.DelegatorAddress, + del.ValidatorAddress, + del.Shares, + val.TokensFromShares(del.Shares).TruncateInt(), + ), nil +} + +func delegationsToDelegationResponses( + ctx sdk.Context, k Keeper, delegations types.Delegations, +) (types.DelegationResponses, sdk.Error) { + + resp := make(types.DelegationResponses, len(delegations), len(delegations)) + for i, del := range delegations { + delResp, err := delegationToDelegationResponse(ctx, k, del) + if err != nil { + return nil, err + } + + resp[i] = delResp + } + + return resp, nil +} + +func redelegationsToRedelegationResponses( + ctx sdk.Context, k Keeper, redels types.Redelegations, +) (types.RedelegationResponses, sdk.Error) { + + resp := make(types.RedelegationResponses, len(redels), len(redels)) + for i, redel := range redels { + val, found := k.GetValidator(ctx, redel.ValidatorDstAddress) + if !found { + return nil, types.ErrNoValidatorFound(types.DefaultCodespace) + } + + entryResponses := make([]types.RedelegationEntryResponse, len(redel.Entries), len(redel.Entries)) + for j, entry := range redel.Entries { + entryResponses[j] = types.NewRedelegationEntryResponse( + entry.CreationHeight, + entry.CompletionTime, + entry.SharesDst, + entry.InitialBalance, + val.TokensFromShares(entry.SharesDst).TruncateInt(), + ) + } + + resp[i] = types.NewRedelegationResponse( + redel.DelegatorAddress, + redel.ValidatorSrcAddress, + redel.ValidatorDstAddress, + entryResponses, + ) + } + + return resp, nil +} diff --git a/x/staking/keeper/querier_test.go b/x/staking/keeper/querier_test.go new file mode 100644 index 0000000..039002e --- /dev/null +++ b/x/staking/keeper/querier_test.go @@ -0,0 +1,553 @@ +package keeper + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +var ( + addrAcc1, addrAcc2 = Addrs[0], Addrs[1] + addrVal1, addrVal2 = sdk.ValAddress(Addrs[0]), sdk.ValAddress(Addrs[1]) + pk1, pk2 = PKs[0], PKs[1] +) + +func TestNewQuerier(t *testing.T) { + cdc := codec.New() + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + // Create Validators + amts := []sdk.Int{sdk.NewInt(9), sdk.NewInt(8)} + var validators [2]types.Validator + for i, amt := range amts { + validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + validators[i], _ = validators[i].AddTokensFromDel(amt) + keeper.SetValidator(ctx, validators[i]) + keeper.SetValidatorByPowerIndex(ctx, validators[i]) + } + + query := abci.RequestQuery{ + Path: "", + Data: []byte{}, + } + + querier := NewQuerier(keeper) + + bz, err := querier(ctx, []string{"other"}, query) + require.NotNil(t, err) + require.Nil(t, bz) + + _, err = querier(ctx, []string{"pool"}, query) + require.Nil(t, err) + + _, err = querier(ctx, []string{"parameters"}, query) + require.Nil(t, err) + + queryValParams := types.NewQueryValidatorParams(addrVal1) + bz, errRes := cdc.MarshalJSON(queryValParams) + require.Nil(t, errRes) + + query.Path = "/custom/staking/validator" + query.Data = bz + + _, err = querier(ctx, []string{"validator"}, query) + require.Nil(t, err) + + _, err = querier(ctx, []string{"validatorDelegations"}, query) + require.Nil(t, err) + + _, err = querier(ctx, []string{"validatorUnbondingDelegations"}, query) + require.Nil(t, err) + + queryDelParams := types.NewQueryDelegatorParams(addrAcc2) + bz, errRes = cdc.MarshalJSON(queryDelParams) + require.Nil(t, errRes) + + query.Path = "/custom/staking/validator" + query.Data = bz + + _, err = querier(ctx, []string{"delegatorDelegations"}, query) + require.Nil(t, err) + + _, err = querier(ctx, []string{"delegatorUnbondingDelegations"}, query) + require.Nil(t, err) + + _, err = querier(ctx, []string{"delegatorValidators"}, query) + require.Nil(t, err) + + bz, errRes = cdc.MarshalJSON(types.NewQueryRedelegationParams(nil, nil, nil)) + require.Nil(t, errRes) + query.Data = bz + + _, err = querier(ctx, []string{"redelegations"}, query) + require.Nil(t, err) +} + +func TestQueryParametersPool(t *testing.T) { + cdc := codec.New() + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + bondDenom := keeper.BondDenom(ctx) + + res, err := queryParameters(ctx, keeper) + require.Nil(t, err) + + var params types.Params + errRes := cdc.UnmarshalJSON(res, ¶ms) + require.Nil(t, errRes) + require.Equal(t, keeper.GetParams(ctx), params) + + res, err = queryPool(ctx, keeper) + require.Nil(t, err) + + var pool types.Pool + bondedPool := keeper.GetBondedPool(ctx) + notBondedPool := keeper.GetNotBondedPool(ctx) + errRes = cdc.UnmarshalJSON(res, &pool) + require.Nil(t, errRes) + require.Equal(t, bondedPool.GetCoins().AmountOf(bondDenom), pool.BondedTokens) + require.Equal(t, notBondedPool.GetCoins().AmountOf(bondDenom), pool.NotBondedTokens) +} + +func TestQueryValidators(t *testing.T) { + cdc := codec.New() + ctx, _, keeper, _ := CreateTestInput(t, false, 10000) + params := keeper.GetParams(ctx) + + // Create Validators + amts := []sdk.Int{sdk.NewInt(9), sdk.NewInt(8), sdk.NewInt(7)} + status := []sdk.BondStatus{sdk.Bonded, sdk.Unbonded, sdk.Unbonding} + var validators [3]types.Validator + for i, amt := range amts { + validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + validators[i], _ = validators[i].AddTokensFromDel(amt) + validators[i] = validators[i].UpdateStatus(status[i]) + } + + keeper.SetValidator(ctx, validators[0]) + keeper.SetValidator(ctx, validators[1]) + keeper.SetValidator(ctx, validators[2]) + + // Query Validators + queriedValidators := keeper.GetValidators(ctx, params.MaxValidators) + + for i, s := range status { + queryValsParams := types.NewQueryValidatorsParams(1, int(params.MaxValidators), s.String()) + bz, err := cdc.MarshalJSON(queryValsParams) + require.Nil(t, err) + + req := abci.RequestQuery{ + Path: fmt.Sprintf("/custom/%s/%s", types.QuerierRoute, types.QueryValidators), + Data: bz, + } + + res, err := queryValidators(ctx, req, keeper) + require.Nil(t, err) + + var validatorsResp []types.Validator + err = cdc.UnmarshalJSON(res, &validatorsResp) + require.Nil(t, err) + + require.Equal(t, 1, len(validatorsResp)) + require.ElementsMatch(t, validators[i].OperatorAddress, validatorsResp[0].OperatorAddress) + + } + + // Query each validator + queryParams := types.NewQueryValidatorParams(addrVal1) + bz, err := cdc.MarshalJSON(queryParams) + require.Nil(t, err) + + query := abci.RequestQuery{ + Path: "/custom/staking/validator", + Data: bz, + } + res, err := queryValidator(ctx, query, keeper) + require.Nil(t, err) + + var validator types.Validator + err = cdc.UnmarshalJSON(res, &validator) + require.Nil(t, err) + + require.Equal(t, queriedValidators[0], validator) +} + +func TestQueryDelegation(t *testing.T) { + cdc := codec.New() + ctx, _, keeper, _ := CreateTestInput(t, false, 10000) + params := keeper.GetParams(ctx) + + // Create Validators and Delegation + val1 := types.NewValidator(addrVal1, pk1, types.Description{}) + keeper.SetValidator(ctx, val1) + keeper.SetValidatorByPowerIndex(ctx, val1) + + val2 := types.NewValidator(addrVal2, pk2, types.Description{}) + keeper.SetValidator(ctx, val2) + keeper.SetValidatorByPowerIndex(ctx, val2) + + delTokens := sdk.TokensFromConsensusPower(20) + keeper.Delegate(ctx, addrAcc2, delTokens, sdk.Unbonded, val1, true) + + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + // Query Delegator bonded validators + queryParams := types.NewQueryDelegatorParams(addrAcc2) + bz, errRes := cdc.MarshalJSON(queryParams) + require.Nil(t, errRes) + + query := abci.RequestQuery{ + Path: "/custom/staking/delegatorValidators", + Data: bz, + } + + delValidators := keeper.GetDelegatorValidators(ctx, addrAcc2, params.MaxValidators) + + res, err := queryDelegatorValidators(ctx, query, keeper) + require.Nil(t, err) + + var validatorsResp []types.Validator + errRes = cdc.UnmarshalJSON(res, &validatorsResp) + require.Nil(t, errRes) + + require.Equal(t, len(delValidators), len(validatorsResp)) + require.ElementsMatch(t, delValidators, validatorsResp) + + // error unknown request + query.Data = bz[:len(bz)-1] + + _, err = queryDelegatorValidators(ctx, query, keeper) + require.NotNil(t, err) + + // Query bonded validator + queryBondParams := types.NewQueryBondsParams(addrAcc2, addrVal1) + bz, errRes = cdc.MarshalJSON(queryBondParams) + require.Nil(t, errRes) + + query = abci.RequestQuery{ + Path: "/custom/staking/delegatorValidator", + Data: bz, + } + + res, err = queryDelegatorValidator(ctx, query, keeper) + require.Nil(t, err) + + var validator types.Validator + errRes = cdc.UnmarshalJSON(res, &validator) + require.Nil(t, errRes) + + require.Equal(t, delValidators[0], validator) + + // error unknown request + query.Data = bz[:len(bz)-1] + + _, err = queryDelegatorValidator(ctx, query, keeper) + require.NotNil(t, err) + + // Query delegation + + query = abci.RequestQuery{ + Path: "/custom/staking/delegation", + Data: bz, + } + + delegation, found := keeper.GetDelegation(ctx, addrAcc2, addrVal1) + require.True(t, found) + + res, err = queryDelegation(ctx, query, keeper) + require.Nil(t, err) + + var delegationRes types.DelegationResponse + errRes = cdc.UnmarshalJSON(res, &delegationRes) + require.Nil(t, errRes) + + require.Equal(t, delegation.ValidatorAddress, delegationRes.ValidatorAddress) + require.Equal(t, delegation.DelegatorAddress, delegationRes.DelegatorAddress) + require.Equal(t, delegation.Shares.TruncateInt(), delegationRes.Balance) + + // Query Delegator Delegations + query = abci.RequestQuery{ + Path: "/custom/staking/delegatorDelegations", + Data: bz, + } + + res, err = queryDelegatorDelegations(ctx, query, keeper) + require.Nil(t, err) + + var delegatorDelegations types.DelegationResponses + errRes = cdc.UnmarshalJSON(res, &delegatorDelegations) + require.Nil(t, errRes) + require.Len(t, delegatorDelegations, 1) + require.Equal(t, delegation.ValidatorAddress, delegatorDelegations[0].ValidatorAddress) + require.Equal(t, delegation.DelegatorAddress, delegatorDelegations[0].DelegatorAddress) + require.Equal(t, delegation.Shares.TruncateInt(), delegatorDelegations[0].Balance) + + // error unknown request + query.Data = bz[:len(bz)-1] + + _, err = queryDelegation(ctx, query, keeper) + require.NotNil(t, err) + + // Query validator delegations + + bz, errRes = cdc.MarshalJSON(types.NewQueryValidatorParams(addrVal1)) + require.Nil(t, errRes) + + query = abci.RequestQuery{ + Path: "custom/staking/validatorDelegations", + Data: bz, + } + + res, err = queryValidatorDelegations(ctx, query, keeper) + require.Nil(t, err) + + var delegationsRes types.DelegationResponses + errRes = cdc.UnmarshalJSON(res, &delegationsRes) + require.Nil(t, errRes) + require.Len(t, delegatorDelegations, 1) + require.Equal(t, delegation.ValidatorAddress, delegationsRes[0].ValidatorAddress) + require.Equal(t, delegation.DelegatorAddress, delegationsRes[0].DelegatorAddress) + require.Equal(t, delegation.Shares.TruncateInt(), delegationsRes[0].Balance) + + // Query unbonging delegation + unbondingTokens := sdk.TokensFromConsensusPower(10) + _, err = keeper.Undelegate(ctx, addrAcc2, val1.OperatorAddress, unbondingTokens.ToDec()) + require.Nil(t, err) + + queryBondParams = types.NewQueryBondsParams(addrAcc2, addrVal1) + bz, errRes = cdc.MarshalJSON(queryBondParams) + require.Nil(t, errRes) + + query = abci.RequestQuery{ + Path: "/custom/staking/unbondingDelegation", + Data: bz, + } + + unbond, found := keeper.GetUnbondingDelegation(ctx, addrAcc2, addrVal1) + require.True(t, found) + + res, err = queryUnbondingDelegation(ctx, query, keeper) + require.Nil(t, err) + + var unbondRes types.UnbondingDelegation + errRes = cdc.UnmarshalJSON(res, &unbondRes) + require.Nil(t, errRes) + + require.Equal(t, unbond, unbondRes) + + // error unknown request + query.Data = bz[:len(bz)-1] + + _, err = queryUnbondingDelegation(ctx, query, keeper) + require.NotNil(t, err) + + // Query Delegator Delegations + + query = abci.RequestQuery{ + Path: "/custom/staking/delegatorUnbondingDelegations", + Data: bz, + } + + res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper) + require.Nil(t, err) + + var delegatorUbds []types.UnbondingDelegation + errRes = cdc.UnmarshalJSON(res, &delegatorUbds) + require.Nil(t, errRes) + require.Equal(t, unbond, delegatorUbds[0]) + + // error unknown request + query.Data = bz[:len(bz)-1] + + _, err = queryDelegatorUnbondingDelegations(ctx, query, keeper) + require.NotNil(t, err) + + // Query redelegation + redelegationTokens := sdk.TokensFromConsensusPower(10) + _, err = keeper.BeginRedelegation(ctx, addrAcc2, val1.OperatorAddress, + val2.OperatorAddress, redelegationTokens.ToDec()) + require.Nil(t, err) + redel, found := keeper.GetRedelegation(ctx, addrAcc2, val1.OperatorAddress, val2.OperatorAddress) + require.True(t, found) + + bz, errRes = cdc.MarshalJSON(types.NewQueryRedelegationParams(addrAcc2, val1.OperatorAddress, val2.OperatorAddress)) + require.Nil(t, errRes) + + query = abci.RequestQuery{ + Path: "/custom/staking/redelegations", + Data: bz, + } + + res, err = queryRedelegations(ctx, query, keeper) + require.Nil(t, err) + + var redelRes types.RedelegationResponses + errRes = cdc.UnmarshalJSON(res, &redelRes) + require.Nil(t, errRes) + require.Len(t, redelRes, 1) + require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress) + require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress) + require.Equal(t, redel.ValidatorDstAddress, redelRes[0].ValidatorDstAddress) + require.Len(t, redel.Entries, len(redelRes[0].Entries)) +} + +func TestQueryRedelegations(t *testing.T) { + cdc := codec.New() + ctx, _, keeper, _ := CreateTestInput(t, false, 10000) + + // Create Validators and Delegation + val1 := types.NewValidator(addrVal1, pk1, types.Description{}) + val2 := types.NewValidator(addrVal2, pk2, types.Description{}) + keeper.SetValidator(ctx, val1) + keeper.SetValidator(ctx, val2) + + delAmount := sdk.TokensFromConsensusPower(100) + keeper.Delegate(ctx, addrAcc2, delAmount, sdk.Unbonded, val1, true) + _ = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + rdAmount := sdk.TokensFromConsensusPower(20) + keeper.BeginRedelegation(ctx, addrAcc2, val1.GetOperator(), val2.GetOperator(), rdAmount.ToDec()) + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + redel, found := keeper.GetRedelegation(ctx, addrAcc2, val1.OperatorAddress, val2.OperatorAddress) + require.True(t, found) + + // delegator redelegations + queryDelegatorParams := types.NewQueryDelegatorParams(addrAcc2) + bz, errRes := cdc.MarshalJSON(queryDelegatorParams) + require.Nil(t, errRes) + + query := abci.RequestQuery{ + Path: "/custom/staking/redelegations", + Data: bz, + } + + res, err := queryRedelegations(ctx, query, keeper) + require.Nil(t, err) + + var redelRes types.RedelegationResponses + errRes = cdc.UnmarshalJSON(res, &redelRes) + require.Nil(t, errRes) + require.Len(t, redelRes, 1) + require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress) + require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress) + require.Equal(t, redel.ValidatorDstAddress, redelRes[0].ValidatorDstAddress) + require.Len(t, redel.Entries, len(redelRes[0].Entries)) + + // validator redelegations + queryValidatorParams := types.NewQueryValidatorParams(val1.GetOperator()) + bz, errRes = cdc.MarshalJSON(queryValidatorParams) + require.Nil(t, errRes) + + query = abci.RequestQuery{ + Path: "/custom/staking/redelegations", + Data: bz, + } + + res, err = queryRedelegations(ctx, query, keeper) + require.Nil(t, err) + + errRes = cdc.UnmarshalJSON(res, &redelRes) + require.Nil(t, errRes) + require.Len(t, redelRes, 1) + require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress) + require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress) + require.Equal(t, redel.ValidatorDstAddress, redelRes[0].ValidatorDstAddress) + require.Len(t, redel.Entries, len(redelRes[0].Entries)) +} + +func TestQueryUnbondingDelegation(t *testing.T) { + cdc := codec.New() + ctx, _, keeper, _ := CreateTestInput(t, false, 10000) + + // Create Validators and Delegation + val1 := types.NewValidator(addrVal1, pk1, types.Description{}) + keeper.SetValidator(ctx, val1) + + // delegate + delAmount := sdk.TokensFromConsensusPower(100) + _, err := keeper.Delegate(ctx, addrAcc1, delAmount, sdk.Unbonded, val1, true) + require.NoError(t, err) + _ = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + // undelegate + undelAmount := sdk.TokensFromConsensusPower(20) + _, err = keeper.Undelegate(ctx, addrAcc1, val1.GetOperator(), undelAmount.ToDec()) + require.NoError(t, err) + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + _, found := keeper.GetUnbondingDelegation(ctx, addrAcc1, val1.OperatorAddress) + require.True(t, found) + + // + // found: query unbonding delegation by delegator and validator + // + queryValidatorParams := types.NewQueryBondsParams(addrAcc1, val1.GetOperator()) + bz, errRes := cdc.MarshalJSON(queryValidatorParams) + require.Nil(t, errRes) + query := abci.RequestQuery{ + Path: "/custom/staking/unbondingDelegation", + Data: bz, + } + res, err := queryUnbondingDelegation(ctx, query, keeper) + require.Nil(t, err) + require.NotNil(t, res) + var ubDel types.UnbondingDelegation + require.NoError(t, cdc.UnmarshalJSON(res, &ubDel)) + require.Equal(t, addrAcc1, ubDel.DelegatorAddress) + require.Equal(t, val1.OperatorAddress, ubDel.ValidatorAddress) + require.Equal(t, 1, len(ubDel.Entries)) + + // + // not found: query unbonding delegation by delegator and validator + // + queryValidatorParams = types.NewQueryBondsParams(addrAcc2, val1.GetOperator()) + bz, errRes = cdc.MarshalJSON(queryValidatorParams) + require.Nil(t, errRes) + query = abci.RequestQuery{ + Path: "/custom/staking/unbondingDelegation", + Data: bz, + } + res, err = queryUnbondingDelegation(ctx, query, keeper) + require.NotNil(t, err) + + // + // found: query unbonding delegation by delegator and validator + // + queryDelegatorParams := types.NewQueryDelegatorParams(addrAcc1) + bz, errRes = cdc.MarshalJSON(queryDelegatorParams) + require.Nil(t, errRes) + query = abci.RequestQuery{ + Path: "/custom/staking/delegatorUnbondingDelegations", + Data: bz, + } + res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper) + require.Nil(t, err) + require.NotNil(t, res) + var ubDels []types.UnbondingDelegation + require.NoError(t, cdc.UnmarshalJSON(res, &ubDels)) + require.Equal(t, 1, len(ubDels)) + require.Equal(t, addrAcc1, ubDels[0].DelegatorAddress) + require.Equal(t, val1.OperatorAddress, ubDels[0].ValidatorAddress) + + // + // not found: query unbonding delegation by delegator and validator + // + queryDelegatorParams = types.NewQueryDelegatorParams(addrAcc2) + bz, errRes = cdc.MarshalJSON(queryDelegatorParams) + require.Nil(t, errRes) + query = abci.RequestQuery{ + Path: "/custom/staking/delegatorUnbondingDelegations", + Data: bz, + } + res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper) + require.Nil(t, err) + require.NotNil(t, res) + require.NoError(t, cdc.UnmarshalJSON(res, &ubDels)) + require.Equal(t, 0, len(ubDels)) +} diff --git a/x/staking/keeper/query_utils.go b/x/staking/keeper/query_utils.go new file mode 100644 index 0000000..6bde68f --- /dev/null +++ b/x/staking/keeper/query_utils.go @@ -0,0 +1,111 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// Return all validators that a delegator is bonded to. If maxRetrieve is supplied, the respective amount will be returned. +func (k Keeper) GetDelegatorValidators(ctx sdk.Context, delegatorAddr sdk.AccAddress, + maxRetrieve uint16) (validators []types.Validator) { + validators = make([]types.Validator, maxRetrieve) + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := types.GetDelegationsKey(delegatorAddr) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) // smallest to largest + defer iterator.Close() + + i := 0 + for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() { + delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value()) + + validator, found := k.GetValidator(ctx, delegation.ValidatorAddress) + if !found { + panic(types.ErrNoValidatorFound(types.DefaultCodespace)) + } + validators[i] = validator + i++ + } + return validators[:i] // trim +} + +// return a validator that a delegator is bonded to +func (k Keeper) GetDelegatorValidator(ctx sdk.Context, delegatorAddr sdk.AccAddress, + validatorAddr sdk.ValAddress) (validator types.Validator, err sdk.Error) { + + delegation, found := k.GetDelegation(ctx, delegatorAddr, validatorAddr) + if !found { + return validator, types.ErrNoDelegation(types.DefaultCodespace) + } + + validator, found = k.GetValidator(ctx, delegation.ValidatorAddress) + if !found { + panic(types.ErrNoValidatorFound(types.DefaultCodespace)) + } + return +} + +//_____________________________________________________________________________________ + +// return all delegations for a delegator +func (k Keeper) GetAllDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress) []types.Delegation { + delegations := make([]types.Delegation, 0) + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := types.GetDelegationsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + defer iterator.Close() + + i := 0 + for ; iterator.Valid(); iterator.Next() { + delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Value()) + delegations = append(delegations, delegation) + i++ + } + + return delegations +} + +// return all unbonding-delegations for a delegator +func (k Keeper) GetAllUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress) []types.UnbondingDelegation { + unbondingDelegations := make([]types.UnbondingDelegation, 0) + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := types.GetUBDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) // smallest to largest + defer iterator.Close() + + for i := 0; iterator.Valid(); iterator.Next() { + unbondingDelegation := types.MustUnmarshalUBD(k.cdc, iterator.Value()) + unbondingDelegations = append(unbondingDelegations, unbondingDelegation) + i++ + } + + return unbondingDelegations +} + +// return all redelegations for a delegator +func (k Keeper) GetAllRedelegations(ctx sdk.Context, delegator sdk.AccAddress, + srcValAddress, dstValAddress sdk.ValAddress) ( + redelegations []types.Redelegation) { + + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := types.GetREDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) // smallest to largest + defer iterator.Close() + + srcValFilter := !(srcValAddress.Empty()) + dstValFilter := !(dstValAddress.Empty()) + + for ; iterator.Valid(); iterator.Next() { + redelegation := types.MustUnmarshalRED(k.cdc, iterator.Value()) + if srcValFilter && !(srcValAddress.Equals(redelegation.ValidatorSrcAddress)) { + continue + } + if dstValFilter && !(dstValAddress.Equals(redelegation.ValidatorDstAddress)) { + continue + } + redelegations = append(redelegations, redelegation) + } + return redelegations +} diff --git a/x/staking/keeper/slash.go b/x/staking/keeper/slash.go new file mode 100644 index 0000000..6e1c540 --- /dev/null +++ b/x/staking/keeper/slash.go @@ -0,0 +1,294 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// Slash a validator for an infraction committed at a known height +// Find the contributing stake at that height and burn the specified slashFactor +// of it, updating unbonding delegations & redelegations appropriately +// +// CONTRACT: +// slashFactor is non-negative +// CONTRACT: +// Infraction was committed equal to or less than an unbonding period in the past, +// so all unbonding delegations and redelegations from that height are stored +// CONTRACT: +// Slash will not slash unbonded validators (for the above reason) +// CONTRACT: +// Infraction was committed at the current height or at a past height, +// not at a height in the future +func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeight int64, power int64, slashFactor sdk.Dec) { + logger := k.Logger(ctx) + + if slashFactor.LT(sdk.ZeroDec()) { + panic(fmt.Errorf("attempted to slash with a negative slash factor: %v", slashFactor)) + } + + // Amount of slashing = slash slashFactor * power at time of infraction + amount := sdk.TokensFromConsensusPower(power) + slashAmountDec := amount.ToDec().Mul(slashFactor) + slashAmount := slashAmountDec.TruncateInt() + + // ref https://github.com/cosmos/cosmos-sdk/issues/1348 + + validator, found := k.GetValidatorByConsAddr(ctx, consAddr) + if !found { + // If not found, the validator must have been overslashed and removed - so we don't need to do anything + // NOTE: Correctness dependent on invariant that unbonding delegations / redelegations must also have been completely + // slashed in this case - which we don't explicitly check, but should be true. + // Log the slash attempt for future reference (maybe we should tag it too) + logger.Error(fmt.Sprintf( + "WARNING: Ignored attempt to slash a nonexistent validator with address %s, we recommend you investigate immediately", + consAddr)) + return + } + + // should not be slashing an unbonded validator + if validator.IsUnbonded() { + panic(fmt.Sprintf("should not be slashing unbonded validator: %s", validator.GetOperator())) + } + + operatorAddress := validator.GetOperator() + + // call the before-modification hook + k.BeforeValidatorModified(ctx, operatorAddress) + + // Track remaining slash amount for the validator + // This will decrease when we slash unbondings and + // redelegations, as that stake has since unbonded + remainingSlashAmount := slashAmount + + switch { + case infractionHeight > ctx.BlockHeight(): + + // Can't slash infractions in the future + panic(fmt.Sprintf( + "impossible attempt to slash future infraction at height %d but we are at height %d", + infractionHeight, ctx.BlockHeight())) + + case infractionHeight == ctx.BlockHeight(): + + // Special-case slash at current height for efficiency - we don't need to look through unbonding delegations or redelegations + logger.Info(fmt.Sprintf( + "slashing at current height %d, not scanning unbonding delegations & redelegations", + infractionHeight)) + + case infractionHeight < ctx.BlockHeight(): + + // Iterate through unbonding delegations from slashed validator + unbondingDelegations := k.GetUnbondingDelegationsFromValidator(ctx, operatorAddress) + for _, unbondingDelegation := range unbondingDelegations { + amountSlashed := k.slashUnbondingDelegation(ctx, unbondingDelegation, infractionHeight, slashFactor) + if amountSlashed.IsZero() { + continue + } + remainingSlashAmount = remainingSlashAmount.Sub(amountSlashed) + } + + // Iterate through redelegations from slashed source validator + redelegations := k.GetRedelegationsFromSrcValidator(ctx, operatorAddress) + for _, redelegation := range redelegations { + amountSlashed := k.slashRedelegation(ctx, validator, redelegation, infractionHeight, slashFactor) + if amountSlashed.IsZero() { + continue + } + remainingSlashAmount = remainingSlashAmount.Sub(amountSlashed) + } + } + + // cannot decrease balance below zero + tokensToBurn := sdk.MinInt(remainingSlashAmount, validator.Tokens) + tokensToBurn = sdk.MaxInt(tokensToBurn, sdk.ZeroInt()) // defensive. + + // we need to calculate the *effective* slash fraction for distribution + if validator.Tokens.GT(sdk.ZeroInt()) { + effectiveFraction := tokensToBurn.ToDec().QuoRoundUp(validator.Tokens.ToDec()) + // possible if power has changed + if effectiveFraction.GT(sdk.OneDec()) { + effectiveFraction = sdk.OneDec() + } + // call the before-slashed hook + k.BeforeValidatorSlashed(ctx, operatorAddress, effectiveFraction) + } + + // Deduct from validator's bonded tokens and update the validator. + // Burn the slashed tokens from the pool account and decrease the total supply. + validator = k.RemoveValidatorTokens(ctx, validator, tokensToBurn) + + switch validator.GetStatus() { + case sdk.Bonded: + if err := k.burnBondedTokens(ctx, tokensToBurn); err != nil { + panic(err) + } + case sdk.Unbonding, sdk.Unbonded: + if err := k.burnNotBondedTokens(ctx, tokensToBurn); err != nil { + panic(err) + } + default: + panic("invalid validator status") + } + + // Log that a slash occurred! + logger.Info(fmt.Sprintf( + "validator %s slashed by slash factor of %s; burned %v tokens", + validator.GetOperator(), slashFactor.String(), tokensToBurn)) + + // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 + return +} + +// jail a validator +func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) { + validator := k.mustGetValidatorByConsAddr(ctx, consAddr) + k.jailValidator(ctx, validator) + logger := k.Logger(ctx) + logger.Info(fmt.Sprintf("validator %s jailed", consAddr)) + // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 + return +} + +// unjail a validator +func (k Keeper) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) { + validator := k.mustGetValidatorByConsAddr(ctx, consAddr) + k.unjailValidator(ctx, validator) + logger := k.Logger(ctx) + logger.Info(fmt.Sprintf("validator %s unjailed", consAddr)) + // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 + return +} + +// slash an unbonding delegation and update the pool +// return the amount that would have been slashed assuming +// the unbonding delegation had enough stake to slash +// (the amount actually slashed may be less if there's +// insufficient stake remaining) +func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation types.UnbondingDelegation, + infractionHeight int64, slashFactor sdk.Dec) (totalSlashAmount sdk.Int) { + + now := ctx.BlockHeader().Time + totalSlashAmount = sdk.ZeroInt() + burnedAmount := sdk.ZeroInt() + + // perform slashing on all entries within the unbonding delegation + for i, entry := range unbondingDelegation.Entries { + + // If unbonding started before this height, stake didn't contribute to infraction + if entry.CreationHeight < infractionHeight { + continue + } + + if entry.IsMature(now) { + // Unbonding delegation no longer eligible for slashing, skip it + continue + } + + // Calculate slash amount proportional to stake contributing to infraction + slashAmountDec := slashFactor.MulInt(entry.InitialBalance) + slashAmount := slashAmountDec.TruncateInt() + totalSlashAmount = totalSlashAmount.Add(slashAmount) + + // Don't slash more tokens than held + // Possible since the unbonding delegation may already + // have been slashed, and slash amounts are calculated + // according to stake held at time of infraction + unbondingSlashAmount := sdk.MinInt(slashAmount, entry.Balance) + + // Update unbonding delegation if necessary + if unbondingSlashAmount.IsZero() { + continue + } + + burnedAmount = burnedAmount.Add(unbondingSlashAmount) + entry.Balance = entry.Balance.Sub(unbondingSlashAmount) + unbondingDelegation.Entries[i] = entry + k.SetUnbondingDelegation(ctx, unbondingDelegation) + } + + if err := k.burnNotBondedTokens(ctx, burnedAmount); err != nil { + panic(err) + } + + return totalSlashAmount +} + +// slash a redelegation and update the pool +// return the amount that would have been slashed assuming +// the unbonding delegation had enough stake to slash +// (the amount actually slashed may be less if there's +// insufficient stake remaining) +// NOTE this is only slashing for prior infractions from the source validator +func (k Keeper) slashRedelegation(ctx sdk.Context, srcValidator types.Validator, redelegation types.Redelegation, + infractionHeight int64, slashFactor sdk.Dec) (totalSlashAmount sdk.Int) { + + now := ctx.BlockHeader().Time + totalSlashAmount = sdk.ZeroInt() + bondedBurnedAmount, notBondedBurnedAmount := sdk.ZeroInt(), sdk.ZeroInt() + + // perform slashing on all entries within the redelegation + for _, entry := range redelegation.Entries { + + // If redelegation started before this height, stake didn't contribute to infraction + if entry.CreationHeight < infractionHeight { + continue + } + + if entry.IsMature(now) { + // Redelegation no longer eligible for slashing, skip it + continue + } + + // Calculate slash amount proportional to stake contributing to infraction + slashAmountDec := slashFactor.MulInt(entry.InitialBalance) + slashAmount := slashAmountDec.TruncateInt() + totalSlashAmount = totalSlashAmount.Add(slashAmount) + + // Unbond from target validator + sharesToUnbond := slashFactor.Mul(entry.SharesDst) + if sharesToUnbond.IsZero() { + continue + } + delegation, found := k.GetDelegation(ctx, redelegation.DelegatorAddress, redelegation.ValidatorDstAddress) + if !found { + // If deleted, delegation has zero shares, and we can't unbond any more + continue + } + if sharesToUnbond.GT(delegation.Shares) { + sharesToUnbond = delegation.Shares + } + + tokensToBurn, err := k.unbond(ctx, redelegation.DelegatorAddress, redelegation.ValidatorDstAddress, sharesToUnbond) + if err != nil { + panic(fmt.Errorf("error unbonding delegator: %v", err)) + } + + dstValidator, found := k.GetValidator(ctx, redelegation.ValidatorDstAddress) + if !found { + panic("destination validator not found") + } + + // tokens of a redelegation currently live in the destination validator + // therefor we must burn tokens from the destination-validator's bonding status + switch { + case dstValidator.IsBonded(): + bondedBurnedAmount = bondedBurnedAmount.Add(tokensToBurn) + case dstValidator.IsUnbonded() || dstValidator.IsUnbonding(): + notBondedBurnedAmount = notBondedBurnedAmount.Add(tokensToBurn) + default: + panic("unknown validator status") + } + } + + if err := k.burnBondedTokens(ctx, bondedBurnedAmount); err != nil { + panic(err) + } + + if err := k.burnNotBondedTokens(ctx, notBondedBurnedAmount); err != nil { + panic(err) + } + + return totalSlashAmount +} diff --git a/x/staking/keeper/slash_test.go b/x/staking/keeper/slash_test.go new file mode 100644 index 0000000..bd46400 --- /dev/null +++ b/x/staking/keeper/slash_test.go @@ -0,0 +1,552 @@ +package keeper + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// TODO integrate with test_common.go helper (CreateTestInput) +// setup helper function - creates two validators +func setupHelper(t *testing.T, power int64) (sdk.Context, Keeper, types.Params) { + + // setup + ctx, _, keeper, _ := CreateTestInput(t, false, power) + params := keeper.GetParams(ctx) + numVals := int64(3) + amt := sdk.TokensFromConsensusPower(power) + bondedCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), amt.MulRaw(numVals))) + + bondedPool := keeper.GetBondedPool(ctx) + err := bondedPool.SetCoins(bondedCoins) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + // add numVals validators + for i := int64(0); i < numVals; i++ { + validator := types.NewValidator(addrVals[i], PKs[i], types.Description{}) + validator, _ = validator.AddTokensFromDel(amt) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + keeper.SetValidatorByConsAddr(ctx, validator) + } + + return ctx, keeper, params +} + +//_________________________________________________________________________________ + +// tests Jail, Unjail +func TestRevocation(t *testing.T) { + + // setup + ctx, keeper, _ := setupHelper(t, 10) + addr := addrVals[0] + consAddr := sdk.ConsAddress(PKs[0].Address()) + + // initial state + val, found := keeper.GetValidator(ctx, addr) + require.True(t, found) + require.False(t, val.IsJailed()) + + // test jail + keeper.Jail(ctx, consAddr) + val, found = keeper.GetValidator(ctx, addr) + require.True(t, found) + require.True(t, val.IsJailed()) + + // test unjail + keeper.Unjail(ctx, consAddr) + val, found = keeper.GetValidator(ctx, addr) + require.True(t, found) + require.False(t, val.IsJailed()) +} + +// tests slashUnbondingDelegation +func TestSlashUnbondingDelegation(t *testing.T) { + ctx, keeper, _ := setupHelper(t, 10) + fraction := sdk.NewDecWithPrec(5, 1) + + // set an unbonding delegation with expiration timestamp (beyond which the + // unbonding delegation shouldn't be slashed) + ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 0, + time.Unix(5, 0), sdk.NewInt(10)) + + keeper.SetUnbondingDelegation(ctx, ubd) + + // unbonding started prior to the infraction height, stakw didn't contribute + slashAmount := keeper.slashUnbondingDelegation(ctx, ubd, 1, fraction) + require.Equal(t, int64(0), slashAmount.Int64()) + + // after the expiration time, no longer eligible for slashing + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(10, 0)}) + keeper.SetUnbondingDelegation(ctx, ubd) + slashAmount = keeper.slashUnbondingDelegation(ctx, ubd, 0, fraction) + require.Equal(t, int64(0), slashAmount.Int64()) + + // test valid slash, before expiration timestamp and to which stake contributed + oldUnbondedPool := keeper.GetNotBondedPool(ctx) + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(0, 0)}) + keeper.SetUnbondingDelegation(ctx, ubd) + slashAmount = keeper.slashUnbondingDelegation(ctx, ubd, 0, fraction) + require.Equal(t, int64(5), slashAmount.Int64()) + ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + + // initial balance unchanged + require.Equal(t, sdk.NewInt(10), ubd.Entries[0].InitialBalance) + + // balance decreased + require.Equal(t, sdk.NewInt(5), ubd.Entries[0].Balance) + newUnbondedPool := keeper.GetNotBondedPool(ctx) + diffTokens := oldUnbondedPool.GetCoins().Sub(newUnbondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) + require.Equal(t, int64(5), diffTokens.Int64()) +} + +// tests slashRedelegation +func TestSlashRedelegation(t *testing.T) { + ctx, keeper, _ := setupHelper(t, 10) + fraction := sdk.NewDecWithPrec(5, 1) + + // add bonded tokens to pool for (re)delegations + startCoins := sdk.NewCoins(sdk.NewInt64Coin(keeper.BondDenom(ctx), 15)) + bondedPool := keeper.GetBondedPool(ctx) + err := bondedPool.SetCoins(bondedPool.GetCoins().Add(startCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + // set a redelegation with an expiration timestamp beyond which the + // redelegation shouldn't be slashed + rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0, + time.Unix(5, 0), sdk.NewInt(10), sdk.NewDec(10)) + + keeper.SetRedelegation(ctx, rd) + + // set the associated delegation + del := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDec(10)) + keeper.SetDelegation(ctx, del) + + // started redelegating prior to the current height, stake didn't contribute to infraction + validator, found := keeper.GetValidator(ctx, addrVals[1]) + require.True(t, found) + slashAmount := keeper.slashRedelegation(ctx, validator, rd, 1, fraction) + require.Equal(t, int64(0), slashAmount.Int64()) + + // after the expiration time, no longer eligible for slashing + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(10, 0)}) + keeper.SetRedelegation(ctx, rd) + validator, found = keeper.GetValidator(ctx, addrVals[1]) + require.True(t, found) + slashAmount = keeper.slashRedelegation(ctx, validator, rd, 0, fraction) + require.Equal(t, int64(0), slashAmount.Int64()) + + // test valid slash, before expiration timestamp and to which stake contributed + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(0, 0)}) + keeper.SetRedelegation(ctx, rd) + validator, found = keeper.GetValidator(ctx, addrVals[1]) + require.True(t, found) + slashAmount = keeper.slashRedelegation(ctx, validator, rd, 0, fraction) + require.Equal(t, int64(5), slashAmount.Int64()) + rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, rd.Entries, 1) + + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + // initialbalance unchanged + require.Equal(t, sdk.NewInt(10), rd.Entries[0].InitialBalance) + + // shares decreased + del, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[1]) + require.True(t, found) + require.Equal(t, int64(5), del.Shares.RoundInt64()) + + // pool bonded tokens should decrease + burnedCoins := sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), slashAmount)) + newBondedPool := keeper.GetBondedPool(ctx) + require.Equal(t, bondedPool.GetCoins().Sub(burnedCoins), newBondedPool.GetCoins()) +} + +// tests Slash at a future height (must panic) +func TestSlashAtFutureHeight(t *testing.T) { + ctx, keeper, _ := setupHelper(t, 10) + consAddr := sdk.ConsAddress(PKs[0].Address()) + fraction := sdk.NewDecWithPrec(5, 1) + require.Panics(t, func() { keeper.Slash(ctx, consAddr, 1, 10, fraction) }) +} + +// test slash at a negative height +// this just represents pre-genesis and should have the same effect as slashing at height 0 +func TestSlashAtNegativeHeight(t *testing.T) { + ctx, keeper, _ := setupHelper(t, 10) + consAddr := sdk.ConsAddress(PKs[0].Address()) + fraction := sdk.NewDecWithPrec(5, 1) + + oldBondedPool := keeper.GetBondedPool(ctx) + validator, found := keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + keeper.Slash(ctx, consAddr, -2, 10, fraction) + + // read updated state + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + newBondedPool := keeper.GetBondedPool(ctx) + + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates), "cons addr: %v, updates: %v", []byte(consAddr), updates) + + validator = keeper.mustGetValidator(ctx, validator.OperatorAddress) + // power decreased + require.Equal(t, int64(5), validator.GetConsensusPower()) + // pool bonded shares decreased + diffTokens := oldBondedPool.GetCoins().Sub(newBondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) + require.Equal(t, sdk.TokensFromConsensusPower(5), diffTokens) +} + +// tests Slash at the current height +func TestSlashValidatorAtCurrentHeight(t *testing.T) { + ctx, keeper, _ := setupHelper(t, 10) + consAddr := sdk.ConsAddress(PKs[0].Address()) + fraction := sdk.NewDecWithPrec(5, 1) + + oldBondedPool := keeper.GetBondedPool(ctx) + validator, found := keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + keeper.Slash(ctx, consAddr, ctx.BlockHeight(), 10, fraction) + + // read updated state + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + newBondedPool := keeper.GetBondedPool(ctx) + + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates), "cons addr: %v, updates: %v", []byte(consAddr), updates) + + validator = keeper.mustGetValidator(ctx, validator.OperatorAddress) + // power decreased + require.Equal(t, int64(5), validator.GetConsensusPower()) + // pool bonded shares decreased + diffTokens := oldBondedPool.GetCoins().Sub(newBondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) + require.Equal(t, sdk.TokensFromConsensusPower(5), diffTokens) +} + +// tests Slash at a previous height with an unbonding delegation +func TestSlashWithUnbondingDelegation(t *testing.T) { + ctx, keeper, _ := setupHelper(t, 10) + consAddr := sdk.ConsAddress(PKs[0].Address()) + fraction := sdk.NewDecWithPrec(5, 1) + + // set an unbonding delegation with expiration timestamp beyond which the + // unbonding delegation shouldn't be slashed + ubdTokens := sdk.TokensFromConsensusPower(4) + ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, + time.Unix(0, 0), ubdTokens) + keeper.SetUnbondingDelegation(ctx, ubd) + + // slash validator for the first time + ctx = ctx.WithBlockHeight(12) + oldBondedPool := keeper.GetBondedPool(ctx) + validator, found := keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + keeper.Slash(ctx, consAddr, 10, 10, fraction) + + // end block + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + + // read updating unbonding delegation + ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + // balance decreased + require.Equal(t, sdk.TokensFromConsensusPower(2), ubd.Entries[0].Balance) + // read updated pool + newBondedPool := keeper.GetBondedPool(ctx) + // bonded tokens burned + diffTokens := oldBondedPool.GetCoins().Sub(newBondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) + require.Equal(t, sdk.TokensFromConsensusPower(3), diffTokens) + // read updated validator + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + // power decreased by 3 - 6 stake originally bonded at the time of infraction + // was still bonded at the time of discovery and was slashed by half, 4 stake + // bonded at the time of discovery hadn't been bonded at the time of infraction + // and wasn't slashed + require.Equal(t, int64(7), validator.GetConsensusPower()) + + // slash validator again + ctx = ctx.WithBlockHeight(13) + keeper.Slash(ctx, consAddr, 9, 10, fraction) + ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + // balance decreased again + require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance) + // read updated pool + newBondedPool = keeper.GetBondedPool(ctx) + // bonded tokens burned again + diffTokens = oldBondedPool.GetCoins().Sub(newBondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) + require.Equal(t, sdk.TokensFromConsensusPower(6), diffTokens) + // read updated validator + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + // power decreased by 3 again + require.Equal(t, int64(4), validator.GetConsensusPower()) + + // slash validator again + // all originally bonded stake has been slashed, so this will have no effect + // on the unbonding delegation, but it will slash stake bonded since the infraction + // this may not be the desirable behaviour, ref https://github.com/cosmos/cosmos-sdk/issues/1440 + ctx = ctx.WithBlockHeight(13) + keeper.Slash(ctx, consAddr, 9, 10, fraction) + ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + // balance unchanged + require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance) + // read updated pool + newBondedPool = keeper.GetBondedPool(ctx) + // bonded tokens burned again + diffTokens = oldBondedPool.GetCoins().Sub(newBondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) + require.Equal(t, sdk.TokensFromConsensusPower(9), diffTokens) + // read updated validator + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + // power decreased by 3 again + require.Equal(t, int64(1), validator.GetConsensusPower()) + + // slash validator again + // all originally bonded stake has been slashed, so this will have no effect + // on the unbonding delegation, but it will slash stake bonded since the infraction + // this may not be the desirable behaviour, ref https://github.com/cosmos/cosmos-sdk/issues/1440 + ctx = ctx.WithBlockHeight(13) + keeper.Slash(ctx, consAddr, 9, 10, fraction) + ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + require.True(t, found) + require.Len(t, ubd.Entries, 1) + // balance unchanged + require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance) + // read updated pool + newBondedPool = keeper.GetBondedPool(ctx) + // just 1 bonded token burned again since that's all the validator now has + diffTokens = oldBondedPool.GetCoins().Sub(newBondedPool.GetCoins()).AmountOf(keeper.BondDenom(ctx)) + require.Equal(t, sdk.TokensFromConsensusPower(10), diffTokens) + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + // read updated validator + // power decreased by 1 again, validator is out of stake + // validator should be in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) +} + +// tests Slash at a previous height with a redelegation +func TestSlashWithRedelegation(t *testing.T) { + ctx, keeper, _ := setupHelper(t, 10) + consAddr := sdk.ConsAddress(PKs[0].Address()) + fraction := sdk.NewDecWithPrec(5, 1) + bondDenom := keeper.BondDenom(ctx) + + // set a redelegation + rdTokens := sdk.TokensFromConsensusPower(6) + rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11, + time.Unix(0, 0), rdTokens, rdTokens.ToDec()) + keeper.SetRedelegation(ctx, rd) + + // set the associated delegation + del := types.NewDelegation(addrDels[0], addrVals[1], rdTokens.ToDec()) + keeper.SetDelegation(ctx, del) + + // update bonded tokens + bondedPool := keeper.GetBondedPool(ctx) + notBondedPool := keeper.GetNotBondedPool(ctx) + rdCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdTokens.MulRaw(2))) + err := bondedPool.SetCoins(bondedPool.GetCoins().Add(rdCoins)) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + oldBonded := bondedPool.GetCoins().AmountOf(bondDenom) + oldNotBonded := notBondedPool.GetCoins().AmountOf(bondDenom) + + // slash validator + ctx = ctx.WithBlockHeight(12) + validator, found := keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + + require.NotPanics(t, func() { keeper.Slash(ctx, consAddr, 10, 10, fraction) }) + burnAmount := sdk.TokensFromConsensusPower(10).ToDec().Mul(fraction).TruncateInt() + + bondedPool = keeper.GetBondedPool(ctx) + notBondedPool = keeper.GetNotBondedPool(ctx) + // burn bonded tokens from only from delegations + require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPool.GetCoins().AmountOf(bondDenom))) + require.True(sdk.IntEq(t, oldNotBonded, notBondedPool.GetCoins().AmountOf(bondDenom))) + oldBonded = bondedPool.GetCoins().AmountOf(bondDenom) + + // read updating redelegation + rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, rd.Entries, 1) + // read updated validator + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + // power decreased by 2 - 4 stake originally bonded at the time of infraction + // was still bonded at the time of discovery and was slashed by half, 4 stake + // bonded at the time of discovery hadn't been bonded at the time of infraction + // and wasn't slashed + require.Equal(t, int64(8), validator.GetConsensusPower()) + + // slash the validator again + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + + require.NotPanics(t, func() { keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) }) + burnAmount = sdk.TokensFromConsensusPower(7) + + // read updated pool + bondedPool = keeper.GetBondedPool(ctx) + notBondedPool = keeper.GetNotBondedPool(ctx) + // seven bonded tokens burned + require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPool.GetCoins().AmountOf(bondDenom))) + require.True(sdk.IntEq(t, oldNotBonded, notBondedPool.GetCoins().AmountOf(bondDenom))) + oldBonded = bondedPool.GetCoins().AmountOf(bondDenom) + + // read updating redelegation + rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, rd.Entries, 1) + // read updated validator + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + // power decreased by 4 + require.Equal(t, int64(4), validator.GetConsensusPower()) + + // slash the validator again, by 100% + ctx = ctx.WithBlockHeight(12) + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.True(t, found) + + require.NotPanics(t, func() { keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) }) + + burnAmount = sdk.TokensFromConsensusPower(10).ToDec().Mul(sdk.OneDec()).TruncateInt() + burnAmount = burnAmount.Sub(sdk.OneDec().MulInt(rdTokens).TruncateInt()) + + // read updated pool + bondedPool = keeper.GetBondedPool(ctx) + notBondedPool = keeper.GetNotBondedPool(ctx) + require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPool.GetCoins().AmountOf(bondDenom))) + require.True(sdk.IntEq(t, oldNotBonded, notBondedPool.GetCoins().AmountOf(bondDenom))) + oldBonded = bondedPool.GetCoins().AmountOf(bondDenom) + + // read updating redelegation + rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, rd.Entries, 1) + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + // read updated validator + // validator decreased to zero power, should be in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) + + // slash the validator again, by 100% + // no stake remains to be slashed + ctx = ctx.WithBlockHeight(12) + // validator still in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) + + require.NotPanics(t, func() { keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) }) + + // read updated pool + bondedPool = keeper.GetBondedPool(ctx) + notBondedPool = keeper.GetNotBondedPool(ctx) + require.True(sdk.IntEq(t, oldBonded, bondedPool.GetCoins().AmountOf(bondDenom))) + require.True(sdk.IntEq(t, oldNotBonded, notBondedPool.GetCoins().AmountOf(bondDenom))) + + // read updating redelegation + rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, rd.Entries, 1) + // read updated validator + // power still zero, still in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) +} + +// tests Slash at a previous height with both an unbonding delegation and a redelegation +func TestSlashBoth(t *testing.T) { + ctx, keeper, _ := setupHelper(t, 10) + fraction := sdk.NewDecWithPrec(5, 1) + bondDenom := keeper.BondDenom(ctx) + + // set a redelegation with expiration timestamp beyond which the + // redelegation shouldn't be slashed + rdATokens := sdk.TokensFromConsensusPower(6) + rdA := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11, + time.Unix(0, 0), rdATokens, + rdATokens.ToDec()) + keeper.SetRedelegation(ctx, rdA) + + // set the associated delegation + delA := types.NewDelegation(addrDels[0], addrVals[1], rdATokens.ToDec()) + keeper.SetDelegation(ctx, delA) + + // set an unbonding delegation with expiration timestamp (beyond which the + // unbonding delegation shouldn't be slashed) + ubdATokens := sdk.TokensFromConsensusPower(4) + ubdA := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, + time.Unix(0, 0), ubdATokens) + keeper.SetUnbondingDelegation(ctx, ubdA) + + bondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdATokens.MulRaw(2))) + notBondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, ubdATokens)) + + // update bonded tokens + bondedPool := keeper.GetBondedPool(ctx) + notBondedPool := keeper.GetNotBondedPool(ctx) + require.NoError(t, bondedPool.SetCoins(bondedPool.GetCoins().Add(bondedCoins))) + require.NoError(t, bondedPool.SetCoins(notBondedPool.GetCoins().Add(notBondedCoins))) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + oldBonded := bondedPool.GetCoins().AmountOf(bondDenom) + oldNotBonded := notBondedPool.GetCoins().AmountOf(bondDenom) + + // slash validator + ctx = ctx.WithBlockHeight(12) + validator, found := keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) + require.True(t, found) + consAddr0 := sdk.ConsAddress(PKs[0].Address()) + keeper.Slash(ctx, consAddr0, 10, 10, fraction) + + burnedNotBondedAmount := fraction.MulInt(ubdATokens).TruncateInt() + burnedBondAmount := sdk.TokensFromConsensusPower(10).ToDec().Mul(fraction).TruncateInt() + burnedBondAmount = burnedBondAmount.Sub(burnedNotBondedAmount) + + // read updated pool + bondedPool = keeper.GetBondedPool(ctx) + notBondedPool = keeper.GetNotBondedPool(ctx) + require.True(sdk.IntEq(t, oldBonded.Sub(burnedBondAmount), bondedPool.GetCoins().AmountOf(bondDenom))) + require.True(sdk.IntEq(t, oldNotBonded.Sub(burnedNotBondedAmount), notBondedPool.GetCoins().AmountOf(bondDenom))) + + // read updating redelegation + rdA, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + require.True(t, found) + require.Len(t, rdA.Entries, 1) + // read updated validator + validator, found = keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) + require.True(t, found) + // power not decreased, all stake was bonded since + require.Equal(t, int64(10), validator.GetConsensusPower()) +} diff --git a/x/staking/keeper/test_common.go b/x/staking/keeper/test_common.go new file mode 100644 index 0000000..a8aac54 --- /dev/null +++ b/x/staking/keeper/test_common.go @@ -0,0 +1,304 @@ +package keeper // noalias + +import ( + "bytes" + "encoding/hex" + "math/rand" + "strconv" + "testing" + + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/supply" +) + +// dummy addresses used for testing +// nolint: unused deadcode +var ( + Addrs = createTestAddrs(500) + PKs = createTestPubKeys(500) + + addrDels = []sdk.AccAddress{ + Addrs[0], + Addrs[1], + } + addrVals = []sdk.ValAddress{ + sdk.ValAddress(Addrs[2]), + sdk.ValAddress(Addrs[3]), + sdk.ValAddress(Addrs[4]), + sdk.ValAddress(Addrs[5]), + sdk.ValAddress(Addrs[6]), + } +) + +//_______________________________________________________________________________________ + +// intended to be used with require/assert: require.True(ValEq(...)) +func ValEq(t *testing.T, exp, got types.Validator) (*testing.T, bool, string, types.Validator, types.Validator) { + return t, exp.TestEquivalent(got), "expected:\t%v\ngot:\t\t%v", exp, got +} + +//_______________________________________________________________________________________ + +// create a codec used only for testing +func MakeTestCodec() *codec.Codec { + var cdc = codec.New() + + // Register Msgs + cdc.RegisterInterface((*sdk.Msg)(nil), nil) + cdc.RegisterConcrete(bank.MsgSend{}, "test/staking/Send", nil) + cdc.RegisterConcrete(types.MsgCreateValidator{}, "test/staking/CreateValidator", nil) + cdc.RegisterConcrete(types.MsgEditValidator{}, "test/staking/EditValidator", nil) + cdc.RegisterConcrete(types.MsgUndelegate{}, "test/staking/Undelegate", nil) + cdc.RegisterConcrete(types.MsgBeginRedelegate{}, "test/staking/BeginRedelegate", nil) + + // Register AppAccount + cdc.RegisterInterface((*auth.Account)(nil), nil) + cdc.RegisterConcrete(&auth.BaseAccount{}, "test/staking/BaseAccount", nil) + supply.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + + return cdc +} + +// Hogpodge of all sorts of input required for testing. +// `initPower` is converted to an amount of tokens. +// If `initPower` is 0, no addrs get created. +func CreateTestInput(t *testing.T, isCheckTx bool, initPower int64) (sdk.Context, auth.AccountKeeper, Keeper, types.SupplyKeeper) { + keyStaking := sdk.NewKVStoreKey(types.StoreKey) + tkeyStaking := sdk.NewTransientStoreKey(types.TStoreKey) + keyAcc := sdk.NewKVStoreKey(auth.StoreKey) + keyParams := sdk.NewKVStoreKey(params.StoreKey) + tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) + keySupply := sdk.NewKVStoreKey(supply.StoreKey) + + db := dbm.NewMemDB() + ms := store.NewCommitMultiStore(db) + ms.MountStoreWithDB(tkeyStaking, sdk.StoreTypeTransient, nil) + ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) + ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db) + err := ms.LoadLatestVersion() + require.Nil(t, err) + + ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, log.NewNopLogger()) + ctx = ctx.WithConsensusParams( + &abci.ConsensusParams{ + Validator: &abci.ValidatorParams{ + PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519}, + }, + }, + ) + cdc := MakeTestCodec() + + feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName) + notBondedPool := supply.NewEmptyModuleAccount(types.NotBondedPoolName, supply.Burner, supply.Staking) + bondPool := supply.NewEmptyModuleAccount(types.BondedPoolName, supply.Burner, supply.Staking) + + blacklistedAddrs := make(map[string]bool) + blacklistedAddrs[feeCollectorAcc.String()] = true + blacklistedAddrs[notBondedPool.String()] = true + blacklistedAddrs[bondPool.String()] = true + + pk := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace) + + accountKeeper := auth.NewAccountKeeper( + cdc, // amino codec + keyAcc, // target store + pk.Subspace(auth.DefaultParamspace), + auth.ProtoBaseAccount, // prototype + ) + + bk := bank.NewBaseKeeper( + accountKeeper, + pk.Subspace(bank.DefaultParamspace), + bank.DefaultCodespace, + blacklistedAddrs, + ) + + maccPerms := map[string][]string{ + auth.FeeCollectorName: nil, + types.NotBondedPoolName: []string{supply.Burner, supply.Staking}, + types.BondedPoolName: []string{supply.Burner, supply.Staking}, + } + supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bk, maccPerms) + + initTokens := sdk.TokensFromConsensusPower(initPower) + initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens)) + totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens.MulRaw(int64(len(Addrs))))) + + supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) + + keeper := NewKeeper(cdc, keyStaking, tkeyStaking, supplyKeeper, pk.Subspace(DefaultParamspace), types.DefaultCodespace) + keeper.SetParams(ctx, types.DefaultParams()) + + // set module accounts + err = notBondedPool.SetCoins(totalSupply) + require.NoError(t, err) + + supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc) + supplyKeeper.SetModuleAccount(ctx, bondPool) + supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + // fill all the addresses with some coins, set the loose pool tokens simultaneously + for _, addr := range Addrs { + _, err := bk.AddCoins(ctx, addr, initCoins) + if err != nil { + panic(err) + } + } + + return ctx, accountKeeper, keeper, supplyKeeper +} + +func NewPubKey(pk string) (res crypto.PubKey) { + pkBytes, err := hex.DecodeString(pk) + if err != nil { + panic(err) + } + //res, err = crypto.PubKeyFromBytes(pkBytes) + var pkEd ed25519.PubKeyEd25519 + copy(pkEd[:], pkBytes[:]) + return pkEd +} + +// for incode address generation +func TestAddr(addr string, bech string) sdk.AccAddress { + + res, err := sdk.AccAddressFromHex(addr) + if err != nil { + panic(err) + } + bechexpected := res.String() + if bech != bechexpected { + panic("Bech encoding doesn't match reference") + } + + bechres, err := sdk.AccAddressFromBech32(bech) + if err != nil { + panic(err) + } + if !bytes.Equal(bechres, res) { + panic("Bech decode and hex decode don't match") + } + + return res +} + +// nolint: unparam +func createTestAddrs(numAddrs int) []sdk.AccAddress { + var addresses []sdk.AccAddress + var buffer bytes.Buffer + + // start at 100 so we can make up to 999 test addresses with valid test addresses + for i := 100; i < (numAddrs + 100); i++ { + numString := strconv.Itoa(i) + buffer.WriteString("A58856F0FD53BF058B4909A21AEC019107BA6") //base address string + + buffer.WriteString(numString) //adding on final two digits to make addresses unique + res, _ := sdk.AccAddressFromHex(buffer.String()) + bech := res.String() + addresses = append(addresses, TestAddr(buffer.String(), bech)) + buffer.Reset() + } + return addresses +} + +// nolint: unparam +func createTestPubKeys(numPubKeys int) []crypto.PubKey { + var publicKeys []crypto.PubKey + var buffer bytes.Buffer + + //start at 10 to avoid changing 1 to 01, 2 to 02, etc + for i := 100; i < (numPubKeys + 100); i++ { + numString := strconv.Itoa(i) + buffer.WriteString("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AF") //base pubkey string + buffer.WriteString(numString) //adding on final two digits to make pubkeys unique + publicKeys = append(publicKeys, NewPubKey(buffer.String())) + buffer.Reset() + } + return publicKeys +} + +//_____________________________________________________________________________________ + +// does a certain by-power index record exist +func ValidatorByPowerIndexExists(ctx sdk.Context, keeper Keeper, power []byte) bool { + store := ctx.KVStore(keeper.storeKey) + return store.Has(power) +} + +// update validator for testing +func TestingUpdateValidator(keeper Keeper, ctx sdk.Context, validator types.Validator, apply bool) types.Validator { + keeper.SetValidator(ctx, validator) + + // Remove any existing power key for validator. + store := ctx.KVStore(keeper.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ValidatorsByPowerIndexKey) + defer iterator.Close() + deleted := false + for ; iterator.Valid(); iterator.Next() { + valAddr := types.ParseValidatorPowerRankKey(iterator.Key()) + if bytes.Equal(valAddr, validator.OperatorAddress) { + if deleted { + panic("found duplicate power index key") + } else { + deleted = true + } + store.Delete(iterator.Key()) + } + } + + keeper.SetValidatorByPowerIndex(ctx, validator) + if apply { + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validator, found := keeper.GetValidator(ctx, validator.OperatorAddress) + if !found { + panic("validator expected but not found") + } + return validator + } + cachectx, _ := ctx.CacheContext() + keeper.ApplyAndReturnValidatorSetUpdates(cachectx) + validator, found := keeper.GetValidator(cachectx, validator.OperatorAddress) + if !found { + panic("validator expected but not found") + } + return validator +} + +// nolint: deadcode unused +func validatorByPowerIndexExists(k Keeper, ctx sdk.Context, power []byte) bool { + store := ctx.KVStore(k.storeKey) + return store.Has(power) +} + +// RandomValidator returns a random validator given access to the keeper and ctx +func RandomValidator(r *rand.Rand, keeper Keeper, ctx sdk.Context) types.Validator { + vals := keeper.GetAllValidators(ctx) + i := r.Intn(len(vals)) + return vals[i] +} + +// RandomBondedValidator returns a random bonded validator given access to the keeper and ctx +func RandomBondedValidator(r *rand.Rand, keeper Keeper, ctx sdk.Context) types.Validator { + vals := keeper.GetBondedValidatorsByPower(ctx) + i := r.Intn(len(vals)) + return vals[i] +} diff --git a/x/staking/keeper/val_state_change.go b/x/staking/keeper/val_state_change.go new file mode 100644 index 0000000..49d8e40 --- /dev/null +++ b/x/staking/keeper/val_state_change.go @@ -0,0 +1,298 @@ +package keeper + +import ( + "bytes" + "fmt" + "sort" + + abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// Apply and return accumulated updates to the bonded validator set. Also, +// * Updates the active valset as keyed by LastValidatorPowerKey. +// * Updates the total power as keyed by LastTotalPowerKey. +// * Updates validator status' according to updated powers. +// * Updates the fee pool bonded vs not-bonded tokens. +// * Updates relevant indices. +// It gets called once after genesis, another time maybe after genesis transactions, +// then once at every EndBlock. +// +// CONTRACT: Only validators with non-zero power or zero-power that were bonded +// at the previous block height or were removed from the validator set entirely +// are returned to Tendermint. +func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate) { + + store := ctx.KVStore(k.storeKey) + maxValidators := k.GetParams(ctx).MaxValidators + totalPower := sdk.ZeroInt() + amtFromBondedToNotBonded, amtFromNotBondedToBonded := sdk.ZeroInt(), sdk.ZeroInt() + + // Retrieve the last validator set. + // The persistent set is updated later in this function. + // (see LastValidatorPowerKey). + last := k.getLastValidatorsByAddr(ctx) + + // Iterate over validators, highest power to lowest. + iterator := sdk.KVStoreReversePrefixIterator(store, types.ValidatorsByPowerIndexKey) + defer iterator.Close() + for count := 0; iterator.Valid() && count < int(maxValidators); iterator.Next() { + + // everything that is iterated in this loop is becoming or already a + // part of the bonded validator set + + // fetch the validator + valAddr := sdk.ValAddress(iterator.Value()) + validator := k.mustGetValidator(ctx, valAddr) + + if validator.Jailed { + panic("should never retrieve a jailed validator from the power store") + } + + // if we get to a zero-power validator (which we don't bond), + // there are no more possible bonded validators + if validator.PotentialConsensusPower() == 0 { + break + } + + // apply the appropriate state change if necessary + switch { + case validator.IsUnbonded(): + validator = k.unbondedToBonded(ctx, validator) + amtFromNotBondedToBonded = amtFromNotBondedToBonded.Add(validator.GetTokens()) + case validator.IsUnbonding(): + validator = k.unbondingToBonded(ctx, validator) + amtFromNotBondedToBonded = amtFromNotBondedToBonded.Add(validator.GetTokens()) + case validator.IsBonded(): + // no state change + default: + panic("unexpected validator status") + } + + // fetch the old power bytes + var valAddrBytes [sdk.AddrLen]byte + copy(valAddrBytes[:], valAddr[:]) + oldPowerBytes, found := last[valAddrBytes] + + // calculate the new power bytes + newPower := validator.ConsensusPower() + newPowerBytes := k.cdc.MustMarshalBinaryLengthPrefixed(newPower) + + // update the validator set if power has changed + if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) { + updates = append(updates, validator.ABCIValidatorUpdate()) + + // set validator power on lookup index + k.SetLastValidatorPower(ctx, valAddr, newPower) + } + + // validator still in the validator set, so delete from the copy + delete(last, valAddrBytes) + + // keep count + count++ + totalPower = totalPower.Add(sdk.NewInt(newPower)) + } + + // sort the no-longer-bonded validators + noLongerBonded := sortNoLongerBonded(last) + + // iterate through the sorted no-longer-bonded validators + for _, valAddrBytes := range noLongerBonded { + + // fetch the validator + validator := k.mustGetValidator(ctx, sdk.ValAddress(valAddrBytes)) + + // bonded to unbonding + validator = k.bondedToUnbonding(ctx, validator) + amtFromBondedToNotBonded = amtFromBondedToNotBonded.Add(validator.GetTokens()) + + // delete from the bonded validator index + k.DeleteLastValidatorPower(ctx, validator.GetOperator()) + + // update the validator set + updates = append(updates, validator.ABCIValidatorUpdateZero()) + } + + // Update the pools based on the recent updates in the validator set: + // - The tokens from the non-bonded candidates that enter the new validator set need to be transferred + // to the Bonded pool. + // - The tokens from the bonded validators that are being kicked out from the validator set + // need to be transferred to the NotBonded pool. + switch { + // Compare and subtract the respective amounts to only perform one transfer. + // This is done in order to avoid doing multiple updates inside each iterator/loop. + case amtFromNotBondedToBonded.GT(amtFromBondedToNotBonded): + k.notBondedTokensToBonded(ctx, amtFromNotBondedToBonded.Sub(amtFromBondedToNotBonded)) + case amtFromNotBondedToBonded.LT(amtFromBondedToNotBonded): + k.bondedTokensToNotBonded(ctx, amtFromBondedToNotBonded.Sub(amtFromNotBondedToBonded)) + default: + // equal amounts of tokens; no update required + } + + // set total power on lookup index if there are any updates + if len(updates) > 0 { + k.SetLastTotalPower(ctx, totalPower) + } + + return updates +} + +// Validator state transitions + +func (k Keeper) bondedToUnbonding(ctx sdk.Context, validator types.Validator) types.Validator { + if !validator.IsBonded() { + panic(fmt.Sprintf("bad state transition bondedToUnbonding, validator: %v\n", validator)) + } + return k.beginUnbondingValidator(ctx, validator) +} + +func (k Keeper) unbondingToBonded(ctx sdk.Context, validator types.Validator) types.Validator { + if !validator.IsUnbonding() { + panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator)) + } + return k.bondValidator(ctx, validator) +} + +func (k Keeper) unbondedToBonded(ctx sdk.Context, validator types.Validator) types.Validator { + if !validator.IsUnbonded() { + panic(fmt.Sprintf("bad state transition unbondedToBonded, validator: %v\n", validator)) + } + return k.bondValidator(ctx, validator) +} + +// switches a validator from unbonding state to unbonded state +func (k Keeper) unbondingToUnbonded(ctx sdk.Context, validator types.Validator) types.Validator { + if !validator.IsUnbonding() { + panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator)) + } + return k.completeUnbondingValidator(ctx, validator) +} + +// send a validator to jail +func (k Keeper) jailValidator(ctx sdk.Context, validator types.Validator) { + if validator.Jailed { + panic(fmt.Sprintf("cannot jail already jailed validator, validator: %v\n", validator)) + } + + validator.Jailed = true + k.SetValidator(ctx, validator) + k.DeleteValidatorByPowerIndex(ctx, validator) +} + +// remove a validator from jail +func (k Keeper) unjailValidator(ctx sdk.Context, validator types.Validator) { + if !validator.Jailed { + panic(fmt.Sprintf("cannot unjail already unjailed validator, validator: %v\n", validator)) + } + + validator.Jailed = false + k.SetValidator(ctx, validator) + k.SetValidatorByPowerIndex(ctx, validator) +} + +// perform all the store operations for when a validator status becomes bonded +func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator { + + // delete the validator by power index, as the key will change + k.DeleteValidatorByPowerIndex(ctx, validator) + + // set the status + validator = validator.UpdateStatus(sdk.Bonded) + + // save the now bonded validator record to the two referenced stores + k.SetValidator(ctx, validator) + k.SetValidatorByPowerIndex(ctx, validator) + + // delete from queue if present + k.DeleteValidatorQueue(ctx, validator) + + // trigger hook + k.AfterValidatorBonded(ctx, validator.ConsAddress(), validator.OperatorAddress) + + return validator +} + +// perform all the store operations for when a validator begins unbonding +func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator { + + params := k.GetParams(ctx) + + // delete the validator by power index, as the key will change + k.DeleteValidatorByPowerIndex(ctx, validator) + + // sanity check + if validator.Status != sdk.Bonded { + panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator)) + } + + // set the status + validator = validator.UpdateStatus(sdk.Unbonding) + + // set the unbonding completion time and completion height appropriately + validator.UnbondingCompletionTime = ctx.BlockHeader().Time.Add(params.UnbondingTime) + validator.UnbondingHeight = ctx.BlockHeader().Height + + // save the now unbonded validator record and power index + k.SetValidator(ctx, validator) + k.SetValidatorByPowerIndex(ctx, validator) + + // Adds to unbonding validator queue + k.InsertValidatorQueue(ctx, validator) + + // trigger hook + k.AfterValidatorBeginUnbonding(ctx, validator.ConsAddress(), validator.OperatorAddress) + + return validator +} + +// perform all the store operations for when a validator status becomes unbonded +func (k Keeper) completeUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator { + validator = validator.UpdateStatus(sdk.Unbonded) + k.SetValidator(ctx, validator) + return validator +} + +// map of operator addresses to serialized power +type validatorsByAddr map[[sdk.AddrLen]byte][]byte + +// get the last validator set +func (k Keeper) getLastValidatorsByAddr(ctx sdk.Context) validatorsByAddr { + last := make(validatorsByAddr) + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.LastValidatorPowerKey) + defer iterator.Close() + // iterate over the last validator set index + for ; iterator.Valid(); iterator.Next() { + var valAddr [sdk.AddrLen]byte + // extract the validator address from the key (prefix is 1-byte) + copy(valAddr[:], iterator.Key()[1:]) + // power bytes is just the value + powerBytes := iterator.Value() + last[valAddr] = make([]byte, len(powerBytes)) + copy(last[valAddr][:], powerBytes[:]) + } + return last +} + +// given a map of remaining validators to previous bonded power +// returns the list of validators to be unbonded, sorted by operator address +func sortNoLongerBonded(last validatorsByAddr) [][]byte { + // sort the map keys for determinism + noLongerBonded := make([][]byte, len(last)) + index := 0 + for valAddrBytes := range last { + valAddr := make([]byte, sdk.AddrLen) + copy(valAddr[:], valAddrBytes[:]) + noLongerBonded[index] = valAddr + index++ + } + // sorted by address - order doesn't matter + sort.SliceStable(noLongerBonded, func(i, j int) bool { + // -1 means strictly less than + return bytes.Compare(noLongerBonded[i], noLongerBonded[j]) == -1 + }) + return noLongerBonded +} diff --git a/x/staking/keeper/validator.go b/x/staking/keeper/validator.go new file mode 100644 index 0000000..5f26071 --- /dev/null +++ b/x/staking/keeper/validator.go @@ -0,0 +1,446 @@ +package keeper + +import ( + "bytes" + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// Cache the amino decoding of validators, as it can be the case that repeated slashing calls +// cause many calls to GetValidator, which were shown to throttle the state machine in our +// simulation. Note this is quite biased though, as the simulator does more slashes than a +// live chain should, however we require the slashing to be fast as noone pays gas for it. +type cachedValidator struct { + val types.Validator + marshalled string // marshalled amino bytes for the validator object (not operator address) +} + +func newCachedValidator(val types.Validator, marshalled string) cachedValidator { + return cachedValidator{ + val: val, + marshalled: marshalled, + } +} + +// get a single validator +func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator types.Validator, found bool) { + store := ctx.KVStore(k.storeKey) + value := store.Get(types.GetValidatorKey(addr)) + if value == nil { + return validator, false + } + + // If these amino encoded bytes are in the cache, return the cached validator + strValue := string(value) + if val, ok := k.validatorCache[strValue]; ok { + valToReturn := val.val + // Doesn't mutate the cache's value + valToReturn.OperatorAddress = addr + return valToReturn, true + } + + // amino bytes weren't found in cache, so amino unmarshal and add it to the cache + validator = types.MustUnmarshalValidator(k.cdc, value) + cachedVal := newCachedValidator(validator, strValue) + k.validatorCache[strValue] = newCachedValidator(validator, strValue) + k.validatorCacheList.PushBack(cachedVal) + + // if the cache is too big, pop off the last element from it + if k.validatorCacheList.Len() > aminoCacheSize { + valToRemove := k.validatorCacheList.Remove(k.validatorCacheList.Front()).(cachedValidator) + delete(k.validatorCache, valToRemove.marshalled) + } + + validator = types.MustUnmarshalValidator(k.cdc, value) + return validator, true +} + +func (k Keeper) mustGetValidator(ctx sdk.Context, addr sdk.ValAddress) types.Validator { + validator, found := k.GetValidator(ctx, addr) + if !found { + panic(fmt.Sprintf("validator record not found for address: %X\n", addr)) + } + return validator +} + +// get a single validator by consensus address +func (k Keeper) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator types.Validator, found bool) { + store := ctx.KVStore(k.storeKey) + opAddr := store.Get(types.GetValidatorByConsAddrKey(consAddr)) + if opAddr == nil { + return validator, false + } + return k.GetValidator(ctx, opAddr) +} + +func (k Keeper) mustGetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) types.Validator { + validator, found := k.GetValidatorByConsAddr(ctx, consAddr) + if !found { + panic(fmt.Errorf("validator with consensus-Address %s not found", consAddr)) + } + return validator +} + +// set the main record holding validator details +func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) { + store := ctx.KVStore(k.storeKey) + bz := types.MustMarshalValidator(k.cdc, validator) + store.Set(types.GetValidatorKey(validator.OperatorAddress), bz) +} + +// validator index +func (k Keeper) SetValidatorByConsAddr(ctx sdk.Context, validator types.Validator) { + store := ctx.KVStore(k.storeKey) + consAddr := sdk.ConsAddress(validator.ConsPubKey.Address()) + store.Set(types.GetValidatorByConsAddrKey(consAddr), validator.OperatorAddress) +} + +// validator index +func (k Keeper) SetValidatorByPowerIndex(ctx sdk.Context, validator types.Validator) { + // jailed validators are not kept in the power index + if validator.Jailed { + return + } + store := ctx.KVStore(k.storeKey) + store.Set(types.GetValidatorsByPowerIndexKey(validator), validator.OperatorAddress) +} + +// validator index +func (k Keeper) DeleteValidatorByPowerIndex(ctx sdk.Context, validator types.Validator) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.GetValidatorsByPowerIndexKey(validator)) +} + +// validator index +func (k Keeper) SetNewValidatorByPowerIndex(ctx sdk.Context, validator types.Validator) { + store := ctx.KVStore(k.storeKey) + store.Set(types.GetValidatorsByPowerIndexKey(validator), validator.OperatorAddress) +} + +// Update the tokens of an existing validator, update the validators power index key +func (k Keeper) AddValidatorTokensAndShares(ctx sdk.Context, validator types.Validator, + tokensToAdd sdk.Int) (valOut types.Validator, addedShares sdk.Dec) { + + k.DeleteValidatorByPowerIndex(ctx, validator) + validator, addedShares = validator.AddTokensFromDel(tokensToAdd) + k.SetValidator(ctx, validator) + k.SetValidatorByPowerIndex(ctx, validator) + return validator, addedShares +} + +// Update the tokens of an existing validator, update the validators power index key +func (k Keeper) RemoveValidatorTokensAndShares(ctx sdk.Context, validator types.Validator, + sharesToRemove sdk.Dec) (valOut types.Validator, removedTokens sdk.Int) { + + k.DeleteValidatorByPowerIndex(ctx, validator) + validator, removedTokens = validator.RemoveDelShares(sharesToRemove) + k.SetValidator(ctx, validator) + k.SetValidatorByPowerIndex(ctx, validator) + return validator, removedTokens +} + +// Update the tokens of an existing validator, update the validators power index key +func (k Keeper) RemoveValidatorTokens(ctx sdk.Context, + validator types.Validator, tokensToRemove sdk.Int) types.Validator { + + k.DeleteValidatorByPowerIndex(ctx, validator) + validator = validator.RemoveTokens(tokensToRemove) + k.SetValidator(ctx, validator) + k.SetValidatorByPowerIndex(ctx, validator) + return validator +} + +// UpdateValidatorCommission attempts to update a validator's commission rate. +// An error is returned if the new commission rate is invalid. +func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, + validator types.Validator, newRate sdk.Dec) (types.Commission, sdk.Error) { + + commission := validator.Commission + blockTime := ctx.BlockHeader().Time + + if err := commission.ValidateNewRate(newRate, blockTime); err != nil { + return commission, err + } + + commission.Rate = newRate + commission.UpdateTime = blockTime + + return commission, nil +} + +// remove the validator record and associated indexes +// except for the bonded validator index which is only handled in ApplyAndReturnTendermintUpdates +func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { + + // first retrieve the old validator record + validator, found := k.GetValidator(ctx, address) + if !found { + return + } + + if !validator.IsUnbonded() { + panic("cannot call RemoveValidator on bonded or unbonding validators") + } + if validator.Tokens.IsPositive() { + panic("attempting to remove a validator which still contains tokens") + } + if validator.Tokens.GT(sdk.ZeroInt()) { + panic("validator being removed should never have positive tokens") + } + + // delete the old validator record + store := ctx.KVStore(k.storeKey) + store.Delete(types.GetValidatorKey(address)) + store.Delete(types.GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address()))) + store.Delete(types.GetValidatorsByPowerIndexKey(validator)) + + // call hooks + k.AfterValidatorRemoved(ctx, validator.ConsAddress(), validator.OperatorAddress) +} + +// get groups of validators + +// get the set of all validators with no limits, used during genesis dump +func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []types.Validator) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.ValidatorsKey) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + validator := types.MustUnmarshalValidator(k.cdc, iterator.Value()) + validators = append(validators, validator) + } + return validators +} + +// return a given amount of all the validators +func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve uint16) (validators []types.Validator) { + store := ctx.KVStore(k.storeKey) + validators = make([]types.Validator, maxRetrieve) + + iterator := sdk.KVStorePrefixIterator(store, types.ValidatorsKey) + defer iterator.Close() + + i := 0 + for ; iterator.Valid() && i < int(maxRetrieve); iterator.Next() { + validator := types.MustUnmarshalValidator(k.cdc, iterator.Value()) + validators[i] = validator + i++ + } + return validators[:i] // trim if the array length < maxRetrieve +} + +// get the current group of bonded validators sorted by power-rank +func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator { + store := ctx.KVStore(k.storeKey) + maxValidators := k.MaxValidators(ctx) + validators := make([]types.Validator, maxValidators) + + iterator := sdk.KVStoreReversePrefixIterator(store, types.ValidatorsByPowerIndexKey) + defer iterator.Close() + + i := 0 + for ; iterator.Valid() && i < int(maxValidators); iterator.Next() { + address := iterator.Value() + validator := k.mustGetValidator(ctx, address) + + if validator.IsBonded() { + validators[i] = validator + i++ + } + } + return validators[:i] // trim +} + +// returns an iterator for the current validator power store +func (k Keeper) ValidatorsPowerStoreIterator(ctx sdk.Context) (iterator sdk.Iterator) { + store := ctx.KVStore(k.storeKey) + iterator = sdk.KVStoreReversePrefixIterator(store, types.ValidatorsByPowerIndexKey) + return iterator +} + +//_______________________________________________________________________ +// Last Validator Index + +// Load the last validator power. +// Returns zero if the operator was not a validator last block. +func (k Keeper) GetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) (power int64) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.GetLastValidatorPowerKey(operator)) + if bz == nil { + return 0 + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &power) + return +} + +// Set the last validator power. +func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power int64) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(power) + store.Set(types.GetLastValidatorPowerKey(operator), bz) +} + +// Delete the last validator power. +func (k Keeper) DeleteLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.GetLastValidatorPowerKey(operator)) +} + +// returns an iterator for the consensus validators in the last block +func (k Keeper) LastValidatorsIterator(ctx sdk.Context) (iterator sdk.Iterator) { + store := ctx.KVStore(k.storeKey) + iterator = sdk.KVStorePrefixIterator(store, types.LastValidatorPowerKey) + return iterator +} + +// Iterate over last validator powers. +func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context, handler func(operator sdk.ValAddress, power int64) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, types.LastValidatorPowerKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + addr := sdk.ValAddress(iter.Key()[len(types.LastValidatorPowerKey):]) + var power int64 + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &power) + if handler(addr, power) { + break + } + } +} + +// get the group of the bonded validators +func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator) { + store := ctx.KVStore(k.storeKey) + + // add the actual validator power sorted store + maxValidators := k.MaxValidators(ctx) + validators = make([]types.Validator, maxValidators) + + iterator := sdk.KVStorePrefixIterator(store, types.LastValidatorPowerKey) + defer iterator.Close() + + i := 0 + for ; iterator.Valid(); iterator.Next() { + + // sanity check + if i >= int(maxValidators) { + panic("more validators than maxValidators found") + } + address := types.AddressFromLastValidatorPowerKey(iterator.Key()) + validator := k.mustGetValidator(ctx, address) + + validators[i] = validator + i++ + } + return validators[:i] // trim +} + +//_______________________________________________________________________ +// Validator Queue + +// gets a specific validator queue timeslice. A timeslice is a slice of ValAddresses corresponding to unbonding validators +// that expire at a certain time. +func (k Keeper) GetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (valAddrs []sdk.ValAddress) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.GetValidatorQueueTimeKey(timestamp)) + if bz == nil { + return []sdk.ValAddress{} + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &valAddrs) + return valAddrs +} + +// Sets a specific validator queue timeslice. +func (k Keeper) SetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []sdk.ValAddress) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(keys) + store.Set(types.GetValidatorQueueTimeKey(timestamp), bz) +} + +// Deletes a specific validator queue timeslice. +func (k Keeper) DeleteValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.GetValidatorQueueTimeKey(timestamp)) +} + +// Insert an validator address to the appropriate timeslice in the validator queue +func (k Keeper) InsertValidatorQueue(ctx sdk.Context, val types.Validator) { + timeSlice := k.GetValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime) + var keys []sdk.ValAddress + if len(timeSlice) == 0 { + keys = []sdk.ValAddress{val.OperatorAddress} + } else { + keys = append(timeSlice, val.OperatorAddress) + } + k.SetValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime, keys) +} + +// Delete a validator address from the validator queue +func (k Keeper) DeleteValidatorQueue(ctx sdk.Context, val types.Validator) { + timeSlice := k.GetValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime) + newTimeSlice := []sdk.ValAddress{} + for _, addr := range timeSlice { + if !bytes.Equal(addr, val.OperatorAddress) { + newTimeSlice = append(newTimeSlice, addr) + } + } + if len(newTimeSlice) == 0 { + k.DeleteValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime) + } else { + k.SetValidatorQueueTimeSlice(ctx, val.UnbondingCompletionTime, newTimeSlice) + } +} + +// Returns all the validator queue timeslices from time 0 until endTime +func (k Keeper) ValidatorQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator { + store := ctx.KVStore(k.storeKey) + return store.Iterator(types.ValidatorQueueKey, sdk.InclusiveEndBytes(types.GetValidatorQueueTimeKey(endTime))) +} + +// Returns a concatenated list of all the timeslices before currTime, and deletes the timeslices from the queue +func (k Keeper) GetAllMatureValidatorQueue(ctx sdk.Context, currTime time.Time) (matureValsAddrs []sdk.ValAddress) { + // gets an iterator for all timeslices from time 0 until the current Blockheader time + validatorTimesliceIterator := k.ValidatorQueueIterator(ctx, ctx.BlockHeader().Time) + defer validatorTimesliceIterator.Close() + + for ; validatorTimesliceIterator.Valid(); validatorTimesliceIterator.Next() { + timeslice := []sdk.ValAddress{} + k.cdc.MustUnmarshalBinaryLengthPrefixed(validatorTimesliceIterator.Value(), ×lice) + matureValsAddrs = append(matureValsAddrs, timeslice...) + } + + return matureValsAddrs +} + +// Unbonds all the unbonding validators that have finished their unbonding period +func (k Keeper) UnbondAllMatureValidatorQueue(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + validatorTimesliceIterator := k.ValidatorQueueIterator(ctx, ctx.BlockHeader().Time) + defer validatorTimesliceIterator.Close() + + for ; validatorTimesliceIterator.Valid(); validatorTimesliceIterator.Next() { + timeslice := []sdk.ValAddress{} + k.cdc.MustUnmarshalBinaryLengthPrefixed(validatorTimesliceIterator.Value(), ×lice) + + for _, valAddr := range timeslice { + val, found := k.GetValidator(ctx, valAddr) + if !found { + panic("validator in the unbonding queue was not found") + } + + if !val.IsUnbonding() { + panic("unexpected validator in unbonding queue; status was not unbonding") + } + val = k.unbondingToUnbonded(ctx, val) + if val.GetDelegatorShares().IsZero() { + k.RemoveValidator(ctx, val.OperatorAddress) + } + } + + store.Delete(validatorTimesliceIterator.Key()) + } +} diff --git a/x/staking/keeper/validator_test.go b/x/staking/keeper/validator_test.go new file mode 100644 index 0000000..a5d909a --- /dev/null +++ b/x/staking/keeper/validator_test.go @@ -0,0 +1,1086 @@ +package keeper + +import ( + "fmt" + "testing" + "time" + + abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +//_______________________________________________________ + +func TestSetValidator(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 10) + + valPubKey := PKs[0] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + valTokens := sdk.TokensFromConsensusPower(10) + + // test how the validator is set from a purely unbonbed pool + validator := types.NewValidator(valAddr, valPubKey, types.Description{}) + validator, _ = validator.AddTokensFromDel(valTokens) + require.Equal(t, sdk.Unbonded, validator.Status) + assert.Equal(t, valTokens, validator.Tokens) + assert.Equal(t, valTokens, validator.DelegatorShares.RoundInt()) + keeper.SetValidator(ctx, validator) + keeper.SetValidatorByPowerIndex(ctx, validator) + + // ensure update + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validator, found := keeper.GetValidator(ctx, valAddr) + require.True(t, found) + require.Equal(t, 1, len(updates)) + require.Equal(t, validator.ABCIValidatorUpdate(), updates[0]) + + // after the save the validator should be bonded + require.Equal(t, sdk.Bonded, validator.Status) + assert.Equal(t, valTokens, validator.Tokens) + assert.Equal(t, valTokens, validator.DelegatorShares.RoundInt()) + + // Check each store for being saved + resVal, found := keeper.GetValidator(ctx, valAddr) + assert.True(ValEq(t, validator, resVal)) + require.True(t, found) + + resVals := keeper.GetLastValidators(ctx) + require.Equal(t, 1, len(resVals)) + assert.True(ValEq(t, validator, resVals[0])) + + resVals = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, 1, len(resVals)) + require.True(ValEq(t, validator, resVals[0])) + + resVals = keeper.GetValidators(ctx, 1) + require.Equal(t, 1, len(resVals)) + require.True(ValEq(t, validator, resVals[0])) + + resVals = keeper.GetValidators(ctx, 10) + require.Equal(t, 1, len(resVals)) + require.True(ValEq(t, validator, resVals[0])) + + allVals := keeper.GetAllValidators(ctx) + require.Equal(t, 1, len(allVals)) +} + +func TestUpdateValidatorByPowerIndex(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + + bondedPool := keeper.GetBondedPool(ctx) + notBondedPool := keeper.GetNotBondedPool(ctx) + bondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), sdk.TokensFromConsensusPower(1234)))) + notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), sdk.TokensFromConsensusPower(10000)))) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + // add a validator + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator, delSharesCreated := validator.AddTokensFromDel(sdk.TokensFromConsensusPower(100)) + require.Equal(t, sdk.Unbonded, validator.Status) + require.Equal(t, sdk.TokensFromConsensusPower(100), validator.Tokens) + TestingUpdateValidator(keeper, ctx, validator, true) + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + require.Equal(t, sdk.TokensFromConsensusPower(100), validator.Tokens) + + power := types.GetValidatorsByPowerIndexKey(validator) + require.True(t, validatorByPowerIndexExists(keeper, ctx, power)) + + // burn half the delegator shares + keeper.DeleteValidatorByPowerIndex(ctx, validator) + validator, burned := validator.RemoveDelShares(delSharesCreated.Quo(sdk.NewDec(2))) + require.Equal(t, sdk.TokensFromConsensusPower(50), burned) + TestingUpdateValidator(keeper, ctx, validator, true) // update the validator, possibly kicking it out + require.False(t, validatorByPowerIndexExists(keeper, ctx, power)) + + validator, found = keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + + power = types.GetValidatorsByPowerIndexKey(validator) + require.True(t, validatorByPowerIndexExists(keeper, ctx, power)) +} + +func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { + numVals := 10 + maxVals := 5 + + // create context, keeper, and pool for tests + ctx, _, keeper, _ := CreateTestInput(t, false, 0) + bondedPool := keeper.GetBondedPool(ctx) + notBondedPool := keeper.GetNotBondedPool(ctx) + + // create keeper parameters + params := keeper.GetParams(ctx) + params.MaxValidators = uint16(maxVals) + keeper.SetParams(ctx, params) + + // create a random pool + bondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), sdk.TokensFromConsensusPower(1234)))) + notBondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), sdk.TokensFromConsensusPower(10000)))) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + validators := make([]types.Validator, numVals) + for i := 0; i < len(validators); i++ { + moniker := fmt.Sprintf("val#%d", int64(i)) + val := types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{Moniker: moniker}) + delTokens := sdk.TokensFromConsensusPower(int64((i + 1) * 10)) + val, _ = val.AddTokensFromDel(delTokens) + + val = TestingUpdateValidator(keeper, ctx, val, true) + validators[i] = val + } + + nextCliffVal := validators[numVals-maxVals+1] + + // remove enough tokens to kick out the validator below the current cliff + // validator and next in line cliff validator + keeper.DeleteValidatorByPowerIndex(ctx, nextCliffVal) + shares := sdk.TokensFromConsensusPower(21) + nextCliffVal, _ = nextCliffVal.RemoveDelShares(shares.ToDec()) + nextCliffVal = TestingUpdateValidator(keeper, ctx, nextCliffVal, true) + + expectedValStatus := map[int]sdk.BondStatus{ + 9: sdk.Bonded, 8: sdk.Bonded, 7: sdk.Bonded, 5: sdk.Bonded, 4: sdk.Bonded, + 0: sdk.Unbonding, 1: sdk.Unbonding, 2: sdk.Unbonding, 3: sdk.Unbonding, 6: sdk.Unbonding, + } + + // require all the validators have their respective statuses + for valIdx, status := range expectedValStatus { + valAddr := validators[valIdx].OperatorAddress + val, _ := keeper.GetValidator(ctx, valAddr) + + assert.Equal( + t, status, val.GetStatus(), + fmt.Sprintf("expected validator at index %v to have status: %s", valIdx, status), + ) + } +} + +func TestSlashToZeroPowerRemoved(t *testing.T) { + // initialize setup + ctx, _, keeper, _ := CreateTestInput(t, false, 100) + + // add a validator + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + valTokens := sdk.TokensFromConsensusPower(100) + + bondedPool := keeper.GetBondedPool(ctx) + err := bondedPool.SetCoins(sdk.NewCoins(sdk.NewCoin(keeper.BondDenom(ctx), valTokens))) + require.NoError(t, err) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + validator, _ = validator.AddTokensFromDel(valTokens) + require.Equal(t, sdk.Unbonded, validator.Status) + require.Equal(t, valTokens, validator.Tokens) + keeper.SetValidatorByConsAddr(ctx, validator) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + require.Equal(t, valTokens, validator.Tokens, "\nvalidator %v\npool %v", validator, valTokens) + + // slash the validator by 100% + consAddr0 := sdk.ConsAddress(PKs[0].Address()) + keeper.Slash(ctx, consAddr0, 0, 100, sdk.OneDec()) + // apply TM updates + keeper.ApplyAndReturnValidatorSetUpdates(ctx) + // validator should be unbonding + validator, _ = keeper.GetValidator(ctx, addrVals[0]) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) +} + +// This function tests UpdateValidator, GetValidator, GetLastValidators, RemoveValidator +func TestValidatorBasics(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + + //construct the validators + var validators [3]types.Validator + powers := []int64{9, 8, 7} + for i, power := range powers { + validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{}) + validators[i].Status = sdk.Unbonded + validators[i].Tokens = sdk.ZeroInt() + tokens := sdk.TokensFromConsensusPower(power) + + validators[i], _ = validators[i].AddTokensFromDel(tokens) + } + assert.Equal(t, sdk.TokensFromConsensusPower(9), validators[0].Tokens) + assert.Equal(t, sdk.TokensFromConsensusPower(8), validators[1].Tokens) + assert.Equal(t, sdk.TokensFromConsensusPower(7), validators[2].Tokens) + + // check the empty keeper first + _, found := keeper.GetValidator(ctx, addrVals[0]) + require.False(t, found) + resVals := keeper.GetLastValidators(ctx) + require.Zero(t, len(resVals)) + + resVals = keeper.GetValidators(ctx, 2) + require.Zero(t, len(resVals)) + + // set and retrieve a record + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) + keeper.SetValidatorByConsAddr(ctx, validators[0]) + resVal, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + assert.True(ValEq(t, validators[0], resVal)) + + // retrieve from consensus + resVal, found = keeper.GetValidatorByConsAddr(ctx, sdk.ConsAddress(PKs[0].Address())) + require.True(t, found) + assert.True(ValEq(t, validators[0], resVal)) + resVal, found = keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) + require.True(t, found) + assert.True(ValEq(t, validators[0], resVal)) + + resVals = keeper.GetLastValidators(ctx) + require.Equal(t, 1, len(resVals)) + assert.True(ValEq(t, validators[0], resVals[0])) + assert.Equal(t, sdk.Bonded, validators[0].Status) + assert.True(sdk.IntEq(t, sdk.TokensFromConsensusPower(9), validators[0].BondedTokens())) + + // modify a records, save, and retrieve + validators[0].Status = sdk.Bonded + validators[0].Tokens = sdk.TokensFromConsensusPower(10) + validators[0].DelegatorShares = validators[0].Tokens.ToDec() + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) + resVal, found = keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + assert.True(ValEq(t, validators[0], resVal)) + + resVals = keeper.GetLastValidators(ctx) + require.Equal(t, 1, len(resVals)) + assert.True(ValEq(t, validators[0], resVals[0])) + + // add other validators + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], true) + validators[2] = TestingUpdateValidator(keeper, ctx, validators[2], true) + resVal, found = keeper.GetValidator(ctx, addrVals[1]) + require.True(t, found) + assert.True(ValEq(t, validators[1], resVal)) + resVal, found = keeper.GetValidator(ctx, addrVals[2]) + require.True(t, found) + assert.True(ValEq(t, validators[2], resVal)) + + resVals = keeper.GetLastValidators(ctx) + require.Equal(t, 3, len(resVals)) + assert.True(ValEq(t, validators[0], resVals[0])) // order doesn't matter here + assert.True(ValEq(t, validators[1], resVals[1])) + assert.True(ValEq(t, validators[2], resVals[2])) + + // remove a record + + // shouldn't be able to remove if status is not unbonded + assert.PanicsWithValue(t, + "cannot call RemoveValidator on bonded or unbonding validators", + func() { keeper.RemoveValidator(ctx, validators[1].OperatorAddress) }) + + // shouldn't be able to remove if there are still tokens left + validators[1].Status = sdk.Unbonded + keeper.SetValidator(ctx, validators[1]) + assert.PanicsWithValue(t, + "attempting to remove a validator which still contains tokens", + func() { keeper.RemoveValidator(ctx, validators[1].OperatorAddress) }) + + validators[1].Tokens = sdk.ZeroInt() // ...remove all tokens + keeper.SetValidator(ctx, validators[1]) // ...set the validator + keeper.RemoveValidator(ctx, validators[1].OperatorAddress) // Now it can be removed. + _, found = keeper.GetValidator(ctx, addrVals[1]) + require.False(t, found) +} + +// test how the validators are sorted, tests GetBondedValidatorsByPower +func GetValidatorSortingUnmixed(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + + // initialize some validators into the state + amts := []int64{0, 100, 1, 400, 200} + n := len(amts) + var validators [5]types.Validator + for i, amt := range amts { + validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + validators[i].Status = sdk.Bonded + validators[i].Tokens = sdk.NewInt(amt) + validators[i].DelegatorShares = sdk.NewDec(amt) + TestingUpdateValidator(keeper, ctx, validators[i], true) + } + + // first make sure everything made it in to the gotValidator group + resValidators := keeper.GetBondedValidatorsByPower(ctx) + assert.Equal(t, n, len(resValidators)) + assert.Equal(t, sdk.NewInt(400), resValidators[0].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(200), resValidators[1].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(100), resValidators[2].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(1), resValidators[3].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(0), resValidators[4].BondedTokens(), "%v", resValidators) + assert.Equal(t, validators[3].OperatorAddress, resValidators[0].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[4].OperatorAddress, resValidators[1].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[1].OperatorAddress, resValidators[2].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[2].OperatorAddress, resValidators[3].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[0].OperatorAddress, resValidators[4].OperatorAddress, "%v", resValidators) + + // test a basic increase in voting power + validators[3].Tokens = sdk.NewInt(500) + TestingUpdateValidator(keeper, ctx, validators[3], true) + resValidators = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, len(resValidators), n) + assert.True(ValEq(t, validators[3], resValidators[0])) + + // test a decrease in voting power + validators[3].Tokens = sdk.NewInt(300) + TestingUpdateValidator(keeper, ctx, validators[3], true) + resValidators = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, len(resValidators), n) + assert.True(ValEq(t, validators[3], resValidators[0])) + assert.True(ValEq(t, validators[4], resValidators[1])) + + // test equal voting power, different age + validators[3].Tokens = sdk.NewInt(200) + ctx = ctx.WithBlockHeight(10) + TestingUpdateValidator(keeper, ctx, validators[3], true) + resValidators = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, len(resValidators), n) + assert.True(ValEq(t, validators[3], resValidators[0])) + assert.True(ValEq(t, validators[4], resValidators[1])) + + // no change in voting power - no change in sort + ctx = ctx.WithBlockHeight(20) + TestingUpdateValidator(keeper, ctx, validators[4], true) + resValidators = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, len(resValidators), n) + assert.True(ValEq(t, validators[3], resValidators[0])) + assert.True(ValEq(t, validators[4], resValidators[1])) + + // change in voting power of both validators, both still in v-set, no age change + validators[3].Tokens = sdk.NewInt(300) + validators[4].Tokens = sdk.NewInt(300) + TestingUpdateValidator(keeper, ctx, validators[3], true) + resValidators = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, len(resValidators), n) + ctx = ctx.WithBlockHeight(30) + TestingUpdateValidator(keeper, ctx, validators[4], true) + resValidators = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, len(resValidators), n, "%v", resValidators) + assert.True(ValEq(t, validators[3], resValidators[0])) + assert.True(ValEq(t, validators[4], resValidators[1])) +} + +func GetValidatorSortingMixed(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + + // now 2 max resValidators + params := keeper.GetParams(ctx) + params.MaxValidators = 2 + keeper.SetParams(ctx, params) + + // initialize some validators into the state + amts := []int64{0, 100, 1, 400, 200} + + n := len(amts) + var validators [5]types.Validator + for i, amt := range amts { + validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + validators[i].DelegatorShares = sdk.NewDec(amt) + } + + validators[0].Status = sdk.Bonded + validators[1].Status = sdk.Bonded + validators[2].Status = sdk.Bonded + validators[0].Tokens = sdk.NewInt(amts[0]) + validators[1].Tokens = sdk.NewInt(amts[1]) + validators[2].Tokens = sdk.NewInt(amts[2]) + + validators[3].Status = sdk.Bonded + validators[4].Status = sdk.Bonded + validators[3].Tokens = sdk.NewInt(amts[3]) + validators[4].Tokens = sdk.NewInt(amts[4]) + + for i := range amts { + TestingUpdateValidator(keeper, ctx, validators[i], true) + } + val0, found := keeper.GetValidator(ctx, sdk.ValAddress(sdk.ValAddress(PKs[0].Address().Bytes()))) + require.True(t, found) + val1, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[1])) + require.True(t, found) + val2, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[2])) + require.True(t, found) + val3, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[3])) + require.True(t, found) + val4, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[4])) + require.True(t, found) + require.Equal(t, sdk.Unbonded, val0.Status) + require.Equal(t, sdk.Unbonded, val1.Status) + require.Equal(t, sdk.Unbonded, val2.Status) + require.Equal(t, sdk.Bonded, val3.Status) + require.Equal(t, sdk.Bonded, val4.Status) + + // first make sure everything made it in to the gotValidator group + resValidators := keeper.GetBondedValidatorsByPower(ctx) + assert.Equal(t, n, len(resValidators)) + assert.Equal(t, sdk.NewInt(400), resValidators[0].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(200), resValidators[1].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(100), resValidators[2].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(1), resValidators[3].BondedTokens(), "%v", resValidators) + assert.Equal(t, sdk.NewInt(0), resValidators[4].BondedTokens(), "%v", resValidators) + assert.Equal(t, validators[3].OperatorAddress, resValidators[0].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[4].OperatorAddress, resValidators[1].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[1].OperatorAddress, resValidators[2].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[2].OperatorAddress, resValidators[3].OperatorAddress, "%v", resValidators) + assert.Equal(t, validators[0].OperatorAddress, resValidators[4].OperatorAddress, "%v", resValidators) +} + +// TODO separate out into multiple tests +func TestGetValidatorsEdgeCases(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + + // set max validators to 2 + params := keeper.GetParams(ctx) + nMax := uint16(2) + params.MaxValidators = nMax + keeper.SetParams(ctx, params) + + // initialize some validators into the state + powers := []int64{0, 100, 400, 400} + var validators [4]types.Validator + for i, power := range powers { + moniker := fmt.Sprintf("val#%d", int64(i)) + validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{Moniker: moniker}) + tokens := sdk.TokensFromConsensusPower(power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + notBondedPool := keeper.GetNotBondedPool(ctx) + require.NoError(t, notBondedPool.SetCoins(notBondedPool.GetCoins().Add(sdk.NewCoins(sdk.NewCoin(params.BondDenom, tokens))))) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + validators[i] = TestingUpdateValidator(keeper, ctx, validators[i], true) + } + + // ensure that the first two bonded validators are the largest validators + resValidators := keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint16(len(resValidators))) + assert.True(ValEq(t, validators[2], resValidators[0])) + assert.True(ValEq(t, validators[3], resValidators[1])) + + // delegate 500 tokens to validator 0 + keeper.DeleteValidatorByPowerIndex(ctx, validators[0]) + delTokens := sdk.TokensFromConsensusPower(500) + validators[0], _ = validators[0].AddTokensFromDel(delTokens) + notBondedPool := keeper.GetNotBondedPool(ctx) + newTokens := sdk.NewCoins(sdk.NewCoin(params.BondDenom, delTokens)) + require.NoError(t, notBondedPool.SetCoins(notBondedPool.GetCoins().Add(newTokens))) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + // test that the two largest validators are + // a) validator 0 with 500 tokens + // b) validator 2 with 400 tokens (delegated before validator 3) + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) + resValidators = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint16(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) + + // A validator which leaves the bonded validator set due to a decrease in voting power, + // then increases to the original voting power, does not get its spot back in the + // case of a tie. + // + // Order of operations for this test: + // - validator 3 enter validator set with 1 new token + // - validator 3 removed validator set by removing 201 tokens (validator 2 enters) + // - validator 3 adds 200 tokens (equal to validator 2 now) and does not get its spot back + + // validator 3 enters bonded validator set + ctx = ctx.WithBlockHeight(40) + + validators[3] = keeper.mustGetValidator(ctx, validators[3].OperatorAddress) + keeper.DeleteValidatorByPowerIndex(ctx, validators[3]) + validators[3], _ = validators[3].AddTokensFromDel(sdk.TokensFromConsensusPower(1)) + + notBondedPool = keeper.GetNotBondedPool(ctx) + newTokens = sdk.NewCoins(sdk.NewCoin(params.BondDenom, sdk.TokensFromConsensusPower(1))) + require.NoError(t, notBondedPool.SetCoins(notBondedPool.GetCoins().Add(newTokens))) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + validators[3] = TestingUpdateValidator(keeper, ctx, validators[3], true) + resValidators = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint16(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[3], resValidators[1])) + + // validator 3 kicked out temporarily + keeper.DeleteValidatorByPowerIndex(ctx, validators[3]) + rmTokens := validators[3].TokensFromShares(sdk.NewDec(201)).TruncateInt() + validators[3], _ = validators[3].RemoveDelShares(sdk.NewDec(201)) + + bondedPool := keeper.GetBondedPool(ctx) + require.NoError(t, bondedPool.SetCoins(bondedPool.GetCoins().Add(sdk.NewCoins(sdk.NewCoin(params.BondDenom, rmTokens))))) + keeper.supplyKeeper.SetModuleAccount(ctx, bondedPool) + + validators[3] = TestingUpdateValidator(keeper, ctx, validators[3], true) + resValidators = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint16(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) + + // validator 3 does not get spot back + keeper.DeleteValidatorByPowerIndex(ctx, validators[3]) + validators[3], _ = validators[3].AddTokensFromDel(sdk.NewInt(200)) + + notBondedPool = keeper.GetNotBondedPool(ctx) + require.NoError(t, notBondedPool.SetCoins(notBondedPool.GetCoins().Add(sdk.NewCoins(sdk.NewCoin(params.BondDenom, sdk.NewInt(200)))))) + keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool) + + validators[3] = TestingUpdateValidator(keeper, ctx, validators[3], true) + resValidators = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, nMax, uint16(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) + _, exists := keeper.GetValidator(ctx, validators[3].OperatorAddress) + require.True(t, exists) +} + +func TestValidatorBondHeight(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + + // now 2 max resValidators + params := keeper.GetParams(ctx) + params.MaxValidators = 2 + keeper.SetParams(ctx, params) + + // initialize some validators into the state + var validators [3]types.Validator + validators[0] = types.NewValidator(sdk.ValAddress(sdk.ValAddress(PKs[0].Address().Bytes())), PKs[0], types.Description{}) + validators[1] = types.NewValidator(sdk.ValAddress(Addrs[1]), PKs[1], types.Description{}) + validators[2] = types.NewValidator(sdk.ValAddress(Addrs[2]), PKs[2], types.Description{}) + + tokens0 := sdk.TokensFromConsensusPower(200) + tokens1 := sdk.TokensFromConsensusPower(100) + tokens2 := sdk.TokensFromConsensusPower(100) + validators[0], _ = validators[0].AddTokensFromDel(tokens0) + validators[1], _ = validators[1].AddTokensFromDel(tokens1) + validators[2], _ = validators[2].AddTokensFromDel(tokens2) + + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) + + //////////////////////////////////////// + // If two validators both increase to the same voting power in the same block, + // the one with the first transaction should become bonded + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], true) + validators[2] = TestingUpdateValidator(keeper, ctx, validators[2], true) + + resValidators := keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, uint16(len(resValidators)), params.MaxValidators) + + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[1], resValidators[1])) + keeper.DeleteValidatorByPowerIndex(ctx, validators[1]) + keeper.DeleteValidatorByPowerIndex(ctx, validators[2]) + delTokens := sdk.TokensFromConsensusPower(50) + validators[1], _ = validators[1].AddTokensFromDel(delTokens) + validators[2], _ = validators[2].AddTokensFromDel(delTokens) + validators[2] = TestingUpdateValidator(keeper, ctx, validators[2], true) + resValidators = keeper.GetBondedValidatorsByPower(ctx) + require.Equal(t, params.MaxValidators, uint16(len(resValidators))) + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], true) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) +} + +func TestFullValidatorSetPowerChange(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + params := keeper.GetParams(ctx) + max := 2 + params.MaxValidators = uint16(2) + keeper.SetParams(ctx, params) + + // initialize some validators into the state + powers := []int64{0, 100, 400, 400, 200} + var validators [5]types.Validator + for i, power := range powers { + validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + tokens := sdk.TokensFromConsensusPower(power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + TestingUpdateValidator(keeper, ctx, validators[i], true) + } + for i := range powers { + var found bool + validators[i], found = keeper.GetValidator(ctx, validators[i].OperatorAddress) + require.True(t, found) + } + assert.Equal(t, sdk.Unbonded, validators[0].Status) + assert.Equal(t, sdk.Unbonding, validators[1].Status) + assert.Equal(t, sdk.Bonded, validators[2].Status) + assert.Equal(t, sdk.Bonded, validators[3].Status) + assert.Equal(t, sdk.Unbonded, validators[4].Status) + resValidators := keeper.GetBondedValidatorsByPower(ctx) + assert.Equal(t, max, len(resValidators)) + assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs + assert.True(ValEq(t, validators[3], resValidators[1])) + + // test a swap in voting power + + tokens := sdk.TokensFromConsensusPower(600) + validators[0], _ = validators[0].AddTokensFromDel(tokens) + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) + resValidators = keeper.GetBondedValidatorsByPower(ctx) + assert.Equal(t, max, len(resValidators)) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) +} + +func TestApplyAndReturnValidatorSetUpdatesAllNone(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + + powers := []int64{10, 20} + var validators [2]types.Validator + for i, power := range powers { + valPubKey := PKs[i+1] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + + validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{}) + tokens := sdk.TokensFromConsensusPower(power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + } + + // test from nothing to something + // tendermintUpdate set: {} -> {c1, c3} + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + keeper.SetValidator(ctx, validators[0]) + keeper.SetValidatorByPowerIndex(ctx, validators[0]) + keeper.SetValidator(ctx, validators[1]) + keeper.SetValidatorByPowerIndex(ctx, validators[1]) + + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + assert.Equal(t, 2, len(updates)) + validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddress) + validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddress) + assert.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1]) + assert.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0]) +} + +func TestApplyAndReturnValidatorSetUpdatesIdentical(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + + powers := []int64{10, 20} + var validators [2]types.Validator + for i, power := range powers { + validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + + tokens := sdk.TokensFromConsensusPower(power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + } + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) + require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + + // test identical, + // tendermintUpdate set: {} -> {} + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) +} + +func TestApplyAndReturnValidatorSetUpdatesSingleValueChange(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + + powers := []int64{10, 20} + var validators [2]types.Validator + for i, power := range powers { + + validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + + tokens := sdk.TokensFromConsensusPower(power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + } + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) + require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + + // test single value change + // tendermintUpdate set: {} -> {c1'} + validators[0].Status = sdk.Bonded + validators[0].Tokens = sdk.TokensFromConsensusPower(600) + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) + + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + + require.Equal(t, 1, len(updates)) + require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) +} + +func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + + powers := []int64{10, 20} + var validators [2]types.Validator + for i, power := range powers { + + validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + + tokens := sdk.TokensFromConsensusPower(power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + } + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) + require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + + // test multiple value change + // tendermintUpdate set: {c1, c3} -> {c1', c3'} + delTokens1 := sdk.TokensFromConsensusPower(190) + delTokens2 := sdk.TokensFromConsensusPower(80) + validators[0], _ = validators[0].AddTokensFromDel(delTokens1) + validators[1], _ = validators[1].AddTokensFromDel(delTokens2) + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) + + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(updates)) + require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) +} + +func TestApplyAndReturnValidatorSetUpdatesInserted(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + + powers := []int64{10, 20, 5, 15, 25} + var validators [5]types.Validator + for i, power := range powers { + + validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + + tokens := sdk.TokensFromConsensusPower(power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + } + + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) + require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + + // test validtor added at the beginning + // tendermintUpdate set: {} -> {c0} + keeper.SetValidator(ctx, validators[2]) + keeper.SetValidatorByPowerIndex(ctx, validators[2]) + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddress) + require.Equal(t, 1, len(updates)) + require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0]) + + // test validtor added at the beginning + // tendermintUpdate set: {} -> {c0} + keeper.SetValidator(ctx, validators[3]) + keeper.SetValidatorByPowerIndex(ctx, validators[3]) + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validators[3], _ = keeper.GetValidator(ctx, validators[3].OperatorAddress) + require.Equal(t, 1, len(updates)) + require.Equal(t, validators[3].ABCIValidatorUpdate(), updates[0]) + + // test validtor added at the end + // tendermintUpdate set: {} -> {c0} + keeper.SetValidator(ctx, validators[4]) + keeper.SetValidatorByPowerIndex(ctx, validators[4]) + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validators[4], _ = keeper.GetValidator(ctx, validators[4].OperatorAddress) + require.Equal(t, 1, len(updates)) + require.Equal(t, validators[4].ABCIValidatorUpdate(), updates[0]) +} + +func TestApplyAndReturnValidatorSetUpdatesWithCliffValidator(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + params := types.DefaultParams() + params.MaxValidators = 2 + keeper.SetParams(ctx, params) + + powers := []int64{10, 20, 5} + var validators [5]types.Validator + for i, power := range powers { + + validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + + tokens := sdk.TokensFromConsensusPower(power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + } + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) + require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + + // test validator added at the end but not inserted in the valset + // tendermintUpdate set: {} -> {} + TestingUpdateValidator(keeper, ctx, validators[2], false) + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 0, len(updates)) + + // test validator change its power and become a gotValidator (pushing out an existing) + // tendermintUpdate set: {} -> {c0, c4} + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + + tokens := sdk.TokensFromConsensusPower(10) + validators[2], _ = validators[2].AddTokensFromDel(tokens) + keeper.SetValidator(ctx, validators[2]) + keeper.SetValidatorByPowerIndex(ctx, validators[2]) + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddress) + require.Equal(t, 2, len(updates), "%v", updates) + require.Equal(t, validators[0].ABCIValidatorUpdateZero(), updates[1]) + require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0]) +} + +func TestApplyAndReturnValidatorSetUpdatesPowerDecrease(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + + powers := []int64{100, 100} + var validators [2]types.Validator + for i, power := range powers { + + validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) + + tokens := sdk.TokensFromConsensusPower(power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + } + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) + require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + + // check initial power + require.Equal(t, int64(100), validators[0].GetConsensusPower()) + require.Equal(t, int64(100), validators[1].GetConsensusPower()) + + // test multiple value change + // tendermintUpdate set: {c1, c3} -> {c1', c3'} + delTokens1 := sdk.TokensFromConsensusPower(20) + delTokens2 := sdk.TokensFromConsensusPower(30) + validators[0], _ = validators[0].RemoveDelShares(delTokens1.ToDec()) + validators[1], _ = validators[1].RemoveDelShares(delTokens2.ToDec()) + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) + validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) + + // power has changed + require.Equal(t, int64(80), validators[0].GetConsensusPower()) + require.Equal(t, int64(70), validators[1].GetConsensusPower()) + + // Tendermint updates should reflect power change + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(updates)) + require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) +} + +func TestApplyAndReturnValidatorSetUpdatesNewValidator(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + params := keeper.GetParams(ctx) + params.MaxValidators = uint16(3) + + keeper.SetParams(ctx, params) + + powers := []int64{100, 100} + var validators [2]types.Validator + + // initialize some validators into the state + for i, power := range powers { + + valPubKey := PKs[i+1] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + + validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{}) + tokens := sdk.TokensFromConsensusPower(power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + keeper.SetValidator(ctx, validators[i]) + keeper.SetValidatorByPowerIndex(ctx, validators[i]) + } + + // verify initial Tendermint updates are correct + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, len(validators), len(updates)) + validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddress) + validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddress) + require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0]) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) + + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + + // update initial validator set + for i, power := range powers { + + keeper.DeleteValidatorByPowerIndex(ctx, validators[i]) + tokens := sdk.TokensFromConsensusPower(power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + + keeper.SetValidator(ctx, validators[i]) + keeper.SetValidatorByPowerIndex(ctx, validators[i]) + } + + // add a new validator that goes from zero power, to non-zero power, back to + // zero power + valPubKey := PKs[len(validators)+1] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + amt := sdk.NewInt(100) + + validator := types.NewValidator(valAddr, valPubKey, types.Description{}) + validator, _ = validator.AddTokensFromDel(amt) + + keeper.SetValidator(ctx, validator) + + validator, _ = validator.RemoveDelShares(amt.ToDec()) + keeper.SetValidator(ctx, validator) + keeper.SetValidatorByPowerIndex(ctx, validator) + + // add a new validator that increases in power + valPubKey = PKs[len(validators)+2] + valAddr = sdk.ValAddress(valPubKey.Address().Bytes()) + + validator = types.NewValidator(valAddr, valPubKey, types.Description{}) + tokens := sdk.TokensFromConsensusPower(500) + validator, _ = validator.AddTokensFromDel(tokens) + keeper.SetValidator(ctx, validator) + keeper.SetValidatorByPowerIndex(ctx, validator) + + // verify initial Tendermint updates are correct + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + validator, _ = keeper.GetValidator(ctx, validator.OperatorAddress) + validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddress) + validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddress) + require.Equal(t, len(validators)+1, len(updates)) + require.Equal(t, validator.ABCIValidatorUpdate(), updates[0]) + require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1]) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[2]) +} + +func TestApplyAndReturnValidatorSetUpdatesBondTransition(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + params := keeper.GetParams(ctx) + params.MaxValidators = uint16(2) + + keeper.SetParams(ctx, params) + + powers := []int64{100, 200, 300} + var validators [3]types.Validator + + // initialize some validators into the state + for i, power := range powers { + moniker := fmt.Sprintf("%d", i) + valPubKey := PKs[i+1] + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + + validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{Moniker: moniker}) + tokens := sdk.TokensFromConsensusPower(power) + validators[i], _ = validators[i].AddTokensFromDel(tokens) + keeper.SetValidator(ctx, validators[i]) + keeper.SetValidatorByPowerIndex(ctx, validators[i]) + } + + // verify initial Tendermint updates are correct + updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 2, len(updates)) + validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddress) + validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddress) + require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0]) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1]) + + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + + // delegate to validator with lowest power but not enough to bond + ctx = ctx.WithBlockHeight(1) + + var found bool + validators[0], found = keeper.GetValidator(ctx, validators[0].OperatorAddress) + require.True(t, found) + + keeper.DeleteValidatorByPowerIndex(ctx, validators[0]) + tokens := sdk.TokensFromConsensusPower(1) + validators[0], _ = validators[0].AddTokensFromDel(tokens) + keeper.SetValidator(ctx, validators[0]) + keeper.SetValidatorByPowerIndex(ctx, validators[0]) + + // verify initial Tendermint updates are correct + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) + + // create a series of events that will bond and unbond the validator with + // lowest power in a single block context (height) + ctx = ctx.WithBlockHeight(2) + + validators[1], found = keeper.GetValidator(ctx, validators[1].OperatorAddress) + require.True(t, found) + + keeper.DeleteValidatorByPowerIndex(ctx, validators[0]) + validators[0], _ = validators[0].RemoveDelShares(validators[0].DelegatorShares) + keeper.SetValidator(ctx, validators[0]) + keeper.SetValidatorByPowerIndex(ctx, validators[0]) + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 0, len(updates)) + + keeper.DeleteValidatorByPowerIndex(ctx, validators[1]) + tokens = sdk.TokensFromConsensusPower(250) + validators[1], _ = validators[1].AddTokensFromDel(tokens) + keeper.SetValidator(ctx, validators[1]) + keeper.SetValidatorByPowerIndex(ctx, validators[1]) + + // verify initial Tendermint updates are correct + updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) + require.Equal(t, 1, len(updates)) + require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0]) + + require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) +} + +func TestUpdateValidatorCommission(t *testing.T) { + ctx, _, keeper, _ := CreateTestInput(t, false, 1000) + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Now().UTC()}) + + commission1 := types.NewCommissionWithTime( + sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(3, 1), + sdk.NewDecWithPrec(1, 1), time.Now().UTC().Add(time.Duration(-1)*time.Hour), + ) + commission2 := types.NewCommission(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(3, 1), sdk.NewDecWithPrec(1, 1)) + + val1 := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + val2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + + val1, _ = val1.SetInitialCommission(commission1) + val2, _ = val2.SetInitialCommission(commission2) + + keeper.SetValidator(ctx, val1) + keeper.SetValidator(ctx, val2) + + testCases := []struct { + validator types.Validator + newRate sdk.Dec + expectedErr bool + }{ + {val1, sdk.ZeroDec(), true}, + {val2, sdk.NewDecWithPrec(-1, 1), true}, + {val2, sdk.NewDecWithPrec(4, 1), true}, + {val2, sdk.NewDecWithPrec(3, 1), true}, + {val2, sdk.NewDecWithPrec(2, 1), false}, + } + + for i, tc := range testCases { + commission, err := keeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate) + + if tc.expectedErr { + require.Error(t, err, "expected error for test case #%d with rate: %s", i, tc.newRate) + } else { + tc.validator.Commission = commission + keeper.SetValidator(ctx, tc.validator) + val, found := keeper.GetValidator(ctx, tc.validator.OperatorAddress) + + require.True(t, found, + "expected to find validator for test case #%d with rate: %s", i, tc.newRate, + ) + require.NoError(t, err, + "unexpected error for test case #%d with rate: %s", i, tc.newRate, + ) + require.Equal(t, tc.newRate, val.Commission.Rate, + "expected new validator commission rate for test case #%d with rate: %s", i, tc.newRate, + ) + require.Equal(t, ctx.BlockHeader().Time, val.Commission.UpdateTime, + "expected new validator commission update time for test case #%d with rate: %s", i, tc.newRate, + ) + } + } +} diff --git a/x/staking/legacy/v0_34/types.go b/x/staking/legacy/v0_34/types.go new file mode 100644 index 0000000..64e997e --- /dev/null +++ b/x/staking/legacy/v0_34/types.go @@ -0,0 +1,173 @@ +// DONTCOVER +// nolint +package v0_34 + +import ( + "time" + + "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + ModuleName = "staking" +) + +type ( + Pool struct { + NotBondedTokens sdk.Int `json:"not_bonded_tokens"` + BondedTokens sdk.Int `json:"bonded_tokens"` + } + + Params struct { + UnbondingTime time.Duration `json:"unbonding_time"` + MaxValidators uint16 `json:"max_validators"` + MaxEntries uint16 `json:"max_entries"` + BondDenom string `json:"bond_denom"` + } + + LastValidatorPower struct { + Address sdk.ValAddress + Power int64 + } + + Description struct { + Moniker string `json:"moniker"` + Identity string `json:"identity"` + Website string `json:"website"` + Details string `json:"details"` + } + + Commission struct { + Rate sdk.Dec `json:"rate"` + MaxRate sdk.Dec `json:"max_rate"` + MaxChangeRate sdk.Dec `json:"max_change_rate"` + UpdateTime time.Time `json:"update_time"` + } + + bechValidator struct { + OperatorAddress sdk.ValAddress `json:"operator_address"` // the bech32 address of the validator's operator + ConsPubKey string `json:"consensus_pubkey"` // the bech32 consensus public key of the validator + Jailed bool `json:"jailed"` // has the validator been jailed from bonded status? + Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded) + Tokens sdk.Int `json:"tokens"` // delegated tokens (incl. self-delegation) + DelegatorShares sdk.Dec `json:"delegator_shares"` // total shares issued to a validator's delegators + Description Description `json:"description"` // description terms for the validator + UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding + UnbondingCompletionTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding + Commission Commission `json:"commission"` // commission parameters + MinSelfDelegation sdk.Int `json:"min_self_delegation"` // minimum self delegation + } + + Validator struct { + OperatorAddress sdk.ValAddress `json:"operator_address"` + ConsPubKey crypto.PubKey `json:"consensus_pubkey"` + Jailed bool `json:"jailed"` + Status sdk.BondStatus `json:"status"` + Tokens sdk.Int `json:"tokens"` + DelegatorShares sdk.Dec `json:"delegator_shares"` + Description Description `json:"description"` + UnbondingHeight int64 `json:"unbonding_height"` + UnbondingCompletionTime time.Time `json:"unbonding_time"` + Commission Commission `json:"commission"` + MinSelfDelegation sdk.Int `json:"min_self_delegation"` + } + + Validators []Validator + + Delegation struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address"` + ValidatorAddress sdk.ValAddress `json:"validator_address"` + Shares sdk.Dec `json:"shares"` + } + + Delegations []Delegation + + UnbondingDelegationEntry struct { + CreationHeight int64 `json:"creation_height"` + CompletionTime time.Time `json:"completion_time"` + InitialBalance sdk.Int `json:"initial_balance"` + Balance sdk.Int `json:"balance"` + } + + UnbondingDelegation struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address"` + ValidatorAddress sdk.ValAddress `json:"validator_address"` + Entries []UnbondingDelegationEntry `json:"entries"` + } + + RedelegationEntry struct { + CreationHeight int64 `json:"creation_height"` + CompletionTime time.Time `json:"completion_time"` + InitialBalance sdk.Int `json:"initial_balance"` + SharesDst sdk.Dec `json:"shares_dst"` + } + + Redelegation struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address"` + ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address"` + ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address"` + Entries []RedelegationEntry `json:"entries"` + } + + GenesisState struct { + Pool Pool `json:"pool"` + Params Params `json:"params"` + LastTotalPower sdk.Int `json:"last_total_power"` + LastValidatorPowers []LastValidatorPower `json:"last_validator_powers"` + Validators Validators `json:"validators"` + Delegations Delegations `json:"delegations"` + UnbondingDelegations []UnbondingDelegation `json:"unbonding_delegations"` + Redelegations []Redelegation `json:"redelegations"` + Exported bool `json:"exported"` + } +) + +func (v Validator) MarshalJSON() ([]byte, error) { + bechConsPubKey, err := sdk.Bech32ifyConsPub(v.ConsPubKey) + if err != nil { + return nil, err + } + + return codec.Cdc.MarshalJSON(bechValidator{ + OperatorAddress: v.OperatorAddress, + ConsPubKey: bechConsPubKey, + Jailed: v.Jailed, + Status: v.Status, + Tokens: v.Tokens, + DelegatorShares: v.DelegatorShares, + Description: v.Description, + UnbondingHeight: v.UnbondingHeight, + UnbondingCompletionTime: v.UnbondingCompletionTime, + MinSelfDelegation: v.MinSelfDelegation, + Commission: v.Commission, + }) +} + +// UnmarshalJSON unmarshals the validator from JSON using Bech32 +func (v *Validator) UnmarshalJSON(data []byte) error { + bv := &bechValidator{} + if err := codec.Cdc.UnmarshalJSON(data, bv); err != nil { + return err + } + consPubKey, err := sdk.GetConsPubKeyBech32(bv.ConsPubKey) + if err != nil { + return err + } + *v = Validator{ + OperatorAddress: bv.OperatorAddress, + ConsPubKey: consPubKey, + Jailed: bv.Jailed, + Tokens: bv.Tokens, + Status: bv.Status, + DelegatorShares: bv.DelegatorShares, + Description: bv.Description, + UnbondingHeight: bv.UnbondingHeight, + UnbondingCompletionTime: bv.UnbondingCompletionTime, + Commission: bv.Commission, + MinSelfDelegation: bv.MinSelfDelegation, + } + return nil +} diff --git a/x/staking/legacy/v0_36/migrate.go b/x/staking/legacy/v0_36/migrate.go new file mode 100644 index 0000000..0d1b0fa --- /dev/null +++ b/x/staking/legacy/v0_36/migrate.go @@ -0,0 +1,52 @@ +// DONTCOVER +// nolint +package v0_36 + +import ( + v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_34" +) + +// Migrate accepts exported genesis state from v0.34 and migrates it to v0.36 +// genesis state. All entries are identical except for validator slashing events +// which now include the period. +func Migrate(oldGenState v034staking.GenesisState) GenesisState { + return NewGenesisState( + oldGenState.Params, + oldGenState.LastTotalPower, + oldGenState.LastValidatorPowers, + migrateValidators(oldGenState.Validators), + oldGenState.Delegations, + oldGenState.UnbondingDelegations, + oldGenState.Redelegations, + oldGenState.Exported, + ) +} + +func migrateValidators(oldValidators v034staking.Validators) Validators { + validators := make(Validators, len(oldValidators)) + + for i, val := range oldValidators { + validators[i] = Validator{ + OperatorAddress: val.OperatorAddress, + ConsPubKey: val.ConsPubKey, + Jailed: val.Jailed, + Status: val.Status, + Tokens: val.Tokens, + DelegatorShares: val.DelegatorShares, + Description: val.Description, + UnbondingHeight: val.UnbondingHeight, + UnbondingCompletionTime: val.UnbondingCompletionTime, + Commission: Commission{ + CommissionRates: CommissionRates{ + Rate: val.Commission.Rate, + MaxRate: val.Commission.MaxRate, + MaxChangeRate: val.Commission.MaxChangeRate, + }, + UpdateTime: val.Commission.UpdateTime, + }, + MinSelfDelegation: val.MinSelfDelegation, + } + } + + return validators +} diff --git a/x/staking/legacy/v0_36/types.go b/x/staking/legacy/v0_36/types.go new file mode 100644 index 0000000..e584228 --- /dev/null +++ b/x/staking/legacy/v0_36/types.go @@ -0,0 +1,74 @@ +// DONTCOVER +// nolint +package v0_36 + +import ( + "time" + + "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" + v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_34" +) + +const ( + ModuleName = "staking" +) + +type ( + Commission struct { + CommissionRates `json:"commission_rates" yaml:"commission_rates"` + UpdateTime time.Time `json:"update_time" yaml:"update_time"` + } + + CommissionRates struct { + Rate sdk.Dec `json:"rate" yaml:"rate"` + MaxRate sdk.Dec `json:"max_rate" yaml:"max_rate"` + MaxChangeRate sdk.Dec `json:"max_change_rate" yaml:"max_change_rate"` + } + + Validator struct { + OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` + ConsPubKey crypto.PubKey `json:"consensus_pubkey" yaml:"consensus_pubkey"` + Jailed bool `json:"jailed" yaml:"jailed"` + Status sdk.BondStatus `json:"status" yaml:"status"` + Tokens sdk.Int `json:"tokens" yaml:"tokens"` + DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` + Description v034staking.Description `json:"description" yaml:"description"` + UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` + UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` + Commission Commission `json:"commission" yaml:"commission"` + MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` + } + + Validators []Validator + + GenesisState struct { + Params v034staking.Params `json:"params"` + LastTotalPower sdk.Int `json:"last_total_power"` + LastValidatorPowers []v034staking.LastValidatorPower `json:"last_validator_powers"` + Validators Validators `json:"validators"` + Delegations v034staking.Delegations `json:"delegations"` + UnbondingDelegations []v034staking.UnbondingDelegation `json:"unbonding_delegations"` + Redelegations []v034staking.Redelegation `json:"redelegations"` + Exported bool `json:"exported"` + } +) + +func NewGenesisState( + params v034staking.Params, lastTotalPower sdk.Int, lastValPowers []v034staking.LastValidatorPower, + validators Validators, delegations v034staking.Delegations, + ubds []v034staking.UnbondingDelegation, reds []v034staking.Redelegation, exported bool, +) GenesisState { + + return GenesisState{ + Params: params, + LastTotalPower: lastTotalPower, + LastValidatorPowers: lastValPowers, + Validators: validators, + Delegations: delegations, + UnbondingDelegations: ubds, + Redelegations: reds, + Exported: exported, + } +} diff --git a/x/staking/module.go b/x/staking/module.go new file mode 100644 index 0000000..8dcb26c --- /dev/null +++ b/x/staking/module.go @@ -0,0 +1,166 @@ +package staking + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + flag "github.com/spf13/pflag" + + abci "github.com/tendermint/tendermint/abci/types" + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/staking/client/cli" + "github.com/cosmos/cosmos-sdk/x/staking/client/rest" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// app module basics object +type AppModuleBasic struct{} + +var _ module.AppModuleBasic = AppModuleBasic{} + +// module name +func (AppModuleBasic) Name() string { + return ModuleName +} + +// register module codec +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) +} + +// default genesis state +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +} + +// module validate genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + var data GenesisState + err := ModuleCdc.UnmarshalJSON(bz, &data) + if err != nil { + return err + } + return ValidateGenesis(data) +} + +// register rest routes +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + rest.RegisterRoutes(ctx, rtr) +} + +// get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetTxCmd(StoreKey, cdc) +} + +// get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetQueryCmd(StoreKey, cdc) +} + +//_____________________________________ +// extra helpers + +// CreateValidatorMsgHelpers - used for gen-tx +func (AppModuleBasic) CreateValidatorMsgHelpers(ipDefault string) ( + fs *flag.FlagSet, nodeIDFlag, pubkeyFlag, amountFlag, defaultsDesc string) { + return cli.CreateValidatorMsgHelpers(ipDefault) +} + +// PrepareFlagsForTxCreateValidator - used for gen-tx +func (AppModuleBasic) PrepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, + chainID string, valPubKey crypto.PubKey) { + cli.PrepareFlagsForTxCreateValidator(config, nodeID, chainID, valPubKey) +} + +// BuildCreateValidatorMsg - used for gen-tx +func (AppModuleBasic) BuildCreateValidatorMsg(cliCtx context.CLIContext, + txBldr authtypes.TxBuilder) (authtypes.TxBuilder, sdk.Msg, error) { + return cli.BuildCreateValidatorMsg(cliCtx, txBldr) +} + +// app module +type AppModule struct { + AppModuleBasic + keeper Keeper + distrKeeper types.DistributionKeeper + accKeeper types.AccountKeeper + supplyKeeper types.SupplyKeeper +} + +// NewAppModule creates a new AppModule object +func NewAppModule(keeper Keeper, distrKeeper types.DistributionKeeper, accKeeper types.AccountKeeper, + supplyKeeper types.SupplyKeeper) AppModule { + + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + keeper: keeper, + distrKeeper: distrKeeper, + accKeeper: accKeeper, + supplyKeeper: supplyKeeper, + } +} + +// module name +func (AppModule) Name() string { + return ModuleName +} + +// register invariants +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + RegisterInvariants(ir, am.keeper) +} + +// module message route name +func (AppModule) Route() string { + return RouterKey +} + +// module handler +func (am AppModule) NewHandler() sdk.Handler { + return NewHandler(am.keeper) +} + +// module querier route name +func (AppModule) QuerierRoute() string { + return QuerierRoute +} + +// module querier +func (am AppModule) NewQuerierHandler() sdk.Querier { + return NewQuerier(am.keeper) +} + +// module init-genesis +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState GenesisState + ModuleCdc.MustUnmarshalJSON(data, &genesisState) + return InitGenesis(ctx, am.keeper, am.accKeeper, am.supplyKeeper, genesisState) +} + +// module export genesis +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + gs := ExportGenesis(ctx, am.keeper) + return ModuleCdc.MustMarshalJSON(gs) +} + +// module begin-block +func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// module end-block +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return EndBlocker(ctx, am.keeper) +} diff --git a/x/staking/simulation/msgs.go b/x/staking/simulation/msgs.go new file mode 100644 index 0000000..c16d407 --- /dev/null +++ b/x/staking/simulation/msgs.go @@ -0,0 +1,222 @@ +package simulation + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/keeper" +) + +// SimulateMsgCreateValidator generates a MsgCreateValidator with random values +func SimulateMsgCreateValidator(m auth.AccountKeeper, k staking.Keeper) simulation.Operation { + handler := staking.NewHandler(k) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account) ( + opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { + + denom := k.GetParams(ctx).BondDenom + description := staking.Description{ + Moniker: simulation.RandStringOfLength(r, 10), + } + + maxCommission := sdk.NewDecWithPrec(r.Int63n(1000), 3) + commission := staking.NewCommissionRates( + simulation.RandomDecAmount(r, maxCommission), + maxCommission, + simulation.RandomDecAmount(r, maxCommission), + ) + + acc := simulation.RandomAcc(r, accs) + address := sdk.ValAddress(acc.Address) + amount := m.GetAccount(ctx, acc.Address).GetCoins().AmountOf(denom) + if amount.GT(sdk.ZeroInt()) { + amount = simulation.RandomAmount(r, amount) + } + + if amount.Equal(sdk.ZeroInt()) { + return simulation.NoOpMsg(staking.ModuleName), nil, nil + } + + selfDelegation := sdk.NewCoin(denom, amount) + msg := staking.NewMsgCreateValidator(address, acc.PubKey, + selfDelegation, description, commission, sdk.OneInt()) + + if msg.ValidateBasic() != nil { + return simulation.NoOpMsg(staking.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + } + + ctx, write := ctx.CacheContext() + ok := handler(ctx, msg).IsOK() + if ok { + write() + } + + opMsg = simulation.NewOperationMsg(msg, ok, "") + return opMsg, nil, nil + } +} + +// SimulateMsgEditValidator generates a MsgEditValidator with random values +func SimulateMsgEditValidator(k staking.Keeper) simulation.Operation { + handler := staking.NewHandler(k) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { + + description := staking.Description{ + Moniker: simulation.RandStringOfLength(r, 10), + Identity: simulation.RandStringOfLength(r, 10), + Website: simulation.RandStringOfLength(r, 10), + Details: simulation.RandStringOfLength(r, 10), + } + + if len(k.GetAllValidators(ctx)) == 0 { + return simulation.NoOpMsg(staking.ModuleName), nil, nil + } + val := keeper.RandomValidator(r, k, ctx) + address := val.GetOperator() + newCommissionRate := simulation.RandomDecAmount(r, val.Commission.MaxRate) + + msg := staking.NewMsgEditValidator(address, description, &newCommissionRate, nil) + + if msg.ValidateBasic() != nil { + return simulation.NoOpMsg(staking.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + } + ctx, write := ctx.CacheContext() + ok := handler(ctx, msg).IsOK() + if ok { + write() + } + opMsg = simulation.NewOperationMsg(msg, ok, "") + return opMsg, nil, nil + } +} + +// SimulateMsgDelegate generates a MsgDelegate with random values +func SimulateMsgDelegate(m auth.AccountKeeper, k staking.Keeper) simulation.Operation { + handler := staking.NewHandler(k) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { + + denom := k.GetParams(ctx).BondDenom + if len(k.GetAllValidators(ctx)) == 0 { + return simulation.NoOpMsg(staking.ModuleName), nil, nil + } + val := keeper.RandomValidator(r, k, ctx) + validatorAddress := val.GetOperator() + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address + amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom) + if amount.GT(sdk.ZeroInt()) { + amount = simulation.RandomAmount(r, amount) + } + if amount.Equal(sdk.ZeroInt()) { + return simulation.NoOpMsg(staking.ModuleName), nil, nil + } + + msg := staking.NewMsgDelegate( + delegatorAddress, validatorAddress, sdk.NewCoin(denom, amount)) + + if msg.ValidateBasic() != nil { + return simulation.NoOpMsg(staking.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + } + ctx, write := ctx.CacheContext() + ok := handler(ctx, msg).IsOK() + if ok { + write() + } + opMsg = simulation.NewOperationMsg(msg, ok, "") + return opMsg, nil, nil + } +} + +// SimulateMsgUndelegate generates a MsgUndelegate with random values +func SimulateMsgUndelegate(m auth.AccountKeeper, k staking.Keeper) simulation.Operation { + handler := staking.NewHandler(k) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { + + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address + delegations := k.GetAllDelegatorDelegations(ctx, delegatorAddress) + if len(delegations) == 0 { + return simulation.NoOpMsg(staking.ModuleName), nil, nil + } + delegation := delegations[r.Intn(len(delegations))] + + validator, found := k.GetValidator(ctx, delegation.GetValidatorAddr()) + if !found { + return simulation.NoOpMsg(staking.ModuleName), nil, nil + } + + totalBond := validator.TokensFromShares(delegation.GetShares()).TruncateInt() + unbondAmt := simulation.RandomAmount(r, totalBond) + if unbondAmt.Equal(sdk.ZeroInt()) { + return simulation.NoOpMsg(staking.ModuleName), nil, nil + } + + msg := staking.NewMsgUndelegate( + delegatorAddress, delegation.ValidatorAddress, sdk.NewCoin(k.GetParams(ctx).BondDenom, unbondAmt), + ) + if msg.ValidateBasic() != nil { + return simulation.NoOpMsg(staking.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s, got error %v", + msg.GetSignBytes(), msg.ValidateBasic()) + } + + ctx, write := ctx.CacheContext() + ok := handler(ctx, msg).IsOK() + if ok { + write() + } + + opMsg = simulation.NewOperationMsg(msg, ok, "") + return opMsg, nil, nil + } +} + +// SimulateMsgBeginRedelegate generates a MsgBeginRedelegate with random values +func SimulateMsgBeginRedelegate(m auth.AccountKeeper, k staking.Keeper) simulation.Operation { + handler := staking.NewHandler(k) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, + accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { + + denom := k.GetParams(ctx).BondDenom + if len(k.GetAllValidators(ctx)) == 0 { + return simulation.NoOpMsg(staking.ModuleName), nil, nil + } + srcVal := keeper.RandomValidator(r, k, ctx) + srcValidatorAddress := srcVal.GetOperator() + destVal := keeper.RandomValidator(r, k, ctx) + destValidatorAddress := destVal.GetOperator() + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address + // TODO + amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom) + if amount.GT(sdk.ZeroInt()) { + amount = simulation.RandomAmount(r, amount) + } + if amount.Equal(sdk.ZeroInt()) { + return simulation.NoOpMsg(staking.ModuleName), nil, nil + } + + msg := staking.NewMsgBeginRedelegate( + delegatorAddress, srcValidatorAddress, destValidatorAddress, sdk.NewCoin(denom, amount), + ) + if msg.ValidateBasic() != nil { + return simulation.NoOpMsg(staking.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + } + + ctx, write := ctx.CacheContext() + ok := handler(ctx, msg).IsOK() + if ok { + write() + } + + opMsg = simulation.NewOperationMsg(msg, ok, "") + return opMsg, nil, nil + } +} diff --git a/x/staking/test_common.go b/x/staking/test_common.go new file mode 100644 index 0000000..94f0f9a --- /dev/null +++ b/x/staking/test_common.go @@ -0,0 +1,57 @@ +package staking + +import ( + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/secp256k1" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// nolint: deadcode unused +var ( + priv1 = secp256k1.GenPrivKey() + addr1 = sdk.AccAddress(priv1.PubKey().Address()) + priv2 = secp256k1.GenPrivKey() + addr2 = sdk.AccAddress(priv2.PubKey().Address()) + addr3 = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + priv4 = secp256k1.GenPrivKey() + addr4 = sdk.AccAddress(priv4.PubKey().Address()) + coins = sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(10))} + fee = auth.NewStdFee( + 100000, + sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(0))}, + ) + + commissionRates = NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) +) + +func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) MsgCreateValidator { + return types.NewMsgCreateValidator( + address, pubKey, sdk.NewCoin(sdk.DefaultBondDenom, amt), Description{}, commissionRates, sdk.OneInt(), + ) +} + +func NewTestMsgCreateValidatorWithCommission(address sdk.ValAddress, pubKey crypto.PubKey, + amt sdk.Int, commissionRate sdk.Dec) MsgCreateValidator { + + commission := NewCommissionRates(commissionRate, sdk.OneDec(), sdk.ZeroDec()) + + return types.NewMsgCreateValidator( + address, pubKey, sdk.NewCoin(sdk.DefaultBondDenom, amt), Description{}, commission, sdk.OneInt(), + ) +} + +func NewTestMsgCreateValidatorWithMinSelfDelegation(address sdk.ValAddress, pubKey crypto.PubKey, + amt sdk.Int, minSelfDelegation sdk.Int) MsgCreateValidator { + + return types.NewMsgCreateValidator( + address, pubKey, sdk.NewCoin(sdk.DefaultBondDenom, amt), Description{}, commissionRates, minSelfDelegation, + ) +} + +func NewTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt sdk.Int) MsgDelegate { + amount := sdk.NewCoin(sdk.DefaultBondDenom, amt) + return NewMsgDelegate(delAddr, valAddr, amount) +} diff --git a/x/staking/types/codec.go b/x/staking/types/codec.go new file mode 100644 index 0000000..6fadfd9 --- /dev/null +++ b/x/staking/types/codec.go @@ -0,0 +1,24 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +// Register concrete types on codec codec +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(MsgCreateValidator{}, "cosmos-sdk/MsgCreateValidator", nil) + cdc.RegisterConcrete(MsgEditValidator{}, "cosmos-sdk/MsgEditValidator", nil) + cdc.RegisterConcrete(MsgDelegate{}, "cosmos-sdk/MsgDelegate", nil) + cdc.RegisterConcrete(MsgUndelegate{}, "cosmos-sdk/MsgUndelegate", nil) + cdc.RegisterConcrete(MsgBeginRedelegate{}, "cosmos-sdk/MsgBeginRedelegate", nil) +} + +// generic sealed codec to be used throughout this module +var ModuleCdc *codec.Codec + +func init() { + ModuleCdc = codec.New() + RegisterCodec(ModuleCdc) + codec.RegisterCrypto(ModuleCdc) + ModuleCdc.Seal() +} diff --git a/x/staking/types/commission.go b/x/staking/types/commission.go new file mode 100644 index 0000000..9b7b327 --- /dev/null +++ b/x/staking/types/commission.go @@ -0,0 +1,122 @@ +package types + +import ( + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type ( + // Commission defines a commission parameters for a given validator. + Commission struct { + CommissionRates `json:"commission_rates" yaml:"commission_rates"` + UpdateTime time.Time `json:"update_time" yaml:"update_time"` // the last time the commission rate was changed + } + + // CommissionRates defines the initial commission rates to be used for creating a + // validator. + CommissionRates struct { + Rate sdk.Dec `json:"rate" yaml:"rate"` // the commission rate charged to delegators, as a fraction + MaxRate sdk.Dec `json:"max_rate" yaml:"max_rate"` // maximum commission rate which validator can ever charge, as a fraction + MaxChangeRate sdk.Dec `json:"max_change_rate" yaml:"max_change_rate"` // maximum daily increase of the validator commission, as a fraction + } +) + +// NewCommissionRates returns an initialized validator commission rates. +func NewCommissionRates(rate, maxRate, maxChangeRate sdk.Dec) CommissionRates { + return CommissionRates{ + Rate: rate, + MaxRate: maxRate, + MaxChangeRate: maxChangeRate, + } +} + +// NewCommission returns an initialized validator commission. +func NewCommission(rate, maxRate, maxChangeRate sdk.Dec) Commission { + return Commission{ + CommissionRates: NewCommissionRates(rate, maxRate, maxChangeRate), + UpdateTime: time.Unix(0, 0).UTC(), + } +} + +// NewCommission returns an initialized validator commission with a specified +// update time which should be the current block BFT time. +func NewCommissionWithTime(rate, maxRate, maxChangeRate sdk.Dec, updatedAt time.Time) Commission { + return Commission{ + CommissionRates: NewCommissionRates(rate, maxRate, maxChangeRate), + UpdateTime: updatedAt, + } +} + +// Equal checks if the given Commission object is equal to the receiving +// Commission object. +func (c Commission) Equal(c2 Commission) bool { + return c.Rate.Equal(c2.Rate) && + c.MaxRate.Equal(c2.MaxRate) && + c.MaxChangeRate.Equal(c2.MaxChangeRate) && + c.UpdateTime.Equal(c2.UpdateTime) +} + +// String implements the Stringer interface for a Commission. +func (c Commission) String() string { + return fmt.Sprintf("rate: %s, maxRate: %s, maxChangeRate: %s, updateTime: %s", + c.Rate, c.MaxRate, c.MaxChangeRate, c.UpdateTime, + ) +} + +// Validate performs basic sanity validation checks of initial commission +// parameters. If validation fails, an SDK error is returned. +func (c CommissionRates) Validate() sdk.Error { + switch { + case c.MaxRate.LT(sdk.ZeroDec()): + // max rate cannot be negative + return ErrCommissionNegative(DefaultCodespace) + + case c.MaxRate.GT(sdk.OneDec()): + // max rate cannot be greater than 1 + return ErrCommissionHuge(DefaultCodespace) + + case c.Rate.LT(sdk.ZeroDec()): + // rate cannot be negative + return ErrCommissionNegative(DefaultCodespace) + + case c.Rate.GT(c.MaxRate): + // rate cannot be greater than the max rate + return ErrCommissionGTMaxRate(DefaultCodespace) + + case c.MaxChangeRate.LT(sdk.ZeroDec()): + // change rate cannot be negative + return ErrCommissionChangeRateNegative(DefaultCodespace) + + case c.MaxChangeRate.GT(c.MaxRate): + // change rate cannot be greater than the max rate + return ErrCommissionChangeRateGTMaxRate(DefaultCodespace) + } + + return nil +} + +// ValidateNewRate performs basic sanity validation checks of a new commission +// rate. If validation fails, an SDK error is returned. +func (c Commission) ValidateNewRate(newRate sdk.Dec, blockTime time.Time) sdk.Error { + switch { + case blockTime.Sub(c.UpdateTime).Hours() < 24: + // new rate cannot be changed more than once within 24 hours + return ErrCommissionUpdateTime(DefaultCodespace) + + case newRate.LT(sdk.ZeroDec()): + // new rate cannot be negative + return ErrCommissionNegative(DefaultCodespace) + + case newRate.GT(c.MaxRate): + // new rate cannot be greater than the max rate + return ErrCommissionGTMaxRate(DefaultCodespace) + + case newRate.Sub(c.Rate).GT(c.MaxChangeRate): + // new rate % points change cannot be greater than the max change rate + return ErrCommissionGTMaxChangeRate(DefaultCodespace) + } + + return nil +} diff --git a/x/staking/types/commission_test.go b/x/staking/types/commission_test.go new file mode 100644 index 0000000..ec61861 --- /dev/null +++ b/x/staking/types/commission_test.go @@ -0,0 +1,72 @@ +package types + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestCommissionValidate(t *testing.T) { + testCases := []struct { + input Commission + expectErr bool + }{ + // invalid commission; max rate < 0% + {NewCommission(sdk.ZeroDec(), sdk.MustNewDecFromStr("-1.00"), sdk.ZeroDec()), true}, + // invalid commission; max rate > 100% + {NewCommission(sdk.ZeroDec(), sdk.MustNewDecFromStr("2.00"), sdk.ZeroDec()), true}, + // invalid commission; rate < 0% + {NewCommission(sdk.MustNewDecFromStr("-1.00"), sdk.ZeroDec(), sdk.ZeroDec()), true}, + // invalid commission; rate > max rate + {NewCommission(sdk.MustNewDecFromStr("0.75"), sdk.MustNewDecFromStr("0.50"), sdk.ZeroDec()), true}, + // invalid commission; max change rate < 0% + {NewCommission(sdk.OneDec(), sdk.OneDec(), sdk.MustNewDecFromStr("-1.00")), true}, + // invalid commission; max change rate > max rate + {NewCommission(sdk.OneDec(), sdk.MustNewDecFromStr("0.75"), sdk.MustNewDecFromStr("0.90")), true}, + // valid commission + {NewCommission(sdk.MustNewDecFromStr("0.20"), sdk.OneDec(), sdk.MustNewDecFromStr("0.10")), false}, + } + + for i, tc := range testCases { + err := tc.input.Validate() + require.Equal(t, tc.expectErr, err != nil, "unexpected result; tc #%d, input: %v", i, tc.input) + } +} + +func TestCommissionValidateNewRate(t *testing.T) { + now := time.Now().UTC() + c1 := NewCommission(sdk.MustNewDecFromStr("0.40"), sdk.MustNewDecFromStr("0.80"), sdk.MustNewDecFromStr("0.10")) + c1.UpdateTime = now + + testCases := []struct { + input Commission + newRate sdk.Dec + blockTime time.Time + expectErr bool + }{ + // invalid new commission rate; last update < 24h ago + {c1, sdk.MustNewDecFromStr("0.50"), now, true}, + // invalid new commission rate; new rate < 0% + {c1, sdk.MustNewDecFromStr("-1.00"), now.Add(48 * time.Hour), true}, + // invalid new commission rate; new rate > max rate + {c1, sdk.MustNewDecFromStr("0.90"), now.Add(48 * time.Hour), true}, + // invalid new commission rate; new rate > max change rate + {c1, sdk.MustNewDecFromStr("0.60"), now.Add(48 * time.Hour), true}, + // valid commission + {c1, sdk.MustNewDecFromStr("0.50"), now.Add(48 * time.Hour), false}, + // valid commission + {c1, sdk.MustNewDecFromStr("0.10"), now.Add(48 * time.Hour), false}, + } + + for i, tc := range testCases { + err := tc.input.ValidateNewRate(tc.newRate, tc.blockTime) + require.Equal( + t, tc.expectErr, err != nil, + "unexpected result; tc #%d, input: %v, newRate: %s, blockTime: %s", + i, tc.input, tc.newRate, tc.blockTime, + ) + } +} diff --git a/x/staking/types/delegation.go b/x/staking/types/delegation.go new file mode 100644 index 0000000..d25059a --- /dev/null +++ b/x/staking/types/delegation.go @@ -0,0 +1,471 @@ +package types + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/exported" +) + +// DVPair is struct that just has a delegator-validator pair with no other data. +// It is intended to be used as a marshalable pointer. For example, a DVPair can be used to construct the +// key to getting an UnbondingDelegation from state. +type DVPair struct { + DelegatorAddress sdk.AccAddress + ValidatorAddress sdk.ValAddress +} + +// DVVTriplet is struct that just has a delegator-validator-validator triplet with no other data. +// It is intended to be used as a marshalable pointer. For example, a DVVTriplet can be used to construct the +// key to getting a Redelegation from state. +type DVVTriplet struct { + DelegatorAddress sdk.AccAddress + ValidatorSrcAddress sdk.ValAddress + ValidatorDstAddress sdk.ValAddress +} + +// Implements Delegation interface +var _ exported.DelegationI = Delegation{} + +// Delegation represents the bond with tokens held by an account. It is +// owned by one delegator, and is associated with the voting power of one +// validator. +type Delegation struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` + ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` + Shares sdk.Dec `json:"shares" yaml:"shares"` +} + +// NewDelegation creates a new delegation object +func NewDelegation(delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress, + shares sdk.Dec) Delegation { + + return Delegation{ + DelegatorAddress: delegatorAddr, + ValidatorAddress: validatorAddr, + Shares: shares, + } +} + +// return the delegation +func MustMarshalDelegation(cdc *codec.Codec, delegation Delegation) []byte { + return cdc.MustMarshalBinaryLengthPrefixed(delegation) +} + +// return the delegation +func MustUnmarshalDelegation(cdc *codec.Codec, value []byte) Delegation { + delegation, err := UnmarshalDelegation(cdc, value) + if err != nil { + panic(err) + } + return delegation +} + +// return the delegation +func UnmarshalDelegation(cdc *codec.Codec, value []byte) (delegation Delegation, err error) { + err = cdc.UnmarshalBinaryLengthPrefixed(value, &delegation) + return delegation, err +} + +// nolint +func (d Delegation) Equal(d2 Delegation) bool { + return bytes.Equal(d.DelegatorAddress, d2.DelegatorAddress) && + bytes.Equal(d.ValidatorAddress, d2.ValidatorAddress) && + d.Shares.Equal(d2.Shares) +} + +// nolint - for Delegation +func (d Delegation) GetDelegatorAddr() sdk.AccAddress { return d.DelegatorAddress } +func (d Delegation) GetValidatorAddr() sdk.ValAddress { return d.ValidatorAddress } +func (d Delegation) GetShares() sdk.Dec { return d.Shares } + +// String returns a human readable string representation of a Delegation. +func (d Delegation) String() string { + return fmt.Sprintf(`Delegation: + Delegator: %s + Validator: %s + Shares: %s`, d.DelegatorAddress, + d.ValidatorAddress, d.Shares) +} + +// Delegations is a collection of delegations +type Delegations []Delegation + +func (d Delegations) String() (out string) { + for _, del := range d { + out += del.String() + "\n" + } + return strings.TrimSpace(out) +} + +// UnbondingDelegation stores all of a single delegator's unbonding bonds +// for a single validator in an time-ordered list +type UnbondingDelegation struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` // delegator + ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` // validator unbonding from operator addr + Entries []UnbondingDelegationEntry `json:"entries" yaml:"entries"` // unbonding delegation entries +} + +// UnbondingDelegationEntry - entry to an UnbondingDelegation +type UnbondingDelegationEntry struct { + CreationHeight int64 `json:"creation_height" yaml:"creation_height"` // height which the unbonding took place + CompletionTime time.Time `json:"completion_time" yaml:"completion_time"` // time at which the unbonding delegation will complete + InitialBalance sdk.Int `json:"initial_balance" yaml:"initial_balance"` // atoms initially scheduled to receive at completion + Balance sdk.Int `json:"balance" yaml:"balance"` // atoms to receive at completion +} + +// IsMature - is the current entry mature +func (e UnbondingDelegationEntry) IsMature(currentTime time.Time) bool { + return !e.CompletionTime.After(currentTime) +} + +// NewUnbondingDelegation - create a new unbonding delegation object +func NewUnbondingDelegation(delegatorAddr sdk.AccAddress, + validatorAddr sdk.ValAddress, creationHeight int64, minTime time.Time, + balance sdk.Int) UnbondingDelegation { + + entry := NewUnbondingDelegationEntry(creationHeight, minTime, balance) + return UnbondingDelegation{ + DelegatorAddress: delegatorAddr, + ValidatorAddress: validatorAddr, + Entries: []UnbondingDelegationEntry{entry}, + } +} + +// NewUnbondingDelegation - create a new unbonding delegation object +func NewUnbondingDelegationEntry(creationHeight int64, completionTime time.Time, + balance sdk.Int) UnbondingDelegationEntry { + + return UnbondingDelegationEntry{ + CreationHeight: creationHeight, + CompletionTime: completionTime, + InitialBalance: balance, + Balance: balance, + } +} + +// AddEntry - append entry to the unbonding delegation +func (d *UnbondingDelegation) AddEntry(creationHeight int64, + minTime time.Time, balance sdk.Int) { + + entry := NewUnbondingDelegationEntry(creationHeight, minTime, balance) + d.Entries = append(d.Entries, entry) +} + +// RemoveEntry - remove entry at index i to the unbonding delegation +func (d *UnbondingDelegation) RemoveEntry(i int64) { + d.Entries = append(d.Entries[:i], d.Entries[i+1:]...) +} + +// return the unbonding delegation +func MustMarshalUBD(cdc *codec.Codec, ubd UnbondingDelegation) []byte { + return cdc.MustMarshalBinaryLengthPrefixed(ubd) +} + +// unmarshal a unbonding delegation from a store value +func MustUnmarshalUBD(cdc *codec.Codec, value []byte) UnbondingDelegation { + ubd, err := UnmarshalUBD(cdc, value) + if err != nil { + panic(err) + } + return ubd +} + +// unmarshal a unbonding delegation from a store value +func UnmarshalUBD(cdc *codec.Codec, value []byte) (ubd UnbondingDelegation, err error) { + err = cdc.UnmarshalBinaryLengthPrefixed(value, &ubd) + return ubd, err +} + +// nolint +// inefficient but only used in testing +func (d UnbondingDelegation) Equal(d2 UnbondingDelegation) bool { + bz1 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&d) + bz2 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&d2) + return bytes.Equal(bz1, bz2) +} + +// String returns a human readable string representation of an UnbondingDelegation. +func (d UnbondingDelegation) String() string { + out := fmt.Sprintf(`Unbonding Delegations between: + Delegator: %s + Validator: %s + Entries:`, d.DelegatorAddress, d.ValidatorAddress) + for i, entry := range d.Entries { + out += fmt.Sprintf(` Unbonding Delegation %d: + Creation Height: %v + Min time to unbond (unix): %v + Expected balance: %s`, i, entry.CreationHeight, + entry.CompletionTime, entry.Balance) + } + return out +} + +// UnbondingDelegations is a collection of UnbondingDelegation +type UnbondingDelegations []UnbondingDelegation + +func (ubds UnbondingDelegations) String() (out string) { + for _, u := range ubds { + out += u.String() + "\n" + } + return strings.TrimSpace(out) +} + +// Redelegation contains the list of a particular delegator's +// redelegating bonds from a particular source validator to a +// particular destination validator +type Redelegation struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` // delegator + ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address" yaml:"validator_src_address"` // validator redelegation source operator addr + ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address" yaml:"validator_dst_address"` // validator redelegation destination operator addr + Entries []RedelegationEntry `json:"entries" yaml:"entries"` // redelegation entries +} + +// RedelegationEntry - entry to a Redelegation +type RedelegationEntry struct { + CreationHeight int64 `json:"creation_height" yaml:"creation_height"` // height at which the redelegation took place + CompletionTime time.Time `json:"completion_time" yaml:"completion_time"` // time at which the redelegation will complete + InitialBalance sdk.Int `json:"initial_balance" yaml:"initial_balance"` // initial balance when redelegation started + SharesDst sdk.Dec `json:"shares_dst" yaml:"shares_dst"` // amount of destination-validator shares created by redelegation +} + +// NewRedelegation - create a new redelegation object +func NewRedelegation(delegatorAddr sdk.AccAddress, validatorSrcAddr, + validatorDstAddr sdk.ValAddress, creationHeight int64, + minTime time.Time, balance sdk.Int, + sharesDst sdk.Dec) Redelegation { + + entry := NewRedelegationEntry(creationHeight, + minTime, balance, sharesDst) + + return Redelegation{ + DelegatorAddress: delegatorAddr, + ValidatorSrcAddress: validatorSrcAddr, + ValidatorDstAddress: validatorDstAddr, + Entries: []RedelegationEntry{entry}, + } +} + +// NewRedelegation - create a new redelegation object +func NewRedelegationEntry(creationHeight int64, + completionTime time.Time, balance sdk.Int, + sharesDst sdk.Dec) RedelegationEntry { + + return RedelegationEntry{ + CreationHeight: creationHeight, + CompletionTime: completionTime, + InitialBalance: balance, + SharesDst: sharesDst, + } +} + +// IsMature - is the current entry mature +func (e RedelegationEntry) IsMature(currentTime time.Time) bool { + return !e.CompletionTime.After(currentTime) +} + +// AddEntry - append entry to the unbonding delegation +func (d *Redelegation) AddEntry(creationHeight int64, + minTime time.Time, balance sdk.Int, + sharesDst sdk.Dec) { + + entry := NewRedelegationEntry(creationHeight, minTime, balance, sharesDst) + d.Entries = append(d.Entries, entry) +} + +// RemoveEntry - remove entry at index i to the unbonding delegation +func (d *Redelegation) RemoveEntry(i int64) { + d.Entries = append(d.Entries[:i], d.Entries[i+1:]...) +} + +// return the redelegation +func MustMarshalRED(cdc *codec.Codec, red Redelegation) []byte { + return cdc.MustMarshalBinaryLengthPrefixed(red) +} + +// unmarshal a redelegation from a store value +func MustUnmarshalRED(cdc *codec.Codec, value []byte) Redelegation { + red, err := UnmarshalRED(cdc, value) + if err != nil { + panic(err) + } + return red +} + +// unmarshal a redelegation from a store value +func UnmarshalRED(cdc *codec.Codec, value []byte) (red Redelegation, err error) { + err = cdc.UnmarshalBinaryLengthPrefixed(value, &red) + return red, err +} + +// nolint +// inefficient but only used in tests +func (d Redelegation) Equal(d2 Redelegation) bool { + bz1 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&d) + bz2 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&d2) + return bytes.Equal(bz1, bz2) +} + +// String returns a human readable string representation of a Redelegation. +func (d Redelegation) String() string { + out := fmt.Sprintf(`Redelegations between: + Delegator: %s + Source Validator: %s + Destination Validator: %s + Entries: +`, + d.DelegatorAddress, d.ValidatorSrcAddress, d.ValidatorDstAddress, + ) + + for i, entry := range d.Entries { + out += fmt.Sprintf(` Redelegation Entry #%d: + Creation height: %v + Min time to unbond (unix): %v + Dest Shares: %s +`, + i, entry.CreationHeight, entry.CompletionTime, entry.SharesDst, + ) + } + + return strings.TrimRight(out, "\n") +} + +// Redelegations are a collection of Redelegation +type Redelegations []Redelegation + +func (d Redelegations) String() (out string) { + for _, red := range d { + out += red.String() + "\n" + } + return strings.TrimSpace(out) +} + +// ---------------------------------------------------------------------------- +// Client Types + +// DelegationResponse is equivalent to Delegation except that it contains a balance +// in addition to shares which is more suitable for client responses. +type DelegationResponse struct { + Delegation + Balance sdk.Int `json:"balance" yaml:"balance"` +} + +func NewDelegationResp(d sdk.AccAddress, v sdk.ValAddress, s sdk.Dec, b sdk.Int) DelegationResponse { + return DelegationResponse{NewDelegation(d, v, s), b} +} + +// String implements the Stringer interface for DelegationResponse. +func (d DelegationResponse) String() string { + return fmt.Sprintf("%s\n Balance: %s", d.Delegation.String(), d.Balance) +} + +type delegationRespAlias DelegationResponse + +// MarshalJSON implements the json.Marshaler interface. This is so we can +// achieve a flattened structure while embedding other types. +func (d DelegationResponse) MarshalJSON() ([]byte, error) { + return json.Marshal((delegationRespAlias)(d)) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. This is so we can +// achieve a flattened structure while embedding other types. +func (d *DelegationResponse) UnmarshalJSON(bz []byte) error { + return json.Unmarshal(bz, (*delegationRespAlias)(d)) +} + +// DelegationResponses is a collection of DelegationResp +type DelegationResponses []DelegationResponse + +// String implements the Stringer interface for DelegationResponses. +func (d DelegationResponses) String() (out string) { + for _, del := range d { + out += del.String() + "\n" + } + return strings.TrimSpace(out) +} + +// RedelegationResponse is equivalent to a Redelegation except that its entries +// contain a balance in addition to shares which is more suitable for client +// responses. +type RedelegationResponse struct { + Redelegation + Entries []RedelegationEntryResponse `json:"entries" yaml:"entries"` // nolint: structtag +} + +// RedelegationEntryResponse is equivalent to a RedelegationEntry except that it +// contains a balance in addition to shares which is more suitable for client +// responses. +type RedelegationEntryResponse struct { + RedelegationEntry + Balance sdk.Int `json:"balance"` +} + +func NewRedelegationResponse(d sdk.AccAddress, vSrc, vDst sdk.ValAddress, entries []RedelegationEntryResponse) RedelegationResponse { + return RedelegationResponse{ + Redelegation{ + DelegatorAddress: d, + ValidatorSrcAddress: vSrc, + ValidatorDstAddress: vDst, + }, + entries, + } +} + +func NewRedelegationEntryResponse(ch int64, ct time.Time, s sdk.Dec, ib, b sdk.Int) RedelegationEntryResponse { + return RedelegationEntryResponse{NewRedelegationEntry(ch, ct, ib, s), b} +} + +// String implements the Stringer interface for RedelegationResp. +func (r RedelegationResponse) String() string { + out := fmt.Sprintf(`Redelegations between: + Delegator: %s + Source Validator: %s + Destination Validator: %s + Entries: +`, + r.DelegatorAddress, r.ValidatorSrcAddress, r.ValidatorDstAddress, + ) + + for i, entry := range r.Entries { + out += fmt.Sprintf(` Redelegation Entry #%d: + Creation height: %v + Min time to unbond (unix): %v + Initial Balance: %s + Shares: %s + Balance: %s +`, + i, entry.CreationHeight, entry.CompletionTime, entry.InitialBalance, entry.SharesDst, entry.Balance, + ) + } + + return strings.TrimRight(out, "\n") +} + +type redelegationRespAlias RedelegationResponse + +// MarshalJSON implements the json.Marshaler interface. This is so we can +// achieve a flattened structure while embedding other types. +func (r RedelegationResponse) MarshalJSON() ([]byte, error) { + return json.Marshal((redelegationRespAlias)(r)) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. This is so we can +// achieve a flattened structure while embedding other types. +func (r *RedelegationResponse) UnmarshalJSON(bz []byte) error { + return json.Unmarshal(bz, (*redelegationRespAlias)(r)) +} + +// RedelegationResponses are a collection of RedelegationResp +type RedelegationResponses []RedelegationResponse + +func (r RedelegationResponses) String() (out string) { + for _, red := range r { + out += red.String() + "\n" + } + return strings.TrimSpace(out) +} diff --git a/x/staking/types/delegation_test.go b/x/staking/types/delegation_test.go new file mode 100644 index 0000000..ae79955 --- /dev/null +++ b/x/staking/types/delegation_test.go @@ -0,0 +1,141 @@ +package types + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestDelegationEqual(t *testing.T) { + d1 := NewDelegation(sdk.AccAddress(valAddr1), valAddr2, sdk.NewDec(100)) + d2 := d1 + + ok := d1.Equal(d2) + require.True(t, ok) + + d2.ValidatorAddress = valAddr3 + d2.Shares = sdk.NewDec(200) + + ok = d1.Equal(d2) + require.False(t, ok) +} + +func TestDelegationString(t *testing.T) { + d := NewDelegation(sdk.AccAddress(valAddr1), valAddr2, sdk.NewDec(100)) + require.NotEmpty(t, d.String()) +} + +func TestUnbondingDelegationEqual(t *testing.T) { + ubd1 := NewUnbondingDelegation(sdk.AccAddress(valAddr1), valAddr2, 0, + time.Unix(0, 0), sdk.NewInt(0)) + ubd2 := ubd1 + + ok := ubd1.Equal(ubd2) + require.True(t, ok) + + ubd2.ValidatorAddress = valAddr3 + + ubd2.Entries[0].CompletionTime = time.Unix(20*20*2, 0) + ok = ubd1.Equal(ubd2) + require.False(t, ok) +} + +func TestUnbondingDelegationString(t *testing.T) { + ubd := NewUnbondingDelegation(sdk.AccAddress(valAddr1), valAddr2, 0, + time.Unix(0, 0), sdk.NewInt(0)) + + require.NotEmpty(t, ubd.String()) +} + +func TestRedelegationEqual(t *testing.T) { + r1 := NewRedelegation(sdk.AccAddress(valAddr1), valAddr2, valAddr3, 0, + time.Unix(0, 0), sdk.NewInt(0), + sdk.NewDec(0)) + r2 := NewRedelegation(sdk.AccAddress(valAddr1), valAddr2, valAddr3, 0, + time.Unix(0, 0), sdk.NewInt(0), + sdk.NewDec(0)) + + ok := r1.Equal(r2) + require.True(t, ok) + + r2.Entries[0].SharesDst = sdk.NewDec(10) + r2.Entries[0].CompletionTime = time.Unix(20*20*2, 0) + + ok = r1.Equal(r2) + require.False(t, ok) +} + +func TestRedelegationString(t *testing.T) { + r := NewRedelegation(sdk.AccAddress(valAddr1), valAddr2, valAddr3, 0, + time.Unix(0, 0), sdk.NewInt(0), + sdk.NewDec(10)) + + require.NotEmpty(t, r.String()) +} + +func TestDelegationResponses(t *testing.T) { + cdc := codec.New() + dr1 := NewDelegationResp(sdk.AccAddress(valAddr1), valAddr2, sdk.NewDec(5), sdk.NewInt(5)) + dr2 := NewDelegationResp(sdk.AccAddress(valAddr1), valAddr3, sdk.NewDec(5), sdk.NewInt(5)) + drs := DelegationResponses{dr1, dr2} + + bz1, err := json.Marshal(dr1) + require.NoError(t, err) + + bz2, err := cdc.MarshalJSON(dr1) + require.NoError(t, err) + + require.Equal(t, bz1, bz2) + + bz1, err = json.Marshal(drs) + require.NoError(t, err) + + bz2, err = cdc.MarshalJSON(drs) + require.NoError(t, err) + + require.Equal(t, bz1, bz2) + + var drs2 DelegationResponses + require.NoError(t, cdc.UnmarshalJSON(bz2, &drs2)) + require.Equal(t, drs, drs2) +} + +func TestRedelegationResponses(t *testing.T) { + cdc := codec.New() + entries := []RedelegationEntryResponse{ + NewRedelegationEntryResponse(0, time.Unix(0, 0), sdk.NewDec(5), sdk.NewInt(5), sdk.NewInt(5)), + NewRedelegationEntryResponse(0, time.Unix(0, 0), sdk.NewDec(5), sdk.NewInt(5), sdk.NewInt(5)), + } + rdr1 := NewRedelegationResponse(sdk.AccAddress(valAddr1), valAddr2, valAddr3, entries) + rdr2 := NewRedelegationResponse(sdk.AccAddress(valAddr2), valAddr1, valAddr3, entries) + rdrs := RedelegationResponses{rdr1, rdr2} + + bz1, err := json.Marshal(rdr1) + require.NoError(t, err) + + bz2, err := cdc.MarshalJSON(rdr1) + require.NoError(t, err) + + require.Equal(t, bz1, bz2) + + bz1, err = json.Marshal(rdrs) + require.NoError(t, err) + + bz2, err = cdc.MarshalJSON(rdrs) + require.NoError(t, err) + + require.Equal(t, bz1, bz2) + + var rdrs2 RedelegationResponses + require.NoError(t, cdc.UnmarshalJSON(bz2, &rdrs2)) + + bz3, err := cdc.MarshalJSON(rdrs2) + require.NoError(t, err) + + require.Equal(t, bz2, bz3) +} diff --git a/x/staking/types/errors.go b/x/staking/types/errors.go new file mode 100644 index 0000000..012f942 --- /dev/null +++ b/x/staking/types/errors.go @@ -0,0 +1,214 @@ +// nolint +package types + +import ( + "fmt" + "strings" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type CodeType = sdk.CodeType + +const ( + DefaultCodespace sdk.CodespaceType = ModuleName + + CodeInvalidValidator CodeType = 101 + CodeInvalidDelegation CodeType = 102 + CodeInvalidInput CodeType = 103 + CodeValidatorJailed CodeType = 104 + CodeInvalidAddress CodeType = sdk.CodeInvalidAddress + CodeUnauthorized CodeType = sdk.CodeUnauthorized + CodeInternal CodeType = sdk.CodeInternal + CodeUnknownRequest CodeType = sdk.CodeUnknownRequest +) + +//validator +func ErrNilValidatorAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidInput, "validator address is nil") +} + +func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidAddress, "validator address is invalid") +} + +func ErrNoValidatorFound(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address") +} + +func ErrValidatorOwnerExists(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "validator already exist for this operator address, must use new validator operator address") +} + +func ErrValidatorPubKeyExists(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "validator already exist for this pubkey, must use new validator pubkey") +} + +func ErrValidatorPubKeyTypeNotSupported(codespace sdk.CodespaceType, keyType string, supportedTypes []string) sdk.Error { + msg := fmt.Sprintf("validator pubkey type %s is not supported, must use %s", keyType, strings.Join(supportedTypes, ",")) + return sdk.NewError(codespace, CodeInvalidValidator, msg) +} + +func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "validator for this address is currently jailed") +} + +func ErrBadRemoveValidator(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "error removing validator") +} + +func ErrDescriptionLength(codespace sdk.CodespaceType, descriptor string, got, max int) sdk.Error { + msg := fmt.Sprintf("bad description length for %v, got length %v, max is %v", descriptor, got, max) + return sdk.NewError(codespace, CodeInvalidValidator, msg) +} + +func ErrCommissionNegative(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "commission must be positive") +} + +func ErrCommissionHuge(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be more than 100%") +} + +func ErrCommissionGTMaxRate(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be more than the max rate") +} + +func ErrCommissionUpdateTime(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be changed more than once in 24h") +} + +func ErrCommissionChangeRateNegative(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "commission change rate must be positive") +} + +func ErrCommissionChangeRateGTMaxRate(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "commission change rate cannot be more than the max rate") +} + +func ErrCommissionGTMaxChangeRate(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be changed more than max change rate") +} + +func ErrSelfDelegationBelowMinimum(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "validator's self delegation must be greater than their minimum self delegation") +} + +func ErrMinSelfDelegationInvalid(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "minimum self delegation must be a positive integer") +} + +func ErrMinSelfDelegationDecreased(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "minimum self delegation cannot be decrease") +} + +func ErrNilDelegatorAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidInput, "delegator address is nil") +} + +func ErrBadDenom(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "invalid coin denomination") +} + +func ErrBadDelegationAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidInput, "unexpected address length for this (address, validator) pair") +} + +func ErrBadDelegationAmount(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "amount must be > 0") +} + +func ErrNoDelegation(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "no delegation for this (address, validator) pair") +} + +func ErrBadDelegatorAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "delegator does not exist for that address") +} + +func ErrNoDelegatorForAddress(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "delegator does not contain this delegation") +} + +func ErrInsufficientShares(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "insufficient delegation shares") +} + +func ErrDelegationValidatorEmpty(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "cannot delegate to an empty validator") +} + +func ErrNotEnoughDelegationShares(codespace sdk.CodespaceType, shares string) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, fmt.Sprintf("not enough shares only have %v", shares)) +} + +func ErrBadSharesAmount(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "shares must be > 0") +} + +func ErrBadSharesPercent(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "shares percent must be >0 and <=1") +} + +func ErrNotMature(codespace sdk.CodespaceType, operation, descriptor string, got, min time.Time) sdk.Error { + msg := fmt.Sprintf("%v is not mature requires a min %v of %v, currently it is %v", + operation, descriptor, got, min) + return sdk.NewError(codespace, CodeUnauthorized, msg) +} + +func ErrNoUnbondingDelegation(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "no unbonding delegation found") +} + +func ErrMaxUnbondingDelegationEntries(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, + "too many unbonding delegation entries in this delegator/validator duo, please wait for some entries to mature") +} + +func ErrBadRedelegationAddr(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidInput, "unexpected address length for this (address, srcValidator, dstValidator) tuple") +} + +func ErrNoRedelegation(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "no redelegation found") +} + +func ErrSelfRedelegation(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "cannot redelegate to the same validator") +} + +func ErrVerySmallRedelegation(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "too few tokens to redelegate, truncates to zero tokens") +} + +func ErrBadRedelegationDst(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "redelegation validator not found") +} + +func ErrTransitiveRedelegation(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, + "redelegation to this validator already in progress, first redelegation to this validator must complete before next redelegation") +} + +func ErrMaxRedelegationEntries(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, + "too many redelegation entries in this delegator/src-validator/dst-validator trio, please wait for some entries to mature") +} + +func ErrDelegatorShareExRateInvalid(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, + "cannot delegate to validators with invalid (zero) ex-rate") +} + +func ErrBothShareMsgsGiven(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidInput, "both shares amount and shares percent provided") +} + +func ErrNeitherShareMsgsGiven(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidInput, "neither shares amount nor shares percent provided") +} + +func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "missing signature") +} diff --git a/x/staking/types/events.go b/x/staking/types/events.go new file mode 100644 index 0000000..f4eb81d --- /dev/null +++ b/x/staking/types/events.go @@ -0,0 +1,21 @@ +package types + +// Staking module event types +var ( + EventTypeCompleteUnbonding = "complete_unbonding" + EventTypeCompleteRedelegation = "complete_redelegation" + EventTypeCreateValidator = "create_validator" + EventTypeEditValidator = "edit_validator" + EventTypeDelegate = "delegate" + EventTypeUnbond = "unbond" + EventTypeRedelegate = "redelegate" + + AttributeKeyValidator = "validator" + AttributeKeyCommissionRate = "commission_rate" + AttributeKeyMinSelfDelegation = "min_self_delegation" + AttributeKeySrcValidator = "source_validator" + AttributeKeyDstValidator = "destination_validator" + AttributeKeyDelegator = "delegator" + AttributeKeyCompletionTime = "completion_time" + AttributeValueCategory = ModuleName +) diff --git a/x/staking/types/expected_keepers.go b/x/staking/types/expected_keepers.go new file mode 100644 index 0000000..d3faecf --- /dev/null +++ b/x/staking/types/expected_keepers.go @@ -0,0 +1,101 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" + stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported" + supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// DistributionKeeper expected distribution keeper (noalias) +type DistributionKeeper interface { + GetFeePoolCommunityCoins(ctx sdk.Context) sdk.DecCoins + GetValidatorOutstandingRewardsCoins(ctx sdk.Context, val sdk.ValAddress) sdk.DecCoins +} + +// AccountKeeper defines the expected account keeper (noalias) +type AccountKeeper interface { + IterateAccounts(ctx sdk.Context, process func(authexported.Account) (stop bool)) +} + +// SupplyKeeper defines the expected supply Keeper (noalias) +type SupplyKeeper interface { + GetSupply(ctx sdk.Context) supplyexported.SupplyI + + GetModuleAddress(name string) sdk.AccAddress + GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI + + // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 + SetModuleAccount(sdk.Context, supplyexported.ModuleAccountI) + + SendCoinsFromModuleToModule(ctx sdk.Context, senderPool, recipientPool string, amt sdk.Coins) sdk.Error + UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) sdk.Error + DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error + + BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error +} + +// ValidatorSet expected properties for the set of all validators (noalias) +type ValidatorSet interface { + // iterate through validators by operator address, execute func for each validator + IterateValidators(sdk.Context, + func(index int64, validator stakingexported.ValidatorI) (stop bool)) + + // iterate through bonded validators by operator address, execute func for each validator + IterateBondedValidatorsByPower(sdk.Context, + func(index int64, validator stakingexported.ValidatorI) (stop bool)) + + // iterate through the consensus validator set of the last block by operator address, execute func for each validator + IterateLastValidators(sdk.Context, + func(index int64, validator stakingexported.ValidatorI) (stop bool)) + + Validator(sdk.Context, sdk.ValAddress) stakingexported.ValidatorI // get a particular validator by operator address + ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) stakingexported.ValidatorI // get a particular validator by consensus address + TotalBondedTokens(sdk.Context) sdk.Int // total bonded tokens within the validator set + StakingTokenSupply(sdk.Context) sdk.Int // total staking token supply + + // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction + Slash(sdk.Context, sdk.ConsAddress, int64, int64, sdk.Dec) + Jail(sdk.Context, sdk.ConsAddress) // jail a validator + Unjail(sdk.Context, sdk.ConsAddress) // unjail a validator + + // Delegation allows for getting a particular delegation for a given validator + // and delegator outside the scope of the staking module. + Delegation(sdk.Context, sdk.AccAddress, sdk.ValAddress) stakingexported.DelegationI + + // MaxValidators returns the maximum amount of bonded validators + MaxValidators(sdk.Context) uint16 +} + +// DelegationSet expected properties for the set of all delegations for a particular (noalias) +type DelegationSet interface { + GetValidatorSet() ValidatorSet // validator set for which delegation set is based upon + + // iterate through all delegations from one delegator by validator-AccAddress, + // execute func for each validator + IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, + fn func(index int64, delegation stakingexported.DelegationI) (stop bool)) +} + +//_______________________________________________________________________________ +// Event Hooks +// These can be utilized to communicate between a staking keeper and another +// keeper which must take particular actions when validators/delegators change +// state. The second keeper must implement this interface, which then the +// staking keeper can call. + +// StakingHooks event hooks for staking validator object (noalias) +type StakingHooks interface { + AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) // Must be called when a validator is created + BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) // Must be called when a validator's state changes + AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator is deleted + + AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator is bonded + AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator begins unbonding + + BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) // Must be called when a delegation is created + BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) // Must be called when a delegation's shares are modified + BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) // Must be called when a delegation is removed + AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) + BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) +} diff --git a/x/staking/types/genesis.go b/x/staking/types/genesis.go new file mode 100644 index 0000000..52e4fde --- /dev/null +++ b/x/staking/types/genesis.go @@ -0,0 +1,38 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// GenesisState - all staking state that must be provided at genesis +type GenesisState struct { + Params Params `json:"params" yaml:"params"` + LastTotalPower sdk.Int `json:"last_total_power" yaml:"last_total_power"` + LastValidatorPowers []LastValidatorPower `json:"last_validator_powers" yaml:"last_validator_powers"` + Validators Validators `json:"validators" yaml:"validators"` + Delegations Delegations `json:"delegations" yaml:"delegations"` + UnbondingDelegations []UnbondingDelegation `json:"unbonding_delegations" yaml:"unbonding_delegations"` + Redelegations []Redelegation `json:"redelegations" yaml:"redelegations"` + Exported bool `json:"exported" yaml:"exported"` +} + +// Last validator power, needed for validator set update logic +type LastValidatorPower struct { + Address sdk.ValAddress + Power int64 +} + +func NewGenesisState(params Params, validators []Validator, delegations []Delegation) GenesisState { + return GenesisState{ + Params: params, + Validators: validators, + Delegations: delegations, + } +} + +// get raw genesis raw message for testing +func DefaultGenesisState() GenesisState { + return GenesisState{ + Params: DefaultParams(), + } +} diff --git a/x/staking/types/hooks.go b/x/staking/types/hooks.go new file mode 100644 index 0000000..de04fae --- /dev/null +++ b/x/staking/types/hooks.go @@ -0,0 +1,64 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// combine multiple staking hooks, all hook functions are run in array sequence +type MultiStakingHooks []StakingHooks + +func NewMultiStakingHooks(hooks ...StakingHooks) MultiStakingHooks { + return hooks +} + +// nolint +func (h MultiStakingHooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { + for i := range h { + h[i].AfterValidatorCreated(ctx, valAddr) + } +} +func (h MultiStakingHooks) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) { + for i := range h { + h[i].BeforeValidatorModified(ctx, valAddr) + } +} +func (h MultiStakingHooks) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { + for i := range h { + h[i].AfterValidatorRemoved(ctx, consAddr, valAddr) + } +} +func (h MultiStakingHooks) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { + for i := range h { + h[i].AfterValidatorBonded(ctx, consAddr, valAddr) + } +} +func (h MultiStakingHooks) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { + for i := range h { + h[i].AfterValidatorBeginUnbonding(ctx, consAddr, valAddr) + } +} +func (h MultiStakingHooks) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { + for i := range h { + h[i].BeforeDelegationCreated(ctx, delAddr, valAddr) + } +} +func (h MultiStakingHooks) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { + for i := range h { + h[i].BeforeDelegationSharesModified(ctx, delAddr, valAddr) + } +} +func (h MultiStakingHooks) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { + for i := range h { + h[i].BeforeDelegationRemoved(ctx, delAddr, valAddr) + } +} +func (h MultiStakingHooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { + for i := range h { + h[i].AfterDelegationModified(ctx, delAddr, valAddr) + } +} +func (h MultiStakingHooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) { + for i := range h { + h[i].BeforeValidatorSlashed(ctx, valAddr, fraction) + } +} diff --git a/x/staking/types/keys.go b/x/staking/types/keys.go new file mode 100644 index 0000000..ce23e4f --- /dev/null +++ b/x/staking/types/keys.go @@ -0,0 +1,280 @@ +package types + +import ( + "encoding/binary" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // ModuleName is the name of the staking module + ModuleName = "staking" + + // StoreKey is the string store representation + StoreKey = ModuleName + + // TStoreKey is the string transient store representation + TStoreKey = "transient_" + ModuleName + + // QuerierRoute is the querier route for the staking module + QuerierRoute = ModuleName + + // RouterKey is the msg router key for the staking module + RouterKey = ModuleName +) + +//nolint +var ( + // Keys for store prefixes + // Last* values are constant during a block. + LastValidatorPowerKey = []byte{0x11} // prefix for each key to a validator index, for bonded validators + LastTotalPowerKey = []byte{0x12} // prefix for the total power + + ValidatorsKey = []byte{0x21} // prefix for each key to a validator + ValidatorsByConsAddrKey = []byte{0x22} // prefix for each key to a validator index, by pubkey + ValidatorsByPowerIndexKey = []byte{0x23} // prefix for each key to a validator index, sorted by power + + DelegationKey = []byte{0x31} // key for a delegation + UnbondingDelegationKey = []byte{0x32} // key for an unbonding-delegation + UnbondingDelegationByValIndexKey = []byte{0x33} // prefix for each key for an unbonding-delegation, by validator operator + RedelegationKey = []byte{0x34} // key for a redelegation + RedelegationByValSrcIndexKey = []byte{0x35} // prefix for each key for an redelegation, by source validator operator + RedelegationByValDstIndexKey = []byte{0x36} // prefix for each key for an redelegation, by destination validator operator + + UnbondingQueueKey = []byte{0x41} // prefix for the timestamps in unbonding queue + RedelegationQueueKey = []byte{0x42} // prefix for the timestamps in redelegations queue + ValidatorQueueKey = []byte{0x43} // prefix for the timestamps in validator queue +) + +// gets the key for the validator with address +// VALUE: staking/Validator +func GetValidatorKey(operatorAddr sdk.ValAddress) []byte { + return append(ValidatorsKey, operatorAddr.Bytes()...) +} + +// gets the key for the validator with pubkey +// VALUE: validator operator address ([]byte) +func GetValidatorByConsAddrKey(addr sdk.ConsAddress) []byte { + return append(ValidatorsByConsAddrKey, addr.Bytes()...) +} + +// Get the validator operator address from LastValidatorPowerKey +func AddressFromLastValidatorPowerKey(key []byte) []byte { + return key[1:] // remove prefix bytes +} + +// get the validator by power index. +// Power index is the key used in the power-store, and represents the relative +// power ranking of the validator. +// VALUE: validator operator address ([]byte) +func GetValidatorsByPowerIndexKey(validator Validator) []byte { + // NOTE the address doesn't need to be stored because counter bytes must always be different + return getValidatorPowerRank(validator) +} + +// get the bonded validator index key for an operator address +func GetLastValidatorPowerKey(operator sdk.ValAddress) []byte { + return append(LastValidatorPowerKey, operator...) +} + +// get the power ranking of a validator +// NOTE the larger values are of higher value +func getValidatorPowerRank(validator Validator) []byte { + + consensusPower := sdk.TokensToConsensusPower(validator.Tokens) + consensusPowerBytes := make([]byte, 8) + binary.BigEndian.PutUint64(consensusPowerBytes[:], uint64(consensusPower)) + + powerBytes := consensusPowerBytes + powerBytesLen := len(powerBytes) // 8 + + // key is of format prefix || powerbytes || addrBytes + key := make([]byte, 1+powerBytesLen+sdk.AddrLen) + + key[0] = ValidatorsByPowerIndexKey[0] + copy(key[1:powerBytesLen+1], powerBytes) + operAddrInvr := sdk.CopyBytes(validator.OperatorAddress) + for i, b := range operAddrInvr { + operAddrInvr[i] = ^b + } + copy(key[powerBytesLen+1:], operAddrInvr) + + return key +} + +// parse the validators operator address from power rank key +func ParseValidatorPowerRankKey(key []byte) (operAddr []byte) { + powerBytesLen := 8 + if len(key) != 1+powerBytesLen+sdk.AddrLen { + panic("Invalid validator power rank key length") + } + operAddr = sdk.CopyBytes(key[powerBytesLen+1:]) + for i, b := range operAddr { + operAddr[i] = ^b + } + return operAddr +} + +// gets the prefix for all unbonding delegations from a delegator +func GetValidatorQueueTimeKey(timestamp time.Time) []byte { + bz := sdk.FormatTimeBytes(timestamp) + return append(ValidatorQueueKey, bz...) +} + +//______________________________________________________________________________ + +// gets the key for delegator bond with validator +// VALUE: staking/Delegation +func GetDelegationKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { + return append(GetDelegationsKey(delAddr), valAddr.Bytes()...) +} + +// gets the prefix for a delegator for all validators +func GetDelegationsKey(delAddr sdk.AccAddress) []byte { + return append(DelegationKey, delAddr.Bytes()...) +} + +//______________________________________________________________________________ + +// gets the key for an unbonding delegation by delegator and validator addr +// VALUE: staking/UnbondingDelegation +func GetUBDKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { + return append( + GetUBDsKey(delAddr.Bytes()), + valAddr.Bytes()...) +} + +// gets the index-key for an unbonding delegation, stored by validator-index +// VALUE: none (key rearrangement used) +func GetUBDByValIndexKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { + return append(GetUBDsByValIndexKey(valAddr), delAddr.Bytes()...) +} + +// rearranges the ValIndexKey to get the UBDKey +func GetUBDKeyFromValIndexKey(IndexKey []byte) []byte { + addrs := IndexKey[1:] // remove prefix bytes + if len(addrs) != 2*sdk.AddrLen { + panic("unexpected key length") + } + valAddr := addrs[:sdk.AddrLen] + delAddr := addrs[sdk.AddrLen:] + return GetUBDKey(delAddr, valAddr) +} + +//______________ + +// gets the prefix for all unbonding delegations from a delegator +func GetUBDsKey(delAddr sdk.AccAddress) []byte { + return append(UnbondingDelegationKey, delAddr.Bytes()...) +} + +// gets the prefix keyspace for the indexes of unbonding delegations for a validator +func GetUBDsByValIndexKey(valAddr sdk.ValAddress) []byte { + return append(UnbondingDelegationByValIndexKey, valAddr.Bytes()...) +} + +// gets the prefix for all unbonding delegations from a delegator +func GetUnbondingDelegationTimeKey(timestamp time.Time) []byte { + bz := sdk.FormatTimeBytes(timestamp) + return append(UnbondingQueueKey, bz...) +} + +//________________________________________________________________________________ + +// gets the key for a redelegation +// VALUE: staking/RedelegationKey +func GetREDKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte { + key := make([]byte, 1+sdk.AddrLen*3) + + copy(key[0:sdk.AddrLen+1], GetREDsKey(delAddr.Bytes())) + copy(key[sdk.AddrLen+1:2*sdk.AddrLen+1], valSrcAddr.Bytes()) + copy(key[2*sdk.AddrLen+1:3*sdk.AddrLen+1], valDstAddr.Bytes()) + + return key +} + +// gets the index-key for a redelegation, stored by source-validator-index +// VALUE: none (key rearrangement used) +func GetREDByValSrcIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte { + REDSFromValsSrcKey := GetREDsFromValSrcIndexKey(valSrcAddr) + offset := len(REDSFromValsSrcKey) + + // key is of the form REDSFromValsSrcKey || delAddr || valDstAddr + key := make([]byte, len(REDSFromValsSrcKey)+2*sdk.AddrLen) + copy(key[0:offset], REDSFromValsSrcKey) + copy(key[offset:offset+sdk.AddrLen], delAddr.Bytes()) + copy(key[offset+sdk.AddrLen:offset+2*sdk.AddrLen], valDstAddr.Bytes()) + return key +} + +// gets the index-key for a redelegation, stored by destination-validator-index +// VALUE: none (key rearrangement used) +func GetREDByValDstIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte { + REDSToValsDstKey := GetREDsToValDstIndexKey(valDstAddr) + offset := len(REDSToValsDstKey) + + // key is of the form REDSToValsDstKey || delAddr || valSrcAddr + key := make([]byte, len(REDSToValsDstKey)+2*sdk.AddrLen) + copy(key[0:offset], REDSToValsDstKey) + copy(key[offset:offset+sdk.AddrLen], delAddr.Bytes()) + copy(key[offset+sdk.AddrLen:offset+2*sdk.AddrLen], valSrcAddr.Bytes()) + + return key +} + +// GetREDKeyFromValSrcIndexKey rearranges the ValSrcIndexKey to get the REDKey +func GetREDKeyFromValSrcIndexKey(indexKey []byte) []byte { + // note that first byte is prefix byte + if len(indexKey) != 3*sdk.AddrLen+1 { + panic("unexpected key length") + } + valSrcAddr := indexKey[1 : sdk.AddrLen+1] + delAddr := indexKey[sdk.AddrLen+1 : 2*sdk.AddrLen+1] + valDstAddr := indexKey[2*sdk.AddrLen+1 : 3*sdk.AddrLen+1] + + return GetREDKey(delAddr, valSrcAddr, valDstAddr) +} + +// GetREDKeyFromValDstIndexKey rearranges the ValDstIndexKey to get the REDKey +func GetREDKeyFromValDstIndexKey(indexKey []byte) []byte { + // note that first byte is prefix byte + if len(indexKey) != 3*sdk.AddrLen+1 { + panic("unexpected key length") + } + valDstAddr := indexKey[1 : sdk.AddrLen+1] + delAddr := indexKey[sdk.AddrLen+1 : 2*sdk.AddrLen+1] + valSrcAddr := indexKey[2*sdk.AddrLen+1 : 3*sdk.AddrLen+1] + return GetREDKey(delAddr, valSrcAddr, valDstAddr) +} + +// gets the prefix for all unbonding delegations from a delegator +func GetRedelegationTimeKey(timestamp time.Time) []byte { + bz := sdk.FormatTimeBytes(timestamp) + return append(RedelegationQueueKey, bz...) +} + +//______________ + +// gets the prefix keyspace for redelegations from a delegator +func GetREDsKey(delAddr sdk.AccAddress) []byte { + return append(RedelegationKey, delAddr.Bytes()...) +} + +// gets the prefix keyspace for all redelegations redelegating away from a source validator +func GetREDsFromValSrcIndexKey(valSrcAddr sdk.ValAddress) []byte { + return append(RedelegationByValSrcIndexKey, valSrcAddr.Bytes()...) +} + +// gets the prefix keyspace for all redelegations redelegating towards a destination validator +func GetREDsToValDstIndexKey(valDstAddr sdk.ValAddress) []byte { + return append(RedelegationByValDstIndexKey, valDstAddr.Bytes()...) +} + +// gets the prefix keyspace for all redelegations redelegating towards a destination validator +// from a particular delegator +func GetREDsByDelToValDstIndexKey(delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) []byte { + return append( + GetREDsToValDstIndexKey(valDstAddr), + delAddr.Bytes()...) +} diff --git a/x/staking/types/keys_test.go b/x/staking/types/keys_test.go new file mode 100644 index 0000000..5875c65 --- /dev/null +++ b/x/staking/types/keys_test.go @@ -0,0 +1,90 @@ +package types + +import ( + "encoding/hex" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/crypto/ed25519" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + keysPK1 = ed25519.GenPrivKeyFromSecret([]byte{1}).PubKey() + keysPK2 = ed25519.GenPrivKeyFromSecret([]byte{2}).PubKey() + keysPK3 = ed25519.GenPrivKeyFromSecret([]byte{3}).PubKey() + keysAddr1 = keysPK1.Address() + keysAddr2 = keysPK2.Address() + keysAddr3 = keysPK3.Address() +) + +func TestGetValidatorPowerRank(t *testing.T) { + valAddr1 := sdk.ValAddress(keysAddr1) + emptyDesc := Description{} + val1 := NewValidator(valAddr1, keysPK1, emptyDesc) + val1.Tokens = sdk.ZeroInt() + val2, val3, val4 := val1, val1, val1 + val2.Tokens = sdk.TokensFromConsensusPower(1) + val3.Tokens = sdk.TokensFromConsensusPower(10) + x := new(big.Int).Exp(big.NewInt(2), big.NewInt(40), big.NewInt(0)) + val4.Tokens = sdk.TokensFromConsensusPower(x.Int64()) + + tests := []struct { + validator Validator + wantHex string + }{ + {val1, "2300000000000000009c288ede7df62742fc3b7d0962045a8cef0f79f6"}, + {val2, "2300000000000000019c288ede7df62742fc3b7d0962045a8cef0f79f6"}, + {val3, "23000000000000000a9c288ede7df62742fc3b7d0962045a8cef0f79f6"}, + {val4, "2300000100000000009c288ede7df62742fc3b7d0962045a8cef0f79f6"}, + } + for i, tt := range tests { + got := hex.EncodeToString(getValidatorPowerRank(tt.validator)) + + assert.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i) + } +} + +func TestGetREDByValDstIndexKey(t *testing.T) { + tests := []struct { + delAddr sdk.AccAddress + valSrcAddr sdk.ValAddress + valDstAddr sdk.ValAddress + wantHex string + }{ + {sdk.AccAddress(keysAddr1), sdk.ValAddress(keysAddr1), sdk.ValAddress(keysAddr1), + "3663d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f08609"}, + {sdk.AccAddress(keysAddr1), sdk.ValAddress(keysAddr2), sdk.ValAddress(keysAddr3), + "363ab62f0d93849be495e21e3e9013a517038f45bd63d771218209d8bd03c482f69dfba57310f086095ef3b5f25c54946d4a89fc0d09d2f126614540f2"}, + {sdk.AccAddress(keysAddr2), sdk.ValAddress(keysAddr1), sdk.ValAddress(keysAddr3), + "363ab62f0d93849be495e21e3e9013a517038f45bd5ef3b5f25c54946d4a89fc0d09d2f126614540f263d771218209d8bd03c482f69dfba57310f08609"}, + } + for i, tt := range tests { + got := hex.EncodeToString(GetREDByValDstIndexKey(tt.delAddr, tt.valSrcAddr, tt.valDstAddr)) + + assert.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i) + } +} + +func TestGetREDByValSrcIndexKey(t *testing.T) { + tests := []struct { + delAddr sdk.AccAddress + valSrcAddr sdk.ValAddress + valDstAddr sdk.ValAddress + wantHex string + }{ + {sdk.AccAddress(keysAddr1), sdk.ValAddress(keysAddr1), sdk.ValAddress(keysAddr1), + "3563d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f08609"}, + {sdk.AccAddress(keysAddr1), sdk.ValAddress(keysAddr2), sdk.ValAddress(keysAddr3), + "355ef3b5f25c54946d4a89fc0d09d2f126614540f263d771218209d8bd03c482f69dfba57310f086093ab62f0d93849be495e21e3e9013a517038f45bd"}, + {sdk.AccAddress(keysAddr2), sdk.ValAddress(keysAddr1), sdk.ValAddress(keysAddr3), + "3563d771218209d8bd03c482f69dfba57310f086095ef3b5f25c54946d4a89fc0d09d2f126614540f23ab62f0d93849be495e21e3e9013a517038f45bd"}, + } + for i, tt := range tests { + got := hex.EncodeToString(GetREDByValSrcIndexKey(tt.delAddr, tt.valSrcAddr, tt.valDstAddr)) + + assert.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i) + } +} diff --git a/x/staking/types/msg.go b/x/staking/types/msg.go new file mode 100644 index 0000000..cc99138 --- /dev/null +++ b/x/staking/types/msg.go @@ -0,0 +1,345 @@ +package types + +import ( + "bytes" + "encoding/json" + + "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// ensure Msg interface compliance at compile time +var ( + _ sdk.Msg = &MsgCreateValidator{} + _ sdk.Msg = &MsgEditValidator{} + _ sdk.Msg = &MsgDelegate{} + _ sdk.Msg = &MsgUndelegate{} + _ sdk.Msg = &MsgBeginRedelegate{} +) + +//______________________________________________________________________ + +// MsgCreateValidator - struct for bonding transactions +type MsgCreateValidator struct { + Description Description `json:"description" yaml:"description"` + Commission CommissionRates `json:"commission" yaml:"commission"` + MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` + DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` + ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` + PubKey crypto.PubKey `json:"pubkey" yaml:"pubkey"` + Value sdk.Coin `json:"value" yaml:"value"` +} + +type msgCreateValidatorJSON struct { + Description Description `json:"description" yaml:"description"` + Commission CommissionRates `json:"commission" yaml:"commission"` + MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` + DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` + ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` + PubKey string `json:"pubkey" yaml:"pubkey"` + Value sdk.Coin `json:"value" yaml:"value"` +} + +// Default way to create validator. Delegator address and validator address are the same +func NewMsgCreateValidator( + valAddr sdk.ValAddress, pubKey crypto.PubKey, selfDelegation sdk.Coin, + description Description, commission CommissionRates, minSelfDelegation sdk.Int, +) MsgCreateValidator { + + return MsgCreateValidator{ + Description: description, + DelegatorAddress: sdk.AccAddress(valAddr), + ValidatorAddress: valAddr, + PubKey: pubKey, + Value: selfDelegation, + Commission: commission, + MinSelfDelegation: minSelfDelegation, + } +} + +//nolint +func (msg MsgCreateValidator) Route() string { return RouterKey } +func (msg MsgCreateValidator) Type() string { return "create_validator" } + +// Return address(es) that must sign over msg.GetSignBytes() +func (msg MsgCreateValidator) GetSigners() []sdk.AccAddress { + // delegator is first signer so delegator pays fees + addrs := []sdk.AccAddress{msg.DelegatorAddress} + + if !bytes.Equal(msg.DelegatorAddress.Bytes(), msg.ValidatorAddress.Bytes()) { + // if validator addr is not same as delegator addr, validator must sign + // msg as well + addrs = append(addrs, sdk.AccAddress(msg.ValidatorAddress)) + } + return addrs +} + +// MarshalJSON implements the json.Marshaler interface to provide custom JSON +// serialization of the MsgCreateValidator type. +func (msg MsgCreateValidator) MarshalJSON() ([]byte, error) { + return json.Marshal(msgCreateValidatorJSON{ + Description: msg.Description, + Commission: msg.Commission, + DelegatorAddress: msg.DelegatorAddress, + ValidatorAddress: msg.ValidatorAddress, + PubKey: sdk.MustBech32ifyConsPub(msg.PubKey), + Value: msg.Value, + MinSelfDelegation: msg.MinSelfDelegation, + }) +} + +// UnmarshalJSON implements the json.Unmarshaler interface to provide custom +// JSON deserialization of the MsgCreateValidator type. +func (msg *MsgCreateValidator) UnmarshalJSON(bz []byte) error { + var msgCreateValJSON msgCreateValidatorJSON + if err := json.Unmarshal(bz, &msgCreateValJSON); err != nil { + return err + } + + msg.Description = msgCreateValJSON.Description + msg.Commission = msgCreateValJSON.Commission + msg.DelegatorAddress = msgCreateValJSON.DelegatorAddress + msg.ValidatorAddress = msgCreateValJSON.ValidatorAddress + var err error + msg.PubKey, err = sdk.GetConsPubKeyBech32(msgCreateValJSON.PubKey) + if err != nil { + return err + } + msg.Value = msgCreateValJSON.Value + msg.MinSelfDelegation = msgCreateValJSON.MinSelfDelegation + + return nil +} + +// GetSignBytes returns the message bytes to sign over. +func (msg MsgCreateValidator) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +// quick validity check +func (msg MsgCreateValidator) ValidateBasic() sdk.Error { + // note that unmarshaling from bech32 ensures either empty or valid + if msg.DelegatorAddress.Empty() { + return ErrNilDelegatorAddr(DefaultCodespace) + } + if msg.ValidatorAddress.Empty() { + return ErrNilValidatorAddr(DefaultCodespace) + } + if !sdk.AccAddress(msg.ValidatorAddress).Equals(msg.DelegatorAddress) { + return ErrBadValidatorAddr(DefaultCodespace) + } + if msg.Value.Amount.LTE(sdk.ZeroInt()) { + return ErrBadDelegationAmount(DefaultCodespace) + } + if msg.Description == (Description{}) { + return sdk.NewError(DefaultCodespace, CodeInvalidInput, "description must be included") + } + if msg.Commission == (CommissionRates{}) { + return sdk.NewError(DefaultCodespace, CodeInvalidInput, "commission must be included") + } + if err := msg.Commission.Validate(); err != nil { + return err + } + if !msg.MinSelfDelegation.GT(sdk.ZeroInt()) { + return ErrMinSelfDelegationInvalid(DefaultCodespace) + } + if msg.Value.Amount.LT(msg.MinSelfDelegation) { + return ErrSelfDelegationBelowMinimum(DefaultCodespace) + } + + return nil +} + +// MsgEditValidator - struct for editing a validator +type MsgEditValidator struct { + Description + ValidatorAddress sdk.ValAddress `json:"address" yaml:"address"` + + // We pass a reference to the new commission rate and min self delegation as it's not mandatory to + // update. If not updated, the deserialized rate will be zero with no way to + // distinguish if an update was intended. + // + // REF: #2373 + CommissionRate *sdk.Dec `json:"commission_rate" yaml:"commission_rate"` + MinSelfDelegation *sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` +} + +func NewMsgEditValidator(valAddr sdk.ValAddress, description Description, newRate *sdk.Dec, newMinSelfDelegation *sdk.Int) MsgEditValidator { + return MsgEditValidator{ + Description: description, + CommissionRate: newRate, + ValidatorAddress: valAddr, + MinSelfDelegation: newMinSelfDelegation, + } +} + +//nolint +func (msg MsgEditValidator) Route() string { return RouterKey } +func (msg MsgEditValidator) Type() string { return "edit_validator" } +func (msg MsgEditValidator) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{sdk.AccAddress(msg.ValidatorAddress)} +} + +// get the bytes for the message signer to sign on +func (msg MsgEditValidator) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +// quick validity check +func (msg MsgEditValidator) ValidateBasic() sdk.Error { + if msg.ValidatorAddress.Empty() { + return sdk.NewError(DefaultCodespace, CodeInvalidInput, "nil validator address") + } + + if msg.Description == (Description{}) { + return sdk.NewError(DefaultCodespace, CodeInvalidInput, "transaction must include some information to modify") + } + + if msg.MinSelfDelegation != nil && !(*msg.MinSelfDelegation).GT(sdk.ZeroInt()) { + return ErrMinSelfDelegationInvalid(DefaultCodespace) + } + + if msg.CommissionRate != nil { + if msg.CommissionRate.GT(sdk.OneDec()) || msg.CommissionRate.LT(sdk.ZeroDec()) { + return sdk.NewError(DefaultCodespace, CodeInvalidInput, "commission rate must be between 0 and 1, inclusive") + } + } + + return nil +} + +// MsgDelegate - struct for bonding transactions +type MsgDelegate struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` + ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` + Amount sdk.Coin `json:"amount" yaml:"amount"` +} + +func NewMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) MsgDelegate { + return MsgDelegate{ + DelegatorAddress: delAddr, + ValidatorAddress: valAddr, + Amount: amount, + } +} + +//nolint +func (msg MsgDelegate) Route() string { return RouterKey } +func (msg MsgDelegate) Type() string { return "delegate" } +func (msg MsgDelegate) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.DelegatorAddress} +} + +// get the bytes for the message signer to sign on +func (msg MsgDelegate) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +// quick validity check +func (msg MsgDelegate) ValidateBasic() sdk.Error { + if msg.DelegatorAddress.Empty() { + return ErrNilDelegatorAddr(DefaultCodespace) + } + if msg.ValidatorAddress.Empty() { + return ErrNilValidatorAddr(DefaultCodespace) + } + if msg.Amount.Amount.LTE(sdk.ZeroInt()) { + return ErrBadDelegationAmount(DefaultCodespace) + } + return nil +} + +//______________________________________________________________________ + +// MsgDelegate - struct for bonding transactions +type MsgBeginRedelegate struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` + ValidatorSrcAddress sdk.ValAddress `json:"validator_src_address" yaml:"validator_src_address"` + ValidatorDstAddress sdk.ValAddress `json:"validator_dst_address" yaml:"validator_dst_address"` + Amount sdk.Coin `json:"amount" yaml:"amount"` +} + +func NewMsgBeginRedelegate(delAddr sdk.AccAddress, valSrcAddr, + valDstAddr sdk.ValAddress, amount sdk.Coin) MsgBeginRedelegate { + + return MsgBeginRedelegate{ + DelegatorAddress: delAddr, + ValidatorSrcAddress: valSrcAddr, + ValidatorDstAddress: valDstAddr, + Amount: amount, + } +} + +//nolint +func (msg MsgBeginRedelegate) Route() string { return RouterKey } +func (msg MsgBeginRedelegate) Type() string { return "begin_redelegate" } +func (msg MsgBeginRedelegate) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.DelegatorAddress} +} + +// get the bytes for the message signer to sign on +func (msg MsgBeginRedelegate) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +// quick validity check +func (msg MsgBeginRedelegate) ValidateBasic() sdk.Error { + if msg.DelegatorAddress.Empty() { + return ErrNilDelegatorAddr(DefaultCodespace) + } + if msg.ValidatorSrcAddress.Empty() { + return ErrNilValidatorAddr(DefaultCodespace) + } + if msg.ValidatorDstAddress.Empty() { + return ErrNilValidatorAddr(DefaultCodespace) + } + if msg.Amount.Amount.LTE(sdk.ZeroInt()) { + return ErrBadSharesAmount(DefaultCodespace) + } + return nil +} + +// MsgUndelegate - struct for unbonding transactions +type MsgUndelegate struct { + DelegatorAddress sdk.AccAddress `json:"delegator_address" yaml:"delegator_address"` + ValidatorAddress sdk.ValAddress `json:"validator_address" yaml:"validator_address"` + Amount sdk.Coin `json:"amount" yaml:"amount"` +} + +func NewMsgUndelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) MsgUndelegate { + return MsgUndelegate{ + DelegatorAddress: delAddr, + ValidatorAddress: valAddr, + Amount: amount, + } +} + +//nolint +func (msg MsgUndelegate) Route() string { return RouterKey } +func (msg MsgUndelegate) Type() string { return "begin_unbonding" } +func (msg MsgUndelegate) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.DelegatorAddress} } + +// get the bytes for the message signer to sign on +func (msg MsgUndelegate) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +// quick validity check +func (msg MsgUndelegate) ValidateBasic() sdk.Error { + if msg.DelegatorAddress.Empty() { + return ErrNilDelegatorAddr(DefaultCodespace) + } + if msg.ValidatorAddress.Empty() { + return ErrNilValidatorAddr(DefaultCodespace) + } + if msg.Amount.Amount.LTE(sdk.ZeroInt()) { + return ErrBadSharesAmount(DefaultCodespace) + } + return nil +} diff --git a/x/staking/types/msg_test.go b/x/staking/types/msg_test.go new file mode 100644 index 0000000..a127e4d --- /dev/null +++ b/x/staking/types/msg_test.go @@ -0,0 +1,156 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + coinPos = sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000) + coinZero = sdk.NewInt64Coin(sdk.DefaultBondDenom, 0) +) + +// test ValidateBasic for MsgCreateValidator +func TestMsgCreateValidator(t *testing.T) { + commission1 := NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) + commission2 := NewCommissionRates(sdk.NewDec(5), sdk.NewDec(5), sdk.NewDec(5)) + + tests := []struct { + name, moniker, identity, website, details string + CommissionRates CommissionRates + minSelfDelegation sdk.Int + validatorAddr sdk.ValAddress + pubkey crypto.PubKey + bond sdk.Coin + expectPass bool + }{ + {"basic good", "a", "b", "c", "d", commission1, sdk.OneInt(), valAddr1, pk1, coinPos, true}, + {"partial description", "", "", "c", "", commission1, sdk.OneInt(), valAddr1, pk1, coinPos, true}, + {"empty description", "", "", "", "", commission2, sdk.OneInt(), valAddr1, pk1, coinPos, false}, + {"empty address", "a", "b", "c", "d", commission2, sdk.OneInt(), emptyAddr, pk1, coinPos, false}, + {"empty pubkey", "a", "b", "c", "d", commission1, sdk.OneInt(), valAddr1, emptyPubkey, coinPos, true}, + {"empty bond", "a", "b", "c", "d", commission2, sdk.OneInt(), valAddr1, pk1, coinZero, false}, + {"zero min self delegation", "a", "b", "c", "d", commission1, sdk.ZeroInt(), valAddr1, pk1, coinPos, false}, + {"negative min self delegation", "a", "b", "c", "d", commission1, sdk.NewInt(-1), valAddr1, pk1, coinPos, false}, + {"delegation less than min self delegation", "a", "b", "c", "d", commission1, coinPos.Amount.Add(sdk.OneInt()), valAddr1, pk1, coinPos, false}, + } + + for _, tc := range tests { + description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details) + msg := NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description, tc.CommissionRates, tc.minSelfDelegation) + if tc.expectPass { + require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) + } else { + require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name) + } + } +} + +// test ValidateBasic for MsgEditValidator +func TestMsgEditValidator(t *testing.T) { + tests := []struct { + name, moniker, identity, website, details string + validatorAddr sdk.ValAddress + expectPass bool + }{ + {"basic good", "a", "b", "c", "d", valAddr1, true}, + {"partial description", "", "", "c", "", valAddr1, true}, + {"empty description", "", "", "", "", valAddr1, false}, + {"empty address", "a", "b", "c", "d", emptyAddr, false}, + } + + for _, tc := range tests { + description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details) + newRate := sdk.ZeroDec() + newMinSelfDelegation := sdk.OneInt() + + msg := NewMsgEditValidator(tc.validatorAddr, description, &newRate, &newMinSelfDelegation) + if tc.expectPass { + require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) + } else { + require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name) + } + } +} + +// test ValidateBasic for MsgDelegate +func TestMsgDelegate(t *testing.T) { + tests := []struct { + name string + delegatorAddr sdk.AccAddress + validatorAddr sdk.ValAddress + bond sdk.Coin + expectPass bool + }{ + {"basic good", sdk.AccAddress(valAddr1), valAddr2, coinPos, true}, + {"self bond", sdk.AccAddress(valAddr1), valAddr1, coinPos, true}, + {"empty delegator", sdk.AccAddress(emptyAddr), valAddr1, coinPos, false}, + {"empty validator", sdk.AccAddress(valAddr1), emptyAddr, coinPos, false}, + {"empty bond", sdk.AccAddress(valAddr1), valAddr2, coinZero, false}, + } + + for _, tc := range tests { + msg := NewMsgDelegate(tc.delegatorAddr, tc.validatorAddr, tc.bond) + if tc.expectPass { + require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) + } else { + require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name) + } + } +} + +// test ValidateBasic for MsgUnbond +func TestMsgBeginRedelegate(t *testing.T) { + tests := []struct { + name string + delegatorAddr sdk.AccAddress + validatorSrcAddr sdk.ValAddress + validatorDstAddr sdk.ValAddress + amount sdk.Coin + expectPass bool + }{ + {"regular", sdk.AccAddress(valAddr1), valAddr2, valAddr3, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), true}, + {"zero amount", sdk.AccAddress(valAddr1), valAddr2, valAddr3, sdk.NewInt64Coin(sdk.DefaultBondDenom, 0), false}, + {"empty delegator", sdk.AccAddress(emptyAddr), valAddr1, valAddr3, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false}, + {"empty source validator", sdk.AccAddress(valAddr1), emptyAddr, valAddr3, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false}, + {"empty destination validator", sdk.AccAddress(valAddr1), valAddr2, emptyAddr, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false}, + } + + for _, tc := range tests { + msg := NewMsgBeginRedelegate(tc.delegatorAddr, tc.validatorSrcAddr, tc.validatorDstAddr, tc.amount) + if tc.expectPass { + require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) + } else { + require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name) + } + } +} + +// test ValidateBasic for MsgUnbond +func TestMsgUndelegate(t *testing.T) { + tests := []struct { + name string + delegatorAddr sdk.AccAddress + validatorAddr sdk.ValAddress + amount sdk.Coin + expectPass bool + }{ + {"regular", sdk.AccAddress(valAddr1), valAddr2, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), true}, + {"zero amount", sdk.AccAddress(valAddr1), valAddr2, sdk.NewInt64Coin(sdk.DefaultBondDenom, 0), false}, + {"empty delegator", sdk.AccAddress(emptyAddr), valAddr1, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false}, + {"empty validator", sdk.AccAddress(valAddr1), emptyAddr, sdk.NewInt64Coin(sdk.DefaultBondDenom, 1), false}, + } + + for _, tc := range tests { + msg := NewMsgUndelegate(tc.delegatorAddr, tc.validatorAddr, tc.amount) + if tc.expectPass { + require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name) + } else { + require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name) + } + } +} diff --git a/x/staking/types/params.go b/x/staking/types/params.go new file mode 100644 index 0000000..82e9c42 --- /dev/null +++ b/x/staking/types/params.go @@ -0,0 +1,118 @@ +package types + +import ( + "bytes" + "fmt" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" +) + +// Staking params default values +const ( + // DefaultUnbondingTime reflects three weeks in seconds as the default + // unbonding time. + // TODO: Justify our choice of default here. + DefaultUnbondingTime time.Duration = time.Hour * 24 * 7 * 3 + + // Default maximum number of bonded validators + DefaultMaxValidators uint16 = 100 + + // Default maximum entries in a UBD/RED pair + DefaultMaxEntries uint16 = 7 +) + +// nolint - Keys for parameter access +var ( + KeyUnbondingTime = []byte("UnbondingTime") + KeyMaxValidators = []byte("MaxValidators") + KeyMaxEntries = []byte("KeyMaxEntries") + KeyBondDenom = []byte("BondDenom") +) + +var _ params.ParamSet = (*Params)(nil) + +// Params defines the high level settings for staking +type Params struct { + UnbondingTime time.Duration `json:"unbonding_time" yaml:"unbonding_time"` // time duration of unbonding + MaxValidators uint16 `json:"max_validators" yaml:"max_validators"` // maximum number of validators (max uint16 = 65535) + MaxEntries uint16 `json:"max_entries" yaml:"max_entries"` // max entries for either unbonding delegation or redelegation (per pair/trio) + // note: we need to be a bit careful about potential overflow here, since this is user-determined + BondDenom string `json:"bond_denom" yaml:"bond_denom"` // bondable coin denomination +} + +// NewParams creates a new Params instance +func NewParams(unbondingTime time.Duration, maxValidators, maxEntries uint16, + bondDenom string) Params { + + return Params{ + UnbondingTime: unbondingTime, + MaxValidators: maxValidators, + MaxEntries: maxEntries, + BondDenom: bondDenom, + } +} + +// Implements params.ParamSet +func (p *Params) ParamSetPairs() params.ParamSetPairs { + return params.ParamSetPairs{ + {KeyUnbondingTime, &p.UnbondingTime}, + {KeyMaxValidators, &p.MaxValidators}, + {KeyMaxEntries, &p.MaxEntries}, + {KeyBondDenom, &p.BondDenom}, + } +} + +// Equal returns a boolean determining if two Param types are identical. +// TODO: This is slower than comparing struct fields directly +func (p Params) Equal(p2 Params) bool { + bz1 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&p) + bz2 := ModuleCdc.MustMarshalBinaryLengthPrefixed(&p2) + return bytes.Equal(bz1, bz2) +} + +// DefaultParams returns a default set of parameters. +func DefaultParams() Params { + return NewParams(DefaultUnbondingTime, DefaultMaxValidators, DefaultMaxEntries, sdk.DefaultBondDenom) +} + +// String returns a human readable string representation of the parameters. +func (p Params) String() string { + return fmt.Sprintf(`Params: + Unbonding Time: %s + Max Validators: %d + Max Entries: %d + Bonded Coin Denom: %s`, p.UnbondingTime, + p.MaxValidators, p.MaxEntries, p.BondDenom) +} + +// unmarshal the current staking params value from store key or panic +func MustUnmarshalParams(cdc *codec.Codec, value []byte) Params { + params, err := UnmarshalParams(cdc, value) + if err != nil { + panic(err) + } + return params +} + +// unmarshal the current staking params value from store key +func UnmarshalParams(cdc *codec.Codec, value []byte) (params Params, err error) { + err = cdc.UnmarshalBinaryLengthPrefixed(value, ¶ms) + if err != nil { + return + } + return +} + +// validate a set of params +func (p Params) Validate() error { + if p.BondDenom == "" { + return fmt.Errorf("staking parameter BondDenom can't be an empty string") + } + if p.MaxValidators == 0 { + return fmt.Errorf("staking parameter MaxValidators must be a positive integer") + } + return nil +} diff --git a/x/staking/types/params_test.go b/x/staking/types/params_test.go new file mode 100644 index 0000000..c18700e --- /dev/null +++ b/x/staking/types/params_test.go @@ -0,0 +1,21 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParamsEqual(t *testing.T) { + p1 := DefaultParams() + p2 := DefaultParams() + + ok := p1.Equal(p2) + require.True(t, ok) + + p2.UnbondingTime = 60 * 60 * 24 * 2 + p2.BondDenom = "soup" + + ok = p1.Equal(p2) + require.False(t, ok) +} diff --git a/x/staking/types/pool.go b/x/staking/types/pool.go new file mode 100644 index 0000000..c19bf59 --- /dev/null +++ b/x/staking/types/pool.go @@ -0,0 +1,39 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// names used as root for pool module accounts: +// +// - NotBondedPool -> "not_bonded_tokens_pool" +// +// - BondedPool -> "bonded_tokens_pool" +const ( + NotBondedPoolName = "not_bonded_tokens_pool" + BondedPoolName = "bonded_tokens_pool" +) + +// Pool - tracking bonded and not-bonded token supply of the bond denomination +type Pool struct { + NotBondedTokens sdk.Int `json:"not_bonded_tokens" yaml:"not_bonded_tokens"` // tokens which are not bonded to a validator (unbonded or unbonding) + BondedTokens sdk.Int `json:"bonded_tokens" yaml:"bonded_tokens"` // tokens which are currently bonded to a validator +} + +// NewPool creates a new Pool instance used for queries +func NewPool(notBonded, bonded sdk.Int) Pool { + return Pool{ + NotBondedTokens: notBonded, + BondedTokens: bonded, + } +} + +// String returns a human readable string representation of a pool. +func (p Pool) String() string { + return fmt.Sprintf(`Pool: + Not Bonded Tokens: %s + Bonded Tokens: %s`, p.NotBondedTokens, + p.BondedTokens) +} diff --git a/x/staking/types/querier.go b/x/staking/types/querier.go new file mode 100644 index 0000000..ba38a55 --- /dev/null +++ b/x/staking/types/querier.go @@ -0,0 +1,98 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// query endpoints supported by the staking Querier +const ( + QueryValidators = "validators" + QueryValidator = "validator" + QueryDelegatorDelegations = "delegatorDelegations" + QueryDelegatorUnbondingDelegations = "delegatorUnbondingDelegations" + QueryRedelegations = "redelegations" + QueryValidatorDelegations = "validatorDelegations" + QueryValidatorRedelegations = "validatorRedelegations" + QueryValidatorUnbondingDelegations = "validatorUnbondingDelegations" + QueryDelegation = "delegation" + QueryUnbondingDelegation = "unbondingDelegation" + QueryDelegatorValidators = "delegatorValidators" + QueryDelegatorValidator = "delegatorValidator" + QueryPool = "pool" + QueryParameters = "parameters" +) + +// defines the params for the following queries: +// - 'custom/staking/delegatorDelegations' +// - 'custom/staking/delegatorUnbondingDelegations' +// - 'custom/staking/delegatorRedelegations' +// - 'custom/staking/delegatorValidators' +type QueryDelegatorParams struct { + DelegatorAddr sdk.AccAddress +} + +func NewQueryDelegatorParams(delegatorAddr sdk.AccAddress) QueryDelegatorParams { + return QueryDelegatorParams{ + DelegatorAddr: delegatorAddr, + } +} + +// defines the params for the following queries: +// - 'custom/staking/validator' +// - 'custom/staking/validatorDelegations' +// - 'custom/staking/validatorUnbondingDelegations' +// - 'custom/staking/validatorRedelegations' +type QueryValidatorParams struct { + ValidatorAddr sdk.ValAddress +} + +func NewQueryValidatorParams(validatorAddr sdk.ValAddress) QueryValidatorParams { + return QueryValidatorParams{ + ValidatorAddr: validatorAddr, + } +} + +// defines the params for the following queries: +// - 'custom/staking/delegation' +// - 'custom/staking/unbondingDelegation' +// - 'custom/staking/delegatorValidator' +type QueryBondsParams struct { + DelegatorAddr sdk.AccAddress + ValidatorAddr sdk.ValAddress +} + +func NewQueryBondsParams(delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) QueryBondsParams { + return QueryBondsParams{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + } +} + +// defines the params for the following queries: +// - 'custom/staking/redelegation' +type QueryRedelegationParams struct { + DelegatorAddr sdk.AccAddress + SrcValidatorAddr sdk.ValAddress + DstValidatorAddr sdk.ValAddress +} + +func NewQueryRedelegationParams(delegatorAddr sdk.AccAddress, + srcValidatorAddr, dstValidatorAddr sdk.ValAddress) QueryRedelegationParams { + + return QueryRedelegationParams{ + DelegatorAddr: delegatorAddr, + SrcValidatorAddr: srcValidatorAddr, + DstValidatorAddr: dstValidatorAddr, + } +} + +// QueryValidatorsParams defines the params for the following queries: +// - 'custom/staking/validators' +type QueryValidatorsParams struct { + Page, Limit int + Status string +} + +func NewQueryValidatorsParams(page, limit int, status string) QueryValidatorsParams { + return QueryValidatorsParams{page, limit, status} +} diff --git a/x/staking/types/test_utils.go b/x/staking/types/test_utils.go new file mode 100644 index 0000000..e5c84b4 --- /dev/null +++ b/x/staking/types/test_utils.go @@ -0,0 +1,24 @@ +package types + +import ( + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// nolint: deadcode unused +var ( + pk1 = ed25519.GenPrivKey().PubKey() + pk2 = ed25519.GenPrivKey().PubKey() + pk3 = ed25519.GenPrivKey().PubKey() + addr1 = pk1.Address() + addr2 = pk2.Address() + addr3 = pk3.Address() + valAddr1 = sdk.ValAddress(addr1) + valAddr2 = sdk.ValAddress(addr2) + valAddr3 = sdk.ValAddress(addr3) + + emptyAddr sdk.ValAddress + emptyPubkey crypto.PubKey +) diff --git a/x/staking/types/validator.go b/x/staking/types/validator.go new file mode 100644 index 0000000..014ff84 --- /dev/null +++ b/x/staking/types/validator.go @@ -0,0 +1,496 @@ +package types + +import ( + "bytes" + "fmt" + "strings" + "time" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + tmtypes "github.com/tendermint/tendermint/types" + yaml "gopkg.in/yaml.v2" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/exported" +) + +// nolint +const ( + // TODO: Why can't we just have one string description which can be JSON by convention + MaxMonikerLength = 70 + MaxIdentityLength = 3000 + MaxWebsiteLength = 140 + MaxDetailsLength = 280 +) + +// Implements Validator interface +var _ exported.ValidatorI = Validator{} + +// Validator defines the total amount of bond shares and their exchange rate to +// coins. Slashing results in a decrease in the exchange rate, allowing correct +// calculation of future undelegations without iterating over delegators. +// When coins are delegated to this validator, the validator is credited with a +// delegation whose number of bond shares is based on the amount of coins delegated +// divided by the current exchange rate. Voting power can be calculated as total +// bonded shares multiplied by exchange rate. +type Validator struct { + OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` // address of the validator's operator; bech encoded in JSON + ConsPubKey crypto.PubKey `json:"consensus_pubkey" yaml:"consensus_pubkey"` // the consensus public key of the validator; bech encoded in JSON + Jailed bool `json:"jailed" yaml:"jailed"` // has the validator been jailed from bonded status? + Status sdk.BondStatus `json:"status" yaml:"status"` // validator status (bonded/unbonding/unbonded) + Tokens sdk.Int `json:"tokens" yaml:"tokens"` // delegated tokens (incl. self-delegation) + DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` // total shares issued to a validator's delegators + Description Description `json:"description" yaml:"description"` // description terms for the validator + UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding + UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding + Commission Commission `json:"commission" yaml:"commission"` // commission parameters + MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` // validator's self declared minimum self delegation +} + +// custom marshal yaml function due to consensus pubkey +func (v Validator) MarshalYAML() (interface{}, error) { + bs, err := yaml.Marshal(struct { + OperatorAddress sdk.ValAddress + ConsPubKey string + Jailed bool + Status sdk.BondStatus + Tokens sdk.Int + DelegatorShares sdk.Dec + Description Description + UnbondingHeight int64 + UnbondingCompletionTime time.Time + Commission Commission + MinSelfDelegation sdk.Int + }{ + OperatorAddress: v.OperatorAddress, + ConsPubKey: sdk.MustBech32ifyConsPub(v.ConsPubKey), + Jailed: v.Jailed, + Status: v.Status, + Tokens: v.Tokens, + DelegatorShares: v.DelegatorShares, + Description: v.Description, + UnbondingHeight: v.UnbondingHeight, + UnbondingCompletionTime: v.UnbondingCompletionTime, + Commission: v.Commission, + MinSelfDelegation: v.MinSelfDelegation, + }) + if err != nil { + return nil, err + } + + return string(bs), nil +} + +// Validators is a collection of Validator +type Validators []Validator + +func (v Validators) String() (out string) { + for _, val := range v { + out += val.String() + "\n" + } + return strings.TrimSpace(out) +} + +// ToSDKValidators - convenience function convert []Validators to []sdk.Validators +func (v Validators) ToSDKValidators() (validators []exported.ValidatorI) { + for _, val := range v { + validators = append(validators, val) + } + return validators +} + +// NewValidator - initialize a new validator +func NewValidator(operator sdk.ValAddress, pubKey crypto.PubKey, description Description) Validator { + return Validator{ + OperatorAddress: operator, + ConsPubKey: pubKey, + Jailed: false, + Status: sdk.Unbonded, + Tokens: sdk.ZeroInt(), + DelegatorShares: sdk.ZeroDec(), + Description: description, + UnbondingHeight: int64(0), + UnbondingCompletionTime: time.Unix(0, 0).UTC(), + Commission: NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), + MinSelfDelegation: sdk.OneInt(), + } +} + +// return the redelegation +func MustMarshalValidator(cdc *codec.Codec, validator Validator) []byte { + return cdc.MustMarshalBinaryLengthPrefixed(validator) +} + +// unmarshal a redelegation from a store value +func MustUnmarshalValidator(cdc *codec.Codec, value []byte) Validator { + validator, err := UnmarshalValidator(cdc, value) + if err != nil { + panic(err) + } + return validator +} + +// unmarshal a redelegation from a store value +func UnmarshalValidator(cdc *codec.Codec, value []byte) (validator Validator, err error) { + err = cdc.UnmarshalBinaryLengthPrefixed(value, &validator) + return validator, err +} + +// String returns a human readable string representation of a validator. +func (v Validator) String() string { + bechConsPubKey, err := sdk.Bech32ifyConsPub(v.ConsPubKey) + if err != nil { + panic(err) + } + return fmt.Sprintf(`Validator + Operator Address: %s + Validator Consensus Pubkey: %s + Jailed: %v + Status: %s + Tokens: %s + Delegator Shares: %s + Description: %s + Unbonding Height: %d + Unbonding Completion Time: %v + Minimum Self Delegation: %v + Commission: %s`, v.OperatorAddress, bechConsPubKey, + v.Jailed, v.Status, v.Tokens, + v.DelegatorShares, v.Description, + v.UnbondingHeight, v.UnbondingCompletionTime, v.MinSelfDelegation, v.Commission) +} + +// this is a helper struct used for JSON de- and encoding only +type bechValidator struct { + OperatorAddress sdk.ValAddress `json:"operator_address" yaml:"operator_address"` // the bech32 address of the validator's operator + ConsPubKey string `json:"consensus_pubkey" yaml:"consensus_pubkey"` // the bech32 consensus public key of the validator + Jailed bool `json:"jailed" yaml:"jailed"` // has the validator been jailed from bonded status? + Status sdk.BondStatus `json:"status" yaml:"status"` // validator status (bonded/unbonding/unbonded) + Tokens sdk.Int `json:"tokens" yaml:"tokens"` // delegated tokens (incl. self-delegation) + DelegatorShares sdk.Dec `json:"delegator_shares" yaml:"delegator_shares"` // total shares issued to a validator's delegators + Description Description `json:"description" yaml:"description"` // description terms for the validator + UnbondingHeight int64 `json:"unbonding_height" yaml:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding + UnbondingCompletionTime time.Time `json:"unbonding_time" yaml:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding + Commission Commission `json:"commission" yaml:"commission"` // commission parameters + MinSelfDelegation sdk.Int `json:"min_self_delegation" yaml:"min_self_delegation"` // minimum self delegation +} + +// MarshalJSON marshals the validator to JSON using Bech32 +func (v Validator) MarshalJSON() ([]byte, error) { + bechConsPubKey, err := sdk.Bech32ifyConsPub(v.ConsPubKey) + if err != nil { + return nil, err + } + + return codec.Cdc.MarshalJSON(bechValidator{ + OperatorAddress: v.OperatorAddress, + ConsPubKey: bechConsPubKey, + Jailed: v.Jailed, + Status: v.Status, + Tokens: v.Tokens, + DelegatorShares: v.DelegatorShares, + Description: v.Description, + UnbondingHeight: v.UnbondingHeight, + UnbondingCompletionTime: v.UnbondingCompletionTime, + MinSelfDelegation: v.MinSelfDelegation, + Commission: v.Commission, + }) +} + +// UnmarshalJSON unmarshals the validator from JSON using Bech32 +func (v *Validator) UnmarshalJSON(data []byte) error { + bv := &bechValidator{} + if err := codec.Cdc.UnmarshalJSON(data, bv); err != nil { + return err + } + consPubKey, err := sdk.GetConsPubKeyBech32(bv.ConsPubKey) + if err != nil { + return err + } + *v = Validator{ + OperatorAddress: bv.OperatorAddress, + ConsPubKey: consPubKey, + Jailed: bv.Jailed, + Tokens: bv.Tokens, + Status: bv.Status, + DelegatorShares: bv.DelegatorShares, + Description: bv.Description, + UnbondingHeight: bv.UnbondingHeight, + UnbondingCompletionTime: bv.UnbondingCompletionTime, + Commission: bv.Commission, + MinSelfDelegation: bv.MinSelfDelegation, + } + return nil +} + +// only the vitals +func (v Validator) TestEquivalent(v2 Validator) bool { + return v.ConsPubKey.Equals(v2.ConsPubKey) && + bytes.Equal(v.OperatorAddress, v2.OperatorAddress) && + v.Status.Equal(v2.Status) && + v.Tokens.Equal(v2.Tokens) && + v.DelegatorShares.Equal(v2.DelegatorShares) && + v.Description == v2.Description && + v.Commission.Equal(v2.Commission) +} + +// return the TM validator address +func (v Validator) ConsAddress() sdk.ConsAddress { + return sdk.ConsAddress(v.ConsPubKey.Address()) +} + +// IsBonded checks if the validator status equals Bonded +func (v Validator) IsBonded() bool { + return v.GetStatus().Equal(sdk.Bonded) +} + +// IsUnbonded checks if the validator status equals Unbonded +func (v Validator) IsUnbonded() bool { + return v.GetStatus().Equal(sdk.Unbonded) +} + +// IsUnbonding checks if the validator status equals Unbonding +func (v Validator) IsUnbonding() bool { + return v.GetStatus().Equal(sdk.Unbonding) +} + +// constant used in flags to indicate that description field should not be updated +const DoNotModifyDesc = "[do-not-modify]" + +// Description - description fields for a validator +type Description struct { + Moniker string `json:"moniker" yaml:"moniker"` // name + Identity string `json:"identity" yaml:"identity"` // optional identity signature (ex. UPort or Keybase) + Website string `json:"website" yaml:"website"` // optional website link + Details string `json:"details" yaml:"details"` // optional details +} + +// NewDescription returns a new Description with the provided values. +func NewDescription(moniker, identity, website, details string) Description { + return Description{ + Moniker: moniker, + Identity: identity, + Website: website, + Details: details, + } +} + +// UpdateDescription updates the fields of a given description. An error is +// returned if the resulting description contains an invalid length. +func (d Description) UpdateDescription(d2 Description) (Description, sdk.Error) { + if d2.Moniker == DoNotModifyDesc { + d2.Moniker = d.Moniker + } + if d2.Identity == DoNotModifyDesc { + d2.Identity = d.Identity + } + if d2.Website == DoNotModifyDesc { + d2.Website = d.Website + } + if d2.Details == DoNotModifyDesc { + d2.Details = d.Details + } + + return Description{ + Moniker: d2.Moniker, + Identity: d2.Identity, + Website: d2.Website, + Details: d2.Details, + }.EnsureLength() +} + +// EnsureLength ensures the length of a validator's description. +func (d Description) EnsureLength() (Description, sdk.Error) { + if len(d.Moniker) > MaxMonikerLength { + return d, ErrDescriptionLength(DefaultCodespace, "moniker", len(d.Moniker), MaxMonikerLength) + } + if len(d.Identity) > MaxIdentityLength { + return d, ErrDescriptionLength(DefaultCodespace, "identity", len(d.Identity), MaxIdentityLength) + } + if len(d.Website) > MaxWebsiteLength { + return d, ErrDescriptionLength(DefaultCodespace, "website", len(d.Website), MaxWebsiteLength) + } + if len(d.Details) > MaxDetailsLength { + return d, ErrDescriptionLength(DefaultCodespace, "details", len(d.Details), MaxDetailsLength) + } + + return d, nil +} + +// ABCIValidatorUpdate returns an abci.ValidatorUpdate from a staking validator type +// with the full validator power +func (v Validator) ABCIValidatorUpdate() abci.ValidatorUpdate { + return abci.ValidatorUpdate{ + PubKey: tmtypes.TM2PB.PubKey(v.ConsPubKey), + Power: v.ConsensusPower(), + } +} + +// ABCIValidatorUpdateZero returns an abci.ValidatorUpdate from a staking validator type +// with zero power used for validator updates. +func (v Validator) ABCIValidatorUpdateZero() abci.ValidatorUpdate { + return abci.ValidatorUpdate{ + PubKey: tmtypes.TM2PB.PubKey(v.ConsPubKey), + Power: 0, + } +} + +// SetInitialCommission attempts to set a validator's initial commission. An +// error is returned if the commission is invalid. +func (v Validator) SetInitialCommission(commission Commission) (Validator, sdk.Error) { + if err := commission.Validate(); err != nil { + return v, err + } + + v.Commission = commission + return v, nil +} + +// In some situations, the exchange rate becomes invalid, e.g. if +// Validator loses all tokens due to slashing. In this case, +// make all future delegations invalid. +func (v Validator) InvalidExRate() bool { + return v.Tokens.IsZero() && v.DelegatorShares.IsPositive() +} + +// calculate the token worth of provided shares +func (v Validator) TokensFromShares(shares sdk.Dec) sdk.Dec { + return (shares.MulInt(v.Tokens)).Quo(v.DelegatorShares) +} + +// calculate the token worth of provided shares, truncated +func (v Validator) TokensFromSharesTruncated(shares sdk.Dec) sdk.Dec { + return (shares.MulInt(v.Tokens)).QuoTruncate(v.DelegatorShares) +} + +// TokensFromSharesRoundUp returns the token worth of provided shares, rounded +// up. +func (v Validator) TokensFromSharesRoundUp(shares sdk.Dec) sdk.Dec { + return (shares.MulInt(v.Tokens)).QuoRoundUp(v.DelegatorShares) +} + +// SharesFromTokens returns the shares of a delegation given a bond amount. It +// returns an error if the validator has no tokens. +func (v Validator) SharesFromTokens(amt sdk.Int) (sdk.Dec, sdk.Error) { + if v.Tokens.IsZero() { + return sdk.ZeroDec(), ErrInsufficientShares(DefaultCodespace) + } + + return v.GetDelegatorShares().MulInt(amt).QuoInt(v.GetTokens()), nil +} + +// SharesFromTokensTruncated returns the truncated shares of a delegation given +// a bond amount. It returns an error if the validator has no tokens. +func (v Validator) SharesFromTokensTruncated(amt sdk.Int) (sdk.Dec, sdk.Error) { + if v.Tokens.IsZero() { + return sdk.ZeroDec(), ErrInsufficientShares(DefaultCodespace) + } + + return v.GetDelegatorShares().MulInt(amt).QuoTruncate(v.GetTokens().ToDec()), nil +} + +// get the bonded tokens which the validator holds +func (v Validator) BondedTokens() sdk.Int { + if v.IsBonded() { + return v.Tokens + } + return sdk.ZeroInt() +} + +// get the consensus-engine power +// a reduction of 10^6 from validator tokens is applied +func (v Validator) ConsensusPower() int64 { + if v.IsBonded() { + return v.PotentialConsensusPower() + } + return 0 +} + +// potential consensus-engine power +func (v Validator) PotentialConsensusPower() int64 { + return sdk.TokensToConsensusPower(v.Tokens) +} + +// UpdateStatus updates the location of the shares within a validator +// to reflect the new status +func (v Validator) UpdateStatus(newStatus sdk.BondStatus) Validator { + v.Status = newStatus + return v +} + +// AddTokensFromDel adds tokens to a validator +func (v Validator) AddTokensFromDel(amount sdk.Int) (Validator, sdk.Dec) { + + // calculate the shares to issue + var issuedShares sdk.Dec + if v.DelegatorShares.IsZero() { + // the first delegation to a validator sets the exchange rate to one + issuedShares = amount.ToDec() + } else { + shares, err := v.SharesFromTokens(amount) + if err != nil { + panic(err) + } + + issuedShares = shares + } + + v.Tokens = v.Tokens.Add(amount) + v.DelegatorShares = v.DelegatorShares.Add(issuedShares) + + return v, issuedShares +} + +// RemoveTokens removes tokens from a validator +func (v Validator) RemoveTokens(tokens sdk.Int) Validator { + if tokens.IsNegative() { + panic(fmt.Sprintf("should not happen: trying to remove negative tokens %v", tokens)) + } + if v.Tokens.LT(tokens) { + panic(fmt.Sprintf("should not happen: only have %v tokens, trying to remove %v", v.Tokens, tokens)) + } + v.Tokens = v.Tokens.Sub(tokens) + return v +} + +// RemoveDelShares removes delegator shares from a validator. +// NOTE: because token fractions are left in the valiadator, +// the exchange rate of future shares of this validator can increase. +func (v Validator) RemoveDelShares(delShares sdk.Dec) (Validator, sdk.Int) { + + remainingShares := v.DelegatorShares.Sub(delShares) + var issuedTokens sdk.Int + if remainingShares.IsZero() { + + // last delegation share gets any trimmings + issuedTokens = v.Tokens + v.Tokens = sdk.ZeroInt() + } else { + + // leave excess tokens in the validator + // however fully use all the delegator shares + issuedTokens = v.TokensFromShares(delShares).TruncateInt() + v.Tokens = v.Tokens.Sub(issuedTokens) + if v.Tokens.IsNegative() { + panic("attempting to remove more tokens than available in validator") + } + } + + v.DelegatorShares = remainingShares + return v, issuedTokens +} + +// nolint - for ValidatorI +func (v Validator) IsJailed() bool { return v.Jailed } +func (v Validator) GetMoniker() string { return v.Description.Moniker } +func (v Validator) GetStatus() sdk.BondStatus { return v.Status } +func (v Validator) GetOperator() sdk.ValAddress { return v.OperatorAddress } +func (v Validator) GetConsPubKey() crypto.PubKey { return v.ConsPubKey } +func (v Validator) GetConsAddr() sdk.ConsAddress { return sdk.ConsAddress(v.ConsPubKey.Address()) } +func (v Validator) GetTokens() sdk.Int { return v.Tokens } +func (v Validator) GetBondedTokens() sdk.Int { return v.BondedTokens() } +func (v Validator) GetConsensusPower() int64 { return v.ConsensusPower() } +func (v Validator) GetCommission() sdk.Dec { return v.Commission.Rate } +func (v Validator) GetMinSelfDelegation() sdk.Int { return v.MinSelfDelegation } +func (v Validator) GetDelegatorShares() sdk.Dec { return v.DelegatorShares } diff --git a/x/staking/types/validator_test.go b/x/staking/types/validator_test.go new file mode 100644 index 0000000..7330866 --- /dev/null +++ b/x/staking/types/validator_test.go @@ -0,0 +1,304 @@ +package types + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + tmtypes "github.com/tendermint/tendermint/types" + "gopkg.in/yaml.v2" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestValidatorTestEquivalent(t *testing.T) { + val1 := NewValidator(valAddr1, pk1, Description{}) + val2 := NewValidator(valAddr1, pk1, Description{}) + + ok := val1.TestEquivalent(val2) + require.True(t, ok) + + val2 = NewValidator(valAddr2, pk2, Description{}) + + ok = val1.TestEquivalent(val2) + require.False(t, ok) +} + +func TestUpdateDescription(t *testing.T) { + d1 := Description{ + Website: "https://validator.cosmos", + Details: "Test validator", + } + + d2 := Description{ + Moniker: DoNotModifyDesc, + Identity: DoNotModifyDesc, + Website: DoNotModifyDesc, + Details: DoNotModifyDesc, + } + + d3 := Description{ + Moniker: "", + Identity: "", + Website: "", + Details: "", + } + + d, err := d1.UpdateDescription(d2) + require.Nil(t, err) + require.Equal(t, d, d1) + + d, err = d1.UpdateDescription(d3) + require.Nil(t, err) + require.Equal(t, d, d3) +} + +func TestABCIValidatorUpdate(t *testing.T) { + validator := NewValidator(valAddr1, pk1, Description{}) + + abciVal := validator.ABCIValidatorUpdate() + require.Equal(t, tmtypes.TM2PB.PubKey(validator.ConsPubKey), abciVal.PubKey) + require.Equal(t, validator.BondedTokens().Int64(), abciVal.Power) +} + +func TestABCIValidatorUpdateZero(t *testing.T) { + validator := NewValidator(valAddr1, pk1, Description{}) + + abciVal := validator.ABCIValidatorUpdateZero() + require.Equal(t, tmtypes.TM2PB.PubKey(validator.ConsPubKey), abciVal.PubKey) + require.Equal(t, int64(0), abciVal.Power) +} + +func TestShareTokens(t *testing.T) { + validator := Validator{ + OperatorAddress: valAddr1, + ConsPubKey: pk1, + Status: sdk.Bonded, + Tokens: sdk.NewInt(100), + DelegatorShares: sdk.NewDec(100), + } + assert.True(sdk.DecEq(t, sdk.NewDec(50), validator.TokensFromShares(sdk.NewDec(50)))) + + validator.Tokens = sdk.NewInt(50) + assert.True(sdk.DecEq(t, sdk.NewDec(25), validator.TokensFromShares(sdk.NewDec(50)))) + assert.True(sdk.DecEq(t, sdk.NewDec(5), validator.TokensFromShares(sdk.NewDec(10)))) +} + +func TestRemoveTokens(t *testing.T) { + valPubKey := pk1 + valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + + validator := Validator{ + OperatorAddress: valAddr, + ConsPubKey: valPubKey, + Status: sdk.Bonded, + Tokens: sdk.NewInt(100), + DelegatorShares: sdk.NewDec(100), + } + + // remove tokens and test check everything + validator = validator.RemoveTokens(sdk.NewInt(10)) + require.Equal(t, int64(90), validator.Tokens.Int64()) + + // update validator to from bonded -> unbonded + validator = validator.UpdateStatus(sdk.Unbonded) + require.Equal(t, sdk.Unbonded, validator.Status) + + validator = validator.RemoveTokens(sdk.NewInt(10)) + require.Panics(t, func() { validator.RemoveTokens(sdk.NewInt(-1)) }) + require.Panics(t, func() { validator.RemoveTokens(sdk.NewInt(100)) }) +} + +func TestAddTokensValidatorBonded(t *testing.T) { + validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{}) + validator = validator.UpdateStatus(sdk.Bonded) + validator, delShares := validator.AddTokensFromDel(sdk.NewInt(10)) + + assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares)) + assert.True(sdk.IntEq(t, sdk.NewInt(10), validator.BondedTokens())) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) +} + +func TestAddTokensValidatorUnbonding(t *testing.T) { + validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{}) + validator = validator.UpdateStatus(sdk.Unbonding) + validator, delShares := validator.AddTokensFromDel(sdk.NewInt(10)) + + assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares)) + assert.Equal(t, sdk.Unbonding, validator.Status) + assert.True(sdk.IntEq(t, sdk.NewInt(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) +} + +func TestAddTokensValidatorUnbonded(t *testing.T) { + + validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{}) + validator = validator.UpdateStatus(sdk.Unbonded) + validator, delShares := validator.AddTokensFromDel(sdk.NewInt(10)) + + assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares)) + assert.Equal(t, sdk.Unbonded, validator.Status) + assert.True(sdk.IntEq(t, sdk.NewInt(10), validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) +} + +// TODO refactor to make simpler like the AddToken tests above +func TestRemoveDelShares(t *testing.T) { + valA := Validator{ + OperatorAddress: sdk.ValAddress(pk1.Address().Bytes()), + ConsPubKey: pk1, + Status: sdk.Bonded, + Tokens: sdk.NewInt(100), + DelegatorShares: sdk.NewDec(100), + } + + // Remove delegator shares + valB, coinsB := valA.RemoveDelShares(sdk.NewDec(10)) + require.Equal(t, int64(10), coinsB.Int64()) + require.Equal(t, int64(90), valB.DelegatorShares.RoundInt64()) + require.Equal(t, int64(90), valB.BondedTokens().Int64()) + + // specific case from random tests + poolTokens := sdk.NewInt(5102) + delShares := sdk.NewDec(115) + validator := Validator{ + OperatorAddress: sdk.ValAddress(pk1.Address().Bytes()), + ConsPubKey: pk1, + Status: sdk.Bonded, + Tokens: poolTokens, + DelegatorShares: delShares, + } + + shares := sdk.NewDec(29) + _, tokens := validator.RemoveDelShares(shares) + + require.True(sdk.IntEq(t, sdk.NewInt(1286), tokens)) +} + +func TestAddTokensFromDel(t *testing.T) { + validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{}) + + validator, shares := validator.AddTokensFromDel(sdk.NewInt(6)) + require.True(sdk.DecEq(t, sdk.NewDec(6), shares)) + require.True(sdk.DecEq(t, sdk.NewDec(6), validator.DelegatorShares)) + require.True(sdk.IntEq(t, sdk.NewInt(6), validator.Tokens)) + + validator, shares = validator.AddTokensFromDel(sdk.NewInt(3)) + require.True(sdk.DecEq(t, sdk.NewDec(3), shares)) + require.True(sdk.DecEq(t, sdk.NewDec(9), validator.DelegatorShares)) + require.True(sdk.IntEq(t, sdk.NewInt(9), validator.Tokens)) +} + +func TestUpdateStatus(t *testing.T) { + validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{}) + validator, _ = validator.AddTokensFromDel(sdk.NewInt(100)) + require.Equal(t, sdk.Unbonded, validator.Status) + require.Equal(t, int64(100), validator.Tokens.Int64()) + + // Unbonded to Bonded + validator = validator.UpdateStatus(sdk.Bonded) + require.Equal(t, sdk.Bonded, validator.Status) + + // Bonded to Unbonding + validator = validator.UpdateStatus(sdk.Unbonding) + require.Equal(t, sdk.Unbonding, validator.Status) + + // Unbonding to Bonded + validator = validator.UpdateStatus(sdk.Bonded) + require.Equal(t, sdk.Bonded, validator.Status) +} + +func TestPossibleOverflow(t *testing.T) { + delShares := sdk.NewDec(391432570689183511).Quo(sdk.NewDec(40113011844664)) + validator := Validator{ + OperatorAddress: sdk.ValAddress(pk1.Address().Bytes()), + ConsPubKey: pk1, + Status: sdk.Bonded, + Tokens: sdk.NewInt(2159), + DelegatorShares: delShares, + } + + newValidator, _ := validator.AddTokensFromDel(sdk.NewInt(71)) + + require.False(t, newValidator.DelegatorShares.IsNegative()) + require.False(t, newValidator.Tokens.IsNegative()) +} + +func TestValidatorMarshalUnmarshalJSON(t *testing.T) { + validator := NewValidator(valAddr1, pk1, Description{}) + js, err := codec.Cdc.MarshalJSON(validator) + require.NoError(t, err) + require.NotEmpty(t, js) + require.Contains(t, string(js), "\"consensus_pubkey\":\"cosmosvalconspu") + got := &Validator{} + err = codec.Cdc.UnmarshalJSON(js, got) + assert.NoError(t, err) + assert.Equal(t, validator, *got) +} + +func TestValidatorSetInitialCommission(t *testing.T) { + val := NewValidator(valAddr1, pk1, Description{}) + testCases := []struct { + validator Validator + commission Commission + expectedErr bool + }{ + {val, NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), false}, + {val, NewCommission(sdk.ZeroDec(), sdk.NewDecWithPrec(-1, 1), sdk.ZeroDec()), true}, + {val, NewCommission(sdk.ZeroDec(), sdk.NewDec(15000000000), sdk.ZeroDec()), true}, + {val, NewCommission(sdk.NewDecWithPrec(-1, 1), sdk.ZeroDec(), sdk.ZeroDec()), true}, + {val, NewCommission(sdk.NewDecWithPrec(2, 1), sdk.NewDecWithPrec(1, 1), sdk.ZeroDec()), true}, + {val, NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(-1, 1)), true}, + {val, NewCommission(sdk.ZeroDec(), sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(2, 1)), true}, + } + + for i, tc := range testCases { + val, err := tc.validator.SetInitialCommission(tc.commission) + + if tc.expectedErr { + require.Error(t, err, + "expected error for test case #%d with commission: %s", i, tc.commission, + ) + } else { + require.NoError(t, err, + "unexpected error for test case #%d with commission: %s", i, tc.commission, + ) + require.Equal(t, tc.commission, val.Commission, + "invalid validator commission for test case #%d with commission: %s", i, tc.commission, + ) + } + } +} + +func TestValidatorMarshalYAML(t *testing.T) { + validator := NewValidator(valAddr1, pk1, Description{}) + bechifiedPub, err := sdk.Bech32ifyConsPub(validator.ConsPubKey) + require.NoError(t, err) + bs, err := yaml.Marshal(validator) + require.NoError(t, err) + want := fmt.Sprintf(`| + operatoraddress: %s + conspubkey: %s + jailed: false + status: 0 + tokens: "0" + delegatorshares: "0.000000000000000000" + description: + moniker: "" + identity: "" + website: "" + details: "" + unbondingheight: 0 + unbondingcompletiontime: 1970-01-01T00:00:00Z + commission: + commission_rates: + rate: "0.000000000000000000" + max_rate: "0.000000000000000000" + max_change_rate: "0.000000000000000000" + update_time: 1970-01-01T00:00:00Z + minselfdelegation: "1" +`, validator.OperatorAddress.String(), bechifiedPub) + require.Equal(t, want, string(bs)) +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..b62fe82 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4697 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@truffle/blockchain-utils@^0.0.11": + version "0.0.11" + resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.11.tgz#9886f4cb7a9f20deded4451ac78f8567ae5c0d75" + integrity sha512-9MyQ/20M96clhIcC7fVFIckGSB8qMsmcdU6iYt98HXJ9GOLNKsCaJFz1OVsJncVreYwTUhoEXTrVBc8zrmPDJQ== + +"@truffle/contract-schema@^3.0.14": + version "3.0.18" + resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.0.18.tgz#ae4cd33ed2e7bb94f0db85ddc1107fcdfdc1ab9a" + integrity sha512-uuuXpgBzuu+RD5On0Xd7S9tB14uJfapnzrpGB5qR8D3wxv/2inUnCZnrHKc1OqjinqskBOW8CuURWBBmZnDJ3w== + dependencies: + ajv "^6.10.0" + crypto-js "^3.1.9-1" + debug "^4.1.0" + +"@truffle/error@^0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.6.tgz#75d499845b4b3a40537889e7d04c663afcaee85d" + integrity sha512-QUM9ZWiwlXGixFGpV18g5I6vua6/r+ZV9W/5DQA5go9A3eZUNPHPaTKMIQPJLYn6+ZV5jg5H28zCHq56LHF3yA== + +"@truffle/error@^0.0.7": + version "0.0.7" + resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.7.tgz#e9db39885575647ef08bf624b0c13fe46d41a209" + integrity sha512-UIfVKsXSXocKnn5+RNklUXNoGd/JVj7V8KmC48TQzmjU33HQI86PX0JDS7SpHMHasI3w9X//1q7Lu7nZtj3Zzg== + +"@truffle/hdwallet-provider@^1.0.18": + version "1.0.24" + resolved "https://registry.yarnpkg.com/@truffle/hdwallet-provider/-/hdwallet-provider-1.0.24.tgz#e091a59868faa9eb84eaa886d78d6782522f056c" + integrity sha512-8Y6mogh96W/5diIZIlV21KoOlX2e0zU8Gf+oqAFjE+iFKD2nO2BSrEDlOgg42AgLkPy+Y//QK5JuQwQPzpd6pA== + dependencies: + "@truffle/provider" "^0.2.1" + any-promise "^1.3.0" + bindings "^1.5.0" + bip39 "^2.4.2" + ethereum-protocol "^1.0.1" + ethereumjs-tx "^1.0.0" + ethereumjs-util "^6.1.0" + ethereumjs-wallet "^0.6.3" + web3 "1.2.2" + web3-provider-engine "https://github.com/trufflesuite/provider-engine#web3-one" + +"@truffle/interface-adapter@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.3.2.tgz#61742799a532a3e1de3fe138b40803b3cfd08c1e" + integrity sha512-hK6wkzfRMotVQ4TqUTXJBpVgISSZfQCjsc4XevkQLbUQzPXHhDqaIqGpIHmQ96RWAHKUlOOkHyJPpbWB4jnu1A== + dependencies: + bn.js "^4.11.8" + ethers "^4.0.32" + lodash "^4.17.13" + web3 "1.2.2" + +"@truffle/provider@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@truffle/provider/-/provider-0.2.1.tgz#7a5b65e0b35b1293a0c017abbc3590590a64e947" + integrity sha512-f87TJTWxZw/RMKw978GwI1PbQW1V+uOK49wztX55X324+Yf7LwjEP4XKVz2j+OQ2hZlsGjwd7I79ZPNQ4/cYkg== + dependencies: + "@truffle/error" "^0.0.7" + "@truffle/interface-adapter" "^0.3.2" + web3 "1.2.2" + +"@types/bn.js@^4.11.3", "@types/bn.js@^4.11.4": + version "4.11.5" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.5.tgz#40e36197433f78f807524ec623afcf0169ac81dc" + integrity sha512-AEAZcIZga0JgVMHNtl1CprA/hXX7/wPt79AgR4XqaDt7jyj3QWYw6LPoOiznPtugDmlubUnAahMs2PFxGcQrng== + dependencies: + "@types/node" "*" + +"@types/node@*", "@types/node@^12.6.1": + version "12.12.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.7.tgz#01e4ea724d9e3bd50d90c11fd5980ba317d8fa11" + integrity sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w== + +"@types/node@^10.12.18", "@types/node@^10.3.2": + version "10.17.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.5.tgz#c1920150f7b90708a7d0f3add12a06bc9123c055" + integrity sha512-RElZIr/7JreF1eY6oD5RF3kpmdcreuQPjg5ri4oQ5g9sq7YWU8HkfB3eH8GwAwxf5OaCh0VPi7r4N/yoTGelrA== + +abstract-leveldown@~2.6.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" + integrity sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" + integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== + dependencies: + xtend "~4.0.0" + +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= + +aes-js@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== + +ajv@^6.10.0, ajv@^6.5.5: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +any-promise@1.3.0, any-promise@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +asn1.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +async-eventemitter@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^1.4.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.0.14, babel-core@^6.26.0: + version "6.26.3" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" + integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.1" + debug "^2.6.9" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.8" + slash "^1.0.0" + source-map "^0.5.7" + +babel-generator@^6.26.0: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + integrity sha1-zORReto1b0IgvK6KAsKzRvmlZmQ= + dependencies: + babel-helper-explode-assignable-expression "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + integrity sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340= + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" + integrity sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8= + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + integrity sha1-8luCz33BBDPFX3BZLVdGQArCLKo= + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + integrity sha1-00dbjAPtmCQqJbSDUasYOZ01gKk= + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + integrity sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + integrity sha1-HssnaJydJVE+rbyZFKc/VAi+enY= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + integrity sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" + integrity sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI= + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + integrity sha1-XsWBgnrXI/7N04HxySg5BnbkVRs= + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + integrity sha1-v22/5Dk40XNpohPKiov3S2qQqxo= + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI= + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + integrity sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + integrity sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU= + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + integrity sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4= + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + integrity sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM= + +babel-plugin-transform-async-to-generator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + integrity sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E= + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + integrity sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + integrity sha1-u8UbSflk1wy42OC5ToICRs46YUE= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.23.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" + integrity sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8= + dependencies: + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-plugin-transform-es2015-classes@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + integrity sha1-WkxYpQyclGHlZLSyo7+ryXolhNs= + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + integrity sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM= + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + integrity sha1-mXux8auWf2gtKwh2/jWNYOdlxW0= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + integrity sha1-c+s9MQypaePvnskcU3QabxV2Qj4= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + integrity sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + integrity sha1-g0yJhTvDaxrw86TF26qU/Y6sqos= + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + integrity sha1-T1SgLWzWbPkVKAAZox0xklN3yi4= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + integrity sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ= + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.26.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" + integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q== + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-es2015-modules-systemjs@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + integrity sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM= + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + integrity sha1-rJl+YoXNGO1hdq22B9YCNErThGg= + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + integrity sha1-JM72muIcuDp/hgPa0CH1cusnj40= + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + integrity sha1-V6w1GrScrxSpfNE7CfZv3wpiXys= + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + integrity sha1-JPh11nIch2YbvZmkYi5R8U3jiqA= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + integrity sha1-1taKmfia7cRTbIGlQujdnxdG+NE= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + integrity sha1-AMHNsaynERLN8M9hJsLta0V8zbw= + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + integrity sha1-qEs0UPfp+PH2g51taH2oS7EjbY0= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + integrity sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + integrity sha1-04sS9C6nMj9yk4fxinxa4frrNek= + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + integrity sha1-KrDJx/MJj6SJB3cruBP+QejeOg4= + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" + integrity sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8= + dependencies: + regenerator-transform "^0.10.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + integrity sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-preset-env@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a" + integrity sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg== + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.23.0" + babel-plugin-transform-es2015-classes "^6.23.0" + babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-destructuring "^6.23.0" + babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-for-of "^6.23.0" + babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.23.0" + babel-plugin-transform-es2015-modules-systemjs "^6.23.0" + babel-plugin-transform-es2015-modules-umd "^6.23.0" + babel-plugin-transform-es2015-object-super "^6.22.0" + babel-plugin-transform-es2015-parameters "^6.23.0" + babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.23.0" + babel-plugin-transform-es2015-unicode-regex "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-regenerator "^6.22.0" + browserslist "^3.2.6" + invariant "^2.2.2" + semver "^5.3.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + integrity sha1-btAhFz4vy0htestFxgCahW9kcHE= + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.24.1, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babelify@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/babelify/-/babelify-7.3.0.tgz#aa56aede7067fd7bd549666ee16dc285087e88e5" + integrity sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU= + dependencies: + babel-core "^6.0.14" + object-assign "^4.0.0" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= + dependencies: + precond "0.2" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base-x@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.7.tgz#1c5a7fafe8f66b4114063e8da102799d4e7c408f" + integrity sha512-zAKJGuQPihXW22fkrfOclUUZXM2g92z5GzlSMHxhO6r6Qj+Nm0ccaGNBzDZojzwOMkpjAv4J0fOv1U4go+a4iw== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +bignumber.js@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-6.0.0.tgz#bbfa047644609a5af093e9cbd83b0461fa3f6002" + integrity sha512-x247jIuy60/+FtMRvscqfxtVHQf8AGx2hm9c6btkgC0x/hp9yt+teISNhvF8WlwRkCc5yF2fDECH8SIMe8j+GA== + +bignumber.js@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" + integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== + +bindings@^1.2.1, bindings@^1.3.1, bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bip39@^2.4.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.6.0.tgz#9e3a720b42ec8b3fbe4038f1e445317b6a99321c" + integrity sha512-RrnQRG2EgEoqO24ea+Q/fftuPUZLmrEM3qNhhGsA3PbaXaCW791LTzPuVyx/VprXQcTbPJ3K3UeTna8ZnVl2sg== + dependencies: + create-hash "^1.1.0" + pbkdf2 "^3.0.9" + randombytes "^2.0.1" + safe-buffer "^5.0.1" + unorm "^1.3.3" + +bip66@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + integrity sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI= + dependencies: + safe-buffer "^5.0.1" + +bl@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" + integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA== + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + +bluebird@^3.5.0, bluebird@^3.5.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de" + integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= + +bn.js@4.11.8, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +body-parser@1.19.0, body-parser@^1.16.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sha3@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/browserify-sha3/-/browserify-sha3-0.0.4.tgz#086c47b8c82316c9d47022c26185954576dd8e26" + integrity sha1-CGxHuMgjFsnUcCLCYYWVRXbdjiY= + dependencies: + js-sha3 "^0.6.1" + safe-buffer "^5.1.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserslist@^3.2.6: + version "3.2.8" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6" + integrity sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ== + dependencies: + caniuse-lite "^1.0.30000844" + electron-to-chromium "^1.3.47" + +bs58@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-2.0.1.tgz#55908d58f1982aba2008fa1bed8f91998a29bf8d" + integrity sha1-VZCNWPGYKrogCPob7Y+RmYopv40= + +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha1-YGSkD6dutDxyOrqe+PbhIW0QURo= + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^5.0.5, buffer@^5.2.1: + version "5.4.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115" + integrity sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +caniuse-lite@^1.0.30000844: + version "1.0.30001008" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001008.tgz#b8841b1df78a9f5ed9702537ef592f1f8772c0d9" + integrity sha512-b8DJyb+VVXZGRgJUa30cbk8gKHZ3LOZTBLaUEEVr2P4xpmFigOCc62CO4uzquW641Ouq1Rm9N+rWLWdSYDaDIw== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chai-as-promised@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" + integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA== + dependencies: + check-error "^1.0.2" + +chai-bignumber@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chai-bignumber/-/chai-bignumber-3.0.0.tgz#e90cf1f468355bbb11a9acd051222586cd2648a9" + integrity sha512-SubOtaSI2AILWTWe2j0c6i2yFT/f9J6UBjeVGDuwDiPLkF/U5+/eTWUE3sbCZ1KgcPF6UJsDVYbIxaYA097MQA== + +chai@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" + integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.0" + type-detect "^4.0.5" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +checkpoint-store@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" + integrity sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY= + dependencies: + functional-red-black-tree "^1.0.1" + +chownr@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" + integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +coinstring@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/coinstring/-/coinstring-2.3.0.tgz#cdb63363a961502404a25afb82c2e26d5ff627a4" + integrity sha1-zbYzY6lhUCQEolr7gsLibV/2J6Q= + dependencies: + bs58 "^2.0.1" + create-hash "^1.1.1" + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@~2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" + integrity sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ= + dependencies: + graceful-readlink ">= 1.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.5.1: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +convert-source-map@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +cookiejar@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== + +core-js@^2.4.0, core-js@^2.5.0: + version "2.6.10" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.10.tgz#8a5b8391f8cc7013da703411ce5b585706300d7f" + integrity sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cors@^2.8.1: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +create-ecdh@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-env@5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.0.5.tgz#4383d364d9660873dd185b398af3bfef5efffef3" + integrity sha1-Q4PTZNlmCHPdGFs5ivO/717//vM= + dependencies: + cross-spawn "^5.1.0" + is-windows "^1.0.0" + +cross-fetch@^2.1.0, cross-fetch@^2.1.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e" + integrity sha512-PrWWNH3yL2NYIb/7WF/5vFG3DCQiXDOVf8k3ijatbrtnwNuhMWLC7YF7uqf53tbTFDzHIUD8oITw4Bxt8ST3Nw== + dependencies: + node-fetch "2.1.2" + whatwg-fetch "2.0.4" + +cross-spawn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +crypto-js@^3.1.9-1: + version "3.1.9-1" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.9-1.tgz#fda19e761fc077e01ffbfdc6e9fdfc59e8806cd8" + integrity sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg= + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9, debug@^2.2.0, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.2.0, decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" + integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== + dependencies: + file-type "^5.2.0" + is-stream "^1.1.0" + tar-stream "^1.5.2" + +decompress-tarbz2@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" + integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== + dependencies: + decompress-tar "^4.1.0" + file-type "^6.1.0" + is-stream "^1.1.0" + seek-bzip "^1.0.5" + unbzip2-stream "^1.0.9" + +decompress-targz@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" + integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== + dependencies: + decompress-tar "^4.1.1" + file-type "^5.2.0" + is-stream "^1.1.0" + +decompress-unzip@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" + integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k= + dependencies: + file-type "^3.8.0" + get-stream "^2.2.0" + pify "^2.3.0" + yauzl "^2.4.2" + +decompress@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d" + integrity sha1-eu3YVCflqS2s/lVnSnxQXpbQH50= + dependencies: + decompress-tar "^4.0.0" + decompress-tarbz2 "^4.0.0" + decompress-targz "^4.0.0" + decompress-unzip "^4.0.1" + graceful-fs "^4.1.10" + make-dir "^1.0.0" + pify "^2.3.0" + strip-dirs "^2.0.0" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +deep-equal@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= + +defer-to-connect@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.0.tgz#b41bd7efa8508cef13f8456975f7a278c72833fd" + integrity sha512-WE2sZoctWm/v4smfCAdjYbrfS55JiMRdlY9ZubFhsYbteCK9+BvAx4YV7nPjYM6ZnX5BcoVKwfmyx9sIFTgQMQ== + +deferred-leveldown@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz#3acd2e0b75d1669924bc0a4b642851131173e1eb" + integrity sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA== + dependencies: + abstract-leveldown "~2.6.0" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +defined@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= + dependencies: + repeating "^2.0.0" + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dom-walk@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" + integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg= + +dotenv@^8.1.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + +drbg.js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" + integrity sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs= + dependencies: + browserify-aes "^1.0.6" + create-hash "^1.1.2" + create-hmac "^1.1.4" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.47: + version "1.3.306" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.306.tgz#e8265301d053d5f74e36cb876486830261fbe946" + integrity sha512-frDqXvrIROoYvikSKTIKbHbzO6M3/qC6kCIt/1FOa9kALe++c4VAJnwjSFvf1tYLEUsP2n9XZ4XSCyqc3l7A/A== + +elliptic@6.3.3: + version "6.3.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.3.tgz#5482d9646d54bcb89fd7d994fc9e2e9568876e3f" + integrity sha1-VILZZG1UvLif19mU/J4ulWiHbj8= + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + inherits "^2.0.1" + +elliptic@^6.0.0, elliptic@^6.4.0, elliptic@^6.4.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.1.tgz#c380f5f909bf1b9b4428d028cd18d3b0efd6b52b" + integrity sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s= + dependencies: + iconv-lite "~0.4.13" + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +errno@~0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== + dependencies: + prr "~1.0.1" + +es-abstract@^1.13.0, es-abstract@^1.5.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d" + integrity sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg== + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.0" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-inspect "^1.6.0" + object-keys "^1.1.1" + string.prototype.trimleft "^2.1.0" + string.prototype.trimright "^2.1.0" + +es-to-primitive@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.52" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.52.tgz#bb21777e919a04263736ded120a9d665f10ea63f" + integrity sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.2" + next-tick "~1.0.0" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eth-block-tracker@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-3.0.1.tgz#95cd5e763c7293e0b1b2790a2a39ac2ac188a5e1" + integrity sha512-WUVxWLuhMmsfenfZvFO5sbl1qFY2IqUlw/FPVmjjdElpqLsZtSG+wPe9Dz7W/sB6e80HgFKknOmKk2eNlznHug== + dependencies: + eth-query "^2.1.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.3" + ethjs-util "^0.1.3" + json-rpc-engine "^3.6.0" + pify "^2.3.0" + tape "^4.6.3" + +eth-ens-namehash@2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-json-rpc-infura@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/eth-json-rpc-infura/-/eth-json-rpc-infura-3.2.1.tgz#26702a821067862b72d979c016fd611502c6057f" + integrity sha512-W7zR4DZvyTn23Bxc0EWsq4XGDdD63+XPUCEhV2zQvQGavDVC4ZpFDK4k99qN7bd7/fjj37+rxmuBOBeIqCA5Mw== + dependencies: + cross-fetch "^2.1.1" + eth-json-rpc-middleware "^1.5.0" + json-rpc-engine "^3.4.0" + json-rpc-error "^2.0.0" + +eth-json-rpc-middleware@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.6.0.tgz#5c9d4c28f745ccb01630f0300ba945f4bef9593f" + integrity sha512-tDVCTlrUvdqHKqivYMjtFZsdD7TtpNLBCfKAcOpaVs7orBMS/A8HWro6dIzNtTZIR05FAbJ3bioFOnZpuCew9Q== + dependencies: + async "^2.5.0" + eth-query "^2.1.2" + eth-tx-summary "^3.1.2" + ethereumjs-block "^1.6.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.2" + ethereumjs-vm "^2.1.0" + fetch-ponyfill "^4.0.0" + json-rpc-engine "^3.6.0" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + tape "^4.6.3" + +eth-lib@0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.7.tgz#2f93f17b1e23aec3759cd4a3fe20c1286a3fc1ca" + integrity sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco= + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.27" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.27.tgz#f0b0fd144f865d2d6bf8257a40004f2e75ca1dd6" + integrity sha512-B8czsfkJYzn2UIEMwjc7Mbj+Cy72V+/OXH/tb44LV8jhrjizQJJ325xMOMyk3+ETa6r6oi0jsUY14+om8mQMWA== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + keccakjs "^0.2.1" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-query@^2.0.2, eth-query@^2.1.0, eth-query@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" + integrity sha1-1nQdkAAQa1FRDHLbktY2VFam2l4= + dependencies: + json-rpc-random-id "^1.0.0" + xtend "^4.0.1" + +eth-sig-util@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-1.4.2.tgz#8d958202c7edbaae839707fba6f09ff327606210" + integrity sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA= + dependencies: + ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git" + ethereumjs-util "^5.1.1" + +eth-tx-summary@^3.1.2: + version "3.2.4" + resolved "https://registry.yarnpkg.com/eth-tx-summary/-/eth-tx-summary-3.2.4.tgz#e10eb95eb57cdfe549bf29f97f1e4f1db679035c" + integrity sha512-NtlDnaVZah146Rm8HMRUNMgIwG/ED4jiqk0TME9zFheMl1jOp6jL1m0NKGjJwehXQ6ZKCPr16MTr+qspKpEXNg== + dependencies: + async "^2.1.2" + clone "^2.0.0" + concat-stream "^1.5.1" + end-of-stream "^1.1.0" + eth-query "^2.0.2" + ethereumjs-block "^1.4.1" + ethereumjs-tx "^1.1.1" + ethereumjs-util "^5.0.1" + ethereumjs-vm "^2.6.0" + through2 "^2.0.3" + +ethereum-bloom-filters@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.6.tgz#9cdebb3ec20de96ec4a434c6bad6ea5a513037aa" + integrity sha512-dE9CGNzgOOsdh7msZirvv8qjHtnHpvBlKe2647kM8v+yeF71IRso55jpojemvHV+jMjr48irPWxMRaHuOWzAFA== + dependencies: + js-sha3 "^0.8.0" + +ethereum-common@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.2.0.tgz#13bf966131cce1eeade62a1b434249bb4cb120ca" + integrity sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA== + +ethereum-common@^0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" + integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= + +ethereum-protocol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ethereum-protocol/-/ethereum-protocol-1.0.1.tgz#b7d68142f4105e0ae7b5e178cf42f8d4dc4b93cf" + integrity sha512-3KLX1mHuEsBW0dKG+c6EOJS1NBNqdCICvZW9sInmZTt5aY0oxmHVggYRE0lJu1tcnMD1K+AKHdLi6U43Awm1Vg== + +"ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git": + version "0.6.8" + resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#1cfbb13862f90f0b391d8a699544d5fe4dfb8c7b" + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-account@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz#eeafc62de544cb07b0ee44b10f572c9c49e00a84" + integrity sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA== + dependencies: + ethereumjs-util "^5.0.0" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-block@^1.2.2, ethereumjs-block@^1.4.1, ethereumjs-block@^1.6.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz#78b88e6cc56de29a6b4884ee75379b6860333c3f" + integrity sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg== + dependencies: + async "^2.0.1" + ethereum-common "0.2.0" + ethereumjs-tx "^1.2.2" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-block@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.0.tgz#8c6c3ab4a5eff0a16d9785fbeedbe643f4dbcbef" + integrity sha512-Ye+uG/L2wrp364Zihdlr/GfC3ft+zG8PdHcRtsBFNNH1CkOhxOwdB8friBU85n89uRZ9eIMAywCq0F4CwT1wAw== + dependencies: + async "^2.0.1" + ethereumjs-common "^1.1.0" + ethereumjs-tx "^1.2.2" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-common@^1.1.0, ethereumjs-common@^1.3.1, ethereumjs-common@^1.3.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.4.0.tgz#a940685f88f3c2587e4061630fe720b089c965b8" + integrity sha512-ser2SAplX/YI5W2AnzU8wmSjKRy4KQd4uxInJ36BzjS3m18E/B9QedPUIresZN1CSEQb/RgNQ2gN7C/XbpTafA== + +ethereumjs-tx@^1.0.0, ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.3: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a" + integrity sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA== + dependencies: + ethereum-common "^0.0.18" + ethereumjs-util "^5.0.0" + +ethereumjs-tx@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.1.tgz#7d204e2b319156c9bc6cec67e9529424a26e8ccc" + integrity sha512-QtVriNqowCFA19X9BCRPMgdVNJ0/gMBS91TQb1DfrhsbR748g4STwxZptFAwfqehMyrF8rDwB23w87PQwru0wA== + dependencies: + ethereumjs-common "^1.3.1" + ethereumjs-util "^6.0.0" + +ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5, ethereumjs-util@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz#3e0c0d1741471acf1036052d048623dee54ad642" + integrity sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA== + dependencies: + bn.js "^4.11.0" + create-hash "^1.1.2" + ethjs-util "^0.1.3" + keccak "^1.0.2" + rlp "^2.0.0" + safe-buffer "^5.1.1" + secp256k1 "^3.0.1" + +ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.0.tgz#23ec79b2488a7d041242f01e25f24e5ad0357960" + integrity sha512-vb0XN9J2QGdZGIEKG2vXM+kUdEivUfU6Wmi5y0cg+LRhDYKnXIZ/Lz7XjFbHRR9VIKq2lVGLzGBkA++y2nOdOQ== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + ethjs-util "0.1.6" + keccak "^2.0.0" + rlp "^2.2.3" + secp256k1 "^3.0.1" + +ethereumjs-vm@^2.1.0, ethereumjs-vm@^2.3.4, ethereumjs-vm@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz#76243ed8de031b408793ac33907fb3407fe400c6" + integrity sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + ethereumjs-account "^2.0.3" + ethereumjs-block "~2.2.0" + ethereumjs-common "^1.1.0" + ethereumjs-util "^6.0.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + +ethereumjs-wallet@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-0.6.3.tgz#b0eae6f327637c2aeb9ccb9047b982ac542e6ab1" + integrity sha512-qiXPiZOsStem+Dj/CQHbn5qex+FVkuPmGH7SvSnA9F3tdRDt8dLMyvIj3+U05QzVZNPYh4HXEdnzoYI4dZkr9w== + dependencies: + aes-js "^3.1.1" + bs58check "^2.1.2" + ethereumjs-util "^6.0.0" + hdkey "^1.1.0" + randombytes "^2.0.6" + safe-buffer "^5.1.2" + scrypt.js "^0.3.0" + utf8 "^3.0.0" + uuid "^3.3.2" + +ethers@4.0.0-beta.3: + version "4.0.0-beta.3" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.0-beta.3.tgz#15bef14e57e94ecbeb7f9b39dd0a4bd435bc9066" + integrity sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog== + dependencies: + "@types/node" "^10.3.2" + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.3.3" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.3" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + +ethers@^4.0.0-beta.1, ethers@^4.0.32: + version "4.0.39" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.39.tgz#5ce9dfffedb03936415743f63b37d96280886a47" + integrity sha512-QVtC8TTUgTrnlQjQvdFJ7fkSWKwp8HVTbKRmrdbVryrPzJHMTf3WSeRNvLF2enGyAFtyHJyFNnjN0fSshcEr9w== + dependencies: + "@types/node" "^10.3.2" + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.3.3" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.4" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk= + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.3: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +eventemitter3@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + +events@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" + integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +express@^4.14.0: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.2.0.tgz#8dd8d2dd21bcced3045be09621fa0cbf73908ba4" + integrity sha512-0ccUQK/9e3NreLFg6K6np8aPyRgwycx+oFGtfx1dSp7Wj00Ozw9r05FgBRlzjf2XBM7LAzwgLyDscRrtSU91hA== + dependencies: + type "^2.0.0" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fake-merkle-patricia-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz#4b8c3acfb520afadf9860b1f14cd8ce3402cddd3" + integrity sha1-S4w6z7Ugr635hgsfFM2M40As3dM= + dependencies: + checkpoint-store "^1.1.0" + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + +fetch-ponyfill@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" + integrity sha1-rjzl9zLGReq4fkroeTQUcJsjmJM= + dependencies: + node-fetch "~1.7.1" + +file-type@^3.8.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" + integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= + +file-type@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" + integrity sha1-LdvqfHP/42No365J3DOMBYwritY= + +file-type@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" + integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +for-each@^0.3.3, for-each@~0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +function-bind@^1.0.2, function-bind@^1.1.1, function-bind@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +get-stream@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" + integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4= + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" + integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== + dependencies: + pump "^3.0.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob@~7.1.4: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@~4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" + integrity sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8= + dependencies: + min-document "^2.19.0" + process "~0.5.1" + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== + +got@9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +got@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.1.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" + integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + +has@^1.0.1, has@^1.0.3, has@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hdkey@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-1.1.1.tgz#c2b3bfd5883ff9529b72f2f08b28be0972a9f64a" + integrity sha512-DvHZ5OuavsfWs5yfVJZestsnc3wzPvLWNk6c2nRUfo6X+OtxypGt20vDDf7Ba+MJzjL3KS1og2nw2eBbLCOUTA== + dependencies: + coinstring "^2.0.0" + safe-buffer "^5.1.1" + secp256k1 "^3.0.1" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg= + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +http-cache-semantics@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz#495704773277eeef6e43f9ab2c2c7d259dda25c5" + integrity sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +iconv-lite@0.4.24, iconv-lite@~0.4.13: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== + dependencies: + punycode "2.1.0" + +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +immediate@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +ipaddr.js@1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" + integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== + +is-callable@^1.1.3, is-callable@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= + dependencies: + number-is-nan "^1.0.0" + +is-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" + integrity sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw= + +is-function@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" + integrity sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU= + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= + +is-natural-number@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" + integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= + +is-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= + dependencies: + has "^1.0.1" + +is-retry-allowed@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + dependencies: + has-symbols "^1.0.0" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-windows@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +js-sha3@0.5.7, js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= + +js-sha3@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.6.1.tgz#5b89f77a7477679877f58c4a075240934b1f95c0" + integrity sha1-W4n3enR3Z5h39YxKB1JAk0sflcA= + +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-rpc-engine@^3.4.0, json-rpc-engine@^3.6.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.8.0.tgz#9d4ff447241792e1d0a232f6ef927302bb0c62a9" + integrity sha512-6QNcvm2gFuuK4TKU1uwfH0Qd/cOSb9c1lls0gbnIhciktIUQJwz6NQNAW4B1KiGPenv7IKu97V222Yo1bNhGuA== + dependencies: + async "^2.0.1" + babel-preset-env "^1.7.0" + babelify "^7.3.0" + json-rpc-error "^2.0.0" + promise-to-callback "^1.0.0" + safe-event-emitter "^1.0.1" + +json-rpc-error@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/json-rpc-error/-/json-rpc-error-2.0.0.tgz#a7af9c202838b5e905c7250e547f1aff77258a02" + integrity sha1-p6+cICg4tekFxyUOVH8a/3cligI= + dependencies: + inherits "^2.0.1" + +json-rpc-random-id@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8" + integrity sha1-uknZat7RRE27jaPSA3SKy7zeyMg= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +keccak@^1.0.2, keccak@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-1.4.0.tgz#572f8a6dbee8e7b3aa421550f9e6408ca2186f80" + integrity sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw== + dependencies: + bindings "^1.2.1" + inherits "^2.0.3" + nan "^2.2.1" + safe-buffer "^5.1.0" + +keccak@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-2.0.0.tgz#7456ea5023284271e6f362b4397e8df4d2bb994c" + integrity sha512-rKe/lRr0KGhjoz97cwg+oeT1Rj/Y4cjae6glArioUC8JBF9ROGZctwIaaruM7d7naovME4Q8WcQSO908A8qcyQ== + dependencies: + bindings "^1.2.1" + inherits "^2.0.3" + nan "^2.2.1" + safe-buffer "^5.1.0" + +keccakjs@^0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/keccakjs/-/keccakjs-0.2.3.tgz#5e4e969ce39689a3861f445d7752ee3477f9fe72" + integrity sha512-BjLkNDcfaZ6l8HBG9tH0tpmDv3sS2mA7FNQxFHpCdzP3Gb2MVruXBSuoM66SnVxKJpAr5dKGdkHD+bDokt8fTg== + dependencies: + browserify-sha3 "^0.0.4" + sha3 "^1.2.2" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +level-codec@~7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" + integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== + +level-errors@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" + integrity sha512-Sw/IJwWbPKF5Ai4Wz60B52yj0zYeqzObLh8k1Tk88jVmD51cJSKWSYpRyhVIvFzZdvsPqlH5wfhp/yxdsaQH4w== + dependencies: + errno "~0.1.1" + +level-errors@~1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.0.5.tgz#83dbfb12f0b8a2516bdc9a31c4876038e227b859" + integrity sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig== + dependencies: + errno "~0.1.1" + +level-iterator-stream@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz#e43b78b1a8143e6fa97a4f485eb8ea530352f2ed" + integrity sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0= + dependencies: + inherits "^2.0.1" + level-errors "^1.0.3" + readable-stream "^1.0.33" + xtend "^4.0.0" + +level-ws@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" + integrity sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos= + dependencies: + readable-stream "~1.0.15" + xtend "~2.1.1" + +levelup@^1.2.1: + version "1.3.9" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-1.3.9.tgz#2dbcae845b2bb2b6bea84df334c475533bbd82ab" + integrity sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ== + dependencies: + deferred-leveldown "~1.2.1" + level-codec "~7.0.0" + level-errors "~1.0.3" + level-iterator-stream "~1.3.0" + prr "~1.0.1" + semver "~5.4.1" + xtend "~4.0.0" + +lodash@^4.17.10, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.4: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +ltgt@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== + dependencies: + pify "^3.0.0" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +memdown@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" + integrity sha1-tOThkhdGZP+65BNhqlAPMRnv4hU= + dependencies: + abstract-leveldown "~2.7.1" + functional-red-black-tree "^1.0.1" + immediate "^3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" + integrity sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g== + dependencies: + async "^1.4.2" + ethereumjs-util "^5.0.0" + level-ws "0.0.0" + levelup "^1.2.1" + memdown "^1.0.0" + readable-stream "^2.0.0" + rlp "^2.0.0" + semaphore ">=1.0.1" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== + +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== + dependencies: + mime-db "1.40.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= + dependencies: + mkdirp "*" + +mkdirp@*, mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +mock-fs@^4.1.0: + version "4.10.3" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.10.3.tgz#d0550663dd2b5d33a7c1b8713c6925aab07a04ae" + integrity sha512-bcukePBvuA3qovmq0Qtqu9+1APCIGkFHnsozrPIVromt5XFGGgkQSfaN0H6RI8gStHkO/hRgimvS3tooNes4pQ== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +nan@2.13.2: + version "2.13.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" + integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw== + +nan@^2.0.8, nan@^2.14.0, nan@^2.2.1: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18= + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +node-fetch@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" + integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U= + +node-fetch@~1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-inspect@^1.6.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" + integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== + +object-inspect@~1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" + integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= + +oboe@2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.4.tgz#20c88cdb0c15371bb04119257d4fdd34b0aa49f6" + integrity sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY= + dependencies: + http-https "^1.0.0" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +openzeppelin-solidity@^2.1.3: + version "2.4.0" + resolved "https://registry.yarnpkg.com/openzeppelin-solidity/-/openzeppelin-solidity-2.4.0.tgz#5f0a7b30571c45493449166e57b947203415349d" + integrity sha512-533gc5jkspxW5YT0qJo02Za5q1LHwXK9CJCc48jNj/22ncNM/3M/3JfWLqfpB90uqLwOKOovpl0JfaMQTR+gXQ== + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= + dependencies: + p-finally "^1.0.0" + +parse-asn1@^5.0.0: + version "5.1.5" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" + integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-headers@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.2.tgz#9545e8a4c1ae5eaea7d24992bca890281ed26e34" + integrity sha512-/LypJhzFmyBIDYP9aDVgeyEb5sQfbfY5mnDq4hVhlQ69js87wXfmEI5V3xI6vvXasqebp0oCytYFLxsBVfCzSg== + dependencies: + for-each "^0.3.3" + string.prototype.trim "^1.1.2" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +pathval@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= + +pbkdf2@^3.0.3, pbkdf2@^3.0.9: + version "3.0.17" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" + integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +private@^0.1.6, private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@~0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" + integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8= + +promise-to-callback@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" + integrity sha1-XSp0kBC/tn2WNZj805YHRqaP7vc= + dependencies: + is-fn "^1.0.0" + set-immediate-shim "^1.0.1" + +proxy-addr@~2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" + integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.0" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.24: + version "1.4.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2" + integrity sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0= + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.0.6, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +randomhex@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/randomhex/-/randomhex-0.1.5.tgz#baceef982329091400f2a2912c6cd02f1094f585" + integrity sha1-us7vmCMpCRQA8qKRLGzQLxCU9YU= + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^1.0.33: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@~1.0.15: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +regenerate@^1.2.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +regenerator-transform@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" + integrity sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q== + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + integrity sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA= + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= + dependencies: + jsesc "~0.5.0" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +request@^2.79.0, request@^2.85.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +resolve@~1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" + integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw== + dependencies: + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +resumer@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" + integrity sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k= + dependencies: + through "~2.3.4" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.0.0, rlp@^2.2.3: + version "2.2.4" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.4.tgz#d6b0e1659e9285fc509a5d169a9bd06f704951c1" + integrity sha512-fdq2yYCWpAQBhwkZv+Z8o/Z4sPmYm1CUq6P7n6lVTOdb949CnqA0sndXal5C1NleSVSZm6q5F3iEbauyVln/iw== + dependencies: + bn.js "^4.11.1" + +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safe-event-emitter@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" + integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== + dependencies: + events "^3.0.0" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +scrypt-js@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4" + integrity sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q= + +scrypt-js@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" + integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== + +"scrypt-shim@github:web3-js/scrypt-shim": + version "0.1.0" + resolved "https://codeload.github.com/web3-js/scrypt-shim/tar.gz/be5e616323a8b5e568788bf94d03c1b8410eac54" + dependencies: + scryptsy "^2.1.0" + semver "^6.3.0" + +scrypt.js@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.3.0.tgz#6c62d61728ad533c8c376a2e5e3e86d41a95c4c0" + integrity sha512-42LTc1nyFsyv/o0gcHtDztrn+aqpkaCNt5Qh7ATBZfhEZU7IC/0oT/qbBH+uRNoAPvs2fwiOId68FDEoSRA8/A== + dependencies: + scryptsy "^1.2.1" + optionalDependencies: + scrypt "^6.0.2" + +scrypt@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/scrypt/-/scrypt-6.0.3.tgz#04e014a5682b53fa50c2d5cce167d719c06d870d" + integrity sha1-BOAUpWgrU/pQwtXM4WfXGcBthw0= + dependencies: + nan "^2.0.8" + +scryptsy@2.1.0, scryptsy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790" + integrity sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w== + +scryptsy@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-1.2.1.tgz#a3225fa4b2524f802700761e2855bdf3b2d92163" + integrity sha1-oyJfpLJST4AnAHYeKFW987LZIWM= + dependencies: + pbkdf2 "^3.0.3" + +secp256k1@^3.0.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.7.1.tgz#12e473e0e9a7c2f2d4d4818e722ad0e14cc1e2f1" + integrity sha512-1cf8sbnRreXrQFdH6qsg2H71Xw91fCCS9Yp021GnUNJzWJS/py96fS4lHbnTnouLp08Xj6jBoBB6V78Tdbdu5g== + dependencies: + bindings "^1.5.0" + bip66 "^1.1.5" + bn.js "^4.11.8" + create-hash "^1.2.0" + drbg.js "^1.0.1" + elliptic "^6.4.1" + nan "^2.14.0" + safe-buffer "^5.1.2" + +seek-bzip@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc" + integrity sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w= + dependencies: + commander "~2.8.1" + +semaphore@>=1.0.1, semaphore@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" + integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== + +semver@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.2.0.tgz#4d813d9590aaf8a9192693d6c85b9344de5901db" + integrity sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A== + +semver@^5.3.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@~5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + +setimmediate@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" + integrity sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48= + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +sha3@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/sha3/-/sha3-1.2.3.tgz#ed5958fa8331df1b1b8529ca9fdf225a340c5418" + integrity sha512-sOWDZi8cDBRkLfWOw18wvJyNblXDHzwMGnRWut8zNNeIeLnmMRO17bjpLc7OzMuj1ASUgx2IyohzUCAl+Kx5vA== + dependencies: + nan "2.13.2" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +simple-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" + integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= + +simple-get@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== + dependencies: + source-map "^0.5.6" + +source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string.prototype.trim@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.0.tgz#75a729b10cfc1be439543dae442129459ce61e3d" + integrity sha512-9EIjYD/WdlvLpn987+ctkLf0FfvBefOCuiEr2henD8X+7jfwPnyvTdmW8OJhj5p+M0/96mBdynLWkxUr+rHlpg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.13.0" + function-bind "^1.1.1" + +string.prototype.trim@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" + integrity sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.0" + function-bind "^1.0.2" + +string.prototype.trimleft@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" + integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimright@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" + integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-dirs@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" + integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== + dependencies: + is-natural-number "^4.0.1" + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= + dependencies: + is-hex-prefixed "1.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +swarm-js@0.1.39: + version "0.1.39" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.39.tgz#79becb07f291d4b2a178c50fee7aa6e10342c0e8" + integrity sha512-QLMqL2rzF6n5s50BptyD6Oi0R1aWlJC5Y17SRIVXRj6OR1DRIPM7nepvrxxkjA1zNzFz6mUOMjfeqeDaWB7OOg== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + decompress "^4.0.0" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request-promise "^0.1.2" + +tape@^4.6.3: + version "4.11.0" + resolved "https://registry.yarnpkg.com/tape/-/tape-4.11.0.tgz#63d41accd95e45a23a874473051c57fdbc58edc1" + integrity sha512-yixvDMX7q7JIs/omJSzSZrqulOV51EC9dK8dM0TzImTIkHWfe2/kFyL5v+d9C+SrCMaICk59ujsqFAVidDqDaA== + dependencies: + deep-equal "~1.0.1" + defined "~1.0.0" + for-each "~0.3.3" + function-bind "~1.1.1" + glob "~7.1.4" + has "~1.0.3" + inherits "~2.0.4" + minimist "~1.2.0" + object-inspect "~1.6.0" + resolve "~1.11.1" + resumer "~0.0.0" + string.prototype.trim "~1.1.2" + through "~2.3.8" + +tar-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" + integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== + dependencies: + bl "^1.0.0" + buffer-alloc "^1.2.0" + end-of-stream "^1.0.0" + fs-constants "^1.0.0" + readable-stream "^2.3.0" + to-buffer "^1.1.1" + xtend "^4.0.0" + +tar@^4.0.2: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +through2@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@^2.3.8, through@~2.3.4, through@~2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + +to-buffer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" + integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== + dependencies: + psl "^1.1.24" + punycode "^1.4.1" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= + +truffle-contract@^4.0.31: + version "4.0.31" + resolved "https://registry.yarnpkg.com/truffle-contract/-/truffle-contract-4.0.31.tgz#e43b7f648e2db352c857d1202d710029b107b68d" + integrity sha512-u3q+p1wiX5C2GpnluGx/d2iaJk7bcWshk2/TohiJyA2iQiTfkS7M4n9D9tY3JqpXR8PmD/TrA69RylO0RhITFA== + dependencies: + "@truffle/blockchain-utils" "^0.0.11" + "@truffle/contract-schema" "^3.0.14" + "@truffle/error" "^0.0.6" + bignumber.js "^7.2.1" + ethers "^4.0.0-beta.1" + truffle-interface-adapter "^0.2.5" + web3 "1.2.1" + web3-core-promievent "1.2.1" + web3-eth-abi "1.2.1" + web3-utils "1.2.1" + +truffle-hdwallet-provider@^1.0.17: + version "1.0.17" + resolved "https://registry.yarnpkg.com/truffle-hdwallet-provider/-/truffle-hdwallet-provider-1.0.17.tgz#fe8edd0d6974eeb31af9959e41525fb19abd74ca" + integrity sha512-s6DvSP83jiIAc6TUcpr7Uqnja1+sLGJ8og3X7n41vfyC4OCaKmBtXL5HOHf+SsU3iblOvnbFDgmN6Y1VBL/fsg== + dependencies: + any-promise "^1.3.0" + bindings "^1.3.1" + web3 "1.2.1" + websocket "^1.0.28" + +truffle-interface-adapter@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/truffle-interface-adapter/-/truffle-interface-adapter-0.2.5.tgz#aa0bee635517b4a8e06adcdc99eacb993e68c243" + integrity sha512-EL39OpP8FcZ99ne1Rno3jImfb92Nectd4iVsZzoEUCBfbwHe7sr0k+i45guoruSoP8nMUE81Mov2s8I5pi6d9Q== + dependencies: + bn.js "^4.11.8" + ethers "^4.0.32" + lodash "^4.17.13" + web3 "1.2.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3" + integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +unbzip2-stream@^1.0.9: + version "1.3.3" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a" + integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg== + dependencies: + buffer "^5.2.1" + through "^2.3.8" + +underscore@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" + integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unorm@^1.3.3: + version "1.6.0" + resolved "https://registry.yarnpkg.com/unorm/-/unorm-1.6.0.tgz#029b289661fba714f1a9af439eb51d9b16c205af" + integrity sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + +utf8@3.0.0, utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" + integrity sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w= + +uuid@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +uuid@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" + integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +web3-bzz@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.1.tgz#c3bd1e8f0c02a13cd6d4e3c3e9e1713f144f6f0d" + integrity sha512-LdOO44TuYbGIPfL4ilkuS89GQovxUpmLz6C1UC7VYVVRILeZS740FVB3j9V4P4FHUk1RenaDfKhcntqgVCHtjw== + dependencies: + got "9.6.0" + swarm-js "0.1.39" + underscore "1.9.1" + +web3-bzz@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.2.tgz#a3b9f613c49fd3e120e0997088a73557d5adb724" + integrity sha512-b1O2ObsqUN1lJxmFSjvnEC4TsaCbmh7Owj3IAIWTKqL9qhVgx7Qsu5O9cD13pBiSPNZJ68uJPaKq380QB4NWeA== + dependencies: + "@types/node" "^10.12.18" + got "9.6.0" + swarm-js "0.1.39" + underscore "1.9.1" + +web3-core-helpers@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz#f5f32d71c60a4a3bd14786118e633ce7ca6d5d0d" + integrity sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw== + dependencies: + underscore "1.9.1" + web3-eth-iban "1.2.1" + web3-utils "1.2.1" + +web3-core-helpers@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.2.tgz#484974f4bd4a487217b85b0d7cfe841af0907619" + integrity sha512-HJrRsIGgZa1jGUIhvGz4S5Yh6wtOIo/TMIsSLe+Xay+KVnbseJpPprDI5W3s7H2ODhMQTbogmmUFquZweW2ImQ== + dependencies: + underscore "1.9.1" + web3-eth-iban "1.2.2" + web3-utils "1.2.2" + +web3-core-method@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.1.tgz#9df1bafa2cd8be9d9937e01c6a47fc768d15d90a" + integrity sha512-Ghg2WS23qi6Xj8Od3VCzaImLHseEA7/usvnOItluiIc5cKs00WYWsNy2YRStzU9a2+z8lwQywPYp0nTzR/QXdQ== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.1" + web3-core-promievent "1.2.1" + web3-core-subscriptions "1.2.1" + web3-utils "1.2.1" + +web3-core-method@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.2.tgz#d4fe2bb1945b7152e5f08e4ea568b171132a1e56" + integrity sha512-szR4fDSBxNHaF1DFqE+j6sFR/afv9Aa36OW93saHZnrh+iXSrYeUUDfugeNcRlugEKeUCkd4CZylfgbK2SKYJA== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.2" + web3-core-promievent "1.2.2" + web3-core-subscriptions "1.2.2" + web3-utils "1.2.2" + +web3-core-promievent@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.1.tgz#003e8a3eb82fb27b6164a6d5b9cad04acf733838" + integrity sha512-IVUqgpIKoeOYblwpex4Hye6npM0aMR+kU49VP06secPeN0rHMyhGF0ZGveWBrGvf8WDPI7jhqPBFIC6Jf3Q3zw== + dependencies: + any-promise "1.3.0" + eventemitter3 "3.1.2" + +web3-core-promievent@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.2.tgz#3b60e3f2a0c96db8a891c927899d29d39e66ab1c" + integrity sha512-tKvYeT8bkUfKABcQswK6/X79blKTKYGk949urZKcLvLDEaWrM3uuzDwdQT3BNKzQ3vIvTggFPX9BwYh0F1WwqQ== + dependencies: + any-promise "1.3.0" + eventemitter3 "3.1.2" + +web3-core-requestmanager@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.1.tgz#fa2e2206c3d738db38db7c8fe9c107006f5c6e3d" + integrity sha512-xfknTC69RfYmLKC+83Jz73IC3/sS2ZLhGtX33D4Q5nQ8yc39ElyAolxr9sJQS8kihOcM6u4J+8gyGMqsLcpIBg== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.1" + web3-providers-http "1.2.1" + web3-providers-ipc "1.2.1" + web3-providers-ws "1.2.1" + +web3-core-requestmanager@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.2.tgz#667ba9ac724c9c76fa8965ae8a3c61f66e68d8d6" + integrity sha512-a+gSbiBRHtHvkp78U2bsntMGYGF2eCb6219aMufuZWeAZGXJ63Wc2321PCbA8hF9cQrZI4EoZ4kVLRI4OF15Hw== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.2" + web3-providers-http "1.2.2" + web3-providers-ipc "1.2.2" + web3-providers-ws "1.2.2" + +web3-core-subscriptions@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.1.tgz#8c2368a839d4eec1c01a4b5650bbeb82d0e4a099" + integrity sha512-nmOwe3NsB8V8UFsY1r+sW6KjdOS68h8nuh7NzlWxBQT/19QSUGiERRTaZXWu5BYvo1EoZRMxCKyCQpSSXLc08g== + dependencies: + eventemitter3 "3.1.2" + underscore "1.9.1" + web3-core-helpers "1.2.1" + +web3-core-subscriptions@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.2.tgz#bf4ba23a653a003bdc3551649958cc0b080b068e" + integrity sha512-QbTgigNuT4eicAWWr7ahVpJyM8GbICsR1Ys9mJqzBEwpqS+RXTRVSkwZ2IsxO+iqv6liMNwGregbJLq4urMFcQ== + dependencies: + eventemitter3 "3.1.2" + underscore "1.9.1" + web3-core-helpers "1.2.2" + +web3-core@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.1.tgz#7278b58fb6495065e73a77efbbce781a7fddf1a9" + integrity sha512-5ODwIqgl8oIg/0+Ai4jsLxkKFWJYE0uLuE1yUKHNVCL4zL6n3rFjRMpKPokd6id6nJCNgeA64KdWQ4XfpnjdMg== + dependencies: + web3-core-helpers "1.2.1" + web3-core-method "1.2.1" + web3-core-requestmanager "1.2.1" + web3-utils "1.2.1" + +web3-core@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.2.tgz#334b99c8222ef9cfd0339e27352f0b58ea789a2f" + integrity sha512-miHAX3qUgxV+KYfaOY93Hlc3kLW2j5fH8FJy6kSxAv+d4d5aH0wwrU2IIoJylQdT+FeenQ38sgsCnFu9iZ1hCQ== + dependencies: + "@types/bn.js" "^4.11.4" + "@types/node" "^12.6.1" + web3-core-helpers "1.2.2" + web3-core-method "1.2.2" + web3-core-requestmanager "1.2.2" + web3-utils "1.2.2" + +web3-eth-abi@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.1.tgz#9b915b1c9ebf82f70cca631147035d5419064689" + integrity sha512-jI/KhU2a/DQPZXHjo2GW0myEljzfiKOn+h1qxK1+Y9OQfTcBMxrQJyH5AP89O6l6NZ1QvNdq99ThAxBFoy5L+g== + dependencies: + ethers "4.0.0-beta.3" + underscore "1.9.1" + web3-utils "1.2.1" + +web3-eth-abi@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.2.tgz#d5616d88a90020f894763423a9769f2da11fe37a" + integrity sha512-Yn/ZMgoOLxhTVxIYtPJ0eS6pnAnkTAaJgUJh1JhZS4ekzgswMfEYXOwpMaD5eiqPJLpuxmZFnXnBZlnQ1JMXsw== + dependencies: + ethers "4.0.0-beta.3" + underscore "1.9.1" + web3-utils "1.2.2" + +web3-eth-accounts@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.1.tgz#2741a8ef337a7219d57959ac8bd118b9d68d63cf" + integrity sha512-26I4qq42STQ8IeKUyur3MdQ1NzrzCqPsmzqpux0j6X/XBD7EjZ+Cs0lhGNkSKH5dI3V8CJasnQ5T1mNKeWB7nQ== + dependencies: + any-promise "1.3.0" + crypto-browserify "3.12.0" + eth-lib "0.2.7" + scryptsy "2.1.0" + semver "6.2.0" + underscore "1.9.1" + uuid "3.3.2" + web3-core "1.2.1" + web3-core-helpers "1.2.1" + web3-core-method "1.2.1" + web3-utils "1.2.1" + +web3-eth-accounts@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.2.tgz#c187e14bff6baa698ac352220290222dbfd332e5" + integrity sha512-KzHOEyXOEZ13ZOkWN3skZKqSo5f4Z1ogPFNn9uZbKCz+kSp+gCAEKxyfbOsB/JMAp5h7o7pb6eYsPCUBJmFFiA== + dependencies: + any-promise "1.3.0" + crypto-browserify "3.12.0" + eth-lib "0.2.7" + ethereumjs-common "^1.3.2" + ethereumjs-tx "^2.1.1" + scrypt-shim "github:web3-js/scrypt-shim" + underscore "1.9.1" + uuid "3.3.2" + web3-core "1.2.2" + web3-core-helpers "1.2.2" + web3-core-method "1.2.2" + web3-utils "1.2.2" + +web3-eth-contract@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.1.tgz#3542424f3d341386fd9ff65e78060b85ac0ea8c4" + integrity sha512-kYFESbQ3boC9bl2rYVghj7O8UKMiuKaiMkxvRH5cEDHil8V7MGEGZNH0slSdoyeftZVlaWSMqkRP/chfnKND0g== + dependencies: + underscore "1.9.1" + web3-core "1.2.1" + web3-core-helpers "1.2.1" + web3-core-method "1.2.1" + web3-core-promievent "1.2.1" + web3-core-subscriptions "1.2.1" + web3-eth-abi "1.2.1" + web3-utils "1.2.1" + +web3-eth-contract@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.2.tgz#84e92714918a29e1028ee7718f0712536e14e9a1" + integrity sha512-EKT2yVFws3FEdotDQoNsXTYL798+ogJqR2//CaGwx3p0/RvQIgfzEwp8nbgA6dMxCsn9KOQi7OtklzpnJMkjtA== + dependencies: + "@types/bn.js" "^4.11.4" + underscore "1.9.1" + web3-core "1.2.2" + web3-core-helpers "1.2.2" + web3-core-method "1.2.2" + web3-core-promievent "1.2.2" + web3-core-subscriptions "1.2.2" + web3-eth-abi "1.2.2" + web3-utils "1.2.2" + +web3-eth-ens@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.1.tgz#a0e52eee68c42a8b9865ceb04e5fb022c2d971d5" + integrity sha512-lhP1kFhqZr2nnbu3CGIFFrAnNxk2veXpOXBY48Tub37RtobDyHijHgrj+xTh+mFiPokyrapVjpFsbGa+Xzye4Q== + dependencies: + eth-ens-namehash "2.0.8" + underscore "1.9.1" + web3-core "1.2.1" + web3-core-helpers "1.2.1" + web3-core-promievent "1.2.1" + web3-eth-abi "1.2.1" + web3-eth-contract "1.2.1" + web3-utils "1.2.1" + +web3-eth-ens@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.2.tgz#0a4abed1d4cbdacbf5e1ab06e502d806d1192bc6" + integrity sha512-CFjkr2HnuyMoMFBoNUWojyguD4Ef+NkyovcnUc/iAb9GP4LHohKrODG4pl76R5u61TkJGobC2ij6TyibtsyVYg== + dependencies: + eth-ens-namehash "2.0.8" + underscore "1.9.1" + web3-core "1.2.2" + web3-core-helpers "1.2.2" + web3-core-promievent "1.2.2" + web3-eth-abi "1.2.2" + web3-eth-contract "1.2.2" + web3-utils "1.2.2" + +web3-eth-iban@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz#2c3801718946bea24e9296993a975c80b5acf880" + integrity sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ== + dependencies: + bn.js "4.11.8" + web3-utils "1.2.1" + +web3-eth-iban@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.2.tgz#76bec73bad214df7c4192388979a59fc98b96c5a" + integrity sha512-gxKXBoUhaTFHr0vJB/5sd4i8ejF/7gIsbM/VvemHT3tF5smnmY6hcwSMmn7sl5Gs+83XVb/BngnnGkf+I/rsrQ== + dependencies: + bn.js "4.11.8" + web3-utils "1.2.2" + +web3-eth-personal@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.1.tgz#244e9911b7b482dc17c02f23a061a627c6e47faf" + integrity sha512-RNDVSiaSoY4aIp8+Hc7z+X72H7lMb3fmAChuSBADoEc7DsJrY/d0R5qQDK9g9t2BO8oxgLrLNyBP/9ub2Hc6Bg== + dependencies: + web3-core "1.2.1" + web3-core-helpers "1.2.1" + web3-core-method "1.2.1" + web3-net "1.2.1" + web3-utils "1.2.1" + +web3-eth-personal@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.2.tgz#eee1c86a8132fa16b5e34c6d421ca92e684f0be6" + integrity sha512-4w+GLvTlFqW3+q4xDUXvCEMU7kRZ+xm/iJC8gm1Li1nXxwwFbs+Y+KBK6ZYtoN1qqAnHR+plYpIoVo27ixI5Rg== + dependencies: + "@types/node" "^12.6.1" + web3-core "1.2.2" + web3-core-helpers "1.2.2" + web3-core-method "1.2.2" + web3-net "1.2.2" + web3-utils "1.2.2" + +web3-eth@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.1.tgz#b9989e2557c73a9e8ffdc107c6dafbe72c79c1b0" + integrity sha512-/2xly4Yry5FW1i+uygPjhfvgUP/MS/Dk+PDqmzp5M88tS86A+j8BzKc23GrlA8sgGs0645cpZK/999LpEF5UdA== + dependencies: + underscore "1.9.1" + web3-core "1.2.1" + web3-core-helpers "1.2.1" + web3-core-method "1.2.1" + web3-core-subscriptions "1.2.1" + web3-eth-abi "1.2.1" + web3-eth-accounts "1.2.1" + web3-eth-contract "1.2.1" + web3-eth-ens "1.2.1" + web3-eth-iban "1.2.1" + web3-eth-personal "1.2.1" + web3-net "1.2.1" + web3-utils "1.2.1" + +web3-eth@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.2.tgz#65a1564634a23b990efd1655bf94ad513904286c" + integrity sha512-UXpC74mBQvZzd4b+baD4Ocp7g+BlwxhBHumy9seyE/LMIcMlePXwCKzxve9yReNpjaU16Mmyya6ZYlyiKKV8UA== + dependencies: + underscore "1.9.1" + web3-core "1.2.2" + web3-core-helpers "1.2.2" + web3-core-method "1.2.2" + web3-core-subscriptions "1.2.2" + web3-eth-abi "1.2.2" + web3-eth-accounts "1.2.2" + web3-eth-contract "1.2.2" + web3-eth-ens "1.2.2" + web3-eth-iban "1.2.2" + web3-eth-personal "1.2.2" + web3-net "1.2.2" + web3-utils "1.2.2" + +web3-net@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.1.tgz#edd249503315dd5ab4fa00220f6509d95bb7ab10" + integrity sha512-Yt1Bs7WgnLESPe0rri/ZoPWzSy55ovioaP35w1KZydrNtQ5Yq4WcrAdhBzcOW7vAkIwrsLQsvA+hrOCy7mNauw== + dependencies: + web3-core "1.2.1" + web3-core-method "1.2.1" + web3-utils "1.2.1" + +web3-net@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.2.tgz#5c3226ca72df7c591422440ce6f1203fd42ddad9" + integrity sha512-K07j2DXq0x4UOJgae65rWZKraOznhk8v5EGSTdFqASTx7vWE/m+NqBijBYGEsQY1lSMlVaAY9UEQlcXK5HzXTw== + dependencies: + web3-core "1.2.2" + web3-core-method "1.2.2" + web3-utils "1.2.2" + +"web3-provider-engine@https://github.com/trufflesuite/provider-engine#web3-one": + version "14.0.6" + resolved "https://github.com/trufflesuite/provider-engine#3538c60bc4836b73ccae1ac3f64c8fed8ef19c1a" + dependencies: + async "^2.5.0" + backoff "^2.5.0" + clone "^2.0.0" + cross-fetch "^2.1.0" + eth-block-tracker "^3.0.0" + eth-json-rpc-infura "^3.1.0" + eth-sig-util "^1.4.2" + ethereumjs-block "^1.2.2" + ethereumjs-tx "^1.2.0" + ethereumjs-util "^5.1.5" + ethereumjs-vm "^2.3.4" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.85.0" + semaphore "^1.0.3" + ws "^5.1.1" + xhr "^2.2.0" + xtend "^4.0.1" + +web3-providers-http@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.1.tgz#c93ea003a42e7b894556f7e19dd3540f947f5013" + integrity sha512-BDtVUVolT9b3CAzeGVA/np1hhn7RPUZ6YYGB/sYky+GjeO311Yoq8SRDUSezU92x8yImSC2B+SMReGhd1zL+bQ== + dependencies: + web3-core-helpers "1.2.1" + xhr2-cookies "1.1.0" + +web3-providers-http@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.2.tgz#155e55c1d69f4c5cc0b411ede40dea3d06720956" + integrity sha512-BNZ7Hguy3eBszsarH5gqr9SIZNvqk9eKwqwmGH1LQS1FL3NdoOn7tgPPdddrXec4fL94CwgNk4rCU+OjjZRNDg== + dependencies: + web3-core-helpers "1.2.2" + xhr2-cookies "1.1.0" + +web3-providers-ipc@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.1.tgz#017bfc687a8fc5398df2241eb98f135e3edd672c" + integrity sha512-oPEuOCwxVx8L4CPD0TUdnlOUZwGBSRKScCz/Ws2YHdr9Ium+whm+0NLmOZjkjQp5wovQbyBzNa6zJz1noFRvFA== + dependencies: + oboe "2.1.4" + underscore "1.9.1" + web3-core-helpers "1.2.1" + +web3-providers-ipc@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.2.tgz#c6d165a12bc68674b4cdd543ea18aec79cafc2e8" + integrity sha512-t97w3zi5Kn/LEWGA6D9qxoO0LBOG+lK2FjlEdCwDQatffB/+vYrzZ/CLYVQSoyFZAlsDoBasVoYSWZK1n39aHA== + dependencies: + oboe "2.1.4" + underscore "1.9.1" + web3-core-helpers "1.2.2" + +web3-providers-ws@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.1.tgz#2d941eaf3d5a8caa3214eff8dc16d96252b842cb" + integrity sha512-oqsQXzu+ejJACVHy864WwIyw+oB21nw/pI65/sD95Zi98+/HQzFfNcIFneF1NC4bVF3VNX4YHTNq2I2o97LAiA== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.1" + websocket "github:web3-js/WebSocket-Node#polyfill/globalThis" + +web3-providers-ws@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.2.tgz#d2c05c68598cea5ad3fa6ef076c3bcb3ca300d29" + integrity sha512-Wb1mrWTGMTXOpJkL0yGvL/WYLt8fUIXx8k/l52QB2IiKzvyd42dTWn4+j8IKXGSYYzOm7NMqv6nhA5VDk12VfA== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.2" + websocket "github:web3-js/WebSocket-Node#polyfill/globalThis" + +web3-shh@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.1.tgz#4460e3c1e07faf73ddec24ccd00da46f89152b0c" + integrity sha512-/3Cl04nza5kuFn25bV3FJWa0s3Vafr5BlT933h26xovQ6HIIz61LmvNQlvX1AhFL+SNJOTcQmK1SM59vcyC8bA== + dependencies: + web3-core "1.2.1" + web3-core-method "1.2.1" + web3-core-subscriptions "1.2.1" + web3-net "1.2.1" + +web3-shh@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.2.tgz#44ed998f2a6ba0ec5cb9d455184a0f647826a49c" + integrity sha512-og258NPhlBn8yYrDWjoWBBb6zo1OlBgoWGT+LL5/LPqRbjPe09hlOYHgscAAr9zZGtohTOty7RrxYw6Z6oDWCg== + dependencies: + web3-core "1.2.2" + web3-core-method "1.2.2" + web3-core-subscriptions "1.2.2" + web3-net "1.2.2" + +web3-utils@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.1.tgz#21466e38291551de0ab34558de21512ac4274534" + integrity sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA== + dependencies: + bn.js "4.11.8" + eth-lib "0.2.7" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randomhex "0.1.5" + underscore "1.9.1" + utf8 "3.0.0" + +web3-utils@1.2.2, web3-utils@^1.0.0-beta.52: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.2.tgz#b53a08c40d2c3f31d3c4a28e7d749405df99c8c0" + integrity sha512-joF+s3243TY5cL7Z7y4h1JsJpUCf/kmFmj+eJar7Y2yNIGVcW961VyrAms75tjUysSuHaUQ3eQXjBEUJueT52A== + dependencies: + bn.js "4.11.8" + eth-lib "0.2.7" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.9.1" + utf8 "3.0.0" + +web3@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.1.tgz#5d8158bcca47838ab8c2b784a2dee4c3ceb4179b" + integrity sha512-nNMzeCK0agb5i/oTWNdQ1aGtwYfXzHottFP2Dz0oGIzavPMGSKyVlr8ibVb1yK5sJBjrWVnTdGaOC2zKDFuFRw== + dependencies: + web3-bzz "1.2.1" + web3-core "1.2.1" + web3-eth "1.2.1" + web3-eth-personal "1.2.1" + web3-net "1.2.1" + web3-shh "1.2.1" + web3-utils "1.2.1" + +web3@1.2.2, web3@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.2.tgz#b1b8b69aafdf94cbaeadbb68a8aa1df2ef266aec" + integrity sha512-/ChbmB6qZpfGx6eNpczt5YSUBHEA5V2+iUCbn85EVb3Zv6FVxrOo5Tv7Lw0gE2tW7EEjASbCyp3mZeiZaCCngg== + dependencies: + "@types/node" "^12.6.1" + web3-bzz "1.2.2" + web3-core "1.2.2" + web3-eth "1.2.2" + web3-eth-personal "1.2.2" + web3-net "1.2.2" + web3-shh "1.2.2" + web3-utils "1.2.2" + +websocket@^1.0.28: + version "1.0.30" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.30.tgz#91d3bd00c3d43e916f0cf962f8f8c451bb0b2373" + integrity sha512-aO6klgaTdSMkhfl5VVJzD5fm+Srhh5jLYbS15+OiI1sN6h/RU/XW6WN9J1uVIpUKNmsTvT3Hs35XAFjn9NMfOw== + dependencies: + debug "^2.2.0" + nan "^2.14.0" + typedarray-to-buffer "^3.1.5" + yaeti "^0.0.6" + +"websocket@github:web3-js/WebSocket-Node#polyfill/globalThis": + version "1.0.29" + resolved "https://codeload.github.com/web3-js/WebSocket-Node/tar.gz/905deb4812572b344f5801f8c9ce8bb02799d82e" + dependencies: + debug "^2.2.0" + es5-ext "^0.10.50" + nan "^2.14.0" + typedarray-to-buffer "^3.1.5" + yaeti "^0.0.6" + +whatwg-fetch@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@^5.1.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + +xhr-request-promise@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.2.tgz#343c44d1ee7726b8648069682d0f840c83b4261d" + integrity sha1-NDxE0e53JrhkgGloLQ+EDIO0Jh0= + dependencies: + xhr-request "^1.0.1" + +xhr-request@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr2-cookies@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" + integrity sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg= + dependencies: + cookiejar "^2.1.1" + +xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: + version "2.5.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.5.0.tgz#bed8d1676d5ca36108667692b74b316c496e49dd" + integrity sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ== + dependencies: + global "~4.3.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xmlhttprequest@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= + +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +xtend@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= + dependencies: + object-keys "~0.4.0" + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^3.0.0, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yauzl@^2.4.2: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0"