Skip to content

Commit

Permalink
Singleton improvements (Chia-Network#2286)
Browse files Browse the repository at this point in the history
* Changed singleton to take puzzlehash as origin.
Updated DID wallet to use this

* fixed hex and sha256tree for singleton puzzle

* require more signature for DID spends

* fix singleton tests
black format did wallet

* fix broken test
fix linting
switch to inline functions

* remove unused defuns from singleton_top_layer
  • Loading branch information
matt-o-how authored Apr 27, 2021
1 parent 50164d1 commit c1846b0
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 85 deletions.
2 changes: 1 addition & 1 deletion chia/wallet/did_wallet/did_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@dataclass(frozen=True)
@streamable
class DIDInfo(Streamable):
my_did: Optional[bytes]
origin_coin: Optional[Coin] # puzzlehash of this coin is our DID
backup_ids: List[bytes]
num_of_backup_ids_needed: uint64
parent_info: List[Tuple[bytes32, Optional[CCParent]]] # {coin.name(): CCParent}
Expand Down
90 changes: 59 additions & 31 deletions chia/wallet/did_wallet/did_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ async def create_new_did_wallet(
if spend_bundle is None:
raise ValueError("failed to generate ID for wallet")
await self.wallet_state_manager.add_new_wallet(self, self.wallet_info.id)

assert self.did_info.origin_coin is not None
did_puzzle_hash = did_wallet_puzzles.create_fullpuz(
self.did_info.current_inner, self.did_info.my_did
self.did_info.current_inner, self.did_info.origin_coin.puzzle_hash
).get_tree_hash()

did_record = TransactionRecord(
Expand Down Expand Up @@ -289,7 +289,7 @@ async def coin_added(self, coin: Coin, header_hash: bytes32, removals: List[Coin
self.log.info("DID wallet has been notified that coin was added")
inner_puzzle = await self.inner_puzzle_for_did_puzzle(coin.puzzle_hash)
new_info = DIDInfo(
self.did_info.my_did,
self.did_info.origin_coin,
self.did_info.backup_ids,
self.did_info.num_of_backup_ids_needed,
self.did_info.parent_info,
Expand All @@ -310,9 +310,12 @@ async def coin_added(self, coin: Coin, header_hash: bytes32, removals: List[Coin

def create_backup(self, filename: str):
assert self.did_info.current_inner is not None
assert self.did_info.origin_coin is not None
try:
f = open(filename, "w")
output_str = f"{self.get_my_DID()}:"
output_str = f"{self.did_info.origin_coin.parent_coin_info}:"
output_str += f"{self.did_info.origin_coin.puzzle_hash}:"
output_str += f"{self.did_info.origin_coin.amount}:"
for did in self.did_info.backup_ids:
output_str = output_str + did.hex() + ","
output_str = output_str[:-1]
Expand All @@ -330,16 +333,16 @@ async def load_backup(self, filename: str):
f = open(filename, "r")
details = f.readline().split(":")
f.close()
genesis_id = bytes.fromhex(details[0])
origin = Coin(bytes.fromhex(details[0]), bytes.fromhex(details[1]), uint64(int(details[2])))
backup_ids = []
for d in details[1].split(","):
for d in details[3].split(","):
backup_ids.append(bytes.fromhex(d))
num_of_backup_ids_needed = uint64(int(details[3]))
num_of_backup_ids_needed = uint64(int(details[5]))
if num_of_backup_ids_needed > len(backup_ids):
raise Exception
innerpuz = Program.from_bytes(bytes.fromhex(details[2]))
innerpuz = Program.from_bytes(bytes.fromhex(details[4]))
did_info = DIDInfo(
genesis_id,
origin,
backup_ids,
num_of_backup_ids_needed,
self.did_info.parent_info,
Expand All @@ -350,7 +353,8 @@ async def load_backup(self, filename: str):
)
await self.save_info(did_info, False)
await self.wallet_state_manager.update_wallet_puzzle_hashes(self.wallet_info.id)
full_puz = did_wallet_puzzles.create_fullpuz(innerpuz, genesis_id)

full_puz = did_wallet_puzzles.create_fullpuz(innerpuz, origin.puzzle_hash)
full_puzzle_hash = full_puz.get_tree_hash()
(
sub_height,
Expand Down Expand Up @@ -395,7 +399,7 @@ async def load_backup(self, filename: str):
if coin.name() in all_parents:
continue
did_info = DIDInfo(
genesis_id,
origin,
backup_ids,
num_of_backup_ids_needed,
self.did_info.parent_info,
Expand All @@ -414,22 +418,26 @@ def puzzle_for_pk(self, pubkey: bytes) -> Program:
innerpuz = did_wallet_puzzles.create_innerpuz(
pubkey, self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed
)
did = self.did_info.my_did
return did_wallet_puzzles.create_fullpuz(innerpuz, did)
if self.did_info.origin_coin is not None:
return did_wallet_puzzles.create_fullpuz(innerpuz, self.did_info.origin_coin.puzzle_hash)
else:
return did_wallet_puzzles.create_fullpuz(innerpuz, 0x00)

async def get_new_puzzle(self) -> Program:
return self.puzzle_for_pk(
bytes((await self.wallet_state_manager.get_unused_derivation_record(self.wallet_info.id)).pubkey)
)

def get_my_DID(self) -> str:
core = self.did_info.my_did
assert self.did_info.origin_coin is not None
core = self.did_info.origin_coin.puzzle_hash
assert core is not None
return core.hex()

# This is used to cash out, or update the id_list
async def create_spend(self, puzhash: bytes32):
assert self.did_info.current_inner is not None
assert self.did_info.origin_coin is not None
coins = await self.select_coins(1)
assert coins is not None
coin = coins.pop()
Expand All @@ -440,13 +448,13 @@ async def create_spend(self, puzhash: bytes32):

full_puzzle: Program = did_wallet_puzzles.create_fullpuz(
innerpuz,
self.did_info.my_did,
self.did_info.origin_coin.puzzle_hash,
)
parent_info = await self.get_parent_for_coin(coin)
assert parent_info is not None

fullsol = Program.to(
[
[self.did_info.origin_coin.parent_coin_info, self.did_info.origin_coin.amount],
[
parent_info.parent_name,
parent_info.inner_puzzle_hash,
Expand All @@ -458,7 +466,11 @@ async def create_spend(self, puzhash: bytes32):
)
list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)]
# sign for AGG_SIG_ME
message = puzhash + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA
message = (
Program.to([coin.amount, puzhash]).get_tree_hash()
+ coin.name()
+ self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA
)
pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz)
index = await self.wallet_state_manager.puzzle_store.index_for_pubkey(pubkey)
private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index)
Expand Down Expand Up @@ -494,6 +506,7 @@ async def create_attestment(
self, recovering_coin_name: bytes32, newpuz: bytes32, pubkey: G1Element, filename=None
) -> SpendBundle:
assert self.did_info.current_inner is not None
assert self.did_info.origin_coin is not None
coins = await self.select_coins(1)
assert coins is not None and coins != set()
coin = coins.pop()
Expand All @@ -505,13 +518,14 @@ async def create_attestment(
innerpuz: Program = self.did_info.current_inner
full_puzzle: Program = did_wallet_puzzles.create_fullpuz(
innerpuz,
self.did_info.my_did,
self.did_info.origin_coin.puzzle_hash,
)
parent_info = await self.get_parent_for_coin(coin)
assert parent_info is not None

fullsol = Program.to(
[
[self.did_info.origin_coin.parent_coin_info, self.did_info.origin_coin.amount],
[
parent_info.parent_name,
parent_info.inner_puzzle_hash,
Expand All @@ -526,7 +540,8 @@ async def create_attestment(

message_spend_bundle = SpendBundle([message_spend], AugSchemeMPL.aggregate([]))
# sign for AGG_SIG_ME
message = innermessage + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA
to_sign = Program.to([coin.puzzle_hash, coin.amount, innermessage]).get_tree_hash()
message = to_sign + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA
pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz)
index = await self.wallet_state_manager.puzzle_store.index_for_pubkey(pubkey)
private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index)
Expand Down Expand Up @@ -622,6 +637,7 @@ async def recovery_spend(
pubkey: G1Element,
spend_bundle: SpendBundle,
) -> SpendBundle:
assert self.did_info.origin_coin is not None
# innerpuz solution is (mode amount new_puz identity my_puz parent_innerpuzhash_amounts_for_recovery_ids)
innersol = Program.to(
[
Expand All @@ -640,12 +656,13 @@ async def recovery_spend(
innerpuz = self.did_info.current_inner
full_puzzle: Program = did_wallet_puzzles.create_fullpuz(
innerpuz,
self.did_info.my_did,
self.did_info.origin_coin.puzzle_hash,
)
parent_info = await self.get_parent_for_coin(coin)
assert parent_info is not None
fullsol = Program.to(
[
[self.did_info.origin_coin.parent_coin_info, self.did_info.origin_coin.amount],
[
parent_info.parent_name,
parent_info.inner_puzzle_hash,
Expand Down Expand Up @@ -741,17 +758,16 @@ async def generate_new_decentralised_id(self, amount: uint64) -> Optional[SpendB
return None

origin = coins.copy().pop()
origin_id = origin.name()

did_inner: Program = await self.get_new_innerpuz()
did_inner_hash = did_inner.get_tree_hash()
did_puz = did_wallet_puzzles.create_fullpuz(did_inner, origin_id)
did_puz = did_wallet_puzzles.create_fullpuz(did_inner, origin.puzzle_hash)
did_puzzle_hash = did_puz.get_tree_hash()

tx_record: Optional[TransactionRecord] = await self.standard_wallet.generate_signed_transaction(
amount, did_puzzle_hash, uint64(0), origin_id, coins
amount, did_puzzle_hash, uint64(0), origin.name(), coins
)
eve_coin = Coin(origin_id, did_puzzle_hash, amount)
eve_coin = Coin(origin.name(), did_puzzle_hash, amount)
future_parent = CCParent(
eve_coin.parent_coin_info,
did_inner_hash,
Expand All @@ -770,7 +786,7 @@ async def generate_new_decentralised_id(self, amount: uint64) -> Optional[SpendB

# Only want to save this information if the transaction is valid
did_info: DIDInfo = DIDInfo(
origin_id,
origin,
self.did_info.backup_ids,
self.did_info.num_of_backup_ids_needed,
self.did_info.parent_info,
Expand All @@ -780,18 +796,30 @@ async def generate_new_decentralised_id(self, amount: uint64) -> Optional[SpendB
None,
)
await self.save_info(did_info, False)
eve_spend = await self.generate_eve_spend(eve_coin, did_puz, origin_id, did_inner)
eve_spend = await self.generate_eve_spend(eve_coin, did_puz, did_inner)
full_spend = SpendBundle.aggregate([tx_record.spend_bundle, eve_spend])
return full_spend

async def generate_eve_spend(self, coin: Coin, full_puzzle: Program, origin_id: bytes, innerpuz: Program):
async def generate_eve_spend(self, coin: Coin, full_puzzle: Program, innerpuz: Program):
assert self.did_info.origin_coin is not None
# innerpuz solution is (mode amount message my_id my_puzhash parent_innerpuzhash_amounts_for_recovery_ids)
innersol = Program.to([0, coin.amount, coin.puzzle_hash, coin.name(), coin.puzzle_hash, []])
# full solution is (parent_info my_amount innersolution)
fullsol = Program.to([coin.parent_coin_info, coin.amount, innersol])
fullsol = Program.to(
[
[self.did_info.origin_coin.parent_coin_info, self.did_info.origin_coin.amount],
coin.parent_coin_info,
coin.amount,
innersol,
]
)
list_of_solutions = [CoinSolution(coin, full_puzzle, fullsol)]
# sign for AGG_SIG_ME
message = coin.puzzle_hash + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA
message = (
Program.to([coin.amount, coin.puzzle_hash]).get_tree_hash()
+ coin.name()
+ self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA
)
pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz)
index = await self.wallet_state_manager.puzzle_store.index_for_pubkey(pubkey)
private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index)
Expand Down Expand Up @@ -820,7 +848,7 @@ async def add_parent(self, name: bytes32, parent: Optional[CCParent], in_transac
current_list = self.did_info.parent_info.copy()
current_list.append((name, parent))
did_info: DIDInfo = DIDInfo(
self.did_info.my_did,
self.did_info.origin_coin,
self.did_info.backup_ids,
self.did_info.num_of_backup_ids_needed,
current_list,
Expand All @@ -835,7 +863,7 @@ async def update_recovery_list(self, recover_list: List[bytes], num_of_backup_id
if num_of_backup_ids_needed > len(recover_list):
return False
did_info: DIDInfo = DIDInfo(
self.did_info.my_did,
self.did_info.origin_coin,
recover_list,
num_of_backup_ids_needed,
self.did_info.parent_info,
Expand Down
14 changes: 2 additions & 12 deletions chia/wallet/did_wallet/did_wallet_puzzles.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,9 @@ def create_innerpuz(pubkey: bytes, identities: List[bytes], num_of_backup_ids_ne
return DID_INNERPUZ_MOD.curry(DID_CORE_MOD.get_tree_hash(), pubkey, backup_ids_hash, num_of_backup_ids_needed)


def create_fullpuz(innerpuz, genesis_id) -> Program:
def create_fullpuz(innerpuz, genesis_puzhash) -> Program:
mod_hash = DID_CORE_MOD.get_tree_hash()
return DID_CORE_MOD.curry(mod_hash, genesis_id, innerpuz)


def fullpuz_hash_for_inner_puzzle_hash(mod_code, genesis_id, inner_puzzle_hash) -> bytes32:
"""
Given an inner puzzle hash, calculate a puzzle program hash for a specific cc.
"""
gid_hash = genesis_id.get_tree_hash()
return mod_code.curry(mod_code.get_tree_hash(), gid_hash, inner_puzzle_hash).get_tree_hash(
gid_hash, inner_puzzle_hash
)
return DID_CORE_MOD.curry(mod_hash, genesis_puzhash, innerpuz)


def get_pubkey_from_innerpuz(innerpuz: Program) -> G1Element:
Expand Down
15 changes: 7 additions & 8 deletions chia/wallet/puzzles/did_innerpuz.clvm
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,19 @@
(qq (q . (((unquote CREATE_COIN_ANNOUNCEMENT) (unquote recovering_coin)) ((unquote AGG_SIG_UNSAFE) (unquote pubkey) (unquote newpuz)))))
)

(defun create_consume_message (coin_id my_id new_innerpuz pubkey)
(defun-inline create_consume_message (coin_id my_id new_innerpuz pubkey)
(list ASSERT_COIN_ANNOUNCEMENT (sha256 (sha256 coin_id (sha256tree1 (make_message_puzzle my_id new_innerpuz pubkey))) my_id))
)

;; return the puzzle hash for a cc with the given `genesis-coin-checker-hash` & `inner-puzzle`
(defun create_fullpuzhash (mod_hash mod_hash_hash genesis_id inner_puzzle_hash)
(defun-inline create_fullpuzhash (mod_hash mod_hash_hash genesis_id inner_puzzle_hash)
(sha256tree_esc (curry mod_hash mod_hash_hash genesis_id inner_puzzle_hash)
mod_hash
mod_hash_hash
inner_puzzle_hash)
)

(defun create_coin_ID_for_recovery (mod_hash mod_hash_hash did parent innerpuzhash amount)
(defun-inline create_coin_ID_for_recovery (mod_hash mod_hash_hash did parent innerpuzhash amount)
(sha256 parent (create_fullpuzhash mod_hash mod_hash_hash did innerpuzhash) amount)
)

Expand Down Expand Up @@ -165,20 +165,19 @@
(if mode
(if (= mode 1)
; mode one - create message
; formerly (list (recreate_self my_puzhash amount) (attest_to_id_and_newpuz identity new_puz) (list AGG_SIG_ME MY_PUBKEY new_puz))
(list (recreate_self my_puzhash amount) (list CREATE_COIN message 0) (list AGG_SIG_ME MY_PUBKEY message))
(list (recreate_self my_puzhash amount) (list CREATE_COIN message 0) (list AGG_SIG_ME MY_PUBKEY (sha256tree1 (list my_puzhash amount message))))
; mode two - recovery
; check that recovery list is not empty
(if recovery_list_reveal
(if (= (sha256tree1 recovery_list_reveal) RECOVERY_DID_LIST_HASH)
(check_messages_from_identities MOD_HASH (sha256tree1 MOD_HASH) NUM_VERIFICATIONS_REQUIRED recovery_list_reveal my_id (list (create_new_coin amount message)) message parent_innerpuzhash_amounts_for_recovery_ids pubkey 0)
(x "recovery list reveal did not match hash")
(x)
)
(x "DID recovery disabled")
(x)
)
)
; mode zero - normal spend
(list (create_new_coin amount message) (list AGG_SIG_ME MY_PUBKEY message))
(list (create_new_coin amount message) (list AGG_SIG_ME MY_PUBKEY (sha256tree1 (list amount message))))
)

)
Loading

0 comments on commit c1846b0

Please sign in to comment.