Skip to content

Commit 479b908

Browse files
authored
chore: make token initialisation more robust (#114)
* Make token initialisation more robust * revert default tokens to mint to 5
1 parent 68c6c02 commit 479b908

File tree

1 file changed

+114
-8
lines changed

1 file changed

+114
-8
lines changed

tools/token-mint-service/init_node_tokens.py

Lines changed: 114 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,45 @@ def __init__(self):
2828
"""Initialize the node token service."""
2929
# Required environment variables
3030
self.rpc_url = os.getenv('RPC_URL', 'http://foundry:8545')
31-
self.token_address = os.getenv('TOKEN_ADDRESS', '0x5FbDB2315678afecb367f032d93F642f64180aa3')
32-
self.contract_address = os.getenv('CONTRACT_ADDRESS', '0x5FC8d32690cc91D4c39d9d3abcBD16989F875707')
31+
self.token_address = os.getenv('TOKEN_ADDRESS', '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512')
32+
self.contract_address = os.getenv('RLN_CONTRACT_ADDRESS', '0x0165878A594ca255338adfa4d48449f69242Eb8F')
3333
# The values for NODE_PRIVATE_KEY, NODE_ADDRESS, and NODE_INDEX are set by the get_account_key.sh script
3434
self.private_key = os.getenv('NODE_PRIVATE_KEY')
3535
self.node_address = os.getenv('NODE_ADDRESS')
3636
self.node_index = os.getenv('NODE_INDEX', '0')
37+
# Approver private key for adding accounts to approved minters list
38+
self.approver_private_key = os.getenv('PRIVATE_KEY')
3739

3840
self.mint_amount = int(os.getenv('MINT_AMOUNT', '5000000000000000000')) # at least 5 tokens required for membership with RLN_RELAY_MSG_LIMIT=100
3941

4042
if not self.private_key:
4143
raise ValueError("NODE_PRIVATE_KEY (Ethereum account private key) environment variable is required")
4244
if not self.node_address:
4345
raise ValueError("NODE_ADDRESS (Ethereum account address) environment variable is required")
46+
if not self.approver_private_key:
47+
logger.error("PRIVATE_KEY environment variable is missing!")
48+
raise ValueError("PRIVATE_KEY (Token contract owner private key) environment variable is required")
49+
else:
50+
logger.info(f"PRIVATE_KEY loaded successfully")
4451

4552
# Initialize Web3
4653
self.w3 = Web3(Web3.HTTPProvider(self.rpc_url))
4754
if not self.w3.is_connected():
4855
raise Exception(f"Failed to connect to Ethereum node at {self.rpc_url}")
4956

57+
# Get approver address from private key
58+
try:
59+
self.approver_address = self.w3.eth.account.from_key(self.approver_private_key).address
60+
logger.info(f"Approver address derived: {self.approver_address}")
61+
except Exception as e:
62+
logger.error(f"Failed to derive approver address from PRIVATE_KEY: {str(e)}")
63+
raise
64+
5065
# Convert addresses to proper checksum format
5166
self.node_address = self.w3.to_checksum_address(self.node_address)
5267
self.token_address = self.w3.to_checksum_address(self.token_address)
5368
self.contract_address = self.w3.to_checksum_address(self.contract_address)
69+
self.approver_address = self.w3.to_checksum_address(self.approver_address)
5470

5571
logger.info(f"Node {self.node_index} initializing tokens")
5672
logger.info(f"Address: {self.node_address}")
@@ -76,26 +92,93 @@ def wait_for_transaction(self, tx_hash: str, timeout: int = 120) -> bool:
7692
logger.error(f"Transaction {tx_hash} timed out after {timeout} seconds")
7793
return False
7894

95+
def approve_account_for_minting(self) -> bool:
96+
"""Add this node's address to the approved minters list."""
97+
logger.info(f"=== STARTING APPROVAL PROCESS ===")
98+
logger.info(f"Node address to approve: {self.node_address}")
99+
logger.info(f"Approver address: {self.approver_address}")
100+
logger.info(f"Token contract: {self.token_address}")
101+
102+
for attempt in range(3):
103+
try:
104+
logger.info(f"Adding {self.node_address} to approved minters list (attempt {attempt + 1}/3)")
105+
106+
# Use the approver's private key (contract owner)
107+
nonce = self.w3.eth.get_transaction_count(self.approver_address, 'pending')
108+
109+
# Build addApprovedAccount transaction
110+
function_signature = self.w3.keccak(text="addMinter(address)")[:4]
111+
encoded_address = self.node_address[2:].lower().zfill(64)
112+
data = function_signature.hex() + encoded_address
113+
114+
# Increase gas price for retries to avoid underpriced transactions
115+
gas_price = self.w3.eth.gas_price
116+
if attempt > 0:
117+
gas_price = int(gas_price * (1.1 ** attempt)) # 10% increase per retry
118+
119+
transaction = {
120+
'to': self.token_address,
121+
'value': 0,
122+
'gas': 200000,
123+
'gasPrice': gas_price,
124+
'nonce': nonce,
125+
'data': data,
126+
}
127+
128+
# Sign and send with approver's key
129+
signed_txn = self.w3.eth.account.sign_transaction(transaction, self.approver_private_key)
130+
tx_hash = self.w3.eth.send_raw_transaction(signed_txn.rawTransaction)
131+
132+
logger.info(f"Approve account transaction sent: {tx_hash.hex()}")
133+
134+
if self.wait_for_transaction(tx_hash.hex()):
135+
logger.info(f"✓ Account approval successful for node {self.node_index}")
136+
return True
137+
else:
138+
logger.error(f"✗ Account approval failed for node {self.node_index} (attempt {attempt + 1})")
139+
if attempt < 2:
140+
logger.info(f"Retrying account approval in 5 seconds...")
141+
time.sleep(5)
142+
continue
143+
144+
except Exception as e:
145+
logger.error(f"✗ Account approval failed for node {self.node_index} (attempt {attempt + 1}): {str(e)}")
146+
logger.error(f"Exception type: {type(e).__name__}")
147+
logger.error(f"Exception details: {repr(e)}")
148+
if attempt < 2:
149+
logger.info(f"Retrying account approval in 5 seconds...")
150+
time.sleep(5)
151+
continue
152+
153+
logger.error(f"✗ Account approval failed for node {self.node_index} after 3 attempts")
154+
logger.error(f"=== APPROVAL PROCESS FAILED ===")
155+
return False
156+
79157
def mint_tokens(self) -> bool:
80158
"""Mint tokens to this node's address using the node's own private key."""
81159
for attempt in range(3):
82160
try:
83161
logger.info(f"Minting {self.mint_amount} tokens to {self.node_address} (attempt {attempt + 1}/3)")
84162

85163
# Use the node's own private key since mint() is public
86-
nonce = self.w3.eth.get_transaction_count(self.node_address)
164+
nonce = self.w3.eth.get_transaction_count(self.node_address, 'pending')
87165

88166
# Build mint transaction
89167
function_signature = self.w3.keccak(text="mint(address,uint256)")[:4]
90168
encoded_address = self.node_address[2:].lower().zfill(64)
91169
encoded_amount = hex(self.mint_amount)[2:].zfill(64)
92170
data = function_signature.hex() + encoded_address + encoded_amount
93171

172+
# Increase gas price for retries to avoid underpriced transactions
173+
gas_price = self.w3.eth.gas_price
174+
if attempt > 0:
175+
gas_price = int(gas_price * (1.1 ** attempt)) # 10% increase per retry
176+
94177
transaction = {
95178
'to': self.token_address,
96179
'value': 0,
97180
'gas': 200000,
98-
'gasPrice': self.w3.eth.gas_price,
181+
'gasPrice': gas_price,
99182
'nonce': nonce,
100183
'data': data,
101184
}
@@ -132,19 +215,24 @@ def approve_tokens(self) -> bool:
132215
try:
133216
logger.info(f"Approving {self.mint_amount} tokens for contract {self.contract_address} (attempt {attempt + 1}/3)")
134217

135-
nonce = self.w3.eth.get_transaction_count(self.node_address)
218+
nonce = self.w3.eth.get_transaction_count(self.node_address, 'pending')
136219

137220
# Build approve transaction
138221
function_signature = self.w3.keccak(text="approve(address,uint256)")[:4]
139222
encoded_contract = self.contract_address[2:].lower().zfill(64)
140223
encoded_amount = hex(self.mint_amount)[2:].zfill(64)
141224
data = function_signature.hex() + encoded_contract + encoded_amount
142225

226+
# Increase gas price for retries to avoid underpriced transactions
227+
gas_price = self.w3.eth.gas_price
228+
if attempt > 0:
229+
gas_price = int(gas_price * (1.1 ** attempt)) # 10% increase per retry
230+
143231
transaction = {
144232
'to': self.token_address,
145233
'value': 0,
146234
'gas': 200000,
147-
'gasPrice': self.w3.eth.gas_price,
235+
'gasPrice': gas_price,
148236
'nonce': nonce,
149237
'data': data,
150238
}
@@ -179,12 +267,30 @@ def run(self) -> bool:
179267
"""Run the token initialization process."""
180268
try:
181269
logger.info(f"Starting token initialization for node {self.node_index}")
270+
logger.info(f"=== DEBUG INFO ===")
271+
logger.info(f"Node address: {self.node_address}")
272+
logger.info(f"Approver address: {self.approver_address}")
273+
logger.info(f"Token address: {self.token_address}")
274+
logger.info(f"PRIVATE_KEY present: {'Yes' if self.approver_private_key else 'No'}")
275+
logger.info(f"==================")
276+
277+
# Step 1: Add node address to approved minters list
278+
logger.info(f"STEP 1: Starting approval process...")
279+
try:
280+
if not self.approve_account_for_minting():
281+
logger.error(f"STEP 1 FAILED: Could not approve account for minting")
282+
return False
283+
logger.info(f"STEP 1 SUCCESS: Account approved for minting")
284+
except Exception as e:
285+
logger.error(f"STEP 1 EXCEPTION: {str(e)}")
286+
logger.error(f"Exception type: {type(e).__name__}")
287+
return False
182288

183-
# Step 1: Mint tokens
289+
# Step 2: Mint tokens
184290
if not self.mint_tokens():
185291
return False
186292

187-
# Step 2: Approve contract
293+
# Step 3: Approve contract
188294
if not self.approve_tokens():
189295
return False
190296

0 commit comments

Comments
 (0)