More wallet simplifications. Placeholder for looking up
secret key by public key.
This commit is contained in:
parent
9f1fc6bdfc
commit
8505550be5
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue