Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
TxMojos.py
import asyncio
import os
import sys
import sqlite3
import pathlib
import random
from chia.consensus.default_constants import DEFAULT_CONSTANTS as constants
from blspy import AugSchemeMPL
from chia.types.coin_spend import CoinSpend
from chia.types.condition_opcodes import ConditionOpcode
from chia.types.spend_bundle import SpendBundle
from chia.util.condition_tools import conditions_by_opcode, conditions_for_solution
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program, SerializedProgram
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.util.hash import std_hash
from chia.util.keychain import Keychain
from chia.wallet.derive_keys import master_sk_to_wallet_sk
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
DEFAULT_HIDDEN_PUZZLE_HASH,
calculate_synthetic_secret_key,
puzzle_for_pk,
)
from chia.wallet.wallet import Wallet
from chia.wallet.wallet_info import WalletInfo
from chia.rpc.full_node_rpc_client import FullNodeRpcClient
from chia.util.ints import uint16
def create_coin_spend(w, private_key, spend_coin, spend_coin_ph_index, dest_ph, fee=0):
private_key = master_sk_to_wallet_sk(private_key, spend_coin_ph_index)
pubkey = private_key.get_g1()
if fee == 0:
primaries = [{
'puzzlehash': dest_ph,
'amount': 1,
}]
else:
primaries = []
message_list = [spend_coin.name()]
for i in primaries:
message_list.append(
Coin(spend_coin.name(), i['puzzlehash'], i['amount']).name()
)
message: bytes32 = std_hash(b"".join(message_list))
puzzle: Program = puzzle_for_pk(bytes(pubkey))
solution: Program = w.make_solution(
primaries=primaries,
coin_announcements={message},
fee=fee,
)
coin_spend = CoinSpend(
spend_coin,
SerializedProgram.from_bytes(bytes(puzzle)),
SerializedProgram.from_bytes(bytes(solution)),
)
err, con, cost = conditions_for_solution(
coin_spend.puzzle_reveal, coin_spend.solution, constants.MAX_BLOCK_COST_CLVM
)
if not con:
raise ValueError(err)
conditions_dict = conditions_by_opcode(con)
synthetic_secret_key = calculate_synthetic_secret_key(
private_key, DEFAULT_HIDDEN_PUZZLE_HASH
)
signatures = []
for cwa in conditions_dict.get(ConditionOpcode.AGG_SIG_UNSAFE, []):
msg = cwa.vars[1]
signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
signatures.append(signature)
for cwa in conditions_dict.get(ConditionOpcode.AGG_SIG_ME, []):
msg = cwa.vars[1] + bytes(coin_spend.coin.name()) + constants.AGG_SIG_ME_ADDITIONAL_DATA
signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
signatures.append(signature)
return coin_spend, signatures
async def new_coin_records_loop(node_rpc_client, coin_records, coin_records_event, phs):
start = 0
phs = list(phs.keys())
while True:
state = await node_rpc_client.get_blockchain_state()
end = state['peak'].height
new_records = [i for i in await node_rpc_client.get_coin_records_by_puzzle_hashes(
phs,
include_spent_coins=False,
start_height=start,
end_height=end,
) if int(i.coin.amount) == 1]
print(f'Found {len(new_records)} new')
coin_records += new_records
coin_records_event.set()
start = end
await asyncio.sleep(20)
async def main():
node_hostname = os.environ.get('NODE_HOSTNAME', 'node')
node_ssl_path = os.environ.get('NODE_SSL_PATH', '/data/node_ssl')
node_rpc_client = await FullNodeRpcClient.create(
node_hostname, uint16(int(os.environ.get('NODE_PORT', 8555))), pathlib.Path(''), {
'private_ssl_ca': {
'crt': f'{node_ssl_path}/ca/private_ca.crt',
'key': f'{node_ssl_path}/ca/private_ca.key',
},
'daemon_ssl': {
'private_crt': f'{node_ssl_path}/daemon/private_daemon.crt',
'private_key': f'{node_ssl_path}/daemon/private_daemon.key',
},
}
)
s = sqlite3.connect(os.environ['LOCAL_DB_PATH'])
cursor = s.cursor()
cursor.execute('select puzzle_hash, derivation_index from derivation_paths order by derivation_index LIMIT 1300')
phs = {bytes.fromhex(i[0]): i[1] for i in cursor.fetchall()[1:]}
cursor.close()
s.close()
coin_records = []
coin_records_event = asyncio.Event()
asyncio.create_task(new_coin_records_loop(node_rpc_client, coin_records, coin_records_event, phs))
fee = int(os.environ['FEE'])
keychain: Keychain = Keychain()
private_key = keychain.get_all_private_keys()[0][0]
# Hack to use Wallet implementation of make_solution
wi = WalletInfo(0, '', 0, '')
w = await Wallet.create(None, wi)
# Wait to get the first round of coin records
await coin_records_event.wait()
phs_list = list(phs.keys())
while True:
num_crs = len(coin_records)
if num_crs == 0:
print("No CRs, waiting")
await asyncio.sleep(10)
continue
num_per_tx = min(num_crs, random.randint(1, 100))
spends = []
signatures = []
coins_to_spend = coin_records[:num_per_tx]
for idx, coin_to_spend in enumerate(coins_to_spend):
coin_records.remove(coin_to_spend)
ph_dest = phs_list[num_per_tx + idx]
spend_coin, sigs = create_coin_spend(w, private_key, coin_to_spend.coin, phs[coin_to_spend.coin.puzzle_hash], ph_dest)
spends.append(spend_coin)
signatures += sigs
if fee > 0 and coin_records:
coin_to_spend = coin_records.pop()
ph_dest = phs_list[0]
spend_coin, sigs = create_coin_spend(w, private_key, coin_to_spend.coin, phs[coin_to_spend.coin.puzzle_hash], ph_dest, fee)
spends.append(spend_coin)
signatures += sigs
aggsig = AugSchemeMPL.aggregate(signatures)
sb = SpendBundle(spends, aggsig)
await node_rpc_client.push_tx(sb)
print(f'{len(coins_to_spend)}.', end='')
sys.stdout.flush()
node_rpc_client.close()
await node_rpc_client.await_closed()
if __name__ == '__main__':
asyncio.run(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment