mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-21 00:24:37 +03:00
New RPC block_spends - Get spends for block using transaction generator (#12062)
* New RPC block_spends - Get spends for block using transaction generator (#10446) * get spends for block using transaction generator * type annotations and use existing function * return None on exception * parse to CoinSpend * specify return type of get_block_spends in rpc client * see what can be asserted * test fix * flags not necessary as we don't validate * simplifying as cost is not required as we are not validating * improve test to cover transaction generator ref_list * simplify test * remove unused import * slight change and cleanup * lint cleanup * clean up * wait until transaction is in mempool * fix lint Co-authored-by: Jack Nelson <jack@jacknelson.xyz> * fix lint Correct type and run lint checker * switch to custom clvm * add new puzzle * curry and make better * forgot to set linter to tools dir * undo run_block changes * Revert "curry and make better" The tests do not like it * correct tests Co-authored-by: Jack Nelson <jack@jacknelson.xyz>
This commit is contained in:
parent
ebe8857083
commit
72c8ba4a29
@ -1,10 +1,14 @@
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from clvm.casts import int_from_bytes
|
||||
|
||||
from chia.consensus.block_record import BlockRecord
|
||||
from chia.consensus.pos_quality import UI_ACTUAL_SPACE_CONSTANT_FACTOR
|
||||
from chia.full_node.full_node import FullNode
|
||||
from chia.full_node.generator import setup_generator_args
|
||||
from chia.full_node.mempool_check_conditions import get_puzzle_and_solution_for_coin
|
||||
from chia.rpc.rpc_server import Endpoint, EndpointResult
|
||||
from chia.types.blockchain_format.coin import Coin
|
||||
from chia.types.blockchain_format.program import Program, SerializedProgram
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.types.coin_record import CoinRecord
|
||||
@ -18,6 +22,7 @@ from chia.util.byte_types import hexstr_to_bytes
|
||||
from chia.util.ints import uint32, uint64, uint128
|
||||
from chia.util.log_exceptions import log_exceptions
|
||||
from chia.util.ws_message import WsRpcMessage, create_payload_dict
|
||||
from chia.wallet.puzzles.decompress_block_spends import DECOMPRESS_BLOCK_SPENDS
|
||||
|
||||
|
||||
def coin_record_dict_backwards_compat(coin_record: Dict[str, Any]):
|
||||
@ -41,6 +46,7 @@ class FullNodeRpcApi:
|
||||
"/get_block_record_by_height": self.get_block_record_by_height,
|
||||
"/get_block_record": self.get_block_record,
|
||||
"/get_block_records": self.get_block_records,
|
||||
"/get_block_spends": self.get_block_spends,
|
||||
"/get_unfinished_block_headers": self.get_unfinished_block_headers,
|
||||
"/get_network_space": self.get_network_space,
|
||||
"/get_additions_and_removals": self.get_additions_and_removals,
|
||||
@ -399,6 +405,32 @@ class FullNodeRpcApi:
|
||||
records.append(record)
|
||||
return {"block_records": records}
|
||||
|
||||
async def get_block_spends(self, request: Dict) -> EndpointResult:
|
||||
if "header_hash" not in request:
|
||||
raise ValueError("No header_hash in request")
|
||||
header_hash = bytes32.from_hexstr(request["header_hash"])
|
||||
full_block: Optional[FullBlock] = await self.service.block_store.get_full_block(header_hash)
|
||||
if full_block is None or full_block.transactions_generator is None:
|
||||
raise ValueError(f"Block {header_hash.hex()} not found or invalid block generator")
|
||||
|
||||
spends: List[CoinSpend] = []
|
||||
block_generator = await self.service.blockchain.get_block_generator(full_block)
|
||||
if block_generator is None:
|
||||
return {"block_spends": spends}
|
||||
|
||||
block_program, block_program_args = setup_generator_args(block_generator)
|
||||
_, coin_spends = DECOMPRESS_BLOCK_SPENDS.run_with_cost(
|
||||
self.service.constants.MAX_BLOCK_COST_CLVM, block_program, block_program_args
|
||||
)
|
||||
|
||||
for spend in coin_spends.as_iter():
|
||||
parent, puzzle, amount, solution = spend.as_iter()
|
||||
puzzle_hash = puzzle.get_tree_hash()
|
||||
coin = Coin(parent.atom, puzzle_hash, int_from_bytes(amount.atom))
|
||||
spends.append(CoinSpend(coin, puzzle, solution))
|
||||
|
||||
return {"block_spends": spends}
|
||||
|
||||
async def get_block_record_by_height(self, request: Dict) -> EndpointResult:
|
||||
if "height" not in request:
|
||||
raise ValueError("No height in request")
|
||||
|
@ -200,6 +200,16 @@ class FullNodeRpcClient(RpcClient):
|
||||
# TODO: return block records
|
||||
return response["block_records"]
|
||||
|
||||
async def get_block_spends(self, header_hash: bytes32) -> Optional[List[CoinSpend]]:
|
||||
try:
|
||||
response = await self.fetch("get_block_spends", {"header_hash": header_hash.hex()})
|
||||
block_spends = []
|
||||
for block_spend in response["block_spends"]:
|
||||
block_spends.append(CoinSpend.from_json_dict(block_spend))
|
||||
return block_spends
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
async def push_tx(self, spend_bundle: SpendBundle):
|
||||
return await self.fetch("push_tx", {"spend_bundle": spend_bundle.to_json_dict()})
|
||||
|
||||
|
16
chia/wallet/puzzles/decompress_block_spends.clvm
Normal file
16
chia/wallet/puzzles/decompress_block_spends.clvm
Normal file
@ -0,0 +1,16 @@
|
||||
(mod (block_program (block_ref))
|
||||
|
||||
(defconstant local_deserialize_mod
|
||||
;; this monstrosity is the assembly output of `chialisp_deserialisation.clvm`
|
||||
;; it's pasted in here because the compiler doesn't yet support nested `mod`
|
||||
;; my apologies -- RK
|
||||
|
||||
(a (q 5 (a 62 (c 2 (c 5 ()))))
|
||||
(c (q ((-1 . 127) -33 . -65) ((a (i (= 11 (q . -128)) (q 4 () (c 5 ())) (q 2 (i (>s 11 24) (q 2 26 (c 2 (c (a (i (>s 11 28) (q 2 (i (>s 11 20) (q 8) (q 4 (concat (logand (q . 31) 11) (substr 5 () (q . 1))) (c (substr 5 (q . 1)) ()))) 1) (q 4 (logand (q . 63) 11) (c 5 ()))) 1) ()))) (q 4 11 (c 5 ()))) 1)) 1) 4 (substr 21 () 9) (c (substr 21 9) ())) (c (c 5 19) (c 43 ())) (a 22 (c 2 (c 9 (c (a 62 (c 2 (c 21 ()))) ())))) 2 (i (= (substr 5 () (q . 1)) 16) (q 2 46 (c 2 (c (a 62 (c 2 (c (substr 5 (q . 1)) ()))) ()))) (q 2 18 (c 2 (c (substr 5 (q . 1)) (c (substr 5 () (q . 1)) ()))))) 1)
|
||||
1))
|
||||
)
|
||||
|
||||
; main
|
||||
; select the first value, and return decompressed block spends.
|
||||
(f (a block_program (list local_deserialize_mod block_ref)))
|
||||
)
|
1
chia/wallet/puzzles/decompress_block_spends.clvm.hex
Normal file
1
chia/wallet/puzzles/decompress_block_spends.clvm.hex
Normal file
@ -0,0 +1 @@
|
||||
ff02ffff01ff05ffff02ff05ffff04ff02ffff04ff13ff8080808080ffff04ffff01ff02ffff01ff05ffff02ff3effff04ff02ffff04ff05ff8080808080ffff04ffff01ffffff81ff7fff81df81bfffffff02ffff03ffff09ff0bffff01818080ffff01ff04ff80ffff04ff05ff808080ffff01ff02ffff03ffff0aff0bff1880ffff01ff02ff1affff04ff02ffff04ffff02ffff03ffff0aff0bff1c80ffff01ff02ffff03ffff0aff0bff1480ffff01ff0880ffff01ff04ffff0effff18ffff011fff0b80ffff0cff05ff80ffff01018080ffff04ffff0cff05ffff010180ff80808080ff0180ffff01ff04ffff18ffff013fff0b80ffff04ff05ff80808080ff0180ff80808080ffff01ff04ff0bffff04ff05ff80808080ff018080ff0180ff04ffff0cff15ff80ff0980ffff04ffff0cff15ff0980ff808080ffff04ffff04ff05ff1380ffff04ff2bff808080ffff02ff16ffff04ff02ffff04ff09ffff04ffff02ff3effff04ff02ffff04ff15ff80808080ff8080808080ff02ffff03ffff09ffff0cff05ff80ffff010180ff1080ffff01ff02ff2effff04ff02ffff04ffff02ff3effff04ff02ffff04ffff0cff05ffff010180ff80808080ff80808080ffff01ff02ff12ffff04ff02ffff04ffff0cff05ffff010180ffff04ffff0cff05ff80ffff010180ff808080808080ff0180ff018080ff018080
|
@ -0,0 +1 @@
|
||||
f890a7866079009517ae0b652d530268d2531bbac99670aaba461d604bc0ff0c
|
3
chia/wallet/puzzles/decompress_block_spends.py
Normal file
3
chia/wallet/puzzles/decompress_block_spends.py
Normal file
@ -0,0 +1,3 @@
|
||||
from chia.wallet.puzzles.load_clvm import load_serialized_clvm
|
||||
|
||||
DECOMPRESS_BLOCK_SPENDS = load_serialized_clvm("decompress_block_spends.clvm", package_or_requirement=__name__)
|
@ -46,6 +46,7 @@ wallet_program_files = set(
|
||||
"chia/wallet/puzzles/nft_state_layer.clvm",
|
||||
"chia/wallet/puzzles/nft_ownership_layer.clvm",
|
||||
"chia/wallet/puzzles/nft_ownership_transfer_program_one_way_claim_with_royalties.clvm",
|
||||
"chia/wallet/puzzles/decompress_block_spends.clvm",
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -187,6 +187,31 @@ class TestRpc:
|
||||
assert len(await client.get_coin_records_by_puzzle_hash(ph, True, 0, blocks[-1].height + 1)) == 2
|
||||
assert len(await client.get_coin_records_by_puzzle_hash(ph, True, 0, 1)) == 0
|
||||
|
||||
coin_records = await client.get_coin_records_by_puzzle_hash(ph, False)
|
||||
|
||||
coin_spends = []
|
||||
|
||||
# Spend 3 coins using standard transaction
|
||||
for i in range(3):
|
||||
spend_bundle = wallet.generate_signed_transaction(
|
||||
coin_records[i].coin.amount, ph_receiver, coin_records[i].coin
|
||||
)
|
||||
await client.push_tx(spend_bundle)
|
||||
coin_spends = coin_spends + spend_bundle.coin_spends
|
||||
await time_out_assert(
|
||||
5, full_node_api_1.full_node.mempool_manager.get_spendbundle, spend_bundle, spend_bundle.name()
|
||||
)
|
||||
|
||||
await full_node_api_1.farm_new_transaction_block(FarmNewBlockProtocol(ph_2))
|
||||
block: FullBlock = (await full_node_api_1.get_all_full_blocks())[-1]
|
||||
|
||||
assert len(block.transactions_generator_ref_list) > 0 # compression has occurred
|
||||
|
||||
block_spends = await client.get_block_spends(block.header_hash)
|
||||
|
||||
assert len(block_spends) == 3
|
||||
assert block_spends == coin_spends
|
||||
|
||||
memo = 32 * b"\f"
|
||||
|
||||
for i in range(2):
|
||||
|
Loading…
Reference in New Issue
Block a user