Initial commit.
This commit is contained in:
commit
7a6e5f9dfe
|
@ -0,0 +1,4 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
.env
|
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "poagov"
|
||||
version = "1.0.0"
|
||||
authors = ["Peter van Nostrand <jnz@riseup.net>"]
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4"
|
||||
clap = "2.31.2"
|
||||
dotenv = "0.11.0"
|
||||
ethabi = "5.1.1"
|
||||
ethereum-types = "0.3.1"
|
||||
hex = "0.3.1"
|
||||
jsonrpc-core = "8.0.1"
|
||||
lazy_static = "1.0.0"
|
||||
lettre = "0.8"
|
||||
lettre_email = "0.8"
|
||||
reqwest = "0.8.5"
|
||||
serde = "1.0.36"
|
||||
serde_derive = "1.0.36"
|
||||
serde_json = "1.0.13"
|
||||
slog = { version = "2.2.3", features = ["release_max_level_trace"] }
|
||||
slog-term = "2.4.0"
|
||||
web3 = "0.3.0"
|
|
@ -0,0 +1,96 @@
|
|||
# poagov
|
||||
|
||||
A tool to monitor the POA Network's blockchain for goverance events
|
||||
[governance events](https://github.com/poanetwork/wiki/wiki/Governance-Overview).
|
||||
|
||||
# Building
|
||||
|
||||
To build the `poagov` CLI tool, run the following:
|
||||
|
||||
$ git clone https://gitlab.com/DrPeterVanNostrand/poagov.git
|
||||
$ cd poagov
|
||||
$ cargo build --release
|
||||
|
||||
# Usage
|
||||
|
||||
Once you have built `poagov`, you can print out the CLI usage by running:
|
||||
|
||||
$ ./target/release/poagov --help
|
||||
|
||||
poagov 1.0
|
||||
Monitores the POA Network's blockchain for governance events.
|
||||
|
||||
USAGE:
|
||||
poagov [FLAGS] [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
--core monitor voting contracts deployed to the Core network (same as using --network=core)
|
||||
--earliest start monitoring for goverance events starting from the first block in the chain
|
||||
--email send governance notifications via email
|
||||
-h, --help prints help information
|
||||
-k monitor the blockchain for ballots to change keys (same as --monitor=keys)
|
||||
--latest start monitoring for goverance events starting from the most recently mined block in the chain
|
||||
--local monitor voting contracts deployed to a locally running POA chain (same as using --network=local)
|
||||
-p monitor the change for ballots to change the proxy address (same as --monitor=proxy)
|
||||
--push send governance notifications via push notification
|
||||
--sokol monitor voting contracts deployed to the Sokol test network (same as using --network=sokol)
|
||||
-t monitor the chain for ballots to change the minimum threshold (same as --monitor=threshold)
|
||||
-V, --version prints version information
|
||||
|
||||
OPTIONS:
|
||||
--block-time <value> the average time it takes to mine a new block
|
||||
--monitor <value> a comma-separated list of ballot types to monitor for governance events; the available values are: keys, threshold, proxy
|
||||
--network <value> the name of the network to monitor for ballots; the values available for this option are: core, sokol, local
|
||||
--rpc <value> the URL for the RPC endpoint
|
||||
--start <value> start monitoring for governance events at this block (inclusive)
|
||||
--tail <value> start monitoring for governance events for the `n` blocks prior to the last mined block in the chain
|
||||
|
||||
# Setting up Email Notifications
|
||||
|
||||
In order to enable email notifications, you must change the name of the
|
||||
`sample.env` file to `.env`. Then, you must add values for the following
|
||||
SMTP config options in your `.env` file:
|
||||
|
||||
SMTP_HOST_DOMAIN=
|
||||
SMTP_USERNAME=
|
||||
SMTP_PASSWORD=
|
||||
OUTGOING_EMAIL_ADDRESS=
|
||||
|
||||
Add a comma-separated list of email address to the "VALIDATORS" config
|
||||
option in your .env file. These addresses will be sent emails when `poagov`
|
||||
encounters governance events on the POA blockchain.
|
||||
|
||||
# An Explained Example
|
||||
|
||||
$ ./target/release/poagov --sokol --earliest -kt --email
|
||||
|
||||
- `--sokol` is used to monitor contracts deployed to POA's test network.
|
||||
- `--earliest` starts monitoring from the first block in the blockchain.
|
||||
- `-k` get notifications for ballots to change keys.
|
||||
- `-t` get notifications for ballots to change the min threshold.
|
||||
- `--email` sends out email notifications to each address in the
|
||||
"VALIDATORS" config value (located in the .env file).
|
||||
|
||||
Press [ctrl-c] to exit `poagov`.
|
||||
|
||||
# Logs
|
||||
|
||||
Logs are output to stderr. Any notifications that were generated, sent,
|
||||
or failed to be sent will be logged. The following is an example log for a
|
||||
a notification for a ballot to change the min threshold that was generated
|
||||
using the command `$ poagov --earliest -t`:
|
||||
|
||||
Apr 21 08:31:54.219 INFO notification, data: Threshold(
|
||||
ThresholdNotification {
|
||||
network: Sokol,
|
||||
endpoint: "https://sokol.poa.network",
|
||||
block_number: 1078816,
|
||||
contract_type: Threshold,
|
||||
ballot_type: ChangeMinThreshold,
|
||||
ballot_id: 2,
|
||||
start_time: 2018-02-23T05:28:22Z,
|
||||
end_time: 2018-02-25T05:33:00Z,
|
||||
memo: "*TEST* ballot to increase the consensus threshold to 51% (rounded to the higher integer) of the total number of validators. The idea is to legitimize passing the ballot by the majority participation.",
|
||||
proposed_value: 4
|
||||
}
|
||||
)
|
|
@ -0,0 +1,773 @@
|
|||
[
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "finalize",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getIsFinalized",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_votingKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "isValidVote",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getMiningKey",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getBallotsStorage",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "activeBallots",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getTotalVoters",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getTime",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_miningKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "withinLimit",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getAffectedKeyType",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_votingKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getMiningByVotingKey",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "activeBallotsLength",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_currentKey",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_affectedKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "checkIfMiningExisted",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getMemo",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "isActive",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_ballotType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_affectedKey",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_affectedKeyType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_miningKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "areBallotParamsValid",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getAffectedKey",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getEndTime",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_startTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_endTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_affectedKey",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_affectedKeyType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_miningKey",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_ballotType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "memo",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"name": "createVotingForKeys",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_choice",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"name": "vote",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getKeysManager",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "proxyStorage",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "maxOldMiningKeysDeepCheck",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getStartTime",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getMinThresholdOfVoters",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_votingKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "hasAlreadyVoted",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getGlobalMinThresholdOfVoters",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_miningKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "areOldMiningKeysVoted",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getBallotType",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "nextBallotId",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getProgress",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "int256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "validatorActiveBallots",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getBallotLimitPerValidator",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "votingState",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "startTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "endTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "affectedKey",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "affectedKeyType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "miningKey",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "totalVoters",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "progress",
|
||||
"type": "int256"
|
||||
},
|
||||
{
|
||||
"name": "isFinalized",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "quorumState",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"name": "ballotType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "index",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "minThresholdOfVoters",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "creator",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "memo",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_proxyStorage",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "decision",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "voter",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "time",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Vote",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "voter",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "BallotFinalized",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "ballotType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "creator",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "BallotCreated",
|
||||
"type": "event"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,773 @@
|
|||
[
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "finalize",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getIsFinalized",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_votingKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "isValidVote",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getMiningKey",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getBallotsStorage",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "activeBallots",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getTotalVoters",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getTime",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_miningKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "withinLimit",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getAffectedKeyType",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_votingKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getMiningByVotingKey",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "activeBallotsLength",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_currentKey",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_affectedKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "checkIfMiningExisted",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getMemo",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "isActive",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_ballotType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_affectedKey",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_affectedKeyType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_miningKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "areBallotParamsValid",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getAffectedKey",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getEndTime",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_startTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_endTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_affectedKey",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_affectedKeyType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_miningKey",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_ballotType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "memo",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"name": "createVotingForKeys",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_choice",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"name": "vote",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getKeysManager",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "proxyStorage",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "maxOldMiningKeysDeepCheck",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getStartTime",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getMinThresholdOfVoters",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_votingKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "hasAlreadyVoted",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getGlobalMinThresholdOfVoters",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_miningKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "areOldMiningKeysVoted",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getBallotType",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "nextBallotId",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getProgress",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "int256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "validatorActiveBallots",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getBallotLimitPerValidator",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "votingState",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "startTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "endTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "affectedKey",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "affectedKeyType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "miningKey",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "totalVoters",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "progress",
|
||||
"type": "int256"
|
||||
},
|
||||
{
|
||||
"name": "isFinalized",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "quorumState",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"name": "ballotType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "index",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "minThresholdOfVoters",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "creator",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "memo",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_proxyStorage",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "decision",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "voter",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "time",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Vote",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "voter",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "BallotFinalized",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "ballotType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "creator",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "BallotCreated",
|
||||
"type": "event"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,665 @@
|
|||
[
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "finalize",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getIsFinalized",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_votingKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "isValidVote",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getBallotsStorage",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "activeBallots",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getTotalVoters",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getTime",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_startTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_endTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_proposedValue",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "_contractType",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"name": "memo",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"name": "createBallotToChangeProxyAddress",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_miningKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "withinLimit",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_votingKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getMiningByVotingKey",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "activeBallotsLength",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getMemo",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "isActive",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getEndTime",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_choice",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"name": "vote",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getKeysManager",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "proxyStorage",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "maxOldMiningKeysDeepCheck",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getStartTime",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getContractType",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getMinThresholdOfVoters",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_votingKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "hasAlreadyVoted",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getGlobalMinThresholdOfVoters",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_miningKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "areOldMiningKeysVoted",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getProposedValue",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "nextBallotId",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getProgress",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "int256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "validatorActiveBallots",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getBallotLimitPerValidator",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "votingState",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "startTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "endTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "totalVoters",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "progress",
|
||||
"type": "int256"
|
||||
},
|
||||
{
|
||||
"name": "isFinalized",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "quorumState",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"name": "index",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "minThresholdOfVoters",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "proposedValue",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "contractType",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"name": "creator",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "memo",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_proxyStorage",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "decision",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "voter",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "time",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Vote",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "voter",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "BallotFinalized",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "ballotType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "creator",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "BallotCreated",
|
||||
"type": "event"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,638 @@
|
|||
[
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "finalize",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_startTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_endTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_proposedValue",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "memo",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"name": "createBallotToChangeThreshold",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getIsFinalized",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_votingKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "isValidVote",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getBallotsStorage",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "activeBallots",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getTotalVoters",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getTime",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_miningKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "withinLimit",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_votingKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getMiningByVotingKey",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "activeBallotsLength",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getMemo",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "isActive",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getEndTime",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": false,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_choice",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"name": "vote",
|
||||
"outputs": [],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getKeysManager",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "proxyStorage",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "maxOldMiningKeysDeepCheck",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getStartTime",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getMinThresholdOfVoters",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_votingKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "hasAlreadyVoted",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getGlobalMinThresholdOfVoters",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "_miningKey",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "areOldMiningKeysVoted",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getProposedValue",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "nextBallotId",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getProgress",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "int256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "validatorActiveBallots",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "getBallotLimitPerValidator",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "votingState",
|
||||
"outputs": [
|
||||
{
|
||||
"name": "startTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "endTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "totalVoters",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "progress",
|
||||
"type": "int256"
|
||||
},
|
||||
{
|
||||
"name": "isFinalized",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "quorumState",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"name": "index",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "minThresholdOfVoters",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "proposedValue",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "creator",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "memo",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"name": "_proxyStorage",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "decision",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "voter",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "time",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Vote",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "voter",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "BallotFinalized",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "id",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "ballotType",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "creator",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "BallotCreated",
|
||||
"type": "event"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,34 @@
|
|||
# Defaults.
|
||||
USE_NETWORK=sokol
|
||||
MONITOR_BALLOTS=keys,threshold,proxy
|
||||
START_BLOCK=latest
|
||||
AVG_BLOCK_TIME_SECS=5
|
||||
SEND_EMAIL_NOTIFICATIONS=false
|
||||
SEND_PUSH_NOTIFICATIONS=false
|
||||
|
||||
# Email configuration.
|
||||
SMTP_HOST_DOMAIN=
|
||||
SMTP_USERNAME=
|
||||
SMTP_PASSWORD=
|
||||
OUTGOING_EMAIL_ADDRESS=
|
||||
|
||||
# Validators (comma-separated list of emails).
|
||||
VALIDATORS=
|
||||
|
||||
# Core network configuration.
|
||||
CORE_RPC_ENDPOINT=https://core.poa.network
|
||||
CORE_KEYS_CONTRACT_ADDRESS=0x215794efe4b86a2fbcbf706bc9ade63663f1eae1
|
||||
CORE_THRESHOLD_CONTRACT_ADDRESS=0x8829ebe113535826e8af17ed51f83755f675789a
|
||||
CORE_PROXY_CONTRACT_ADDRESS=0x6b728399b41a38d4109f7af2213d4cc31ca87812
|
||||
|
||||
# Sokol network configuration.
|
||||
SOKOL_RPC_ENDPOINT=https://sokol.poa.network
|
||||
SOKOL_KEYS_CONTRACT_ADDRESS=0xc40cdf254a4a35498aa84f35e9842c110729a2a0
|
||||
SOKOL_THRESHOLD_CONTRACT_ADDRESS=0x700db8ba3128087f3b23f60de4bc3179bafa467d
|
||||
SOKOL_PROXY_CONTRACT_ADDRESS=0x0aa4a75549757a90f62f88b3b96b69bead2db0ff
|
||||
|
||||
# Personal/local network configuration.
|
||||
LOCAL_RPC_ENDPOINT=http://127.0.0.1:8545
|
||||
LOCAL_KEYS_CONTRACT_ADDRESS=
|
||||
LOCAL_THRESHOLD_CONTRACT_ADDRESS=
|
||||
LOCAL_PROXY_CONTRACT_ADDRESS=
|
|
@ -0,0 +1,30 @@
|
|||
use clap::{ArgMatches, App};
|
||||
|
||||
pub struct Cli;
|
||||
|
||||
impl Cli {
|
||||
pub fn load() -> ArgMatches<'static> {
|
||||
App::new("poagov")
|
||||
.version("1.0")
|
||||
.about("A tool to monitor POA Network's blockchain for governance events.")
|
||||
.args_from_usage(
|
||||
"[network] --network [value] 'the name of the network to monitor for ballots; the values available for this option are: core, sokol, local'
|
||||
[core] --core 'monitor voting contracts deployed to the Core network (same as using --network=core)'
|
||||
[sokol] --sokol 'monitor voting contracts deployed to the Sokol test network (same as using --network=sokol)'
|
||||
[local] --local 'monitor voting contracts deployed to a locally running POA chain (same as using --network=local)'
|
||||
[rpc] --rpc [value] 'the URL for the RPC endpoint'
|
||||
[monitor] --monitor [value] 'a comma-separated list of ballot types to monitor for governance events; the available values are: keys, threshold, proxy`
|
||||
[keys] -k 'monitor the blockchain for ballots to change keys (same as --monitor=keys)'
|
||||
[threshold] -t 'monitor the chain for ballots to change the minimum threshold (same as --monitor=threshold)'
|
||||
[proxy] -p 'monitor the change for ballots to change the proxy address (same as --monitor=proxy)'
|
||||
[start_block] --start [value] 'start monitoring for governance events at this block (inclusive)'
|
||||
[tail] --tail [value] 'start monitoring for governance events for the `n` blocks prior to the last mined block in the chain'
|
||||
[earliest] --earliest 'start monitoring for goverance events starting from the first block in the chain'
|
||||
[latest] --latest 'start monitoring for goverance events starting from the most recently mined block in the chain'
|
||||
[email] --email 'send governance notifications via email'
|
||||
[push] --push 'send governance notifications via push notification'
|
||||
[block_time] --block-time [value] 'the average time it takes to mine a new block'"
|
||||
)
|
||||
.get_matches()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
use std::env;
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use std::fs::File;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use dotenv::dotenv;
|
||||
use ethabi::{Contract, Event, Function};
|
||||
use ethereum_types::Address;
|
||||
use web3::types::BlockNumber;
|
||||
|
||||
use cli::Cli;
|
||||
use utils::hex_string_to_u64;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Network { Core, Sokol, Local }
|
||||
|
||||
impl<'a> From<&'a str> for Network {
|
||||
fn from(s: &'a str) -> Self {
|
||||
match s {
|
||||
"core" => Network::Core,
|
||||
"sokol" => Network::Sokol,
|
||||
"local" => Network::Local,
|
||||
_ => panic!(format!("Invalid network: {}", s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Network {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Network::Core => write!(f, "core"),
|
||||
Network::Sokol => write!(f, "sokol"),
|
||||
Network::Local => write!(f, "local")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Network {
|
||||
fn to_uppercase(&self) -> String {
|
||||
format!("{}", self).to_uppercase()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ContractType { Keys, Threshold, Proxy }
|
||||
|
||||
impl<'a> From<&'a str> for ContractType {
|
||||
fn from(s: &'a str) -> Self {
|
||||
match s {
|
||||
"keys" => ContractType::Keys,
|
||||
"threshold" => ContractType::Threshold,
|
||||
"proxy" => ContractType::Proxy,
|
||||
_ => panic!("Invalid contract type: {}", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ContractType {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ContractType::Keys => write!(f, "keys"),
|
||||
ContractType::Threshold => write!(f, "threshold"),
|
||||
ContractType::Proxy => write!(f, "proxy")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ContractType {
|
||||
fn to_uppercase(&self) -> String {
|
||||
format!("{}", self).to_uppercase()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct StartBlock {
|
||||
pub block: BlockNumber,
|
||||
pub tail: u64
|
||||
}
|
||||
|
||||
impl StartBlock {
|
||||
fn latest() -> Self {
|
||||
StartBlock { block: BlockNumber::Latest, tail: 0 }
|
||||
}
|
||||
|
||||
fn earliest() -> Self {
|
||||
StartBlock { block: BlockNumber::Earliest, tail: 0 }
|
||||
}
|
||||
|
||||
fn tail(tail: u64) -> Self {
|
||||
StartBlock { block: BlockNumber::Latest, tail }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for StartBlock {
|
||||
fn from(s: &'a str) -> Self {
|
||||
let mut start_block = StartBlock::latest();
|
||||
|
||||
if s.starts_with("-") {
|
||||
start_block.tail = s[1..].parse().expect("Invalid start-block");
|
||||
} else if s == "earliest" {
|
||||
start_block.block = BlockNumber::Number(0);
|
||||
} else if s.starts_with("0x") {
|
||||
start_block.block = hex_string_to_u64(s).expect("Invalid start-block").into();
|
||||
} else if s != "latest" {
|
||||
start_block.block = s.parse::<u64>().expect("Invalid start-block").into();
|
||||
}
|
||||
|
||||
start_block
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Validator {
|
||||
pub name: String,
|
||||
pub email: String
|
||||
}
|
||||
|
||||
pub struct PoaContract {
|
||||
pub kind: ContractType,
|
||||
pub addr: Address,
|
||||
pub abi: Contract
|
||||
}
|
||||
|
||||
impl PoaContract {
|
||||
fn new(kind: ContractType, addr: Address, abi: Contract) -> Self {
|
||||
PoaContract { kind, addr, abi }
|
||||
}
|
||||
|
||||
pub fn event(&self, event: &str) -> Event {
|
||||
self.abi.event(event).unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn function(&self, function: &str) -> Function {
|
||||
self.abi.function(function).unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PoaContract {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "PoaContract({:?}, {:?})", self.kind, self.addr)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PoaContract {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
pub network: Network,
|
||||
pub endpoint: String,
|
||||
pub contracts: Vec<PoaContract>,
|
||||
pub start_block: StartBlock,
|
||||
pub send_email_notifications: bool,
|
||||
pub send_push_notifications: bool,
|
||||
pub validators: Vec<Validator>,
|
||||
pub avg_block_time: Duration,
|
||||
pub smtp_host_domain: String,
|
||||
pub smtp_username: String,
|
||||
pub smtp_password: String,
|
||||
pub outgoing_email: String
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load() -> Self {
|
||||
dotenv().ok();
|
||||
let cli = Cli::load();
|
||||
|
||||
let network = if let Some(s) = cli.value_of("network") {
|
||||
s.into()
|
||||
} else if cli.is_present("core") {
|
||||
Network::Core
|
||||
} else if cli.is_present("sokol") {
|
||||
Network::Sokol
|
||||
} else if cli.is_present("local") {
|
||||
Network::Local
|
||||
} else {
|
||||
env::var("USE_NETWORK").unwrap().as_str().into()
|
||||
};
|
||||
|
||||
let network_uppercase = network.to_uppercase();
|
||||
|
||||
let endpoint = if let Some(s) = cli.value_of("rpc") {
|
||||
s.into()
|
||||
} else {
|
||||
let env_var = format!("{}_RPC_ENDPOINT", network_uppercase);
|
||||
env::var(&env_var).unwrap()
|
||||
};
|
||||
|
||||
let mut contract_types: Vec<ContractType> = vec![];
|
||||
if let Some(s) = cli.value_of("monitor") {
|
||||
s.split(',').for_each(|s| contract_types.push(s.into()));
|
||||
}
|
||||
if cli.is_present("keys") {
|
||||
contract_types.push(ContractType::Keys);
|
||||
}
|
||||
if cli.is_present("threshold") {
|
||||
contract_types.push(ContractType::Threshold);
|
||||
}
|
||||
if cli.is_present("proxy") {
|
||||
contract_types.push(ContractType::Proxy);
|
||||
}
|
||||
if contract_types.is_empty() {
|
||||
env::var("MONITOR_BALLOTS").unwrap().split(',')
|
||||
.for_each(|s| contract_types.push(s.into()));
|
||||
}
|
||||
|
||||
let contracts: Vec<PoaContract> = contract_types.iter()
|
||||
.map(|contract_type| {
|
||||
let env_var = format!(
|
||||
"{}_{}_CONTRACT_ADDRESS",
|
||||
network_uppercase,
|
||||
contract_type.to_uppercase()
|
||||
);
|
||||
let hex = env::var(&env_var)
|
||||
.expect(&format!("Contract address not found: {}", env_var));
|
||||
let addr = Address::from_str(hex.trim_left_matches("0x")).unwrap();
|
||||
let abi_path = format!("abis/{}/{}.json", network, contract_type);
|
||||
let file = File::open(&abi_path)
|
||||
.expect(&format!("ABI file not found: {}", abi_path));
|
||||
let abi = Contract::load(&file)
|
||||
.expect(&format!("Invalid ABI file: {}", abi_path));
|
||||
PoaContract::new(*contract_type, addr, abi)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let start_block = if let Some(s) = cli.value_of("start_block") {
|
||||
s.into()
|
||||
} else if cli.is_present("earliest") {
|
||||
StartBlock::earliest()
|
||||
} else if cli.is_present("latest") {
|
||||
StartBlock::latest()
|
||||
} else if let Some(s) = cli.value_of("tail") {
|
||||
StartBlock::tail(s.parse().expect("Invalid tail value"))
|
||||
} else {
|
||||
env::var("START_BLOCK").unwrap().as_str().into()
|
||||
};
|
||||
|
||||
let send_email_notifications = if cli.is_present("email") {
|
||||
true
|
||||
} else {
|
||||
env::var("SEND_EMAIL_NOTIFICATIONS").unwrap().parse().unwrap()
|
||||
};
|
||||
|
||||
let send_push_notifications = if cli.is_present("push") {
|
||||
true
|
||||
} else {
|
||||
env::var("SEND_PUSH_NOTIFICATIONS").unwrap().parse().unwrap()
|
||||
};
|
||||
|
||||
let validators = env::var("VALIDATORS").unwrap().split(',')
|
||||
.map(|s| Validator { email: s.into(), name: "".into() })
|
||||
.collect();
|
||||
|
||||
let avg_block_time = if let Some(s) = cli.value_of("block_time") {
|
||||
Duration::from_secs(s.parse().unwrap())
|
||||
} else {
|
||||
let s = env::var("AVG_BLOCK_TIME_SECS").unwrap();
|
||||
Duration::from_secs(s.parse().unwrap())
|
||||
};
|
||||
|
||||
let smtp_host_domain = env::var("SMTP_HOST_DOMAIN").unwrap();
|
||||
let smtp_username = env::var("SMTP_USERNAME").unwrap();
|
||||
let smtp_password = env::var("SMTP_PASSWORD").unwrap();
|
||||
let outgoing_email = env::var("OUTGOING_EMAIL_ADDRESS").unwrap();
|
||||
|
||||
Config {
|
||||
network, endpoint, contracts, start_block,
|
||||
send_email_notifications,
|
||||
send_push_notifications,
|
||||
validators, avg_block_time,
|
||||
smtp_host_domain, smtp_username,
|
||||
smtp_password, outgoing_email
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
use std::io::stderr;
|
||||
|
||||
use lettre::smtp;
|
||||
use slog::{Drain, Logger};
|
||||
use slog_term::{FullFormat, PlainSyncDecorator};
|
||||
|
||||
use notify::Notification;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref LOGGER: Logger = {
|
||||
let log_decorator = PlainSyncDecorator::new(stderr());
|
||||
let drain = FullFormat::new(log_decorator).build().fuse();
|
||||
Logger::root(drain, o!())
|
||||
};
|
||||
}
|
||||
|
||||
pub fn log_notification(notif: &Notification) {
|
||||
info!(LOGGER, "notification"; "data" => format!("{:#?}", notif));
|
||||
}
|
||||
|
||||
pub fn log_email_sent(email: &str) {
|
||||
info!(LOGGER, "email sent"; "to" => email);
|
||||
}
|
||||
|
||||
pub fn log_email_failed(email: &str, error: smtp::error::Error) {
|
||||
warn!(
|
||||
LOGGER,
|
||||
"email failed";
|
||||
"to" => email,
|
||||
"error" => format!("{}", error)
|
||||
);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
extern crate chrono;
|
||||
extern crate clap;
|
||||
extern crate dotenv;
|
||||
extern crate ethabi;
|
||||
extern crate ethereum_types;
|
||||
extern crate hex;
|
||||
extern crate jsonrpc_core;
|
||||
#[macro_use] extern crate lazy_static;
|
||||
extern crate lettre;
|
||||
extern crate lettre_email;
|
||||
extern crate reqwest;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
#[macro_use] extern crate slog;
|
||||
extern crate slog_term;
|
||||
extern crate web3;
|
||||
|
||||
mod cli;
|
||||
mod config;
|
||||
mod logging;
|
||||
mod notify;
|
||||
mod rpc;
|
||||
mod utils;
|
||||
|
||||
use config::Config;
|
||||
use notify::Notifier;
|
||||
use rpc::{BlockWindows, RpcClient};
|
||||
|
||||
fn main() {
|
||||
let config = Config::load();
|
||||
let client = RpcClient::new(&config.endpoint);
|
||||
let block_windows = BlockWindows::new(&client, config.avg_block_time);
|
||||
let mut notifier = Notifier::new(&config).unwrap();
|
||||
|
||||
for (start, stop) in block_windows {
|
||||
for contract in &config.contracts {
|
||||
let ballot_created_logs = client
|
||||
.get_ballot_created_logs(contract, start, stop)
|
||||
.unwrap();
|
||||
|
||||
for log in &ballot_created_logs {
|
||||
let voting_data = client.get_voting_state(contract, log.ballot_id).unwrap();
|
||||
let notif = notifier.build_notification(log, &voting_data);
|
||||
notifier.notify_validators(¬if);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use ethereum_types::Address;
|
||||
use lettre::{EmailTransport, SmtpTransport};
|
||||
use lettre::smtp::{self, ConnectionReuseParameters};
|
||||
use lettre::smtp::authentication::{Credentials, Mechanism};
|
||||
use lettre_email::{self, Email, EmailBuilder};
|
||||
|
||||
use config::{Config, ContractType, Network, Validator};
|
||||
use logging::{log_email_failed, log_email_sent, log_notification};
|
||||
use rpc::{BallotCreatedLog, BallotType, KeyType, VotingData};
|
||||
|
||||
type BuildEmailResult = Result<Email, lettre_email::error::Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Notification {
|
||||
Keys(KeysNotification),
|
||||
Threshold(ThresholdNotification),
|
||||
Proxy(ProxyNotification)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KeysNotification {
|
||||
pub network: Network,
|
||||
pub endpoint: String,
|
||||
pub block_number: u64,
|
||||
pub contract_type: ContractType,
|
||||
pub ballot_type: BallotType,
|
||||
pub ballot_id: u64,
|
||||
pub start_time: DateTime<Utc>,
|
||||
pub end_time: DateTime<Utc>,
|
||||
pub memo: String,
|
||||
pub affected_key: Address,
|
||||
pub affected_key_type: KeyType
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ThresholdNotification {
|
||||
pub network: Network,
|
||||
pub endpoint: String,
|
||||
pub block_number: u64,
|
||||
pub contract_type: ContractType,
|
||||
pub ballot_type: BallotType,
|
||||
pub ballot_id: u64,
|
||||
pub start_time: DateTime<Utc>,
|
||||
pub end_time: DateTime<Utc>,
|
||||
pub memo: String,
|
||||
pub proposed_value: u64
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProxyNotification {
|
||||
pub network: Network,
|
||||
pub endpoint: String,
|
||||
pub block_number: u64,
|
||||
pub contract_type: ContractType,
|
||||
pub ballot_type: BallotType,
|
||||
pub ballot_id: u64,
|
||||
pub start_time: DateTime<Utc>,
|
||||
pub end_time: DateTime<Utc>,
|
||||
pub memo: String,
|
||||
pub proposed_value: Address
|
||||
}
|
||||
|
||||
impl Notification {
|
||||
fn new(config: &Config, log: &BallotCreatedLog, voting_data: &VotingData) -> Self {
|
||||
let network = config.network;
|
||||
let endpoint = config.endpoint.clone();
|
||||
|
||||
let block_number = log.block_number;
|
||||
let ballot_type = log.ballot_type;
|
||||
let ballot_id = log.ballot_id;
|
||||
|
||||
let start_time = voting_data.start_time();
|
||||
let end_time = voting_data.end_time();
|
||||
let memo = voting_data.memo();
|
||||
|
||||
match *voting_data {
|
||||
VotingData::Keys(ref data) => {
|
||||
let contract_type = ContractType::Keys;
|
||||
let affected_key = data.affected_key;
|
||||
let affected_key_type = data.affected_key_type;
|
||||
let notification = KeysNotification {
|
||||
network, endpoint, block_number,
|
||||
contract_type, ballot_type, ballot_id,
|
||||
start_time, end_time, memo,
|
||||
affected_key, affected_key_type
|
||||
};
|
||||
Notification::Keys(notification)
|
||||
},
|
||||
VotingData::Threshold(ref data) => {
|
||||
let contract_type = ContractType::Threshold;
|
||||
let proposed_value = data.proposed_value;
|
||||
let notification = ThresholdNotification {
|
||||
network, endpoint, block_number,
|
||||
contract_type, ballot_type,
|
||||
ballot_id, start_time, end_time,
|
||||
memo, proposed_value
|
||||
};
|
||||
Notification::Threshold(notification)
|
||||
},
|
||||
VotingData::Proxy(ref data) => {
|
||||
let contract_type = ContractType::Proxy;
|
||||
let proposed_value = data.proposed_value;
|
||||
let notification = ProxyNotification {
|
||||
network, endpoint, block_number,
|
||||
contract_type, ballot_type,
|
||||
ballot_id, start_time, end_time,
|
||||
memo, proposed_value
|
||||
};
|
||||
Notification::Proxy(notification)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Notifier<'a> {
|
||||
config: &'a Config,
|
||||
mailer: Option<SmtpTransport>
|
||||
}
|
||||
|
||||
impl<'a> Notifier<'a> {
|
||||
pub fn new(config: &'a Config) -> Result<Self, smtp::error::Error> {
|
||||
let mailer = if config.send_email_notifications {
|
||||
let creds = Credentials::new(
|
||||
config.smtp_username.clone(),
|
||||
config.smtp_password.clone()
|
||||
);
|
||||
let mailer = SmtpTransport::simple_builder(&config.smtp_host_domain)?
|
||||
.connection_reuse(ConnectionReuseParameters::ReuseUnlimited)
|
||||
.authentication_mechanism(Mechanism::Plain)
|
||||
.credentials(creds)
|
||||
.build();
|
||||
Some(mailer)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Notifier { config, mailer })
|
||||
}
|
||||
|
||||
pub fn build_notification(&self, log: &BallotCreatedLog, voting_data: &VotingData) -> Notification {
|
||||
Notification::new(&self.config, log, voting_data)
|
||||
}
|
||||
|
||||
pub fn notify_validators(&mut self, notif: &Notification) {
|
||||
log_notification(notif);
|
||||
for validator in &self.config.validators {
|
||||
if self.config.send_email_notifications {
|
||||
let email = self.build_email(validator, notif).unwrap();
|
||||
if let Some(ref mut mailer) = self.mailer {
|
||||
match mailer.send(&email) {
|
||||
Ok(_) => log_email_sent(&validator.email),
|
||||
Err(e) => log_email_failed(&validator.email, e)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if self.config.send_push_notifications {
|
||||
println!("Push Notifications not yet implemented.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_email(&self, validator: &Validator, notif: &Notification) -> BuildEmailResult {
|
||||
let body = match *notif {
|
||||
Notification::Keys(ref inner) => format!("{:#?}\n", inner),
|
||||
Notification::Threshold(ref inner) => format!("{:#?}\n", inner),
|
||||
Notification::Proxy(ref inner) => format!("{:#?}\n", inner)
|
||||
};
|
||||
EmailBuilder::new()
|
||||
.to(validator.email.as_str())
|
||||
.from(self.config.outgoing_email.as_str())
|
||||
.subject("POA Network Governance Notification")
|
||||
.text(body)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for Notifier<'a> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(ref mut mailer) = self.mailer {
|
||||
mailer.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,494 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::i64;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::u64;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use ethabi::{Event, Token};
|
||||
use ethereum_types::{Address, H256, U256};
|
||||
use hex;
|
||||
use jsonrpc_core as rpc;
|
||||
use reqwest;
|
||||
use serde_json as json;
|
||||
use web3::types::{BlockNumber, Bytes, CallRequest, Filter, FilterBuilder, Log};
|
||||
|
||||
use config::{ContractType, PoaContract};
|
||||
use utils::{hex_string_to_u64, u256_to_datetime};
|
||||
|
||||
const JSONRPC_VERSION: rpc::Version = rpc::Version::V2;
|
||||
const REQUEST_ID: rpc::Id = rpc::Id::Num(1);
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum BallotType {
|
||||
InvalidKey,
|
||||
AddKey,
|
||||
RemoveKey,
|
||||
SwapKey,
|
||||
ChangeMinThreshold,
|
||||
ChangeProxyAddress
|
||||
}
|
||||
|
||||
// Used when converting from an element in a Log's "topics" vector.
|
||||
impl From<H256> for BallotType {
|
||||
fn from(topic: H256) -> Self {
|
||||
match topic.low_u64() {
|
||||
0 => BallotType::InvalidKey,
|
||||
1 => BallotType::AddKey,
|
||||
2 => BallotType::RemoveKey,
|
||||
3 => BallotType::SwapKey,
|
||||
4 => BallotType::ChangeMinThreshold,
|
||||
5 => BallotType::ChangeProxyAddress,
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used when converting from an output-token returned from a contract's
|
||||
// `votingState` function.
|
||||
impl From<U256> for BallotType {
|
||||
fn from(output: U256) -> Self {
|
||||
match output.as_u64() {
|
||||
0 => BallotType::InvalidKey,
|
||||
1 => BallotType::AddKey,
|
||||
2 => BallotType::RemoveKey,
|
||||
3 => BallotType::SwapKey,
|
||||
4 => BallotType::ChangeMinThreshold,
|
||||
5 => BallotType::ChangeProxyAddress,
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum KeyType {
|
||||
Invalid,
|
||||
MiningKey,
|
||||
VotingKey,
|
||||
PayoutKey
|
||||
}
|
||||
|
||||
impl From<U256> for KeyType {
|
||||
fn from(key_type: U256) -> Self {
|
||||
match key_type.as_u64() {
|
||||
0 => KeyType::Invalid,
|
||||
1 => KeyType::MiningKey,
|
||||
2 => KeyType::VotingKey,
|
||||
3 => KeyType::PayoutKey,
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum QuorumState {
|
||||
Invalid,
|
||||
InProgress,
|
||||
Accepted,
|
||||
Rejected
|
||||
}
|
||||
|
||||
impl From<U256> for QuorumState {
|
||||
fn from(quorum_state: U256) -> Self {
|
||||
match quorum_state.as_u64() {
|
||||
0 => QuorumState::Invalid,
|
||||
1 => QuorumState::InProgress,
|
||||
2 => QuorumState::Accepted,
|
||||
3 => QuorumState::Rejected,
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BallotCreatedLog {
|
||||
pub block_number: u64,
|
||||
pub ballot_id: u64,
|
||||
pub ballot_type: BallotType
|
||||
}
|
||||
|
||||
impl From<Log> for BallotCreatedLog {
|
||||
fn from(log: Log) -> Self {
|
||||
BallotCreatedLog {
|
||||
block_number: log.block_number.unwrap().as_u64(),
|
||||
ballot_id: log.topics[1].low_u64(),
|
||||
ballot_type: log.topics[2].into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VotingData {
|
||||
Keys(KeysVotingData),
|
||||
Threshold(ThresholdVotingData),
|
||||
Proxy(ProxyVotingData)
|
||||
}
|
||||
|
||||
impl VotingData {
|
||||
pub fn keys(tokens: Vec<Token>) -> Self {
|
||||
let voting_data: KeysVotingData = tokens.into();
|
||||
VotingData::Keys(voting_data)
|
||||
}
|
||||
|
||||
pub fn threshold(tokens: Vec<Token>) -> Self {
|
||||
let voting_data: ThresholdVotingData = tokens.into();
|
||||
VotingData::Threshold(voting_data)
|
||||
}
|
||||
|
||||
pub fn proxy(tokens: Vec<Token>) -> Self {
|
||||
let voting_data: ProxyVotingData = tokens.into();
|
||||
VotingData::Proxy(voting_data)
|
||||
}
|
||||
|
||||
pub fn start_time(&self) -> DateTime<Utc> {
|
||||
match *self {
|
||||
VotingData::Keys(ref inner) => inner.start_time,
|
||||
VotingData::Threshold(ref inner) => inner.start_time,
|
||||
VotingData::Proxy(ref inner) => inner.start_time
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_time(&self) -> DateTime<Utc> {
|
||||
match *self {
|
||||
VotingData::Keys(ref inner) => inner.end_time,
|
||||
VotingData::Threshold(ref inner) => inner.end_time,
|
||||
VotingData::Proxy(ref inner) => inner.end_time
|
||||
}
|
||||
}
|
||||
|
||||
pub fn memo(&self) -> String {
|
||||
match *self {
|
||||
VotingData::Keys(ref inner) => inner.memo.clone(),
|
||||
VotingData::Threshold(ref inner) => inner.memo.clone(),
|
||||
VotingData::Proxy(ref inner) => inner.memo.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KeysVotingData {
|
||||
pub start_time: DateTime<Utc>,
|
||||
pub end_time: DateTime<Utc>,
|
||||
pub affected_key: Address,
|
||||
pub affected_key_type: KeyType,
|
||||
pub mining_key: Address,
|
||||
pub total_voters: u64,
|
||||
pub progress: i64,
|
||||
pub is_finalized: bool,
|
||||
pub quorum_state: QuorumState,
|
||||
pub ballot_type: BallotType,
|
||||
pub index: u64,
|
||||
pub min_threshold_of_voters: u64,
|
||||
pub creator: Address,
|
||||
pub memo: String
|
||||
}
|
||||
|
||||
impl From<Vec<Token>> for KeysVotingData {
|
||||
fn from(tokens: Vec<Token>) -> Self {
|
||||
let start_time = tokens[0].clone().to_uint()
|
||||
.map(|uint| u256_to_datetime(uint))
|
||||
.unwrap();
|
||||
|
||||
let end_time = tokens[1].clone().to_uint()
|
||||
.map(|uint| u256_to_datetime(uint))
|
||||
.unwrap();
|
||||
|
||||
let affected_key = tokens[2].clone().to_address().unwrap();
|
||||
let affected_key_type = tokens[3].clone().to_uint().unwrap().into();
|
||||
let mining_key = tokens[4].clone().to_address().unwrap();
|
||||
let total_voters = tokens[5].clone().to_uint().unwrap().as_u64();
|
||||
let progress = match tokens[6].clone().to_int().unwrap().low_u64() {
|
||||
lsb if lsb <= total_voters => lsb as i64,
|
||||
lsb => i64::try_from(u64::MAX - lsb + 1).unwrap()
|
||||
};
|
||||
let is_finalized = tokens[7].clone().to_bool().unwrap();
|
||||
let quorum_state = tokens[8].clone().to_uint().unwrap().into();
|
||||
let ballot_type = tokens[9].clone().to_uint().unwrap().into();
|
||||
let index = tokens[10].clone().to_uint().unwrap().as_u64();
|
||||
let min_threshold_of_voters = tokens[11].clone().to_uint().unwrap().as_u64();
|
||||
let creator = tokens[12].clone().to_address().unwrap();
|
||||
let memo = tokens[13].clone().to_string().unwrap();
|
||||
|
||||
KeysVotingData {
|
||||
start_time, end_time,
|
||||
affected_key, affected_key_type,
|
||||
mining_key, total_voters,
|
||||
progress, is_finalized,
|
||||
quorum_state, ballot_type,
|
||||
index, min_threshold_of_voters,
|
||||
creator, memo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ThresholdVotingData {
|
||||
pub start_time: DateTime<Utc>,
|
||||
pub end_time: DateTime<Utc>,
|
||||
pub total_voters: u64,
|
||||
pub progress: i64,
|
||||
pub is_finalized: bool,
|
||||
pub quorum_state: QuorumState,
|
||||
pub index: u64,
|
||||
pub proposed_value: u64,
|
||||
pub min_threshold_of_voters: u64,
|
||||
pub creator: Address,
|
||||
pub memo: String
|
||||
}
|
||||
|
||||
impl From<Vec<Token>> for ThresholdVotingData {
|
||||
fn from(tokens: Vec<Token>) -> Self {
|
||||
let start_time = tokens[0].clone().to_uint()
|
||||
.map(|uint| u256_to_datetime(uint))
|
||||
.unwrap();
|
||||
|
||||
let end_time = tokens[1].clone().to_uint()
|
||||
.map(|uint| u256_to_datetime(uint))
|
||||
.unwrap();
|
||||
|
||||
let total_voters = tokens[2].clone().to_uint().unwrap().as_u64();
|
||||
let progress = match tokens[3].clone().to_int().unwrap().low_u64() {
|
||||
lsb if lsb <= total_voters => lsb as i64,
|
||||
lsb => i64::try_from(u64::MAX - lsb + 1).unwrap()
|
||||
};
|
||||
let is_finalized = tokens[4].clone().to_bool().unwrap();
|
||||
let quorum_state = tokens[5].clone().to_uint().unwrap().into();
|
||||
let index = tokens[6].clone().to_uint().unwrap().as_u64();
|
||||
let proposed_value = tokens[7].clone().to_uint().unwrap().as_u64();
|
||||
let min_threshold_of_voters = tokens[8].clone().to_uint().unwrap().as_u64();
|
||||
let creator = tokens[9].clone().to_address().unwrap();
|
||||
let memo = tokens[10].clone().to_string().unwrap();
|
||||
|
||||
ThresholdVotingData {
|
||||
start_time, end_time,
|
||||
total_voters, progress,
|
||||
is_finalized, quorum_state,
|
||||
index, proposed_value,
|
||||
min_threshold_of_voters, creator,
|
||||
memo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProxyVotingData {
|
||||
pub start_time: DateTime<Utc>,
|
||||
pub end_time: DateTime<Utc>,
|
||||
pub total_voters: u64,
|
||||
pub progress: i64,
|
||||
pub is_finalized: bool,
|
||||
pub quorum_state: QuorumState,
|
||||
pub index: u64,
|
||||
pub min_threshold_of_voters: u64,
|
||||
pub proposed_value: Address,
|
||||
pub contract_type: u64,
|
||||
pub creator: Address,
|
||||
pub memo: String
|
||||
}
|
||||
|
||||
impl From<Vec<Token>> for ProxyVotingData {
|
||||
fn from(tokens: Vec<Token>) -> Self {
|
||||
let start_time = tokens[0].clone().to_uint()
|
||||
.map(|uint| u256_to_datetime(uint))
|
||||
.unwrap();
|
||||
|
||||
let end_time = tokens[1].clone().to_uint()
|
||||
.map(|uint| u256_to_datetime(uint))
|
||||
.unwrap();
|
||||
|
||||
let total_voters = tokens[2].clone().to_uint().unwrap().as_u64();
|
||||
let progress = match tokens[3].clone().to_int().unwrap().low_u64() {
|
||||
lsb if lsb <= total_voters => lsb as i64,
|
||||
lsb => i64::try_from(u64::MAX - lsb + 1).unwrap()
|
||||
};
|
||||
let is_finalized = tokens[4].clone().to_bool().unwrap();
|
||||
let quorum_state = tokens[5].clone().to_uint().unwrap().into();
|
||||
let index = tokens[6].clone().to_uint().unwrap().as_u64();
|
||||
let min_threshold_of_voters = tokens[7].clone().to_uint().unwrap().as_u64();
|
||||
let proposed_value = tokens[8].clone().to_address().unwrap();
|
||||
let contract_type = tokens[9].clone().to_uint().unwrap().as_u64();
|
||||
let creator = tokens[10].clone().to_address().unwrap();
|
||||
let memo = tokens[11].clone().to_string().unwrap();
|
||||
|
||||
ProxyVotingData {
|
||||
start_time, end_time,
|
||||
total_voters, progress,
|
||||
is_finalized, quorum_state,
|
||||
index, min_threshold_of_voters,
|
||||
proposed_value, contract_type,
|
||||
creator, memo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct BlockWindows<'a> {
|
||||
client: &'a RpcClient,
|
||||
block_time: Duration,
|
||||
on_first_iteration: bool,
|
||||
start: u64,
|
||||
stop: u64
|
||||
}
|
||||
|
||||
impl<'a> BlockWindows<'a> {
|
||||
pub fn new(client: &'a RpcClient, block_time: Duration) -> Self {
|
||||
let start = 0;
|
||||
let stop = 0;
|
||||
let on_first_iteration = true;
|
||||
BlockWindows { client, block_time, start, stop, on_first_iteration }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for BlockWindows<'a> {
|
||||
type Item = (BlockNumber, BlockNumber);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.on_first_iteration {
|
||||
self.stop = self.client.latest_block_number().unwrap();
|
||||
self.on_first_iteration = false;
|
||||
} else {
|
||||
self.start = self.stop + 1;
|
||||
while self.start >= self.stop {
|
||||
thread::sleep(self.block_time);
|
||||
self.stop = self.client.latest_block_number().unwrap();
|
||||
}
|
||||
}
|
||||
Some((
|
||||
BlockNumber::Number(self.start),
|
||||
BlockNumber::Number(self.stop)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Method {
|
||||
CallContractFunction,
|
||||
GetLogs,
|
||||
LastMinedBlockNumber
|
||||
}
|
||||
|
||||
impl From<Method> for String {
|
||||
fn from(method: Method) -> Self {
|
||||
let s = match method {
|
||||
Method::CallContractFunction => "eth_call",
|
||||
Method::GetLogs => "eth_getLogs",
|
||||
Method::LastMinedBlockNumber => "eth_blockNumber"
|
||||
};
|
||||
s.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventFilter;
|
||||
|
||||
impl EventFilter {
|
||||
pub fn new(ev: Event, addr: Address, from: BlockNumber, to: BlockNumber) -> Filter {
|
||||
let topic = vec![ev.signature()];
|
||||
FilterBuilder::default()
|
||||
.topics(Some(topic), None, None, None)
|
||||
.address(vec![addr])
|
||||
.from_block(from)
|
||||
.to_block(to)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RpcClient {
|
||||
endpoint: String,
|
||||
client: reqwest::Client
|
||||
}
|
||||
|
||||
impl RpcClient {
|
||||
pub fn new(endpoint: &str) -> Self {
|
||||
let endpoint = endpoint.into();
|
||||
let client = reqwest::Client::new();
|
||||
RpcClient { endpoint, client }
|
||||
}
|
||||
|
||||
fn build_request(&self, method: Method, params: Vec<json::Value>) -> reqwest::Request {
|
||||
let jsonrpc = Some(JSONRPC_VERSION);
|
||||
let id = REQUEST_ID.clone();
|
||||
let method = method.into();
|
||||
let params = Some(rpc::Params::Array(params));
|
||||
let method_call = rpc::MethodCall { jsonrpc, method, params, id };
|
||||
let body = rpc::Call::MethodCall(method_call);
|
||||
self.client.post(&self.endpoint).json(&body).build().unwrap()
|
||||
}
|
||||
|
||||
fn send(&self, req: reqwest::Request) -> reqwest::Result<json::Value> {
|
||||
let resp: rpc::Response = self.client.execute(req)?.json().unwrap();
|
||||
if let rpc::Response::Single(resp_status) = resp {
|
||||
if let rpc::Output::Success(resp) = resp_status {
|
||||
return Ok(resp.result);
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn latest_block_number(&self) -> reqwest::Result<u64> {
|
||||
let req = self.build_request(Method::LastMinedBlockNumber, vec![]);
|
||||
if let json::Value::String(hex) = self.send(req)? {
|
||||
return Ok(hex_string_to_u64(&hex).unwrap());
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn get_logs(&self, filter: Filter) -> reqwest::Result<Vec<Log>> {
|
||||
let params = vec![json::to_value(filter).unwrap()];
|
||||
let req = self.build_request(Method::GetLogs, params);
|
||||
let result = self.send(req)?;
|
||||
Ok(json::from_value(result).unwrap())
|
||||
}
|
||||
|
||||
pub fn get_ballot_created_logs(
|
||||
&self,
|
||||
contract: &PoaContract,
|
||||
start: BlockNumber,
|
||||
stop: BlockNumber
|
||||
) -> reqwest::Result<Vec<BallotCreatedLog>>
|
||||
{
|
||||
let event = contract.event("BallotCreated");
|
||||
let filter = EventFilter::new(event, contract.addr, start, stop);
|
||||
let logs = self.get_logs(filter)?;
|
||||
let ballot_created_logs: Vec<BallotCreatedLog> = logs.into_iter()
|
||||
.map(|log| log.into())
|
||||
.collect();
|
||||
Ok(ballot_created_logs)
|
||||
}
|
||||
|
||||
pub fn get_voting_state(&self, contract: &PoaContract, ballot_id: u64) -> reqwest::Result<VotingData> {
|
||||
let function = contract.function("votingState");
|
||||
let tokens = vec![Token::Uint(U256::from(ballot_id))];
|
||||
let encoded_input: Bytes = function.encode_input(&tokens).unwrap().into();
|
||||
|
||||
let call = CallRequest {
|
||||
to: contract.addr,
|
||||
data: Some(encoded_input),
|
||||
from: None,
|
||||
gas: None,
|
||||
gas_price: None,
|
||||
value: None
|
||||
};
|
||||
|
||||
let params = vec![
|
||||
json::to_value(call).unwrap(),
|
||||
json::to_value(BlockNumber::Latest).unwrap()
|
||||
];
|
||||
|
||||
let req = self.build_request(Method::CallContractFunction, params);
|
||||
let result = self.send(req)?;
|
||||
|
||||
if let json::Value::String(hex) = result {
|
||||
let bytes = hex::decode(hex.trim_left_matches("0x")).unwrap();
|
||||
let outputs = function.decode_output(&bytes).unwrap();
|
||||
let voting_data = match contract.kind {
|
||||
ContractType::Keys => VotingData::keys(outputs.into()),
|
||||
ContractType::Threshold => VotingData::threshold(outputs.into()),
|
||||
ContractType::Proxy => VotingData::proxy(outputs.into())
|
||||
};
|
||||
return Ok(voting_data);
|
||||
}
|
||||
|
||||
unreachable!();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::i64;
|
||||
use std::num::ParseIntError;
|
||||
use std::u64;
|
||||
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use ethereum_types::U256;
|
||||
|
||||
pub fn hex_string_to_u64(hex: &str) -> Result<u64, ParseIntError> {
|
||||
let hex = hex.trim_left_matches("0x");
|
||||
u64::from_str_radix(hex, 16)
|
||||
}
|
||||
|
||||
pub fn u256_to_datetime(uint: U256) -> DateTime<Utc> {
|
||||
let n_secs = i64::try_from(uint.as_u64()).unwrap();
|
||||
let timestamp = NaiveDateTime::from_timestamp(n_secs, 0);
|
||||
DateTime::from_utc(timestamp, Utc)
|
||||
}
|
Loading…
Reference in New Issue