@@ -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