More wallet simplifications. Placeholder for looking up

secret key by public key.
This commit is contained in:
Richard Kiss 2020-09-16 14:25:10 -07:00 committed by Gene Hoffman
parent 9f1fc6bdfc
commit 8505550be5
2 changed files with 56 additions and 39 deletions

View File

@ -1,7 +1,11 @@
import time import time
<<<<<<< HEAD
from typing import Dict, List, Tuple, Set, Any from typing import Dict, List, Tuple, Set, Any
=======
from typing import Dict, Optional, List, Set, Any
>>>>>>> More wallet simplifications. Placeholder for looking up
import logging import logging
from blspy import G2Element, AugSchemeMPL from blspy import G1Element, G2Element, AugSchemeMPL, PrivateKey
from src.types.coin import Coin from src.types.coin import Coin
from src.types.coin_solution import CoinSolution from src.types.coin_solution import CoinSolution
from src.types.program import Program from src.types.program import Program
@ -33,6 +37,7 @@ class Wallet(AbstractWallet):
wallet_state_manager: Any wallet_state_manager: Any
log: logging.Logger log: logging.Logger
wallet_info: WalletInfo wallet_info: WalletInfo
_pk2sk: Dict[G1Element, PrivateKey]
@staticmethod @staticmethod
async def create( async def create(
@ -47,6 +52,13 @@ class Wallet(AbstractWallet):
self.log = logging.getLogger(__name__) self.log = logging.getLogger(__name__)
self.wallet_state_manager = wallet_state_manager self.wallet_state_manager = wallet_state_manager
self.wallet_info = info self.wallet_info = info
<<<<<<< HEAD
=======
# HACK
self._pk2sk = {}
>>>>>>> More wallet simplifications. Placeholder for looking up
return self return self
async def get_confirmed_balance(self) -> uint64: async def get_confirmed_balance(self) -> uint64:
@ -213,7 +225,7 @@ class Wallet(AbstractWallet):
fee: uint64 = uint64(0), fee: uint64 = uint64(0),
origin_id: bytes32 = None, origin_id: bytes32 = None,
coins: Set[Coin] = None, coins: Set[Coin] = None,
) -> List[Tuple[Program, CoinSolution]]: ) -> List[CoinSolution]:
""" """
Generates a unsigned transaction in form of List(Puzzle, Solutions) Generates a unsigned transaction in form of List(Puzzle, Solutions)
""" """
@ -225,7 +237,7 @@ class Wallet(AbstractWallet):
spend_value = sum([coin.amount for coin in coins]) spend_value = sum([coin.amount for coin in coins])
change = spend_value - amount - fee change = spend_value - amount - fee
spends: List[Tuple[Program, CoinSolution]] = [] spends: List[CoinSolution] = []
output_created = False output_created = False
for coin in coins: for coin in coins:
@ -243,15 +255,7 @@ class Wallet(AbstractWallet):
puzzle: Program = puzzle_for_pk(bytes(pubkey)) puzzle: Program = puzzle_for_pk(bytes(pubkey))
# Only one coin creates outputs # Only one coin creates outputs
if output_created is False and origin_id is None: if not output_created and origin_id in (None, coin.name()):
primaries = [{"puzzlehash": newpuzzlehash, "amount": amount}]
if change > 0:
changepuzzlehash = await self.get_new_puzzlehash()
primaries.append({"puzzlehash": changepuzzlehash, "amount": change})
solution = self.make_solution(primaries=primaries, fee=fee)
output_created = True
elif output_created is False and origin_id == coin.name():
primaries = [{"puzzlehash": newpuzzlehash, "amount": amount}] primaries = [{"puzzlehash": newpuzzlehash, "amount": amount}]
if change > 0: if change > 0:
changepuzzlehash = await self.get_new_puzzlehash() changepuzzlehash = await self.get_new_puzzlehash()
@ -262,29 +266,34 @@ class Wallet(AbstractWallet):
else: else:
solution = self.make_solution() solution = self.make_solution()
spends.append((puzzle, CoinSolution(coin, solution))) puzzle_solution_pair = Program.to([puzzle, solution])
spends.append(CoinSolution(coin, puzzle_solution_pair))
self.log.info(f"Spends is {spends}") self.log.info(f"Spends is {spends}")
return spends return spends
def secret_key_for_public_key(self, public_key: G1Element) -> Optional[PrivateKey]:
return self._pk2sk.get(bytes(public_key))
async def sign_transaction( async def sign_transaction(
self, spends: List[Tuple[Program, CoinSolution]] self, coin_solutions: List[CoinSolution]
) -> SpendBundle: ) -> SpendBundle:
signatures = [] signatures = []
for puzzle, solution in spends:
for coin_solution in coin_solutions:
# Get keys # Get keys
keys = await self.wallet_state_manager.get_keys(solution.coin.puzzle_hash) keys = await self.wallet_state_manager.get_keys(coin_solution.coin.puzzle_hash)
if not keys: if not keys:
error_msg = f"Sign transaction failed, No Keys for puzzlehash {solution.coin.puzzle_hash}" error_msg = f"Sign transaction failed, No Keys for puzzlehash {coin_solution.coin.puzzle_hash}"
self.log.error(error_msg) self.log.error(error_msg)
raise ValueError(error_msg) raise ValueError(error_msg)
pubkey, secretkey = keys pubkey, secretkey = keys
code_ = [puzzle, solution.solution] # HACK
sexp = Program.to(code_) self._pk2sk[bytes(pubkey)] = secretkey
# Get AGGSIG conditions # Get AGGSIG conditions
err, con, cost = conditions_for_solution(sexp) err, con, cost = conditions_for_solution(coin_solution.solution)
if err or not con: if err or not con:
error_msg = f"Sign transaction failed, con:{con}, error: {err}" error_msg = f"Sign transaction failed, con:{con}, error: {err}"
self.log.error(error_msg) self.log.error(error_msg)
@ -294,20 +303,36 @@ class Wallet(AbstractWallet):
# Create signature # Create signature
for _, msg in pkm_pairs_for_conditions_dict( for _, msg in pkm_pairs_for_conditions_dict(
conditions_dict, bytes(solution.coin) conditions_dict, bytes(coin_solution.coin)
): ):
secret_key = self.secret_key_for_public_key(_)
if secret_key is None:
self.log.error(f"no secret key for for {_}")
return None
signature = AugSchemeMPL.sign(secretkey, msg) signature = AugSchemeMPL.sign(secretkey, msg)
signatures.append(signature) signatures.append(signature)
# Aggregate signatures # Aggregate signatures
aggsig = AugSchemeMPL.aggregate(signatures) aggsig = AugSchemeMPL.aggregate(signatures)
solution_list: List[CoinSolution] = [ return SpendBundle(coin_solutions, aggsig)
CoinSolution(
coin_solution.coin, Program.to([puzzle, coin_solution.solution]) async def generate_signed_transaction_dict(
) self, data: Dict[str, Any]
for (puzzle, coin_solution) in spends ) -> Optional[TransactionRecord]:
] """ Use this to generate transaction. """
return SpendBundle(solution_list, aggsig) # Check that both are integers
if not isinstance(data["amount"], int) or not isinstance(data["amount"], int):
raise ValueError("An integer amount or fee is required (too many decimals)")
amount = uint64(data["amount"])
if "fee" in data:
fee = uint64(data["fee"])
else:
fee = uint64(0)
puzzle_hash = decode_puzzle_hash(data["puzzle_hash"])
return await self.generate_signed_transaction(amount, puzzle_hash, fee)
async def generate_signed_transaction( async def generate_signed_transaction(
self, self,
@ -328,13 +353,8 @@ class Wallet(AbstractWallet):
spend_bundle: SpendBundle = await self.sign_transaction(transaction) spend_bundle: SpendBundle = await self.sign_transaction(transaction)
now = uint64(int(time.time())) now = uint64(int(time.time()))
add_list: List[Coin] = [] add_list: List[Coin] = list(spend_bundle.additions())
rem_list: List[Coin] = [] rem_list: List[Coin] = list(spend_bundle.removals())
for add in spend_bundle.additions():
add_list.append(add)
for rem in spend_bundle.removals():
rem_list.append(rem)
return TransactionRecord( return TransactionRecord(
confirmed_at_index=uint32(0), confirmed_at_index=uint32(0),

View File

@ -12,7 +12,6 @@ from src.util.ints import uint32
def test_1(): def test_1():
puzzle_program_0 = puzzle_program_for_index(uint32(0))
puzzle_program_1 = puzzle_program_for_index(uint32(1)) puzzle_program_1 = puzzle_program_for_index(uint32(1))
puzzle_program_2 = puzzle_program_for_index(uint32(2)) puzzle_program_2 = puzzle_program_for_index(uint32(2))
@ -22,9 +21,7 @@ def test_1():
] ]
assert conditions is not None assert conditions is not None
puzzle_hash_solution = p2_delegated_puzzle.solution_for_conditions( puzzle_hash_solution = p2_delegated_puzzle.solution_for_conditions(conditions)
puzzle_program_0, conditions
)
error, output_conditions, cost = conditions_for_solution(puzzle_hash_solution) error, output_conditions, cost = conditions_for_solution(puzzle_hash_solution)
assert error is None assert error is None