fix: use explicit SQL UPDATE for balance changes and add value field mapping
Changed transaction failure logging from error to warning level in PoA proposer. Removed immediate session.commit() after state transition as balance changes are now persisted via explicit SQL UPDATE statements. Added "value" field mapping from "amount" in transaction normalization and PoA proposer to ensure state transition compatibility. Replaced SQLAlchemy ORM balance updates with explicit SQL UPDATE statements using
This commit is contained in:
@@ -278,18 +278,16 @@ class PoAProposer:
|
|||||||
# Use original tx_data from mempool to preserve type and payload
|
# Use original tx_data from mempool to preserve type and payload
|
||||||
tx_data_for_transition = tx.content.copy()
|
tx_data_for_transition = tx.content.copy()
|
||||||
tx_data_for_transition["nonce"] = sender_account.nonce
|
tx_data_for_transition["nonce"] = sender_account.nonce
|
||||||
|
# Map "amount" to "value" for state transition compatibility
|
||||||
|
tx_data_for_transition["value"] = tx_data_for_transition.get("amount", 0)
|
||||||
success, error_msg = state_transition.apply_transaction(
|
success, error_msg = state_transition.apply_transaction(
|
||||||
session, self._config.chain_id, tx_data_for_transition, tx.tx_hash
|
session, self._config.chain_id, tx_data_for_transition, tx.tx_hash
|
||||||
)
|
)
|
||||||
|
|
||||||
if not success:
|
if not success:
|
||||||
self._logger.error(f"[PROPOSE] Failed to apply transaction {tx.tx_hash}: {error_msg}")
|
self._logger.warning(f"[PROPOSE] Failed to apply transaction {tx.tx_hash}: {error_msg}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Commit the balance changes immediately after successful state transition
|
|
||||||
session.commit()
|
|
||||||
self._logger.info(f"[PROPOSE] Committed balance changes for tx {tx.tx_hash}")
|
|
||||||
|
|
||||||
# Check if transaction already exists in database
|
# Check if transaction already exists in database
|
||||||
existing_tx = session.exec(
|
existing_tx = session.exec(
|
||||||
select(Transaction).where(
|
select(Transaction).where(
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ def _normalize_transaction_data(tx_data: Dict[str, Any], chain_id: str) -> Dict[
|
|||||||
"from": sender.strip(),
|
"from": sender.strip(),
|
||||||
"to": recipient.strip(),
|
"to": recipient.strip(),
|
||||||
"amount": amount,
|
"amount": amount,
|
||||||
|
"value": amount, # Add value field for state transition compatibility
|
||||||
"fee": fee,
|
"fee": fee,
|
||||||
"nonce": nonce,
|
"nonce": nonce,
|
||||||
"payload": payload,
|
"payload": payload,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
|
from sqlalchemy import select, text
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from ..models import Account, Transaction, Receipt
|
from ..models import Account, Transaction, Receipt
|
||||||
@@ -165,6 +166,7 @@ class StateTransition:
|
|||||||
Returns:
|
Returns:
|
||||||
Tuple of (success, error_message)
|
Tuple of (success, error_message)
|
||||||
"""
|
"""
|
||||||
|
logger.info(f"apply_transaction called for tx {tx_hash}, tx_data keys: {list(tx_data.keys())}")
|
||||||
# Validate first
|
# Validate first
|
||||||
is_valid, error_msg = self.validate_transaction(session, chain_id, tx_data, tx_hash)
|
is_valid, error_msg = self.validate_transaction(session, chain_id, tx_data, tx_hash)
|
||||||
if not is_valid:
|
if not is_valid:
|
||||||
@@ -198,7 +200,7 @@ class StateTransition:
|
|||||||
else:
|
else:
|
||||||
tx_type = "TRANSFER"
|
tx_type = "TRANSFER"
|
||||||
|
|
||||||
# Apply balance changes
|
# Apply balance changes using explicit SQL UPDATE
|
||||||
value = tx_data.get("value", 0)
|
value = tx_data.get("value", 0)
|
||||||
fee = tx_data.get("fee", 0)
|
fee = tx_data.get("fee", 0)
|
||||||
|
|
||||||
@@ -209,12 +211,20 @@ class StateTransition:
|
|||||||
total_cost = value + fee
|
total_cost = value + fee
|
||||||
recipient_account = session.get(Account, (chain_id, recipient_addr))
|
recipient_account = session.get(Account, (chain_id, recipient_addr))
|
||||||
|
|
||||||
sender_account.balance -= total_cost
|
# Use explicit SQL UPDATE to ensure persistence
|
||||||
sender_account.nonce += 1
|
logger.info(f"Updating sender balance: {sender_addr} -= {total_cost}")
|
||||||
|
session.execute(
|
||||||
|
text("UPDATE account SET balance = balance - :total_cost, nonce = nonce + 1 WHERE chain_id = :chain_id AND address = :sender_addr"),
|
||||||
|
{"total_cost": total_cost, "chain_id": chain_id, "sender_addr": sender_addr}
|
||||||
|
)
|
||||||
|
|
||||||
# For MESSAGE transactions, skip recipient balance change
|
# For MESSAGE transactions, skip recipient balance change
|
||||||
if tx_type != "MESSAGE":
|
if tx_type != "MESSAGE":
|
||||||
recipient_account.balance += value
|
logger.info(f"Updating recipient balance: {recipient_addr} += {value}")
|
||||||
|
session.execute(
|
||||||
|
text("UPDATE account SET balance = balance + :value WHERE chain_id = :chain_id AND address = :recipient_addr"),
|
||||||
|
{"value": value, "chain_id": chain_id, "recipient_addr": recipient_addr}
|
||||||
|
)
|
||||||
|
|
||||||
# For RECEIPT_CLAIM transactions, mint reward and update receipt status
|
# For RECEIPT_CLAIM transactions, mint reward and update receipt status
|
||||||
if tx_type == "RECEIPT_CLAIM":
|
if tx_type == "RECEIPT_CLAIM":
|
||||||
|
|||||||
Reference in New Issue
Block a user