Remove rl wallet (#13429)

* remove all references to RL wallet

* Missed one

* Some new references from main

* Fix is_transaction_confirmed

* mypy

* check for 'confirmed'

* Try again to fix is_transaction_confirmed

* remove from mypy.ini
This commit is contained in:
Matt Hauff 2022-09-13 15:07:29 -07:00 committed by GitHub
parent b081c208e3
commit 2de55aff17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 13 additions and 1307 deletions

View File

@ -110,7 +110,6 @@ extend_skip=
chia/wallet/puzzles/puzzle_utils.py
chia/wallet/puzzles/singleton_top_layer.py
chia/wallet/puzzles/tails.py
chia/wallet/rl_wallet/rl_wallet.py
chia/wallet/sign_coin_spends.py
chia/wallet/trade_manager.py
chia/wallet/trade_record.py

View File

@ -686,7 +686,7 @@ async def cancel_offer(args: dict, wallet_client: WalletRpcClient, fingerprint:
def wallet_coin_unit(typ: WalletType, address_prefix: str) -> Tuple[str, int]:
if typ == WalletType.CAT:
return "", units["cat"]
if typ in [WalletType.STANDARD_WALLET, WalletType.POOLING_WALLET, WalletType.MULTI_SIG, WalletType.RATE_LIMITED]:
if typ in [WalletType.STANDARD_WALLET, WalletType.POOLING_WALLET, WalletType.MULTI_SIG]:
return address_prefix, units["chia"]
return "", units["mojo"]

View File

@ -48,7 +48,6 @@ from chia.wallet.nft_wallet.nft_wallet import NFTWallet
from chia.wallet.nft_wallet.uncurry_nft import UncurriedNFT
from chia.wallet.outer_puzzles import AssetType
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
from chia.wallet.rl_wallet.rl_wallet import RLWallet
from chia.wallet.trade_record import TradeRecord
from chia.wallet.trading.offer import Offer
from chia.wallet.transaction_record import TransactionRecord
@ -155,10 +154,6 @@ class WalletRpcApi:
"/nft_transfer_nft": self.nft_transfer_nft,
"/nft_add_uri": self.nft_add_uri,
"/nft_calculate_royalties": self.nft_calculate_royalties,
# RL wallet
"/rl_set_user_info": self.rl_set_user_info,
"/send_clawback_transaction:": self.send_clawback_transaction,
"/add_rate_limited_funds:": self.add_rate_limited_funds,
# Pool Wallet
"/pw_join_pool": self.pw_join_pool,
"/pw_self_pool": self.pw_self_pool,
@ -511,41 +506,6 @@ class WalletRpcApi:
else: # undefined mode
pass
elif request["wallet_type"] == "rl_wallet":
if request["rl_type"] == "admin":
log.info("Create rl admin wallet")
async with self.service.wallet_state_manager.lock:
rl_admin: RLWallet = await RLWallet.create_rl_admin(wallet_state_manager)
success = await rl_admin.admin_create_coin(
uint64(int(request["interval"])),
uint64(int(request["limit"])),
request["pubkey"],
uint64(int(request["amount"])),
uint64(int(request["fee"])) if "fee" in request else uint64(0),
)
assert rl_admin.rl_info.admin_pubkey is not None
return {
"success": success,
"id": rl_admin.id(),
"type": rl_admin.type(),
"origin": rl_admin.rl_info.rl_origin,
"pubkey": rl_admin.rl_info.admin_pubkey.hex(),
}
elif request["rl_type"] == "user":
log.info("Create rl user wallet")
async with self.service.wallet_state_manager.lock:
rl_user: RLWallet = await RLWallet.create_rl_user(wallet_state_manager)
assert rl_user.rl_info.user_pubkey is not None
return {
"id": rl_user.id(),
"type": rl_user.type(),
"pubkey": rl_user.rl_info.user_pubkey.hex(),
}
else: # undefined rl_type
pass
elif request["wallet_type"] == "did_wallet":
if request["did_type"] == "new":
backup_dids = []
@ -1849,48 +1809,6 @@ class WalletRpcApi:
{asset["asset"]: uint64(asset["amount"]) for asset in request.get("fungible_assets", [])},
)
##########################################################################################
# Rate Limited Wallet
##########################################################################################
async def rl_set_user_info(self, request) -> EndpointResult:
wallet_id = uint32(int(request["wallet_id"]))
rl_user = self.service.wallet_state_manager.wallets[wallet_id]
origin = request["origin"]
async with self.service.wallet_state_manager.lock:
await rl_user.set_user_info(
uint64(request["interval"]),
uint64(request["limit"]),
origin["parent_coin_info"],
origin["puzzle_hash"],
origin["amount"],
request["admin_pubkey"],
)
return {}
async def send_clawback_transaction(self, request) -> EndpointResult:
wallet_id = uint32(request["wallet_id"])
wallet: RLWallet = self.service.wallet_state_manager.wallets[wallet_id]
fee = int(request["fee"])
async with self.service.wallet_state_manager.lock:
tx = await wallet.clawback_rl_coin_transaction(fee)
await wallet.push_transaction(tx)
# Transaction may not have been included in the mempool yet. Use get_transaction to check.
return {
"transaction": tx,
"transaction_id": tx.name,
}
async def add_rate_limited_funds(self, request) -> EndpointResult:
wallet_id = uint32(request["wallet_id"])
wallet: RLWallet = self.service.wallet_state_manager.wallets[wallet_id]
puzzle_hash = wallet.rl_get_aggregation_puzzlehash(wallet.rl_info.rl_puzzle_hash)
async with self.service.wallet_state_manager.lock:
await wallet.rl_add_funds(request["amount"], puzzle_hash, request["fee"])
return {"status": "SUCCESS"}
async def get_farmed_amount(self, request) -> EndpointResult:
tx_records: List[TransactionRecord] = await self.service.wallet_state_manager.tx_store.get_farming_rewards()
amount = 0

View File

@ -1,107 +0,0 @@
(mod (pubkey
rate_amount
interval_time
origin_id
clawback_pubkey
mode
. args)
(defconstant RATE_LIMITED_MODE 1)
(defconstant AGGREGATE_MODE 2)
(defconstant CLAWBACK_MODE 3)
(include condition_codes.clvm)
(defun sha256tree (tree)
(if (l tree)
(sha256 2 (sha256tree (f tree)) (sha256tree (r tree)))
(sha256 1 tree)))
(defun-inline aggsig-solution-with-key (key)
(list AGG_SIG_UNSAFE key (sha256tree (c mode args)))
)
(defmacro assert items
(if (r items)
(list if (f items) (c assert (r items)) (q (x)))
(f items)
)
)
(defmacro or args
(if args
(qq (if (unquote (f args))
1
(unquote (c or (r args)))))
0))
(defun create-lock (consolidating_primary_input consolidating_coin_puzzle_hash outgoing_amount)
(list CREATE_COIN_ANNOUNCEMENT
(sha256 consolidating_primary_input
consolidating_coin_puzzle_hash
outgoing_amount))
)
(defun aggregation (origin_id
(my_puzzle_hash
consolidating_primary_input
consolidating_coin_puzzle_hash
outgoing_amount
primary_input
incoming_amount
parent_amount
my_parent_parent_id))
(assert (or (= (sha256 my_parent_parent_id my_puzzle_hash parent_amount)
primary_input)
(= origin_id primary_input))
(list (generate-assert-id-condition primary_input my_puzzle_hash incoming_amount)
(create-lock consolidating_primary_input consolidating_coin_puzzle_hash outgoing_amount)
(create-new-coin my_puzzle_hash (+ outgoing_amount incoming_amount)))))
(defun >= (a b)
(or (> a b)
(= a b)))
(defun generate-block-age-condition (min_block_time outgoing_amount rate_amount interval_time)
(assert (>= (* min_block_time rate_amount)
(* outgoing_amount interval_time))
(list ASSERT_HEIGHT_RELATIVE min_block_time)))
(defun create-change (my_puzzlehash my_amount outgoing_amount fee)
(list CREATE_COIN my_puzzlehash (- my_amount (+ outgoing_amount fee))))
(defun generate-assert-id-condition (my_parent_id my_puzzlehash my_amount)
(list ASSERT_MY_COIN_ID (sha256 my_parent_id my_puzzlehash my_amount)))
(defun create-new-coin (outgoing_puzzle_hash outgoing_amount)
(list CREATE_COIN outgoing_puzzle_hash outgoing_amount))
(defun rate-limited-puzzle
(rate_amount
interval_time
origin_id
(my_parent_id
my_puzzlehash
my_amount
outgoing_puzzle_hash
outgoing_amount
min_block_time
parent_parent_id
parent_amount
fee))
(assert (or (= (sha256 parent_parent_id my_puzzlehash parent_amount)
my_parent_id)
(= origin_id my_parent_id))
(list (generate-block-age-condition min_block_time outgoing_amount rate_amount interval_time)
(create-change my_puzzlehash my_amount outgoing_amount fee)
(generate-assert-id-condition my_parent_id my_puzzlehash my_amount)
(create-new-coin outgoing_puzzle_hash outgoing_amount))))
(if (= mode CLAWBACK_MODE)
(c (aggsig-solution-with-key clawback_pubkey)
args)
(c (aggsig-solution-with-key pubkey)
(if (= mode RATE_LIMITED_MODE)
(rate-limited-puzzle rate_amount interval_time origin_id args)
(aggregation origin_id args))))
)

View File

@ -1 +0,0 @@
ff02ffff01ff02ffff03ffff09ff81bfff2480ffff01ff04ffff04ff30ffff04ff5fffff04ffff02ff3effff04ff02ffff04ffff04ff81bfff81ff80ff80808080ff80808080ff81ff80ffff01ff04ffff04ff30ffff04ff05ffff04ffff02ff3effff04ff02ffff04ffff04ff81bfff81ff80ff80808080ff80808080ffff02ffff03ffff09ff81bfff3c80ffff01ff02ff2effff04ff02ffff04ff0bffff04ff17ffff04ff2fffff04ff81ffff80808080808080ffff01ff02ff22ffff04ff02ffff04ff2fffff04ff81ffff808080808080ff01808080ff0180ffff04ffff01ffffffffff02ffff03ffff15ff05ff0b80ffff01ff0101ffff01ff02ffff03ffff09ff05ff0b80ffff01ff0101ff8080ff018080ff018031ff5246ffff0333ff3c01ffffffff02ffff03ffff02ffff03ffff09ffff0bff820bfbff13ff8205fb80ff82017b80ffff01ff0101ffff01ff02ffff03ffff09ff05ff82017b80ffff01ff0101ff8080ff018080ff0180ffff01ff04ffff02ff26ffff04ff02ffff04ff82017bffff04ff13ffff04ff8202fbff808080808080ffff04ffff02ff2affff04ff02ffff04ff2bffff04ff5bffff04ff81bbff808080808080ffff04ffff02ff3affff04ff02ffff04ff13ffff04ffff10ff81bbff8202fb80ff8080808080ff80808080ffff01ffff08808080ff0180ff04ff34ffff04ff05ffff04ffff11ff0bffff10ff17ff2f8080ff80808080ffff04ff2cffff04ffff0bff05ff0bff1780ff808080ff04ff34ffff04ff05ffff04ff0bff80808080ffffff04ff38ffff04ffff0bff05ff0bff1780ff808080ff02ffff03ffff02ff20ffff04ff02ffff04ffff12ff05ff1780ffff04ffff12ff0bff2f80ff8080808080ffff01ff04ff28ffff04ff05ff808080ffff01ffff08808080ff0180ffff02ffff03ffff02ffff03ffff09ffff0bff8217efff81afff822fef80ff4f80ffff01ff0101ffff01ff02ffff03ffff09ff17ff4f80ffff01ff0101ff8080ff018080ff0180ffff01ff04ffff02ff36ffff04ff02ffff04ff820befffff04ff8205efffff04ff05ffff04ff0bff80808080808080ffff04ffff02ff32ffff04ff02ffff04ff81afffff04ff82016fffff04ff8205efffff04ff825fefff80808080808080ffff04ffff02ff26ffff04ff02ffff04ff4fffff04ff81afffff04ff82016fff808080808080ffff04ffff02ff3affff04ff02ffff04ff8202efffff04ff8205efff8080808080ff8080808080ffff01ffff08808080ff0180ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff3effff04ff02ffff04ff09ff80808080ffff02ff3effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080

View File

@ -1 +0,0 @@
f663796a8c522b85bd9472cbea2bf7f138e8351e8e4032706fc0539e87f94faf

View File

@ -1,26 +0,0 @@
(mod (wallet_puzzle
my-id
wallet-coin-primary-input
wallet-coin-amount)
(include condition_codes.clvm)
(defun sha256tree (tree)
(if (l tree)
(sha256 2 (sha256tree (f tree)) (sha256tree (r tree)))
(sha256 1 tree)))
(defun-inline create-my-id-condition ()
(list ASSERT_MY_COIN_ID my-id))
(include create-lock-puzzlehash.clvm)
(defun-inline parent-coin-id ()
(sha256 wallet-coin-primary-input wallet_puzzle wallet-coin-amount))
(defun-inline input-of-lock ()
(list ASSERT_COIN_ANNOUNCEMENT (sha256 (parent-coin-id) my-id)))
(list (create-my-id-condition)
(input-of-lock))
)

View File

@ -1 +0,0 @@
ff02ffff01ff04ffff04ff06ffff04ff0bff808080ffff04ffff04ff04ffff04ffff0bffff0bff17ff05ff2f80ff0b80ff808080ff808080ffff04ffff01ff3d46ff018080

View File

@ -1 +0,0 @@
007400187f63927ee023a7172bb88f14d49aaa4beb790ecaf7dde7c1a79b6481

View File

@ -1,708 +0,0 @@
# RLWallet is subclass of Wallet
import json
import time
from dataclasses import dataclass
from secrets import token_bytes
from typing import Any, List, Optional, Tuple
from blspy import AugSchemeMPL, G1Element, PrivateKey
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.coin_spend import CoinSpend
from chia.types.spend_bundle import SpendBundle
from chia.util.byte_types import hexstr_to_bytes
from chia.util.ints import uint8, uint32, uint64, uint128
from chia.util.streamable import Streamable, streamable
from chia.wallet.derivation_record import DerivationRecord
from chia.wallet.derive_keys import master_sk_to_wallet_sk
from chia.wallet.rl_wallet.rl_wallet_puzzles import (
make_clawback_solution,
rl_make_aggregation_puzzle,
rl_make_aggregation_solution,
rl_make_solution_mode_2,
rl_puzzle_for_pk,
solution_for_rl,
)
from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.util.transaction_type import TransactionType
from chia.wallet.util.wallet_types import WalletType
from chia.wallet.util.compute_memos import compute_memos
from chia.wallet.wallet import Wallet
from chia.wallet.wallet_coin_record import WalletCoinRecord
from chia.wallet.wallet_info import WalletInfo
@streamable
@dataclass(frozen=True)
class RLInfo(Streamable):
type: str
admin_pubkey: Optional[bytes]
user_pubkey: Optional[bytes]
limit: Optional[uint64]
interval: Optional[uint64]
rl_origin: Optional[Coin]
rl_origin_id: Optional[bytes32]
rl_puzzle_hash: Optional[bytes32]
initialized: bool
class RLWallet:
wallet_state_manager: Any
wallet_info: WalletInfo
rl_coin_record: Optional[WalletCoinRecord]
rl_info: RLInfo
main_wallet: Wallet
private_key: PrivateKey
@staticmethod
async def create_rl_admin(
wallet_state_manager: Any,
):
unused: Optional[uint32] = await wallet_state_manager.puzzle_store.get_unused_derivation_path()
if unused is None:
await wallet_state_manager.create_more_puzzle_hashes()
unused = await wallet_state_manager.puzzle_store.get_unused_derivation_path()
assert unused is not None
private_key = master_sk_to_wallet_sk(wallet_state_manager.private_key, unused)
pubkey: G1Element = private_key.get_g1()
rl_info = RLInfo("admin", bytes(pubkey), None, None, None, None, None, None, False)
info_as_string = json.dumps(rl_info.to_json_dict())
wallet_info: WalletInfo = await wallet_state_manager.user_store.create_wallet(
"RL Admin", WalletType.RATE_LIMITED, info_as_string
)
await wallet_state_manager.puzzle_store.add_derivation_paths(
[
DerivationRecord(
unused,
bytes32(token_bytes(32)),
pubkey,
WalletType.RATE_LIMITED,
wallet_info.id,
False,
)
]
)
await wallet_state_manager.puzzle_store.set_used_up_to(unused)
self = await RLWallet.create(wallet_state_manager, wallet_info)
await wallet_state_manager.add_new_wallet(self, self.id())
return self
@staticmethod
async def create_rl_user(
wallet_state_manager: Any,
):
async with wallet_state_manager.puzzle_store.lock:
unused: Optional[uint32] = await wallet_state_manager.puzzle_store.get_unused_derivation_path()
if unused is None:
await wallet_state_manager.create_more_puzzle_hashes()
unused = await wallet_state_manager.puzzle_store.get_unused_derivation_path()
assert unused is not None
private_key = wallet_state_manager.private_key
pubkey: G1Element = master_sk_to_wallet_sk(private_key, unused).get_g1()
rl_info = RLInfo("user", None, bytes(pubkey), None, None, None, None, None, False)
info_as_string = json.dumps(rl_info.to_json_dict())
wallet_info = await wallet_state_manager.user_store.create_wallet(
"RL User",
WalletType.RATE_LIMITED,
info_as_string,
)
self = await RLWallet.create(wallet_state_manager, wallet_info)
await wallet_state_manager.puzzle_store.add_derivation_paths(
[
DerivationRecord(
unused, bytes32(token_bytes(32)), pubkey, WalletType.RATE_LIMITED, wallet_info.id, False
)
]
)
await wallet_state_manager.puzzle_store.set_used_up_to(unused)
await wallet_state_manager.add_new_wallet(self, self.id())
return self
@staticmethod
async def create(wallet_state_manager: Any, info: WalletInfo):
self = RLWallet()
self.private_key = wallet_state_manager.private_key
self.wallet_state_manager = wallet_state_manager
self.wallet_info = info
self.rl_info = RLInfo.from_json_dict(json.loads(info.data))
self.main_wallet = wallet_state_manager.main_wallet
return self
@classmethod
def type(cls) -> uint8:
return uint8(WalletType.RATE_LIMITED)
def id(self) -> uint32:
return self.wallet_info.id
async def admin_create_coin(
self,
interval: uint64,
limit: uint64,
user_pubkey: str,
amount: uint64,
fee: uint64,
) -> bool:
coins = await self.wallet_state_manager.main_wallet.select_coins(amount)
if coins is None:
return False
origin = coins.copy().pop()
origin_id = origin.name()
user_pubkey_bytes = hexstr_to_bytes(user_pubkey)
assert self.rl_info.admin_pubkey is not None
rl_puzzle = rl_puzzle_for_pk(
pubkey=user_pubkey_bytes,
rate_amount=limit,
interval_time=interval,
origin_id=origin_id,
clawback_pk=self.rl_info.admin_pubkey,
)
rl_puzzle_hash = rl_puzzle.get_tree_hash()
index = await self.wallet_state_manager.puzzle_store.index_for_pubkey(
G1Element.from_bytes(self.rl_info.admin_pubkey)
)
assert index is not None
record = DerivationRecord(
index,
rl_puzzle_hash,
G1Element.from_bytes(self.rl_info.admin_pubkey),
WalletType.RATE_LIMITED,
self.id(),
False,
)
await self.wallet_state_manager.puzzle_store.add_derivation_paths([record])
spend_bundle = await self.main_wallet.generate_signed_transaction(amount, rl_puzzle_hash, fee, origin_id, coins)
if spend_bundle is None:
return False
await self.main_wallet.push_transaction(spend_bundle)
new_rl_info = RLInfo(
"admin",
self.rl_info.admin_pubkey,
user_pubkey_bytes,
limit,
interval,
origin,
origin.name(),
rl_puzzle_hash,
True,
)
data_str = json.dumps(new_rl_info.to_json_dict())
new_wallet_info = WalletInfo(self.id(), self.wallet_info.name, self.type(), data_str)
await self.wallet_state_manager.user_store.update_wallet(new_wallet_info)
await self.wallet_state_manager.add_new_wallet(self, self.id())
self.wallet_info = new_wallet_info
self.rl_info = new_rl_info
return True
async def set_user_info(
self,
interval: uint64,
limit: uint64,
origin_parent_id: str,
origin_puzzle_hash: str,
origin_amount: uint64,
admin_pubkey: str,
) -> None:
admin_pubkey_bytes = hexstr_to_bytes(admin_pubkey)
assert self.rl_info.user_pubkey is not None
origin = Coin(
bytes32(hexstr_to_bytes(origin_parent_id)),
bytes32(hexstr_to_bytes(origin_puzzle_hash)),
origin_amount,
)
rl_puzzle = rl_puzzle_for_pk(
pubkey=self.rl_info.user_pubkey,
rate_amount=limit,
interval_time=interval,
origin_id=origin.name(),
clawback_pk=admin_pubkey_bytes,
)
rl_puzzle_hash = rl_puzzle.get_tree_hash()
new_rl_info = RLInfo(
"user",
admin_pubkey_bytes,
self.rl_info.user_pubkey,
limit,
interval,
origin,
origin.name(),
rl_puzzle_hash,
True,
)
rl_puzzle_hash = rl_puzzle.get_tree_hash()
if await self.wallet_state_manager.puzzle_store.puzzle_hash_exists(rl_puzzle_hash):
raise ValueError(
"Cannot create multiple Rate Limited wallets under the same keys. This will change in a future release."
)
user_pubkey: G1Element = G1Element.from_bytes(self.rl_info.user_pubkey)
index = await self.wallet_state_manager.puzzle_store.index_for_pubkey(user_pubkey)
assert index is not None
record = DerivationRecord(
uint32(index),
rl_puzzle_hash,
user_pubkey,
WalletType.RATE_LIMITED,
self.id(),
False,
)
aggregation_puzzlehash = self.rl_get_aggregation_puzzlehash(new_rl_info.rl_puzzle_hash)
record2 = DerivationRecord(
uint32(index + 1),
aggregation_puzzlehash,
user_pubkey,
WalletType.RATE_LIMITED,
self.id(),
False,
)
await self.wallet_state_manager.puzzle_store.add_derivation_paths([record, record2])
self.wallet_state_manager.set_coin_with_puzzlehash_created_callback(
aggregation_puzzlehash, self.aggregate_this_coin
)
data_str = json.dumps(new_rl_info.to_json_dict())
new_wallet_info = WalletInfo(self.id(), self.wallet_info.name, self.type(), data_str)
await self.wallet_state_manager.user_store.update_wallet(new_wallet_info)
await self.wallet_state_manager.add_new_wallet(self, self.id())
self.wallet_info = new_wallet_info
self.rl_info = new_rl_info
async def aggregate_this_coin(self, coin: Coin):
spend_bundle = await self.rl_generate_signed_aggregation_transaction(
self.rl_info, coin, await self._get_rl_parent(), await self._get_rl_coin()
)
rl_coin = await self._get_rl_coin()
puzzle_hash = rl_coin.puzzle_hash if rl_coin is not None else None
assert puzzle_hash is not None
tx_record = TransactionRecord(
confirmed_at_height=uint32(0),
created_at_time=uint64(int(time.time())),
to_puzzle_hash=puzzle_hash,
amount=uint64(0),
fee_amount=uint64(0),
confirmed=False,
sent=uint32(0),
spend_bundle=spend_bundle,
additions=spend_bundle.additions(),
removals=spend_bundle.removals(),
wallet_id=self.id(),
sent_to=[],
trade_id=None,
type=uint32(TransactionType.OUTGOING_TX.value),
name=spend_bundle.name(),
memos=list(compute_memos(spend_bundle).items()),
)
await self.push_transaction(tx_record)
async def rl_available_balance(self) -> uint64:
self.rl_coin_record = await self._get_rl_coin_record()
if self.rl_coin_record is None:
return uint64(0)
peak = self.wallet_state_manager.blockchain.get_peak()
height = peak.height if peak else 0
assert self.rl_info.limit is not None
if self.rl_info.interval is None:
raise RuntimeError("rl_available_balance: rl_info.interval is undefined")
unlocked = int(
((height - self.rl_coin_record.confirmed_block_height) / self.rl_info.interval) * int(self.rl_info.limit)
)
total_amount = self.rl_coin_record.coin.amount
available_amount = min(unlocked, total_amount)
return uint64(available_amount)
async def get_confirmed_balance(self, unspent_records=None) -> uint128:
return await self.wallet_state_manager.get_confirmed_balance_for_wallet(self.id(), unspent_records)
async def get_unconfirmed_balance(self, unspent_records=None) -> uint128:
return await self.wallet_state_manager.get_unconfirmed_balance(self.id(), unspent_records)
async def get_spendable_balance(self, unspent_records=None) -> uint128:
spendable_am = await self.wallet_state_manager.get_confirmed_spendable_balance_for_wallet(self.id())
return spendable_am
async def get_max_send_amount(self, records=None):
# Rate limited wallet is a singleton, max send is same as spendable
return await self.get_spendable_balance()
async def get_pending_change_balance(self) -> uint64:
unconfirmed_tx = await self.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(self.id())
addition_amount = 0
for record in unconfirmed_tx:
if not record.is_in_mempool():
continue
our_spend = False
for coin in record.removals:
if await self.wallet_state_manager.does_coin_belong_to_wallet(coin, self.id()):
our_spend = True
break
if our_spend is not True:
continue
for coin in record.additions:
if await self.wallet_state_manager.does_coin_belong_to_wallet(coin, self.id()):
addition_amount += coin.amount
return uint64(addition_amount)
def get_new_puzzle(self) -> Program:
if (
self.rl_info.limit is None
or self.rl_info.interval is None
or self.rl_info.user_pubkey is None
or self.rl_info.admin_pubkey is None
or self.rl_info.rl_origin_id is None
):
raise ValueError("One or more of the RL info fields is None")
return rl_puzzle_for_pk(
pubkey=self.rl_info.user_pubkey,
rate_amount=self.rl_info.limit,
interval_time=self.rl_info.interval,
origin_id=self.rl_info.rl_origin_id,
clawback_pk=self.rl_info.admin_pubkey,
)
def get_new_puzzlehash(self) -> bytes32:
return self.get_new_puzzle().get_tree_hash()
async def can_generate_rl_puzzle_hash(self, hash) -> bool:
return await self.wallet_state_manager.puzzle_store.puzzle_hash_exists(hash)
def puzzle_for_pk(self, pk) -> Optional[Program]:
if self.rl_info.initialized is False:
return None
if (
self.rl_info.limit is None
or self.rl_info.interval is None
or self.rl_info.user_pubkey is None
or self.rl_info.admin_pubkey is None
or self.rl_info.rl_origin_id is None
):
return None
return rl_puzzle_for_pk(
pubkey=self.rl_info.user_pubkey,
rate_amount=self.rl_info.limit,
interval_time=self.rl_info.interval,
origin_id=self.rl_info.rl_origin_id,
clawback_pk=self.rl_info.admin_pubkey,
)
def puzzle_hash_for_pk(self, pubkey: G1Element) -> Optional[bytes32]:
# TODO: rewrite using curry_and_treehash
puzzle = self.puzzle_for_pk(pubkey)
if puzzle is None:
return puzzle
return puzzle.get_tree_hash()
async def get_keys(self, puzzle_hash: bytes32) -> Tuple[G1Element, PrivateKey]:
"""
Returns keys for puzzle_hash.
"""
index_for_puzzlehash = await self.wallet_state_manager.puzzle_store.index_for_puzzle_hash_and_wallet(
puzzle_hash, self.id()
)
if index_for_puzzlehash is None:
raise ValueError(f"index_for_puzzlehash is None ph {puzzle_hash}")
private = master_sk_to_wallet_sk(self.private_key, index_for_puzzlehash)
pubkey = private.get_g1()
return pubkey, private
async def get_keys_pk(self, clawback_pubkey: bytes):
"""
Return keys for pubkey
"""
index_for_pubkey = await self.wallet_state_manager.puzzle_store.index_for_pubkey(
G1Element.from_bytes(clawback_pubkey)
)
if index_for_pubkey is None:
raise ValueError(f"index_for_pubkey is None pk {clawback_pubkey.hex()}")
private = master_sk_to_wallet_sk(self.private_key, index_for_pubkey)
pubkey = private.get_g1()
return pubkey, private
async def _get_rl_coin(self) -> Optional[Coin]:
if self.rl_info.rl_puzzle_hash is None:
return None
rl_coins = await self.wallet_state_manager.coin_store.get_coin_records_by_puzzle_hash(
self.rl_info.rl_puzzle_hash
)
for coin_record in rl_coins:
if coin_record.spent is False:
return coin_record.coin
return None
async def _get_rl_coin_record(self) -> Optional[WalletCoinRecord]:
if self.rl_info.rl_puzzle_hash is None:
return None
rl_coins = await self.wallet_state_manager.coin_store.get_coin_records_by_puzzle_hash(
self.rl_info.rl_puzzle_hash
)
for coin_record in rl_coins:
if coin_record.spent is False:
return coin_record
return None
async def _get_rl_parent(self) -> Optional[Coin]:
self.rl_coin_record = await self._get_rl_coin_record()
if not self.rl_coin_record:
return None
rl_parent_id = self.rl_coin_record.coin.parent_coin_info
if rl_parent_id == self.rl_info.rl_origin_id:
return self.rl_info.rl_origin
rl_parent = await self.wallet_state_manager.coin_store.get_coin_record(rl_parent_id)
if rl_parent is None:
return None
return rl_parent.coin
async def rl_generate_unsigned_transaction(self, to_puzzlehash, amount, fee) -> List[CoinSpend]:
spends = []
assert self.rl_coin_record is not None
coin = self.rl_coin_record.coin
puzzle_hash = coin.puzzle_hash
pubkey = self.rl_info.user_pubkey
rl_parent: Optional[Coin] = await self._get_rl_parent()
if rl_parent is None:
raise ValueError("No RL parent coin")
# these lines make mypy happy
assert pubkey is not None
assert self.rl_info.limit is not None
assert self.rl_info.interval is not None
assert self.rl_info.rl_origin_id is not None
assert self.rl_info.admin_pubkey is not None
puzzle = rl_puzzle_for_pk(
bytes(pubkey),
self.rl_info.limit,
self.rl_info.interval,
self.rl_info.rl_origin_id,
self.rl_info.admin_pubkey,
)
solution = solution_for_rl(
coin.parent_coin_info,
puzzle_hash,
uint64(coin.amount),
to_puzzlehash,
amount,
rl_parent.parent_coin_info,
uint64(rl_parent.amount),
self.rl_info.interval,
self.rl_info.limit,
fee,
)
spends.append(CoinSpend(coin, puzzle, solution))
return spends
async def generate_signed_transaction(
self, amount, to_puzzle_hash, fee: uint64 = uint64(0), memo: Optional[List[bytes]] = None
) -> TransactionRecord:
self.rl_coin_record = await self._get_rl_coin_record()
if not self.rl_coin_record:
raise ValueError("No unspent coin (zero balance)")
if amount > self.rl_coin_record.coin.amount:
raise ValueError(f"Coin value not sufficient: {amount} > {self.rl_coin_record.coin.amount}")
transaction = await self.rl_generate_unsigned_transaction(to_puzzle_hash, amount, fee)
spend_bundle = await self.rl_sign_transaction(transaction)
return TransactionRecord(
confirmed_at_height=uint32(0),
created_at_time=uint64(int(time.time())),
to_puzzle_hash=to_puzzle_hash,
amount=uint64(amount),
fee_amount=uint64(0),
confirmed=False,
sent=uint32(0),
spend_bundle=spend_bundle,
additions=spend_bundle.additions(),
removals=spend_bundle.removals(),
wallet_id=self.id(),
sent_to=[],
trade_id=None,
type=uint32(TransactionType.OUTGOING_TX.value),
name=spend_bundle.name(),
memos=list(compute_memos(spend_bundle).items()),
)
async def rl_sign_transaction(self, spends: List[CoinSpend]) -> SpendBundle:
sigs = []
for coin_spend in spends:
pubkey, secretkey = await self.get_keys(coin_spend.coin.puzzle_hash)
signature = AugSchemeMPL.sign(secretkey, coin_spend.solution.get_tree_hash())
sigs.append(signature)
aggsig = AugSchemeMPL.aggregate(sigs)
return SpendBundle(spends, aggsig)
def generate_unsigned_clawback_transaction(self, clawback_coin: Coin, clawback_puzzle_hash: bytes32, fee):
if (
self.rl_info.limit is None
or self.rl_info.interval is None
or self.rl_info.user_pubkey is None
or self.rl_info.admin_pubkey is None
):
raise ValueError("One ore more of the elements of rl_info is None")
spends = []
coin = clawback_coin
if self.rl_info.rl_origin is None:
raise ValueError("Origin not initialized")
puzzle = rl_puzzle_for_pk(
self.rl_info.user_pubkey,
self.rl_info.limit,
self.rl_info.interval,
self.rl_info.rl_origin.name(),
self.rl_info.admin_pubkey,
)
solution = make_clawback_solution(clawback_puzzle_hash, clawback_coin.amount, fee)
spends.append((puzzle, CoinSpend(coin, puzzle, solution)))
return spends
async def sign_clawback_transaction(self, spends: List[Tuple[Program, CoinSpend]], clawback_pubkey) -> SpendBundle:
sigs = []
for puzzle, solution in spends:
pubkey, secretkey = await self.get_keys_pk(clawback_pubkey)
signature = AugSchemeMPL.sign(secretkey, solution.solution.get_tree_hash())
sigs.append(signature)
aggsig = AugSchemeMPL.aggregate(sigs)
solution_list = []
for puzzle, coin_spend in spends:
solution_list.append(coin_spend)
return SpendBundle(solution_list, aggsig)
async def clawback_rl_coin(self, clawback_puzzle_hash: bytes32, fee) -> SpendBundle:
rl_coin = await self._get_rl_coin()
if rl_coin is None:
raise ValueError("rl_coin is None")
transaction = self.generate_unsigned_clawback_transaction(rl_coin, clawback_puzzle_hash, fee)
return await self.sign_clawback_transaction(transaction, self.rl_info.admin_pubkey)
async def clawback_rl_coin_transaction(self, fee) -> TransactionRecord:
to_puzzle_hash = await self.main_wallet.get_new_puzzlehash()
spend_bundle = await self.clawback_rl_coin(to_puzzle_hash, fee)
return TransactionRecord(
confirmed_at_height=uint32(0),
created_at_time=uint64(int(time.time())),
to_puzzle_hash=to_puzzle_hash,
amount=uint64(0),
fee_amount=fee,
confirmed=False,
sent=uint32(0),
spend_bundle=spend_bundle,
additions=spend_bundle.additions(),
removals=spend_bundle.removals(),
wallet_id=self.id(),
sent_to=[],
trade_id=None,
type=uint32(TransactionType.OUTGOING_TX.value),
name=spend_bundle.name(),
memos=list(compute_memos(spend_bundle).items()),
)
# This is for using the AC locked coin and aggregating it into wallet - must happen in same block as RL Mode 2
async def rl_generate_signed_aggregation_transaction(self, rl_info, consolidating_coin, rl_parent, rl_coin):
if (
rl_info.limit is None
or rl_info.interval is None
or rl_info.user_pubkey is None
or rl_info.admin_pubkey is None
):
raise ValueError("One or more of the elements of rl_info is None")
if self.rl_coin_record is None:
raise ValueError("Rl coin record is None")
list_of_coin_spends = []
self.rl_coin_record = await self._get_rl_coin_record()
pubkey, secretkey = await self.get_keys(self.rl_coin_record.coin.puzzle_hash)
# Spend wallet coin
puzzle = rl_puzzle_for_pk(
rl_info.user_pubkey,
rl_info.limit,
rl_info.interval,
rl_info.rl_origin_id,
rl_info.admin_pubkey,
)
solution = rl_make_solution_mode_2(
rl_coin.puzzle_hash,
consolidating_coin.parent_coin_info,
consolidating_coin.puzzle_hash,
consolidating_coin.amount,
rl_coin.parent_coin_info,
rl_coin.amount,
rl_parent.amount,
rl_parent.parent_coin_info,
)
signature = AugSchemeMPL.sign(secretkey, solution.get_tree_hash())
rl_spend = CoinSpend(self.rl_coin_record.coin, puzzle, solution)
list_of_coin_spends.append(rl_spend)
# Spend consolidating coin
puzzle = rl_make_aggregation_puzzle(self.rl_coin_record.coin.puzzle_hash)
solution = rl_make_aggregation_solution(
consolidating_coin.name(),
self.rl_coin_record.coin.parent_coin_info,
self.rl_coin_record.coin.amount,
)
agg_spend = CoinSpend(consolidating_coin, puzzle, solution)
list_of_coin_spends.append(agg_spend)
aggsig = AugSchemeMPL.aggregate([signature])
return SpendBundle(list_of_coin_spends, aggsig)
def rl_get_aggregation_puzzlehash(self, wallet_puzzle):
puzzle_hash = rl_make_aggregation_puzzle(wallet_puzzle).get_tree_hash()
return puzzle_hash
async def rl_add_funds(self, amount, puzzle_hash, fee):
spend_bundle = await self.main_wallet.generate_signed_transaction(amount, puzzle_hash, fee)
if spend_bundle is None:
return False
await self.main_wallet.push_transaction(spend_bundle)
async def push_transaction(self, tx: TransactionRecord) -> None:
"""Use this API to send transactions."""
await self.wallet_state_manager.add_pending_transaction(tx)

View File

@ -1,124 +0,0 @@
import math
from binascii import hexlify
from clvm_tools import binutils
from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.condition_opcodes import ConditionOpcode
from chia.util.ints import uint64
from chia.wallet.chialisp import sexp
from chia.wallet.puzzles.load_clvm import load_clvm
RATE_LIMITED_MODE = 1
AGGREGATION_MODE = 2
CLAWBACK_MODE = 3
def rl_puzzle_for_pk(
pubkey: bytes,
rate_amount: uint64,
interval_time: uint64,
origin_id: bytes32,
clawback_pk: bytes,
):
"""
Solution to this puzzle must be in format:
(1 my_parent_id, my_puzzlehash, my_amount, outgoing_puzzle_hash, outgoing_amount,
min_block_time, parent_parent_id, parent_amount, fee)
RATE LIMIT LOGIC:
M - chia_per_interval
N - interval_blocks
V - amount being spent
MIN_BLOCK_AGE = V / (M / N)
if not (min_block_age * M >= V * N) do X (raise)
ASSERT_COIN_BLOCK_AGE_EXCEEDS min_block_age
"""
MOD = load_clvm("rl.clvm")
return MOD.curry(pubkey, rate_amount, interval_time, origin_id, clawback_pk)
def rl_make_aggregation_solution(myid, wallet_coin_primary_input, wallet_coin_amount):
opcode_myid = "0x" + hexlify(myid).decode("ascii")
primary_input = "0x" + hexlify(wallet_coin_primary_input).decode("ascii")
sol = sexp(opcode_myid, primary_input, wallet_coin_amount)
return Program.to(binutils.assemble(sol))
def make_clawback_solution(puzzlehash, amount, fee):
opcode_create = hexlify(ConditionOpcode.CREATE_COIN).decode("ascii")
solution = sexp(CLAWBACK_MODE, sexp("0x" + opcode_create, "0x" + str(puzzlehash), amount - fee))
return Program.to(binutils.assemble(solution))
def rl_make_solution_mode_2(
my_puzzle_hash,
consolidating_primary_input,
consolidating_coin_puzzle_hash,
outgoing_amount,
my_primary_input,
incoming_amount,
parent_amount,
my_parent_parent_id,
):
my_puzzle_hash = hexlify(my_puzzle_hash).decode("ascii")
consolidating_primary_input = hexlify(consolidating_primary_input).decode("ascii")
consolidating_coin_puzzle_hash = hexlify(consolidating_coin_puzzle_hash).decode("ascii")
primary_input = hexlify(my_primary_input).decode("ascii")
sol = sexp(
AGGREGATION_MODE,
"0x" + my_puzzle_hash,
"0x" + consolidating_primary_input,
"0x" + consolidating_coin_puzzle_hash,
outgoing_amount,
"0x" + primary_input,
incoming_amount,
parent_amount,
"0x" + str(my_parent_parent_id),
)
return Program.to(binutils.assemble(sol))
def solution_for_rl(
my_parent_id: bytes32,
my_puzzlehash: bytes32,
my_amount: uint64,
out_puzzlehash: bytes32,
out_amount: uint64,
my_parent_parent_id: bytes32,
parent_amount: uint64,
interval,
limit,
fee,
):
"""
Solution is (1 my_parent_id, my_puzzlehash, my_amount, outgoing_puzzle_hash, outgoing_amount,
min_block_time, parent_parent_id, parent_amount, fee)
min block time = Math.ceil((new_amount * self.interval) / self.limit)
"""
min_block_count = math.ceil((out_amount * interval) / limit)
solution = sexp(
RATE_LIMITED_MODE,
"0x" + my_parent_id.hex(),
"0x" + my_puzzlehash.hex(),
my_amount,
"0x" + out_puzzlehash.hex(),
out_amount,
min_block_count,
"0x" + my_parent_parent_id.hex(),
parent_amount,
fee,
)
return Program.to(binutils.assemble(solution))
def rl_make_aggregation_puzzle(wallet_puzzle):
"""
If Wallet A wants to send further funds to Wallet B then they can lock them up using this code
Solution will be (my_id wallet_coin_primary_input wallet_coin_amount)
"""
MOD = load_clvm("rl_aggregation.clvm")
return MOD.curry(wallet_puzzle)

View File

@ -10,7 +10,6 @@ from chia.util.ints import uint64
class WalletType(IntEnum):
# Wallet Types
STANDARD_WALLET = 0
RATE_LIMITED = 1
ATOMIC_SWAP = 2
AUTHORIZED_PAYEE = 3
MULTI_SIG = 4

View File

@ -58,7 +58,6 @@ from chia.wallet.nft_wallet.uncurry_nft import UncurriedNFT
from chia.wallet.outer_puzzles import AssetType
from chia.wallet.puzzle_drivers import PuzzleInfo
from chia.wallet.puzzles.cat_loader import CAT_MOD, CAT_MOD_HASH
from chia.wallet.rl_wallet.rl_wallet import RLWallet
from chia.wallet.settings.user_settings import UserSettings
from chia.wallet.trade_manager import TradeManager
from chia.wallet.transaction_record import TransactionRecord
@ -227,8 +226,6 @@ class WalletStateManager:
self.main_wallet,
wallet_info,
)
elif wallet_info.type == WalletType.RATE_LIMITED:
wallet = await RLWallet.create(self, wallet_info)
elif wallet_info.type == WalletType.DECENTRALIZED_ID:
wallet = await DIDWallet.create(
self,

File diff suppressed because one or more lines are too long

View File

@ -111,7 +111,6 @@ kwargs = dict(
"chia.wallet",
"chia.wallet.db_wallet",
"chia.wallet.puzzles",
"chia.wallet.rl_wallet",
"chia.wallet.cat_wallet",
"chia.wallet.did_wallet",
"chia.wallet.nft_wallet",

View File

@ -27,11 +27,11 @@ from chia.util.byte_types import hexstr_to_bytes
from chia.util.config import save_config
from chia.util.ints import uint16, uint32
from chia.wallet.trading.offer import Offer as TradingOffer
from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.wallet import Wallet
from chia.wallet.wallet_node import WalletNode
from tests.setup_nodes import setup_simulators_and_wallets
from tests.util.wallet_is_synced import wallet_is_synced
from tests.wallet.rl_wallet.test_rl_rpc import is_transaction_confirmed
pytestmark = pytest.mark.data_layer
nodes = Tuple[WalletNode, FullNodeSimulator]
@ -148,6 +148,15 @@ async def farm_block_check_singelton(
await time_out_assert(10, check_singleton_confirmed, True, data_layer, store_id)
async def is_transaction_confirmed(user_wallet_id: uint32, api: WalletRpcApi, tx_id: bytes32) -> bool:
try:
val = await api.get_transaction({"wallet_id": user_wallet_id, "transaction_id": tx_id.hex()})
except ValueError:
return False
return True if TransactionRecord.from_json_dict_convenience(val["transaction"]).confirmed else False # mypy
async def farm_block_with_spend(
full_node_api: FullNodeSimulator, ph: bytes32, tx_rec: bytes32, wallet_rpc_api: WalletRpcApi
) -> None:

View File

@ -1,174 +0,0 @@
import asyncio
import pytest
from chia.rpc.wallet_rpc_api import WalletRpcApi
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
from chia.simulator.time_out_assert import time_out_assert
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
from chia.types.peer_info import PeerInfo
from chia.util.bech32m import encode_puzzle_hash
from chia.util.ints import uint16
from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.util.wallet_types import WalletType
from tests.wallet.sync.test_wallet_sync import wallet_height_at_least
async def is_transaction_in_mempool(user_wallet_id, api, tx_id: bytes32) -> bool:
try:
val = await api.get_transaction({"wallet_id": user_wallet_id, "transaction_id": tx_id.hex()})
except ValueError:
return False
for _, mis, _ in TransactionRecord.from_json_dict_convenience(val["transaction"]).sent_to:
if (
MempoolInclusionStatus(mis) == MempoolInclusionStatus.SUCCESS
or MempoolInclusionStatus(mis) == MempoolInclusionStatus.PENDING
):
return True
return False
async def is_transaction_confirmed(user_wallet_id, api, tx_id: bytes32) -> bool:
try:
val = await api.get_transaction({"wallet_id": user_wallet_id, "transaction_id": tx_id.hex()})
except ValueError:
return False
return TransactionRecord.from_json_dict_convenience(val["transaction"]).confirmed
async def check_balance(api, wallet_id):
balance_response = await api.get_wallet_balance({"wallet_id": wallet_id})
balance = balance_response["wallet_balance"]["confirmed_wallet_balance"]
return balance
class TestRLWallet:
@pytest.mark.asyncio
@pytest.mark.skip
async def test_create_rl_coin(self, three_wallet_nodes, self_hostname):
num_blocks = 4
full_nodes, wallets, _ = three_wallet_nodes
full_node_api = full_nodes[0]
full_node_server = full_node_api.server
wallet_node, server_2 = wallets[0]
wallet_node_1, wallet_server_1 = wallets[1]
wallet_node_2, wallet_server_2 = wallets[2]
wallet = wallet_node.wallet_state_manager.main_wallet
ph = await wallet.get_new_puzzlehash()
await server_2.start_client(PeerInfo(self_hostname, uint16(full_node_server._port)), None)
await wallet_server_1.start_client(PeerInfo(self_hostname, uint16(full_node_server._port)), None)
await wallet_server_2.start_client(PeerInfo(self_hostname, uint16(full_node_server._port)), None)
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
for i in range(0, num_blocks + 1):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
await time_out_assert(15, wallet_height_at_least, True, wallet_node, 6)
fund_owners_initial_balance = await wallet.get_confirmed_balance()
api_user = WalletRpcApi(wallet_node_1)
val = await api_user.create_new_wallet(
{"wallet_type": "rl_wallet", "rl_type": "user", "host": f"{self_hostname}:5000"}
)
await asyncio.sleep(2)
assert isinstance(val, dict)
if "success" in val:
assert val["success"]
assert val["id"]
assert val["type"] == WalletType.RATE_LIMITED.value
user_wallet_id = val["id"]
pubkey = val["pubkey"]
api_admin = WalletRpcApi(wallet_node)
val = await api_admin.create_new_wallet(
{
"wallet_type": "rl_wallet",
"rl_type": "admin",
"interval": 2,
"limit": 10,
"pubkey": pubkey,
"amount": 100,
"fee": 1,
"host": f"{self_hostname}:5000",
}
)
assert isinstance(val, dict)
if "success" in val:
assert val["success"]
assert val["id"]
assert val["type"] == WalletType.RATE_LIMITED.value
assert val["origin"]
assert val["pubkey"]
admin_wallet_id = val["id"]
admin_pubkey = val["pubkey"]
origin: Coin = val["origin"]
await asyncio.sleep(2)
await api_user.rl_set_user_info(
{
"wallet_id": user_wallet_id,
"interval": 2,
"limit": 10,
"origin": {
"parent_coin_info": origin.parent_coin_info.hex(),
"puzzle_hash": origin.puzzle_hash.hex(),
"amount": origin.amount,
},
"admin_pubkey": admin_pubkey,
}
)
await asyncio.sleep(2)
assert (await api_user.get_wallet_balance({"wallet_id": user_wallet_id}))["wallet_balance"][
"confirmed_wallet_balance"
] == 0
for i in range(0, 2 * num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
await time_out_assert(15, wallet_height_at_least, True, wallet_node, 14)
assert await wallet.get_confirmed_balance() == fund_owners_initial_balance - 101
assert await check_balance(api_user, user_wallet_id) == 100
receiving_wallet = wallet_node_2.wallet_state_manager.main_wallet
address = encode_puzzle_hash(await receiving_wallet.get_new_puzzlehash(), "xch")
assert await receiving_wallet.get_spendable_balance() == 0
val = await api_user.send_transaction({"wallet_id": user_wallet_id, "amount": 3, "fee": 2, "address": address})
await asyncio.sleep(2)
assert "transaction_id" in val
await time_out_assert(15, is_transaction_in_mempool, True, user_wallet_id, api_user, val["transaction_id"])
for i in range(0, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
await time_out_assert(15, wallet_height_at_least, True, wallet_node, 18)
assert await is_transaction_confirmed(user_wallet_id, api_user, val["transaction_id"])
assert await check_balance(api_user, user_wallet_id) == 95
assert await receiving_wallet.get_spendable_balance() == 3
val = await api_admin.add_rate_limited_funds({"wallet_id": admin_wallet_id, "amount": 100, "fee": 7})
assert val["status"] == "SUCCESS"
await asyncio.sleep(2)
for i in range(0, 50):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
await time_out_assert(15, wallet_height_at_least, True, wallet_node, 68)
assert await check_balance(api_user, user_wallet_id) == 195
# test spending
puzzle_hash = encode_puzzle_hash(await receiving_wallet.get_new_puzzlehash(), "xch")
val = await api_user.send_transaction(
{"wallet_id": user_wallet_id, "amount": 105, "fee": 0, "address": puzzle_hash}
)
await asyncio.sleep(2)
await time_out_assert(15, is_transaction_in_mempool, True, user_wallet_id, api_user, val["transaction_id"])
for i in range(0, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
await time_out_assert(15, wallet_height_at_least, True, wallet_node, 72)
assert await is_transaction_confirmed(user_wallet_id, api_user, val["transaction_id"])
assert await check_balance(api_user, user_wallet_id) == 90
assert await receiving_wallet.get_spendable_balance() == 108
val = await api_admin.send_clawback_transaction({"wallet_id": admin_wallet_id, "fee": 11})
await asyncio.sleep(2)
await time_out_assert(15, is_transaction_in_mempool, True, user_wallet_id, api_admin, val["transaction_id"])
for i in range(0, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
await time_out_assert(15, wallet_height_at_least, True, wallet_node, 76)
assert await is_transaction_confirmed(user_wallet_id, api_admin, val["transaction_id"])
assert await check_balance(api_user, user_wallet_id) == 0
final_balance = await wallet.get_confirmed_balance()
assert final_balance == fund_owners_initial_balance - 129

View File

@ -1,72 +0,0 @@
import pytest
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
from chia.simulator.time_out_assert import time_out_assert
from chia.types.peer_info import PeerInfo
from chia.util.ints import uint16, uint64
from chia.wallet.rl_wallet.rl_wallet import RLWallet
class TestCATWallet:
@pytest.mark.asyncio
@pytest.mark.skip
async def test_create_rl_coin(self, two_wallet_nodes, self_hostname):
num_blocks = 4
full_nodes, wallets, _ = two_wallet_nodes
full_node_api = full_nodes[0]
full_node_server = full_node_api.server
wallet_node, server_2 = wallets[0]
wallet_node_1, wallet_server_1 = wallets[1]
wallet = wallet_node.wallet_state_manager.main_wallet
ph = await wallet.get_new_puzzlehash()
await server_2.start_client(PeerInfo(self_hostname, uint16(full_node_server._port)), None)
await wallet_server_1.start_client(PeerInfo(self_hostname, uint16(full_node_server._port)), None)
for i in range(0, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
rl_admin: RLWallet = await RLWallet.create_rl_admin(wallet_node.wallet_state_manager)
rl_user: RLWallet = await RLWallet.create_rl_user(wallet_node_1.wallet_state_manager)
interval = uint64(2)
limit = uint64(1)
amount = uint64(100)
await rl_admin.admin_create_coin(interval, limit, rl_user.rl_info.user_pubkey.hex(), amount, 0)
origin = rl_admin.rl_info.rl_origin
admin_pubkey = rl_admin.rl_info.admin_pubkey
await rl_user.set_user_info(
interval,
limit,
origin.parent_coin_info.hex(),
origin.puzzle_hash.hex(),
origin.amount,
admin_pubkey.hex(),
)
for i in range(0, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
for i in range(0, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
await time_out_assert(15, rl_user.get_confirmed_balance, 100)
balance = await rl_user.rl_available_balance()
tx_record = await rl_user.generate_signed_transaction(1, 32 * b"\0")
await wallet_node_1.wallet_state_manager.main_wallet.push_transaction(tx_record)
for i in range(0, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"\0"))
balance = await rl_user.get_confirmed_balance()
print(balance)
await time_out_assert(15, rl_user.get_confirmed_balance, 99)
rl_user.rl_get_aggregation_puzzlehash(rl_user.get_new_puzzle())
# rl_admin.rl_generate_signed_aggregation_transaction()

View File

@ -37,7 +37,7 @@ class TestPuzzleStore:
uint32(i),
token_bytes(32),
AugSchemeMPL.key_gen(token_bytes(32)).get_g1(),
WalletType.RATE_LIMITED,
WalletType.CAT,
uint32(2),
False,
)