first pass at updating cc_wallet

trades do not work
This commit is contained in:
Matthew Howard 2020-08-27 20:52:45 +01:00 committed by Gene Hoffman
parent 42e47749c0
commit 0882821c4c
6 changed files with 145 additions and 235 deletions

4
.gitignore vendored
View File

@ -77,6 +77,10 @@ vdf_bench
# Offer Files
*.offer
# Compiled CLVM
*.hex
main.sym
# Dev config react
electron-react/src/dev_config.js
# React built app

View File

@ -4,19 +4,11 @@ from typing import List, Optional, Tuple
from src.types.program import Program
from src.types.sized_bytes import bytes32
from src.util.streamable import streamable, Streamable
from src.wallet.cc_wallet import cc_wallet_puzzles
from src.wallet.cc_wallet.ccparent import CCParent
from src.types.program import Program
@dataclass(frozen=True)
@streamable
class CCInfo(Streamable):
parent_info: List[Tuple[bytes32, Optional[CCParent]]] # {coin.name(): CCParent}
my_colour_name: str
def puzzle_for_inner_puzzle(self, inner_puzzle: Program) -> Program:
genesis_id = bytes.fromhex(self.my_colour_name) if self.my_colour_name else 0
return cc_wallet_puzzles.puzzle_for_inner_puzzle(inner_puzzle, genesis_id)
def genesis_id(self):
return self.my_colour_name
my_genesis_checker: Optional[Program] # this is the program
lineage_proofs: List[Tuple[bytes32, Optional[Program]]] # {coin.name(): lineage_proof}

View File

@ -2,7 +2,7 @@ import dataclasses
from typing import Any, List, Optional, Tuple
from blspy import G2Element
from blspy import G2Element, AugSchemeMPL
from clvm_tools.curry import curry as ct_curry, uncurry
@ -24,6 +24,9 @@ CC_MOD = load_clvm("cc.clvm", package_or_requirement=__name__)
ZERO_GENESIS_MOD = load_clvm("zero-genesis.clvm", package_or_requirement=__name__)
NULL_SIGNATURE = G2Element.generator() * 0
ANYONE_CAN_SPEND_PUZZLE = Program.to(1) # simply return the conditions
# information needed to spend a cc
# if we ever support more genesis conditions, like a re-issuable coin,
@ -164,6 +167,7 @@ def spend_bundle_for_spendable_ccs(
genesis_coin_checker: Program,
spendable_cc_list: List[SpendableCC],
inner_solutions: List[Program],
sigs: Optional[List[G2Element]] = []
) -> SpendBundle:
"""
Given a list of `SpendableCC` objects and inner solutions for those objects, create a `SpendBundle`
@ -239,8 +243,10 @@ def spend_bundle_for_spendable_ccs(
parent_coin, next_coin, output_amount, subtotal
)
coin_solutions.append(coin_solution)
return SpendBundle(coin_solutions, NULL_SIGNATURE)
if sigs is None or sigs == []:
return SpendBundle(coin_solutions, NULL_SIGNATURE)
else:
return SpendBundle(coin_solutions, AugSchemeMPL.aggregate(sigs))
def is_cc_mod(inner_f: Program):
@ -266,6 +272,19 @@ def uncurry_cc(puzzle: Program) -> Optional[Tuple[Program, Program, Program]]:
return mod_hash, genesis_coin_checker, inner_puzzle
def get_lineage_proof_from_coin_and_puz(parent_coin, parent_puzzle):
r = uncurry_cc(parent_puzzle)
if r:
mod_hash, genesis_checker, inner_puzzle = r
lineage_proof = lineage_proof_for_cc_parent(parent_coin, inner_puzzle.get_tree_hash())
else:
if parent_coin.amount == 0:
lineage_proof = lineage_proof_for_zero(parent_coin)
else:
lineage_proof = lineage_proof_for_genesis(parent_coin)
return lineage_proof
def spendable_cc_list_from_coin_solution(
coin_solution: CoinSolution, hash_to_puzzle_f
) -> List[SpendableCC]:

View File

@ -19,13 +19,6 @@ from src.util.json_util import dict_to_json_str
from src.util.ints import uint64, uint32
from src.wallet.block_record import BlockRecord
from src.wallet.cc_wallet.cc_info import CCInfo
from src.wallet.cc_wallet.cc_wallet_puzzles import (
get_inner_puzzle_hash_from_puzzle,
cc_generate_eve_spend,
create_spend_for_auditor,
create_spend_for_ephemeral,
)
from src.wallet.cc_wallet.ccparent import CCParent
from src.wallet.transaction_record import TransactionRecord
from src.wallet.util.wallet_types import WalletType
from src.wallet.wallet import Wallet
@ -33,13 +26,24 @@ from src.wallet.wallet_coin_record import WalletCoinRecord
from src.wallet.wallet_info import WalletInfo
from src.wallet.derivation_record import DerivationRecord
from src.wallet.cc_wallet import cc_wallet_puzzles
from src.wallet.cc_wallet.cc_utils import (
SpendableCC,
cc_puzzle_for_inner_puzzle,
create_genesis_or_zero_coin_checker,
cc_puzzle_hash_for_inner_puzzle_hash,
cc_puzzle_for_inner_puzzle,
spend_bundle_for_spendable_ccs,
genesis_coin_id_for_genesis_coin_checker,
coin_as_list,
lineage_proof_for_genesis,
get_lineage_proof_from_coin_and_puz,
uncurry_cc,
CC_MOD,
ZERO_GENESIS_MOD,
ANYONE_CAN_SPEND_PUZZLE
)
from dataclasses import replace
# TODO: write tests based on wallet tests
# TODO: {Matt} compatibility based on deriving innerpuzzle from derivation record
# TODO: {Matt} convert this into wallet_state_manager.puzzle_store
# TODO: {Matt} add hooks in WebSocketServer for all UI functions
class CCWallet:
wallet_state_manager: Any
@ -69,7 +73,7 @@ class CCWallet:
self.wallet_state_manager = wallet_state_manager
self.cc_info = CCInfo([], "")
self.cc_info = CCInfo(None, [])
info_as_string = bytes(self.cc_info).hex()
self.wallet_info = await wallet_state_manager.user_store.create_wallet(
"CC Wallet", WalletType.COLOURED_COIN.value, info_as_string
@ -138,7 +142,7 @@ class CCWallet:
@staticmethod
async def create_wallet_for_cc(
wallet_state_manager: Any, wallet: Wallet, colour: str, name: str = None
wallet_state_manager: Any, wallet: Wallet, genesis_checker_hex: str, name: str = None
):
self = CCWallet()
@ -152,7 +156,7 @@ class CCWallet:
self.wallet_state_manager = wallet_state_manager
self.cc_info = CCInfo([], colour)
self.cc_info = CCInfo(Program.from_bytes(bytes.fromhex(genesis_checker_hex)), [])
info_as_string = bytes(self.cc_info).hex()
self.wallet_info = await wallet_state_manager.user_store.create_wallet(
"CC Wallet", WalletType.COLOURED_COIN.value, info_as_string
@ -194,8 +198,8 @@ class CCWallet:
amount: uint64 = uint64(0)
for record in record_list:
parent = await self.get_parent_for_coin(record.coin)
if parent is not None:
lineage = await self.get_lineage_proof_for_coin(record.coin)
if lineage is not None:
amount = uint64(amount + record.coin.amount)
self.log.info(
@ -235,7 +239,7 @@ class CCWallet:
await self.wallet_state_manager.user_store.update_wallet(self.wallet_info)
def get_colour(self):
return self.cc_info.my_colour_name
return bytes(self.cc_info.my_genesis_checker).hex()
async def coin_added(
self, coin: Coin, height: int, header_hash: bytes32, removals: List[Coin]
@ -245,14 +249,20 @@ class CCWallet:
search_for_parent: bool = True
inner_puzzle = await self.inner_puzzle_for_cc_puzzle(coin.puzzle_hash)
future_parent = CCParent(
coin.parent_coin_info, inner_puzzle.get_tree_hash(), coin.amount
inner_puzzle = await self.inner_puzzle_for_cc_puzhash(coin.puzzle_hash)
lineage_proof = Program.to(
(
1,
[
coin.parent_coin_info,
inner_puzzle.get_tree_hash(),
coin.amount,
],
)
)
await self.add_lineage(coin.name(), lineage_proof)
await self.add_parent(coin.name(), future_parent)
for name, ccparent in self.cc_info.parent_info:
for name, lineage_proofs in self.cc_info.lineage_proofs:
if coin.parent_coin_info == name:
search_for_parent = False
break
@ -339,20 +349,15 @@ class CCWallet:
break
if coin is not None:
if cc_wallet_puzzles.check_is_cc_puzzle(puzzle_program):
inner_puzzle_hash = get_inner_puzzle_hash_from_puzzle(
puzzle_program
)
mod_hash, genesis_coin_checker, inner_puzzle = uncurry_cc(puzzle_program)
if inner_puzzle is not None:
self.log.info(
f"parent: {coin_name} inner_puzzle for parent is {inner_puzzle_hash.hex()}"
f"parent: {coin_name} inner_puzzle for parent is {inner_puzzle}"
)
await self.add_parent(
lineage_proof = get_lineage_proof_from_coin_and_puz(coin, puzzle_program)
await self.add_lineage(
coin_name,
CCParent(
coin.parent_coin_info, inner_puzzle_hash, coin.amount
),
lineage_proof
)
return True
@ -382,27 +387,15 @@ class CCWallet:
async def get_new_puzzlehash(self) -> bytes32:
return await self.standard_wallet.get_new_puzzlehash()
def do_replace(self, sexp, magic, magic_replacement):
""" Generic way to replace anything inside a SEXP, not used currentyl """
if sexp.listp():
return self.do_replace(sexp.first(), magic, magic_replacement).cons(
self.do_replace(sexp.rest(), magic, magic_replacement)
)
if sexp.as_atom() == magic:
return sexp.to(magic_replacement)
return sexp
def specific_replace(self, old, magic, magic_replacement):
"""binutil.assemble is slow, using this hack to swap inner_puzzle_hash. """
new = old.replace(magic, magic_replacement)
return new
def puzzle_for_pk(self, pubkey) -> Program:
inner_puzzle = self.standard_wallet.puzzle_for_pk(bytes(pubkey))
inner_puzzle_hash = inner_puzzle.get_tree_hash()
cc_puzzle: Program = self.cc_info.puzzle_for_inner_puzzle(inner_puzzle)
inner_puzzle = self.standard_wallet.puzzle_for_pk(
bytes(pubkey)
)
cc_puzzle: Program = cc_puzzle_for_inner_puzzle(
CC_MOD, self.cc_info.my_genesis_checker, inner_puzzle
)
self.base_puzzle_program = bytes(cc_puzzle)
self.base_inner_puzzle_hash = inner_puzzle_hash
self.base_inner_puzzle_hash = inner_puzzle.get_tree_hash()
return cc_puzzle
async def get_new_cc_puzzle_hash(self):
@ -416,6 +409,8 @@ class CCWallet:
async def generate_zero_val_coin(
self, send=True, exclude: List[Coin] = None
) -> Optional[SpendBundle]:
if self.cc_info.my_genesis_checker is None:
return None
if exclude is None:
exclude = []
coins = await self.standard_wallet.select_coins(0, exclude)
@ -425,33 +420,31 @@ class CCWallet:
origin = coins.copy().pop()
origin_id = origin.name()
cc_inner_puzzle = await self.get_new_inner_puzzle()
cc_inner_puzzle_hash = cc_inner_puzzle.get_tree_hash()
cc_puzzle = self.cc_info.puzzle_for_inner_puzzle(cc_inner_puzzle)
cc_puzzle_hash = cc_puzzle.get_tree_hash()
cc_inner = await self.get_new_inner_hash()
cc_puzzle_hash: Program = cc_puzzle_hash_for_inner_puzzle_hash(
CC_MOD, self.cc_info.my_genesis_checker, cc_inner
)
tx = await self.standard_wallet.generate_signed_transaction(
uint64(0), cc_puzzle_hash, uint64(0), origin_id, coins
)
full_spend = tx.spend_bundle
self.log.info(f"Generate zero val coin: cc_puzzle_hash is {cc_puzzle_hash}")
# generate eve coin so we can add future lineage_proofs even if we don't eve spend
eve_coin = Coin(origin_id, cc_puzzle_hash, uint64(0))
if tx is None or tx.spend_bundle is None:
return None
genesis_id = bytes.fromhex(self.cc_info.my_colour_name)
eve_spend = cc_generate_eve_spend(eve_coin, cc_inner_puzzle, genesis_id)
full_spend = SpendBundle.aggregate([tx.spend_bundle, eve_spend])
future_parent = CCParent(
eve_coin.parent_coin_info, cc_inner_puzzle_hash, eve_coin.amount
)
eve_parent = CCParent(
origin.parent_coin_info, origin.puzzle_hash, origin.amount
)
await self.add_parent(eve_coin.name(), future_parent)
await self.add_parent(eve_coin.parent_coin_info, eve_parent)
await self.add_lineage(eve_coin.name(), Program.to(
(
1,
[
eve_coin.parent_coin_info,
cc_inner,
eve_coin.amount
],
)
))
await self.add_lineage(eve_coin.parent_coin_info, Program.to((0, [coin_as_list(origin), 1])))
if send:
regular_record = TransactionRecord(
@ -540,8 +533,8 @@ class CCWallet:
)
for record in record_list:
parent = await self.get_parent_for_coin(record.coin)
if parent is not None:
lineage = await self.get_lineage_proof_for_coin(record.coin)
if lineage is not None:
result.append(record)
return result
@ -607,20 +600,18 @@ class CCWallet:
sigs.append(signature)
return sigs
async def inner_puzzle_for_cc_puzzle(self, cc_hash: bytes32) -> Program:
async def inner_puzzle_for_cc_puzhash(self, cc_hash: bytes32) -> Program:
record: DerivationRecord = await self.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(
cc_hash.hex()
)
inner_puzzle: Program = self.standard_wallet.puzzle_for_pk(bytes(record.pubkey))
return inner_puzzle
async def get_parent_for_coin(self, coin) -> Optional[CCParent]:
parent_info = None
for name, ccparent in self.cc_info.parent_info:
async def get_lineage_proof_for_coin(self, coin) -> Optional[Program]:
for name, proof in self.cc_info.lineage_proofs:
if name == coin.parent_coin_info:
parent_info = ccparent
return parent_info
return proof
return None
async def generate_signed_transaction(
self,
@ -643,122 +634,31 @@ class CCWallet:
total_amount = sum([x.amount for x in selected_coins])
change = total_amount - amount
# first coin becomes the auditor special case
auditor = selected_coins.pop()
puzzle_hash = auditor.puzzle_hash
inner_puzzle: Program = await self.inner_puzzle_for_cc_puzzle(puzzle_hash)
auditor_info = (
auditor.parent_coin_info,
inner_puzzle.get_tree_hash(),
auditor.amount,
)
list_of_solutions = []
# auditees should be (primary_input, innerpuzhash, coin_amount, output_amount)
auditees = [
(
auditor.parent_coin_info,
inner_puzzle.get_tree_hash(),
auditor.amount,
total_amount,
)
]
for coin in selected_coins:
coin_inner_puzzle: Program = await self.inner_puzzle_for_cc_puzzle(
coin.puzzle_hash
)
auditees.append(
(
coin.parent_coin_info,
coin_inner_puzzle[coin],
coin.amount,
0,
)
)
primaries = [{"puzzlehash": to_address, "amount": amount}]
if change > 0:
changepuzzlehash = await self.get_new_inner_hash()
primaries.append({"puzzlehash": changepuzzlehash, "amount": change})
innersol = self.standard_wallet.make_solution(primaries=primaries)
coin = selected_coins.pop()
inner_puzzle = await self.inner_puzzle_for_cc_puzhash(coin.puzzle_hash)
genesis_id = genesis_coin_id_for_genesis_coin_checker(self.cc_info.my_genesis_checker)
innersol_list = [innersol]
sigs = sigs + await self.get_sigs(inner_puzzle, innersol)
parent_info = await self.get_parent_for_coin(auditor)
assert parent_info is not None
lineage_proof = await self.get_lineage_proof_for_coin(coin)
assert lineage_proof is not None
spendable_cc_list = [SpendableCC(coin, genesis_id, inner_puzzle, lineage_proof)]
assert self.cc_info.my_genesis_checker is not None
solution = cc_wallet_puzzles.cc_make_solution(
self.cc_info.my_colour_name,
(
parent_info.parent_name,
parent_info.inner_puzzle_hash,
parent_info.amount,
),
auditor.amount,
inner_puzzle,
innersol,
auditor_info,
auditees,
False,
)
puzzle = self.cc_info.puzzle_for_inner_puzzle(inner_puzzle)
main_coin_solution = CoinSolution(
auditor,
Program.to([puzzle, solution]),
)
list_of_solutions.append(main_coin_solution)
# main = SpendBundle([main_coin_solution], ZERO96)
ephemeral_coin_solution = create_spend_for_ephemeral(
auditor, auditor, total_amount
)
list_of_solutions.append(ephemeral_coin_solution)
# eph = SpendBundle([ephemeral_coin_solution], ZERO96)
auditor_coin_colution = create_spend_for_auditor(auditor, auditor)
list_of_solutions.append(auditor_coin_colution)
# aud = SpendBundle([auditor_coin_colution], ZERO96)
# loop through remaining spends, treating them as aggregatees
for coin in selected_coins:
coin_inner_puzzle = await self.inner_puzzle_for_cc_puzzle(coin.puzzle_hash)
coin_inner_puzzle = await self.inner_puzzle_for_cc_puzhash(coin.puzzle_hash)
innersol = self.standard_wallet.make_solution()
innersol_list.append(innersol)
parent_info = await self.get_parent_for_coin(coin)
assert parent_info is not None
sigs = sigs + await self.get_sigs(coin_inner_puzzle, innersol)
solution = cc_wallet_puzzles.cc_make_solution(
self.cc_info.my_colour_name,
(
parent_info.parent_name,
parent_info.inner_puzzle_hash,
parent_info.amount,
),
coin.amount,
coin_inner_puzzle,
innersol,
auditor_info,
None,
)
list_of_solutions.append(
CoinSolution(
coin,
Program.to(
[
self.cc_info.puzzle_for_inner_puzzle(coin_inner_puzzle),
solution,
]
),
)
)
list_of_solutions.append(create_spend_for_ephemeral(coin, auditor, 0))
list_of_solutions.append(create_spend_for_auditor(auditor, coin))
aggsig = AugSchemeMPL.aggregate(sigs)
spend_bundle = SpendBundle(list_of_solutions, aggsig)
spend_bundle = spend_bundle_for_spendable_ccs(CC_MOD, self.cc_info.my_genesis_checker, spendable_cc_list, innersol_list, sigs)
tx_record = TransactionRecord(
confirmed_at_index=uint32(0),
created_at_time=uint64(int(time.time())),
@ -778,11 +678,13 @@ class CCWallet:
return tx_record
async def add_parent(self, name: bytes32, parent: Optional[CCParent]):
self.log.info(f"Adding parent {name}: {parent}")
current_list = self.cc_info.parent_info.copy()
current_list.append((name, parent))
cc_info: CCInfo = CCInfo(current_list, self.cc_info.my_colour_name)
async def add_lineage(self, name: bytes32, lineage: Optional[Program]):
self.log.info(f"Adding parent {name}: {lineage}")
current_list = self.cc_info.lineage_proofs.copy()
current_list.append((name, lineage))
cc_info: CCInfo = CCInfo(
self.cc_info.my_genesis_checker, current_list
)
await self.save_info(cc_info)
async def save_info(self, cc_info: CCInfo):
@ -803,32 +705,27 @@ class CCWallet:
origin = coins.copy().pop()
origin_id = origin.name()
# self.add_parent(origin_id, origin_id)
cc_info: CCInfo = CCInfo([], origin_id.hex())
await self.save_info(cc_info)
cc_inner_hash = await self.get_new_inner_hash()
await self.add_lineage(origin_id, Program.to((0, [coin_as_list(origin), 0])))
genesis_coin_checker = create_genesis_or_zero_coin_checker(origin_id)
cc_inner_puzzle = await self.get_new_inner_puzzle()
cc_puzzle = cc_wallet_puzzles.puzzle_for_inner_puzzle(
cc_inner_puzzle, origin_id
minted_cc_puzzle_hash = cc_puzzle_hash_for_inner_puzzle_hash(
CC_MOD, genesis_coin_checker, cc_inner_hash
)
cc_puzzle_hash = cc_puzzle.get_tree_hash()
tx_record: Optional[
TransactionRecord
] = await self.standard_wallet.generate_signed_transaction(
amount, cc_puzzle_hash, uint64(0), origin_id, coins
amount, minted_cc_puzzle_hash, uint64(0), origin_id, coins
)
self.log.info(f"cc_puzzle_hash is {cc_puzzle_hash}")
eve_coin = Coin(origin_id, cc_puzzle_hash, amount)
if tx_record is None or tx_record.spend_bundle is None:
return None
eve_spend = cc_generate_eve_spend(eve_coin, cc_inner_puzzle, origin_id)
full_spend = SpendBundle.aggregate([tx_record.spend_bundle, eve_spend])
return full_spend
lineage_proofs = [(origin_id, lineage_proof_for_genesis(origin))]
cc_info: CCInfo = CCInfo(genesis_coin_checker, lineage_proofs)
await self.save_info(cc_info)
return tx_record.spend_bundle
"""
async def create_spend_bundle_relative_amount(
self, cc_amount, zero_coin: Coin = None
):
@ -862,7 +759,7 @@ class CCWallet:
output_created = coin
else:
innersol = self.standard_wallet.make_solution()
innerpuz: Program = await self.inner_puzzle_for_cc_puzzle(coin.puzzle_hash)
innerpuz: Program = await self.inner_puzzle_for_cc_puzhash(coin.puzzle_hash)
parent_info = await self.get_parent_for_coin(coin)
assert parent_info is not None
@ -894,8 +791,4 @@ class CCWallet:
spend_bundle = SpendBundle(list_of_solutions, aggsig)
return spend_bundle
# Create an offer spend bundle for chia given an amount of relative change (i.e -400 or 1000)
# This is to be aggregated together with a coloured coin offer to ensure that the trade happens
async def create_spend_bundle_relative_chia(self, chia_amount: uint64):
self.log.error("Not implemented")
# TODO MATT: Implement
"""

View File

@ -1450,7 +1450,7 @@ class WalletStateManager:
for wallet_id in self.wallets:
wallet = self.wallets[wallet_id]
if wallet.wallet_info.type == WalletType.COLOURED_COIN.value:
if wallet.cc_info.genesis_id() == colour:
if bytes(wallet.cc_info.my_genesis_checker).hex() == colour:
return wallet
return None

View File

@ -103,13 +103,14 @@ class TestCCWallet:
await time_out_assert(15, cc_wallet.get_confirmed_balance, 100)
await time_out_assert(15, cc_wallet.get_unconfirmed_balance, 100)
colour = cc_wallet.cc_info.genesis_id()
assert cc_wallet.cc_info.my_genesis_checker is not None
colour = cc_wallet.get_colour()
cc_wallet_2: CCWallet = await CCWallet.create_wallet_for_cc(
wallet_node_2.wallet_state_manager, wallet2, colour
)
assert cc_wallet.cc_info.genesis_id() == cc_wallet_2.cc_info.genesis_id()
assert cc_wallet.cc_info.my_genesis_checker == cc_wallet_2.cc_info.my_genesis_checker
cc_2_hash = await cc_wallet_2.get_new_inner_hash()
tx_record = await cc_wallet.generate_signed_transaction(uint64(60), cc_2_hash)
@ -206,13 +207,14 @@ class TestCCWallet:
await time_out_assert(15, cc_wallet.get_confirmed_balance, 100)
await time_out_assert(15, cc_wallet.get_unconfirmed_balance, 100)
colour = cc_wallet.cc_info.genesis_id()
assert cc_wallet.cc_info.my_genesis_checker is not None
colour = cc_wallet.get_colour()
cc_wallet_2: CCWallet = await CCWallet.create_wallet_for_cc(
wallet_node_2.wallet_state_manager, wallet2, colour
)
assert cc_wallet.cc_info.genesis_id() == cc_wallet_2.cc_info.genesis_id()
assert cc_wallet.cc_info.my_genesis_checker == cc_wallet_2.cc_info.my_genesis_checker
await cc_wallet_2.generate_zero_val_coin()
@ -264,14 +266,14 @@ class TestCCWallet:
await time_out_assert(15, cc_wallet.get_confirmed_balance, 100)
await time_out_assert(15, cc_wallet.get_unconfirmed_balance, 100)
assert cc_wallet.cc_info.genesis_id() is not None
colour = cc_wallet.cc_info.genesis_id()
assert cc_wallet.cc_info.my_genesis_checker is not None
colour = cc_wallet.get_colour()
cc_wallet_2: CCWallet = await CCWallet.create_wallet_for_cc(
wallet_node_2.wallet_state_manager, wallet2, colour
)
assert cc_wallet.cc_info.genesis_id() == cc_wallet_2.cc_info.genesis_id()
assert cc_wallet.cc_info.my_genesis_checker == cc_wallet_2.cc_info.my_genesis_checker
cc_2_hash = await cc_wallet_2.get_new_inner_hash()
tx_record = await cc_wallet.generate_signed_transaction(uint64(60), cc_2_hash)