Add KoreAnchor Ethereum integration - real on-chain anchoring

This commit is contained in:
VZ Build
2026-07-14 18:18:07 +00:00
parent da632737e0
commit e6f6e2979d
2 changed files with 202 additions and 0 deletions

172
eth_anchor.py Normal file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env python3
"""
Vector Zulu — Ethereum KoreAnchor Caller
Calls KoreAnchor.sol at 0x8AaA785c7dC116b834E3A1766866078051fC0313
to anchor KoreIDs immutably on Ethereum mainnet.
Transmitter: 0xA08298ed3Bf2aD64499BEB115441aed85E0985Cc
Uses Chainlink oracle wallet keystore for signing.
"""
import os, json, hashlib, logging
from web3 import Web3
log = logging.getLogger(__name__)
KORE_ANCHOR_ADDRESS = '0x8AaA785c7dC116b834E3A1766866078051fC0313'
ETH_RPC = os.environ.get('ETH_RPC', 'https://eth-mainnet.g.alchemy.com/v2/nRVT_IneMUDF3Pokubawx')
KEYSTORE_PATH = os.environ.get('ANCHOR_KEYSTORE', '/opt/eth-deploy-v21/oracle_keystore.json')
KORE_ANCHOR_ABI = [
{
"inputs": [
{"name": "koreId", "type": "string"},
{"name": "uetr", "type": "string"},
{"name": "eventType", "type": "string"},
{"name": "payloadHash", "type": "bytes32"},
{"name": "amount", "type": "uint256"},
{"name": "currency", "type": "bytes3"},
{"name": "linkedTo", "type": "string"}
],
"name": "anchor",
"outputs": [{"name": "anchoredAt", "type": "uint256"}],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{"name": "koreId", "type": "string"}],
"name": "isAnchored",
"outputs": [{"name": "", "type": "bool"}],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalAnchors",
"outputs": [{"name": "", "type": "uint256"}],
"stateMutability": "view",
"type": "function"
},
{
"anonymous": False,
"inputs": [
{"indexed": True, "name": "koreId", "type": "string"},
{"indexed": True, "name": "uetr", "type": "string"},
{"indexed": False, "name": "eventType", "type": "string"},
{"indexed": False, "name": "amount", "type": "uint256"},
{"indexed": False, "name": "currency", "type": "bytes3"},
{"indexed": False, "name": "anchoredAt", "type": "uint256"}
],
"name": "AnchorCreated",
"type": "event"
}
]
class EthereumAnchor:
def __init__(self):
self.w3 = Web3(Web3.HTTPProvider(ETH_RPC))
self.contract = self.w3.eth.contract(
address=Web3.to_checksum_address(KORE_ANCHOR_ADDRESS),
abi=KORE_ANCHOR_ABI
)
self._account = None
log.info(f"EthereumAnchor initialized — contract {KORE_ANCHOR_ADDRESS}")
def _get_account(self):
if self._account:
return self._account
if not os.path.exists(KEYSTORE_PATH):
log.warning(f"Keystore not found at {KEYSTORE_PATH}")
return None
with open(KEYSTORE_PATH) as f:
ks = json.load(f)
# Empty string password confirmed working
self._account = self.w3.eth.account.from_key(
self.w3.eth.account.decrypt(ks, '')
)
log.info(f"Wallet loaded: {self._account.address}")
return self._account
def payload_hash(self, payload: dict) -> bytes:
"""SHA256 of canonical JSON payload."""
canonical = json.dumps(payload, separators=(',', ':'), sort_keys=True)
return hashlib.sha256(canonical.encode()).digest()
def anchor(self, kore_id: str, uetr: str, event_type: str,
amount: float, currency: str, linked_to: str,
payload: dict = None) -> str:
"""
Anchor a KoreID on Ethereum mainnet.
Returns tx hash or empty string on failure.
"""
account = self._get_account()
if not account:
log.error("No signing account available")
return ''
try:
# Check if already anchored
if self.contract.functions.isAnchored(kore_id).call():
log.info(f"Already anchored: {kore_id}")
return 'already_anchored'
# Build payload hash
ph = self.payload_hash(payload or {
'koreId': kore_id, 'uetr': uetr,
'amount': amount, 'currency': currency
})
# Convert amount to uint256 (x1e8)
amount_int = int(amount * 1e8)
# Convert currency to bytes3
currency_bytes = currency.encode('utf-8').ljust(3, b'\x00')[:3]
# Build transaction
tx = self.contract.functions.anchor(
kore_id, uetr, event_type,
ph, amount_int, currency_bytes, linked_to
).build_transaction({
'from': account.address,
'nonce': self.w3.eth.get_transaction_count(account.address),
'gas': 200000,
})
# Sign and send
signed = account.sign_transaction(tx)
tx_hash = self.w3.eth.send_raw_transaction(signed.raw_transaction)
tx_hex = tx_hash.hex()
log.info(f"Anchored {kore_id} — tx: {tx_hex}")
return tx_hex
except Exception as e:
log.error(f"Anchor failed for {kore_id}: {e}")
return ''
def total_anchors(self) -> int:
try:
return self.contract.functions.totalAnchors().call()
except Exception as e:
log.error(f"totalAnchors error: {e}")
return 0
# Singleton
_anchor = None
def get_anchor() -> EthereumAnchor:
global _anchor
if _anchor is None:
_anchor = EthereumAnchor()
return _anchor
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
a = EthereumAnchor()
print(f"Total anchors on-chain: {a.total_anchors()}")
print(f"Contract: {KORE_ANCHOR_ADDRESS}")
print(f"Connected: {a.w3.is_connected()}")
print(f"Block: {a.w3.eth.block_number}")

View File

@@ -16,6 +16,15 @@ import os, json, time, logging, requests, redis, psycopg2, urllib3
from datetime import datetime, timezone
from lick import build_headers
# Ethereum anchoring — import conditionally (web3 may not be installed)
try:
from eth_anchor import get_anchor
ETH_ANCHOR_ENABLED = True
except ImportError:
ETH_ANCHOR_ENABLED = False
import logging
logging.getLogger(__name__).warning("eth_anchor not available — Ethereum anchoring disabled")
urllib3.disable_warnings()
logging.basicConfig(level=logging.INFO, format='%(asctime)s [KORE-POSTER] %(levelname)s %(message)s')
log = logging.getLogger(__name__)
@@ -145,6 +154,27 @@ class KoreChainPoster:
if success:
store_posted(self.pg, payload, 'POSTED', response_koreid)
log.info(f"{koreid} posted")
# Anchor to Ethereum mainnet
if ETH_ANCHOR_ENABLED:
try:
anchor = get_anchor()
tx_hash = anchor.anchor(
kore_id=koreid,
uetr=payload.get('uetr', ''),
event_type=payload.get('step', 'ANCHOR'),
amount=float(payload.get('amount', 0)),
currency=payload.get('currency', 'USD'),
linked_to=payload.get('linked_to', 'GENESIS-07'),
payload=payload,
)
if tx_hash and tx_hash != 'already_anchored':
log.info(f"⛓ Ethereum anchor tx: {tx_hash}")
store_posted(self.pg, {**payload, 'ethereum_tx': tx_hash}, 'ETH_ANCHORED', tx_hash)
elif tx_hash == 'already_anchored':
log.info(f"⛓ Already on Ethereum: {koreid}")
except Exception as e:
log.error(f"Ethereum anchor error for {koreid}: {e}")
else:
retries += 1
if retries < MAX_RETRIES: