chia-blockchain/chia/rpc/wallet_rpc_client.py
matt-o-how 0096f755a1
DID Update Merge (#8129)
* fixed the bugs related to DID puzzles

* change test so that wallets recover into other wallets

* fix parent_info fetching when recovering

* fix did_test

* delete did tests related to singleton behaviours as that is tested elsewhere

* linting fixes

* update hash commit for did_innerpuz.clvm

* update DID wallet RPC calls

* more lint fixes

* delete further references to deprecated did_spend function

* fix bug in wallet state manager related to falsely detecting pool create

* added did_rpc test

* make sure amounts are uint64

* lint fixes

* Fix deadlock in DID wallet creation, and introduce create_new_did_wallet API call

* rename solution_to_pool_state

* Remove tests dir from packages

* added potential fix

* Allow getting unconfirmed balance from wallet_state_manager when under lock

* Remove a deadlock from create_new_did_wallet

* Update DID wallet test to use async check for farmed chia in wallet setup

* Fix unsigned arithmetic error

* Make DID wallet tests compatible with WalletStateManager lock checking

* check if removal belongs to the wallet

* unconfirmed

* did test cleanup

* fix temppuzhash to be an innerpuz

* clean up DID Wallet, add test for previously broken case

* added state_change call when coin added
added did_info for transaction sent

* update delete wallet parameters

* add comments to DID innerpuz

* fix duplicating bug with tx store

* re-enable did tests

* fix cc wallet bare raise

* remove unused assignation

* fix bare spend in did_wallet

* fix small bug

* messages are a cons box where the first value denotes type of message

* cc_wallet uses new parameter for get_confired_balance

* updates to the puzzle based upon suggestion by Richard

* update git submodule chia-blockchain-gui to did_branch

* updated gui to did_branch 76f9e6cea9f58a30984580b594631f3ae2679752

* updated gui to 041ac79be4

Co-authored-by: Adam Kelly <338792+aqk@users.noreply.github.com>
Co-authored-by: Yostra <straya@chia.net>
Co-authored-by: William Blanke <wjb98672@gmail.com>
2021-09-07 12:48:41 -07:00

276 lines
11 KiB
Python

from pathlib import Path
from typing import Dict, List, Optional, Any, Tuple
from chia.pools.pool_wallet_info import PoolWalletInfo
from chia.rpc.rpc_client import RpcClient
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.util.bech32m import decode_puzzle_hash
from chia.util.ints import uint32, uint64
from chia.wallet.transaction_record import TransactionRecord
class WalletRpcClient(RpcClient):
"""
Client to Chia RPC, connects to a local wallet. Uses HTTP/JSON, and converts back from
JSON into native python objects before returning. All api calls use POST requests.
Note that this is not the same as the peer protocol, or wallet protocol (which run Chia's
protocol on top of TCP), it's a separate protocol on top of HTTP that provides easy access
to the full node.
"""
# Key Management APIs
async def log_in(self, fingerprint: int) -> Dict:
try:
return await self.fetch(
"log_in",
{"host": "https://backup.chia.net", "fingerprint": fingerprint, "type": "start"},
)
except ValueError as e:
return e.args[0]
async def log_in_and_restore(self, fingerprint: int, file_path) -> Dict:
try:
return await self.fetch(
"log_in",
{
"host": "https://backup.chia.net",
"fingerprint": fingerprint,
"type": "restore_backup",
"file_path": file_path,
},
)
except ValueError as e:
return e.args[0]
async def log_in_and_skip(self, fingerprint: int) -> Dict:
try:
return await self.fetch(
"log_in",
{"host": "https://backup.chia.net", "fingerprint": fingerprint, "type": "skip"},
)
except ValueError as e:
return e.args[0]
async def get_public_keys(self) -> List[int]:
return (await self.fetch("get_public_keys", {}))["public_key_fingerprints"]
async def get_private_key(self, fingerprint: int) -> Dict:
return (await self.fetch("get_private_key", {"fingerprint": fingerprint}))["private_key"]
async def generate_mnemonic(self) -> List[str]:
return (await self.fetch("generate_mnemonic", {}))["mnemonic"]
async def add_key(self, mnemonic: List[str], request_type: str = "new_wallet") -> None:
return await self.fetch("add_key", {"mnemonic": mnemonic, "type": request_type})
async def delete_key(self, fingerprint: int) -> None:
return await self.fetch("delete_key", {"fingerprint": fingerprint})
async def check_delete_key(self, fingerprint: int) -> None:
return await self.fetch("check_delete_key", {"fingerprint": fingerprint})
async def delete_all_keys(self) -> None:
return await self.fetch("delete_all_keys", {})
# Wallet Node APIs
async def get_sync_status(self) -> bool:
return (await self.fetch("get_sync_status", {}))["syncing"]
async def get_synced(self) -> bool:
return (await self.fetch("get_sync_status", {}))["synced"]
async def get_height_info(self) -> uint32:
return (await self.fetch("get_height_info", {}))["height"]
async def farm_block(self, address: str) -> None:
return await self.fetch("farm_block", {"address": address})
# Wallet Management APIs
async def get_wallets(self) -> Dict:
return (await self.fetch("get_wallets", {}))["wallets"]
# Wallet APIs
async def get_wallet_balance(self, wallet_id: str) -> Dict:
return (await self.fetch("get_wallet_balance", {"wallet_id": wallet_id}))["wallet_balance"]
async def get_transaction(self, wallet_id: str, transaction_id: bytes32) -> TransactionRecord:
res = await self.fetch(
"get_transaction",
{"walled_id": wallet_id, "transaction_id": transaction_id.hex()},
)
return TransactionRecord.from_json_dict(res["transaction"])
async def get_transactions(
self,
wallet_id: str,
) -> List[TransactionRecord]:
res = await self.fetch(
"get_transactions",
{"wallet_id": wallet_id},
)
reverted_tx: List[TransactionRecord] = []
for modified_tx in res["transactions"]:
# Server returns address instead of ph, but TransactionRecord requires ph
modified_tx["to_puzzle_hash"] = decode_puzzle_hash(modified_tx["to_address"]).hex()
del modified_tx["to_address"]
reverted_tx.append(TransactionRecord.from_json_dict(modified_tx))
return reverted_tx
async def get_next_address(self, wallet_id: str, new_address: bool) -> str:
return (await self.fetch("get_next_address", {"wallet_id": wallet_id, "new_address": new_address}))["address"]
async def send_transaction(
self, wallet_id: str, amount: uint64, address: str, fee: uint64 = uint64(0)
) -> TransactionRecord:
res = await self.fetch(
"send_transaction",
{"wallet_id": wallet_id, "amount": amount, "address": address, "fee": fee},
)
return TransactionRecord.from_json_dict(res["transaction"])
async def send_transaction_multi(
self, wallet_id: str, additions: List[Dict], coins: List[Coin] = None, fee: uint64 = uint64(0)
) -> TransactionRecord:
# Converts bytes to hex for puzzle hashes
additions_hex = [{"amount": ad["amount"], "puzzle_hash": ad["puzzle_hash"].hex()} for ad in additions]
if coins is not None and len(coins) > 0:
coins_json = [c.to_json_dict() for c in coins]
response: Dict = await self.fetch(
"send_transaction_multi",
{"wallet_id": wallet_id, "additions": additions_hex, "coins": coins_json, "fee": fee},
)
else:
response = await self.fetch(
"send_transaction_multi", {"wallet_id": wallet_id, "additions": additions_hex, "fee": fee}
)
return TransactionRecord.from_json_dict(response["transaction"])
async def delete_unconfirmed_transactions(self, wallet_id: str) -> None:
await self.fetch(
"delete_unconfirmed_transactions",
{"wallet_id": wallet_id},
)
return None
async def create_backup(self, file_path: Path) -> None:
return await self.fetch("create_backup", {"file_path": str(file_path.resolve())})
async def get_farmed_amount(self) -> Dict:
return await self.fetch("get_farmed_amount", {})
async def create_signed_transaction(
self, additions: List[Dict], coins: List[Coin] = None, fee: uint64 = uint64(0)
) -> TransactionRecord:
# Converts bytes to hex for puzzle hashes
additions_hex = [{"amount": ad["amount"], "puzzle_hash": ad["puzzle_hash"].hex()} for ad in additions]
if coins is not None and len(coins) > 0:
coins_json = [c.to_json_dict() for c in coins]
response: Dict = await self.fetch(
"create_signed_transaction", {"additions": additions_hex, "coins": coins_json, "fee": fee}
)
else:
response = await self.fetch("create_signed_transaction", {"additions": additions_hex, "fee": fee})
return TransactionRecord.from_json_dict(response["signed_tx"])
async def create_new_did_wallet(self, amount):
request: Dict[str, Any] = {
"wallet_type": "did_wallet",
"did_type": "new",
"backup_dids": [],
"num_of_backup_ids_needed": 0,
"amount": amount,
"host": f"{self.hostname}:{self.port}",
}
response = await self.fetch("create_new_wallet", request)
return response
async def create_new_did_wallet_from_recovery(self, filename):
request: Dict[str, Any] = {
"wallet_type": "did_wallet",
"did_type": "recovery",
"filename": filename,
"host": f"{self.hostname}:{self.port}",
}
response = await self.fetch("create_new_wallet", request)
return response
async def did_create_attest(self, wallet_id, coin_name, pubkey, puzhash, file_name):
request: Dict[str, Any] = {
"wallet_id": wallet_id,
"coin_name": coin_name,
"pubkey": pubkey,
"puzhash": puzhash,
"filename": file_name,
}
response = await self.fetch("did_create_attest", request)
return response
async def did_recovery_spend(self, wallet_id, attest_filenames):
request: Dict[str, Any] = {
"wallet_id": wallet_id,
"attest_filenames": attest_filenames,
}
response = await self.fetch("did_recovery_spend", request)
return response
async def create_new_pool_wallet(
self,
target_puzzlehash: Optional[bytes32],
pool_url: Optional[str],
relative_lock_height: uint32,
backup_host: str,
mode: str,
state: str,
p2_singleton_delay_time: Optional[uint64] = None,
p2_singleton_delayed_ph: Optional[bytes32] = None,
) -> TransactionRecord:
request: Dict[str, Any] = {
"wallet_type": "pool_wallet",
"mode": mode,
"host": backup_host,
"initial_target_state": {
"target_puzzle_hash": target_puzzlehash.hex() if target_puzzlehash else None,
"relative_lock_height": relative_lock_height,
"pool_url": pool_url,
"state": state,
},
}
if p2_singleton_delay_time is not None:
request["p2_singleton_delay_time"] = p2_singleton_delay_time
if p2_singleton_delayed_ph is not None:
request["p2_singleton_delayed_ph"] = p2_singleton_delayed_ph.hex()
res = await self.fetch("create_new_wallet", request)
return TransactionRecord.from_json_dict(res["transaction"])
async def pw_self_pool(self, wallet_id: str) -> TransactionRecord:
return TransactionRecord.from_json_dict(
(await self.fetch("pw_self_pool", {"wallet_id": wallet_id}))["transaction"]
)
async def pw_join_pool(
self, wallet_id: str, target_puzzlehash: bytes32, pool_url: str, relative_lock_height: uint32
) -> TransactionRecord:
request = {
"wallet_id": int(wallet_id),
"target_puzzlehash": target_puzzlehash.hex(),
"relative_lock_height": relative_lock_height,
"pool_url": pool_url,
}
return TransactionRecord.from_json_dict((await self.fetch("pw_join_pool", request))["transaction"])
async def pw_absorb_rewards(self, wallet_id: str, fee: uint64 = uint64(0)) -> TransactionRecord:
return TransactionRecord.from_json_dict(
(await self.fetch("pw_absorb_rewards", {"wallet_id": wallet_id, "fee": fee}))["transaction"]
)
async def pw_status(self, wallet_id: str) -> Tuple[PoolWalletInfo, List[TransactionRecord]]:
json_dict = await self.fetch("pw_status", {"wallet_id": wallet_id})
return (
PoolWalletInfo.from_json_dict(json_dict["state"]),
[TransactionRecord.from_json_dict(tr) for tr in json_dict["unconfirmed_transactions"]],
)