aboutsummaryrefslogtreecommitdiffstats
path: root/dex/recovery.go
diff options
context:
space:
mode:
Diffstat (limited to 'dex/recovery.go')
-rw-r--r--dex/recovery.go484
1 files changed, 484 insertions, 0 deletions
diff --git a/dex/recovery.go b/dex/recovery.go
new file mode 100644
index 000000000..cfc8ae203
--- /dev/null
+++ b/dex/recovery.go
@@ -0,0 +1,484 @@
+// Copyright 2018 The dexon-consensus Authors
+// This file is part of the dexon-consensus library.
+//
+// The dexon-consensus library is free software: you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// The dexon-consensus library is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the dexon-consensus library. If not, see
+// <http://www.gnu.org/licenses/>.
+
+package dex
+
+import (
+ "crypto/ecdsa"
+ "encoding/hex"
+ "fmt"
+ "math/big"
+ "strconv"
+ "strings"
+
+ "github.com/dexon-foundation/dexon/accounts/abi"
+ "github.com/dexon-foundation/dexon/common"
+ "github.com/dexon-foundation/dexon/core/types"
+ "github.com/dexon-foundation/dexon/crypto"
+ "github.com/dexon-foundation/dexon/params"
+ "github.com/dexon-foundation/dexon/rlp"
+ "github.com/onrik/ethrpc"
+)
+
+const numConfirmation = 1
+
+const recoveryABI = `
+[
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "voted",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [],
+ "name": "renounceOwnership",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "isOwner",
+ "outputs": [
+ {
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "depositValue",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "votes",
+ "outputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "withdrawable",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "height",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "name": "voter",
+ "type": "address"
+ }
+ ],
+ "name": "VotedForRecovery",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "Withdrawn",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "name": "previousOwner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "OwnershipTransferred",
+ "type": "event"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "DepositValue",
+ "type": "uint256"
+ }
+ ],
+ "name": "setDeposit",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "height",
+ "type": "uint256"
+ },
+ {
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "refund",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [],
+ "name": "withdraw",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "height",
+ "type": "uint256"
+ }
+ ],
+ "name": "voteForSkipBlock",
+ "outputs": [],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "name": "height",
+ "type": "uint256"
+ }
+ ],
+ "name": "numVotes",
+ "outputs": [
+ {
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+]
+`
+
+var abiObject abi.ABI
+
+func init() {
+ var err error
+ abiObject, err = abi.JSON(strings.NewReader(recoveryABI))
+ if err != nil {
+ panic(err)
+ }
+}
+
+type Recovery struct {
+ gov *DexconGovernance
+ contract common.Address
+ confirmation int
+ privateKey *ecdsa.PrivateKey
+ nodeAddress common.Address
+ client *ethrpc.EthRPC
+}
+
+func NewRecovery(config *params.RecoveryConfig, networkRPC string,
+ gov *DexconGovernance, privKey *ecdsa.PrivateKey) *Recovery {
+ client := ethrpc.New(networkRPC)
+ return &Recovery{
+ gov: gov,
+ contract: config.Contract,
+ confirmation: config.Confirmation,
+ privateKey: privKey,
+ nodeAddress: crypto.PubkeyToAddress(privKey.PublicKey),
+ client: client,
+ }
+}
+
+func (r *Recovery) genVoteForSkipBlockTx(height uint64) (*types.Transaction, error) {
+ netVersion, err := r.client.NetVersion()
+ if err != nil {
+ return nil, err
+ }
+
+ networkID, err := strconv.Atoi(netVersion)
+ if err != nil {
+ return nil, err
+ }
+
+ data, err := abiObject.Pack("depositValue")
+ if err != nil {
+ return nil, err
+ }
+
+ res, err := r.client.EthCall(ethrpc.T{
+ From: r.nodeAddress.String(),
+ To: r.contract.String(),
+ Data: "0x" + hex.EncodeToString(data),
+ }, "latest")
+ if err != nil {
+ return nil, err
+ }
+
+ resBytes, err := hex.DecodeString(res[2:])
+ if err != nil {
+ return nil, err
+ }
+
+ var depositValue *big.Int
+ err = abiObject.Unpack(&depositValue, "depositValue", resBytes)
+ if err != nil {
+ return nil, err
+ }
+
+ data, err = abiObject.Pack("voteForSkipBlock", new(big.Int).SetUint64(height))
+ if err != nil {
+ return nil, err
+ }
+
+ gasPrice, err := r.client.EthGasPrice()
+ if err != nil {
+ return nil, err
+ }
+
+ nonce, err := r.client.EthGetTransactionCount(r.nodeAddress.String(), "pending")
+ if err != nil {
+ return nil, err
+ }
+
+ // Increase gasPrice to 3 times of suggested gas price to make sure it will
+ // be included in time.
+ useGasPrice := new(big.Int).Mul(&gasPrice, big.NewInt(3))
+
+ tx := types.NewTransaction(
+ uint64(nonce),
+ r.contract,
+ depositValue,
+ uint64(100000),
+ useGasPrice,
+ data)
+
+ signer := types.NewEIP155Signer(big.NewInt(int64(networkID)))
+ return types.SignTx(tx, signer, r.privateKey)
+}
+
+func (r *Recovery) ProposeSkipBlock(height uint64) error {
+ tx, err := r.genVoteForSkipBlockTx(height)
+ if err != nil {
+ return err
+ }
+
+ txData, err := rlp.EncodeToBytes(tx)
+ if err != nil {
+ return err
+ }
+ _, err = r.client.EthSendRawTransaction("0x" + hex.EncodeToString(txData))
+ return err
+}
+
+func (r *Recovery) Votes(height uint64) (uint64, error) {
+ data, err := abiObject.Pack("numVotes", new(big.Int).SetUint64(height))
+ if err != nil {
+ return 0, err
+ }
+
+ bn, err := r.client.EthBlockNumber()
+ if err != nil {
+ return 0, err
+ }
+
+ snapshotHeight := bn - numConfirmation
+
+ res, err := r.client.EthCall(ethrpc.T{
+ From: r.nodeAddress.String(),
+ To: r.contract.String(),
+ Data: "0x" + hex.EncodeToString(data),
+ }, fmt.Sprintf("0x%x", snapshotHeight))
+ if err != nil {
+ return 0, err
+ }
+
+ resBytes, err := hex.DecodeString(res[2:])
+ if err != nil {
+ return 0, err
+ }
+
+ votes := new(big.Int)
+ err = abiObject.Unpack(&votes, "numVotes", resBytes)
+ if err != nil {
+ return 0, err
+ }
+
+ notarySet, err := r.gov.NotarySetAddresses(r.gov.Round())
+ if err != nil {
+ return 0, err
+ }
+
+ count := uint64(0)
+
+ for i := uint64(0); i < votes.Uint64(); i++ {
+ data, err = abiObject.Pack(
+ "votes", new(big.Int).SetUint64(height), new(big.Int).SetUint64(i))
+ if err != nil {
+ return 0, err
+ }
+
+ res, err = r.client.EthCall(ethrpc.T{
+ From: r.nodeAddress.String(),
+ To: r.contract.String(),
+ Data: "0x" + hex.EncodeToString(data),
+ }, fmt.Sprintf("0x%x", snapshotHeight))
+ if err != nil {
+ return 0, err
+ }
+
+ resBytes, err := hex.DecodeString(res[2:])
+ if err != nil {
+ return 0, err
+ }
+
+ var addr common.Address
+ err = abiObject.Unpack(&addr, "votes", resBytes)
+ if err != nil {
+ return 0, err
+ }
+
+ if _, ok := notarySet[addr]; ok {
+ count += 1
+ }
+ }
+ return count, nil
+}