Skip to content

Commit 04a073e

Browse files
committed
Bug Fixes, prevent accidental key deletion
1 parent 2f5e1ff commit 04a073e

File tree

3 files changed

+91
-28
lines changed

3 files changed

+91
-28
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ Cli wrapper on top of cardano-cli for governance actions on cardano blockchain
1212

1313
Tutorial : [Quick Start with Gov-Cli](https://cardanoapi.github.io/gov-cli/docs/intro)
1414

15+
### Env Variables
16+
| **Environment Variable** | **Description** | **Possible Values** |
17+
|------------------------------|----------------------------------------|----------------------------------------------------|
18+
| `CARDANO_NODE_SOCKET_PATH` | Node's socket path | (e.g., path to the socket file) |
19+
| `NETWORK` | Cardano network | `1`, `2`, `3`, `4`, `mainnet`, `preprod`, `sancho`, `preview` |
20+
| `KEYS_DIR` | Where keys are generated and stored | `$HOME/.cardano/key` |
21+
| `LOG_CLI` | Show commands executed in CLI | `yes`, `no`, `false` |
22+
1523

1624
### Installation
1725
```

gov_cli/gov_cli.py

Lines changed: 82 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env python
2+
from ctypes import Array
23
import shutil
34
import subprocess
45
from subprocess import CalledProcessError, CompletedProcess
@@ -8,12 +9,15 @@
89
import json
910

1011
HOME=os.environ.get("HOME","/root")
12+
log_cli= 'LOG_CLI' in os.environ and os.environ['LOG_CLI'].lower() not in ('no','off','false','0')
1113
# Define paths
1214
KEYS_DIR = os.environ.get("CARDANO_KEYS_DIR",os.path.join(HOME,".cardano","keys"))
1315
NETWORK = "--testnet-magic=4"
1416
CARDANO_NODE_SOCKET_PATH=os.environ.get("CARDANO_NODE_SOCKET_PATH",os.path.join(HOME,'.cardano','testnet','node.socket')).strip()
15-
if not os.path.exists(KEYS_DIR):
16-
os.makedirs(KEYS_DIR)
17+
TEMP_DIR= os.path.join(KEYS_DIR,'tmp')
18+
if not os.path.exists(TEMP_DIR):
19+
os.makedirs(TEMP_DIR)
20+
1721
if(os.environ.get("NETWORK")):
1822
NETWORK = os.environ.get("NETWORK").strip()
1923

@@ -25,7 +29,7 @@
2529
except ValueError:
2630
if NETWORK == 'mainnet':
2731
NETWORK = "--mainnet"
28-
elif NETWORK == 'snacho' or NETWORK == 'sanchonet':
32+
elif NETWORK == 'sancho' or NETWORK == 'sanchonet':
2933
NETWORK="--testnet-magic=4"
3034
elif NETWORK == 'preview':
3135
NETWORK="--testnet-magic=2"
@@ -39,7 +43,6 @@ class SubmitResult:
3943
def __init__(self,process,txid):
4044
self.process:CompletedProcess=process
4145
self.txid:str=txid
42-
log_cli= 'LOG_CLI' in os.environ and os.environ['LOG_CLI'].lower() not in ('no','off','false','0')
4346
# Utility function to run cardano-cli commands
4447
def run_cli_command(command: List[str],raise_error=True):
4548
if log_cli:
@@ -80,6 +83,8 @@ def gen_wallet(self,cli:'CardanoCLI'):
8083
stake_vkey = os.path.join(self.keys_dir, "stake.vkey")
8184
stake_skey = os.path.join(self.keys_dir, "stake.skey")
8285
payment_addr = os.path.join(self.keys_dir, "payment.addr")
86+
check_if_file_exists(payment_vkey,payment_skey,stake_vkey,stake_skey,payment_addr)
87+
8388

8489
# Generate keys
8590
cli.cardano_cli( "address", "key-gen", ["--verification-key-file", payment_vkey, "--signing-key-file", payment_skey])
@@ -98,6 +103,7 @@ def gen_drep_key(self,cli:'CardanoCLI'):
98103
drep_vkey = os.path.join(self.keys_dir, "drep.vkey")
99104
drep_skey = os.path.join(self.keys_dir, "drep.skey")
100105
drep_id = os.path.join(self.keys_dir, "drep.id.hex.txt")
106+
check_if_file_exists(drep_vkey,drep_skey,drep_id)
101107

102108
cli.cardano_cli_conway("governance", "drep",["key-gen", "--verification-key-file", drep_vkey, "--signing-key-file", drep_skey])
103109
cli.cardano_cli_conway("governance", "drep", ["id","--drep-verification-key-file",drep_vkey,"--out-file",drep_id,"--output-format","hex"])
@@ -126,7 +132,7 @@ def gen_cc_keys(self,cli:'CardanoCLI')->(Key):
126132
hot_vkey = self.file_path("cc-hot.vkey")
127133
hot_skey = self.file_path("cc-hot.skey")
128134
authorization_cert=self.file_path("cc-hot-key-authorization.cert")
129-
135+
check_if_file_exists(cold_vkey,cold_skey,hot_vkey,hot_skey,cold_key_hash,authorization_cert)
130136

131137
cli.cardano_cli_conway("governance","committee",[
132138
"key-gen-cold",
@@ -206,7 +212,7 @@ def build_tx(self,wallet:Wallet,tx_name:str,commands:List[str],add_collateral=Fa
206212
commands.extend(["--tx-in-collateral",list(utxos.keys())[0]])
207213

208214
# Build the transaction
209-
tx_body_file = os.path.join(KEYS_DIR, tx_name+"_tx.raw")
215+
tx_body_file = os.path.join(TEMP_DIR, tx_name+"_tx.raw")
210216

211217

212218
self.cardano_cli_conway("transaction", "build",
@@ -216,7 +222,7 @@ def build_tx(self,wallet:Wallet,tx_name:str,commands:List[str],add_collateral=Fa
216222

217223
def build_and_submit(self,wallet:Wallet,tx_name:str,commands:List[str],raise_error=True,extra_keys=[],add_collateral=False):
218224

219-
signed_tx_file = os.path.join(KEYS_DIR, tx_name+"_signed_tx.json")
225+
signed_tx_file = os.path.join(TEMP_DIR, tx_name+"_signed_tx.json")
220226

221227
tx_body_file=self.build_tx(wallet,tx_name,commands,add_collateral)
222228
return self.sign_and_submit(wallet,tx_body_file,signed_tx_file,raise_error=raise_error,extra_keys=extra_keys)
@@ -240,7 +246,7 @@ def load_gov_state(self,):
240246
def propose(self,wallet:Wallet,args):
241247
self.load_gov_state()
242248
proposal_type=args[0] if len (args) >0 else ''
243-
proposal_file=proposal_type+".proposal"
249+
proposal_file=os.path.join(TEMP_DIR,proposal_type+".proposal")
244250
req_prev=["create-no-confidence","update-committee","create-constitution","create-protocol-parameters-update","create-hardfork"]
245251
no_prev=["create-treasury-withdrawal","create-info"]
246252
require_guardrail=['create-treasury-withdrawal',"create-protocol-parameters-update"]
@@ -301,7 +307,7 @@ def propose(self,wallet:Wallet,args):
301307

302308

303309
def register_stake(self, wallet: Wallet):
304-
stake_cert = os.path.join(KEYS_DIR, "stake_reg.cert")
310+
stake_cert = os.path.join(TEMP_DIR, "stake_reg.cert")
305311
# Generate stake key registration certificate
306312
self.load_gov_state()
307313

@@ -338,7 +344,10 @@ def vote(self,vote,wallet:Wallet,key:Key,role:str,action_tx,action_tx_index):
338344
key_arg="--cc-hot-verification-key-file"
339345
elif role == 'spo':
340346
key_arg="--cold-verification-key-file"
341-
vote_file=os.path.join(KEYS_DIR,"vote_"+vote+"_"+action_tx+'_'+action_tx_index+".vote")
347+
else:
348+
print("Invalid Role :", role)
349+
exit(1)
350+
vote_file=os.path.join(TEMP_DIR,"vote_"+vote+"_"+action_tx+'_'+action_tx_index+".vote")
342351
self.cardano_cli_conway("governance","vote",[
343352

344353
"create",
@@ -354,12 +363,24 @@ def vote(self,vote,wallet:Wallet,key:Key,role:str,action_tx,action_tx_index):
354363
"--witness-override", "2"
355364
],extra_keys=[key.private],raise_error=False)
356365
process=result.process
357-
if 'GovActionsDoNotExist' in process.stderr:
358-
print("ERROR: Gov Action doesn't exist on chain : ",action_tx+"#"+action_tx_index)
359-
print("Nothing was done on-chain")
360-
366+
if process.returncode > 0:
367+
if 'GovActionsDoNotExist' in process.stderr:
368+
print("ERROR: Gov Action doesn't exist on chain : ",action_tx+"#"+action_tx_index)
369+
print("Nothing was done on-chain")
370+
elif 'VotersDoNotExist (DRepVoter' in process.stderr:
371+
print("ERROR: You are not a Registred Drep")
372+
print("Nothing was done on-chain")
373+
elif 'DisallowedVoters ((CommitteeVoter' in process.stderr:
374+
print("ERROR: You are not a CC Member")
375+
print("Nothing was done on-chain")
376+
else :
377+
print(process.stderr)
378+
print("Nothing was done on-chain")
379+
else:
380+
print("Submitted :",result.txid)
381+
361382
def register_drep(self, wallet: Wallet, drep: Key):
362-
drep_cert = os.path.join(KEYS_DIR, "drep_reg.cert")
383+
drep_cert = os.path.join(TEMP_DIR, "drep_reg.cert")
363384
# Generate DRep registration certificate
364385
self.load_gov_state()
365386

@@ -404,7 +425,7 @@ def sign_and_submit(self,wallet:Wallet,tx_raw_file,signed_tx_file,raise_error=Tr
404425
return self.get_tx_id(tx_raw_file)
405426

406427
def delegate(self,wallet:Wallet,own_drep:Key,drep:str):
407-
delegation_cert = os.path.join(KEYS_DIR, "vote_deleg.cert")
428+
delegation_cert = os.path.join(TEMP_DIR, "vote_deleg.cert")
408429

409430
if drep == 'abstain':
410431
delegation=["--always-abstain"]
@@ -429,38 +450,53 @@ def delegate(self,wallet:Wallet,own_drep:Key,drep:str):
429450
"--certificate-file", delegation_cert],extra_keys=[wallet.stake_skey],raise_error=False)
430451
process=result.process
431452
if(process.returncode!=0):
453+
432454
if 'StakeKeyNotRegisteredDELEG' in process.stderr:
433-
print("ERROR: Drep is not registered:",drep)
434-
elif 'StakeKeyNotRegisteredDELEG' in process.stderr:
435-
print("Your stake key is not registered")
455+
print("ERROR: Stake Address is not registered")
436456
print("run > gov-cli register stake")
437-
print("To register your stake")
438-
else:
457+
print("To register your stake")
458+
else: ## Ledger allows delegating to non-existing drep.
439459
raise Exception("Process failed "+process.stdout + process.stderr)
440460
else:
441461
print("Submitted :",result.txid)
442462

443463
def cc_authorize_hot_key(self,wallet:Wallet,hot_key:Key,cold_key:Key):
444464

445-
txid=self.build_and_submit(wallet,'cc_hot_key_register',[
465+
result:SubmitResult=self.build_and_submit(wallet,'cc_hot_key_register',[
446466
"--certificate-file",hot_key.id,
447467
"--witness-override","2"
448-
],extra_keys=[cold_key.private])
468+
],extra_keys=[cold_key.private],raise_error=False)
449469

450-
print("Submitted :",txid)
470+
process=result.process
471+
if(process.returncode!=0):
451472

473+
if 'ConwayCommitteeIsUnknown' in process.stderr:
474+
print("CC Cold Key Hash: ",cold_key.id)
475+
print("ERROR: Cold Key is not registered as CC Member")
476+
else: ## Ledger allows delegating to non-existing drep.
477+
raise Exception("Process failed "+process.stdout + process.stderr)
478+
else:
479+
print("Submitted :",result.txid)
452480

453481

454482
def query_utxos(self, wallet: Wallet):
455483
utxos = self.cardano_cli("query", "utxo", ["--address", wallet.address], include_network=True, include_socket=True,)
456484
return utxos
457485

458486
def query_utxos_json(self, wallet: Wallet):
487+
459488
file_path = os.path.join(KEYS_DIR, 'utxo.json')
460489
utxos = self.cardano_cli("query", "utxo", ["--address", wallet.address ,"--out-file",file_path], include_network=True, include_socket=True,)
461490

462491
with open(file_path) as f:
463492
utxos=json.load(f)
493+
if len(utxos) ==0:
494+
print("Wallet Adddress : ",wallet.address)
495+
print(" ERROR: Missing Balance")
496+
497+
exit(1)
498+
499+
464500
return utxos
465501

466502

@@ -559,6 +595,7 @@ def vote(store:WalletStore,role):
559595

560596
elif command == "wallet":
561597
store = WalletStore(KEYS_DIR)
598+
print("Wallet Store :",KEYS_DIR)
562599
try:
563600
wallet = store.load_wallet()
564601
print(f"Address : {wallet.address}")
@@ -573,14 +610,19 @@ def vote(store:WalletStore,role):
573610

574611
try:
575612
wallet = store.load_cc_cold_keys()
613+
hot_key = store.load_cc_hot_keys()
614+
576615
print(f"CC key Hash : {wallet.id}")
616+
print(f"CC Hot KeyHash: {hot_key.id}")
617+
577618
except:
578619
print(":: CC keys not generated > gov-cli gen cc")
579620

580621

581622
elif command == "register":
582623
store=WalletStore(KEYS_DIR)
583624
wallet = store.load_wallet()
625+
584626
if len(sys.argv)>2:
585627
if sys.argv[2] == "drep":
586628
drep=store.load_drep_key()
@@ -595,6 +637,7 @@ def vote(store:WalletStore,role):
595637
print("Invalid option for register \"" + sys.argv[2]+"\". Expected \"drep\" or \"stake\"")
596638

597639
else:
640+
print("Registering stake key")
598641
# Register stake key
599642
cli.register_stake(wallet)
600643
elif command == 'delegate':
@@ -604,7 +647,7 @@ def vote(store:WalletStore,role):
604647
if len(sys.argv) ==3:
605648
cli.delegate(wallet,drep,sys.argv[2])
606649
else:
607-
print("Usage:\n gov-cli delegate [no-confidence|abstain|drep_id]")
650+
print("Usage:\n gov-cli delegate [no-confidence|abstain|drep_id|self]")
608651

609652
elif command == "propose":
610653
store=WalletStore(KEYS_DIR)
@@ -641,7 +684,6 @@ def vote(store:WalletStore,role):
641684
sys.exit(1)
642685

643686
# Query UTXOs for the specified address
644-
print("queryutxo")
645687
wallet = Wallet(payment_vkey=None, payment_skey=None, stake_vkey=None, stake_skey=None, address=address)
646688
result=cli.query_utxos(wallet)
647689
print(result)
@@ -678,4 +720,17 @@ def vote(store:WalletStore,role):
678720
print('Tx Submitted :',tx)
679721

680722
else:
681-
help()
723+
help()
724+
725+
def check_if_file_exists(*files):
726+
existing_files = []
727+
for file in files:
728+
if os.path.exists(file):
729+
existing_files.append(file)
730+
731+
if len(existing_files) > 0:
732+
print("File Already Exists:")
733+
for file in existing_files:
734+
print(" -",file)
735+
print("Backup and remove them to generate new keys")
736+
exit(1)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
setup(
44
name="gov-cli",
5-
version="0.3",
5+
version="1.0",
66
packages=find_packages(),
77
install_requires=[
88
# List your dependencies here, e.g.:

0 commit comments

Comments
 (0)