From d56d4eba8aa617c678d1da8d97e6abcb29432700 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 6 Jul 2018 15:01:41 +0300 Subject: [PATCH 01/78] sentry node architecture docs --- docs/validators.rst | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/docs/validators.rst b/docs/validators.rst index 7c2b4b063..ff04c2202 100755 --- a/docs/validators.rst +++ b/docs/validators.rst @@ -129,3 +129,52 @@ Becoming validator in testnet DDOS protection. Sentry node architecture ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Denial-of-service attacks occur when an attacker sends a flood of internet traffic to an IP +address to prevent the server at the IP address from connecting to the internet. + +An attacker scans the network, tries to learn the IP address of various validator +nodes and disconnect them from communication by flooding them with traffic. + +One recommended way to mitigate these risks is for validators to carefully +structure their network topology in a so-called sentry node architecture. + +Validator nodes should only connect to full-nodes they trust because they +operate them themselves or are run by other validators they know socially. +A validator node will typically run in a data center. Most data centers provide +direct links the networks of major cloud providers. The validator can use +those links to connect to sentry nodes in the cloud. This shifts the burden +of denial-of-service from the validator's node directly to its sentry nodes, +and may require new sentry nodes be spun up or activated to mitigate attacks +on existing ones. + +Sentry nodes can be quickly spun up or change their IP addresses. Because +the links to the sentry nodes are in private IP space, an internet based +attacked cannot disturb them directly. This will ensure validator block +proposals and votes always make it to the rest of the network. + +It is expected that good operating procedures on that part of validators will +completely mitigate these threats. + +Practical instructions +---------------------- + +To setup your sentry node architecture you can follow the instructions below: + +Validators nodes should edit their ``config.toml``: + +:: + + # Comma separated list of nodes to keep persistent connections to + # Do not add private peers to this list if you don't want them advertised + persistent_peers =[list of sentry nodes] + + # Set true to enable the peer-exchange reactor + pex = false + +Sentry Nodes should edit their ``config.toml``: + +:: + + # Comma separated list of peer IDs to keep private (will not be gossiped to other peers) + private_peer_ids = "ipaddress of validator nodes" From e476775a29742de507dd84cd19f88e06b0ed0b94 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 6 Jul 2018 15:05:25 +0300 Subject: [PATCH 02/78] add block rewards info --- docs/validators.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/validators.rst b/docs/validators.rst index ff04c2202..ac6b3c8f5 100755 --- a/docs/validators.rst +++ b/docs/validators.rst @@ -49,6 +49,8 @@ Rewards Rewards for blocks and commissions are accumulated and proportionally (based on stake value) payed once per ``12 blocks`` (approx 1 minute) to all active validators (and their delegators). +Block rewards are configured to decrease from 111 to 0 BIP (MNT) in 7 years. + Delegators receive their rewards at the same time after paying commission to their validators (commission value is based on validator's settings). From 63e7539fbc0283ae89ca8d54ef09f12f8686ae4e Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 6 Jul 2018 15:09:41 +0300 Subject: [PATCH 03/78] describe pips better --- docs/coins.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/coins.rst b/docs/coins.rst index c54a3ebfd..d3badd878 100755 --- a/docs/coins.rst +++ b/docs/coins.rst @@ -6,9 +6,16 @@ Minter Blockchain is multi-coin system. | Base coin in testnet is ``MNT``. | Base coin in mainnet is ``BIP``. -| Smallest part of a coin is called ``pip``. +| Smallest part of *each* coin is called ``pip``. | 1 pip = 1^-18 of any coin. In Blockchain and API we only operating with pips. +**Note:** +Each coin has its **own** pip. You should treat pip like atomic part of a coin, not as currency. + +| 1 MNT = 10^18 pip (MNT's pip) +| 1 ABC = 10^18 pip (ABC's pip) +| 1 MNT != 1 ABC + Coin Issuance ^^^^^^^^^^^^^ From 5e517a92673c82c3b5645bad8c846cb63dd7d6c0 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Tue, 17 Jul 2018 13:24:27 +0300 Subject: [PATCH 04/78] divide commissions by 10 --- core/commissions/commissions.go | 5 ++--- core/transaction/executor.go | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/commissions/commissions.go b/core/commissions/commissions.go index 13ce845ff..429cfd5b2 100644 --- a/core/commissions/commissions.go +++ b/core/commissions/commissions.go @@ -1,7 +1,7 @@ package commissions -// all commissions are divided by 10^15 -// actual commission is SendTx * 10^15 = 1 000 000 000 000 000 PIP = 0,001 BIP +// all commissions are divided by 10^14 +// actual commission is SendTx * 10^14 = 10 000 000 000 000 000 PIP = 0,01 BIP const ( SendTx int64 = 10 ConvertTx int64 = 1000 @@ -9,7 +9,6 @@ const ( DeclareCandidacyTx int64 = 10000 DelegateTx int64 = 100 UnbondTx int64 = 100 - RedeemCheckTx int64 = 10 PayloadByte int64 = 2 ToggleCandidateStatus int64 = 100 ) diff --git a/core/transaction/executor.go b/core/transaction/executor.go index b6b81b6d3..16f99a026 100644 --- a/core/transaction/executor.go +++ b/core/transaction/executor.go @@ -10,7 +10,7 @@ import ( ) var ( - CommissionMultiplier = big.NewInt(10e15) + CommissionMultiplier = big.NewInt(10e14) ) const ( From ff774cbc8e30921cf95f93ffc7992c406cb0c362 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Tue, 17 Jul 2018 16:58:42 +0300 Subject: [PATCH 05/78] update delegators and validators docs --- docs/delegator-faq.rst | 77 ++++++++++++++++++++++++++++++++++++++++-- docs/validators.rst | 29 ++++++++++------ 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/docs/delegator-faq.rst b/docs/delegator-faq.rst index ddeec27e1..de64a6e32 100755 --- a/docs/delegator-faq.rst +++ b/docs/delegator-faq.rst @@ -4,17 +4,90 @@ Delegator FAQ What is a delegator? ^^^^^^^^^^^^^^^^^^^^ -Choosing a validator -^^^^^^^^^^^^^^^^^^^^ +People that cannot, or do not want to run validator operations, can still participate in +the staking process as delegators. Indeed, validators are not chosen based on their own +stake but based on their total stake, which is the sum of their own stake and of the stake +that is delegated to them. This is an important property, as it makes delegators a +safeguard against validators that exhibit bad behavior. If a validator misbehaves, its +delegators will move their staked coins away from it, thereby reducing its stake. Eventually, +if a validator's stake falls under the top addresses with highest stake, it will exit the +validator set. + +Delegators share the revenue of their validators, but they also share the risks. In terms +of revenue, validators and delegators differ in that validators can apply a commission on +the revenue that goes to their delegator before it is distributed. This commission is +known to delegators beforehand and cannot be changed. In terms of risk, delegators' coins +can be slashed if their validator misbehaves. For more, see Risks section. + +To become delegators, coin holders need to send a "Delegate transaction" where they specify +how many coins they want to bond and to which validator. Later, if a delegator wants to +unbond part or all of its stake, it needs to send an "Unbond transaction". From there, the +delegator will have to wait 30 days to retrieve its coins. Directives of delegators ^^^^^^^^^^^^^^^^^^^^^^^^ +Being a delegator is not a passive task. Here are the main directives of a delegator: + +- Perform careful due diligence on validators before delegating. If a validator misbehaves, + part of its total stake, which includes the stake of its delegators, can be slashed. Delegators + should therefore carefully select validators they think will behave correctly. + +- Actively monitor their validator after having delegated. Delegators should ensure that the + validators they're delegating to behaves correctly, meaning that they have good uptime, do not + get hacked and participate in governance. If a delegator is not satisfied with its validator, + it can unbond and switch to another validator. + Revenue ^^^^^^^ +Validators and delegators earn revenue in exchange for their services. This revenue is given in three forms: + +- Block rewards +- Transaction fees: Each transaction on the Minter Network comes with transactions fees. Fees are distributed to + validators and delegators in proportion to their stake. + Validator's commission ^^^^^^^^^^^^^^^^^^^^^^ +Each validator's staking pool receives revenue in proportion to its total stake. However, before this revenue is +distributed to delegators inside the staking pool, the validator can apply a commission. In other words, delegators +have to pay a commission to their validators on the revenue they earn. + +``5%`` from reward going to DAO account. + +Lets consider a validator whose stake (i.e. self-bonded stake + delegated stake) is 10% of the total stake of all +validators. This validator has 20% self-bonded stake and applies a commission of 10%. Now let us consider a block +with the following revenue: + +- 111 Bips as block reward +- 10 Bips as transaction fees. +This amounts to a total of 121 Bips to be distributed among all staking pools. + +Our validator's staking pool represents 10% of the total stake, which means the pool obtains 12.1 bips. Now let us +look at the internal distribution of revenue: + +- Commission = 10% * 80% * 12.1 bips = 0.69696 bips +- Validator's revenue = 20% * 12.1 bips + Commission = 3.11696 bips +- Delegators' total revenue = 80% * 12.1 bips - Commission = 8.98304 bips + +Then, each delegator in the staking pool can claim its portion of the delegators' total revenue. + Risks ^^^^^ + +Staking coins is not free of risk. First, staked coins are locked up, and retrieving them requires a 30 days waiting +period called unbonding period. Additionally, if a validator misbehaves, a portion of its total stake can be slashed +(i.e. destroyed). This includes the stake of their delegators. + +There are 2 main slashing conditions: + +- **Double signing**: If someone reports on chain A that a validator signed two blocks at the same height on chain + A and chain B, this validator will get slashed on chain A +- **Unavailability**: If a validator's signature has not been included in the last 12 blocks, + 1% of stake will get slashed and validator will be turned off + +This is why delegators should perform careful due diligence on validators before delegating. It is also important +that delegators actively monitor the activity of their validators. If a validator behaves suspiciously or is too +often offline, delegators can choose to unbond from it or switch to another validator. Delegators can also mitigate +risk by distributing their stake across multiple validators. diff --git a/docs/validators.rst b/docs/validators.rst index ac6b3c8f5..9429eb63e 100755 --- a/docs/validators.rst +++ b/docs/validators.rst @@ -23,9 +23,9 @@ Requirements Minimal requirements for running Validator's Node are: -- 2GB RAM -- 100GB of disk space -- 1.4 GHz 2v CPU +- 4GB RAM +- 200GB SSD +- x64 2.0 GHz 4 vCPUs SSD disks are preferable for high transaction throughput. @@ -33,7 +33,8 @@ Recommended: - 4GB RAM - 200GB SSD -- x64 2.0 GHz 4v CPU +- x64 3.4 GHz 8 vCPUs +- HSM Validators limitations ^^^^^^^^^^^^^^^^^^^^^^ @@ -71,7 +72,7 @@ of funds for a validator and its delegators: - **Double signing**: If someone reports on chain A that a validator signed two blocks at the same height on chain A and chain B, this validator will get slashed on chain A -- **Unavailability**: If a validator's signature has not been included in the last X blocks, +- **Unavailability**: If a validator's signature has not been included in the last 12 blocks, 1% of stake will get slashed and validator will be turned off Note that even if a validator does not intentionally misbehave, it can still be slashed if its node crashes, @@ -92,11 +93,6 @@ Becoming validator in testnet 4. Go to `Vault `__ and send 2 transactions: Fill and send ``Declare candidacy`` and ``Set candidate online`` forms. - If you cannot open Vault because of invalid certificate: - `reset HSTS `__ for domains - ``minter.network`` and ``vault.minter.network``. Then try to open - `HTTP version of Vault `__. - P.S. You can receive testnet coins in our telegram wallet @BipWallet_Bot. 4.1. Declare candidacy @@ -169,14 +165,25 @@ Validators nodes should edit their ``config.toml``: # Comma separated list of nodes to keep persistent connections to # Do not add private peers to this list if you don't want them advertised - persistent_peers =[list of sentry nodes] + persistent_peers = [list of sentry nodes] # Set true to enable the peer-exchange reactor pex = false + # Disable transaction indexer for better performance + indexer = "null" + index_all_tags = false + Sentry Nodes should edit their ``config.toml``: :: # Comma separated list of peer IDs to keep private (will not be gossiped to other peers) private_peer_ids = "ipaddress of validator nodes" + + +Also you can disable Minter API on Validator node to improve performance: + +:: + + minter --disable-api From 5e979b2bb145774a4c7103e39285c86cd6132a38 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Tue, 17 Jul 2018 17:04:41 +0300 Subject: [PATCH 06/78] 0.1x transaction fees, 10x default send/recv rate --- CHANGELOG.md | 11 +++++++++++ Gopkg.lock | 3 ++- core/transaction/executor.go | 2 +- networks/testnet/config.toml | 4 ++-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2d317443..84b5bcee9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.1.0 +*Jule 17th, 2018* + +BREAKING CHANGES + +- [core] 0.1x transaction fees + +IMPROVEMENT + +- [config] 10x default send/recv rate + ## 0.0.6 *Jule 16th, 2018* diff --git a/Gopkg.lock b/Gopkg.lock index 6bf7d4c34..21896f42e 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -179,6 +179,7 @@ revision = "40f013a808ec4fa79def444a1a56de4d1727efcb" [[projects]] + branch = "master" name = "github.com/rcrowley/go-metrics" packages = ["."] revision = "e2704e165165ec55d062f5919b4b29494e9fa790" @@ -376,6 +377,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "fe1d4dfba2b86ce6861297f5a51daef78af4bd3489b827d917f19b8f0986e66c" + inputs-digest = "1c35f22aa0442b823988c777d05af72e5ad79329c000833560561029387eda7e" solver-name = "gps-cdcl" solver-version = 1 diff --git a/core/transaction/executor.go b/core/transaction/executor.go index b6b81b6d3..16f99a026 100644 --- a/core/transaction/executor.go +++ b/core/transaction/executor.go @@ -10,7 +10,7 @@ import ( ) var ( - CommissionMultiplier = big.NewInt(10e15) + CommissionMultiplier = big.NewInt(10e14) ) const ( diff --git a/networks/testnet/config.toml b/networks/testnet/config.toml index 8026d56a9..4625a0f73 100644 --- a/networks/testnet/config.toml +++ b/networks/testnet/config.toml @@ -88,10 +88,10 @@ max_num_peers = 50 max_msg_packet_payload_size = 2048 # Rate at which packets can be sent, in bytes/second -send_rate = 1024000 +send_rate = 5120000 # Rate at which packets can be received, in bytes/second -recv_rate = 1024000 +recv_rate = 5120000 # Set true to enable the peer-exchange reactor pex = true From 839a6825191a57413bf1bc864b8c8d8c3e98dd73 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Tue, 17 Jul 2018 17:07:04 +0300 Subject: [PATCH 07/78] update version --- docker-compose.yml | 2 +- networks/testnet/genesis.json | 2 +- version/version.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 179da8cda..f59e46890 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.4" services: minter: - image: minterteam/minter:0.0.6 + image: minterteam/minter:0.0.7 command: --tendermint_addr=tcp://tendermint:46657 volumes: - ~/.minter:/minter diff --git a/networks/testnet/genesis.json b/networks/testnet/genesis.json index cf75e8a27..67a7a49a0 100644 --- a/networks/testnet/genesis.json +++ b/networks/testnet/genesis.json @@ -1,6 +1,6 @@ { "genesis_time": "2018-06-09T00:00:00Z", - "chain_id": "minter-test-network-10", + "chain_id": "minter-test-network-11", "validators": [ { "pub_key": { diff --git a/version/version.go b/version/version.go index 7c1c78936..d29da7782 100755 --- a/version/version.go +++ b/version/version.go @@ -4,12 +4,12 @@ package version const ( Maj = "0" Min = "0" - Fix = "6" + Fix = "7" ) var ( // Must be a string because scripts like dist.sh read this file. - Version = "0.0.6" + Version = "0.0.7" // GitCommit is the current HEAD set using ldflags. GitCommit string From b4ff51e599bf82333e66019d45f1c64aabe26872 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Tue, 17 Jul 2018 13:24:27 +0300 Subject: [PATCH 08/78] divide commissions by 10 --- core/commissions/commissions.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/commissions/commissions.go b/core/commissions/commissions.go index 13ce845ff..429cfd5b2 100644 --- a/core/commissions/commissions.go +++ b/core/commissions/commissions.go @@ -1,7 +1,7 @@ package commissions -// all commissions are divided by 10^15 -// actual commission is SendTx * 10^15 = 1 000 000 000 000 000 PIP = 0,001 BIP +// all commissions are divided by 10^14 +// actual commission is SendTx * 10^14 = 10 000 000 000 000 000 PIP = 0,01 BIP const ( SendTx int64 = 10 ConvertTx int64 = 1000 @@ -9,7 +9,6 @@ const ( DeclareCandidacyTx int64 = 10000 DelegateTx int64 = 100 UnbondTx int64 = 100 - RedeemCheckTx int64 = 10 PayloadByte int64 = 2 ToggleCandidateStatus int64 = 100 ) From bec153ab177e1092459f4c44db2e5b8c330e8aec Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 11:05:36 +0300 Subject: [PATCH 09/78] Check transaction nonce before adding to mempool --- CHANGELOG.md | 5 +++++ core/transaction/executor.go | 14 +++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84b5bcee9..1f5a84dce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## TBD + +- [api] Add validators rewards to block api + ## 0.1.0 *Jule 17th, 2018* @@ -10,6 +14,7 @@ BREAKING CHANGES IMPROVEMENT - [config] 10x default send/recv rate +- [core] Check transaction nonce before adding to mempool ## 0.0.6 *Jule 16th, 2018* diff --git a/core/transaction/executor.go b/core/transaction/executor.go index 16f99a026..4fb11d9b9 100644 --- a/core/transaction/executor.go +++ b/core/transaction/executor.go @@ -70,15 +70,11 @@ func RunTx(context *state.StateDB, isCheck bool, rawTx []byte, rewardPull *big.I Log: err.Error()} } - // do not look at nonce of transaction while checking tx - // this will allow us to send multiple transaction from one account in one block - // in the future we should use "last known nonce" approach from Ethereum - if !isCheck { - if expectedNonce := context.GetNonce(sender) + 1; expectedNonce != tx.Nonce { - return Response{ - Code: code.WrongNonce, - Log: fmt.Sprintf("Unexpected nonce. Expected: %d, got %d.", expectedNonce, tx.Nonce)} - } + // TODO: deal with multiple pending transactions from one account + if expectedNonce := context.GetNonce(sender) + 1; expectedNonce != tx.Nonce { + return Response{ + Code: code.WrongNonce, + Log: fmt.Sprintf("Unexpected nonce. Expected: %d, got %d.", expectedNonce, tx.Nonce)} } return tx.decodedData.Run(sender, tx, context, isCheck, rewardPull, currentBlock) From 4078c5e1e03b1e0d1871c86db1265d5f0aa3b199 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 11:10:17 +0300 Subject: [PATCH 10/78] Recheck after empty blocks --- CHANGELOG.md | 1 + networks/testnet/config.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f5a84dce..6800e0be8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ BREAKING CHANGES IMPROVEMENT - [config] 10x default send/recv rate +- [config] Recheck after empty blocks - [core] Check transaction nonce before adding to mempool ## 0.0.6 diff --git a/networks/testnet/config.toml b/networks/testnet/config.toml index 4625a0f73..405828e9e 100644 --- a/networks/testnet/config.toml +++ b/networks/testnet/config.toml @@ -106,7 +106,7 @@ seed_mode = false [mempool] recheck = true -recheck_empty = false +recheck_empty = true broadcast = true wal_dir = "data/mempool.wal" cache_size = 100000 From 9ed599a55002504daf4b166b7c50ed44d65bee40 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 15:02:26 +0300 Subject: [PATCH 11/78] Now Minter is available as single binary. There is no need to install Tendermint. --- CHANGELOG.md | 1 + api/api.go | 7 +- cmd/minter/main.go | 49 ++++++++--- config/config.go | 41 +++++++++ networks/testnet/config.toml | 163 ----------------------------------- 5 files changed, 82 insertions(+), 179 deletions(-) create mode 100644 config/config.go delete mode 100644 networks/testnet/config.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index 6800e0be8..999b2a0af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ BREAKING CHANGES IMPROVEMENT +- [binary] Now Minter is available as single binary. There is no need to install Tendermint. - [config] 10x default send/recv rate - [config] Recheck after empty blocks - [core] Check transaction nonce before adding to mempool diff --git a/api/api.go b/api/api.go index 19f6708ad..7ae0d8629 100644 --- a/api/api.go +++ b/api/api.go @@ -10,6 +10,7 @@ import ( "github.com/MinterTeam/minter-go-node/cmd/utils" "github.com/MinterTeam/minter-go-node/core/minter" "github.com/MinterTeam/minter-go-node/core/state" + "github.com/tendermint/tendermint/node" rpc "github.com/tendermint/tendermint/rpc/client" "strconv" "time" @@ -17,11 +18,11 @@ import ( var ( blockchain *minter.Blockchain - client *rpc.HTTP + client *rpc.Local ) -func RunApi(b *minter.Blockchain) { - client = rpc.NewHTTP(*utils.TendermintRpcAddrFlag, "/websocket") +func RunApi(b *minter.Blockchain, node *node.Node) { + client = rpc.NewLocal(node) blockchain = b diff --git a/cmd/minter/main.go b/cmd/minter/main.go index c6010303d..3db61ea10 100644 --- a/cmd/minter/main.go +++ b/cmd/minter/main.go @@ -1,35 +1,58 @@ package main import ( + "fmt" "github.com/MinterTeam/minter-go-node/api" "github.com/MinterTeam/minter-go-node/cmd/utils" + "github.com/MinterTeam/minter-go-node/config" "github.com/MinterTeam/minter-go-node/core/minter" "github.com/MinterTeam/minter-go-node/log" - "github.com/tendermint/tendermint/abci/server" "github.com/tendermint/tendermint/libs/common" + tmNode "github.com/tendermint/tendermint/node" + "github.com/tendermint/tendermint/privval" + "github.com/tendermint/tendermint/proxy" ) func main() { - app := minter.NewMinterBlockchain() - // Start the listener - srv, err := server.NewServer(*utils.MinterAppAddrFlag, "socket", app) - if err != nil { - panic(err) - } - srv.SetLogger(log.With("module", "abci-server")) - if err := srv.Start(); err != nil { - panic(err) - } + app := minter.NewMinterBlockchain() + node := startTendermint(app) if !*utils.DisableApi { - go api.RunApi(app) + go api.RunApi(app, node) } // Wait forever common.TrapSignal(func() { // Cleanup + node.Stop() app.Stop() - srv.Stop() }) } + +func startTendermint(app *minter.Blockchain) *tmNode.Node { + + cfg := config.GetConfig() + + node, err := tmNode.NewNode( + cfg, + privval.LoadOrGenFilePV(cfg.PrivValidatorFile()), + proxy.NewLocalClientCreator(app), + tmNode.DefaultGenesisDocProviderFunc(cfg), + tmNode.DefaultDBProvider, + tmNode.DefaultMetricsProvider, + log.With("module", "tendermint"), + ) + + if err != nil { + fmt.Errorf("Failed to create a node: %v", err) + } + + if err = node.Start(); err != nil { + fmt.Errorf("Failed to start node: %v", err) + } + + log.Info("Started node", "nodeInfo", node.Switch().NodeInfo()) + + return node +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 000000000..d47eebe9a --- /dev/null +++ b/config/config.go @@ -0,0 +1,41 @@ +package config + +import ( + tmConfig "github.com/tendermint/tendermint/config" + "os" +) + +var homeDir = os.Getenv("HOME") +var MinterDir = homeDir + "/.minter" +var config = tmConfig.DefaultConfig() + +func GetConfig() *tmConfig.Config { + + config.P2P.PersistentPeers = "249c62818bf4601605a65b5adc35278236bd5312@95.216.148.138:26656" + + config.Moniker = "MinterNode" + config.TxIndex = &tmConfig.TxIndexConfig{ + Indexer: "kv", + IndexTags: "", + IndexAllTags: true, + } + config.DBPath = MinterDir + "/tmdata" + config.Mempool.CacheSize = 100000 + config.Mempool.WalPath = MinterDir + "/tmdata/mempool.wal" + config.Consensus.WalPath = MinterDir + "tmdata/cs.wal/wal" + config.Genesis = MinterDir + "/config/genesis.json" + config.PrivValidator = MinterDir + "/config/priv_validator.json" + config.NodeKey = MinterDir + "/config/node_key.json" + config.P2P.AddrBook = MinterDir + "/config/addrbook.json" + config.P2P.ListenAddress = "tcp://0.0.0.0:26656" + + config.Consensus.TimeoutPropose = 3000 + config.Consensus.TimeoutProposeDelta = 500 + config.Consensus.TimeoutPrevote = 1000 + config.Consensus.TimeoutPrevoteDelta = 500 + config.Consensus.TimeoutPrecommit = 1000 + config.Consensus.TimeoutPrecommitDelta = 500 + config.Consensus.TimeoutCommit = 5000 + + return config +} diff --git a/networks/testnet/config.toml b/networks/testnet/config.toml deleted file mode 100644 index 405828e9e..000000000 --- a/networks/testnet/config.toml +++ /dev/null @@ -1,163 +0,0 @@ -# This is a TOML config file. -# For more information, see https://github.com/toml-lang/toml - -##### main base config options ##### - -# TCP or UNIX socket address of the ABCI application, -# or the name of an ABCI application compiled in with the Tendermint binary -proxy_app = "tcp://127.0.0.1:46658" - -# A custom human readable name for this node -moniker = "MinterNode" - -# If this node is many blocks behind the tip of the chain, FastSync -# allows them to catchup quickly by downloading blocks in parallel -# and verifying their commits -fast_sync = true - -# Database backend: leveldb | memdb -db_backend = "leveldb" - -# Database directory -db_path = "data" - -# Output level for logging, including package level options -log_level = "main:info,state:info,*:error" - -##### additional base config options ##### - -# Path to the JSON file containing the initial validator set and other meta data -genesis_file = "config/genesis.json" - -# Path to the JSON file containing the private key to use as a validator in the consensus protocol -priv_validator_file = "config/priv_validator.json" - -# Path to the JSON file containing the private key to use for node authentication in the p2p protocol -node_key_file = "config/node_key.json" - -# Mechanism to connect to the ABCI application: socket | grpc -abci = "socket" - -# TCP or UNIX socket address for the profiling server to listen on -prof_laddr = "" - -# If true, query the ABCI app on connecting to a new peer -# so the app can decide if we should keep the connection or not -filter_peers = false - -##### advanced configuration options ##### - -##### rpc server configuration options ##### -[rpc] - -# TCP or UNIX socket address for the RPC server to listen on -laddr = "tcp://0.0.0.0:46657" - -# TCP or UNIX socket address for the gRPC server to listen on -# NOTE: This server only supports /broadcast_tx_commit -grpc_laddr = "" - -# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool -unsafe = false - -##### peer to peer configuration options ##### -[p2p] - -# Address to listen for incoming connections -laddr = "tcp://0.0.0.0:46656" - -# Comma separated list of seed nodes to connect to -seeds = "" - -# Comma separated list of nodes to keep persistent connections to -persistent_peers = "249c62818bf4601605a65b5adc35278236bd5312@95.216.148.138:46656" - -# Path to address book -addr_book_file = "config/addrbook.json" - -# Set true for strict address routability rules -addr_book_strict = true - -# Time to wait before flushing messages out on the connection, in ms -flush_throttle_timeout = 100 - -# Maximum number of peers to connect to -max_num_peers = 50 - -# Maximum size of a message packet payload, in bytes -max_msg_packet_payload_size = 2048 - -# Rate at which packets can be sent, in bytes/second -send_rate = 5120000 - -# Rate at which packets can be received, in bytes/second -recv_rate = 5120000 - -# Set true to enable the peer-exchange reactor -pex = true - -# Seed mode, in which node constantly crawls the network and looks for -# peers. If another node asks it for addresses, it responds and disconnects. -# -# Does not work if the peer-exchange reactor is disabled. -seed_mode = false - -##### mempool configuration options ##### -[mempool] - -recheck = true -recheck_empty = true -broadcast = true -wal_dir = "data/mempool.wal" -cache_size = 100000 - -##### consensus configuration options ##### -[consensus] - -wal_file = "data/cs.wal/wal" - -# All timeouts are in milliseconds -timeout_propose = 3000 -timeout_propose_delta = 500 -timeout_prevote = 1000 -timeout_prevote_delta = 500 -timeout_precommit = 1000 -timeout_precommit_delta = 500 -timeout_commit = 5000 - -# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) -skip_timeout_commit = false - -# BlockSize -max_block_size_txs = 10000 -max_block_size_bytes = 1 - -# EmptyBlocks mode and possible interval between empty blocks in seconds -create_empty_blocks = true -create_empty_blocks_interval = 0 - -# Reactor sleep duration parameters are in milliseconds -peer_gossip_sleep_duration = 100 -peer_query_maj23_sleep_duration = 2000 - -##### transactions indexer configuration options ##### -[tx_index] - -# What indexer to use for transactions -# -# Options: -# 1) "null" (default) -# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). -indexer = "kv" - -# Comma-separated list of tags to index (by default the only tag is tx hash) -# -# It's recommended to index only a subset of tags due to possible memory -# bloat. This is, of course, depends on the indexer's DB and the volume of -# transactions. -index_tags = "" - -# When set to true, tells indexer to index all tags. Note this may be not -# desirable (see the comment above). IndexTags has a precedence over -# IndexAllTags (i.e. when given both, IndexTags will be indexed). -index_all_tags = true \ No newline at end of file From 6309e484aadf678f1434742aee14dce419fc2e0b Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 15:10:58 +0300 Subject: [PATCH 12/78] fix docker --- config/config.go | 5 ++--- docker-compose.yml | 22 +--------------------- tests/acceptance/docker/docker-compose.yml | 9 --------- 3 files changed, 3 insertions(+), 33 deletions(-) diff --git a/config/config.go b/config/config.go index d47eebe9a..52fb910bf 100644 --- a/config/config.go +++ b/config/config.go @@ -1,12 +1,11 @@ package config import ( + "github.com/MinterTeam/minter-go-node/cmd/utils" tmConfig "github.com/tendermint/tendermint/config" - "os" ) -var homeDir = os.Getenv("HOME") -var MinterDir = homeDir + "/.minter" +var MinterDir = utils.GetMinterHome() var config = tmConfig.DefaultConfig() func GetConfig() *tmConfig.Config { diff --git a/docker-compose.yml b/docker-compose.yml index f59e46890..9c7691a28 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,6 @@ version: "3.4" services: minter: image: minterteam/minter:0.0.7 - command: --tendermint_addr=tcp://tendermint:46657 volumes: - ~/.minter:/minter ports: @@ -13,23 +12,4 @@ services: interval: 5s timeout: 5s retries: 3 - start_period: 30s - tendermint: - image: tendermint/tendermint:0.22.4 - command: node --proxy_app=tcp://minter:46658 - volumes: - - ~/.tendermint:/tendermint - ports: - - "46656:46656" - - "46657:46657" - restart: always - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:46657/health"] - interval: 5s - timeout: 5s - retries: 3 - start_period: 30s - ulimits: - nofile: - soft: 1048576 - hard: 1048576 + start_period: 30s \ No newline at end of file diff --git a/tests/acceptance/docker/docker-compose.yml b/tests/acceptance/docker/docker-compose.yml index 5f23a56e5..b33434147 100644 --- a/tests/acceptance/docker/docker-compose.yml +++ b/tests/acceptance/docker/docker-compose.yml @@ -2,16 +2,7 @@ version: "3.4" services: minter: image: minterteam/minter:latest - command: --tendermint_addr=tcp://tendermint:46657 volumes: - ./data/.minter:/minter ports: - "8841:8841" - tendermint: - image: tendermint/tendermint:0.22.0 - command: node --proxy_app=tcp://minter:46658 - volumes: - - ./data/.tendermint:/tendermint - ports: - - "46656:46656" - - "46657:46657" From 6b17e972ad4bef5de593e820848ad982ee7a7ae8 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 15:12:47 +0300 Subject: [PATCH 13/78] fix config --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 52fb910bf..a5fffc06d 100644 --- a/config/config.go +++ b/config/config.go @@ -21,7 +21,7 @@ func GetConfig() *tmConfig.Config { config.DBPath = MinterDir + "/tmdata" config.Mempool.CacheSize = 100000 config.Mempool.WalPath = MinterDir + "/tmdata/mempool.wal" - config.Consensus.WalPath = MinterDir + "tmdata/cs.wal/wal" + config.Consensus.WalPath = MinterDir + "/tmdata/cs.wal/wal" config.Genesis = MinterDir + "/config/genesis.json" config.PrivValidator = MinterDir + "/config/priv_validator.json" config.NodeKey = MinterDir + "/config/node_key.json" From 248e18fae5a6f2bd450328335e20ca7bda9c8716 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 15:13:18 +0300 Subject: [PATCH 14/78] remove unused flag --- cmd/utils/flags.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index a4140091f..3c26e2a85 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -8,7 +8,6 @@ import ( var ( TendermintRpcAddrFlag = flag.String("tendermint_addr", "tcp://0.0.0.0:46657", "This is the address that minter will use to connect to the tendermint core node. Please provide a port.") - MinterAppAddrFlag = flag.String("minter_addr", "tcp://0.0.0.0:46658", "This is the address that minter will use to open ABCI application server. Please provide a port.") MinterAPIAddrFlag = flag.String("api_addr", ":8841", "This is the address that minter will use to open API server. Please provide a port.") MinterHome = flag.String("home", "", "Path to minter data directory") DisableApi = flag.Bool("disable-api", false, "") From b2dd7e50d6c1e50f2e0f5e9a865137fd30a8d424 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 15:17:49 +0300 Subject: [PATCH 15/78] dep ensure --- Gopkg.lock | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 21896f42e..433879d2b 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -179,7 +179,6 @@ revision = "40f013a808ec4fa79def444a1a56de4d1727efcb" [[projects]] - branch = "master" name = "github.com/rcrowley/go-metrics" packages = ["."] revision = "e2704e165165ec55d062f5919b4b29494e9fa790" @@ -231,7 +230,6 @@ "abci/client", "abci/example/code", "abci/example/kvstore", - "abci/server", "abci/types", "blockchain", "config", @@ -377,6 +375,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "1c35f22aa0442b823988c777d05af72e5ad79329c000833560561029387eda7e" + inputs-digest = "6d00d7552efb4b12eec308fbd3142897972ed35ff6eb79a361b441504fab2a52" solver-name = "gps-cdcl" solver-version = 1 From 17d36b0cf60c0e856e746a28808622562ef90c1e Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 15:20:48 +0300 Subject: [PATCH 16/78] update docs --- docs/install.rst | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index cecb2bc06..94f6998b5 100755 --- a/docs/install.rst +++ b/docs/install.rst @@ -26,12 +26,10 @@ Prepare folders and configs .. code-block:: bash :lineno-start: 3 - mkdir -p ~/.tendermint/data mkdir -p ~/.minter/data - cp -R networks/testnet/ ~/.tendermint/config + cp -R networks/testnet/ ~/.minter/config - chmod -R 0777 ~/.tendermint chmod -R 0777 ~/.minter Start Minter @@ -49,10 +47,6 @@ From Source You'll need ``go`` `installed `__ and the required `environment variables set `__ -Install Tendermint 0.22.4 -^^^^^^^^^^^^^^^^^^^^^^^^^ -`Read official instructions `__ - Clone Minter source code to your machine ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -92,30 +86,21 @@ to put the binary in ``./build``. The latest ``minter version`` is now installed. -Create data directories -^^^^^^^^^^^^^^^^^^^^^^^ +Create data directory +^^^^^^^^^^^^^^^^^^^^^ .. code-block:: bash :lineno-start: 9 - mkdir -p ~/.tendermint/data mkdir -p ~/.minter/data -Copy config and genesis file -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Copy genesis file +^^^^^^^^^^^^^^^^^ .. code-block:: bash :lineno-start: 11 - cp -R networks/testnet/ ~/.tendermint/config - -Run Tendermint -^^^^^^^^^^^^^^ - -.. code-block:: bash - :lineno-start: 12 - - tendermint node + cp -R networks/testnet/ ~/.minter/config Run Minter ^^^^^^^^^^ From 3983f7f46cd4e0672650c2e18864e59428526f02 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 15:41:58 +0300 Subject: [PATCH 17/78] change version and add binary instructions --- docker-compose.yml | 2 +- docs/install.rst | 29 +++++++++++++++++++++++++++++ version/version.go | 6 +++--- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 9c7691a28..fde5b81eb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.4" services: minter: - image: minterteam/minter:0.0.7 + image: minterteam/minter:0.1.0 volumes: - ~/.minter:/minter ports: diff --git a/docs/install.rst b/docs/install.rst index 94f6998b5..71bffa333 100755 --- a/docs/install.rst +++ b/docs/install.rst @@ -5,6 +5,35 @@ Install Minter There are several ways you can install Minter Blockchain Testnet node on your machine: +Using binary +------------ + +Download Minter +^^^^^^^^^^^^^^^ + +Get `latest binary build `__ suitable for your architecture and +unpack it to desired folder. + +Prepare folders and configs +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + :lineno-start: 3 + + mkdir -p ~/.minter/data + + cp -R networks/testnet/ ~/.minter/config + + chmod -R 0777 ~/.minter + +Run Minter +^^^^^^^^^^ + +.. code-block:: bash + :lineno-start: 13 + + ./minter + Using Docker ------------ diff --git a/version/version.go b/version/version.go index d29da7782..fd87602e6 100755 --- a/version/version.go +++ b/version/version.go @@ -3,13 +3,13 @@ package version // Version components const ( Maj = "0" - Min = "0" - Fix = "7" + Min = "1" + Fix = "0" ) var ( // Must be a string because scripts like dist.sh read this file. - Version = "0.0.7" + Version = "0.1.0" // GitCommit is the current HEAD set using ldflags. GitCommit string From e554112adc476edfe1d4bb062e088cf438639d70 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 15:47:36 +0300 Subject: [PATCH 18/78] fix docs --- docs/install.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/install.rst b/docs/install.rst index 71bffa333..6f7a7d3fe 100755 --- a/docs/install.rst +++ b/docs/install.rst @@ -22,6 +22,7 @@ Prepare folders and configs mkdir -p ~/.minter/data + git clone https://github.com/MinterTeam/minter-go-node.git && cd minter-go-node cp -R networks/testnet/ ~/.minter/config chmod -R 0777 ~/.minter From 82845bd26d69a9b9292b4d1096801417ee4bf7fb Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 15:48:50 +0300 Subject: [PATCH 19/78] fix docs --- docs/install.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index 6f7a7d3fe..a54622948 100755 --- a/docs/install.rst +++ b/docs/install.rst @@ -20,12 +20,12 @@ Prepare folders and configs .. code-block:: bash :lineno-start: 3 - mkdir -p ~/.minter/data + mkdir -p ~/.minter/data - git clone https://github.com/MinterTeam/minter-go-node.git && cd minter-go-node - cp -R networks/testnet/ ~/.minter/config + git clone https://github.com/MinterTeam/minter-go-node.git && cd minter-go-node + cp -R networks/testnet/ ~/.minter/config - chmod -R 0777 ~/.minter + chmod -R 0777 ~/.minter Run Minter ^^^^^^^^^^ @@ -33,7 +33,7 @@ Run Minter .. code-block:: bash :lineno-start: 13 - ./minter + ./minter Using Docker ------------ From 1aa414d6e80f308bf67abefb34ae2f1f9d0e99a9 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 16:01:26 +0300 Subject: [PATCH 20/78] encapsulate genesis --- cmd/minter/main.go | 15 +++++++++++++ docs/install.rst | 42 ----------------------------------- genesis/genesis.go | 33 +++++++++++++++++++++++++++ helpers/helpers.go | 4 +++- networks/testnet/genesis.json | 32 -------------------------- 5 files changed, 51 insertions(+), 75 deletions(-) delete mode 100644 networks/testnet/genesis.json diff --git a/cmd/minter/main.go b/cmd/minter/main.go index 3db61ea10..a347ce518 100644 --- a/cmd/minter/main.go +++ b/cmd/minter/main.go @@ -6,15 +6,30 @@ import ( "github.com/MinterTeam/minter-go-node/cmd/utils" "github.com/MinterTeam/minter-go-node/config" "github.com/MinterTeam/minter-go-node/core/minter" + "github.com/MinterTeam/minter-go-node/genesis" "github.com/MinterTeam/minter-go-node/log" "github.com/tendermint/tendermint/libs/common" tmNode "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" + "io/ioutil" + "os" ) func main() { + configDir := utils.GetMinterHome() + "/config/" + genesisFile := configDir + "genesis.json" + + if !common.FileExists(genesisFile) { + os.MkdirAll(configDir, 0777) + err := ioutil.WriteFile(genesisFile, []byte(genesis.TestnetGenesis), 0644) + + if err != nil { + panic(err) + } + } + app := minter.NewMinterBlockchain() node := startTendermint(app) diff --git a/docs/install.rst b/docs/install.rst index a54622948..fffed550b 100755 --- a/docs/install.rst +++ b/docs/install.rst @@ -14,19 +14,6 @@ Download Minter Get `latest binary build `__ suitable for your architecture and unpack it to desired folder. -Prepare folders and configs -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - :lineno-start: 3 - - mkdir -p ~/.minter/data - - git clone https://github.com/MinterTeam/minter-go-node.git && cd minter-go-node - cp -R networks/testnet/ ~/.minter/config - - chmod -R 0777 ~/.minter - Run Minter ^^^^^^^^^^ @@ -49,19 +36,6 @@ Clone Minter source code to your machine git clone https://github.com/MinterTeam/minter-go-node.git cd minter-go-node - -Prepare folders and configs -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - :lineno-start: 3 - - mkdir -p ~/.minter/data - - cp -R networks/testnet/ ~/.minter/config - - chmod -R 0777 ~/.minter - Start Minter ^^^^^^^^^^^^ @@ -116,22 +90,6 @@ to put the binary in ``./build``. The latest ``minter version`` is now installed. -Create data directory -^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - :lineno-start: 9 - - mkdir -p ~/.minter/data - -Copy genesis file -^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - :lineno-start: 11 - - cp -R networks/testnet/ ~/.minter/config - Run Minter ^^^^^^^^^^ diff --git a/genesis/genesis.go b/genesis/genesis.go index 2120a8ce3..20b2dbf2a 100644 --- a/genesis/genesis.go +++ b/genesis/genesis.go @@ -2,6 +2,39 @@ package genesis import "github.com/MinterTeam/minter-go-node/core/types" +var TestnetGenesis = `{ + "genesis_time": "2018-06-09T00:00:00Z", + "chain_id": "minter-test-network-11", + "validators": [ + { + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "qu4d3zD/VMkHFdkotWZS/FEb7Tci5Ylz6O+Ub12uOXk=" + }, + "power": "100", + "name": "" + } + ], + "app_state": { + "first_validator_address": "Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", + "initial_balances": [ + { + "address": "Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", + "balance": { + "MNT": "10000000000000000000000000" + } + }, + { + "address": "Mxfe60014a6e9ac91618f5d1cab3fd58cded61ee99", + "balance": { + "MNT": "10000000000000000000000000" + } + } + ] + }, + "app_hash": "0000000000000000000000000000000000000000000000000000000000000000" +}` + type AppState struct { FirstValidatorAddress types.Address `json:"first_validator_address"` InitialBalances []Account `json:"initial_balances"` diff --git a/helpers/helpers.go b/helpers/helpers.go index df492c909..37e959dba 100644 --- a/helpers/helpers.go +++ b/helpers/helpers.go @@ -1,6 +1,8 @@ package helpers -import "math/big" +import ( + "math/big" +) func BipToPip(bip *big.Int) *big.Int { p := big.NewInt(10) diff --git a/networks/testnet/genesis.json b/networks/testnet/genesis.json deleted file mode 100644 index 67a7a49a0..000000000 --- a/networks/testnet/genesis.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "genesis_time": "2018-06-09T00:00:00Z", - "chain_id": "minter-test-network-11", - "validators": [ - { - "pub_key": { - "type": "tendermint/PubKeyEd25519", - "value": "qu4d3zD/VMkHFdkotWZS/FEb7Tci5Ylz6O+Ub12uOXk=" - }, - "power": "100", - "name": "" - } - ], - "app_state": { - "first_validator_address": "Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", - "initial_balances": [ - { - "address": "Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", - "balance": { - "MNT": "10000000000000000000000000" - } - }, - { - "address": "Mxfe60014a6e9ac91618f5d1cab3fd58cded61ee99", - "balance": { - "MNT": "10000000000000000000000000" - } - } - ] - }, - "app_hash": "0000000000000000000000000000000000000000000000000000000000000000" -} \ No newline at end of file From bf005956674ba00b6b6657276670183fbbc8c8ac Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 17:25:22 +0300 Subject: [PATCH 21/78] fix blockchain rpc connection --- cmd/minter/main.go | 2 ++ cmd/utils/flags.go | 7 +++---- core/minter/minter.go | 12 ++++++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/cmd/minter/main.go b/cmd/minter/main.go index a347ce518..09025964a 100644 --- a/cmd/minter/main.go +++ b/cmd/minter/main.go @@ -33,6 +33,8 @@ func main() { app := minter.NewMinterBlockchain() node := startTendermint(app) + app.RunRPC(node) + if !*utils.DisableApi { go api.RunApi(app, node) } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 3c26e2a85..ba7c2d9db 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -7,10 +7,9 @@ import ( ) var ( - TendermintRpcAddrFlag = flag.String("tendermint_addr", "tcp://0.0.0.0:46657", "This is the address that minter will use to connect to the tendermint core node. Please provide a port.") - MinterAPIAddrFlag = flag.String("api_addr", ":8841", "This is the address that minter will use to open API server. Please provide a port.") - MinterHome = flag.String("home", "", "Path to minter data directory") - DisableApi = flag.Bool("disable-api", false, "") + MinterAPIAddrFlag = flag.String("api_addr", ":8841", "This is the address that minter will use to open API server. Please provide a port.") + MinterHome = flag.String("home", "", "Path to minter data directory") + DisableApi = flag.Bool("disable-api", false, "") ) func init() { diff --git a/core/minter/minter.go b/core/minter/minter.go index 2a21b0856..c665f70a3 100644 --- a/core/minter/minter.go +++ b/core/minter/minter.go @@ -14,6 +14,7 @@ import ( "github.com/MinterTeam/minter-go-node/helpers" "github.com/MinterTeam/minter-go-node/mintdb" abciTypes "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/node" rpc "github.com/tendermint/tendermint/rpc/client" "math/big" ) @@ -29,7 +30,7 @@ type Blockchain struct { rewards *big.Int activeValidators abciTypes.Validators validatorsStatuses map[string]int8 - tendermintRPC *rpc.HTTP + tendermintRPC *rpc.Local BaseCoin types.CoinSymbol } @@ -58,9 +59,8 @@ func NewMinterBlockchain() *Blockchain { } blockchain = &Blockchain{ - db: db, - BaseCoin: types.GetBaseCoin(), - tendermintRPC: rpc.NewHTTP(*utils.TendermintRpcAddrFlag, "/websocket"), + db: db, + BaseCoin: types.GetBaseCoin(), } blockchain.updateCurrentRootHash() @@ -69,6 +69,10 @@ func NewMinterBlockchain() *Blockchain { return blockchain } +func (app *Blockchain) RunRPC(node *node.Node) { + app.tendermintRPC = rpc.NewLocal(node) +} + func (app *Blockchain) SetOption(req abciTypes.RequestSetOption) abciTypes.ResponseSetOption { return abciTypes.ResponseSetOption{} } From c37b66fe7aba2939f3668715563b7d2383820602 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 17:44:29 +0300 Subject: [PATCH 22/78] encapsulate genesis --- cmd/minter/main.go | 23 ++++++++++------------- config/config.go | 1 - genesis/genesis.go | 21 ++++++++++++++++++++- log/log.go | 4 ++++ 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/cmd/minter/main.go b/cmd/minter/main.go index 09025964a..514f25057 100644 --- a/cmd/minter/main.go +++ b/cmd/minter/main.go @@ -12,26 +12,21 @@ import ( tmNode "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" - "io/ioutil" + "github.com/tendermint/tendermint/types" "os" ) func main() { - configDir := utils.GetMinterHome() + "/config/" - genesisFile := configDir + "genesis.json" + err := common.EnsureDir(utils.GetMinterHome()+"/config", 0777) - if !common.FileExists(genesisFile) { - os.MkdirAll(configDir, 0777) - err := ioutil.WriteFile(genesisFile, []byte(genesis.TestnetGenesis), 0644) - - if err != nil { - panic(err) - } + if err != nil { + log.Error(err.Error()) + os.Exit(1) } app := minter.NewMinterBlockchain() - node := startTendermint(app) + node := startTendermintNode(app) app.RunRPC(node) @@ -47,7 +42,7 @@ func main() { }) } -func startTendermint(app *minter.Blockchain) *tmNode.Node { +func startTendermintNode(app *minter.Blockchain) *tmNode.Node { cfg := config.GetConfig() @@ -55,7 +50,9 @@ func startTendermint(app *minter.Blockchain) *tmNode.Node { cfg, privval.LoadOrGenFilePV(cfg.PrivValidatorFile()), proxy.NewLocalClientCreator(app), - tmNode.DefaultGenesisDocProviderFunc(cfg), + func() (*types.GenesisDoc, error) { + return genesis.GetTestnetGenesis(), nil + }, tmNode.DefaultDBProvider, tmNode.DefaultMetricsProvider, log.With("module", "tendermint"), diff --git a/config/config.go b/config/config.go index a5fffc06d..0039362bf 100644 --- a/config/config.go +++ b/config/config.go @@ -22,7 +22,6 @@ func GetConfig() *tmConfig.Config { config.Mempool.CacheSize = 100000 config.Mempool.WalPath = MinterDir + "/tmdata/mempool.wal" config.Consensus.WalPath = MinterDir + "/tmdata/cs.wal/wal" - config.Genesis = MinterDir + "/config/genesis.json" config.PrivValidator = MinterDir + "/config/priv_validator.json" config.NodeKey = MinterDir + "/config/node_key.json" config.P2P.AddrBook = MinterDir + "/config/addrbook.json" diff --git a/genesis/genesis.go b/genesis/genesis.go index 20b2dbf2a..fce125d7f 100644 --- a/genesis/genesis.go +++ b/genesis/genesis.go @@ -1,6 +1,17 @@ package genesis -import "github.com/MinterTeam/minter-go-node/core/types" +import ( + "github.com/MinterTeam/minter-go-node/core/types" + "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto" + tmtypes "github.com/tendermint/tendermint/types" +) + +var cdc = amino.NewCodec() + +func init() { + crypto.RegisterAmino(cdc) +} var TestnetGenesis = `{ "genesis_time": "2018-06-09T00:00:00Z", @@ -35,6 +46,14 @@ var TestnetGenesis = `{ "app_hash": "0000000000000000000000000000000000000000000000000000000000000000" }` +func GetTestnetGenesis() *tmtypes.GenesisDoc { + genDoc := tmtypes.GenesisDoc{} + cdc.UnmarshalJSON([]byte(TestnetGenesis), &genDoc) + genDoc.ValidateAndComplete() + + return &genDoc +} + type AppState struct { FirstValidatorAddress types.Address `json:"first_validator_address"` InitialBalances []Account `json:"initial_balances"` diff --git a/log/log.go b/log/log.go index 5dc2973f7..28d0d0ad2 100644 --- a/log/log.go +++ b/log/log.go @@ -22,6 +22,10 @@ func Info(msg string, ctx ...interface{}) { logger.Info(msg, ctx...) } +func Error(msg string, ctx ...interface{}) { + logger.Error(msg, ctx...) +} + func With(keyvals ...interface{}) log.Logger { return logger.With(keyvals...) } From f292bf872e6bf8da175ee8db47b9ff3b3fe03402 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 18:23:12 +0300 Subject: [PATCH 23/78] fix raw transaction output in api --- api/block.go | 4 ++-- api/transaction.go | 2 +- api/transactions.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/block.go b/api/block.go index c8761738d..87cef6a75 100644 --- a/api/block.go +++ b/api/block.go @@ -65,8 +65,8 @@ func Block(w http.ResponseWriter, r *http.Request) { sender, _ := tx.Sender() txs[i] = BlockTransactionResponse{ - Hash: fmt.Sprintf("Mt%x", types.Tx(rawTx).Hash()), - RawTx: fmt.Sprintf("%x", rawTx), + Hash: fmt.Sprintf("Mt%x", rawTx.Hash()), + RawTx: fmt.Sprintf("%x", []byte(rawTx)), From: sender.String(), Nonce: tx.Nonce, GasPrice: tx.GasPrice, diff --git a/api/transaction.go b/api/transaction.go index 43adabf1c..d8b45e4c8 100644 --- a/api/transaction.go +++ b/api/transaction.go @@ -39,7 +39,7 @@ func Transaction(w http.ResponseWriter, r *http.Request) { Code: 0, Result: TransactionResponse{ Hash: common.HexBytes(tx.Tx.Hash()), - RawTx: fmt.Sprintf("%x", tx.Tx), + RawTx: fmt.Sprintf("%x", []byte(tx.Tx)), Height: tx.Height, Index: tx.Index, TxResult: ResponseDeliverTx{ diff --git a/api/transactions.go b/api/transactions.go index 2a47e9ba1..977c12c99 100644 --- a/api/transactions.go +++ b/api/transactions.go @@ -67,7 +67,7 @@ func Transactions(w http.ResponseWriter, r *http.Request) { result[i] = TransactionResponse{ Hash: common.HexBytes(tx.Tx.Hash()), - RawTx: fmt.Sprintf("%x", tx.Tx), + RawTx: fmt.Sprintf("%x", []byte(tx.Tx)), Height: tx.Height, Index: tx.Index, TxResult: ResponseDeliverTx{ From 9308f17c5f3bc732337defd17ccad8098535a68f Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 18:23:44 +0300 Subject: [PATCH 24/78] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 999b2a0af..11e56e9f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ IMPROVEMENT - [config] Recheck after empty blocks - [core] Check transaction nonce before adding to mempool +BUG FIXES + +- [api] Fixed raw transaction output + ## 0.0.6 *Jule 16th, 2018* From 200e9ba9355b25f5d0c55464b238516643876459 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 18:27:10 +0300 Subject: [PATCH 25/78] fix address representation --- core/types/types.go | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/core/types/types.go b/core/types/types.go index 6e1bd21a4..0bb406bb4 100644 --- a/core/types/types.go +++ b/core/types/types.go @@ -24,8 +24,7 @@ import ( "reflect" "bytes" - "github.com/MinterTeam/minter-go-node/crypto/sha3" - "github.com/MinterTeam/minter-go-node/hexutil" + "github.com/MinterTeam/minter-go-node/hexutil" ) const ( @@ -194,26 +193,8 @@ func (a Address) Bytes() []byte { return a[:] } func (a Address) Big() *big.Int { return new(big.Int).SetBytes(a[:]) } func (a Address) Hash() Hash { return BytesToHash(a[:]) } -// Hex returns an EIP55-compliant hex string representation of the address. func (a Address) Hex() string { - unchecksummed := hex.EncodeToString(a[:]) - sha := sha3.NewKeccak256() - sha.Write([]byte(unchecksummed)) - hash := sha.Sum(nil) - - result := []byte(unchecksummed) - for i := 0; i < len(result); i++ { - hashByte := hash[i/2] - if i%2 == 0 { - hashByte = hashByte >> 4 - } else { - hashByte &= 0xf - } - if result[i] > '9' && hashByte > 7 { - result[i] -= 32 - } - } - return "Mx" + string(result) + return "Mx" + hex.EncodeToString(a[:]) } // String implements the stringer interface and is used also by the logger. From 784932dc5c86f51d544cfdf57064c7f41dd4a2d6 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 18:27:25 +0300 Subject: [PATCH 26/78] fmt --- core/types/types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/types/types.go b/core/types/types.go index 0bb406bb4..a032a7b70 100644 --- a/core/types/types.go +++ b/core/types/types.go @@ -24,7 +24,7 @@ import ( "reflect" "bytes" - "github.com/MinterTeam/minter-go-node/hexutil" + "github.com/MinterTeam/minter-go-node/hexutil" ) const ( From d4fb8ee45a76ec54e34c70086899e493c861b3d7 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 19:02:15 +0300 Subject: [PATCH 27/78] gui template --- cmd/minter/main.go | 11 ++++ gui/index.html | 124 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 gui/index.html diff --git a/cmd/minter/main.go b/cmd/minter/main.go index 514f25057..e6fbea81a 100644 --- a/cmd/minter/main.go +++ b/cmd/minter/main.go @@ -14,6 +14,8 @@ import ( "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" "os" + "github.com/gobuffalo/packr" + "net/http" ) func main() { @@ -30,6 +32,8 @@ func main() { app.RunRPC(node) + go runGUI() + if !*utils.DisableApi { go api.RunApi(app, node) } @@ -42,6 +46,13 @@ func main() { }) } +func runGUI() { + box := packr.NewBox("./../../gui") + + http.Handle("/", http.FileServer(box)) + log.Error(http.ListenAndServe(":3000", nil).Error()) +} + func startTendermintNode(app *minter.Blockchain) *tmNode.Node { cfg := config.GetConfig() diff --git a/gui/index.html b/gui/index.html new file mode 100644 index 000000000..d21844b1c --- /dev/null +++ b/gui/index.html @@ -0,0 +1,124 @@ + + + + + + Minter Node GUI + + + + + + +
+ +
+
+
+
+
+ Node Info +
+ + + + + + + + + + + + + + + + + + + + + + + +
Moniker{{ status.node_info.moniker }}
Node ID{{ status.node_info.id }}
Listen Addr{{ status.node_info.listen_addr }}
Network ID{{ status.node_info.network }}
Tendermint Version{{ status.node_info.version }}
+
+
+
+
+
+ Syncing Info +
+ + + + + + + + + + + +
Is Synced{{ status.sync_info.catching_up != false }}
Latest Block + #{{ status.sync_info.latest_block_height }} at {{ status.sync_info.latest_block_time }} +
+
+
+
+ +
+
+ + + \ No newline at end of file From 6ec83604b494a3948f595a302bbe82c8f0c24099 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 18 Jul 2018 19:06:42 +0300 Subject: [PATCH 28/78] fix gui --- gui/index.html | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/gui/index.html b/gui/index.html index d21844b1c..f18df7388 100644 --- a/gui/index.html +++ b/gui/index.html @@ -5,7 +5,8 @@ content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> Minter Node GUI - + @@ -86,12 +87,13 @@ Is Synced - {{ status.sync_info.catching_up != false }} + {{ status.sync_info.catching_up == false ? 'true' : 'false' }} Latest Block - #{{ status.sync_info.latest_block_height }} at {{ status.sync_info.latest_block_time }} + #{{ status.sync_info.latest_block_height }} at {{ status.sync_info.latest_block_time }} @@ -99,7 +101,6 @@ - + + - +
-
-
+
From 0e6ef9cb23d6b5a568951a030858fc1b7fc4ba24 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Thu, 19 Jul 2018 17:38:56 +0300 Subject: [PATCH 49/78] fix --- core/transaction/sell_all_coin.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/transaction/sell_all_coin.go b/core/transaction/sell_all_coin.go index a4c48d0e0..5e5e577fc 100644 --- a/core/transaction/sell_all_coin.go +++ b/core/transaction/sell_all_coin.go @@ -21,11 +21,9 @@ type SellAllCoinData struct { func (data SellAllCoinData) MarshalJSON() ([]byte, error) { return json.Marshal(struct { CoinToSell types.CoinSymbol `json:"coin_to_sell,string"` - ValueToSell string `json:"value_to_sell"` CoinToBuy types.CoinSymbol `json:"coin_to_buy,string"` }{ CoinToSell: data.CoinToSell, - ValueToSell: data.ValueToSell.String(), CoinToBuy: data.CoinToBuy, }) } From 5eb5eaf2b9dacea0a9dd74c9bf0666b24427140f Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 11:06:42 +0300 Subject: [PATCH 50/78] fix api --- core/transaction/switch_candidate_status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/transaction/switch_candidate_status.go b/core/transaction/switch_candidate_status.go index e6b07923d..eb7caa057 100644 --- a/core/transaction/switch_candidate_status.go +++ b/core/transaction/switch_candidate_status.go @@ -77,7 +77,7 @@ type SetCandidateOffData struct { func (data SetCandidateOffData) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - PubKey string `json:"pubkey"` + PubKey string `json:"pub_key"` }{ PubKey: fmt.Sprintf("Mp%x", data.PubKey), }) From 5e096e1f3675fa85bcf82038a9eb030dde837489 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 12:20:29 +0300 Subject: [PATCH 51/78] update api --- CHANGELOG.md | 3 ++- api/balance.go | 16 ++++++++++------ api/balance_watcher.go | 16 ++++++++++++++++ api/estimate_coin_buy.go | 32 +++++++++++++++++++++++++++++-- api/estimate_coin_sell.go | 32 +++++++++++++++++++++++++++++-- core/minter/minter.go | 4 ++++ core/state/balance_watcher.go | 27 ++++++++++++++++---------- core/transaction/sell_all_coin.go | 8 ++++---- 8 files changed, 113 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2da499b2..e9c759095 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,9 @@ BREAKING CHANGES - [core] 0.1x transaction fees - [core] Genesis is now encapsulated in code -- [config] New config directories - [core] Add new transaction type: SellAllCoin +- [config] New config directories +- [api] Huge API update. For more info see docs IMPROVEMENT diff --git a/api/balance.go b/api/balance.go index 8bf56bf96..ece356c64 100644 --- a/api/balance.go +++ b/api/balance.go @@ -7,7 +7,9 @@ import ( "net/http" ) -type BalanceResponse map[string]string +type BalanceResponse struct { + Balance map[string]string `json:"balance"` +} type BalanceRequest struct { Address types.Address `json:"address"` @@ -21,15 +23,17 @@ func GetBalance(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) address := types.HexToAddress(vars["address"]) - balance := BalanceResponse{} + balanceResponse := BalanceResponse{ + Balance: make(map[string]string), + } balances := cState.GetBalances(address) for k, v := range balances.Data { - balance[k.String()] = v.String() + balanceResponse.Balance[k.String()] = v.String() } - if _, exists := balance[types.GetBaseCoin().String()]; !exists { - balance[types.GetBaseCoin().String()] = "0" + if _, exists := balanceResponse.Balance[types.GetBaseCoin().String()]; !exists { + balanceResponse.Balance[types.GetBaseCoin().String()] = "0" } w.Header().Set("Content-Type", "application/json; charset=UTF-8") @@ -37,6 +41,6 @@ func GetBalance(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(Response{ Code: 0, - Result: balance, + Result: balanceResponse, }) } diff --git a/api/balance_watcher.go b/api/balance_watcher.go index 754d3a8d6..990d5ce83 100644 --- a/api/balance_watcher.go +++ b/api/balance_watcher.go @@ -1,9 +1,13 @@ package api import ( + "github.com/MinterTeam/minter-go-node/core/minter" "github.com/MinterTeam/minter-go-node/core/state" + "github.com/MinterTeam/minter-go-node/core/types" + "github.com/MinterTeam/minter-go-node/formula" "github.com/gorilla/websocket" "log" + "math/big" "net/http" ) @@ -26,6 +30,18 @@ func GetBalanceWatcher(w http.ResponseWriter, r *http.Request) { func handleBalanceChanges() { for { msg := <-state.BalanceChangeChan + + var balanceInBasecoin *big.Int + + if msg.Coin == types.GetBaseCoin() { + balanceInBasecoin = msg.Balance + } else { + sCoin := minter.GetBlockchain().CurrentState().GetStateCoin(msg.Coin).Data() + balanceInBasecoin = formula.CalculateSaleReturn(sCoin.Volume, sCoin.ReserveBalance, sCoin.Crr, msg.Balance) + } + + msg.BalanceInBasecoin = balanceInBasecoin + for client := range clients { err := client.WriteJSON(msg) if err != nil { diff --git a/api/estimate_coin_buy.go b/api/estimate_coin_buy.go index 8da521323..ba15a1ad8 100644 --- a/api/estimate_coin_buy.go +++ b/api/estimate_coin_buy.go @@ -4,12 +4,19 @@ import ( "encoding/json" "fmt" "github.com/MinterTeam/minter-go-node/core/code" + "github.com/MinterTeam/minter-go-node/core/commissions" + "github.com/MinterTeam/minter-go-node/core/transaction" "github.com/MinterTeam/minter-go-node/core/types" "github.com/MinterTeam/minter-go-node/formula" "math/big" "net/http" ) +type EstimateCoinBuyResponse struct { + WillPay string + Commission string +} + func EstimateCoinBuy(w http.ResponseWriter, r *http.Request) { cState := GetStateForRequest(r) @@ -56,6 +63,24 @@ func EstimateCoinBuy(w http.ResponseWriter, r *http.Request) { return } + commissionInBaseCoin := big.NewInt(commissions.ConvertTx) + commissionInBaseCoin.Mul(commissionInBaseCoin, transaction.CommissionMultiplier) + commission := big.NewInt(0).Set(commissionInBaseCoin) + + if coinToSellSymbol != types.GetBaseCoin() { + coin := cState.GetStateCoin(coinToSellSymbol) + + if coin.ReserveBalance().Cmp(commissionInBaseCoin) < 0 { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(Response{ + Code: 1, + Log: fmt.Sprintf("Coin reserve balance is not sufficient for transaction. Has: %s, required %s", coin.ReserveBalance().String(), commissionInBaseCoin.String()), + }) + } + + commission = formula.CalculateSaleAmount(coin.Volume(), coin.ReserveBalance(), coin.Data().Crr, commissionInBaseCoin) + } + if coinToSellSymbol == types.GetBaseCoin() { coin := cState.GetStateCoin(coinToBuySymbol).Data() result = formula.CalculatePurchaseAmount(coin.Volume, coin.ReserveBalance, coin.Crr, valueToBuy) @@ -71,7 +96,10 @@ func EstimateCoinBuy(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(Response{ - Code: 0, - Result: result.String(), + Code: 0, + Result: EstimateCoinBuyResponse{ + WillPay: result.String(), + Commission: commission.String(), + }, }) } diff --git a/api/estimate_coin_sell.go b/api/estimate_coin_sell.go index 75d2ce8df..728eeb344 100644 --- a/api/estimate_coin_sell.go +++ b/api/estimate_coin_sell.go @@ -4,12 +4,19 @@ import ( "encoding/json" "fmt" "github.com/MinterTeam/minter-go-node/core/code" + "github.com/MinterTeam/minter-go-node/core/commissions" + "github.com/MinterTeam/minter-go-node/core/transaction" "github.com/MinterTeam/minter-go-node/core/types" "github.com/MinterTeam/minter-go-node/formula" "math/big" "net/http" ) +type EstimateCoinSellResponse struct { + WillGet string + Commission string +} + func EstimateCoinSell(w http.ResponseWriter, r *http.Request) { cState := GetStateForRequest(r) @@ -56,6 +63,24 @@ func EstimateCoinSell(w http.ResponseWriter, r *http.Request) { return } + commissionInBaseCoin := big.NewInt(commissions.ConvertTx) + commissionInBaseCoin.Mul(commissionInBaseCoin, transaction.CommissionMultiplier) + commission := big.NewInt(0).Set(commissionInBaseCoin) + + if coinToSellSymbol != types.GetBaseCoin() { + coin := cState.GetStateCoin(coinToSellSymbol) + + if coin.ReserveBalance().Cmp(commissionInBaseCoin) < 0 { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(Response{ + Code: 1, + Log: fmt.Sprintf("Coin reserve balance is not sufficient for transaction. Has: %s, required %s", coin.ReserveBalance().String(), commissionInBaseCoin.String()), + }) + } + + commission = formula.CalculateSaleAmount(coin.Volume(), coin.ReserveBalance(), coin.Data().Crr, commissionInBaseCoin) + } + if coinToSellSymbol == types.GetBaseCoin() { coin := cState.GetStateCoin(coinToBuySymbol).Data() result = formula.CalculatePurchaseReturn(coin.Volume, coin.ReserveBalance, coin.Crr, valueToSell) @@ -71,7 +96,10 @@ func EstimateCoinSell(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(Response{ - Code: 0, - Result: result.String(), + Code: 0, + Result: EstimateCoinSellResponse{ + WillGet: result.String(), + Commission: commission.String(), + }, }) } diff --git a/core/minter/minter.go b/core/minter/minter.go index c665f70a3..c34e039c6 100644 --- a/core/minter/minter.go +++ b/core/minter/minter.go @@ -346,3 +346,7 @@ func (app *Blockchain) GetStateForHeight(height int) (*state.StateDB, error) { func (app *Blockchain) Height() uint64 { return app.height } + +func GetBlockchain() *Blockchain { + return blockchain +} diff --git a/core/state/balance_watcher.go b/core/state/balance_watcher.go index 00055fc57..af505357a 100644 --- a/core/state/balance_watcher.go +++ b/core/state/balance_watcher.go @@ -11,23 +11,30 @@ var ( ) type BalanceChangeStruct struct { - Address types.Address - Coin types.CoinSymbol - Balance *big.Int + Address types.Address + Coin types.CoinSymbol + Balance *big.Int + BalanceInBasecoin *big.Int } func (s BalanceChangeStruct) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - Address types.Address `json:"address"` - Coin string `json:"coin"` - Balance string `json:"balance"` + Address types.Address `json:"address"` + Coin string `json:"coin"` + Balance string `json:"balance"` + BalanceInBasecoin string `json:"balance_in_basecoin"` }{ - Address: s.Address, - Coin: s.Coin.String(), - Balance: s.Balance.String(), + Address: s.Address, + Coin: s.Coin.String(), + Balance: s.Balance.String(), + BalanceInBasecoin: s.BalanceInBasecoin.String(), }) } func EmitBalanceChange(address types.Address, coin types.CoinSymbol, balance *big.Int) { - BalanceChangeChan <- BalanceChangeStruct{Address: address, Coin: coin, Balance: balance} + BalanceChangeChan <- BalanceChangeStruct{ + Address: address, + Coin: coin, + Balance: balance, + } } diff --git a/core/transaction/sell_all_coin.go b/core/transaction/sell_all_coin.go index 5e5e577fc..9c3b73ecc 100644 --- a/core/transaction/sell_all_coin.go +++ b/core/transaction/sell_all_coin.go @@ -20,11 +20,11 @@ type SellAllCoinData struct { func (data SellAllCoinData) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - CoinToSell types.CoinSymbol `json:"coin_to_sell,string"` - CoinToBuy types.CoinSymbol `json:"coin_to_buy,string"` + CoinToSell types.CoinSymbol `json:"coin_to_sell,string"` + CoinToBuy types.CoinSymbol `json:"coin_to_buy,string"` }{ - CoinToSell: data.CoinToSell, - CoinToBuy: data.CoinToBuy, + CoinToSell: data.CoinToSell, + CoinToBuy: data.CoinToBuy, }) } From ac4b3fb72a884f253381fc2960f1277a3d2ffcbe Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 12:34:50 +0300 Subject: [PATCH 52/78] update api --- api/api.go | 8 ++++++++ api/block.go | 7 ++++--- api/status.go | 15 ++++++++------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/api/api.go b/api/api.go index 52a71a12c..a200ea227 100644 --- a/api/api.go +++ b/api/api.go @@ -10,12 +10,20 @@ import ( "github.com/MinterTeam/minter-go-node/cmd/utils" "github.com/MinterTeam/minter-go-node/core/minter" "github.com/MinterTeam/minter-go-node/core/state" + "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/node" rpc "github.com/tendermint/tendermint/rpc/client" "strconv" "time" ) +var cdc = amino.NewCodec() + +func init() { + crypto.RegisterAmino(cdc) +} + var ( blockchain *minter.Blockchain client *rpc.Local diff --git a/api/block.go b/api/block.go index 87cef6a75..e711293e2 100644 --- a/api/block.go +++ b/api/block.go @@ -6,7 +6,6 @@ import ( "github.com/MinterTeam/minter-go-node/core/transaction" "github.com/gorilla/mux" "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/types" "math/big" "net/http" "strconv" @@ -20,7 +19,7 @@ type BlockResponse struct { NumTxs int64 `json:"num_txs"` TotalTxs int64 `json:"total_txs"` Transactions []BlockTransactionResponse `json:"transactions"` - Precommits []*types.Vote `json:"precommits"` + Precommits json.RawMessage `json:"precommits"` } type BlockTransactionResponse struct { @@ -87,13 +86,15 @@ func Block(w http.ResponseWriter, r *http.Request) { } } + precommits, _ := cdc.MarshalJSON(block.Block.LastCommit.Precommits) + response := BlockResponse{ Hash: block.Block.Hash(), Height: block.Block.Height, Time: block.Block.Time, NumTxs: block.Block.NumTxs, TotalTxs: block.Block.TotalTxs, - Precommits: block.Block.LastCommit.Precommits, + Precommits: json.RawMessage(precommits), Transactions: txs, } diff --git a/api/status.go b/api/status.go index 72708e49d..56f1d147a 100644 --- a/api/status.go +++ b/api/status.go @@ -3,17 +3,16 @@ package api import ( "encoding/json" "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/rpc/core/types" "net/http" "time" ) type StatusResponse struct { - LatestBlockHash common.HexBytes `json:"latest_block_hash"` - LatestAppHash common.HexBytes `json:"latest_app_hash"` - LatestBlockHeight int64 `json:"latest_block_height"` - LatestBlockTime time.Time `json:"latest_block_time"` - TmStatus *core_types.ResultStatus `json:"tm_status"` + LatestBlockHash common.HexBytes `json:"latest_block_hash"` + LatestAppHash common.HexBytes `json:"latest_app_hash"` + LatestBlockHeight int64 `json:"latest_block_height"` + LatestBlockTime time.Time `json:"latest_block_time"` + TmStatus json.RawMessage `json:"tm_status"` } func Status(w http.ResponseWriter, r *http.Request) { @@ -32,6 +31,8 @@ func Status(w http.ResponseWriter, r *http.Request) { return } + tmStatus, _ := cdc.MarshalJSON(result) + json.NewEncoder(w).Encode(Response{ Code: 0, Result: StatusResponse{ @@ -39,7 +40,7 @@ func Status(w http.ResponseWriter, r *http.Request) { LatestAppHash: common.HexBytes(result.SyncInfo.LatestAppHash), LatestBlockHeight: result.SyncInfo.LatestBlockHeight, LatestBlockTime: result.SyncInfo.LatestBlockTime, - TmStatus: result, + TmStatus: json.RawMessage(tmStatus), }, }) } From c7f317f3d55b6006549fb3dcf0dc76932065d1b6 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 12:56:30 +0300 Subject: [PATCH 53/78] fix gui --- cmd/minter/main-packr.go | 2 +- gui/index.html | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cmd/minter/main-packr.go b/cmd/minter/main-packr.go index 316f2e6cc..02a14dda4 100644 --- a/cmd/minter/main-packr.go +++ b/cmd/minter/main-packr.go @@ -7,5 +7,5 @@ import "github.com/gobuffalo/packr" // You can use the "packr clean" command to clean up this, // and any other packr generated files. func init() { - packr.PackJSONBytes("./../../gui", "index.html", "\"PGh0bWw+CjxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0iVVRGLTgiPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiCiAgICAgICAgICBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIHVzZXItc2NhbGFibGU9bm8sIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCwgbWluaW11bS1zY2FsZT0xLjAiPgogICAgPG1ldGEgaHR0cC1lcXVpdj0iWC1VQS1Db21wYXRpYmxlIiBjb250ZW50PSJpZT1lZGdlIj4KICAgIDx0aXRsZT5NaW50ZXIgTm9kZSBHVUk8L3RpdGxlPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL3N0YWNrcGF0aC5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC80LjEuMC9jc3MvYm9vdHN0cmFwLm1pbi5jc3MiCiAgICAgICAgICBpbnRlZ3JpdHk9InNoYTM4NC05Z1ZRNGRZRnd3V1NqSURabkxFV254Q2plU1dGcGhKaXdHUFhyMWpkZEloT2VnaXUxRndPNXFSR3ZGWE9kSlo0IiBjcm9zc29yaWdpbj0iYW5vbnltb3VzIj4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly91c2UuZm9udGF3ZXNvbWUuY29tL3JlbGVhc2VzL3Y1LjEuMS9jc3MvYWxsLmNzcyIgaW50ZWdyaXR5PSJzaGEzODQtTzh3aFMzZmhHMk9uQTVLYXMwWTlsM2NmcG1ZamFwakkwRTR0aGVINGl1TUQrcExoYmY2SkkwaklNZlljSzN5WiIgY3Jvc3NvcmlnaW49ImFub255bW91cyI+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS92dWUiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2F4aW9zLzAuMTguMC9heGlvcy5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2NyeXB0by1qcy8zLjEuOS0xL2NyeXB0by1qcy5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL3R3ZWV0bmFjbC8xLjAuMC9uYWNsLm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c3R5bGU+CgogICAgICAgIC5jYXJkIHsKICAgICAgICAgICAgbWFyZ2luLWJvdHRvbTogMjBweDsKICAgICAgICB9CgogICAgICAgIGh0bWwsYm9keSwuYm9keSB7CiAgICAgICAgICAgIGhlaWdodDogMTAwJTsKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgLmJvZHkgewogICAgICAgICAgICBwYWRkaW5nLXRvcDogMTVweDsKICAgICAgICB9CgogICAgICAgIC50YWJsZSB7CiAgICAgICAgICAgIG1hcmdpbi1ib3R0b206IDA7CiAgICAgICAgICAgIHRhYmxlLWxheW91dDogZml4ZWQ7CiAgICAgICAgfQoKICAgICAgICAuY2FyZC1oZWFkZXIgewogICAgICAgICAgICBmb250LXdlaWdodDogYm9sZDsKICAgICAgICB9CgogICAgICAgIC5jYXJkLWhlYWRlciB7CiAgICAgICAgICAgIHBhZGRpbmctbGVmdDogMTJweDsKICAgICAgICB9CgogICAgICAgIC5oIHsKICAgICAgICAgICAgd2lkdGg6IDIwMHB4OwogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjNmM2YzOwogICAgICAgICAgICBib3JkZXItcmlnaHQ6IDFweCBzb2xpZCAjY2NjOwogICAgICAgIH0KCiAgICAgICAgLmJnLXN1Y2Nlc3MsIC5iZy1kYW5nZXIgewogICAgICAgICAgICBjb2xvcjogd2hpdGU7CiAgICAgICAgfQoKICAgICAgICAuYmctZGFuZ2VyIHsKICAgICAgICAgICAgYm9yZGVyLWNvbG9yOiAjZGMzNTQ1ICFpbXBvcnRhbnQ7CiAgICAgICAgfQoKICAgICAgICAuYmctc3VjY2VzcyB7CiAgICAgICAgICAgIGJvcmRlci1jb2xvcjogIzI4YTc0NSAhaW1wb3J0YW50OwogICAgICAgIH0KICAgIDwvc3R5bGU+CjwvaGVhZD4KPGJvZHkgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICMzNDNhNDAxYSI+CjxkaXYgaWQ9ImFwcCI+CiAgICA8bmF2IGNsYXNzPSJuYXZiYXIgbmF2YmFyLWV4cGFuZC1sZyBuYXZiYXItZGFyayBiZy1kYXJrIj4KICAgICAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIiPgogICAgICAgICAgICA8c3BhbiBjbGFzcz0ibmF2YmFyLWJyYW5kIG1iLTAgaDEiPjxpIGNsYXNzPSJmYXMgZmEtdGVybWluYWwiPjwvaT4gJm5ic3A7IE1pbnRlciBGdWxsIE5vZGUgU3RhdHVzPC9zcGFuPgogICAgICAgIDwvZGl2PgogICAgPC9uYXY+CiAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIgYm9keSIgdi1pZj0iZXJyb3IiPgogICAgICAgIDxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWRhbmdlciIgcm9sZT0iYWxlcnQiPgogICAgICAgICAgICA8aDQgY2xhc3M9ImFsZXJ0LWhlYWRpbmciPkVycm9yIHdoaWxlIGNvbm5lY3RpbmcgdG8gbG9jYWwgbm9kZTwvaDQ+CiAgICAgICAgICAgIDxwIGNsYXNzPSJtYi0wIj57eyBlcnJvciB9fTwvcD4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgogICAgPGRpdiBjbGFzcz0iY29udGFpbmVyIGJvZHkgYmctd2hpdGUiIHYtaWY9InN0YXR1cyAmJiAhZXJyb3IiPgogICAgICAgIDxkaXYgY2xhc3M9InJvdyI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbCI+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIE5vZGUgSW5mbwogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgIDx0YWJsZSBjbGFzcz0idGFibGUgY2FyZC1ib2R5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHRib2R5PgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPk1vbmlrZXI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubW9uaWtlciB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+Tm9kZSBJRDwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgc3RhdHVzLm5vZGVfaW5mby5pZCB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TGlzdGVuIEFkZHI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubGlzdGVuX2FkZHIgfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPk5ldHdvcmsgSUQ8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubmV0d29yayB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+VGVuZGVybWludCBWZXJzaW9uPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD57eyBzdGF0dXMubm9kZV9pbmZvLnZlcnNpb24gfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8L3Rib2R5PgogICAgICAgICAgICAgICAgICAgIDwvdGFibGU+CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQiIHYtaWY9Im5ldF9pbmZvIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIE5ldCBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+SXMgTGlzdGVuaW5nPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCA6Y2xhc3M9InsnYmctc3VjY2Vzcyc6IG5ldF9pbmZvLmxpc3RlbmluZywgJ2JnLWRhbmdlcic6ICFuZXRfaW5mby5saXN0ZW5pbmd9Ij57eyBuZXRfaW5mby5saXN0ZW5pbmcgfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPkNvbm5lY3RlZCBQZWVyczwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgbmV0X2luZm8ubl9wZWVycyB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICAgICAgPC90YWJsZT4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iY29sIj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQiPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQtaGVhZGVyIj4KICAgICAgICAgICAgICAgICAgICAgICAgU3luY2luZyBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+SXMgU3luY2VkPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCA6Y2xhc3M9InsnYmctc3VjY2Vzcyc6IHN0YXR1cy5zeW5jX2luZm8uY2F0Y2hpbmdfdXAgPT0gZmFsc2UsICdiZy1kYW5nZXInOiBzdGF0dXMuc3luY19pbmZvLmNhdGNoaW5nX3VwID09IHRydWV9Ij57eyBzdGF0dXMuc3luY19pbmZvLmNhdGNoaW5nX3VwID09IGZhbHNlID8gJ3RydWUnIDogJ2ZhbHNlJyB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TGF0ZXN0IEJsb2NrPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAje3sgc3RhdHVzLnN5bmNfaW5mby5sYXRlc3RfYmxvY2tfaGVpZ2h0IH19IDxzcGFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPSJ0ZXh0LW11dGVkIj5hdCB7eyBzdGF0dXMuc3luY19pbmZvLmxhdGVzdF9ibG9ja190aW1lIH19PC9zcGFuPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPC90Ym9keT4KICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIFZhbGlkYXRvciBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5QdWJsaWMgS2V5PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5NcHt7IGJ5dGVzVG9IZXgoc3RhdHVzLnZhbGlkYXRvcl9pbmZvLnB1Yl9rZXkpIH19PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPHRyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPlZvdGluZyBQb3dlcjwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgbmljZU51bShzdGF0dXMudmFsaWRhdG9yX2luZm8udm90aW5nX3Bvd2VyKSB9fSA8c3BhbiBjbGFzcz0idGV4dC1tdXRlZCI+b2YgMTAwLDAwMCwwMDA8L3NwYW4+PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPC90Ym9keT4KICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgIDwvZGl2PgogICAgPC9kaXY+CjwvZGl2Pgo8c2NyaXB0PgogICAgbmV3IFZ1ZSh7CiAgICAgICAgZWw6ICcjYXBwJywKICAgICAgICBkYXRhOiB7CiAgICAgICAgICAgIHN0YXR1czogbnVsbCwKICAgICAgICAgICAgbmV0X2luZm86IG51bGwsCiAgICAgICAgICAgIGVycm9yOiBudWxsCiAgICAgICAgfSwKICAgICAgICBtb3VudGVkKCkgewogICAgICAgICAgICB0aGlzLnJlZnJlc2goKQoKICAgICAgICAgICAgc2V0SW50ZXJ2YWwodGhpcy5yZWZyZXNoLCAxMDAwKQogICAgICAgIH0sCiAgICAgICAgbWV0aG9kczogewogICAgICAgICAgICBuaWNlTnVtKG51bSkgewogICAgICAgICAgICAgICAgcmV0dXJuIG51bS50b1N0cmluZygpLnJlcGxhY2UoL1xCKD89KFxkezN9KSsoPyFcZCkpL2csICIsIikKICAgICAgICAgICAgfSwKICAgICAgICAgICAgYnl0ZXNUb0hleChieXRlcykgewogICAgICAgICAgICAgICAgcmV0dXJuIEFycmF5LmZyb20oYnl0ZXMsIGZ1bmN0aW9uIChieXRlKSB7CiAgICAgICAgICAgICAgICAgICAgcmV0dXJuICgnMCcgKyAoYnl0ZSAmIDB4RkYpLnRvU3RyaW5nKDE2KSkuc2xpY2UoLTIpOwogICAgICAgICAgICAgICAgfSkuam9pbignJykKICAgICAgICAgICAgfSwKICAgICAgICAgICAgcmVmcmVzaCgpIHsKICAgICAgICAgICAgICAgIGF4aW9zLmdldCgiLy8iICsgd2luZG93LmxvY2F0aW9uLmhvc3RuYW1lICsgJzo4ODQxL2FwaS9zdGF0dXMnKS50aGVuKGZ1bmN0aW9uIChkYXRhKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5zdGF0dXMgPSBkYXRhLmRhdGEucmVzdWx0LnRtX3N0YXR1cwogICAgICAgICAgICAgICAgICAgIHRoaXMuZXJyb3IgPSBudWxsCiAgICAgICAgICAgICAgICB9LmJpbmQodGhpcykpLmNhdGNoKGZ1bmN0aW9uIChyZWFzb24pIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmVycm9yID0gcmVhc29uLnRvU3RyaW5nKCk7CiAgICAgICAgICAgICAgICB9LmJpbmQodGhpcykpCgogICAgICAgICAgICAgICAgYXhpb3MuZ2V0KCIvLyIgKyB3aW5kb3cubG9jYXRpb24uaG9zdG5hbWUgKyAnOjg4NDEvYXBpL25ldF9pbmZvJykudGhlbihmdW5jdGlvbiAoZGF0YSkgewogICAgICAgICAgICAgICAgICAgIHRoaXMubmV0X2luZm8gPSBkYXRhLmRhdGEucmVzdWx0CiAgICAgICAgICAgICAgICAgICAgdGhpcy5lcnJvciA9IG51bGwKICAgICAgICAgICAgICAgIH0uYmluZCh0aGlzKSkuY2F0Y2goZnVuY3Rpb24gKHJlYXNvbikgewogICAgICAgICAgICAgICAgICAgIHRoaXMuZXJyb3IgPSByZWFzb24udG9TdHJpbmcoKTsKICAgICAgICAgICAgICAgIH0uYmluZCh0aGlzKSkKICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0pCjwvc2NyaXB0Pgo8L2JvZHk+CjwvaHRtbD4=\"") + packr.PackJSONBytes("./../../gui", "index.html", "\"PGh0bWw+CjxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0iVVRGLTgiPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiCiAgICAgICAgICBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIHVzZXItc2NhbGFibGU9bm8sIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCwgbWluaW11bS1zY2FsZT0xLjAiPgogICAgPG1ldGEgaHR0cC1lcXVpdj0iWC1VQS1Db21wYXRpYmxlIiBjb250ZW50PSJpZT1lZGdlIj4KICAgIDx0aXRsZT5NaW50ZXIgTm9kZSBHVUk8L3RpdGxlPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL3N0YWNrcGF0aC5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC80LjEuMC9jc3MvYm9vdHN0cmFwLm1pbi5jc3MiCiAgICAgICAgICBpbnRlZ3JpdHk9InNoYTM4NC05Z1ZRNGRZRnd3V1NqSURabkxFV254Q2plU1dGcGhKaXdHUFhyMWpkZEloT2VnaXUxRndPNXFSR3ZGWE9kSlo0IiBjcm9zc29yaWdpbj0iYW5vbnltb3VzIj4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly91c2UuZm9udGF3ZXNvbWUuY29tL3JlbGVhc2VzL3Y1LjEuMS9jc3MvYWxsLmNzcyIgaW50ZWdyaXR5PSJzaGEzODQtTzh3aFMzZmhHMk9uQTVLYXMwWTlsM2NmcG1ZamFwakkwRTR0aGVINGl1TUQrcExoYmY2SkkwaklNZlljSzN5WiIgY3Jvc3NvcmlnaW49ImFub255bW91cyI+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS92dWUiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2F4aW9zLzAuMTguMC9heGlvcy5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2NyeXB0by1qcy8zLjEuOS0xL2NyeXB0by1qcy5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL3R3ZWV0bmFjbC8xLjAuMC9uYWNsLm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c3R5bGU+CgogICAgICAgIC5jYXJkIHsKICAgICAgICAgICAgbWFyZ2luLWJvdHRvbTogMjBweDsKICAgICAgICB9CgogICAgICAgIGh0bWwsYm9keSwuYm9keSB7CiAgICAgICAgICAgIGhlaWdodDogMTAwJTsKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgLmJvZHkgewogICAgICAgICAgICBwYWRkaW5nLXRvcDogMTVweDsKICAgICAgICB9CgogICAgICAgIC50YWJsZSB7CiAgICAgICAgICAgIG1hcmdpbi1ib3R0b206IDA7CiAgICAgICAgICAgIHRhYmxlLWxheW91dDogZml4ZWQ7CiAgICAgICAgfQoKICAgICAgICAuY2FyZC1oZWFkZXIgewogICAgICAgICAgICBmb250LXdlaWdodDogYm9sZDsKICAgICAgICB9CgogICAgICAgIC5jYXJkLWhlYWRlciB7CiAgICAgICAgICAgIHBhZGRpbmctbGVmdDogMTJweDsKICAgICAgICB9CgogICAgICAgIC5oIHsKICAgICAgICAgICAgd2lkdGg6IDIwMHB4OwogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjNmM2YzOwogICAgICAgICAgICBib3JkZXItcmlnaHQ6IDFweCBzb2xpZCAjY2NjOwogICAgICAgIH0KCiAgICAgICAgLmJnLXN1Y2Nlc3MsIC5iZy1kYW5nZXIgewogICAgICAgICAgICBjb2xvcjogd2hpdGU7CiAgICAgICAgfQoKICAgICAgICAuYmctZGFuZ2VyIHsKICAgICAgICAgICAgYm9yZGVyLWNvbG9yOiAjZGMzNTQ1ICFpbXBvcnRhbnQ7CiAgICAgICAgfQoKICAgICAgICAuYmctc3VjY2VzcyB7CiAgICAgICAgICAgIGJvcmRlci1jb2xvcjogIzI4YTc0NSAhaW1wb3J0YW50OwogICAgICAgIH0KICAgIDwvc3R5bGU+CjwvaGVhZD4KPGJvZHkgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICMzNDNhNDAxYSI+CjxkaXYgaWQ9ImFwcCI+CiAgICA8bmF2IGNsYXNzPSJuYXZiYXIgbmF2YmFyLWV4cGFuZC1sZyBuYXZiYXItZGFyayBiZy1kYXJrIj4KICAgICAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIiPgogICAgICAgICAgICA8c3BhbiBjbGFzcz0ibmF2YmFyLWJyYW5kIG1iLTAgaDEiPjxpIGNsYXNzPSJmYXMgZmEtdGVybWluYWwiPjwvaT4gJm5ic3A7IE1pbnRlciBGdWxsIE5vZGUgU3RhdHVzPC9zcGFuPgogICAgICAgIDwvZGl2PgogICAgPC9uYXY+CiAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIgYm9keSIgdi1pZj0iZXJyb3IiPgogICAgICAgIDxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWRhbmdlciIgcm9sZT0iYWxlcnQiPgogICAgICAgICAgICA8aDQgY2xhc3M9ImFsZXJ0LWhlYWRpbmciPkVycm9yIHdoaWxlIGNvbm5lY3RpbmcgdG8gbG9jYWwgbm9kZTwvaDQ+CiAgICAgICAgICAgIDxwIGNsYXNzPSJtYi0wIj57eyBlcnJvciB9fTwvcD4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgogICAgPGRpdiBjbGFzcz0iY29udGFpbmVyIGJvZHkgYmctd2hpdGUiIHYtaWY9InN0YXR1cyAmJiAhZXJyb3IiPgogICAgICAgIDxkaXYgY2xhc3M9InJvdyI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbCI+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIE5vZGUgSW5mbwogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgIDx0YWJsZSBjbGFzcz0idGFibGUgY2FyZC1ib2R5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHRib2R5PgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPk1vbmlrZXI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubW9uaWtlciB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+Tm9kZSBJRDwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgc3RhdHVzLm5vZGVfaW5mby5pZCB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TGlzdGVuIEFkZHI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubGlzdGVuX2FkZHIgfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPk5ldHdvcmsgSUQ8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubmV0d29yayB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+VGVuZGVybWludCBWZXJzaW9uPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD57eyBzdGF0dXMubm9kZV9pbmZvLnZlcnNpb24gfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8L3Rib2R5PgogICAgICAgICAgICAgICAgICAgIDwvdGFibGU+CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQiIHYtaWY9Im5ldF9pbmZvIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIE5ldCBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+SXMgTGlzdGVuaW5nPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCA6Y2xhc3M9InsnYmctc3VjY2Vzcyc6IG5ldF9pbmZvLmxpc3RlbmluZywgJ2JnLWRhbmdlcic6ICFuZXRfaW5mby5saXN0ZW5pbmd9Ij57eyBuZXRfaW5mby5saXN0ZW5pbmcgfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPkNvbm5lY3RlZCBQZWVyczwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgbmV0X2luZm8ubl9wZWVycyB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICAgICAgPC90YWJsZT4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iY29sIj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQiPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQtaGVhZGVyIj4KICAgICAgICAgICAgICAgICAgICAgICAgU3luY2luZyBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+SXMgU3luY2VkPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCA6Y2xhc3M9InsnYmctc3VjY2Vzcyc6IHN0YXR1cy5zeW5jX2luZm8uY2F0Y2hpbmdfdXAgPT0gZmFsc2UsICdiZy1kYW5nZXInOiBzdGF0dXMuc3luY19pbmZvLmNhdGNoaW5nX3VwID09IHRydWV9Ij57eyBzdGF0dXMuc3luY19pbmZvLmNhdGNoaW5nX3VwID09IGZhbHNlID8gJ3RydWUnIDogJ2ZhbHNlJyB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TGF0ZXN0IEJsb2NrPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAje3sgc3RhdHVzLnN5bmNfaW5mby5sYXRlc3RfYmxvY2tfaGVpZ2h0IH19IDxzcGFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPSJ0ZXh0LW11dGVkIj5hdCB7eyBzdGF0dXMuc3luY19pbmZvLmxhdGVzdF9ibG9ja190aW1lIH19PC9zcGFuPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPC90Ym9keT4KICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIFZhbGlkYXRvciBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5QdWJsaWMgS2V5PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5NcHt7IGJhc2U2NFRvSGV4KHN0YXR1cy52YWxpZGF0b3JfaW5mby5wdWJfa2V5LnZhbHVlKSB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5Wb3RpbmcgUG93ZXI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IG5pY2VOdW0oc3RhdHVzLnZhbGlkYXRvcl9pbmZvLnZvdGluZ19wb3dlcikgfX0gPHNwYW4gY2xhc3M9InRleHQtbXV0ZWQiPm9mIDEwMCwwMDAsMDAwPC9zcGFuPjwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICAgICAgPC90YWJsZT4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2Pgo8L2Rpdj4KPHNjcmlwdD4KICAgIG5ldyBWdWUoewogICAgICAgIGVsOiAnI2FwcCcsCiAgICAgICAgZGF0YTogewogICAgICAgICAgICBzdGF0dXM6IG51bGwsCiAgICAgICAgICAgIG5ldF9pbmZvOiBudWxsLAogICAgICAgICAgICBlcnJvcjogbnVsbAogICAgICAgIH0sCiAgICAgICAgbW91bnRlZCgpIHsKICAgICAgICAgICAgdGhpcy5yZWZyZXNoKCkKCiAgICAgICAgICAgIHNldEludGVydmFsKHRoaXMucmVmcmVzaCwgMTAwMCkKICAgICAgICB9LAogICAgICAgIG1ldGhvZHM6IHsKICAgICAgICAgICAgbmljZU51bShudW0pIHsKICAgICAgICAgICAgICAgIHJldHVybiBudW0udG9TdHJpbmcoKS5yZXBsYWNlKC9cQig/PShcZHszfSkrKD8hXGQpKS9nLCAiLCIpCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIGJhc2U2NFRvSGV4KGJhc2U2NCkgewogICAgICAgICAgICAgICAgcmV0dXJuIENyeXB0b0pTLmVuYy5CYXNlNjQucGFyc2UoYmFzZTY0KS50b1N0cmluZygpCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIHJlZnJlc2goKSB7CiAgICAgICAgICAgICAgICBheGlvcy5nZXQoIi8vIiArIHdpbmRvdy5sb2NhdGlvbi5ob3N0bmFtZSArICc6ODg0MS9hcGkvc3RhdHVzJykudGhlbihmdW5jdGlvbiAoZGF0YSkgewogICAgICAgICAgICAgICAgICAgIHRoaXMuc3RhdHVzID0gZGF0YS5kYXRhLnJlc3VsdC50bV9zdGF0dXMKICAgICAgICAgICAgICAgICAgICB0aGlzLmVycm9yID0gbnVsbAogICAgICAgICAgICAgICAgfS5iaW5kKHRoaXMpKS5jYXRjaChmdW5jdGlvbiAocmVhc29uKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5lcnJvciA9IHJlYXNvbi50b1N0cmluZygpOwogICAgICAgICAgICAgICAgfS5iaW5kKHRoaXMpKQoKICAgICAgICAgICAgICAgIGF4aW9zLmdldCgiLy8iICsgd2luZG93LmxvY2F0aW9uLmhvc3RuYW1lICsgJzo4ODQxL2FwaS9uZXRfaW5mbycpLnRoZW4oZnVuY3Rpb24gKGRhdGEpIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLm5ldF9pbmZvID0gZGF0YS5kYXRhLnJlc3VsdAogICAgICAgICAgICAgICAgICAgIHRoaXMuZXJyb3IgPSBudWxsCiAgICAgICAgICAgICAgICB9LmJpbmQodGhpcykpLmNhdGNoKGZ1bmN0aW9uIChyZWFzb24pIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmVycm9yID0gcmVhc29uLnRvU3RyaW5nKCk7CiAgICAgICAgICAgICAgICB9LmJpbmQodGhpcykpCiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9KQo8L3NjcmlwdD4KPC9ib2R5Pgo8L2h0bWw+\"") } diff --git a/gui/index.html b/gui/index.html index 91261ef80..8ce59ac3f 100644 --- a/gui/index.html +++ b/gui/index.html @@ -150,7 +150,7 @@

Error while connecting to local node

Public Key - Mp{{ bytesToHex(status.validator_info.pub_key) }} + Mp{{ base64ToHex(status.validator_info.pub_key.value) }} Voting Power @@ -180,10 +180,8 @@

Error while connecting to local node

niceNum(num) { return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") }, - bytesToHex(bytes) { - return Array.from(bytes, function (byte) { - return ('0' + (byte & 0xFF).toString(16)).slice(-2); - }).join('') + base64ToHex(base64) { + return CryptoJS.enc.Base64.parse(base64).toString() }, refresh() { axios.get("//" + window.location.hostname + ':8841/api/status').then(function (data) { From bab69a9785764f1576713dcaae9a24fb498ccfe5 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 12:56:43 +0300 Subject: [PATCH 54/78] remove unused codes --- core/code/code.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/code/code.go b/core/code/code.go index e0c5df7cf..4a7906ede 100644 --- a/core/code/code.go +++ b/core/code/code.go @@ -6,11 +6,9 @@ const ( WrongNonce uint32 = 101 CoinNotExists uint32 = 102 CoinReserveNotSufficient uint32 = 103 - TooLongPayload uint32 = 104 TxTooLarge uint32 = 105 DecodeError uint32 = 106 InsufficientFunds uint32 = 107 - UnknownTransactionType uint32 = 108 TxPayloadTooLarge uint32 = 109 TxServiceDataTooLarge uint32 = 110 From e6255272057c22c921cbb32fc3f5970a0bf90eb6 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 13:04:25 +0300 Subject: [PATCH 55/78] update gui --- api/status.go | 3 +++ cmd/minter/main-packr.go | 2 +- gui/index.html | 10 ++++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/api/status.go b/api/status.go index 56f1d147a..86f740c45 100644 --- a/api/status.go +++ b/api/status.go @@ -2,12 +2,14 @@ package api import ( "encoding/json" + "github.com/MinterTeam/minter-go-node/version" "github.com/tendermint/tendermint/libs/common" "net/http" "time" ) type StatusResponse struct { + MinterVersion string `json:"version"` LatestBlockHash common.HexBytes `json:"latest_block_hash"` LatestAppHash common.HexBytes `json:"latest_app_hash"` LatestBlockHeight int64 `json:"latest_block_height"` @@ -36,6 +38,7 @@ func Status(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(Response{ Code: 0, Result: StatusResponse{ + MinterVersion: version.Version, LatestBlockHash: common.HexBytes(result.SyncInfo.LatestBlockHash), LatestAppHash: common.HexBytes(result.SyncInfo.LatestAppHash), LatestBlockHeight: result.SyncInfo.LatestBlockHeight, diff --git a/cmd/minter/main-packr.go b/cmd/minter/main-packr.go index 02a14dda4..03a201430 100644 --- a/cmd/minter/main-packr.go +++ b/cmd/minter/main-packr.go @@ -7,5 +7,5 @@ import "github.com/gobuffalo/packr" // You can use the "packr clean" command to clean up this, // and any other packr generated files. func init() { - packr.PackJSONBytes("./../../gui", "index.html", "\"PGh0bWw+CjxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0iVVRGLTgiPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiCiAgICAgICAgICBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIHVzZXItc2NhbGFibGU9bm8sIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCwgbWluaW11bS1zY2FsZT0xLjAiPgogICAgPG1ldGEgaHR0cC1lcXVpdj0iWC1VQS1Db21wYXRpYmxlIiBjb250ZW50PSJpZT1lZGdlIj4KICAgIDx0aXRsZT5NaW50ZXIgTm9kZSBHVUk8L3RpdGxlPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL3N0YWNrcGF0aC5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC80LjEuMC9jc3MvYm9vdHN0cmFwLm1pbi5jc3MiCiAgICAgICAgICBpbnRlZ3JpdHk9InNoYTM4NC05Z1ZRNGRZRnd3V1NqSURabkxFV254Q2plU1dGcGhKaXdHUFhyMWpkZEloT2VnaXUxRndPNXFSR3ZGWE9kSlo0IiBjcm9zc29yaWdpbj0iYW5vbnltb3VzIj4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly91c2UuZm9udGF3ZXNvbWUuY29tL3JlbGVhc2VzL3Y1LjEuMS9jc3MvYWxsLmNzcyIgaW50ZWdyaXR5PSJzaGEzODQtTzh3aFMzZmhHMk9uQTVLYXMwWTlsM2NmcG1ZamFwakkwRTR0aGVINGl1TUQrcExoYmY2SkkwaklNZlljSzN5WiIgY3Jvc3NvcmlnaW49ImFub255bW91cyI+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS92dWUiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2F4aW9zLzAuMTguMC9heGlvcy5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2NyeXB0by1qcy8zLjEuOS0xL2NyeXB0by1qcy5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL3R3ZWV0bmFjbC8xLjAuMC9uYWNsLm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c3R5bGU+CgogICAgICAgIC5jYXJkIHsKICAgICAgICAgICAgbWFyZ2luLWJvdHRvbTogMjBweDsKICAgICAgICB9CgogICAgICAgIGh0bWwsYm9keSwuYm9keSB7CiAgICAgICAgICAgIGhlaWdodDogMTAwJTsKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgLmJvZHkgewogICAgICAgICAgICBwYWRkaW5nLXRvcDogMTVweDsKICAgICAgICB9CgogICAgICAgIC50YWJsZSB7CiAgICAgICAgICAgIG1hcmdpbi1ib3R0b206IDA7CiAgICAgICAgICAgIHRhYmxlLWxheW91dDogZml4ZWQ7CiAgICAgICAgfQoKICAgICAgICAuY2FyZC1oZWFkZXIgewogICAgICAgICAgICBmb250LXdlaWdodDogYm9sZDsKICAgICAgICB9CgogICAgICAgIC5jYXJkLWhlYWRlciB7CiAgICAgICAgICAgIHBhZGRpbmctbGVmdDogMTJweDsKICAgICAgICB9CgogICAgICAgIC5oIHsKICAgICAgICAgICAgd2lkdGg6IDIwMHB4OwogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjNmM2YzOwogICAgICAgICAgICBib3JkZXItcmlnaHQ6IDFweCBzb2xpZCAjY2NjOwogICAgICAgIH0KCiAgICAgICAgLmJnLXN1Y2Nlc3MsIC5iZy1kYW5nZXIgewogICAgICAgICAgICBjb2xvcjogd2hpdGU7CiAgICAgICAgfQoKICAgICAgICAuYmctZGFuZ2VyIHsKICAgICAgICAgICAgYm9yZGVyLWNvbG9yOiAjZGMzNTQ1ICFpbXBvcnRhbnQ7CiAgICAgICAgfQoKICAgICAgICAuYmctc3VjY2VzcyB7CiAgICAgICAgICAgIGJvcmRlci1jb2xvcjogIzI4YTc0NSAhaW1wb3J0YW50OwogICAgICAgIH0KICAgIDwvc3R5bGU+CjwvaGVhZD4KPGJvZHkgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICMzNDNhNDAxYSI+CjxkaXYgaWQ9ImFwcCI+CiAgICA8bmF2IGNsYXNzPSJuYXZiYXIgbmF2YmFyLWV4cGFuZC1sZyBuYXZiYXItZGFyayBiZy1kYXJrIj4KICAgICAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIiPgogICAgICAgICAgICA8c3BhbiBjbGFzcz0ibmF2YmFyLWJyYW5kIG1iLTAgaDEiPjxpIGNsYXNzPSJmYXMgZmEtdGVybWluYWwiPjwvaT4gJm5ic3A7IE1pbnRlciBGdWxsIE5vZGUgU3RhdHVzPC9zcGFuPgogICAgICAgIDwvZGl2PgogICAgPC9uYXY+CiAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIgYm9keSIgdi1pZj0iZXJyb3IiPgogICAgICAgIDxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWRhbmdlciIgcm9sZT0iYWxlcnQiPgogICAgICAgICAgICA8aDQgY2xhc3M9ImFsZXJ0LWhlYWRpbmciPkVycm9yIHdoaWxlIGNvbm5lY3RpbmcgdG8gbG9jYWwgbm9kZTwvaDQ+CiAgICAgICAgICAgIDxwIGNsYXNzPSJtYi0wIj57eyBlcnJvciB9fTwvcD4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgogICAgPGRpdiBjbGFzcz0iY29udGFpbmVyIGJvZHkgYmctd2hpdGUiIHYtaWY9InN0YXR1cyAmJiAhZXJyb3IiPgogICAgICAgIDxkaXYgY2xhc3M9InJvdyI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbCI+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIE5vZGUgSW5mbwogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgIDx0YWJsZSBjbGFzcz0idGFibGUgY2FyZC1ib2R5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHRib2R5PgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPk1vbmlrZXI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubW9uaWtlciB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+Tm9kZSBJRDwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgc3RhdHVzLm5vZGVfaW5mby5pZCB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TGlzdGVuIEFkZHI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubGlzdGVuX2FkZHIgfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPk5ldHdvcmsgSUQ8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubmV0d29yayB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+VGVuZGVybWludCBWZXJzaW9uPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD57eyBzdGF0dXMubm9kZV9pbmZvLnZlcnNpb24gfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8L3Rib2R5PgogICAgICAgICAgICAgICAgICAgIDwvdGFibGU+CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQiIHYtaWY9Im5ldF9pbmZvIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIE5ldCBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+SXMgTGlzdGVuaW5nPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCA6Y2xhc3M9InsnYmctc3VjY2Vzcyc6IG5ldF9pbmZvLmxpc3RlbmluZywgJ2JnLWRhbmdlcic6ICFuZXRfaW5mby5saXN0ZW5pbmd9Ij57eyBuZXRfaW5mby5saXN0ZW5pbmcgfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPkNvbm5lY3RlZCBQZWVyczwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgbmV0X2luZm8ubl9wZWVycyB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICAgICAgPC90YWJsZT4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iY29sIj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQiPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQtaGVhZGVyIj4KICAgICAgICAgICAgICAgICAgICAgICAgU3luY2luZyBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+SXMgU3luY2VkPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCA6Y2xhc3M9InsnYmctc3VjY2Vzcyc6IHN0YXR1cy5zeW5jX2luZm8uY2F0Y2hpbmdfdXAgPT0gZmFsc2UsICdiZy1kYW5nZXInOiBzdGF0dXMuc3luY19pbmZvLmNhdGNoaW5nX3VwID09IHRydWV9Ij57eyBzdGF0dXMuc3luY19pbmZvLmNhdGNoaW5nX3VwID09IGZhbHNlID8gJ3RydWUnIDogJ2ZhbHNlJyB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TGF0ZXN0IEJsb2NrPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAje3sgc3RhdHVzLnN5bmNfaW5mby5sYXRlc3RfYmxvY2tfaGVpZ2h0IH19IDxzcGFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPSJ0ZXh0LW11dGVkIj5hdCB7eyBzdGF0dXMuc3luY19pbmZvLmxhdGVzdF9ibG9ja190aW1lIH19PC9zcGFuPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPC90Ym9keT4KICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIFZhbGlkYXRvciBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5QdWJsaWMgS2V5PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5NcHt7IGJhc2U2NFRvSGV4KHN0YXR1cy52YWxpZGF0b3JfaW5mby5wdWJfa2V5LnZhbHVlKSB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5Wb3RpbmcgUG93ZXI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IG5pY2VOdW0oc3RhdHVzLnZhbGlkYXRvcl9pbmZvLnZvdGluZ19wb3dlcikgfX0gPHNwYW4gY2xhc3M9InRleHQtbXV0ZWQiPm9mIDEwMCwwMDAsMDAwPC9zcGFuPjwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICAgICAgPC90YWJsZT4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2Pgo8L2Rpdj4KPHNjcmlwdD4KICAgIG5ldyBWdWUoewogICAgICAgIGVsOiAnI2FwcCcsCiAgICAgICAgZGF0YTogewogICAgICAgICAgICBzdGF0dXM6IG51bGwsCiAgICAgICAgICAgIG5ldF9pbmZvOiBudWxsLAogICAgICAgICAgICBlcnJvcjogbnVsbAogICAgICAgIH0sCiAgICAgICAgbW91bnRlZCgpIHsKICAgICAgICAgICAgdGhpcy5yZWZyZXNoKCkKCiAgICAgICAgICAgIHNldEludGVydmFsKHRoaXMucmVmcmVzaCwgMTAwMCkKICAgICAgICB9LAogICAgICAgIG1ldGhvZHM6IHsKICAgICAgICAgICAgbmljZU51bShudW0pIHsKICAgICAgICAgICAgICAgIHJldHVybiBudW0udG9TdHJpbmcoKS5yZXBsYWNlKC9cQig/PShcZHszfSkrKD8hXGQpKS9nLCAiLCIpCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIGJhc2U2NFRvSGV4KGJhc2U2NCkgewogICAgICAgICAgICAgICAgcmV0dXJuIENyeXB0b0pTLmVuYy5CYXNlNjQucGFyc2UoYmFzZTY0KS50b1N0cmluZygpCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIHJlZnJlc2goKSB7CiAgICAgICAgICAgICAgICBheGlvcy5nZXQoIi8vIiArIHdpbmRvdy5sb2NhdGlvbi5ob3N0bmFtZSArICc6ODg0MS9hcGkvc3RhdHVzJykudGhlbihmdW5jdGlvbiAoZGF0YSkgewogICAgICAgICAgICAgICAgICAgIHRoaXMuc3RhdHVzID0gZGF0YS5kYXRhLnJlc3VsdC50bV9zdGF0dXMKICAgICAgICAgICAgICAgICAgICB0aGlzLmVycm9yID0gbnVsbAogICAgICAgICAgICAgICAgfS5iaW5kKHRoaXMpKS5jYXRjaChmdW5jdGlvbiAocmVhc29uKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5lcnJvciA9IHJlYXNvbi50b1N0cmluZygpOwogICAgICAgICAgICAgICAgfS5iaW5kKHRoaXMpKQoKICAgICAgICAgICAgICAgIGF4aW9zLmdldCgiLy8iICsgd2luZG93LmxvY2F0aW9uLmhvc3RuYW1lICsgJzo4ODQxL2FwaS9uZXRfaW5mbycpLnRoZW4oZnVuY3Rpb24gKGRhdGEpIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLm5ldF9pbmZvID0gZGF0YS5kYXRhLnJlc3VsdAogICAgICAgICAgICAgICAgICAgIHRoaXMuZXJyb3IgPSBudWxsCiAgICAgICAgICAgICAgICB9LmJpbmQodGhpcykpLmNhdGNoKGZ1bmN0aW9uIChyZWFzb24pIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmVycm9yID0gcmVhc29uLnRvU3RyaW5nKCk7CiAgICAgICAgICAgICAgICB9LmJpbmQodGhpcykpCiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9KQo8L3NjcmlwdD4KPC9ib2R5Pgo8L2h0bWw+\"") + packr.PackJSONBytes("./../../gui", "index.html", "\"PGh0bWw+CjxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0iVVRGLTgiPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiCiAgICAgICAgICBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIHVzZXItc2NhbGFibGU9bm8sIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCwgbWluaW11bS1zY2FsZT0xLjAiPgogICAgPG1ldGEgaHR0cC1lcXVpdj0iWC1VQS1Db21wYXRpYmxlIiBjb250ZW50PSJpZT1lZGdlIj4KICAgIDx0aXRsZT5NaW50ZXIgTm9kZSBHVUk8L3RpdGxlPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL3N0YWNrcGF0aC5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC80LjEuMC9jc3MvYm9vdHN0cmFwLm1pbi5jc3MiCiAgICAgICAgICBpbnRlZ3JpdHk9InNoYTM4NC05Z1ZRNGRZRnd3V1NqSURabkxFV254Q2plU1dGcGhKaXdHUFhyMWpkZEloT2VnaXUxRndPNXFSR3ZGWE9kSlo0IiBjcm9zc29yaWdpbj0iYW5vbnltb3VzIj4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly91c2UuZm9udGF3ZXNvbWUuY29tL3JlbGVhc2VzL3Y1LjEuMS9jc3MvYWxsLmNzcyIgaW50ZWdyaXR5PSJzaGEzODQtTzh3aFMzZmhHMk9uQTVLYXMwWTlsM2NmcG1ZamFwakkwRTR0aGVINGl1TUQrcExoYmY2SkkwaklNZlljSzN5WiIgY3Jvc3NvcmlnaW49ImFub255bW91cyI+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS92dWUiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2F4aW9zLzAuMTguMC9heGlvcy5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2NyeXB0by1qcy8zLjEuOS0xL2NyeXB0by1qcy5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL3R3ZWV0bmFjbC8xLjAuMC9uYWNsLm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c3R5bGU+CgogICAgICAgIC5jYXJkIHsKICAgICAgICAgICAgbWFyZ2luLWJvdHRvbTogMjBweDsKICAgICAgICB9CgogICAgICAgIGh0bWwsYm9keSwuYm9keSB7CiAgICAgICAgICAgIGhlaWdodDogMTAwJTsKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgLmJvZHkgewogICAgICAgICAgICBwYWRkaW5nLXRvcDogMTVweDsKICAgICAgICB9CgogICAgICAgIC50YWJsZSB7CiAgICAgICAgICAgIG1hcmdpbi1ib3R0b206IDA7CiAgICAgICAgICAgIHRhYmxlLWxheW91dDogZml4ZWQ7CiAgICAgICAgfQoKICAgICAgICAuY2FyZC1oZWFkZXIgewogICAgICAgICAgICBmb250LXdlaWdodDogYm9sZDsKICAgICAgICB9CgogICAgICAgIC5jYXJkLWhlYWRlciB7CiAgICAgICAgICAgIHBhZGRpbmctbGVmdDogMTJweDsKICAgICAgICB9CgogICAgICAgIC5oIHsKICAgICAgICAgICAgd2lkdGg6IDIwMHB4OwogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjNmM2YzOwogICAgICAgICAgICBib3JkZXItcmlnaHQ6IDFweCBzb2xpZCAjY2NjOwogICAgICAgIH0KCiAgICAgICAgLmJnLXN1Y2Nlc3MsIC5iZy1kYW5nZXIgewogICAgICAgICAgICBjb2xvcjogd2hpdGU7CiAgICAgICAgfQoKICAgICAgICAuYmctZGFuZ2VyIHsKICAgICAgICAgICAgYm9yZGVyLWNvbG9yOiAjZGMzNTQ1ICFpbXBvcnRhbnQ7CiAgICAgICAgfQoKICAgICAgICAuYmctc3VjY2VzcyB7CiAgICAgICAgICAgIGJvcmRlci1jb2xvcjogIzI4YTc0NSAhaW1wb3J0YW50OwogICAgICAgIH0KICAgIDwvc3R5bGU+CjwvaGVhZD4KPGJvZHkgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICMzNDNhNDAxYSI+CjxkaXYgaWQ9ImFwcCI+CiAgICA8bmF2IGNsYXNzPSJuYXZiYXIgbmF2YmFyLWV4cGFuZC1sZyBuYXZiYXItZGFyayBiZy1kYXJrIj4KICAgICAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIiPgogICAgICAgICAgICA8c3BhbiBjbGFzcz0ibmF2YmFyLWJyYW5kIG1iLTAgaDEiPjxpIGNsYXNzPSJmYXMgZmEtdGVybWluYWwiPjwvaT4gJm5ic3A7IE1pbnRlciBGdWxsIE5vZGUgU3RhdHVzPC9zcGFuPgogICAgICAgIDwvZGl2PgogICAgPC9uYXY+CiAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIgYm9keSIgdi1pZj0iZXJyb3IiPgogICAgICAgIDxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWRhbmdlciIgcm9sZT0iYWxlcnQiPgogICAgICAgICAgICA8aDQgY2xhc3M9ImFsZXJ0LWhlYWRpbmciPkVycm9yIHdoaWxlIGNvbm5lY3RpbmcgdG8gbG9jYWwgbm9kZTwvaDQ+CiAgICAgICAgICAgIDxwIGNsYXNzPSJtYi0wIj57eyBlcnJvciB9fTwvcD4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgogICAgPGRpdiBjbGFzcz0iY29udGFpbmVyIGJvZHkgYmctd2hpdGUiIHYtaWY9InN0YXR1cyAmJiAhZXJyb3IiPgogICAgICAgIDxkaXYgY2xhc3M9InJvdyI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbCI+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIE5vZGUgSW5mbwogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgIDx0YWJsZSBjbGFzcz0idGFibGUgY2FyZC1ib2R5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHRib2R5PgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPk1vbmlrZXI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubW9uaWtlciB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+Tm9kZSBJRDwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgc3RhdHVzLm5vZGVfaW5mby5pZCB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TGlzdGVuIEFkZHI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubGlzdGVuX2FkZHIgfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPk5ldHdvcmsgSUQ8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubmV0d29yayB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TWludGVyIFZlcnNpb248L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHZlcnNpb24gfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPlRlbmRlcm1pbnQgVmVyc2lvbjwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgc3RhdHVzLm5vZGVfaW5mby52ZXJzaW9uIH19PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPC90Ym9keT4KICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIiB2LWlmPSJuZXRfaW5mbyI+CiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iY2FyZC1oZWFkZXIiPgogICAgICAgICAgICAgICAgICAgICAgICBOZXQgSW5mbwogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgIDx0YWJsZSBjbGFzcz0idGFibGUgY2FyZC1ib2R5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHRib2R5PgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPklzIExpc3RlbmluZzwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgOmNsYXNzPSJ7J2JnLWRhbmdlcic6ICFuZXRfaW5mby5saXN0ZW5pbmd9Ij57eyBuZXRfaW5mby5saXN0ZW5pbmcgfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPkNvbm5lY3RlZCBQZWVyczwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgOmNsYXNzPSJ7J2JnLWRhbmdlcic6IG5ldF9pbmZvLm5fcGVlcnMgPCAxfSI+e3sgbmV0X2luZm8ubl9wZWVycyB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICAgICAgPC90YWJsZT4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iY29sIj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQiPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQtaGVhZGVyIj4KICAgICAgICAgICAgICAgICAgICAgICAgU3luY2luZyBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+SXMgU3luY2VkPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCA6Y2xhc3M9InsnYmctc3VjY2Vzcyc6IHN0YXR1cy5zeW5jX2luZm8uY2F0Y2hpbmdfdXAgPT0gZmFsc2UsICdiZy1kYW5nZXInOiBzdGF0dXMuc3luY19pbmZvLmNhdGNoaW5nX3VwID09IHRydWV9Ij57eyBzdGF0dXMuc3luY19pbmZvLmNhdGNoaW5nX3VwID09IGZhbHNlID8gJ3RydWUnIDogJ2ZhbHNlJyB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TGF0ZXN0IEJsb2NrPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAje3sgc3RhdHVzLnN5bmNfaW5mby5sYXRlc3RfYmxvY2tfaGVpZ2h0IH19IDxzcGFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPSJ0ZXh0LW11dGVkIj5hdCB7eyBzdGF0dXMuc3luY19pbmZvLmxhdGVzdF9ibG9ja190aW1lIH19PC9zcGFuPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPC90Ym9keT4KICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIFZhbGlkYXRvciBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5QdWJsaWMgS2V5PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5NcHt7IGJhc2U2NFRvSGV4KHN0YXR1cy52YWxpZGF0b3JfaW5mby5wdWJfa2V5LnZhbHVlKSB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5Wb3RpbmcgUG93ZXI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IG5pY2VOdW0oc3RhdHVzLnZhbGlkYXRvcl9pbmZvLnZvdGluZ19wb3dlcikgfX0gPHNwYW4gY2xhc3M9InRleHQtbXV0ZWQiPm9mIDEwMCwwMDAsMDAwPC9zcGFuPjwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICAgICAgPC90YWJsZT4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2Pgo8L2Rpdj4KPHNjcmlwdD4KICAgIG5ldyBWdWUoewogICAgICAgIGVsOiAnI2FwcCcsCiAgICAgICAgZGF0YTogewogICAgICAgICAgICBzdGF0dXM6IG51bGwsCiAgICAgICAgICAgIHZlcnNpb246IG51bGwsCiAgICAgICAgICAgIG5ldF9pbmZvOiBudWxsLAogICAgICAgICAgICBlcnJvcjogbnVsbAogICAgICAgIH0sCiAgICAgICAgbW91bnRlZCgpIHsKICAgICAgICAgICAgdGhpcy5yZWZyZXNoKCkKCiAgICAgICAgICAgIHNldEludGVydmFsKHRoaXMucmVmcmVzaCwgMTAwMCkKICAgICAgICB9LAogICAgICAgIG1ldGhvZHM6IHsKICAgICAgICAgICAgbmljZU51bShudW0pIHsKICAgICAgICAgICAgICAgIHJldHVybiBudW0udG9TdHJpbmcoKS5yZXBsYWNlKC9cQig/PShcZHszfSkrKD8hXGQpKS9nLCAiLCIpCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIGJhc2U2NFRvSGV4KGJhc2U2NCkgewogICAgICAgICAgICAgICAgcmV0dXJuIENyeXB0b0pTLmVuYy5CYXNlNjQucGFyc2UoYmFzZTY0KS50b1N0cmluZygpCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIHJlZnJlc2goKSB7CiAgICAgICAgICAgICAgICBheGlvcy5nZXQoIi8vIiArIHdpbmRvdy5sb2NhdGlvbi5ob3N0bmFtZSArICc6ODg0MS9hcGkvc3RhdHVzJykudGhlbihmdW5jdGlvbiAoZGF0YSkgewogICAgICAgICAgICAgICAgICAgIHRoaXMuc3RhdHVzID0gZGF0YS5kYXRhLnJlc3VsdC50bV9zdGF0dXMKICAgICAgICAgICAgICAgICAgICB0aGlzLnZlcnNpb24gPSBkYXRhLmRhdGEucmVzdWx0LnZlcnNpb24KICAgICAgICAgICAgICAgICAgICB0aGlzLmVycm9yID0gbnVsbAogICAgICAgICAgICAgICAgfS5iaW5kKHRoaXMpKS5jYXRjaChmdW5jdGlvbiAocmVhc29uKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5lcnJvciA9IHJlYXNvbi50b1N0cmluZygpOwogICAgICAgICAgICAgICAgfS5iaW5kKHRoaXMpKQoKICAgICAgICAgICAgICAgIGF4aW9zLmdldCgiLy8iICsgd2luZG93LmxvY2F0aW9uLmhvc3RuYW1lICsgJzo4ODQxL2FwaS9uZXRfaW5mbycpLnRoZW4oZnVuY3Rpb24gKGRhdGEpIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLm5ldF9pbmZvID0gZGF0YS5kYXRhLnJlc3VsdAogICAgICAgICAgICAgICAgICAgIHRoaXMuZXJyb3IgPSBudWxsCiAgICAgICAgICAgICAgICB9LmJpbmQodGhpcykpLmNhdGNoKGZ1bmN0aW9uIChyZWFzb24pIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmVycm9yID0gcmVhc29uLnRvU3RyaW5nKCk7CiAgICAgICAgICAgICAgICB9LmJpbmQodGhpcykpCiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9KQo8L3NjcmlwdD4KPC9ib2R5Pgo8L2h0bWw+\"") } diff --git a/gui/index.html b/gui/index.html index 8ce59ac3f..93608b85a 100644 --- a/gui/index.html +++ b/gui/index.html @@ -96,6 +96,10 @@

Error while connecting to local node

Network ID {{ status.node_info.network }} + + Minter Version + {{ version }} + Tendermint Version {{ status.node_info.version }} @@ -111,11 +115,11 @@

Error while connecting to local node

Is Listening - {{ net_info.listening }} + {{ net_info.listening }} Connected Peers - {{ net_info.n_peers }} + {{ net_info.n_peers }} @@ -168,6 +172,7 @@

Error while connecting to local node

el: '#app', data: { status: null, + version: null, net_info: null, error: null }, @@ -186,6 +191,7 @@

Error while connecting to local node

refresh() { axios.get("//" + window.location.hostname + ':8841/api/status').then(function (data) { this.status = data.data.result.tm_status + this.version = data.data.result.version this.error = null }.bind(this)).catch(function (reason) { this.error = reason.toString(); From dcb8bb8e9a70296e305b40a45d1a0a97702b57a7 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 13:18:20 +0300 Subject: [PATCH 56/78] update api --- api/get_bip_volume.go | 10 ++++++++-- api/send_transaction.go | 10 ++++++++-- api/send_transaction_async.go | 6 ++++-- api/send_transaction_sync.go | 6 ++++-- api/transaction_count.go | 10 +++++++--- 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/api/get_bip_volume.go b/api/get_bip_volume.go index 3813e09f6..326a9da7e 100644 --- a/api/get_bip_volume.go +++ b/api/get_bip_volume.go @@ -9,6 +9,10 @@ import ( "strconv" ) +type BipVolumeResult struct { + Volume string `json:"volume"` +} + func GetBipVolume(w http.ResponseWriter, r *http.Request) { height, _ := strconv.Atoi(r.URL.Query().Get("height")) @@ -27,8 +31,10 @@ func GetBipVolume(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(Response{ - Code: 0, - Result: CalcBipVolume(height).String(), + Code: 0, + Result: BipVolumeResult{ + Volume: CalcBipVolume(height).String(), + }, }) } diff --git a/api/send_transaction.go b/api/send_transaction.go index 83da7052c..d1c38ec75 100644 --- a/api/send_transaction.go +++ b/api/send_transaction.go @@ -15,6 +15,10 @@ type SendTransactionRequest struct { Transaction string `json:"transaction"` } +type SendTransactionResponse struct { + Hash string `json:"hash"` +} + func SendTransaction(w http.ResponseWriter, r *http.Request) { var req SendTransactionRequest @@ -52,7 +56,9 @@ func SendTransaction(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(Response{ - Code: code.OK, - Result: "Mt" + strings.ToLower(result.Hash.String()), + Code: code.OK, + Result: SendTransactionResponse{ + Hash: "Mt" + strings.ToLower(result.Hash.String()), + }, }) } diff --git a/api/send_transaction_async.go b/api/send_transaction_async.go index e88c9d957..1ddbd3b12 100644 --- a/api/send_transaction_async.go +++ b/api/send_transaction_async.go @@ -27,7 +27,9 @@ func SendTransactionAsync(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(Response{ - Code: code.OK, - Result: "Mt" + strings.ToLower(result.Hash.String()), + Code: code.OK, + Result: SendTransactionResponse{ + Hash: "Mt" + strings.ToLower(result.Hash.String()), + }, }) } diff --git a/api/send_transaction_sync.go b/api/send_transaction_sync.go index 15d90e2bc..4377d3c44 100644 --- a/api/send_transaction_sync.go +++ b/api/send_transaction_sync.go @@ -38,7 +38,9 @@ func SendTransactionSync(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(Response{ - Code: code.OK, - Result: "Mt" + strings.ToLower(result.Hash.String()), + Code: code.OK, + Result: SendTransactionResponse{ + Hash: "Mt" + strings.ToLower(result.Hash.String()), + }, }) } diff --git a/api/transaction_count.go b/api/transaction_count.go index 33f67538e..f1ca70736 100644 --- a/api/transaction_count.go +++ b/api/transaction_count.go @@ -7,7 +7,9 @@ import ( "net/http" ) -type TransactionCountResponse uint64 +type TransactionCountResponse struct { + Count uint64 `json:"count"` +} func GetTransactionCount(w http.ResponseWriter, r *http.Request) { @@ -20,7 +22,9 @@ func GetTransactionCount(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(Response{ - Code: 0, - Result: cState.GetNonce(address), + Code: 0, + Result: TransactionCountResponse{ + Count: cState.GetNonce(address), + }, }) } From b88dd35099e5e4ca5d84b2401580fdae55de7cab Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 13:19:51 +0300 Subject: [PATCH 57/78] refactor api --- api/api.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/api/api.go b/api/api.go index a200ea227..674cd7f79 100644 --- a/api/api.go +++ b/api/api.go @@ -18,17 +18,16 @@ import ( "time" ) -var cdc = amino.NewCodec() - -func init() { - crypto.RegisterAmino(cdc) -} - var ( + cdc = amino.NewCodec() blockchain *minter.Blockchain client *rpc.Local ) +func init() { + crypto.RegisterAmino(cdc) +} + func RunApi(b *minter.Blockchain, node *node.Node) { client = rpc.NewLocal(node) @@ -63,6 +62,12 @@ func RunApi(b *minter.Blockchain, node *node.Node) { handler := c.Handler(router) // wait for tendermint to start + waitForTendermint() + + log.Fatal(http.ListenAndServe(*utils.MinterAPIAddrFlag, handler)) +} + +func waitForTendermint() { for true { _, err := client.Health() if err == nil { @@ -71,8 +76,6 @@ func RunApi(b *minter.Blockchain, node *node.Node) { time.Sleep(1 * time.Second) } - - log.Fatal(http.ListenAndServe(*utils.MinterAPIAddrFlag, handler)) } type Response struct { From f54520e84c553f032e3b202f5598619b8c2469c1 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 13:29:20 +0300 Subject: [PATCH 58/78] remove unused code --- genesis/genesis.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/genesis/genesis.go b/genesis/genesis.go index fcbe5f60d..2deb31162 100644 --- a/genesis/genesis.go +++ b/genesis/genesis.go @@ -4,19 +4,12 @@ import ( "encoding/hex" "encoding/json" "github.com/MinterTeam/minter-go-node/core/types" - "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/libs/common" tmtypes "github.com/tendermint/tendermint/types" "time" ) -var cdc = amino.NewCodec() - -func init() { - crypto.RegisterAmino(cdc) -} - func GetTestnetGenesis() *tmtypes.GenesisDoc { validatorPubKeyBytes, _ := hex.DecodeString("aaee1ddf30ff54c90715d928b56652fc511bed3722e58973e8ef946f5dae3979") From 83e774349e96500757f8f825d05e8f3d9c742910 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 13:40:28 +0300 Subject: [PATCH 59/78] refactor genesis --- genesis/genesis.go | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/genesis/genesis.go b/genesis/genesis.go index 2deb31162..6c45ace4f 100644 --- a/genesis/genesis.go +++ b/genesis/genesis.go @@ -4,9 +4,11 @@ import ( "encoding/hex" "encoding/json" "github.com/MinterTeam/minter-go-node/core/types" + "github.com/MinterTeam/minter-go-node/helpers" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/libs/common" tmtypes "github.com/tendermint/tendermint/types" + "math/big" "time" ) @@ -18,23 +20,25 @@ func GetTestnetGenesis() *tmtypes.GenesisDoc { appHash, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") - appState := `{ - "first_validator_address": "Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", - "initial_balances": [ - { - "address": "Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", - "balance": { - "MNT": "10000000000000000000000000" - } - }, - { - "address": "Mxfe60014a6e9ac91618f5d1cab3fd58cded61ee99", - "balance": { - "MNT": "10000000000000000000000000" - } - } - ] - }` + appState := AppState{ + FirstValidatorAddress: types.HexToAddress("Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f"), + InitialBalances: []Account{ + { + Address: types.HexToAddress("Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f"), + Balance: map[string]string{ + "MNT": helpers.BipToPip(big.NewInt(100000000)).String(), + }, + }, + { + Address: types.HexToAddress("Mxfe60014a6e9ac91618f5d1cab3fd58cded61ee99"), + Balance: map[string]string{ + "MNT": helpers.BipToPip(big.NewInt(10000000)).String(), + }, + }, + }, + } + + appStateJSON, _ := json.Marshal(appState) genesis := tmtypes.GenesisDoc{ GenesisTime: time.Date(2018, 7, 19, 0, 0, 0, 0, time.UTC), @@ -47,7 +51,7 @@ func GetTestnetGenesis() *tmtypes.GenesisDoc { }, }, AppHash: common.HexBytes(appHash), - AppState: json.RawMessage([]byte(appState)), + AppState: json.RawMessage([]byte(appStateJSON)), } genesis.ValidateAndComplete() From f1d0d3fc5b88481733c81ce75e480b78eacf196f Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 15:26:34 +0300 Subject: [PATCH 60/78] update gui --- cmd/minter/main-packr.go | 2 +- gui/index.html | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/cmd/minter/main-packr.go b/cmd/minter/main-packr.go index 03a201430..c6d7f9070 100644 --- a/cmd/minter/main-packr.go +++ b/cmd/minter/main-packr.go @@ -7,5 +7,5 @@ import "github.com/gobuffalo/packr" // You can use the "packr clean" command to clean up this, // and any other packr generated files. func init() { - packr.PackJSONBytes("./../../gui", "index.html", "\"PGh0bWw+CjxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0iVVRGLTgiPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiCiAgICAgICAgICBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIHVzZXItc2NhbGFibGU9bm8sIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCwgbWluaW11bS1zY2FsZT0xLjAiPgogICAgPG1ldGEgaHR0cC1lcXVpdj0iWC1VQS1Db21wYXRpYmxlIiBjb250ZW50PSJpZT1lZGdlIj4KICAgIDx0aXRsZT5NaW50ZXIgTm9kZSBHVUk8L3RpdGxlPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL3N0YWNrcGF0aC5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC80LjEuMC9jc3MvYm9vdHN0cmFwLm1pbi5jc3MiCiAgICAgICAgICBpbnRlZ3JpdHk9InNoYTM4NC05Z1ZRNGRZRnd3V1NqSURabkxFV254Q2plU1dGcGhKaXdHUFhyMWpkZEloT2VnaXUxRndPNXFSR3ZGWE9kSlo0IiBjcm9zc29yaWdpbj0iYW5vbnltb3VzIj4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly91c2UuZm9udGF3ZXNvbWUuY29tL3JlbGVhc2VzL3Y1LjEuMS9jc3MvYWxsLmNzcyIgaW50ZWdyaXR5PSJzaGEzODQtTzh3aFMzZmhHMk9uQTVLYXMwWTlsM2NmcG1ZamFwakkwRTR0aGVINGl1TUQrcExoYmY2SkkwaklNZlljSzN5WiIgY3Jvc3NvcmlnaW49ImFub255bW91cyI+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS92dWUiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2F4aW9zLzAuMTguMC9heGlvcy5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2NyeXB0by1qcy8zLjEuOS0xL2NyeXB0by1qcy5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL3R3ZWV0bmFjbC8xLjAuMC9uYWNsLm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c3R5bGU+CgogICAgICAgIC5jYXJkIHsKICAgICAgICAgICAgbWFyZ2luLWJvdHRvbTogMjBweDsKICAgICAgICB9CgogICAgICAgIGh0bWwsYm9keSwuYm9keSB7CiAgICAgICAgICAgIGhlaWdodDogMTAwJTsKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgLmJvZHkgewogICAgICAgICAgICBwYWRkaW5nLXRvcDogMTVweDsKICAgICAgICB9CgogICAgICAgIC50YWJsZSB7CiAgICAgICAgICAgIG1hcmdpbi1ib3R0b206IDA7CiAgICAgICAgICAgIHRhYmxlLWxheW91dDogZml4ZWQ7CiAgICAgICAgfQoKICAgICAgICAuY2FyZC1oZWFkZXIgewogICAgICAgICAgICBmb250LXdlaWdodDogYm9sZDsKICAgICAgICB9CgogICAgICAgIC5jYXJkLWhlYWRlciB7CiAgICAgICAgICAgIHBhZGRpbmctbGVmdDogMTJweDsKICAgICAgICB9CgogICAgICAgIC5oIHsKICAgICAgICAgICAgd2lkdGg6IDIwMHB4OwogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjNmM2YzOwogICAgICAgICAgICBib3JkZXItcmlnaHQ6IDFweCBzb2xpZCAjY2NjOwogICAgICAgIH0KCiAgICAgICAgLmJnLXN1Y2Nlc3MsIC5iZy1kYW5nZXIgewogICAgICAgICAgICBjb2xvcjogd2hpdGU7CiAgICAgICAgfQoKICAgICAgICAuYmctZGFuZ2VyIHsKICAgICAgICAgICAgYm9yZGVyLWNvbG9yOiAjZGMzNTQ1ICFpbXBvcnRhbnQ7CiAgICAgICAgfQoKICAgICAgICAuYmctc3VjY2VzcyB7CiAgICAgICAgICAgIGJvcmRlci1jb2xvcjogIzI4YTc0NSAhaW1wb3J0YW50OwogICAgICAgIH0KICAgIDwvc3R5bGU+CjwvaGVhZD4KPGJvZHkgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICMzNDNhNDAxYSI+CjxkaXYgaWQ9ImFwcCI+CiAgICA8bmF2IGNsYXNzPSJuYXZiYXIgbmF2YmFyLWV4cGFuZC1sZyBuYXZiYXItZGFyayBiZy1kYXJrIj4KICAgICAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIiPgogICAgICAgICAgICA8c3BhbiBjbGFzcz0ibmF2YmFyLWJyYW5kIG1iLTAgaDEiPjxpIGNsYXNzPSJmYXMgZmEtdGVybWluYWwiPjwvaT4gJm5ic3A7IE1pbnRlciBGdWxsIE5vZGUgU3RhdHVzPC9zcGFuPgogICAgICAgIDwvZGl2PgogICAgPC9uYXY+CiAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIgYm9keSIgdi1pZj0iZXJyb3IiPgogICAgICAgIDxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWRhbmdlciIgcm9sZT0iYWxlcnQiPgogICAgICAgICAgICA8aDQgY2xhc3M9ImFsZXJ0LWhlYWRpbmciPkVycm9yIHdoaWxlIGNvbm5lY3RpbmcgdG8gbG9jYWwgbm9kZTwvaDQ+CiAgICAgICAgICAgIDxwIGNsYXNzPSJtYi0wIj57eyBlcnJvciB9fTwvcD4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgogICAgPGRpdiBjbGFzcz0iY29udGFpbmVyIGJvZHkgYmctd2hpdGUiIHYtaWY9InN0YXR1cyAmJiAhZXJyb3IiPgogICAgICAgIDxkaXYgY2xhc3M9InJvdyI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbCI+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIE5vZGUgSW5mbwogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgIDx0YWJsZSBjbGFzcz0idGFibGUgY2FyZC1ib2R5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHRib2R5PgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPk1vbmlrZXI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubW9uaWtlciB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+Tm9kZSBJRDwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgc3RhdHVzLm5vZGVfaW5mby5pZCB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TGlzdGVuIEFkZHI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubGlzdGVuX2FkZHIgfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPk5ldHdvcmsgSUQ8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubmV0d29yayB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TWludGVyIFZlcnNpb248L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHZlcnNpb24gfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPlRlbmRlcm1pbnQgVmVyc2lvbjwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgc3RhdHVzLm5vZGVfaW5mby52ZXJzaW9uIH19PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPC90Ym9keT4KICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIiB2LWlmPSJuZXRfaW5mbyI+CiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iY2FyZC1oZWFkZXIiPgogICAgICAgICAgICAgICAgICAgICAgICBOZXQgSW5mbwogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgIDx0YWJsZSBjbGFzcz0idGFibGUgY2FyZC1ib2R5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHRib2R5PgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPklzIExpc3RlbmluZzwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgOmNsYXNzPSJ7J2JnLWRhbmdlcic6ICFuZXRfaW5mby5saXN0ZW5pbmd9Ij57eyBuZXRfaW5mby5saXN0ZW5pbmcgfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPkNvbm5lY3RlZCBQZWVyczwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgOmNsYXNzPSJ7J2JnLWRhbmdlcic6IG5ldF9pbmZvLm5fcGVlcnMgPCAxfSI+e3sgbmV0X2luZm8ubl9wZWVycyB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICAgICAgPC90YWJsZT4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iY29sIj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQiPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQtaGVhZGVyIj4KICAgICAgICAgICAgICAgICAgICAgICAgU3luY2luZyBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+SXMgU3luY2VkPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCA6Y2xhc3M9InsnYmctc3VjY2Vzcyc6IHN0YXR1cy5zeW5jX2luZm8uY2F0Y2hpbmdfdXAgPT0gZmFsc2UsICdiZy1kYW5nZXInOiBzdGF0dXMuc3luY19pbmZvLmNhdGNoaW5nX3VwID09IHRydWV9Ij57eyBzdGF0dXMuc3luY19pbmZvLmNhdGNoaW5nX3VwID09IGZhbHNlID8gJ3RydWUnIDogJ2ZhbHNlJyB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TGF0ZXN0IEJsb2NrPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAje3sgc3RhdHVzLnN5bmNfaW5mby5sYXRlc3RfYmxvY2tfaGVpZ2h0IH19IDxzcGFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPSJ0ZXh0LW11dGVkIj5hdCB7eyBzdGF0dXMuc3luY19pbmZvLmxhdGVzdF9ibG9ja190aW1lIH19PC9zcGFuPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPC90Ym9keT4KICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIFZhbGlkYXRvciBJbmZvCiAgICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICAgICAgPHRhYmxlIGNsYXNzPSJ0YWJsZSBjYXJkLWJvZHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5QdWJsaWMgS2V5PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5NcHt7IGJhc2U2NFRvSGV4KHN0YXR1cy52YWxpZGF0b3JfaW5mby5wdWJfa2V5LnZhbHVlKSB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD5Wb3RpbmcgUG93ZXI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IG5pY2VOdW0oc3RhdHVzLnZhbGlkYXRvcl9pbmZvLnZvdGluZ19wb3dlcikgfX0gPHNwYW4gY2xhc3M9InRleHQtbXV0ZWQiPm9mIDEwMCwwMDAsMDAwPC9zcGFuPjwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICAgICAgPC90YWJsZT4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2Pgo8L2Rpdj4KPHNjcmlwdD4KICAgIG5ldyBWdWUoewogICAgICAgIGVsOiAnI2FwcCcsCiAgICAgICAgZGF0YTogewogICAgICAgICAgICBzdGF0dXM6IG51bGwsCiAgICAgICAgICAgIHZlcnNpb246IG51bGwsCiAgICAgICAgICAgIG5ldF9pbmZvOiBudWxsLAogICAgICAgICAgICBlcnJvcjogbnVsbAogICAgICAgIH0sCiAgICAgICAgbW91bnRlZCgpIHsKICAgICAgICAgICAgdGhpcy5yZWZyZXNoKCkKCiAgICAgICAgICAgIHNldEludGVydmFsKHRoaXMucmVmcmVzaCwgMTAwMCkKICAgICAgICB9LAogICAgICAgIG1ldGhvZHM6IHsKICAgICAgICAgICAgbmljZU51bShudW0pIHsKICAgICAgICAgICAgICAgIHJldHVybiBudW0udG9TdHJpbmcoKS5yZXBsYWNlKC9cQig/PShcZHszfSkrKD8hXGQpKS9nLCAiLCIpCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIGJhc2U2NFRvSGV4KGJhc2U2NCkgewogICAgICAgICAgICAgICAgcmV0dXJuIENyeXB0b0pTLmVuYy5CYXNlNjQucGFyc2UoYmFzZTY0KS50b1N0cmluZygpCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIHJlZnJlc2goKSB7CiAgICAgICAgICAgICAgICBheGlvcy5nZXQoIi8vIiArIHdpbmRvdy5sb2NhdGlvbi5ob3N0bmFtZSArICc6ODg0MS9hcGkvc3RhdHVzJykudGhlbihmdW5jdGlvbiAoZGF0YSkgewogICAgICAgICAgICAgICAgICAgIHRoaXMuc3RhdHVzID0gZGF0YS5kYXRhLnJlc3VsdC50bV9zdGF0dXMKICAgICAgICAgICAgICAgICAgICB0aGlzLnZlcnNpb24gPSBkYXRhLmRhdGEucmVzdWx0LnZlcnNpb24KICAgICAgICAgICAgICAgICAgICB0aGlzLmVycm9yID0gbnVsbAogICAgICAgICAgICAgICAgfS5iaW5kKHRoaXMpKS5jYXRjaChmdW5jdGlvbiAocmVhc29uKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5lcnJvciA9IHJlYXNvbi50b1N0cmluZygpOwogICAgICAgICAgICAgICAgfS5iaW5kKHRoaXMpKQoKICAgICAgICAgICAgICAgIGF4aW9zLmdldCgiLy8iICsgd2luZG93LmxvY2F0aW9uLmhvc3RuYW1lICsgJzo4ODQxL2FwaS9uZXRfaW5mbycpLnRoZW4oZnVuY3Rpb24gKGRhdGEpIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLm5ldF9pbmZvID0gZGF0YS5kYXRhLnJlc3VsdAogICAgICAgICAgICAgICAgICAgIHRoaXMuZXJyb3IgPSBudWxsCiAgICAgICAgICAgICAgICB9LmJpbmQodGhpcykpLmNhdGNoKGZ1bmN0aW9uIChyZWFzb24pIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmVycm9yID0gcmVhc29uLnRvU3RyaW5nKCk7CiAgICAgICAgICAgICAgICB9LmJpbmQodGhpcykpCiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9KQo8L3NjcmlwdD4KPC9ib2R5Pgo8L2h0bWw+\"") + packr.PackJSONBytes("./../../gui", "index.html", "\"PGh0bWw+CjxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0iVVRGLTgiPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiCiAgICAgICAgICBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIHVzZXItc2NhbGFibGU9bm8sIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCwgbWluaW11bS1zY2FsZT0xLjAiPgogICAgPG1ldGEgaHR0cC1lcXVpdj0iWC1VQS1Db21wYXRpYmxlIiBjb250ZW50PSJpZT1lZGdlIj4KICAgIDx0aXRsZT5NaW50ZXIgTm9kZSBHVUk8L3RpdGxlPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL3N0YWNrcGF0aC5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC80LjEuMC9jc3MvYm9vdHN0cmFwLm1pbi5jc3MiCiAgICAgICAgICBpbnRlZ3JpdHk9InNoYTM4NC05Z1ZRNGRZRnd3V1NqSURabkxFV254Q2plU1dGcGhKaXdHUFhyMWpkZEloT2VnaXUxRndPNXFSR3ZGWE9kSlo0IiBjcm9zc29yaWdpbj0iYW5vbnltb3VzIj4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly91c2UuZm9udGF3ZXNvbWUuY29tL3JlbGVhc2VzL3Y1LjEuMS9jc3MvYWxsLmNzcyIgaW50ZWdyaXR5PSJzaGEzODQtTzh3aFMzZmhHMk9uQTVLYXMwWTlsM2NmcG1ZamFwakkwRTR0aGVINGl1TUQrcExoYmY2SkkwaklNZlljSzN5WiIgY3Jvc3NvcmlnaW49ImFub255bW91cyI+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L25wbS92dWUiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2F4aW9zLzAuMTguMC9heGlvcy5taW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Imh0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL2NyeXB0by1qcy8zLjEuOS0xL2NyeXB0by1qcy5taW4uanMiPjwvc2NyaXB0PgogICAgPHN0eWxlPgoKICAgICAgICAuY2FyZCB7CiAgICAgICAgICAgIG1hcmdpbi1ib3R0b206IDIwcHg7CiAgICAgICAgfQoKICAgICAgICBodG1sLGJvZHksLmJvZHkgewogICAgICAgICAgICBoZWlnaHQ6IDEwMCU7CiAgICAgICAgfQogICAgICAgIAogICAgICAgIC5ib2R5IHsKICAgICAgICAgICAgcGFkZGluZy10b3A6IDE1cHg7CiAgICAgICAgfQoKICAgICAgICAudGFibGUgewogICAgICAgICAgICBtYXJnaW4tYm90dG9tOiAwOwogICAgICAgICAgICB0YWJsZS1sYXlvdXQ6IGZpeGVkOwogICAgICAgIH0KCiAgICAgICAgLmNhcmQtaGVhZGVyIHsKICAgICAgICAgICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICAgICAgfQoKICAgICAgICAuY2FyZC1oZWFkZXIgewogICAgICAgICAgICBwYWRkaW5nLWxlZnQ6IDEycHg7CiAgICAgICAgfQoKICAgICAgICAuaCB7CiAgICAgICAgICAgIHdpZHRoOiAyMDBweDsKICAgICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2YzZjNmMzsKICAgICAgICAgICAgYm9yZGVyLXJpZ2h0OiAxcHggc29saWQgI2NjYzsKICAgICAgICB9CgogICAgICAgIC5iZy1zdWNjZXNzLCAuYmctZGFuZ2VyIHsKICAgICAgICAgICAgY29sb3I6IHdoaXRlOwogICAgICAgIH0KCiAgICAgICAgLmJnLWRhbmdlciB7CiAgICAgICAgICAgIGJvcmRlci1jb2xvcjogI2RjMzU0NSAhaW1wb3J0YW50OwogICAgICAgIH0KCiAgICAgICAgLmJnLXN1Y2Nlc3MgewogICAgICAgICAgICBib3JkZXItY29sb3I6ICMyOGE3NDUgIWltcG9ydGFudDsKICAgICAgICB9CgogICAgICAgIC5mYS1jaGVjayB7CiAgICAgICAgICAgIGNvbG9yOiBncmVlbjsKICAgICAgICB9CgogICAgICAgIC5mYS1leGNsYW1hdGlvbi1jaXJjbGUgewogICAgICAgICAgICBjb2xvcjogcmVkOwogICAgICAgIH0KICAgIDwvc3R5bGU+CjwvaGVhZD4KPGJvZHkgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICMzNDNhNDAxYSI+CjxkaXYgaWQ9ImFwcCI+CiAgICA8bmF2IGNsYXNzPSJuYXZiYXIgbmF2YmFyLWV4cGFuZC1sZyBuYXZiYXItZGFyayBiZy1kYXJrIj4KICAgICAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIiPgogICAgICAgICAgICA8c3BhbiBjbGFzcz0ibmF2YmFyLWJyYW5kIG1iLTAgaDEiPjxpIGNsYXNzPSJmYXMgZmEtdGVybWluYWwiPjwvaT4gJm5ic3A7IE1pbnRlciBGdWxsIE5vZGUgU3RhdHVzPC9zcGFuPgogICAgICAgIDwvZGl2PgogICAgPC9uYXY+CiAgICA8ZGl2IGNsYXNzPSJjb250YWluZXIgYm9keSIgdi1pZj0iZXJyb3IiPgogICAgICAgIDxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWRhbmdlciIgcm9sZT0iYWxlcnQiPgogICAgICAgICAgICA8aDQgY2xhc3M9ImFsZXJ0LWhlYWRpbmciPkVycm9yIHdoaWxlIGNvbm5lY3RpbmcgdG8gbG9jYWwgbm9kZTwvaDQ+CiAgICAgICAgICAgIDxwIGNsYXNzPSJtYi0wIj57eyBlcnJvciB9fTwvcD4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgogICAgPGRpdiBjbGFzcz0iY29udGFpbmVyIGJvZHkgYmctd2hpdGUiIHYtaWY9InN0YXR1cyAmJiAhZXJyb3IiPgogICAgICAgIDxkaXYgY2xhc3M9InJvdyI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbCI+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIE5vZGUgSW5mbwogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgIDx0YWJsZSBjbGFzcz0idGFibGUgY2FyZC1ib2R5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHRib2R5PgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPk1vbmlrZXI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubW9uaWtlciB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+Tm9kZSBJRDwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgc3RhdHVzLm5vZGVfaW5mby5pZCB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TGlzdGVuIEFkZHI8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubGlzdGVuX2FkZHIgfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPk5ldHdvcmsgSUQ8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHN0YXR1cy5ub2RlX2luZm8ubmV0d29yayB9fTwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+TWludGVyIFZlcnNpb248L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPnt7IHZlcnNpb24gfX08L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPlRlbmRlcm1pbnQgVmVyc2lvbjwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgc3RhdHVzLm5vZGVfaW5mby52ZXJzaW9uIH19PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPC90Ym9keT4KICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIiB2LWlmPSJuZXRfaW5mbyI+CiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iY2FyZC1oZWFkZXIiPgogICAgICAgICAgICAgICAgICAgICAgICBOZXQgSW5mbwogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgIDx0YWJsZSBjbGFzcz0idGFibGUgY2FyZC1ib2R5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHRib2R5PgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPklzIExpc3RlbmluZzwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+PGkgOmNsYXNzPSJ7J2ZhLWNoZWNrJzogbmV0X2luZm8ubGlzdGVuaW5nfSIgY2xhc3M9ImZhcyI+PC9pPjwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCBjbGFzcz0iaCI+Q29ubmVjdGVkIFBlZXJzPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZD57eyBuZXRfaW5mby5uX3BlZXJzIH19IDxpIDpjbGFzcz0ieydmYS1leGNsYW1hdGlvbi1jaXJjbGUnOiBuZXRfaW5mby5uX3BlZXJzIDwgMX0iIGNsYXNzPSJmYXMiPjwvaT48L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8L3Rib2R5PgogICAgICAgICAgICAgICAgICAgIDwvdGFibGU+CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvbCI+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkIj4KICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJjYXJkLWhlYWRlciI+CiAgICAgICAgICAgICAgICAgICAgICAgIFN5bmNpbmcgSW5mbwogICAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgICAgIDx0YWJsZSBjbGFzcz0idGFibGUgY2FyZC1ib2R5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHRib2R5PgogICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgY2xhc3M9ImgiPklzIFN5bmNlZDwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4gdi1pZj0ic3RhdHVzLnN5bmNfaW5mby5jYXRjaGluZ191cCI+Tm88L3NwYW4+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4gdi1pZj0iIXN0YXR1cy5zeW5jX2luZm8uY2F0Y2hpbmdfdXAiPlllczwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8aSA6Y2xhc3M9InsnZmEtY2hlY2snOiAhc3RhdHVzLnN5bmNfaW5mby5jYXRjaGluZ191cCwgJ2ZhLWV4Y2xhbWF0aW9uLWNpcmNsZSc6IHN0YXR1cy5zeW5jX2luZm8uY2F0Y2hpbmdfdXB9IiBjbGFzcz0iZmFzIj48L2k+PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPHRyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkIGNsYXNzPSJoIj5MYXRlc3QgQmxvY2s8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICN7eyBzdGF0dXMuc3luY19pbmZvLmxhdGVzdF9ibG9ja19oZWlnaHQgfX0gPHNwYW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9InRleHQtbXV0ZWQiPmF0IHt7IHN0YXR1cy5zeW5jX2luZm8ubGF0ZXN0X2Jsb2NrX3RpbWUgfX08L3NwYW4+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICA8L3Rib2R5PgogICAgICAgICAgICAgICAgICAgIDwvdGFibGU+CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQiPgogICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNhcmQtaGVhZGVyIj4KICAgICAgICAgICAgICAgICAgICAgICAgVmFsaWRhdG9yIEluZm8KICAgICAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgICAgICA8dGFibGUgY2xhc3M9InRhYmxlIGNhcmQtYm9keSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0Ym9keT4KICAgICAgICAgICAgICAgICAgICAgICAgPHRyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPlB1YmxpYyBLZXk8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPk1we3sgYmFzZTY0VG9IZXgoc3RhdHVzLnZhbGlkYXRvcl9pbmZvLnB1Yl9rZXkudmFsdWUpIH19PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPHRyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkPlZvdGluZyBQb3dlcjwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQ+e3sgbmljZU51bShzdGF0dXMudmFsaWRhdG9yX2luZm8udm90aW5nX3Bvd2VyKSB9fSA8c3BhbiBjbGFzcz0idGV4dC1tdXRlZCI+b2YgMTAwLDAwMCwwMDA8L3NwYW4+PC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgPC90Ym9keT4KICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgIDwvZGl2PgogICAgPC9kaXY+CjwvZGl2Pgo8c2NyaXB0PgogICAgbmV3IFZ1ZSh7CiAgICAgICAgZWw6ICcjYXBwJywKICAgICAgICBkYXRhOiB7CiAgICAgICAgICAgIHN0YXR1czogbnVsbCwKICAgICAgICAgICAgdmVyc2lvbjogbnVsbCwKICAgICAgICAgICAgbmV0X2luZm86IG51bGwsCiAgICAgICAgICAgIGVycm9yOiBudWxsCiAgICAgICAgfSwKICAgICAgICBtb3VudGVkKCkgewogICAgICAgICAgICB0aGlzLnJlZnJlc2goKQoKICAgICAgICAgICAgc2V0SW50ZXJ2YWwodGhpcy5yZWZyZXNoLCAxMDAwKQogICAgICAgIH0sCiAgICAgICAgbWV0aG9kczogewogICAgICAgICAgICBuaWNlTnVtKG51bSkgewogICAgICAgICAgICAgICAgcmV0dXJuIG51bS50b1N0cmluZygpLnJlcGxhY2UoL1xCKD89KFxkezN9KSsoPyFcZCkpL2csICIsIikKICAgICAgICAgICAgfSwKICAgICAgICAgICAgYmFzZTY0VG9IZXgoYmFzZTY0KSB7CiAgICAgICAgICAgICAgICByZXR1cm4gQ3J5cHRvSlMuZW5jLkJhc2U2NC5wYXJzZShiYXNlNjQpLnRvU3RyaW5nKCkKICAgICAgICAgICAgfSwKICAgICAgICAgICAgcmVmcmVzaCgpIHsKICAgICAgICAgICAgICAgIGF4aW9zLmdldCgiLy8iICsgd2luZG93LmxvY2F0aW9uLmhvc3RuYW1lICsgJzo4ODQxL2FwaS9zdGF0dXMnKS50aGVuKGZ1bmN0aW9uIChkYXRhKSB7CiAgICAgICAgICAgICAgICAgICAgdGhpcy5zdGF0dXMgPSBkYXRhLmRhdGEucmVzdWx0LnRtX3N0YXR1cwogICAgICAgICAgICAgICAgICAgIHRoaXMudmVyc2lvbiA9IGRhdGEuZGF0YS5yZXN1bHQudmVyc2lvbgogICAgICAgICAgICAgICAgICAgIHRoaXMuZXJyb3IgPSBudWxsCiAgICAgICAgICAgICAgICB9LmJpbmQodGhpcykpLmNhdGNoKGZ1bmN0aW9uIChyZWFzb24pIHsKICAgICAgICAgICAgICAgICAgICB0aGlzLmVycm9yID0gcmVhc29uLnRvU3RyaW5nKCk7CiAgICAgICAgICAgICAgICB9LmJpbmQodGhpcykpCgogICAgICAgICAgICAgICAgYXhpb3MuZ2V0KCIvLyIgKyB3aW5kb3cubG9jYXRpb24uaG9zdG5hbWUgKyAnOjg4NDEvYXBpL25ldF9pbmZvJykudGhlbihmdW5jdGlvbiAoZGF0YSkgewogICAgICAgICAgICAgICAgICAgIHRoaXMubmV0X2luZm8gPSBkYXRhLmRhdGEucmVzdWx0CiAgICAgICAgICAgICAgICAgICAgdGhpcy5lcnJvciA9IG51bGwKICAgICAgICAgICAgICAgIH0uYmluZCh0aGlzKSkuY2F0Y2goZnVuY3Rpb24gKHJlYXNvbikgewogICAgICAgICAgICAgICAgICAgIHRoaXMuZXJyb3IgPSByZWFzb24udG9TdHJpbmcoKTsKICAgICAgICAgICAgICAgIH0uYmluZCh0aGlzKSkKICAgICAgICAgICAgfQogICAgICAgIH0KICAgIH0pCjwvc2NyaXB0Pgo8L2JvZHk+CjwvaHRtbD4=\"") } diff --git a/gui/index.html b/gui/index.html index 93608b85a..5cfa29477 100644 --- a/gui/index.html +++ b/gui/index.html @@ -11,7 +11,6 @@ - @@ -115,11 +122,11 @@

Error while connecting to local node

Is Listening - {{ net_info.listening }} + Connected Peers - {{ net_info.n_peers }} + {{ net_info.n_peers }} @@ -134,7 +141,10 @@

Error while connecting to local node

Is Synced - {{ status.sync_info.catching_up == false ? 'true' : 'false' }} + + No + Yes + Latest Block From 02a738cf954c2bc6a66375780840a73175a32ef5 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 16:11:58 +0300 Subject: [PATCH 61/78] fix api --- api/balance_watcher.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/api/balance_watcher.go b/api/balance_watcher.go index 990d5ce83..14c54ef8a 100644 --- a/api/balance_watcher.go +++ b/api/balance_watcher.go @@ -31,13 +31,16 @@ func handleBalanceChanges() { for { msg := <-state.BalanceChangeChan - var balanceInBasecoin *big.Int + balanceInBasecoin := big.NewInt(0) if msg.Coin == types.GetBaseCoin() { balanceInBasecoin = msg.Balance } else { - sCoin := minter.GetBlockchain().CurrentState().GetStateCoin(msg.Coin).Data() - balanceInBasecoin = formula.CalculateSaleReturn(sCoin.Volume, sCoin.ReserveBalance, sCoin.Crr, msg.Balance) + sCoin := minter.GetBlockchain().CurrentState().GetStateCoin(msg.Coin) + + if sCoin != nil { + balanceInBasecoin = formula.CalculateSaleReturn(sCoin.Data().Volume, sCoin.Data().ReserveBalance, sCoin.Data().Crr, msg.Balance) + } } msg.BalanceInBasecoin = balanceInBasecoin From d9ecd327261ca9c5470eef99655bb7ff140967b9 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 16:13:16 +0300 Subject: [PATCH 62/78] update docs --- api/estimate_coin_buy.go | 4 +- api/estimate_coin_sell.go | 4 +- docs/api.rst | 285 ++++++++++++++++++++++++++------------ docs/delegator-faq.rst | 1 + 4 files changed, 205 insertions(+), 89 deletions(-) diff --git a/api/estimate_coin_buy.go b/api/estimate_coin_buy.go index ba15a1ad8..395144157 100644 --- a/api/estimate_coin_buy.go +++ b/api/estimate_coin_buy.go @@ -13,8 +13,8 @@ import ( ) type EstimateCoinBuyResponse struct { - WillPay string - Commission string + WillPay string `json:"will_pay"` + Commission string `json:"commission"` } func EstimateCoinBuy(w http.ResponseWriter, r *http.Request) { diff --git a/api/estimate_coin_sell.go b/api/estimate_coin_sell.go index 728eeb344..ff08e4845 100644 --- a/api/estimate_coin_sell.go +++ b/api/estimate_coin_sell.go @@ -13,8 +13,8 @@ import ( ) type EstimateCoinSellResponse struct { - WillGet string - Commission string + WillGet string `json:"will_get"` + Commission string `json:"commission"` } func EstimateCoinSell(w http.ResponseWriter, r *http.Request) { diff --git a/docs/api.rst b/docs/api.rst index 7edf453c6..aa74bdf61 100755 --- a/docs/api.rst +++ b/docs/api.rst @@ -20,13 +20,47 @@ normal mode. .. code-block:: json { - "code": 0, - "result": { - "latest_block_hash": "30AAD93FC07CBFC7ABC9E34D6FDC29FF0928A5C5", - "latest_app_hash": "8D10D20C2BC74AAF82ABC41ADA9852D5EF89DDE17382CED2C21B84BE36365583", - "latest_block_height": 29783, - "latest_block_time": "2018-06-21T13:58:53.078510484+03:00" - } + "code":0, + "result":{ + "version":"0.1.0", + "latest_block_hash":"BF2647887AEBF12ABF92D240613907E84E757E34", + "latest_app_hash":"C92D2073E15519C0D684A896AF8DF9AAD536423A9564987F979CFCC13FBE57D7", + "latest_block_height":81, + "latest_block_time":"2018-07-20T16:03:42.001313931+03:00", + "tm_status":{ + "node_info":{ + "id":"30231c71e87db942ea902ad6ad22cfefa3b15560", + "listen_addr":"192.168.1.102:26656", + "network":"minter-test-network-11-private", + "version":"0.22.4", + "channels":"4020212223303800", + "moniker":"MinterNode", + "other":[ + "amino_version=0.10.1", + "p2p_version=0.5.0", + "consensus_version=v1/0.2.2", + "rpc_version=0.7.0/3", + "tx_index=on", + "rpc_addr=tcp://0.0.0.0:26657" + ] + }, + "sync_info":{ + "latest_block_hash":"BF2647887AEBF12ABF92D240613907E84E757E34", + "latest_app_hash":"C92D2073E15519C0D684A896AF8DF9AAD536423A9564987F979CFCC13FBE57D7", + "latest_block_height":"81", + "latest_block_time":"2018-07-20T13:03:42.001313931Z", + "catching_up":false + }, + "validator_info":{ + "address":"F974AA1C211BC294DAB21B4F5866603144E025E8", + "pub_key":{ + "type":"tendermint/PubKeyEd25519", + "value":"YfdhnC3qkBZqgQl76+lY99f0xfGJLyTdgTOLJ2CSvnA=" + }, + "voting_power":"0" + } + } + } } Volume of Base Coin in Blockchain @@ -42,8 +76,10 @@ relayed rewards. .. code-block:: json { - "code": 0, - "result": "20000111000000000000000000" + "code":0, + "result":{ + "volume":"20000222000000000000000000" + } } Candidate @@ -74,26 +110,26 @@ found. .. code-block:: json { - "code": 0, - "result": { - "candidate": { - "candidate_address": "Mx655a96de0e7928bf78c41f555010391581a5afab", - "total_stake": "49500000000000000000", - "pub_key": "Mp34e647f46a5dd89e9f21acdbd0c45c8c768fdc17082d0783b683bfb0da7ce989", - "commission": 50, - "accumulated_reward": "0", - "stakes": [ - { - "owner": "Mx655a96de0e7928bf78c41f555010391581a5afab", - "coin": "MNT", - "value": "49500000000000000000" - } - ], - "created_at_block": 27447, - "status": 1, - "absent_times": 0 - } - } + "code":0, + "result":{ + "candidate":{ + "candidate_address":"Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", + "total_stake":"1", + "pub_key":"Mpc0d436ce0a9e7129cb3dbbfb059ec3a45865305a4102bc68cf6ed41d41d53e99", + "commission":10, + "accumulated_reward":"0", + "stakes":[ + { + "owner":"Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", + "coin":"MNT", + "value":"1" + } + ], + "created_at_block":1, + "status":2, + "absent_times":0 + } + } } Validators @@ -108,26 +144,26 @@ Returns list of active validators. .. code-block:: json { - "code": 0, - "result": [ - { - "candidate_address": "Mx655a96de0e7928bf78c41f555010391581a5afab", - "total_stake": "49500000000000000000", - "pub_key": "Mp34e647f46a5dd89e9f21acdbd0c45c8c768fdc17082d0783b683bfb0da7ce989", - "commission": 50, - "accumulated_reward": "0", - "stakes": [ - { - "owner": "Mx655a96de0e7928bf78c41f555010391581a5afab", - "coin": "MNT", - "value": "49500000000000000000" - } - ], - "created_at_block": 27447, - "status": 1, - "absent_times": 0 - } - ] + "code":0, + "result":[ + { + "candidate_address":"Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", + "total_stake":"1", + "pub_key":"Mpc0d436ce0a9e7129cb3dbbfb059ec3a45865305a4102bc68cf6ed41d41d53e99", + "commission":10, + "accumulated_reward":"666000000000000000000", + "stakes":[ + { + "owner":"Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", + "coin":"MNT", + "value":"1" + } + ], + "created_at_block":1, + "status":2, + "absent_times":0 + } + ] } Balance @@ -142,10 +178,12 @@ Returns balance of an account. .. code-block:: json { - "code": 0, - "result": { - "MNT": "670983232356790123336" - } + "code":0, + "result":{ + "balance":{ + "MNT":"100011877000000000000000000" + } + } } **Result**: Map of balances. CoinSymbol => Balance (in pips). @@ -163,8 +201,10 @@ transaction. .. code-block:: json { - "code": 0, - "result": 3 + "code":0, + "result":{ + "count":1 + } } **Result**: Count of transactions sent from given account. @@ -182,7 +222,9 @@ Sends transaction to the Minter Network. { "code": 0, - "result": "Mtfd5c3ecad1e8333564cf6e3f968578b9db5acea3" + "result": { + "hash": "Mtfd5c3ecad1e8333564cf6e3f968578b9db5acea3" + } } **Result**: Transaction hash. @@ -190,8 +232,6 @@ Sends transaction to the Minter Network. Transaction ^^^^^^^^^^^ -*In development* - .. code-block:: bash curl -s 'localhost:8841/api/transaction/{hash}' @@ -199,8 +239,48 @@ Transaction .. code-block:: json { - "code": 0, - "result": {} + "code":0, + "result":{ + "hash":"47B0CF9BFAA60CA343392FBE1E366EB221231F38", + "raw_tx":"f873010101aae98a4d4e540000000000000094a93163fdf10724dc4785ff5cbfb9ac0b5949409f880de0b6b3a764000080801ba0da1b6fd187bc5c757d1d1497d03471a3b5d1fd4d8025859ea127841975ce0df4a0158b54aaf8066be9ef26aae9f1a953777c346e58a6c6f45eb2d465efea74e5af", + "height":41, + "index":0, + "tx_result":{ + "gas_wanted":10, + "gas_used":10, + "tags":[ + { + "key":"dHgudHlwZQ==", + "value":"AQ==" + }, + { + "key":"dHguZnJvbQ==", + "value":"YTkzMTYzZmRmMTA3MjRkYzQ3ODVmZjVjYmZiOWFjMGI1OTQ5NDA5Zg==" + }, + { + "key":"dHgudG8=", + "value":"YTkzMTYzZmRmMTA3MjRkYzQ3ODVmZjVjYmZiOWFjMGI1OTQ5NDA5Zg==" + }, + { + "key":"dHguY29pbg==", + "value":"TU5U" + } + ], + "fee":{ + + } + }, + "from":"Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", + "nonce":1, + "gas_price":1, + "type":1, + "data":{ + "coin":"MNT", + "to":"Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", + "value":"1000000000000000000" + }, + "payload":"" + } } Block @@ -217,44 +297,73 @@ Returns block data at given height. { "code":0, "result":{ - "hash":"A83F3A3909C8B863305C5A444C8C34C514A03590", - "height":108805, - "time":"2018-07-03T09:46:54.359423195Z", + "hash":"8E07E206FBB41D7697D105CBC7FE477DDFAA2D5B", + "height":41, + "time":"2018-07-20T13:00:21.575014435Z", "num_txs":1, - "total_txs":1174135, + "total_txs":1, "transactions":[ { - "hash":"Mt3f85c77911f058c9c2f79d73c5d68b2c7dd3c2cd", - "from":"Mxa93163fdF10724DC4785FF5cBfB9aC0B5949409F", - "nonce":81, - "gasPrice":1, - "type":5, + "hash":"Mt47b0cf9bfaa60ca343392fbe1e366eb221231f38", + "raw_tx":"f873010101aae98a4d4e540000000000000094a93163fdf10724dc4785ff5cbfb9ac0b5949409f880de0b6b3a764000080801ba0da1b6fd187bc5c757d1d1497d03471a3b5d1fd4d8025859ea127841975ce0df4a0158b54aaf8066be9ef26aae9f1a953777c346e58a6c6f45eb2d465efea74e5af", + "from":"Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", + "nonce":1, + "gas_price":1, + "type":1, "data":{ - "PubKey":"Mp079138d379aaf423c911506a3ccbe1d590a7d4d9aecbc7eb05816d81b41848d6", - "Coin":"BLTCOIN", - "Stake":"2000000000000000000" + "coin":"MNT", + "to":"Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f", + "value":"1000000000000000000" }, "payload":"", - "serviceData":"", - "gas":10000 + "service_data":"", + "gas":10, + "tx_result":{ + "gas_wanted":10, + "gas_used":10, + "tags":[ + { + "key":"dHgudHlwZQ==", + "value":"AQ==" + }, + { + "key":"dHguZnJvbQ==", + "value":"YTkzMTYzZmRmMTA3MjRkYzQ3ODVmZjVjYmZiOWFjMGI1OTQ5NDA5Zg==" + }, + { + "key":"dHgudG8=", + "value":"YTkzMTYzZmRmMTA3MjRkYzQ3ODVmZjVjYmZiOWFjMGI1OTQ5NDA5Zg==" + }, + { + "key":"dHguY29pbg==", + "value":"TU5U" + } + ], + "fee":{ + + } + } } ], "precommits":[ { - "validator_address":"04E5DCA0DFCF35605A3EB1292DBDBF7C97B476B8", - "validator_index":0, - "height":108804, - "round":0, - "timestamp":"2018-07-03T09:47:33.79209988Z", + "validator_address":"8055BB821C535279E169FDF60BBEBEBE1452DBA8", + "validator_index":"0", + "height":"40", + "round":"0", + "timestamp":"2018-07-20T13:00:16.571443571Z", "type":2, "block_id":{ - "hash":"2222959DA3EEA441DB6D0E01C12F1546B210DA72", + "hash":"0F06CA442183BED91E66010314FA6CADBC598801", "parts":{ - "total":1, - "hash":"3821D8B2A09A1C6932712523B8DEB588375D7BFA" + "total":"1", + "hash":"3D119516E329A211B74D728728A7E283E3BC956E" } }, - "signature":[] + "signature":{ + "type":"tendermint/SignatureEd25519", + "value":"lhNyaFgSYmC7YF/FPSwZ2yksWwViaclK6rGwdN2+nVnp/uMQherRMyZv6hJB/YedAjgo49/fBhGZUcyOO7Y+AA==" + } } ] } @@ -311,7 +420,10 @@ Request params: { "code": 0, - "result": "29808848728151191" + "result": { + "will_get": "29808848728151191", + "commission": "443372813245" + } } **Result**: Amount of "to_coin" user should get. @@ -335,7 +447,10 @@ Request params: { "code": 0, - "result": "29808848728151191" + "result": { + "will_pay": "29808848728151191", + "commission": "443372813245" + } } **Result**: Amount of "to_coin" user should give. diff --git a/docs/delegator-faq.rst b/docs/delegator-faq.rst index de64a6e32..9d863fa4a 100755 --- a/docs/delegator-faq.rst +++ b/docs/delegator-faq.rst @@ -62,6 +62,7 @@ with the following revenue: - 111 Bips as block reward - 10 Bips as transaction fees. + This amounts to a total of 121 Bips to be distributed among all staking pools. Our validator's staking pool represents 10% of the total stake, which means the pool obtains 12.1 bips. Now let us From 19e1b7c4f57b7b53db977a89d7b2bf1d05760663 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Fri, 20 Jul 2018 17:53:17 +0300 Subject: [PATCH 63/78] add gas coin field --- CHANGELOG.md | 3 ++- api/block.go | 3 +++ api/transaction.go | 1 + api/transactions.go | 3 +++ core/transaction/transaction.go | 1 + 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9c759095..7fe550bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,14 @@ - [api] Add validators rewards to block api ## 0.1.0 -*Jule 17th, 2018* +*Jule 20th, 2018* BREAKING CHANGES - [core] 0.1x transaction fees - [core] Genesis is now encapsulated in code - [core] Add new transaction type: SellAllCoin +- [core] Add GasCoin field to transaction - [config] New config directories - [api] Huge API update. For more info see docs diff --git a/api/block.go b/api/block.go index e711293e2..c2288a81f 100644 --- a/api/block.go +++ b/api/block.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "github.com/MinterTeam/minter-go-node/core/transaction" + "github.com/MinterTeam/minter-go-node/core/types" "github.com/gorilla/mux" "github.com/tendermint/tendermint/libs/common" "math/big" @@ -33,6 +34,7 @@ type BlockTransactionResponse struct { Payload []byte `json:"payload"` ServiceData []byte `json:"service_data"` Gas int64 `json:"gas"` + GasCoin types.CoinSymbol `json:"gas_coin"` TxResult ResponseDeliverTx `json:"tx_result"` } @@ -74,6 +76,7 @@ func Block(w http.ResponseWriter, r *http.Request) { Payload: tx.Payload, ServiceData: tx.ServiceData, Gas: tx.Gas(), + GasCoin: tx.GasCoin, TxResult: ResponseDeliverTx{ Code: blockResults.Results.DeliverTx[i].Code, Data: blockResults.Results.DeliverTx[i].Data, diff --git a/api/transaction.go b/api/transaction.go index d8b45e4c8..63ea45a9e 100644 --- a/api/transaction.go +++ b/api/transaction.go @@ -54,6 +54,7 @@ func Transaction(w http.ResponseWriter, r *http.Request) { From: sender.String(), Nonce: decodedTx.Nonce, GasPrice: decodedTx.GasPrice, + GasCoin: decodedTx.GasCoin, Type: decodedTx.Type, Data: decodedTx.GetDecodedData(), Payload: decodedTx.Payload, diff --git a/api/transactions.go b/api/transactions.go index 977c12c99..4dba95f76 100644 --- a/api/transactions.go +++ b/api/transactions.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "github.com/MinterTeam/minter-go-node/core/transaction" + "github.com/MinterTeam/minter-go-node/core/types" "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/rpc/core/types" "math/big" @@ -19,6 +20,7 @@ type TransactionResponse struct { From string `json:"from"` Nonce uint64 `json:"nonce"` GasPrice *big.Int `json:"gas_price"` + GasCoin types.CoinSymbol `json:"gas_coin"` Type byte `json:"type"` Data transaction.Data `json:"data"` Payload []byte `json:"payload"` @@ -82,6 +84,7 @@ func Transactions(w http.ResponseWriter, r *http.Request) { From: sender.String(), Nonce: decodedTx.Nonce, GasPrice: decodedTx.GasPrice, + GasCoin: decodedTx.GasCoin, Type: decodedTx.Type, Data: decodedTx.GetDecodedData(), Payload: decodedTx.Payload, diff --git a/core/transaction/transaction.go b/core/transaction/transaction.go index ad6c1301b..d8ca7f0b4 100644 --- a/core/transaction/transaction.go +++ b/core/transaction/transaction.go @@ -35,6 +35,7 @@ const ( type Transaction struct { Nonce uint64 GasPrice *big.Int + GasCoin types.CoinSymbol Type byte Data RawData Payload []byte From c4429aaded382d711730048cd22524b7cadb3185 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Sat, 21 Jul 2018 21:57:12 +0300 Subject: [PATCH 64/78] add go report to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3bc556b21..12542d08b 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Minter is a blockchain network that lets people, projects, and companies issue a [![license](https://img.shields.io/github/license/MinterTeam/minter-go-node.svg)](https://github.com/MinterTeam/minter-go-node/blob/master/LICENSE) [![last-commit](https://img.shields.io/github/last-commit/MinterTeam/minter-go-node.svg)](https://github.com/MinterTeam/minter-go-node/commits/master) [![Documentation Status](//readthedocs.org/projects/minter-go-node/badge/?version=latest)](https://minter-go-node.readthedocs.io/en/latest/?badge=latest) +[![Go Report Card](https://goreportcard.com/badge/github.com/MinterTeam/minter-go-node)](https://goreportcard.com/report/github.com/MinterTeam/minter-go-node) _NOTE: This is alpha software. Please contact us if you intend to run it in production._ From 5fe7625ec8ecd0da34a1f17f1d4bf9b84eb78c63 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Sun, 22 Jul 2018 08:46:15 +0300 Subject: [PATCH 65/78] fix readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 12542d08b..da6534834 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ interfaces exposed to other processes, but does not include the in-process Go AP In an effort to avoid accumulating technical debt prior to 1.0.0, we do not guarantee that breaking changes (ie. bumps in the MINOR version) -will work with existing tendermint blockchains. In these cases you will +will work with existing blockchain. In these cases you will have to start a new blockchain, or write something custom to get the old data into the new chain. From b3e90ea5154bfaf3a766bd5ad67a0c45b5bfc665 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Sun, 22 Jul 2018 09:05:03 +0300 Subject: [PATCH 66/78] remove unused code from Makefile --- Makefile | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 5005b327b..a7615a389 100644 --- a/Makefile +++ b/Makefile @@ -106,17 +106,4 @@ build-linux: GOOS=linux GOARCH=amd64 $(MAKE) build build-compress: - upx --brute -9 build/minter - -build-docker-localnode: - cd networks/local - make - -# Run a 4-node testnet locally -localnet-start: localnet-stop - @if ! [ -f build/node0/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/minter:Z minter/localnode testnet --v 4 --o . --populate-persistent-peers --starting-ip-address 192.167.10.2 ; fi - docker-compose up - -# Stop testnet -localnet-stop: - docker-compose down + upx --brute -9 build/minter \ No newline at end of file From 7a12e7b7a9eccd6f360aa613761740103e764621 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Sun, 22 Jul 2018 09:47:30 +0300 Subject: [PATCH 67/78] fix log levels --- Gopkg.lock | 4 ++-- log/log.go | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 3473c84a4..43a283c11 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -186,7 +186,6 @@ revision = "ae68e2d4c00fed4943b5f6698d504a5fe083da8a" [[projects]] - branch = "master" name = "github.com/rcrowley/go-metrics" packages = ["."] revision = "e2704e165165ec55d062f5919b4b29494e9fa790" @@ -248,6 +247,7 @@ "crypto/tmhash", "evidence", "libs/autofile", + "libs/cli/flags", "libs/clist", "libs/common", "libs/db", @@ -384,6 +384,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "22f981ab3844fb5ea210c41876001238322fb5eccb156d57827654904b5f671b" + inputs-digest = "23202c1428a7bc0a7a1946e1c160b380ef49332be93b6b091750e567f0bea609" solver-name = "gps-cdcl" solver-version = 1 diff --git a/log/log.go b/log/log.go index 28d0d0ad2..6680aa64d 100644 --- a/log/log.go +++ b/log/log.go @@ -1,6 +1,8 @@ package log import ( + "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/cli/flags" "github.com/tendermint/tendermint/libs/log" "os" ) @@ -10,7 +12,7 @@ var ( ) func init() { - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + logger, _ := flags.ParseLogLevel(config.DefaultPackageLogLevels(), log.NewTMLogger(os.Stdout), "info") SetLogger(logger) } From 17048fe32150e996e518159df01a3666ff340880 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Sun, 22 Jul 2018 09:48:22 +0300 Subject: [PATCH 68/78] fix linter errors --- api/api.go | 2 +- core/minter/minter.go | 2 +- core/state/state_candidate.go | 2 +- core/state/state_frozen_fund.go | 2 +- core/state/statedb.go | 21 +++++++++------------ core/transaction/redeem_check.go | 2 +- core/transaction/switch_candidate_status.go | 4 ++-- crypto/crypto.go | 6 ------ crypto/crypto_test.go | 2 +- crypto/sha3/xor.go | 2 -- crypto/sha3/xor_unaligned.go | 2 -- tests/acceptance/api/tests.go | 12 ++---------- tests/acceptance/main.go | 2 +- 13 files changed, 20 insertions(+), 41 deletions(-) diff --git a/api/api.go b/api/api.go index 674cd7f79..d36218b62 100644 --- a/api/api.go +++ b/api/api.go @@ -68,7 +68,7 @@ func RunApi(b *minter.Blockchain, node *node.Node) { } func waitForTendermint() { - for true { + for { _, err := client.Health() if err == nil { break diff --git a/core/minter/minter.go b/core/minter/minter.go index c34e039c6..0354a65c1 100644 --- a/core/minter/minter.go +++ b/core/minter/minter.go @@ -205,7 +205,7 @@ func (app *Blockchain) EndBlock(req abciTypes.RequestEndBlock) abciTypes.Respons for _, validator := range app.activeValidators { persisted := false for _, newValidator := range newValidators { - if bytes.Compare(validator.PubKey.Data, newValidator.PubKey.Data) == 0 { + if bytes.Equal(validator.PubKey.Data, newValidator.PubKey.Data) { persisted = true break } diff --git a/core/state/state_candidate.go b/core/state/state_candidate.go index 597d9b57a..f4f23b9d6 100644 --- a/core/state/state_candidate.go +++ b/core/state/state_candidate.go @@ -98,7 +98,7 @@ type Candidate struct { func (candidate Candidate) GetStakeOfAddress(addr types.Address, coin types.CoinSymbol) *Stake { for i, stake := range candidate.Stakes { - if bytes.Compare(stake.Coin.Bytes(), coin.Bytes()) == 0 && bytes.Compare(stake.Owner.Bytes(), addr.Bytes()) == 0 { + if bytes.Equal(stake.Coin.Bytes(), coin.Bytes()) && bytes.Equal(stake.Owner.Bytes(), addr.Bytes()) { return &(candidate.Stakes[i]) } } diff --git a/core/state/state_frozen_fund.go b/core/state/state_frozen_fund.go index 0a350e134..60c983c6c 100644 --- a/core/state/state_frozen_fund.go +++ b/core/state/state_frozen_fund.go @@ -123,7 +123,7 @@ func (c *stateFrozenFund) removeFund(candidateKey []byte) { for _, item := range c.data.List { // skip fund with given candidate key - if bytes.Compare(item.CandidateKey, candidateKey) == 0 { + if bytes.Equal(item.CandidateKey, candidateKey) { continue } diff --git a/core/state/statedb.go b/core/state/statedb.go index b3eaa9f0b..74aa57ba9 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -590,11 +590,8 @@ func (s *StateDB) CoinExists(symbol types.CoinSymbol) bool { } stateCoin := s.getStateCoin(symbol) - if stateCoin != nil { - return true - } - return false + return stateCoin != nil } func (s *StateDB) CandidateExists(key types.Pubkey) bool { @@ -606,7 +603,7 @@ func (s *StateDB) CandidateExists(key types.Pubkey) bool { } for _, candidate := range stateCandidates.data { - if bytes.Compare(candidate.PubKey, key) == 0 { + if bytes.Equal(candidate.PubKey, key) { return true } } @@ -622,7 +619,7 @@ func (s *StateDB) GetStateCandidate(key types.Pubkey) *Candidate { } for i, candidate := range stateCandidates.data { - if bytes.Compare(candidate.PubKey, key) == 0 { + if bytes.Equal(candidate.PubKey, key) { return &(stateCandidates.data[i]) } } @@ -708,7 +705,7 @@ func (s *StateDB) AddAccumReward(pubkey types.Pubkey, reward *big.Int) { stateCandidates := s.getStateCandidates() for i := range stateCandidates.data { - if bytes.Compare(stateCandidates.data[i].PubKey, pubkey) == 0 { + if bytes.Equal(stateCandidates.data[i].PubKey, pubkey) { stateCandidates.data[i].AccumReward.Add(stateCandidates.data[i].AccumReward, reward) s.setStateCandidates(stateCandidates) s.MarkStateCandidateDirty() @@ -846,7 +843,7 @@ func (s *StateDB) SetCandidateOnline(pubkey []byte) { for i := range stateCandidates.data { candidate := &stateCandidates.data[i] - if bytes.Compare(candidate.PubKey, pubkey) == 0 { + if bytes.Equal(candidate.PubKey, pubkey) { candidate.Status = CandidateStatusOnline } } @@ -860,7 +857,7 @@ func (s *StateDB) SetCandidateOffline(pubkey []byte) { for i := range stateCandidates.data { candidate := &stateCandidates.data[i] - if bytes.Compare(candidate.PubKey, pubkey) == 0 { + if bytes.Equal(candidate.PubKey, pubkey) { candidate.Status = CandidateStatusOffline } } @@ -874,7 +871,7 @@ func (s *StateDB) SetValidatorAbsent(pubkey types.Pubkey) { for i := range stateCandidates.data { candidate := &stateCandidates.data[i] - if bytes.Compare(candidate.PubKey, pubkey) == 0 { + if bytes.Equal(candidate.PubKey, pubkey) { if candidate.Status == CandidateStatusOffline { return @@ -917,7 +914,7 @@ func (s *StateDB) PunishByzantineCandidate(PubKey []byte) { for i := range stateCandidates.data { candidate := &stateCandidates.data[i] - if bytes.Compare(candidate.PubKey, PubKey) == 0 { + if bytes.Equal(candidate.PubKey, PubKey) { candidate.AbsentTimes = candidate.AbsentTimes + 1 candidate.Stakes = []Stake{} @@ -948,7 +945,7 @@ func (s *StateDB) SetValidatorPresent(pubkey types.Pubkey) { for i := range stateCandidates.data { candidate := &stateCandidates.data[i] - if bytes.Compare(candidate.PubKey, pubkey) == 0 { + if bytes.Equal(candidate.PubKey, pubkey) { candidate.AbsentTimes = 0 } } diff --git a/core/transaction/redeem_check.go b/core/transaction/redeem_check.go index 26a8df569..3d602737c 100644 --- a/core/transaction/redeem_check.go +++ b/core/transaction/redeem_check.go @@ -106,7 +106,7 @@ func (data RedeemCheckData) Run(sender types.Address, tx *Transaction, context * Log: err.Error()} } - if bytes.Compare(lockPublicKey, pub) != 0 { + if !bytes.Equal(lockPublicKey, pub) { return Response{ Code: code.CheckInvalidLock, Log: fmt.Sprintf("Invalid proof")} diff --git a/core/transaction/switch_candidate_status.go b/core/transaction/switch_candidate_status.go index eb7caa057..7c8ddd5ad 100644 --- a/core/transaction/switch_candidate_status.go +++ b/core/transaction/switch_candidate_status.go @@ -50,7 +50,7 @@ func (data SetCandidateOnData) Run(sender types.Address, tx *Transaction, contex candidate := context.GetStateCandidate(data.PubKey) - if bytes.Compare(candidate.CandidateAddress.Bytes(), sender.Bytes()) != 0 { + if !bytes.Equal(candidate.CandidateAddress.Bytes(), sender.Bytes()) { return Response{ Code: code.IsNotOwnerOfCandidate, Log: fmt.Sprintf("Sender is not an owner of a candidate")} @@ -110,7 +110,7 @@ func (data SetCandidateOffData) Run(sender types.Address, tx *Transaction, conte candidate := context.GetStateCandidate(data.PubKey) - if bytes.Compare(candidate.CandidateAddress.Bytes(), sender.Bytes()) != 0 { + if !bytes.Equal(candidate.CandidateAddress.Bytes(), sender.Bytes()) { return Response{ Code: code.IsNotOwnerOfCandidate, Log: fmt.Sprintf("Sender is not an owner of a candidate")} diff --git a/crypto/crypto.go b/crypto/crypto.go index b41d468f1..62aaa6e69 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -198,9 +198,3 @@ func PubkeyToAddress(p ecdsa.PublicKey) types.Address { pubBytes := FromECDSAPub(&p) return types.BytesToAddress(Keccak256(pubBytes[1:])[12:]) } - -func zeroBytes(bytes []byte) { - for i := range bytes { - bytes[i] = 0 - } -} diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 9a3987133..88bfd54f1 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -176,7 +176,7 @@ func TestLoadECDSAFile(t *testing.T) { func TestValidateSignatureValues(t *testing.T) { check := func(expected bool, v byte, r, s *big.Int) { - if ValidateSignatureValues(v, r, s, false) != expected { + if ValidateSignatureValues(v, r, s) != expected { t.Errorf("mismatch for v: %d r: %d s: %d want: %v", v, r, s, expected) } } diff --git a/crypto/sha3/xor.go b/crypto/sha3/xor.go index 46a0d63a6..42137426b 100644 --- a/crypto/sha3/xor.go +++ b/crypto/sha3/xor.go @@ -12,5 +12,3 @@ var ( xorInUnaligned = xorInGeneric copyOutUnaligned = copyOutGeneric ) - -const xorImplementationUnaligned = "generic" diff --git a/crypto/sha3/xor_unaligned.go b/crypto/sha3/xor_unaligned.go index 929a486a7..6b7a6628c 100644 --- a/crypto/sha3/xor_unaligned.go +++ b/crypto/sha3/xor_unaligned.go @@ -54,5 +54,3 @@ var ( xorIn = xorInUnaligned copyOut = copyOutUnaligned ) - -const xorImplementationUnaligned = "unaligned" diff --git a/tests/acceptance/api/tests.go b/tests/acceptance/api/tests.go index b2bd811c5..991323808 100644 --- a/tests/acceptance/api/tests.go +++ b/tests/acceptance/api/tests.go @@ -32,11 +32,7 @@ func TestApiStatus() error { err = json.Unmarshal(data, &status) - if err != nil { - return err - } - - return nil + return err } func TestApiBlock() error { @@ -59,9 +55,5 @@ func TestApiBlock() error { err = json.Unmarshal(data, &blockResult) - if err != nil { - return err - } - - return nil + return err } diff --git a/tests/acceptance/main.go b/tests/acceptance/main.go index 8d568c13b..8c0dce2be 100644 --- a/tests/acceptance/main.go +++ b/tests/acceptance/main.go @@ -45,7 +45,7 @@ func main() { logger.Fatalf("Failed test \"%s\"\nReason: %s", testName, err) } - elapsed := time.Now().Sub(start) + elapsed := time.Since(start) logger.Printf("Completed \"%s\" in %s \n", testName, elapsed) } From 321228856711efcabd5665471c8f4eebbbf20c6b Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Sun, 22 Jul 2018 09:54:02 +0300 Subject: [PATCH 69/78] fix log level --- log/log.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/log/log.go b/log/log.go index 6680aa64d..754891696 100644 --- a/log/log.go +++ b/log/log.go @@ -1,7 +1,6 @@ package log import ( - "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/cli/flags" "github.com/tendermint/tendermint/libs/log" "os" @@ -12,7 +11,7 @@ var ( ) func init() { - logger, _ := flags.ParseLogLevel(config.DefaultPackageLogLevels(), log.NewTMLogger(os.Stdout), "info") + logger, _ := flags.ParseLogLevel("consensus:info,state:info,*:error", log.NewTMLogger(os.Stdout), "info") SetLogger(logger) } From fe794487b9c19e90cedc4f7ce9dfe4f5a9f7ee83 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Mon, 23 Jul 2018 11:13:40 +0300 Subject: [PATCH 70/78] add custom gas coin processing --- core/transaction/buy_coin.go | 42 +++++++++------- core/transaction/create_coin.go | 35 ++++++++++---- core/transaction/declare_candidacy.go | 19 +++++--- core/transaction/delegate.go | 19 +++++--- core/transaction/sell_coin.go | 19 ++++---- core/transaction/send.go | 23 +++++---- core/transaction/switch_candidate_status.go | 53 ++++++++++++++++----- core/transaction/unbond.go | 26 +++++++--- 8 files changed, 160 insertions(+), 76 deletions(-) diff --git a/core/transaction/buy_coin.go b/core/transaction/buy_coin.go index 1161f8e55..fcf50e165 100644 --- a/core/transaction/buy_coin.go +++ b/core/transaction/buy_coin.go @@ -47,34 +47,46 @@ func (data BuyCoinData) Run(sender types.Address, tx *Transaction, context *stat Log: fmt.Sprintf("\"From\" coin equals to \"to\" coin")} } + if !context.CoinExists(tx.GasCoin) { + return Response{ + Code: code.CoinNotExists, + Log: fmt.Sprintf("Coin %s not exists", tx.GasCoin)} + } + if !context.CoinExists(data.CoinToSell) { return Response{ Code: code.CoinNotExists, - Log: fmt.Sprintf("Coin not exists")} + Log: fmt.Sprintf("Coin %s not exists", data.CoinToSell)} } if !context.CoinExists(data.CoinToBuy) { return Response{ Code: code.CoinNotExists, - Log: fmt.Sprintf("Coin not exists")} + Log: fmt.Sprintf("Coin %s not exists", data.CoinToBuy)} } commissionInBaseCoin := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) commission := big.NewInt(0).Set(commissionInBaseCoin) - if data.CoinToSell != types.GetBaseCoin() { - coin := context.GetStateCoin(data.CoinToSell) + if tx.GasCoin != types.GetBaseCoin() { + coin := context.GetStateCoin(tx.GasCoin) if coin.ReserveBalance().Cmp(commissionInBaseCoin) < 0 { return Response{ Code: code.CoinReserveNotSufficient, - Log: fmt.Sprintf("Coin reserve balance is not sufficient for transaction. Has: %s, required %s", coin.ReserveBalance().String(), commissionInBaseCoin.String())} + Log: fmt.Sprintf("Gas coin reserve balance is not sufficient for transaction. Has: %s %s, required %s %s", coin.ReserveBalance().String(), types.GetBaseCoin(), commissionInBaseCoin.String(), types.GetBaseCoin())} } commission = formula.CalculateSaleAmount(coin.Volume(), coin.ReserveBalance(), coin.Data().Crr, commissionInBaseCoin) } + if context.GetBalance(sender, tx.GasCoin).Cmp(commission) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s.", sender.String(), commission.String(), tx.GasCoin)} + } + var value *big.Int if data.CoinToSell == types.GetBaseCoin() { @@ -82,11 +94,10 @@ func (data BuyCoinData) Run(sender types.Address, tx *Transaction, context *stat value = formula.CalculatePurchaseAmount(coin.Volume, coin.ReserveBalance, coin.Crr, data.ValueToBuy) - totalTxCost := big.NewInt(0).Add(value, commission) - if context.GetBalance(sender, data.CoinToSell).Cmp(totalTxCost) < 0 { + if context.GetBalance(sender, data.CoinToSell).Cmp(value) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), totalTxCost)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), value.String(), data.CoinToSell)} } if !isCheck { @@ -103,7 +114,7 @@ func (data BuyCoinData) Run(sender types.Address, tx *Transaction, context *stat if context.GetBalance(sender, data.CoinToSell).Cmp(totalTxCost) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), totalTxCost)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s ", sender.String(), totalTxCost.String(), data.CoinToSell)} } if !isCheck { @@ -118,11 +129,10 @@ func (data BuyCoinData) Run(sender types.Address, tx *Transaction, context *stat baseCoinNeeded := formula.CalculatePurchaseAmount(coinTo.Volume, coinTo.ReserveBalance, coinTo.Crr, data.ValueToBuy) value = formula.CalculateSaleAmount(coinFrom.Volume, coinFrom.ReserveBalance, coinFrom.Crr, baseCoinNeeded) - totalTxCost := big.NewInt(0).Add(value, commission) - if context.GetBalance(sender, data.CoinToSell).Cmp(totalTxCost) < 0 { + if context.GetBalance(sender, data.CoinToSell).Cmp(value) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), totalTxCost)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), value.String(), data.CoinToSell)} } if !isCheck { @@ -139,11 +149,11 @@ func (data BuyCoinData) Run(sender types.Address, tx *Transaction, context *stat if !isCheck { rewardPull.Add(rewardPull, commissionInBaseCoin) - context.SubBalance(sender, data.CoinToSell, commission) + context.SubBalance(sender, tx.GasCoin, commission) - if data.CoinToSell != types.GetBaseCoin() { - context.SubCoinVolume(data.CoinToSell, commission) - context.SubCoinReserve(data.CoinToSell, commissionInBaseCoin) + if tx.GasCoin != types.GetBaseCoin() { + context.SubCoinVolume(tx.GasCoin, commission) + context.SubCoinReserve(tx.GasCoin, commissionInBaseCoin) } context.AddBalance(sender, data.CoinToBuy, value) diff --git a/core/transaction/create_coin.go b/core/transaction/create_coin.go index af2eb3c3d..2f8dd1f14 100644 --- a/core/transaction/create_coin.go +++ b/core/transaction/create_coin.go @@ -8,6 +8,7 @@ import ( "github.com/MinterTeam/minter-go-node/core/commissions" "github.com/MinterTeam/minter-go-node/core/state" "github.com/MinterTeam/minter-go-node/core/types" + "github.com/MinterTeam/minter-go-node/formula" "github.com/tendermint/tendermint/libs/common" "math/big" "regexp" @@ -62,8 +63,8 @@ func (data CreateCoinData) Run(sender types.Address, tx *Transaction, context *s Log: fmt.Sprintf("Invalid coin symbol. Should be %s", allowedCoinSymbols)} } - commission := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) - commission.Mul(commission, CommissionMultiplier) + commissionInBaseCoin := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) + commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) // compute additional price from letters count lettersCount := len(data.Symbol.String()) @@ -85,14 +86,31 @@ func (data CreateCoinData) Run(sender types.Address, tx *Transaction, context *s p := big.NewInt(10) p.Exp(p, big.NewInt(18), nil) p.Mul(p, big.NewInt(price)) - commission.Add(commission, p) + commissionInBaseCoin.Add(commissionInBaseCoin, p) + commission := big.NewInt(0).Set(commissionInBaseCoin) - totalTxCost := big.NewInt(0).Add(data.InitialReserve, commission) + if tx.GasCoin != types.GetBaseCoin() { + coin := context.GetStateCoin(tx.GasCoin) - if context.GetBalance(sender, types.GetBaseCoin()).Cmp(totalTxCost) < 0 { + if coin.ReserveBalance().Cmp(commissionInBaseCoin) < 0 { + return Response{ + Code: code.CoinReserveNotSufficient, + Log: fmt.Sprintf("Gas coin reserve balance is not sufficient for transaction. Has: %s %s, required %s %s", coin.ReserveBalance().String(), types.GetBaseCoin(), commissionInBaseCoin.String(), types.GetBaseCoin())} + } + + commission = formula.CalculateSaleAmount(coin.Volume(), coin.ReserveBalance(), coin.Data().Crr, commissionInBaseCoin) + } + + if context.GetBalance(sender, tx.GasCoin).Cmp(commission) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), commission.String(), tx.GasCoin)} + } + + if context.GetBalance(sender, types.GetBaseCoin()).Cmp(data.InitialReserve) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), totalTxCost)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), data.InitialReserve.String(), types.GetBaseCoin())} } if context.CoinExists(data.Symbol) { @@ -108,9 +126,10 @@ func (data CreateCoinData) Run(sender types.Address, tx *Transaction, context *s } if !isCheck { - rewardPull.Add(rewardPull, commission) + rewardPull.Add(rewardPull, commissionInBaseCoin) - context.SubBalance(sender, types.GetBaseCoin(), totalTxCost) + context.SubBalance(sender, types.GetBaseCoin(), data.InitialReserve) + context.SubBalance(sender, tx.GasCoin, commission) context.CreateCoin(data.Symbol, data.Name, data.InitialAmount, data.ConstantReserveRatio, data.InitialReserve, sender) context.AddBalance(sender, data.Symbol, data.InitialAmount) context.SetNonce(sender, tx.Nonce) diff --git a/core/transaction/declare_candidacy.go b/core/transaction/declare_candidacy.go index 72a5cfa4f..dd583fb3e 100644 --- a/core/transaction/declare_candidacy.go +++ b/core/transaction/declare_candidacy.go @@ -59,8 +59,8 @@ func (data DeclareCandidacyData) Run(sender types.Address, tx *Transaction, cont commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) commission := big.NewInt(0).Set(commissionInBaseCoin) - if data.Coin != types.GetBaseCoin() { - coin := context.GetStateCoin(data.Coin) + if tx.GasCoin != types.GetBaseCoin() { + coin := context.GetStateCoin(tx.GasCoin) if coin.ReserveBalance().Cmp(commissionInBaseCoin) < 0 { return Response{ @@ -71,12 +71,16 @@ func (data DeclareCandidacyData) Run(sender types.Address, tx *Transaction, cont commission = formula.CalculateSaleAmount(coin.Volume(), coin.ReserveBalance(), coin.Data().Crr, commissionInBaseCoin) } - totalTxCost := big.NewInt(0).Add(data.Stake, commission) + if context.GetBalance(sender, data.Coin).Cmp(data.Stake) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), data.Stake, data.Coin)} + } - if context.GetBalance(sender, data.Coin).Cmp(totalTxCost) < 0 { + if context.GetBalance(sender, tx.GasCoin).Cmp(commission) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), totalTxCost)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), commission, tx.GasCoin)} } if context.CandidateExists(data.PubKey) { @@ -94,9 +98,10 @@ func (data DeclareCandidacyData) Run(sender types.Address, tx *Transaction, cont // TODO: limit number of candidates to prevent flooding if !isCheck { - rewardPull.Add(rewardPull, commission) + rewardPull.Add(rewardPull, commissionInBaseCoin) - context.SubBalance(sender, data.Coin, totalTxCost) + context.SubBalance(sender, data.Coin, data.Stake) + context.SubBalance(sender, tx.GasCoin, commission) context.CreateCandidate(data.Address, data.PubKey, data.Commission, uint(currentBlock), data.Coin, data.Stake) context.SetNonce(sender, tx.Nonce) } diff --git a/core/transaction/delegate.go b/core/transaction/delegate.go index 533db5c2c..f2dbea27b 100644 --- a/core/transaction/delegate.go +++ b/core/transaction/delegate.go @@ -44,8 +44,8 @@ func (data DelegateData) Run(sender types.Address, tx *Transaction, context *sta commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) commission := big.NewInt(0).Set(commissionInBaseCoin) - if data.Coin != types.GetBaseCoin() { - coin := context.GetStateCoin(data.Coin) + if tx.GasCoin != types.GetBaseCoin() { + coin := context.GetStateCoin(tx.GasCoin) if coin.ReserveBalance().Cmp(commissionInBaseCoin) < 0 { return Response{ @@ -56,12 +56,16 @@ func (data DelegateData) Run(sender types.Address, tx *Transaction, context *sta commission = formula.CalculateSaleAmount(coin.Volume(), coin.ReserveBalance(), coin.Data().Crr, commissionInBaseCoin) } - totalTxCost := big.NewInt(0).Add(data.Stake, commission) + if context.GetBalance(sender, tx.GasCoin).Cmp(commission) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), commission, tx.GasCoin)} + } - if context.GetBalance(sender, data.Coin).Cmp(totalTxCost) < 0 { + if context.GetBalance(sender, data.Coin).Cmp(data.Stake) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), totalTxCost)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), data.Stake, data.Coin)} } if !context.CandidateExists(data.PubKey) { @@ -71,9 +75,10 @@ func (data DelegateData) Run(sender types.Address, tx *Transaction, context *sta } if !isCheck { - rewardPull.Add(rewardPull, commission) + rewardPull.Add(rewardPull, commissionInBaseCoin) - context.SubBalance(sender, data.Coin, totalTxCost) + context.SubBalance(sender, tx.GasCoin, commission) + context.SubBalance(sender, data.Coin, data.Stake) context.Delegate(sender, data.PubKey, data.Coin, data.Stake) context.SetNonce(sender, tx.Nonce) } diff --git a/core/transaction/sell_coin.go b/core/transaction/sell_coin.go index b462b4ac5..cb1829604 100644 --- a/core/transaction/sell_coin.go +++ b/core/transaction/sell_coin.go @@ -63,8 +63,8 @@ func (data SellCoinData) Run(sender types.Address, tx *Transaction, context *sta commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) commission := big.NewInt(0).Set(commissionInBaseCoin) - if data.CoinToSell != types.GetBaseCoin() { - coin := context.GetStateCoin(data.CoinToSell) + if tx.GasCoin != types.GetBaseCoin() { + coin := context.GetStateCoin(tx.GasCoin) if coin.ReserveBalance().Cmp(commissionInBaseCoin) < 0 { return Response{ @@ -75,22 +75,21 @@ func (data SellCoinData) Run(sender types.Address, tx *Transaction, context *sta commission = formula.CalculateSaleAmount(coin.Volume(), coin.ReserveBalance(), coin.Data().Crr, commissionInBaseCoin) } - totalTxCost := big.NewInt(0).Add(data.ValueToSell, commission) - - if context.GetBalance(sender, data.CoinToSell).Cmp(totalTxCost) < 0 { + if context.GetBalance(sender, data.CoinToSell).Cmp(data.ValueToSell) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), totalTxCost)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), data.ValueToSell)} } if !isCheck { rewardPull.Add(rewardPull, commissionInBaseCoin) - context.SubBalance(sender, data.CoinToSell, totalTxCost) + context.SubBalance(sender, data.CoinToSell, data.ValueToSell) + context.SubBalance(sender, tx.GasCoin, commission) - if data.CoinToSell != types.GetBaseCoin() { - context.SubCoinVolume(data.CoinToSell, commission) - context.SubCoinReserve(data.CoinToSell, commissionInBaseCoin) + if tx.GasCoin != types.GetBaseCoin() { + context.SubCoinVolume(tx.GasCoin, commission) + context.SubCoinReserve(tx.GasCoin, commissionInBaseCoin) } } diff --git a/core/transaction/send.go b/core/transaction/send.go index 26d786a02..44ba03f4e 100644 --- a/core/transaction/send.go +++ b/core/transaction/send.go @@ -51,8 +51,8 @@ func (data SendData) Run(sender types.Address, tx *Transaction, context *state.S commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) commission := big.NewInt(0).Set(commissionInBaseCoin) - if data.Coin != types.GetBaseCoin() { - coin := context.GetStateCoin(data.Coin) + if tx.GasCoin != types.GetBaseCoin() { + coin := context.GetStateCoin(tx.GasCoin) if coin.ReserveBalance().Cmp(commissionInBaseCoin) < 0 { return Response{ @@ -63,23 +63,28 @@ func (data SendData) Run(sender types.Address, tx *Transaction, context *state.S commission = formula.CalculateSaleAmount(coin.Volume(), coin.ReserveBalance(), coin.Data().Crr, commissionInBaseCoin) } - totalTxCost := big.NewInt(0).Add(data.Value, commission) + if context.GetBalance(sender, data.Coin).Cmp(data.Value) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), data.Value, data.Coin)} + } - if context.GetBalance(sender, data.Coin).Cmp(totalTxCost) < 0 { + if context.GetBalance(sender, tx.GasCoin).Cmp(commission) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), totalTxCost)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), data.Value, tx.GasCoin)} } if !isCheck { rewardPull.Add(rewardPull, commissionInBaseCoin) - if data.Coin != types.GetBaseCoin() { - context.SubCoinVolume(data.Coin, commission) - context.SubCoinReserve(data.Coin, commissionInBaseCoin) + if tx.GasCoin != types.GetBaseCoin() { + context.SubCoinVolume(tx.GasCoin, commission) + context.SubCoinReserve(tx.GasCoin, commissionInBaseCoin) } - context.SubBalance(sender, data.Coin, totalTxCost) + context.SubBalance(sender, tx.GasCoin, commission) + context.SubBalance(sender, data.Coin, data.Value) context.AddBalance(data.To, data.Coin, data.Value) context.SetNonce(sender, tx.Nonce) } diff --git a/core/transaction/switch_candidate_status.go b/core/transaction/switch_candidate_status.go index 7c8ddd5ad..8092c7326 100644 --- a/core/transaction/switch_candidate_status.go +++ b/core/transaction/switch_candidate_status.go @@ -8,6 +8,7 @@ import ( "github.com/MinterTeam/minter-go-node/core/commissions" "github.com/MinterTeam/minter-go-node/core/state" "github.com/MinterTeam/minter-go-node/core/types" + "github.com/MinterTeam/minter-go-node/formula" "math/big" ) @@ -33,13 +34,26 @@ func (data SetCandidateOnData) Gas() int64 { } func (data SetCandidateOnData) Run(sender types.Address, tx *Transaction, context *state.StateDB, isCheck bool, rewardPull *big.Int, currentBlock uint64) Response { - commission := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) - commission.Mul(commission, CommissionMultiplier) + commissionInBaseCoin := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) + commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) + commission := big.NewInt(0).Set(commissionInBaseCoin) - if context.GetBalance(sender, types.GetBaseCoin()).Cmp(commission) < 0 { + if tx.GasCoin != types.GetBaseCoin() { + coin := context.GetStateCoin(tx.GasCoin) + + if coin.ReserveBalance().Cmp(commissionInBaseCoin) < 0 { + return Response{ + Code: code.CoinReserveNotSufficient, + Log: fmt.Sprintf("Coin reserve balance is not sufficient for transaction. Has: %s, required %s", coin.ReserveBalance().String(), commissionInBaseCoin.String())} + } + + commission = formula.CalculateSaleAmount(coin.Volume(), coin.ReserveBalance(), coin.Data().Crr, commissionInBaseCoin) + } + + if context.GetBalance(sender, tx.GasCoin).Cmp(commission) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), commission)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), commission, tx.GasCoin)} } if !context.CandidateExists(data.PubKey) { @@ -57,9 +71,9 @@ func (data SetCandidateOnData) Run(sender types.Address, tx *Transaction, contex } if !isCheck { - rewardPull.Add(rewardPull, commission) + rewardPull.Add(rewardPull, commissionInBaseCoin) - context.SubBalance(sender, types.GetBaseCoin(), commission) + context.SubBalance(sender, tx.GasCoin, commission) context.SetCandidateOnline(data.PubKey) context.SetNonce(sender, tx.Nonce) } @@ -93,19 +107,32 @@ func (data SetCandidateOffData) Gas() int64 { } func (data SetCandidateOffData) Run(sender types.Address, tx *Transaction, context *state.StateDB, isCheck bool, rewardPull *big.Int, currentBlock uint64) Response { - commission := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) - commission.Mul(commission, CommissionMultiplier) + commissionInBaseCoin := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) + commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) + commission := big.NewInt(0).Set(commissionInBaseCoin) + + if tx.GasCoin != types.GetBaseCoin() { + coin := context.GetStateCoin(tx.GasCoin) - if context.GetBalance(sender, types.GetBaseCoin()).Cmp(commission) < 0 { + if coin.ReserveBalance().Cmp(commissionInBaseCoin) < 0 { + return Response{ + Code: code.CoinReserveNotSufficient, + Log: fmt.Sprintf("Coin reserve balance is not sufficient for transaction. Has: %s, required %s", coin.ReserveBalance().String(), commissionInBaseCoin.String())} + } + + commission = formula.CalculateSaleAmount(coin.Volume(), coin.ReserveBalance(), coin.Data().Crr, commissionInBaseCoin) + } + + if context.GetBalance(sender, tx.GasCoin).Cmp(commission) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), commission)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), commission, tx.GasCoin)} } if !context.CandidateExists(data.PubKey) { return Response{ Code: code.CandidateNotFound, - Log: fmt.Sprintf("Candidate with such public key not found")} + Log: fmt.Sprintf("Candidate with such public key (%x) not found", data.PubKey)} } candidate := context.GetStateCandidate(data.PubKey) @@ -117,9 +144,9 @@ func (data SetCandidateOffData) Run(sender types.Address, tx *Transaction, conte } if !isCheck { - rewardPull.Add(rewardPull, commission) + rewardPull.Add(rewardPull, commissionInBaseCoin) - context.SubBalance(sender, types.GetBaseCoin(), commission) + context.SubBalance(sender, tx.GasCoin, commission) context.SetCandidateOffline(data.PubKey) context.SetNonce(sender, tx.Nonce) } diff --git a/core/transaction/unbond.go b/core/transaction/unbond.go index 41d52a4d3..154fb8094 100644 --- a/core/transaction/unbond.go +++ b/core/transaction/unbond.go @@ -7,6 +7,7 @@ import ( "github.com/MinterTeam/minter-go-node/core/commissions" "github.com/MinterTeam/minter-go-node/core/state" "github.com/MinterTeam/minter-go-node/core/types" + "github.com/MinterTeam/minter-go-node/formula" "github.com/MinterTeam/minter-go-node/hexutil" "math/big" ) @@ -41,13 +42,26 @@ func (data UnbondData) Gas() int64 { } func (data UnbondData) Run(sender types.Address, tx *Transaction, context *state.StateDB, isCheck bool, rewardPull *big.Int, currentBlock uint64) Response { - commission := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) - commission.Mul(commission, CommissionMultiplier) + commissionInBaseCoin := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) + commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) + commission := big.NewInt(0).Set(commissionInBaseCoin) - if context.GetBalance(sender, types.GetBaseCoin()).Cmp(commission) < 0 { + if tx.GasCoin != types.GetBaseCoin() { + coin := context.GetStateCoin(tx.GasCoin) + + if coin.ReserveBalance().Cmp(commissionInBaseCoin) < 0 { + return Response{ + Code: code.CoinReserveNotSufficient, + Log: fmt.Sprintf("Coin reserve balance is not sufficient for transaction. Has: %s, required %s", coin.ReserveBalance().String(), commissionInBaseCoin.String())} + } + + commission = formula.CalculateSaleAmount(coin.Volume(), coin.ReserveBalance(), coin.Data().Crr, commissionInBaseCoin) + } + + if context.GetBalance(sender, tx.GasCoin).Cmp(commission) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), commission)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), commission, tx.GasCoin)} } if !context.CandidateExists(data.PubKey) { @@ -76,9 +90,9 @@ func (data UnbondData) Run(sender types.Address, tx *Transaction, context *state // now + 31 days unbondAtBlock := currentBlock + unbondPeriod - rewardPull.Add(rewardPull, commission) + rewardPull.Add(rewardPull, commissionInBaseCoin) - context.SubBalance(sender, types.GetBaseCoin(), commission) + context.SubBalance(sender, tx.GasCoin, commission) context.SubStake(sender, data.PubKey, data.Coin, data.Value) context.GetOrNewStateFrozenFunds(unbondAtBlock).AddFund(sender, data.PubKey, data.Coin, data.Value) context.SetNonce(sender, tx.Nonce) From c393615c55dc08081f290cd205256e7a3ee34fef Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Mon, 23 Jul 2018 11:26:29 +0300 Subject: [PATCH 71/78] check if gas coin exists --- core/transaction/create_coin.go | 7 +++++++ core/transaction/declare_candidacy.go | 7 +++++++ core/transaction/delegate.go | 7 +++++++ core/transaction/sell_coin.go | 6 ++++++ core/transaction/send.go | 8 +++++++- core/transaction/switch_candidate_status.go | 14 ++++++++++++++ core/transaction/unbond.go | 7 +++++++ 7 files changed, 55 insertions(+), 1 deletion(-) diff --git a/core/transaction/create_coin.go b/core/transaction/create_coin.go index 2f8dd1f14..05a41ba9e 100644 --- a/core/transaction/create_coin.go +++ b/core/transaction/create_coin.go @@ -51,6 +51,13 @@ func (data CreateCoinData) Gas() int64 { } func (data CreateCoinData) Run(sender types.Address, tx *Transaction, context *state.StateDB, isCheck bool, rewardPull *big.Int, currentBlock uint64) Response { + + if !context.CoinExists(tx.GasCoin) { + return Response{ + Code: code.CoinNotExists, + Log: fmt.Sprintf("Coin %s not exists", tx.GasCoin)} + } + if len(data.Name) > maxCoinNameBytes { return Response{ Code: code.InvalidCoinName, diff --git a/core/transaction/declare_candidacy.go b/core/transaction/declare_candidacy.go index dd583fb3e..5d3b52c73 100644 --- a/core/transaction/declare_candidacy.go +++ b/core/transaction/declare_candidacy.go @@ -49,6 +49,13 @@ func (data DeclareCandidacyData) Gas() int64 { } func (data DeclareCandidacyData) Run(sender types.Address, tx *Transaction, context *state.StateDB, isCheck bool, rewardPull *big.Int, currentBlock uint64) Response { + + if !context.CoinExists(tx.GasCoin) { + return Response{ + Code: code.CoinNotExists, + Log: fmt.Sprintf("Coin %s not exists", tx.GasCoin)} + } + if len(data.PubKey) != 32 { return Response{ Code: code.IncorrectPubKey, diff --git a/core/transaction/delegate.go b/core/transaction/delegate.go index f2dbea27b..24acd64f0 100644 --- a/core/transaction/delegate.go +++ b/core/transaction/delegate.go @@ -40,6 +40,13 @@ func (data DelegateData) Gas() int64 { } func (data DelegateData) Run(sender types.Address, tx *Transaction, context *state.StateDB, isCheck bool, rewardPull *big.Int, currentBlock uint64) Response { + + if !context.CoinExists(tx.GasCoin) { + return Response{ + Code: code.CoinNotExists, + Log: fmt.Sprintf("Coin %s not exists", tx.GasCoin)} + } + commissionInBaseCoin := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) commission := big.NewInt(0).Set(commissionInBaseCoin) diff --git a/core/transaction/sell_coin.go b/core/transaction/sell_coin.go index cb1829604..5ad00050a 100644 --- a/core/transaction/sell_coin.go +++ b/core/transaction/sell_coin.go @@ -59,6 +59,12 @@ func (data SellCoinData) Run(sender types.Address, tx *Transaction, context *sta Log: fmt.Sprintf("Coin not exists")} } + if !context.CoinExists(tx.GasCoin) { + return Response{ + Code: code.CoinNotExists, + Log: fmt.Sprintf("Coin %s not exists", tx.GasCoin)} + } + commissionInBaseCoin := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) commission := big.NewInt(0).Set(commissionInBaseCoin) diff --git a/core/transaction/send.go b/core/transaction/send.go index 44ba03f4e..7fd0b3b52 100644 --- a/core/transaction/send.go +++ b/core/transaction/send.go @@ -44,7 +44,13 @@ func (data SendData) Run(sender types.Address, tx *Transaction, context *state.S if !context.CoinExists(data.Coin) { return Response{ Code: code.CoinNotExists, - Log: fmt.Sprintf("Coin not exists")} + Log: fmt.Sprintf("Coin %s not exists", data.Coin)} + } + + if !context.CoinExists(tx.GasCoin) { + return Response{ + Code: code.CoinNotExists, + Log: fmt.Sprintf("Coin %s not exists", tx.GasCoin)} } commissionInBaseCoin := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) diff --git a/core/transaction/switch_candidate_status.go b/core/transaction/switch_candidate_status.go index 8092c7326..f15a65ce4 100644 --- a/core/transaction/switch_candidate_status.go +++ b/core/transaction/switch_candidate_status.go @@ -34,6 +34,13 @@ func (data SetCandidateOnData) Gas() int64 { } func (data SetCandidateOnData) Run(sender types.Address, tx *Transaction, context *state.StateDB, isCheck bool, rewardPull *big.Int, currentBlock uint64) Response { + + if !context.CoinExists(tx.GasCoin) { + return Response{ + Code: code.CoinNotExists, + Log: fmt.Sprintf("Coin %s not exists", tx.GasCoin)} + } + commissionInBaseCoin := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) commission := big.NewInt(0).Set(commissionInBaseCoin) @@ -107,6 +114,13 @@ func (data SetCandidateOffData) Gas() int64 { } func (data SetCandidateOffData) Run(sender types.Address, tx *Transaction, context *state.StateDB, isCheck bool, rewardPull *big.Int, currentBlock uint64) Response { + + if !context.CoinExists(tx.GasCoin) { + return Response{ + Code: code.CoinNotExists, + Log: fmt.Sprintf("Coin %s not exists", tx.GasCoin)} + } + commissionInBaseCoin := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) commission := big.NewInt(0).Set(commissionInBaseCoin) diff --git a/core/transaction/unbond.go b/core/transaction/unbond.go index 154fb8094..c5ded0207 100644 --- a/core/transaction/unbond.go +++ b/core/transaction/unbond.go @@ -42,6 +42,13 @@ func (data UnbondData) Gas() int64 { } func (data UnbondData) Run(sender types.Address, tx *Transaction, context *state.StateDB, isCheck bool, rewardPull *big.Int, currentBlock uint64) Response { + + if !context.CoinExists(tx.GasCoin) { + return Response{ + Code: code.CoinNotExists, + Log: fmt.Sprintf("Coin %s not exists", tx.GasCoin)} + } + commissionInBaseCoin := big.NewInt(0).Mul(tx.GasPrice, big.NewInt(tx.Gas())) commissionInBaseCoin.Mul(commissionInBaseCoin, CommissionMultiplier) commission := big.NewInt(0).Set(commissionInBaseCoin) From 4b69cae8b7e971137d210732b4a16590cc069693 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Mon, 23 Jul 2018 11:29:51 +0300 Subject: [PATCH 72/78] fix --- core/transaction/send.go | 2 +- core/transaction/transaction.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/transaction/send.go b/core/transaction/send.go index 7fd0b3b52..65556e36c 100644 --- a/core/transaction/send.go +++ b/core/transaction/send.go @@ -78,7 +78,7 @@ func (data SendData) Run(sender types.Address, tx *Transaction, context *state.S if context.GetBalance(sender, tx.GasCoin).Cmp(commission) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), data.Value, tx.GasCoin)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), commission, tx.GasCoin)} } if !isCheck { diff --git a/core/transaction/transaction.go b/core/transaction/transaction.go index d8ca7f0b4..c8e88337a 100644 --- a/core/transaction/transaction.go +++ b/core/transaction/transaction.go @@ -102,6 +102,7 @@ func (tx *Transaction) Hash() types.Hash { return rlpHash([]interface{}{ tx.Nonce, tx.GasPrice, + tx.GasCoin, tx.Type, tx.Data, tx.Payload, From 8b646403e9fe6f402fb799791b67495827d45a21 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Mon, 23 Jul 2018 11:37:00 +0300 Subject: [PATCH 73/78] advanced check if balance is sufficient --- core/transaction/buy_coin.go | 38 ++++++++++++++++++++++++++- core/transaction/create_coin.go | 12 +++++++++ core/transaction/declare_candidacy.go | 12 +++++++++ core/transaction/delegate.go | 12 +++++++++ core/transaction/sell_coin.go | 12 +++++++++ core/transaction/send.go | 12 +++++++++ 6 files changed, 97 insertions(+), 1 deletion(-) diff --git a/core/transaction/buy_coin.go b/core/transaction/buy_coin.go index fcf50e165..5f2af9af7 100644 --- a/core/transaction/buy_coin.go +++ b/core/transaction/buy_coin.go @@ -100,6 +100,18 @@ func (data BuyCoinData) Run(sender types.Address, tx *Transaction, context *stat Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), value.String(), data.CoinToSell)} } + if data.CoinToSell == tx.GasCoin { + totalTxCost := big.NewInt(0) + totalTxCost.Add(totalTxCost, value) + totalTxCost.Add(totalTxCost, commission) + + if context.GetBalance(sender, data.CoinToSell).Cmp(totalTxCost) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), totalTxCost.String(), tx.GasCoin)} + } + } + if !isCheck { context.SubBalance(sender, data.CoinToSell, value) context.AddCoinVolume(data.CoinToBuy, data.ValueToBuy) @@ -114,7 +126,19 @@ func (data BuyCoinData) Run(sender types.Address, tx *Transaction, context *stat if context.GetBalance(sender, data.CoinToSell).Cmp(totalTxCost) < 0 { return Response{ Code: code.InsufficientFunds, - Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s ", sender.String(), totalTxCost.String(), data.CoinToSell)} + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), totalTxCost.String(), data.CoinToSell)} + } + + if data.CoinToSell == tx.GasCoin { + totalTxCost := big.NewInt(0) + totalTxCost.Add(totalTxCost, value) + totalTxCost.Add(totalTxCost, commission) + + if context.GetBalance(sender, data.CoinToSell).Cmp(totalTxCost) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), totalTxCost.String(), tx.GasCoin)} + } } if !isCheck { @@ -135,6 +159,18 @@ func (data BuyCoinData) Run(sender types.Address, tx *Transaction, context *stat Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), value.String(), data.CoinToSell)} } + if data.CoinToSell == tx.GasCoin { + totalTxCost := big.NewInt(0) + totalTxCost.Add(totalTxCost, value) + totalTxCost.Add(totalTxCost, commission) + + if context.GetBalance(sender, data.CoinToSell).Cmp(totalTxCost) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), totalTxCost.String(), tx.GasCoin)} + } + } + if !isCheck { context.SubBalance(sender, data.CoinToSell, value) diff --git a/core/transaction/create_coin.go b/core/transaction/create_coin.go index 05a41ba9e..8df7f2cb7 100644 --- a/core/transaction/create_coin.go +++ b/core/transaction/create_coin.go @@ -120,6 +120,18 @@ func (data CreateCoinData) Run(sender types.Address, tx *Transaction, context *s Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), data.InitialReserve.String(), types.GetBaseCoin())} } + if types.GetBaseCoin() == tx.GasCoin { + totalTxCost := big.NewInt(0) + totalTxCost.Add(totalTxCost, data.InitialReserve) + totalTxCost.Add(totalTxCost, commission) + + if context.GetBalance(sender, types.GetBaseCoin()).Cmp(totalTxCost) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), totalTxCost.String(), tx.GasCoin)} + } + } + if context.CoinExists(data.Symbol) { return Response{ Code: code.CoinAlreadyExists, diff --git a/core/transaction/declare_candidacy.go b/core/transaction/declare_candidacy.go index 5d3b52c73..aea246b27 100644 --- a/core/transaction/declare_candidacy.go +++ b/core/transaction/declare_candidacy.go @@ -90,6 +90,18 @@ func (data DeclareCandidacyData) Run(sender types.Address, tx *Transaction, cont Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), commission, tx.GasCoin)} } + if data.Coin == tx.GasCoin { + totalTxCost := big.NewInt(0) + totalTxCost.Add(totalTxCost, data.Stake) + totalTxCost.Add(totalTxCost, commission) + + if context.GetBalance(sender, tx.GasCoin).Cmp(totalTxCost) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), totalTxCost.String(), tx.GasCoin)} + } + } + if context.CandidateExists(data.PubKey) { return Response{ Code: code.CandidateExists, diff --git a/core/transaction/delegate.go b/core/transaction/delegate.go index 24acd64f0..151f95578 100644 --- a/core/transaction/delegate.go +++ b/core/transaction/delegate.go @@ -75,6 +75,18 @@ func (data DelegateData) Run(sender types.Address, tx *Transaction, context *sta Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), data.Stake, data.Coin)} } + if data.Coin == tx.GasCoin { + totalTxCost := big.NewInt(0) + totalTxCost.Add(totalTxCost, data.Stake) + totalTxCost.Add(totalTxCost, commission) + + if context.GetBalance(sender, tx.GasCoin).Cmp(totalTxCost) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), totalTxCost.String(), tx.GasCoin)} + } + } + if !context.CandidateExists(data.PubKey) { return Response{ Code: code.CandidateNotFound, diff --git a/core/transaction/sell_coin.go b/core/transaction/sell_coin.go index 5ad00050a..75a2986b0 100644 --- a/core/transaction/sell_coin.go +++ b/core/transaction/sell_coin.go @@ -87,6 +87,18 @@ func (data SellCoinData) Run(sender types.Address, tx *Transaction, context *sta Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %d ", sender.String(), data.ValueToSell)} } + if data.CoinToSell == tx.GasCoin { + totalTxCost := big.NewInt(0) + totalTxCost.Add(totalTxCost, data.ValueToSell) + totalTxCost.Add(totalTxCost, commission) + + if context.GetBalance(sender, tx.GasCoin).Cmp(totalTxCost) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), totalTxCost.String(), tx.GasCoin)} + } + } + if !isCheck { rewardPull.Add(rewardPull, commissionInBaseCoin) diff --git a/core/transaction/send.go b/core/transaction/send.go index 65556e36c..30265b899 100644 --- a/core/transaction/send.go +++ b/core/transaction/send.go @@ -81,6 +81,18 @@ func (data SendData) Run(sender types.Address, tx *Transaction, context *state.S Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), commission, tx.GasCoin)} } + if data.Coin == tx.GasCoin { + totalTxCost := big.NewInt(0) + totalTxCost.Add(totalTxCost, data.Value) + totalTxCost.Add(totalTxCost, commission) + + if context.GetBalance(sender, tx.GasCoin).Cmp(totalTxCost) < 0 { + return Response{ + Code: code.InsufficientFunds, + Log: fmt.Sprintf("Insufficient funds for sender account: %s. Wanted %s %s", sender.String(), totalTxCost.String(), tx.GasCoin)} + } + } + if !isCheck { rewardPull.Add(rewardPull, commissionInBaseCoin) From f47202cd9da68ce3a557443c6e268a4900426ef5 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Mon, 23 Jul 2018 11:39:58 +0300 Subject: [PATCH 74/78] update validators docs --- docs/validators.rst | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/docs/validators.rst b/docs/validators.rst index 9429eb63e..c000b1ced 100755 --- a/docs/validators.rst +++ b/docs/validators.rst @@ -84,44 +84,39 @@ Becoming validator in testnet 1. Install and run Minter Full Node. See :ref:`install-minter`. Make sure your node successfully synchronized. -2. Generate and install validator's key using our `tool `__. - If you already have ``priv_validator.json`` file – just replace it with new one. +2. Get your validator's public key from `Minter GUI `__. -3. Restart Minter Node and Tendermint. - Restarting will apply changes to ``priv_validator.json`` file. - -4. Go to `Vault `__ and send 2 transactions: +3. Go to `Vault `__ and send 2 transactions: Fill and send ``Declare candidacy`` and ``Set candidate online`` forms. P.S. You can receive testnet coins in our telegram wallet @BipWallet_Bot. - 4.1. Declare candidacy + 3.1. Declare candidacy Validators should declare their candidacy, after which users can delegate and, if they so wish, unbond. Then declaring candidacy validator should fill a form: - Address - You will receive rewards to this address and will be able to on/off your validator. - - Public Key - Paste public key you created in step 2 *(Mp...)*. + - Public Key - Paste public key from step 2 *(Mp...)*. - Commission - Set commission for delegated stakes. - - Coin - Enter coin of your stake (MNT). + - Coin - Enter coin of your stake (i.e. MNT). - Stake - Enter value of your stake in given coin. .. figure:: assets/vault-declare.png :width: 300px - 4.2. Set candidate online + 3.2. Set candidate online Validator is **offline** by default. When offline, validator is not included in the list of Minter Blockchain validators, so he is not receiving any rewards and cannot be punished for low availability. - To turn your validator **on**, you should provide Public Key (which you created in step - 2 *(Mp...)*). + To turn your validator **on**, you should provide Public Key (from step 2 *(Mp...)*). - *Note: You should send transaction from address you choose in Address field in step 4.2* + *Note: You should send transaction from address you choose in Address field in step 3.2* .. figure:: assets/vault-candidate-on.png :width: 300px -5. Done. +4. Done. Now you will receive reward as long as your node is running and available. From e590d03f05167780370f27bce3cf3ac07b820a6f Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Mon, 23 Jul 2018 11:55:41 +0300 Subject: [PATCH 75/78] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fe550bee..c69a23e7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - [api] Add validators rewards to block api ## 0.1.0 -*Jule 20th, 2018* +*Jule 23th, 2018* BREAKING CHANGES From f4920e38b10a57c998f7fd705215b90cc2d6c100 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Mon, 23 Jul 2018 12:06:11 +0300 Subject: [PATCH 76/78] update genesis --- genesis/genesis.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/genesis/genesis.go b/genesis/genesis.go index 6c45ace4f..db5f6f039 100644 --- a/genesis/genesis.go +++ b/genesis/genesis.go @@ -1,6 +1,7 @@ package genesis import ( + "encoding/base64" "encoding/hex" "encoding/json" "github.com/MinterTeam/minter-go-node/core/types" @@ -14,7 +15,7 @@ import ( func GetTestnetGenesis() *tmtypes.GenesisDoc { - validatorPubKeyBytes, _ := hex.DecodeString("aaee1ddf30ff54c90715d928b56652fc511bed3722e58973e8ef946f5dae3979") + validatorPubKeyBytes, _ := base64.StdEncoding.DecodeString("qu4d3zD/VMkHFdkotWZS/FEb7Tci5Ylz6O+Ub12uOXk=") var validatorPubKey crypto.PubKeyEd25519 copy(validatorPubKey[:], validatorPubKeyBytes) @@ -41,8 +42,8 @@ func GetTestnetGenesis() *tmtypes.GenesisDoc { appStateJSON, _ := json.Marshal(appState) genesis := tmtypes.GenesisDoc{ - GenesisTime: time.Date(2018, 7, 19, 0, 0, 0, 0, time.UTC), - ChainID: "minter-test-network-11", + GenesisTime: time.Date(2018, 7, 23, 0, 0, 0, 0, time.UTC), + ChainID: "minter-test-network-14", ConsensusParams: nil, Validators: []tmtypes.GenesisValidator{ { From b82c29943e190cd63c7a07239cd68eba1afbdf45 Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Mon, 23 Jul 2018 12:06:41 +0300 Subject: [PATCH 77/78] update genesis --- genesis/genesis.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genesis/genesis.go b/genesis/genesis.go index db5f6f039..5e7ffa592 100644 --- a/genesis/genesis.go +++ b/genesis/genesis.go @@ -27,7 +27,7 @@ func GetTestnetGenesis() *tmtypes.GenesisDoc { { Address: types.HexToAddress("Mxa93163fdf10724dc4785ff5cbfb9ac0b5949409f"), Balance: map[string]string{ - "MNT": helpers.BipToPip(big.NewInt(100000000)).String(), + "MNT": helpers.BipToPip(big.NewInt(1000000000)).String(), }, }, { From b01e2185ebdef033d1719730ad58206596852e1b Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Mon, 23 Jul 2018 17:56:46 +0300 Subject: [PATCH 78/78] limit redeem check transaction to accept only basecoin as gas coin --- core/code/code.go | 1 + core/transaction/redeem_check.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/core/code/code.go b/core/code/code.go index 4a7906ede..24a4004fb 100644 --- a/core/code/code.go +++ b/core/code/code.go @@ -35,4 +35,5 @@ const ( CheckExpired uint32 = 502 CheckUsed uint32 = 503 TooHighGasPrice uint32 = 504 + WrongGasCoin uint32 = 505 ) diff --git a/core/transaction/redeem_check.go b/core/transaction/redeem_check.go index 3d602737c..794d58e6c 100644 --- a/core/transaction/redeem_check.go +++ b/core/transaction/redeem_check.go @@ -58,6 +58,12 @@ func (data RedeemCheckData) Run(sender types.Address, tx *Transaction, context * Log: err.Error()} } + if tx.GasCoin != types.GetBaseCoin() { + return Response{ + Code: code.WrongGasCoin, + Log: fmt.Sprintf("Gas for for redeem check transaction can only be %s", types.GetBaseCoin())} + } + if !context.CoinExists(decodedCheck.Coin) { return Response{ Code: code.CoinNotExists,