def test_cp_receive(): remote = make_client_server() run = asyncio.get_event_loop().run_until_complete wallet_a = CPWallet() wallet_b = CPWallet() wallet_c = CPWallet() wallets = [wallet_a, wallet_b, wallet_c] pub_a = hexbytes(wallet_a.get_next_public_key()) pub_b = hexbytes(wallet_b.get_next_public_key()) wallet_b.pubkey_permission = pub_a wallet_b.unlock_time = 100 b_puzzle = wallet_b.cp_puzzle(pub_b, pub_a, 100) b_puzzlehash = ProgramHash(b_puzzle) commit_and_notify(remote, wallets, wallet_a) assert wallet_a.current_balance == 1000000000 assert wallet_b.current_balance == 0 assert wallet_c.current_balance == 0 spend_bundle = wallet_a.generate_signed_transaction(1000, b_puzzlehash) _ = run(remote.push_tx(tx=spend_bundle)) commit_and_notify(remote, wallets, Wallet()) assert wallet_a.current_balance == 999999000 assert wallet_b.cp_balance == 1000 assert wallet_c.current_balance == 0
async def create_custody(wallet, ledger_api): option = input("Press (c) for custodian, or press (a) for authorizer") if option == "c": print("Custodian selected") pubkey = hexbytes(wallet.get_next_public_key().serialize()) print(f"Custodian pubkey: {pubkey}") pubkey_auth = input("Enter Authorizer's pubkey: ") wallet.pubkey_permission = pubkey_auth unlock_time = get_int( "Specify unlock time (milliseconds since 1970): ") wallet.unlock_time = unlock_time return elif option == "a": pubkey = hexbytes(wallet.get_next_public_key().serialize()) wallet.pubkey_approval = pubkey print("Authorizer Selected") print(f"Authorizer pubkey is: {pubkey}") pubkey_custody = input("Enter Custodian's pubkey: ") unlock_time = get_int( "Enter the same lock time as in custody wallet: ") amount = get_int("Enter Chia amount to send to custody: ") wallet.unlock_time = unlock_time puzzle_hash = ProgramHash( wallet.cp_puzzle(pubkey_custody, pubkey, unlock_time)) spend_bundle = wallet.generate_signed_transaction(amount, puzzle_hash) _ = await ledger_api.push_tx(tx=spend_bundle) return else: print("Invalid option, returning...") return
def test_cp_with_permission(): remote = make_client_server() run = asyncio.get_event_loop().run_until_complete wallet_a = CPWallet() wallet_b = CPWallet() wallet_c = CPWallet() wallets = [wallet_a, wallet_b, wallet_c] pub_a = hexbytes(wallet_a.get_next_public_key()) pub_b = hexbytes(wallet_b.get_next_public_key()) unlock_time = 5 wallet_b.pubkey_permission = pub_a wallet_b.unlock_time = unlock_time wallet_a.pubkey_approval = pub_a b_puzzle = wallet_b.cp_puzzle(pub_b, pub_a, unlock_time) b_puzzlehash = ProgramHash(b_puzzle) commit_and_notify(remote, wallets, wallet_a) # Set time to 4, time needs to exceed 5, unless there is approval from authorizer _ = run(remote.skip_milliseconds(ms=uint64(4).to_bytes(4, 'big'))) assert wallet_a.current_balance == 1000000000 assert wallet_b.current_balance == 0 assert wallet_c.current_balance == 0 spend_bundle = wallet_a.generate_signed_transaction(1000, b_puzzlehash) _ = run(remote.push_tx(tx=spend_bundle)) commit_and_notify(remote, wallets, Wallet()) assert wallet_a.current_balance == 999999000 assert wallet_b.cp_balance == 1000 assert wallet_c.current_balance == 0 puzzlehash_c = wallet_c.get_new_puzzlehash() amount = 100 mode = 2 transaction = wallet_b.cp_generate_unsigned_transaction( puzzlehash_c, amount, mode) solution = transaction[0][1].solution approval_a = wallet_a.cp_approval_signature_for_transaction(solution).sig spend_bundle = wallet_b.cp_generate_signed_transaction_with_approval( puzzlehash_c, amount, approval_a) _ = run(remote.push_tx(tx=spend_bundle)) commit_and_notify(remote, wallets, Wallet()) assert wallet_a.current_balance == 999999000 assert wallet_b.cp_balance == 900 assert wallet_c.current_balance == 100
def rl_puzzle_for_pk(self, pubkey, rate_amount, interval_time, origin_id, clawback_pk): hex_pk = hexbytes(pubkey) #breakpoint() opcode_aggsig = hexlify(ConditionOpcode.AGG_SIG).decode('ascii') opcode_coin_block_age = hexlify( ConditionOpcode.ASSERT_BLOCK_AGE_EXCEEDS).decode('ascii') opcode_create = hexlify(ConditionOpcode.CREATE_COIN).decode('ascii') opcode_myid = hexlify( ConditionOpcode.ASSERT_MY_COIN_ID).decode('ascii') if (not origin_id): return None TEMPLATE_MY_PARENT_ID = "(sha256 (f (r (r (r (r (r (r (a)))))))) (f (r (a))) (uint64 (f (r (r (r (r (r (r (r (a)))))))))))" TEMPLATE_SINGLETON_RL = f"((c (i (i (= {TEMPLATE_MY_PARENT_ID} (f (a))) (q 1) (= (f (a)) (q 0x{origin_id}))) (q (c (q 1) (q ()))) (q (x (q \"Parent doesnt satisfy RL conditions\")))) (a)))" TEMPLATE_BLOCK_AGE = f"((c (i (i (= (* (f (r (r (r (r (r (a))))))) (q {rate_amount})) (* (f (r (r (r (r (a)))))) (q {interval_time}))) (q 1) (q (> (* (f (r (r (r (r (r (a))))))) (q {rate_amount})) (* (f (r (r (r (r (a))))))) (q {interval_time})))) (q (c (q 0x{opcode_coin_block_age}) (c (f (r (r (r (r (r (a))))))) (q ())))) (q (x (q \"wrong min block time\")))) (a) ))" TEMPLATE_MY_ID = f"(c (q 0x{opcode_myid}) (c (sha256 (f (a)) (f (r (a))) (uint64 (f (r (r (a)))))) (q ())))" CREATE_CHANGE = f"(c (q 0x{opcode_create}) (c (f (r (a))) (c (- (f (r (r (a)))) (f (r (r (r (r (a))))))) (q ()))))" CREATE_NEW_COIN = f"(c (q 0x{opcode_create}) (c (f (r (r (r (a))))) (c (f (r (r (r (r (a)))))) (q ()))))" RATE_LIMIT_PUZZLE = f"(c {TEMPLATE_SINGLETON_RL} (c {TEMPLATE_BLOCK_AGE} (c {CREATE_CHANGE} (c {TEMPLATE_MY_ID} (c {CREATE_NEW_COIN} (q ()))))))" TEMPLATE_MY_PARENT_ID_2 = "(sha256 (f (r (r (r (r (r (r (r (r (a)))))))))) (f (r (a))) (uint64 (f (r (r (r (r (r (r (r (a)))))))))))" TEMPLATE_SINGLETON_RL_2 = f"((c (i (i (= {TEMPLATE_MY_PARENT_ID_2} (f (r (r (r (r (r (a)))))))) (q 1) (= (f (r (r (r (r (r (a))))))) (q 0x{origin_id}))) (q (c (q 1) (q ()))) (q (x (q \"Parent doesnt satisfy RL conditions\")))) (a)))" CREATE_CONSOLIDATED = f"(c (q 0x{opcode_create}) (c (f (r (a))) (c (+ (f (r (r (r (r (a)))))) (f (r (r (r (r (r (r (a))))))))) (q ()))))" MODE_TWO_ME_STRING = f"(c (q 0x{opcode_myid}) (c (sha256 (f (r (r (r (r (r (a))))))) (f (r (a))) (uint64 (f (r (r (r (r (r (r (a)))))))))) (q ())))" CREATE_LOCK = f"(c (q 0x{opcode_create}) (c (sha256 (wrap (c (q 7) (c (c (q 5) (c (c (q 1) (c (sha256 (f (r (r (a)))) (f (r (r (r (a))))) (uint64 (f (r (r (r (r (a)))))))) (q ()))) (c (q (q ())) (q ())))) (q ()))))) (c (uint64 (q 0)) (q ()))))" MODE_TWO = f"(c {TEMPLATE_SINGLETON_RL_2} (c {MODE_TWO_ME_STRING} (c {CREATE_LOCK} (c {CREATE_CONSOLIDATED} (q ())))))" AGGSIG_ENTIRE_SOLUTION = f"(c (q 0x{opcode_aggsig}) (c (q 0x{hex_pk}) (c (sha256 (wrap (a))) (q ()))))" WHOLE_PUZZLE = f"(c {AGGSIG_ENTIRE_SOLUTION} ((c (i (= (f (a)) (q 1)) (q ((c (q {RATE_LIMIT_PUZZLE}) (r (a))))) (q {MODE_TWO})) (a))) (q ()))" CLAWBACK = f"(c (c (q 0x{opcode_aggsig}) (c (q 0x{clawback_pk}) (c (sha256 (wrap (a))) (q ())))) (r (a)))" WHOLE_PUZZLE_WITH_CLAWBACK = f"((c (i (= (f (a)) (q 3)) (q {CLAWBACK}) (q {WHOLE_PUZZLE})) (a)))" return Program(binutils.assemble(WHOLE_PUZZLE_WITH_CLAWBACK))
def get_keys_pk(self, clawback_pubkey): for child in reversed(range(self.next_address)): pubkey = self.extended_secret_key.public_child( child).get_public_key() if hexbytes(pubkey.serialize()) == clawback_pubkey: return pubkey, self.extended_secret_key.private_child( child).get_private_key()
def get_new_puzzle_with_params_and_root(self, recovery_pubkey, pubkey, stake_factor, duration): op_create = ConditionOpcode.CREATE_COIN[0] op_consumed = ConditionOpcode.ASSERT_COIN_CONSUMED[0] solution = args(0) solution_args = args(1) secure_switch = args(2) parent = args(3) puzzle_hash = args(4) value = args(5) new_value = args(6) evaluate_solution = eval(solution, solution_args) standard_conditions = make_list(aggsig_condition(pubkey), terminator=evaluate_solution) escrow_program = self.get_escrow_puzzle_with_params( recovery_pubkey, pubkey, duration) escrow_puzzlehash = f'0x' + str(hexbytes(ProgramHash(escrow_program))) f = Fraction(stake_factor) stake_factor_numerator = quote(f.numerator) stake_factor_denominator = quote(f.denominator) create_condition = make_if( equal(multiply(new_value, stake_factor_denominator), multiply(value, stake_factor_numerator)), make_list(quote(op_create), quote(escrow_puzzlehash), new_value), fail()) coin_id = sha256(parent, puzzle_hash, uint64(value)) consumed_condition = make_list(quote(op_consumed), coin_id) escrow_conditions = make_list(create_condition, consumed_condition) puzzle = make_if(is_zero(secure_switch), standard_conditions, escrow_conditions) program = Program(binutils.assemble(puzzle)) return program
def generate_recovery_transaction(self, coins, root_public_key, secret_key, escrow_duration): recovery_pubkey = root_public_key.public_child( 0).get_public_key().serialize() signatures = [] coin_solutions = [] secret_key = BLSPrivateKey(secret_key) for coin in coins: pubkey = self.find_pubkey_for_escrow_puzzle( coin, root_public_key, escrow_duration) puzzle = self.get_escrow_puzzle_with_params( recovery_pubkey, pubkey.serialize(), escrow_duration) op_create_coin = ConditionOpcode.CREATE_COIN[0] puzzlehash = f'0x' + str(hexbytes(self.get_new_puzzlehash())) solution_src = sexp( quote(sexp(sexp(op_create_coin, puzzlehash, coin.amount))), sexp(), 1) solution = Program(binutils.assemble(solution_src)) puzzle_solution_list = clvm.to_sexp_f([puzzle, solution]) coin_solution = CoinSolution(coin, puzzle_solution_list) coin_solutions.append(coin_solution) conditions_dict = conditions_by_opcode( conditions_for_solution(puzzle_solution_list)) for _ in hash_key_pairs_for_conditions_dict(conditions_dict): signature = secret_key.sign(_.message_hash) signatures.append(signature) coin_solution_list = CoinSolutionList(coin_solutions) aggsig = BLSSignature.aggregate(signatures) spend_bundle = SpendBundle(coin_solution_list, aggsig) return spend_bundle
def get_backup_string(self): d = dict() d['root_public_key'] = self.get_recovery_hd_root_public_key( ).serialize() d['secret_key'] = self.get_recovery_private_key().serialize() d['escrow_duration'] = self.get_escrow_duration() d['stake_factor'] = self.get_stake_factor().as_tuple() return str(hexbytes(cbor.dumps(d)))
def test_rl_spend_all(): remote = make_client_server() run = asyncio.get_event_loop().run_until_complete # A gives B some money, but B can only send that money to C (and generate change for itself) wallet_a = RLWallet() wallet_b = RLWallet() wallet_c = RLWallet() wallets = [wallet_a, wallet_b, wallet_c] limit = 100 interval = 1 commit_and_notify(remote, wallets, wallet_a) origin_coin = wallet_a.my_utxos.copy().pop() wallet_b_pk = wallet_b.get_next_public_key().serialize() wallet_b.set_origin(origin_coin) wallet_b.limit = limit wallet_b.interval = interval clawback_pk = wallet_a.get_next_public_key().serialize() clawback_pk = hexbytes(clawback_pk) wallet_b.rl_clawback_pk = clawback_pk rl_puzzle = wallet_b.rl_puzzle_for_pk(wallet_b_pk, limit, interval, origin_coin.name(), clawback_pk) rl_puzzlehash = ProgramHash(rl_puzzle) wallet_a.clawback_puzzlehash = rl_puzzlehash wallet_a.rl_receiver_pk = wallet_b_pk wallet_a.clawback_pk = clawback_pk wallet_a.clawback_interval = interval wallet_a.clawback_limit = limit wallet_a.clawback_origin = origin_coin.name() # wallet A is normal wallet, it sends coin that's rate limited to wallet B amount = 300 spend_bundle = wallet_a.generate_signed_transaction_with_origin( amount, rl_puzzlehash, origin_coin.name()) _ = run(remote.push_tx(tx=spend_bundle)) commit_and_notify(remote, wallets, Wallet()) assert wallet_a.current_balance == 999999700 assert wallet_b.current_rl_balance == 300 assert wallet_c.current_balance == 0 commit_and_notify(remote, wallets, Wallet()) assert wallet_b.rl_available_balance() == 100 commit_and_notify(remote, wallets, Wallet()) assert wallet_b.rl_available_balance() == 200 commit_and_notify(remote, wallets, Wallet()) assert wallet_b.rl_available_balance() == 300 amount = 300 spend_bundle = wallet_b.rl_generate_signed_transaction( amount, wallet_c.get_new_puzzlehash()) _ = run(remote.push_tx(tx=spend_bundle)) commit_and_notify(remote, wallets, Wallet()) assert wallet_a.current_balance == 999999700 assert wallet_b.current_rl_balance == 0 assert wallet_c.current_balance == 300
def can_generate_cp_puzzle_hash(self, hash): if self.pubkey_permission is None: return None return any( map( lambda child: hash == ProgramHash( self.cp_puzzle( hexbytes(self.extended_secret_key.public_child(child)), self.pubkey_permission, self.unlock_time)), reversed(range(self.next_address))))
def get_keys(self, hash): s = super().get_keys(hash) if s is not None: return s for child in reversed(range(self.next_address)): pubkey = self.extended_secret_key.public_child(child) if hash == ProgramHash( self.cp_puzzle(hexbytes(pubkey), self.pubkey_permission, self.unlock_time)): return pubkey, self.extended_secret_key.private_child(child)
def test_cp_send_solo(): remote = make_client_server() run = asyncio.get_event_loop().run_until_complete wallet_a = CPWallet() wallet_b = CPWallet() wallet_c = CPWallet() wallets = [wallet_a, wallet_b, wallet_c] pub_a = hexbytes(wallet_a.get_next_public_key()) pub_b = hexbytes(wallet_b.get_next_public_key()) wallet_b.pubkey_permission = pub_a wallet_b.unlock_time = 3 b_puzzle = wallet_b.cp_puzzle(pub_b, pub_a, 3) b_puzzlehash = ProgramHash(b_puzzle) # Set ledger api to _ = run(remote.skip_milliseconds(ms=uint64(4).to_bytes(4, 'big'))) commit_and_notify(remote, wallets, wallet_a) assert wallet_a.current_balance == 1000000000 assert wallet_b.current_balance == 0 assert wallet_c.current_balance == 0 spend_bundle = wallet_a.generate_signed_transaction(1000, b_puzzlehash) _ = run(remote.push_tx(tx=spend_bundle)) commit_and_notify(remote, wallets, Wallet()) assert wallet_a.current_balance == 999999000 assert wallet_b.cp_balance == 1000 assert wallet_c.current_balance == 0 puzzlehash_c = wallet_c.get_new_puzzlehash() spend_bundle = wallet_b.cp_generate_signed_transaction(puzzlehash_c, 100) _ = run(remote.push_tx(tx=spend_bundle)) commit_and_notify(remote, wallets, Wallet()) assert wallet_a.current_balance == 999999000 assert wallet_b.cp_balance == 900 assert wallet_c.current_balance == 100
async def main_loop(): ledger_api = await connect_to_ledger_sim("localhost", 9868) selection = "" wallet = CPWallet() most_recent_header = None print_leaf() print() print("Welcome to your Chia Custody Wallet.") print() my_pubkey_orig = wallet.get_next_public_key().serialize() wallet.pubkey_approval = hexbytes(wallet.get_next_public_key().serialize()) wallet.pubkey_orig = my_pubkey_orig print("Your pubkey is: " + hexlify(my_pubkey_orig).decode('ascii')) while selection != "q": print() print(divider) print(" \u2447 Menu \u2447") print() tip = await ledger_api.get_tip() print("Block: ", tip["tip_index"]) print() print("Select a function:") print("\u2448 1 Wallet Details") print("\u2448 2 View Funds") print("\u2448 3 Get Update") print("\u2448 4 *GOD MODE* Farm Block / Get Money") print("\u2448 5 Create Custody") print("\u2448 6 Move Custody") print("\u2448 7 Approve Transaction") print("\u2448 q Quit") print(divider) print() selection = input(prompt) if selection == "1": await print_my_details(wallet, ledger_api) elif selection == "2": view_funds(wallet) elif selection == "3": most_recent_header = await update_ledger(wallet, ledger_api, most_recent_header) elif selection == "4": most_recent_header = await new_block(wallet, ledger_api) elif selection == "5": await create_custody(wallet, ledger_api) elif selection == "6": await move_custody(wallet, ledger_api) elif selection == "7": await approve_transaction(wallet)
def cp_generate_unsigned_transaction(self, new_puzzle_hash, amount, mode): outputs = [] output = new_puzzle_hash, amount outputs.append(output) change = self.cp_coin.amount - amount if change > 0: change_output = self.cp_coin.puzzle_hash, change outputs.append(change_output) spends = [] puzzle_hash = self.cp_coin.puzzle_hash pubkey, secretkey = self.get_keys(puzzle_hash) puzzle = self.cp_puzzle(hexbytes(pubkey), self.pubkey_permission, self.unlock_time) if mode == 1: solution = self.solution_for_cp_solo(outputs) else: solution = self.solution_for_cp_permission(outputs) spends.append((puzzle, CoinSolution(self.cp_coin, solution))) return spends
async def create_rl_coin(wallet, ledger_api): utxo_list = list(wallet.my_utxos) if len(utxo_list) == 0: print("No UTXOs available.") return print("Select UTXO for origin: ") num = 0 for utxo in utxo_list: print(f"{num}) coin_name:{utxo.name()} amount:{utxo.amount}") num += 1 selected = get_int("Select UTXO for origin: ") origin = utxo_list[selected] print("Rate limit is defined as amount of Chia per time interval.(Blocks)\n") rate = get_int("Specify the Chia amount limit: ") interval = get_int("Specify the interval length (blocks): ") print("Specify the pubkey of receiver") pubkey = input(prompt) my_pubkey = hexbytes(wallet.get_next_public_key().serialize()) send_amount = get_int("Enter amount to give recipient: ") print(f"\n\nInitialization string: {origin.parent_coin_info}:{origin.puzzle_hash}:" f"{origin.amount}:{origin.name()}:{rate}:{interval}:{my_pubkey}") print("\nPaste Initialization string to the receiver") print("Press Enter to continue:") input(prompt) pubkey = PublicKey.from_bytes(bytes.fromhex(pubkey)).serialize() rl_puzzle = wallet.rl_puzzle_for_pk(pubkey, rate, interval, origin.name(), my_pubkey) rl_puzzlehash = ProgramHash(rl_puzzle) wallet.clawback_puzzlehash = rl_puzzlehash wallet.clawback_origin = origin.name() wallet.clawback_limit = rate wallet.clawback_interval = interval wallet.clawback_pk = my_pubkey wallet.rl_receiver_pk = pubkey spend_bundle = wallet.generate_signed_transaction_with_origin(send_amount, rl_puzzlehash, origin.name()) _ = await ledger_api.push_tx(tx=spend_bundle)
def get_keys_pk(self, approval_pubkey): for child in reversed(range(self.next_address)): pubkey = self.extended_secret_key.public_child(child) if hexbytes(pubkey) == approval_pubkey: return pubkey, self.extended_secret_key.private_child(child)
def puzzle_for_tree_hash(tree_hash): TEMPLATE = """ (q 0x%s) """ return Program(binutils.assemble(TEMPLATE % hexbytes(tree_hash)))
def to_hexbytes(s): if isinstance(s, bytes): return hexbytes(s) return s