Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create add masternodes script #69

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion devnet/genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,10 @@
},
"03c0d9bc556BE68870B96976e81D32ebb49d335D": {
"balance": "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
},
"377B97484925d616886816754c6CD4FB2004D4C1": {
"balance": "0x7912752226cec5131e000000"
}

},
"number": "0x0",
"gasUsed": "0x0",
Expand Down
26 changes: 26 additions & 0 deletions devnet/masternodes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Script to Turn Nodes into Masternodes

## Usage and Result
```
python add_masternode.py -n some_value
# -n refers to the number of masternode to add into the chain. The maximum value is 3122.
```

After running the script,
- A list of masternodes' private key are generated into **MASTERNODE_KEY_FOLDER**.
- A transaction record will be created to help checking. Each line in the file can be used to be track the transaction status(like which block it is added into).

## Configuration Parameters in config.py
- **NODE_RPC**: RPC endpoint the web3 should connect to. It should be the rpc link used by the default nodes
- **CONTRACT_JSON**: path to the XDCValidator contract abi. Default is sufficient
- **RICH_KEY**: path to the rich account private key who owns a lot of XDC tokens. This file should not be modified
- **OWNER_KEY**: path to the owner account private key. All the masternodes generated belong to this owner
- **GENESIS**: path to genesis file
- **MASTERNODE_KEY_FOLDER**: path to directory where masternode private keys are created into
- **TRANSACTION_RECORD**: path to the transaction record

## Correct Workflow
1. Start initial nodes.
2. Update config.py and run add_masternode.py
3. Start other nodes where their wallet will import private keys from **MASTERNODE_KEY_FOLDER**
4. Wait for the next epoch to be added into masternodes
176 changes: 176 additions & 0 deletions devnet/masternodes/add_masternode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import json
from multiprocessing.sharedctypes import Value
from time import sleep
import requests
from web3 import Web3
from eth_account import Account
from config import *
import argparse
import sys
import os


# Connect with all nodes
w3 = Web3(Web3.HTTPProvider(NODE_RPC))

# Load contract abi
with open(CONTRACT_JSON, "r") as f:
contract = json.load(f)
# Load contract abi into web3
val_addr = "0x0000000000000000000000000000000000000088"
val_abi = contract["abi"]
val_contract = w3.eth.contract(address=val_addr, abi=val_abi)

# Load genesis file
with open(GENESIS, "r") as f:
genesis = json.load(f)

# Load rich account
with open(RICH_KEY, "r") as f:
privKey = f.readline().strip()
rich_account = Account.from_key(f"0x{privKey}")

# Load generated key 1 for testing
with open(OWNER_KEY, "r") as f:
privKey = f.readline().strip()
owner_account = Account.from_key(f"0x{privKey}")

def parse_args():
parser = argparse.ArgumentParser(description="Masternode key generation and election")
parser.add_argument("-n", "--number", default=1, help="number to masternodes to add")
return parser.parse_args()

def transfer(value, from_account, to_addr):
signed_transaction = w3.eth.account.sign_transaction({
"from": from_account.address,
"to": to_addr,
"nonce": w3.eth.get_transaction_count(from_account.address),
"gas": 21000,
"gasPrice": 250000000,
"value": value
}, from_account.key)

txn_hash = w3.eth.send_raw_transaction(signed_transaction.rawTransaction)
return txn_hash.hex()

def uploadKYC(kyhash, from_account):
estimate_gas = val_contract.functions["uploadKYC"](kyhash).estimate_gas()
encoded_data = val_contract.encodeABI(fn_name="uploadKYC", args=[kyhash])
signed_transaction = w3.eth.account.sign_transaction({
"from": from_account.address,
"to": val_addr,
"nonce": w3.eth.get_transaction_count(from_account.address),
"gas": estimate_gas,
"gasPrice": 250000000,
"value": 0,
"data": encoded_data
}, from_account.key)

txn_hash = w3.eth.send_raw_transaction(signed_transaction.rawTransaction)
return txn_hash.hex()

def propose(candidate_addr, owner_account):
encoded_data = val_contract.encodeABI(fn_name="propose", args=[candidate_addr])
signed_transaction = w3.eth.account.sign_transaction({
"from": owner_account.address,
"to": val_addr,
"nonce": w3.eth.get_transaction_count(owner_account.address),
"gas": 470000,
"gasPrice": 250000000,
"value": 10000000000000000000000000,
"data": encoded_data
}, owner_account.key)

txn_hash = w3.eth.send_raw_transaction(signed_transaction.rawTransaction)
return txn_hash.hex()

def eth_getTransactionByHash(hash):
res = requests.post(
NODE_RPC,
json={
"method":"eth_getTransactionByHash",
"params":[hash],
"id": 1,
"jsonrpc":"2.0"
}
)
return res.json()["result"]

def eth_sendRawTransaction(raw_transaction):
res = requests.post(
NODE_RPC,
json={
"method":"eth_sendRawTransaction",
"params":[raw_transaction.hex()],
"id": 1,
"jsonrpc":"2.0"
}
)
return res.json()["result"]

def eth_getTransactionByBlockNumberAndIndex(blockNumber, index):
res = requests.post(
NODE_RPC,
json={
"method":"eth_getTransactionByBlockNumberAndIndex",
"params":[hex(blockNumber), hex(index)],
"id": 1,
"jsonrpc":"2.0"
}
)
return res.json()["result"]

def eth_getBlockTransactionCountByNumber(blockNumber):
res = requests.post(
NODE_RPC,
json={
"method":"eth_getBlockTransactionCountByNumber",
"params":[hex(blockNumber)],
"id": 1,
"jsonrpc":"2.0"
}
)
return res.json()["result"]

def eth_getBlockByNumber(blockNumber):
res = requests.post(
NODE_RPC,
json={
"method":"eth_getBlockByNumber",
"params":[hex(blockNumber), False],
"id": 1,
"jsonrpc":"2.0"
}
)
return res.json()["result"]



if __name__ == "__main__":
args = parse_args()
num_masternodes = int(args.number)
if num_masternodes > 3122:
print("Too many requested masternodes. Maximum amount is 3122")
sys.exit(1)

# 1. transfer entire amount of XDC from rich account to owner account
txn1 = transfer(12000000000000000000000000 * num_masternodes, rich_account, owner_account.address)
sleep(2)

# 2. submit kyhash from owner account to add it into whitelist
txn2 = uploadKYC("test", owner_account)
sleep(2)

txns = []
for i in range(num_masternodes):
with open(f"{MASTERNODE_KEY_FOLDER}/GENERATED_KEY_{i+1}.txt", "w") as f:
masternode_account = Account.create(os.urandom(64))
f.write(masternode_account.key.hex()[2:])
txn = propose(masternode_account.address, owner_account)
txns.append(txn)

with open(TRANSACTION_RECORD, "w") as f:
f.write(f"{txn1}\n")
f.write(f"{txn2}\n")
for txn in txns:
f.write(f"{txn}\n")
Loading