Created
November 3, 2021 16:27
-
-
Save thechiaplot/ba2c903fca9926348f05c98e95eaeab1 to your computer and use it in GitHub Desktop.
TxMojos.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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