js代码改写py

This commit is contained in:
fengche
2026-01-30 17:47:21 +08:00
parent a482e4ea77
commit aad77378ab
20 changed files with 2649 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
import aiomysql
class DBPool:
def __init__(self, coin, options):
self.coin = coin
self.pool = None
self.options = options
async def _get_pool(self):
if self.pool is None:
self.pool = await aiomysql.create_pool(**self.options)
return self.pool
# -------------------------
# 工具:判断是否 SELECT
# -------------------------
def _is_select(self, sql: str) -> bool:
return sql.lstrip().lower().startswith("select")
# -------------------------
# 非事务 SQL
# -------------------------
async def exec(self, sql, values=None):
if values is None:
values = []
pool = await self._get_pool()
async with pool.acquire() as conn:
async with conn.cursor(aiomysql.DictCursor) as cur:
await cur.execute(sql, values)
if self._is_select(sql):
# JS: SELECT 返回 rows
return await cur.fetchall()
else:
# JS: 非 SELECT 返回 OkPacket
return {
"affectedRows": cur.rowcount,
"insertId": cur.lastrowid,
}
# -------------------------
# 单 SQL 事务
# -------------------------
async def exec_transaction(self, sql, values=None):
if values is None:
values = []
pool = await self._get_pool()
async with pool.acquire() as conn:
async with conn.cursor(aiomysql.DictCursor) as cur:
await conn.begin()
try:
await cur.execute(sql, values)
if self._is_select(sql):
result = await cur.fetchall()
else:
result = {
"affectedRows": cur.rowcount,
"insertId": cur.lastrowid,
}
await conn.commit()
return result
except Exception:
await conn.rollback()
raise
# -------------------------
# 多 SQL 合并事务
# -------------------------
async def exec_transaction_together(self, params):
pool = await self._get_pool()
async with pool.acquire() as conn:
async with conn.cursor() as cur:
await conn.begin()
try:
for item in params:
sql = item["sql"]
param = item.get("param", [])
await cur.execute(sql, param)
await conn.commit()
except Exception:
await conn.rollback()
raise
# -------------------------
# 写锁事务
# -------------------------
async def exec_write_lock(self, sql, values=None, table_name=None):
if values is None:
values = []
pool = await self._get_pool()
async with pool.acquire() as conn:
async with conn.cursor(aiomysql.DictCursor) as cur:
await conn.begin()
try:
await cur.execute(f"LOCK TABLES {table_name} WRITE")
await cur.execute(sql, values)
if self._is_select(sql):
result = await cur.fetchall()
else:
result = {
"affectedRows": cur.rowcount,
"insertId": cur.lastrowid,
}
await conn.commit()
return result
except Exception:
await conn.rollback()
raise
finally:
# 对齐 JS始终解锁
await conn.cursor().execute("UNLOCK TABLES")
__all__ = ["DBPool"]

View File

@@ -0,0 +1,298 @@
import httpx
class BaseRPCNode:
def __init__(self, NODE_OPTION):
self.rpcUser = NODE_OPTION.get("rpcUser")
self.rpcHost = NODE_OPTION.get("rpcHost")
self.rpcPassword = NODE_OPTION.get("rpcPassword")
self.rpcPort = NODE_OPTION.get("rpcPort")
self.base_url = f"http://{self.rpcHost}:{self.rpcPort}"
self.client = httpx.AsyncClient(
base_url=self.base_url,
auth=(self.rpcUser, self.rpcPassword),
timeout=5.0
)
async def callRpcMethod(self, method, params=None):
if params is None:
params = []
try:
response = await self.client.post("/", json={
"jsonrpc": "1.0",
"id": "testnet",
"method": method,
"params": params
})
response.raise_for_status()
return response.json()["result"]
except httpx.HTTPStatusError as e:
print("RPC Error:", e.response.text)
raise
except Exception as e:
print("RPC Error:", str(e))
raise
async def getblockcount(self):
return await self.callRpcMethod("getblockcount", [])
async def getblockhash(self, height):
return await self.callRpcMethod("getblockhash", [height])
async def getblock(self, param):
if isinstance(param, str):
return await self.callRpcMethod("getblock", [param, 2])
elif isinstance(param, int):
hash_ = await self.getblockhash(param)
return await self.callRpcMethod("getblock", [hash_, 2])
else:
raise ValueError("param must be str or int")
# ================= NEXA =================
class NEXARPCNode(BaseRPCNode):
async def verify_wallet(self, address):
try:
await self.callRpcMethod("getreceivedbyaddress", [address])
return True
except Exception:
return False
async def getblock(self, height):
return await self.callRpcMethod("getblock", [height, 2])
async def verify_block(self, height, address):
block_data = await self.getblock(height)
for item in block_data["tx"]:
if len(item["vin"]) == 0:
addresses = item["vout"][0]["scriptPubKey"]["addresses"]
if address == addresses[0]:
return block_data
else:
return False
def block(self, block_data):
tx = block_data["tx"]
time = block_data["time"]
hash_ = block_data["hash"]
height = block_data["height"]
block_fees = 0
block_reward = 0
for item in tx:
if len(item["vin"]) == 0:
block_reward = item["sends"]
else:
block_fees += item.get("fee", 0)
return {
"height": height,
"hash": hash_,
"time": time,
"block_reward": block_reward,
"block_fees": block_fees
}
# ================= GRS =================
class GRSRPCNode(BaseRPCNode):
async def verify_block(self, height, REPORT_ADDRESS):
block_data = await self.getblock(height)
for item in block_data["tx"]:
vin = item["vin"]
vout = item["vout"]
if vin[0].get("coinbase"):
for value in vout:
addr = value["scriptPubKey"].get("address")
if addr:
if addr == REPORT_ADDRESS:
return block_data
else:
return False
return False
def block(self, data):
hash_ = data["hash"]
tx = data["tx"]
height = data["height"]
time = data["time"]
reward = 0
fees = 0
for item in tx:
vin = item["vin"]
vout = item["vout"]
if vin[0].get("coinbase"):
for value in vout:
reward += value["value"]
else:
fees += item.get("fee", 0)
return {
"height": height,
"hash": hash_,
"time": time,
"block_reward": reward,
"block_fees": fees
}
# ================= MONA =================
class MONARPCNode(BaseRPCNode):
async def verify_block(self, height, REPORT_ADDRESS):
block_data = await self.getblock(height)
for item in block_data["tx"]:
vin = item["vin"]
vout = item["vout"]
if vin[0].get("coinbase"):
for value in vout:
addresses = value["scriptPubKey"].get("addresses")
if addresses:
first = addresses.get("0") if isinstance(addresses, dict) else addresses[0]
if first == REPORT_ADDRESS:
return block_data
else:
return False
return False
def block(self, data):
hash_ = data["hash"]
tx = data["tx"]
height = data["height"]
time = data["time"]
reward = 0
for item in tx:
vin = item["vin"]
vout = item["vout"]
if vin[0].get("coinbase"):
for value in vout:
if value["scriptPubKey"].get("addresses"):
reward += value["value"]
return {
"height": height,
"hash": hash_,
"time": time,
"block_reward": reward,
"block_fees": None
}
# ================= DGB =================
class DGBRPCNode(BaseRPCNode):
async def verify_block(self, height, REPORT_ADDRESS):
block_data = await self.getblock(height)
for item in block_data["tx"]:
vin = item["vin"]
vout = item["vout"]
if vin[0].get("coinbase"):
for value in vout:
addresses = value["scriptPubKey"].get("addresses")
if addresses and addresses[0] == REPORT_ADDRESS:
return block_data
return False
async def verify_block_with_algo(self, height, REPORT_ADDRESS, algorithm):
block_data = await self.getblock(height)
if block_data.get("pow_algo") == algorithm:
for item in block_data["tx"]:
vin = item["vin"]
vout = item["vout"]
if vin[0].get("coinbase"):
for value in vout:
addresses = value["scriptPubKey"].get("addresses")
if addresses and addresses[0] == REPORT_ADDRESS:
return block_data
return False
def block(self, data):
hash_ = data["hash"]
tx = data["tx"]
height = data["height"]
time = data["time"]
reward = 0
for item in tx:
vin = item["vin"]
vout = item["vout"]
if vin[0].get("coinbase"):
for value in vout:
if value["scriptPubKey"].get("addresses"):
reward += value["value"]
return {
"height": height,
"hash": hash_,
"time": time,
"block_reward": reward,
"block_fees": None
}
# ================= RXD =================
class RXDRPCNode(BaseRPCNode):
async def verify_block(self, height, REPORT_ADDRESS):
block_data = await self.getblock(height)
for item in block_data["tx"]:
vin = item["vin"]
vout = item["vout"]
if vin[0].get("coinbase"):
for value in vout:
addresses = value["scriptPubKey"].get("addresses")
if addresses and addresses[0] == REPORT_ADDRESS:
hash_ = await self.getblockhash(height)
blockstats = await self.callRpcMethod("getblockstats", [hash_])
return {
"height": blockstats["height"],
"hash": blockstats["blockhash"],
"time": blockstats["time"],
"block_reward": blockstats["subsidy"] + blockstats["totalfee"],
"block_fees": blockstats["totalfee"]
}
return False
def block(self, data):
return data
__all__ = [
"NEXARPCNode",
"GRSRPCNode",
"MONARPCNode",
"DGBRPCNode",
"RXDRPCNode"
]

View File

@@ -0,0 +1,40 @@
import json
import redis.asyncio as redis_async
class Cache:
def __init__(self, options):
if isinstance(options, str):
self.redis = redis_async.from_url(options, decode_responses=True)
elif isinstance(options, dict):
host = options.get("host", "localhost")
port = options.get("port", 6379)
db = options.get("db", 0)
password = options.get("password")
if password:
url = f"redis://:{password}@{host}:{port}/{db}"
else:
url = f"redis://{host}:{port}/{db}"
self.redis = redis_async.from_url(url, decode_responses=True)
else:
raise TypeError("options must be dict or redis url string")
async def set(self, key, value):
try:
await self.redis.set(key, value)
except Exception as err:
raise err
async def get(self, key):
try:
data = await self.redis.get(key)
return json.loads(data)
except Exception as err:
raise err
__all__ = ["Cache"]