mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-20 16:08:51 +03:00
Add Announcements (#881)
* cherrypicked add announcement opcodes * remove unused import in test_mempool * fixed broken test * fix black superlint errors * simple convert of coloured coins to announcements announcements message now type bytes * removed assert coin consumed * updated RL higher level puzzle * fix RL wallet to use announcement instead of lock * rebase add announcement to blockchain_check_conditions * lint and re-enable tests * remove lingering reference to coin_consumed * fixed bug with block validation * fix RL wallet * -update CC to use more simple announcement message * lint fix for announcement class * remove breakpoint improve announcement set checking delete TODO * deleted commented out code use .extend() instead of nested for * fix mempool tests and aesthetic changes for richard * fix mempool and add to debug_spend_bundle * fixed test_blockchain_transactions * flake * update test_mempool annouce tests to use height not subheight Co-authored-by: Yostra <straya@chia.net>
This commit is contained in:
parent
9d064fea4a
commit
b37e6e767b
@ -21,9 +21,10 @@ from src.consensus.cost_calculator import calculate_cost_of_program, CostResult
|
||||
from src.consensus.sub_block_record import SubBlockRecord
|
||||
from src.types.coin import Coin
|
||||
from src.types.coin_record import CoinRecord
|
||||
from src.types.announcement import Announcement
|
||||
from src.types.condition_opcodes import ConditionOpcode
|
||||
from src.types.condition_var_pair import ConditionVarPair
|
||||
from src.types.full_block import FullBlock, additions_for_npc
|
||||
from src.types.full_block import FullBlock, additions_for_npc, announcements_for_npc
|
||||
from src.types.name_puzzle_condition import NPC
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.types.unfinished_block import UnfinishedBlock
|
||||
@ -141,6 +142,7 @@ async def validate_block_body(
|
||||
removals: List[bytes32] = []
|
||||
coinbase_additions: List[Coin] = list(expected_reward_coins)
|
||||
additions: List[Coin] = []
|
||||
announcements: List[Announcement] = []
|
||||
npc_list: List[NPC] = []
|
||||
removals_puzzle_dic: Dict[bytes32, bytes32] = {}
|
||||
cost: uint64 = uint64(0)
|
||||
@ -168,6 +170,7 @@ async def validate_block_body(
|
||||
removals_puzzle_dic[npc.coin_name] = npc.puzzle_hash
|
||||
|
||||
additions = additions_for_npc(npc_list)
|
||||
announcements = announcements_for_npc(npc_list)
|
||||
|
||||
# 9. Check that the correct cost is in the transactions info
|
||||
if block.transactions_info.cost != cost:
|
||||
@ -342,10 +345,12 @@ async def validate_block_body(
|
||||
pairs_pks = []
|
||||
pairs_msgs = []
|
||||
for npc in npc_list:
|
||||
unspent = removal_coin_records[npc.coin_name]
|
||||
assert height is not None
|
||||
unspent = removal_coin_records[npc.coin_name]
|
||||
error = blockchain_check_conditions_dict(
|
||||
unspent,
|
||||
removal_coin_records,
|
||||
announcements,
|
||||
npc.condition_dict,
|
||||
prev_transaction_block_height,
|
||||
block.foliage_block.timestamp,
|
||||
|
@ -1,25 +1,14 @@
|
||||
from typing import Optional, Dict, List
|
||||
from typing import Optional, Dict, List, Set
|
||||
|
||||
from src.types.condition_var_pair import ConditionVarPair
|
||||
from src.types.coin_record import CoinRecord
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.types.announcement import Announcement
|
||||
from src.util.clvm import int_from_bytes
|
||||
from src.util.condition_tools import ConditionOpcode
|
||||
from src.util.errors import Err
|
||||
from src.util.ints import uint64, uint32
|
||||
|
||||
|
||||
def blockchain_assert_coin_consumed(condition: ConditionVarPair, removed: Dict[bytes32, CoinRecord]) -> Optional[Err]:
|
||||
"""
|
||||
Checks coin consumed conditions
|
||||
Returns None if conditions are met, if not returns the reason why it failed
|
||||
"""
|
||||
coin_name = condition.vars[0]
|
||||
if coin_name not in removed:
|
||||
return Err.ASSERT_COIN_CONSUMED_FAILED
|
||||
return None
|
||||
|
||||
|
||||
def blockchain_assert_my_coin_id(condition: ConditionVarPair, unspent: CoinRecord) -> Optional[Err]:
|
||||
"""
|
||||
Checks if CoinID matches the id from the condition
|
||||
@ -91,9 +80,20 @@ def blockchain_assert_relative_time_exceeds(condition: ConditionVarPair, unspent
|
||||
return None
|
||||
|
||||
|
||||
def blockchain_assert_announcement(condition: ConditionVarPair, announcements: Set[bytes]) -> Optional[Err]:
|
||||
"""
|
||||
Check if an announcement is included in the list of announcements
|
||||
"""
|
||||
announcement_hash = condition.vars[0]
|
||||
if announcement_hash not in announcements:
|
||||
return Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def blockchain_check_conditions_dict(
|
||||
unspent: CoinRecord,
|
||||
removed: Dict[bytes32, CoinRecord],
|
||||
announcements: List[Announcement],
|
||||
conditions_dict: Dict[ConditionOpcode, List[ConditionVarPair]],
|
||||
prev_transaction_block_height: uint32,
|
||||
timestamp: uint64,
|
||||
@ -101,14 +101,15 @@ def blockchain_check_conditions_dict(
|
||||
"""
|
||||
Check all conditions against current state.
|
||||
"""
|
||||
announcement_names = set([a.name() for a in announcements])
|
||||
for con_list in conditions_dict.values():
|
||||
cvp: ConditionVarPair
|
||||
for cvp in con_list:
|
||||
error = None
|
||||
if cvp.opcode is ConditionOpcode.ASSERT_COIN_CONSUMED:
|
||||
error = blockchain_assert_coin_consumed(cvp, removed)
|
||||
elif cvp.opcode is ConditionOpcode.ASSERT_MY_COIN_ID:
|
||||
if cvp.opcode is ConditionOpcode.ASSERT_MY_COIN_ID:
|
||||
error = blockchain_assert_my_coin_id(cvp, unspent)
|
||||
elif cvp.opcode is ConditionOpcode.ASSERT_ANNOUNCEMENT:
|
||||
error = blockchain_assert_announcement(cvp, announcement_names)
|
||||
elif cvp.opcode is ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS:
|
||||
error = blockchain_assert_block_index_exceeds(cvp, prev_transaction_block_height)
|
||||
elif cvp.opcode is ConditionOpcode.ASSERT_BLOCK_AGE_EXCEEDS:
|
||||
|
@ -5,10 +5,11 @@ class ConditionCost(Enum):
|
||||
# Condition Costs
|
||||
AGG_SIG = 20
|
||||
CREATE_COIN = 200
|
||||
ASSERT_COIN_CONSUMED = 0
|
||||
ASSERT_MY_COIN_ID = 0
|
||||
ASSERT_TIME_EXCEEDS = 0
|
||||
ASSERT_RELATIVE_TIME_EXCEEDS = 0
|
||||
ASSERT_BLOCK_INDEX_EXCEEDS = 0
|
||||
ASSERT_BLOCK_AGE_EXCEEDS = 0
|
||||
ASSERT_FEE = 0
|
||||
CREATE_ANNOUNCEMENT = 0
|
||||
ASSERT_ANNOUNCEMENT = 0
|
||||
|
@ -47,10 +47,12 @@ def calculate_cost_of_program(
|
||||
total_vbyte_cost += len(cvp_list) * ConditionCost.ASSERT_BLOCK_INDEX_EXCEEDS.value
|
||||
elif condition is ConditionOpcode.ASSERT_MY_COIN_ID:
|
||||
total_vbyte_cost += len(cvp_list) * ConditionCost.ASSERT_MY_COIN_ID.value
|
||||
elif condition is ConditionOpcode.ASSERT_COIN_CONSUMED:
|
||||
total_vbyte_cost += len(cvp_list) * ConditionCost.ASSERT_COIN_CONSUMED.value
|
||||
elif condition is ConditionOpcode.ASSERT_FEE:
|
||||
total_vbyte_cost += len(cvp_list) * ConditionCost.ASSERT_FEE.value
|
||||
elif condition is ConditionOpcode.CREATE_ANNOUNCEMENT:
|
||||
total_vbyte_cost += len(cvp_list) * ConditionCost.CREATE_ANNOUNCEMENT.value
|
||||
elif condition is ConditionOpcode.ASSERT_ANNOUNCEMENT:
|
||||
total_vbyte_cost += len(cvp_list) * ConditionCost.ASSERT_ANNOUNCEMENT.value
|
||||
else:
|
||||
# We ignore unknown conditions in order to allow for future soft forks
|
||||
pass
|
||||
|
@ -15,15 +15,15 @@ from src.util.ints import uint64, uint32
|
||||
from src.wallet.puzzles.generator_loader import GENERATOR_MOD, GENERATOR_FOR_SINGLE_COIN_MOD
|
||||
|
||||
|
||||
def mempool_assert_coin_consumed(condition: ConditionVarPair, spend_bundle: SpendBundle) -> Optional[Err]:
|
||||
def mempool_assert_announcement_consumed(condition: ConditionVarPair, spend_bundle: SpendBundle) -> Optional[Err]:
|
||||
"""
|
||||
Checks coin consumed conditions
|
||||
Returns None if conditions are met, if not returns the reason why it failed
|
||||
Check if an announcement is included in the list of announcements
|
||||
"""
|
||||
bundle_removals = spend_bundle.removal_names()
|
||||
coin_name = condition.vars[0]
|
||||
if coin_name not in bundle_removals:
|
||||
return Err.ASSERT_COIN_CONSUMED_FAILED
|
||||
announcements = spend_bundle.announcements()
|
||||
announcement_hash = condition.vars[0]
|
||||
if announcement_hash not in [ann.name() for ann in announcements]:
|
||||
return Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@ -99,8 +99,7 @@ def mempool_assert_relative_time_exceeds(condition: ConditionVarPair, unspent: C
|
||||
|
||||
def get_name_puzzle_conditions(block_program: SerializedProgram, safe_mode: bool):
|
||||
# TODO: allow generator mod to take something (future)
|
||||
# TODO: check strict mode locations are set correctly
|
||||
# TODO: write various tests
|
||||
# TODO: write more tests
|
||||
try:
|
||||
if safe_mode:
|
||||
cost, result = GENERATOR_MOD.run_safe_with_cost(block_program)
|
||||
@ -160,10 +159,10 @@ def mempool_check_conditions_dict(
|
||||
cvp: ConditionVarPair
|
||||
for cvp in con_list:
|
||||
error = None
|
||||
if cvp.opcode is ConditionOpcode.ASSERT_COIN_CONSUMED:
|
||||
error = mempool_assert_coin_consumed(cvp, spend_bundle)
|
||||
elif cvp.opcode is ConditionOpcode.ASSERT_MY_COIN_ID:
|
||||
if cvp.opcode is ConditionOpcode.ASSERT_MY_COIN_ID:
|
||||
error = mempool_assert_my_coin_id(cvp, unspent)
|
||||
elif cvp.opcode is ConditionOpcode.ASSERT_ANNOUNCEMENT:
|
||||
error = mempool_assert_announcement_consumed(cvp, spend_bundle)
|
||||
elif cvp.opcode is ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS:
|
||||
error = mempool_assert_block_index_exceeds(cvp, prev_transaction_block_height)
|
||||
elif cvp.opcode is ConditionOpcode.ASSERT_BLOCK_AGE_EXCEEDS:
|
||||
|
12
src/types/announcement.py
Normal file
12
src/types/announcement.py
Normal file
@ -0,0 +1,12 @@
|
||||
from src.types.sized_bytes import bytes32
|
||||
from dataclasses import dataclass
|
||||
from src.util.hash import std_hash
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Announcement:
|
||||
parent_coin_info: bytes32
|
||||
message: bytes
|
||||
|
||||
def name(self) -> bytes32:
|
||||
return std_hash(bytes(self.parent_coin_info + self.message))
|
@ -4,7 +4,8 @@ from dataclasses import dataclass
|
||||
|
||||
from .coin import Coin
|
||||
from .program import Program
|
||||
from src.util.chain_utils import additions_for_solution
|
||||
from .announcement import Announcement
|
||||
from src.util.chain_utils import additions_for_solution, announcements_for_solution
|
||||
from src.util.streamable import Streamable, streamable
|
||||
|
||||
|
||||
@ -22,3 +23,6 @@ class CoinSolution(Streamable):
|
||||
|
||||
def additions(self) -> List[Coin]:
|
||||
return additions_for_solution(self.coin.name(), self.solution)
|
||||
|
||||
def announcements(self) -> List[Announcement]:
|
||||
return announcements_for_solution(self.coin.name(), self.solution)
|
||||
|
@ -6,7 +6,7 @@ class ConditionOpcode(bytes, enum.Enum):
|
||||
UNKNOWN = bytes([49])
|
||||
AGG_SIG = bytes([50])
|
||||
CREATE_COIN = bytes([51])
|
||||
ASSERT_COIN_CONSUMED = bytes([52])
|
||||
ASSERT_ANNOUNCEMENT = bytes([52])
|
||||
ASSERT_MY_COIN_ID = bytes([53])
|
||||
ASSERT_RELATIVE_TIME_EXCEEDS = bytes([54])
|
||||
ASSERT_BLOCK_INDEX_EXCEEDS = bytes([55])
|
||||
@ -14,6 +14,7 @@ class ConditionOpcode(bytes, enum.Enum):
|
||||
AGG_SIG_ME = bytes([57])
|
||||
ASSERT_FEE = bytes([58])
|
||||
ASSERT_TIME_EXCEEDS = bytes([59])
|
||||
CREATE_ANNOUNCEMENT = bytes([60])
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return bytes(self.value)
|
||||
|
@ -6,9 +6,13 @@ from chiabip158 import PyBIP158
|
||||
from src.types.header_block import HeaderBlock
|
||||
from src.types.name_puzzle_condition import NPC
|
||||
from src.types.coin import Coin
|
||||
from src.types.announcement import Announcement
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.full_node.mempool_check_conditions import get_name_puzzle_conditions
|
||||
from src.util.condition_tools import created_outputs_for_conditions_dict
|
||||
from src.util.condition_tools import (
|
||||
created_outputs_for_conditions_dict,
|
||||
created_announcements_for_conditions_dict,
|
||||
)
|
||||
from src.util.streamable import Streamable, streamable
|
||||
from src.types.vdf import VDFProof
|
||||
from src.types.reward_chain_sub_block import RewardChainSubBlock
|
||||
@ -140,3 +144,12 @@ def additions_for_npc(npc_list: List[NPC]) -> List[Coin]:
|
||||
additions.append(coin)
|
||||
|
||||
return additions
|
||||
|
||||
|
||||
def announcements_for_npc(npc_list: List[NPC]) -> List[Announcement]:
|
||||
announcements: List[Announcement] = []
|
||||
|
||||
for npc in npc_list:
|
||||
announcements.extend(created_announcements_for_conditions_dict(npc.condition_dict, npc.coin_name))
|
||||
|
||||
return announcements
|
||||
|
@ -2,6 +2,7 @@ from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
from src.types.coin import Coin
|
||||
from src.types.announcement import Announcement
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.streamable import Streamable, streamable
|
||||
from .coin_solution import CoinSolution
|
||||
@ -37,6 +38,12 @@ class SpendBundle(Streamable):
|
||||
items.extend(coin_solution.additions())
|
||||
return items
|
||||
|
||||
def announcements(self) -> List[Announcement]:
|
||||
items: List[Announcement] = []
|
||||
for coin_solution in self.coin_solutions:
|
||||
items.extend(coin_solution.announcements())
|
||||
return items
|
||||
|
||||
def removals(self) -> List[Coin]:
|
||||
""" This should be used only by wallet"""
|
||||
return [_.coin for _ in self.coin_solutions]
|
||||
|
@ -1,9 +1,11 @@
|
||||
from typing import List
|
||||
|
||||
from src.types.coin import Coin
|
||||
from src.types.announcement import Announcement
|
||||
from src.util.condition_tools import (
|
||||
created_outputs_for_conditions_dict,
|
||||
conditions_dict_for_solution,
|
||||
created_announcements_for_conditions_dict,
|
||||
)
|
||||
|
||||
|
||||
@ -15,3 +17,13 @@ def additions_for_solution(coin_name, solution) -> List[Coin]:
|
||||
if err or dic is None:
|
||||
return []
|
||||
return created_outputs_for_conditions_dict(dic, coin_name)
|
||||
|
||||
|
||||
def announcements_for_solution(coin_name, solution) -> List[Announcement]:
|
||||
"""
|
||||
Checks the conditions created by CoinSolution and returns the list of announcements
|
||||
"""
|
||||
err, dic, cost = conditions_dict_for_solution(solution)
|
||||
if err or dic is None:
|
||||
return []
|
||||
return created_announcements_for_conditions_dict(dic, coin_name)
|
||||
|
@ -5,6 +5,7 @@ from blspy import G1Element
|
||||
from src.types.condition_var_pair import ConditionVarPair
|
||||
from src.types.condition_opcodes import ConditionOpcode
|
||||
from src.types.coin import Coin
|
||||
from src.types.announcement import Announcement
|
||||
from src.types.program import Program
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.util.clvm import int_from_bytes
|
||||
@ -107,6 +108,22 @@ def created_outputs_for_conditions_dict(
|
||||
return output_coins
|
||||
|
||||
|
||||
def created_announcements_for_conditions_dict(
|
||||
conditions_dict: Dict[ConditionOpcode, List[ConditionVarPair]],
|
||||
input_coin_name: bytes32,
|
||||
) -> List[Announcement]:
|
||||
output_announcements = []
|
||||
for cvp in conditions_dict.get(ConditionOpcode.CREATE_ANNOUNCEMENT, []):
|
||||
# TODO: check condition very carefully
|
||||
# (ensure there are the correct number and type of parameters)
|
||||
# maybe write a type-checking framework for conditions
|
||||
# and don't just fail with asserts
|
||||
message = cvp.vars[0]
|
||||
announcement = Announcement(input_coin_name, message)
|
||||
output_announcements.append(announcement)
|
||||
return output_announcements
|
||||
|
||||
|
||||
def conditions_dict_for_solution(
|
||||
solution,
|
||||
) -> Tuple[Optional[Err], Optional[Dict[ConditionOpcode, List[ConditionVarPair]]], uint64]:
|
||||
|
@ -31,7 +31,7 @@ class Err(Enum):
|
||||
BAD_FARMER_COIN_AMOUNT = 9
|
||||
INVALID_CONDITION = 10
|
||||
ASSERT_MY_COIN_ID_FAILED = 11
|
||||
ASSERT_COIN_CONSUMED_FAILED = 12
|
||||
ASSERT_ANNOUNCE_CONSUMED_FAILED = 12
|
||||
ASSERT_BLOCK_AGE_EXCEEDS_FAILED = 13
|
||||
ASSERT_BLOCK_INDEX_EXCEEDS_FAILED = 14
|
||||
ASSERT_TIME_EXCEEDS_FAILED = 15
|
||||
|
@ -22,7 +22,8 @@ from src.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
|
||||
DEFAULT_HIDDEN_PUZZLE_HASH,
|
||||
)
|
||||
from src.wallet.puzzles.puzzle_utils import (
|
||||
make_assert_coin_consumed_condition,
|
||||
make_create_announcement,
|
||||
make_assert_announcement,
|
||||
make_assert_my_coin_id_condition,
|
||||
make_create_coin_condition,
|
||||
make_assert_block_index_exceeds_condition,
|
||||
@ -99,10 +100,12 @@ class WalletTool:
|
||||
for cvp in con_list:
|
||||
if cvp.opcode == ConditionOpcode.CREATE_COIN:
|
||||
ret.append(make_create_coin_condition(cvp.vars[0], cvp.vars[1]))
|
||||
if cvp.opcode == ConditionOpcode.CREATE_ANNOUNCEMENT:
|
||||
ret.append(make_create_announcement(cvp.vars[0]))
|
||||
if cvp.opcode == ConditionOpcode.AGG_SIG:
|
||||
ret.append(make_assert_aggsig_condition(cvp.vars[0]))
|
||||
if cvp.opcode == ConditionOpcode.ASSERT_COIN_CONSUMED:
|
||||
ret.append(make_assert_coin_consumed_condition(cvp.vars[0]))
|
||||
if cvp.opcode == ConditionOpcode.ASSERT_ANNOUNCEMENT:
|
||||
ret.append(make_assert_announcement(cvp.vars[0]))
|
||||
if cvp.opcode == ConditionOpcode.ASSERT_TIME_EXCEEDS:
|
||||
ret.append(make_assert_time_exceeds_condition(cvp.vars[0]))
|
||||
if cvp.opcode == ConditionOpcode.ASSERT_MY_COIN_ID:
|
||||
@ -113,7 +116,6 @@ class WalletTool:
|
||||
ret.append(make_assert_block_age_exceeds_condition(cvp.vars[0]))
|
||||
if cvp.opcode == ConditionOpcode.ASSERT_FEE:
|
||||
ret.append(make_assert_fee_condition(cvp.vars[0]))
|
||||
|
||||
return solution_for_conditions(Program.to(ret))
|
||||
|
||||
def generate_unsigned_transaction(
|
||||
|
@ -162,15 +162,6 @@ def spend_bundle_for_spendable_ccs(
|
||||
coin_solution = CoinSolution(input_coins[index], full_solution)
|
||||
coin_solutions.append(coin_solution)
|
||||
|
||||
# now add solutions to consume the lock coins
|
||||
|
||||
for _ in range(N):
|
||||
prev_index = (_ - 1) % N
|
||||
prev_coin = spendable_cc_list[prev_index].coin
|
||||
this_coin = spendable_cc_list[_].coin
|
||||
subtotal = subtotals[_]
|
||||
coin_solution = coin_solution_for_lock_coin(prev_coin, subtotal, this_coin)
|
||||
coin_solutions.append(coin_solution)
|
||||
if sigs is None or sigs == []:
|
||||
return SpendBundle(coin_solutions, NULL_SIGNATURE)
|
||||
else:
|
||||
|
@ -24,7 +24,7 @@ KFA = {v: k for k, v in CONDITIONS.items()}
|
||||
|
||||
def disassemble(sexp):
|
||||
"""
|
||||
This version of `disassemble` also disassembles condition opcodes like `ASSERT_COIN_CONSUMED`.
|
||||
This version of `disassemble` also disassembles condition opcodes like `ASSERT_ANNOUNCEMENT_CONSUMED`.
|
||||
"""
|
||||
kfa = dict(KEYWORD_FROM_ATOM)
|
||||
kfa.update((Program.to(k).as_atom(), v) for k, v in KFA.items())
|
||||
@ -48,8 +48,6 @@ def debug_spend_bundle(spend_bundle: SpendBundle) -> None:
|
||||
its clvm.
|
||||
"""
|
||||
|
||||
assert_consumed_set = set()
|
||||
|
||||
pks = []
|
||||
msgs = []
|
||||
|
||||
@ -85,8 +83,6 @@ def debug_spend_bundle(spend_bundle: SpendBundle) -> None:
|
||||
as_prog = Program.to([c.opcode, c.vars[0], c.vars[1]])
|
||||
print(f" {disassemble(as_prog)}")
|
||||
print()
|
||||
for _ in conditions.get(ConditionOpcode.ASSERT_COIN_CONSUMED, []):
|
||||
assert_consumed_set.add(bytes32(c.vars[0]))
|
||||
else:
|
||||
print("(no output conditions generated)")
|
||||
print()
|
||||
@ -119,15 +115,11 @@ def debug_spend_bundle(spend_bundle: SpendBundle) -> None:
|
||||
print(f" => created coin id {coin.name()}")
|
||||
|
||||
print()
|
||||
print(f"assert_consumed_set = {sorted(assert_consumed_set)}")
|
||||
print()
|
||||
print(f"zero_coin_set = {sorted(zero_coin_set)}")
|
||||
print()
|
||||
set_difference = zero_coin_set ^ assert_consumed_set
|
||||
print(f"zero_coin_set ^ assert_consumed_set = {sorted(set_difference)}")
|
||||
if len(set_difference):
|
||||
print("not all zero coins asserted consumed or vice versa")
|
||||
|
||||
print(f"created announcements = {spend_bundle.announcements()}")
|
||||
print()
|
||||
print()
|
||||
print("=" * 80)
|
||||
print()
|
||||
|
@ -78,7 +78,8 @@
|
||||
;;;;; start library code
|
||||
|
||||
(defconstant CREATE_COIN 51)
|
||||
(defconstant ASSERT_COIN_CONSUMED 52)
|
||||
(defconstant CREATE_ANNOUNCEMENT 60)
|
||||
(defconstant ASSERT_ANNOUNCEMENT 52)
|
||||
(defconstant ASSERT_MY_COIN_ID 53)
|
||||
|
||||
(defmacro assert items
|
||||
@ -185,12 +186,12 @@
|
||||
|
||||
;; calculate the coin id of a lock coin
|
||||
(defun-inline calculate-next-lock-coin-id (my-coin-info subtotal next-coin-info)
|
||||
(sha256 (coin-id-for-coin next-coin-info) (puzzle-hash-for-lock my-coin-info subtotal) 0)
|
||||
(sha256 (coin-id-for-coin next-coin-info) (puzzle-hash-for-lock my-coin-info subtotal))
|
||||
)
|
||||
|
||||
;; create the `ASSERT_COIN_CONSUMED` condition that ensures the next coin's lock is spent
|
||||
(defun-inline create-assert-next-lock-consumed-condition (my-coin-info right-subtotal next-coin-info)
|
||||
(list ASSERT_COIN_CONSUMED
|
||||
(list ASSERT_ANNOUNCEMENT
|
||||
(calculate-next-lock-coin-id my-coin-info
|
||||
right-subtotal
|
||||
next-coin-info
|
||||
@ -199,15 +200,14 @@
|
||||
)
|
||||
|
||||
;; calculate the puzzle hash for a lock coin
|
||||
(defun puzzle-hash-for-lock (prev-coin-info left-subtotal)
|
||||
(sha256tree1 (curry (lambda ARGS ()) prev-coin-info left-subtotal))
|
||||
(defun-inline puzzle-hash-for-lock (prev-coin-info left-subtotal)
|
||||
(sha256tree1 prev-coin-info left-subtotal)
|
||||
)
|
||||
|
||||
;; here we commit to O_k, I_{k+1} and S_k
|
||||
(defun-inline create-lock-coin-condition (prev-coin-info subtotal)
|
||||
(list CREATE_COIN
|
||||
(list CREATE_ANNOUNCEMENT
|
||||
(puzzle-hash-for-lock prev-coin-info subtotal)
|
||||
0
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -1 +1 @@
|
||||
ffff05ffff01ffffff05ff5affff05ff02ffff05ffff05ff05ffff05ffffff05ff2effff05ff02ffff05ff05ffff01ff808080808080ffff05ff0bffff05ffffff05ff2effff05ff02ffff05ff0bffff01ff808080808080ffff01ff808080808080ffff05ffffff05ff17ff2f8080ffff05ff5fffff05ff8200bfffff05ff82017fffff05ff8202ffffff01ff808080808080808080808080ffff05ffff01ffffffffff3435ff33ffff05ff5effff05ff02ffff05ffffff05ff34ffff05ff02ffff05ff09ffff05ff15ffff05ff5dffff05ff0bffff01ff808080808080808080ffff05ff09ffff05ff15ffff05ff5dffff05ff0bffff01ff80808080808080808080ffffff0bff09ff15ff2d80ffff05ff2cffff05ff02ffff05ff05ffff05ff07ffff01ff80808080808080ffff05ffff05ffff01ff0580ffff05ffff05ffff01ff0180ffff05ff05ffff01ff80808080ffff05ffffff05ff5cffff05ff02ffff05ff0bffff01ffff01808080808080ffff01ff8080808080ffff01ff808080ffffff05ffff04ff05ffff01ffff05ffff01ff0580ffff05ffff05ffff01ff0180ffff05ff09ffff01ff80808080ffff05ffffff05ff5cffff05ff02ffff05ff0dffff05ff0bffff01ff80808080808080ffff01ff808080808080ffff01ff0b8080ff0180802dffffffffff05ffff04ff15ffff01ffffff05ff2affff05ff02ffff05ff0bffff05ff09ffff05ff1dffff01ff808080808080808080ffff01ffffff05ffffff05ff7cffff05ff02ffff05ff0bffff01ff808080808080ffff05ff0bffff05ff09ffff05ff1dffff01ff808080808080808080ff018080ffff05ffff04ff0bffff01ffffff05ffff04ffff0aff05ff1380ffff01ffff01ff018080ffff01ffffff05ff32ffff05ff02ffff05ff05ffff05ff1bffff01ff808080808080808080ff01808080ffff01ffff01ff80808080ff018080ffff0aff13ffff0bff27ffffff05ff38ffff05ff02ffff05ff05ffff05ff57ffff01ff80808080808080ff8200b78080ffffff05ffff04ffffff05ff22ffff05ff02ffff05ff17ffff05ff05ffff01ff80808080808080ffff01ffffff05ffff04ffffff05ff22ffff05ff02ffff05ff2fffff05ff05ffff01ff80808080808080ffff01ffffff05ffff04ffffff05ff22ffff05ff02ffff05ff5fffff05ff05ffff01ff80808080808080ffff01ffff05ffff05ff30ffff05ffffff05ff24ffff05ff02ffff05ff4fffff01ff808080808080ffff01ff80808080ffff05ffff05ff28ffff05ffffff05ff36ffff05ff02ffff05ff27ffff05ff8200bfffff01ff80808080808080ffff01ffff8080808080ffff05ffff05ff20ffff05ffff0bffffff05ff24ffff05ff02ffff05ff82009fffff01ff808080808080ffffff05ff36ffff05ff02ffff05ff4fffff05ffff0cff8200bfffff0dff8202cfffffff05ff26ffff05ff02ffff05ff0bffff01ff8080808080808080ffff01ff80808080808080ffff01ff808080ffff01ff80808080ffffff05ff7affff05ff02ffff05ff0bffff05ff05ffff01ff8080808080808080808080ffff01ffff09808080ff01808080ffff01ffff09808080ff01808080ffff01ffff09808080ff018080ffff05ffff04ff05ffff01ffff05ffffff05ffff04ffff0aff11ff2880ffff01ffff05ff28ffff05ffffff05ff38ffff05ff02ffff05ff0bffff05ff29ffff01ff80808080808080ffff05ff59ffff01ff808080808080ffff01ff098080ff018080ffffff05ff7affff05ff02ffff05ff0dffff05ff0bffff01ff808080808080808080ffff01ffff01ff80808080ff018080ffffffff05ffff04ff05ffff01ffff0cffffff05ffff04ffff0aff11ff2880ffff01ff5980ffff01ffff01ff80808080ff018080ffffff05ff26ffff05ff02ffff05ff0dffff01ff8080808080808080ffff01ffff01ff80808080ff018080ffff05ff2effff05ff02ffff05ffffff05ff34ffff05ff02ffff05ffff01ffff01ff808080ffff05ff05ffff05ff0bffff01ff8080808080808080ffff01ff808080808080ffffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff2effff05ff02ffff05ff09ffff01ff808080808080ffffff05ff2effff05ff02ffff05ff0dffff01ff8080808080808080ffff01ffff0bffff01ff0180ff05808080ff018080ffffff05ff7effff05ff02ffff05ff05ffff05ff07ffff01ff80808080808080ffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff7effff05ff02ffff05ff09ffff05ff0bffff01ff80808080808080ffffff05ff7effff05ff02ffff05ff0dffff05ff0bffff01ff808080808080808080ffff01ffffff05ffff04ffffff05ff32ffff05ff02ffff05ff05ffff05ff0bffff01ff80808080808080ffff01ff0580ffff01ffff0bffff01ff0180ff05808080ff0180808080ff01808080ff01808080
|
||||
ffff05ffff01ffffff05ff7affff05ff02ffff05ffff05ff05ffff05ffffff05ff2effff05ff02ffff05ff05ffff01ff808080808080ffff05ff0bffff05ffffff05ff2effff05ff02ffff05ff0bffff01ff808080808080ffff01ff808080808080ffff05ffffff05ff17ff2f8080ffff05ff5fffff05ff8200bfffff05ff82017fffff05ff8202ffffff01ff808080808080808080808080ffff05ffff01ffffffffff3435ff3c33ffffffff05ff5effff05ff02ffff05ffffff05ff2cffff05ff02ffff05ff09ffff05ff15ffff05ff5dffff05ff0bffff01ff808080808080808080ffff05ff09ffff05ff15ffff05ff5dffff05ff0bffff01ff80808080808080808080ff0bff09ff15ff2d80ffffff05ff5cffff05ff02ffff05ff05ffff05ff07ffff01ff80808080808080ffff05ffff05ffff01ff0580ffff05ffff05ffff01ff0180ffff05ff05ffff01ff80808080ffff05ffffff05ff7cffff05ff02ffff05ff0bffff01ffff01808080808080ffff01ff8080808080ffff01ff808080ffff05ffff04ff05ffff01ffff05ffff01ff0580ffff05ffff05ffff01ff0180ffff05ff09ffff01ff80808080ffff05ffffff05ff7cffff05ff02ffff05ff0dffff05ff0bffff01ff80808080808080ffff01ff808080808080ffff01ff0b8080ff018080ffffff2dffff05ffff04ff15ffff01ffffff05ff5affff05ff02ffff05ff0bffff05ff09ffff05ff1dffff01ff808080808080808080ffff01ffffff05ffffff05ff22ffff05ff02ffff05ff0bffff01ff808080808080ffff05ff0bffff05ff09ffff05ff1dffff01ff808080808080808080ff018080ffffff05ffff04ff0bffff01ffffff05ffff04ffff0aff05ff1380ffff01ffff01ff018080ffff01ffffff05ff2affff05ff02ffff05ff05ffff05ff1bffff01ff808080808080808080ff01808080ffff01ffff01ff80808080ff018080ffff0aff13ffff0bff27ffffff05ff24ffff05ff02ffff05ff05ffff05ff57ffff01ff80808080808080ff8200b78080ffff05ffff04ffffff05ff32ffff05ff02ffff05ff17ffff05ff05ffff01ff80808080808080ffff01ffffff05ffff04ffffff05ff32ffff05ff02ffff05ff2fffff05ff05ffff01ff80808080808080ffff01ffffff05ffff04ffffff05ff32ffff05ff02ffff05ff5fffff05ff05ffff01ff80808080808080ffff01ffff05ffff05ff30ffff05ffffff05ff34ffff05ff02ffff05ff4fffff01ff808080808080ffff01ff80808080ffff05ffff05ff28ffff05ffffff05ff2effff05ff02ffff05ff27ffff05ff8200bfffff01ff80808080808080ffff01ff80808080ffff05ffff05ff20ffff05ffff0bffffff05ff34ffff05ff02ffff05ff82009fffff01ff808080808080ffffff05ff2effff05ff02ffff05ff4fffff05ffff0cff8200bfffff0dff8202cfffffff05ff36ffff05ff02ffff05ff0bffff01ff8080808080808080ffff01ff8080808080808080ffff01ff80808080ffffff05ff26ffff05ff02ffff05ff0bffff05ff05ffff01ff8080808080808080808080ffff01ffff09808080ff01808080ffff01ffff09808080ff01808080ffff01ffff09808080ff018080ffffffff05ffff04ff05ffff01ffff05ffffff05ffff04ffff0aff11ff3880ffff01ffff05ff38ffff05ffffff05ff24ffff05ff02ffff05ff0bffff05ff29ffff01ff80808080808080ffff05ff59ffff01ff808080808080ffff01ff098080ff018080ffffff05ff26ffff05ff02ffff05ff0dffff05ff0bffff01ff808080808080808080ffff01ffff01ff80808080ff018080ffff05ffff04ff05ffff01ffff0cffffff05ffff04ffff0aff11ff3880ffff01ff5980ffff01ffff01ff80808080ff018080ffffff05ff36ffff05ff02ffff05ff0dffff01ff8080808080808080ffff01ffff01ff80808080ff018080ffffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff2effff05ff02ffff05ff09ffff01ff808080808080ffffff05ff2effff05ff02ffff05ff0dffff01ff8080808080808080ffff01ffff0bffff01ff0180ff05808080ff018080ffffff05ff7effff05ff02ffff05ff05ffff05ff07ffff01ff80808080808080ffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff7effff05ff02ffff05ff09ffff05ff0bffff01ff80808080808080ffffff05ff7effff05ff02ffff05ff0dffff05ff0bffff01ff808080808080808080ffff01ffffff05ffff04ffffff05ff2affff05ff02ffff05ff05ffff05ff0bffff01ff80808080808080ffff01ff0580ffff01ffff0bffff01ff0180ff05808080ff0180808080ff01808080ff01808080
|
@ -1,321 +0,0 @@
|
||||
(mod (mod_hash
|
||||
genesis_id
|
||||
inner_puzzle
|
||||
parent_info
|
||||
my_amount
|
||||
solution
|
||||
(auditor_parent auditor_inner_puzzle auditor_amount)
|
||||
aggee_list)
|
||||
|
||||
; C0: a spendable colored coin input that is special and runs extra logic. The "auditor" coin
|
||||
; C1... CN (red): other spendable coloured coin inputs that do NOT run the extra logic
|
||||
; A0... AN (yellow): ephemeral coloured coin children of C0 corresponding to A0... AN
|
||||
;; As are created because they "prove that we are being audited".
|
||||
;; Each of C0, C1, ... CN does `ASSERT_COIN_CONSUMED` its A_k
|
||||
; E0... EN (blue): ephemeral coin created by corresponding C_k
|
||||
;; Es are created because they commit to the outputs created by C_n: "prove we are creating outputs valued at X"
|
||||
|
||||
; these constants are for creating output conditions
|
||||
|
||||
(defconstant CREATE_COIN 51)
|
||||
(defconstant ASSERT_COIN_CONSUMED 52)
|
||||
(defconstant ASSERT_MY_COIN_ID 53)
|
||||
|
||||
(defmacro and ARGS
|
||||
(if ARGS
|
||||
(qq (if (unquote (f ARGS))
|
||||
(unquote (c and (r ARGS)))
|
||||
()))
|
||||
1))
|
||||
|
||||
(defun-inline not (ARGS)
|
||||
(if ARGS 0 1)
|
||||
)
|
||||
|
||||
;; utility function used by `curry_args`
|
||||
(defun fix_curry_args (items core)
|
||||
(if items
|
||||
(qq (c (q (unquote (f items))) (unquote (fix_curry_args (r items) core))))
|
||||
core
|
||||
)
|
||||
)
|
||||
|
||||
; (curry_args sum (list 50 60)) => returns a function that is like (sum 50 60 ...)
|
||||
(defun curry_args (func list_of_args) (qq ((c (q (unquote func)) (unquote (fix_curry_args list_of_args (q 1)))))))
|
||||
|
||||
;; (curry sum 50 60) => returns a function that is like (sum 50 60 ...)
|
||||
(defun curry (func . args) (curry_args func args))
|
||||
|
||||
(defun is-in-list (atom items)
|
||||
(if items
|
||||
(if (= atom (f items))
|
||||
1
|
||||
(is-in-list atom (r items))
|
||||
)
|
||||
0
|
||||
)
|
||||
)
|
||||
|
||||
;; hash a tree with escape values representing already-hashed subtrees
|
||||
;; This optimization can be useful if you know the puzzle hash of a sub-expression.
|
||||
(defun sha256tree_esc_list
|
||||
(TREE LITERALS)
|
||||
(if (l TREE)
|
||||
(sha256 2 (sha256tree_esc_list (f TREE) LITERALS) (sha256tree_esc_list (r TREE) LITERALS))
|
||||
(if (is-in-list TREE LITERALS)
|
||||
TREE
|
||||
(sha256 1 TREE)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; hash a tree with escape values representing already-hashed subtrees
|
||||
;; This optimization can be useful if you know the tree hash of a sub-expression.
|
||||
(defun sha256tree_esc
|
||||
(TREE . LITERAL)
|
||||
(sha256tree_esc_list TREE LITERAL)
|
||||
)
|
||||
|
||||
; takes a lisp tree and returns the hash of it
|
||||
(defun sha256tree1 (TREE)
|
||||
(if (l TREE)
|
||||
(sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE)))
|
||||
(sha256 1 TREE)))
|
||||
|
||||
; takes a hash of the innerpuzzle and a hash of the core and creates a hash of the full puzzle
|
||||
; using the sha256tree with escape
|
||||
(defun calculate_puzzle_hash (mod_hash genesis_id inner_puzzle_hash)
|
||||
(sha256tree_esc (curry mod_hash (sha256tree1 mod_hash) genesis_id inner_puzzle_hash)
|
||||
mod_hash
|
||||
(sha256tree1 mod_hash)
|
||||
inner_puzzle_hash
|
||||
)
|
||||
)
|
||||
|
||||
; take a puzzle program and a solution and returns the result of running the program with the solution
|
||||
(defun-inline conditions_from_inner_puzzle (puzzle solution)
|
||||
((c puzzle solution)))
|
||||
|
||||
; replaces one of the generated create coin conditions with a coloured coin
|
||||
; which uses the created puzzlehash as the inner puzzlehash
|
||||
(defun-inline transform_create_coin_condition (mod_hash genesis_id condition)
|
||||
(list CREATE_COIN (calculate_puzzle_hash mod_hash genesis_id (f (r condition))) (f (r (r condition))))
|
||||
)
|
||||
|
||||
; creates the puzzle for the lock which lets the auditor confirm how much value we are outputting
|
||||
|
||||
(defun-inline puzzle_for_e_coin (auditor_id output_sum)
|
||||
(curry (lambda ARGS ()) auditor_id output_sum)
|
||||
)
|
||||
|
||||
; creates the coin for the lock which lets the auditor confirm how much value we are outputting
|
||||
(defun-inline generate_create_e_coin_condition (auditor_id output_sum)
|
||||
; build the condition that creates the ephemeral accounting coin
|
||||
(list CREATE_COIN
|
||||
(sha256tree1 (puzzle_for_e_coin auditor_id output_sum))
|
||||
0))
|
||||
|
||||
; creates the puzzle for the lock which lets the us confirm the auditor is including us
|
||||
(defun-inline create_a_puz_for_cn (my_id)
|
||||
(curry (lambda ARGS ()) my_id)
|
||||
)
|
||||
|
||||
; creates the coin for the lock which lets us confirm the auditor is including us
|
||||
(defun-inline generate_assert_consumed_a_condition (my_id auditor_id)
|
||||
; we need to ensure that the coin is consumed
|
||||
(list ASSERT_COIN_CONSUMED
|
||||
(sha256 auditor_id
|
||||
(sha256tree1 (create_a_puz_for_cn my_id))
|
||||
(q 0))))
|
||||
|
||||
; assembles information from the solution to create the auditor's full ID
|
||||
(defun-inline calculate_auditor_id (mod_hash genesis_id auditor_parent auditor_inner_puzzle_hash auditor_amount)
|
||||
(sha256 auditor_parent
|
||||
(calculate_puzzle_hash mod_hash genesis_id auditor_inner_puzzle_hash)
|
||||
auditor_amount))
|
||||
|
||||
; assembles information from the solution to create our own full ID including asserting our parent is a coloured coin
|
||||
(defun-inline calculate_my_id (mod_hash genesis_id parent_parent parent_inner_puzzle_hash parent_amount inner_puzzle_hash my_amount)
|
||||
(sha256 (sha256 parent_parent
|
||||
(calculate_puzzle_hash mod_hash genesis_id parent_inner_puzzle_hash)
|
||||
parent_amount)
|
||||
(calculate_puzzle_hash mod_hash genesis_id inner_puzzle_hash)
|
||||
my_amount))
|
||||
|
||||
; asserts that the information in the solution about ourselves is correct, and that our parent is a coloured coin
|
||||
(defun-inline generate_assert_my_id_condition (my_id)
|
||||
(list ASSERT_MY_COIN_ID my_id))
|
||||
|
||||
; this macro adds some conditions to the returned output of the normal case
|
||||
(defun-inline prepend_additional_conditions (my_id auditor_id running_sum conditions)
|
||||
(c (generate_assert_my_id_condition my_id)
|
||||
(c (generate_assert_consumed_a_condition my_id auditor_id)
|
||||
(c (generate_create_e_coin_condition auditor_id running_sum)
|
||||
conditions))))
|
||||
|
||||
; this function loops through the created outputs from running the puzzle reveal with the solution and replaces the
|
||||
; CREATE_COIN conditions with a create coloured coin it then adds the locks and assertions about itself at the end
|
||||
(defun normal_case_everybody (mod_hash genesis_id conditions transformed_conditions_acc running_sum my_id auditor_id)
|
||||
(if conditions
|
||||
(if (= (f (f conditions)) CREATE_COIN)
|
||||
(normal_case_everybody mod_hash
|
||||
genesis_id
|
||||
(r conditions)
|
||||
(c (transform_create_coin_condition mod_hash
|
||||
genesis_id
|
||||
(f conditions)
|
||||
)
|
||||
transformed_conditions_acc)
|
||||
(+ (f (r (r (f conditions))))
|
||||
running_sum)
|
||||
my_id
|
||||
auditor_id)
|
||||
(normal_case_everybody mod_hash
|
||||
genesis_id
|
||||
(r conditions)
|
||||
(c (f conditions)
|
||||
transformed_conditions_acc)
|
||||
running_sum
|
||||
my_id
|
||||
auditor_id))
|
||||
(prepend_additional_conditions my_id auditor_id running_sum transformed_conditions_acc)))
|
||||
|
||||
; this function takes an auditee and returns its id
|
||||
(defun get_aggee_id (mod_hash genesis_id aggee)
|
||||
(sha256 (f aggee)
|
||||
(calculate_puzzle_hash mod_hash
|
||||
genesis_id
|
||||
(f (r aggee))
|
||||
)
|
||||
(f (r (r aggee)))))
|
||||
|
||||
; this will create the lock which lets coins know that the auditor is including them
|
||||
(defun-inline create_a (aggee_id)
|
||||
(list CREATE_COIN
|
||||
(sha256tree1 (create_a_puz_for_cn aggee_id))
|
||||
(q 0)))
|
||||
|
||||
; this will consume the lock which lets the auditor know how much each coin is outputting
|
||||
(defun-inline consume_e (aggee_id my_id spend_amount)
|
||||
(list ASSERT_COIN_CONSUMED
|
||||
(sha256 aggee_id
|
||||
(sha256tree1 (puzzle_for_e_coin my_id spend_amount))
|
||||
(q 0))))
|
||||
|
||||
; this adds the conditions related to the locks to the output of the auditor case
|
||||
(defun create_a_and_consume_e (aggee_id my_id spend_amount output)
|
||||
(c (consume_e aggee_id my_id spend_amount)
|
||||
(c (create_a aggee_id)
|
||||
output)))
|
||||
|
||||
; this loops through a list of coins to be audited and creates the relevant conditions for each
|
||||
; it also compares the total value of coins being spent with total output amount of the transaction,
|
||||
; and if they aren't equal it will fail
|
||||
(defun consume_es_generate_as (mod_hash genesis_id aggee_list my_id output running_actual running_e)
|
||||
(if (l aggee_list)
|
||||
(consume_es_generate_as mod_hash
|
||||
genesis_id
|
||||
(r aggee_list)
|
||||
my_id
|
||||
(create_a_and_consume_e (get_aggee_id mod_hash genesis_id (f aggee_list))
|
||||
my_id
|
||||
(f (r (r (r (f aggee_list)))))
|
||||
output)
|
||||
(+ (f (r (r (f aggee_list)))) running_actual)
|
||||
(+ (f (r (r (r (f aggee_list))))) running_e))
|
||||
(if (= running_actual running_e)
|
||||
output
|
||||
(x))))
|
||||
|
||||
; this checks if our coin is the auditor and if so runs the auditor code, otherwise outputs the standard output
|
||||
(defun-inline normal_case_aggee_checker (mod_hash genesis_id my_id aggee_list conditions)
|
||||
(if aggee_list
|
||||
(consume_es_generate_as mod_hash genesis_id aggee_list my_id conditions 0 0)
|
||||
conditions))
|
||||
|
||||
; this calculates the standard output and passes it to the auditor route checker
|
||||
(defun normal_case (mod_hash genesis_id conditions my_id auditor_id aggee_list)
|
||||
(normal_case_aggee_checker mod_hash
|
||||
genesis_id
|
||||
my_id
|
||||
aggee_list
|
||||
(normal_case_everybody mod_hash genesis_id conditions () 0 my_id auditor_id)))
|
||||
|
||||
; this returns a conditions which asserts that our parent is the actual parent to stop us lying about our heritage
|
||||
(defun-inline generate_condition_proving_my_parent_is_genesis (parent_id my_puzzle_hash my_amount)
|
||||
(list ASSERT_MY_COIN_ID
|
||||
(sha256 parent_id
|
||||
my_puzzle_hash
|
||||
my_amount)))
|
||||
|
||||
; this returns a conditions which allows us to have any parent but asserts our value must be 0 if so
|
||||
(defun-inline generate_condition_proving_my_value_is_zero (parent_id my_puzzle_hash)
|
||||
(list ASSERT_MY_COIN_ID
|
||||
(sha256 parent_id
|
||||
my_puzzle_hash
|
||||
0)))
|
||||
|
||||
; this will create a new coin which has the same puzzle hash as us
|
||||
(defun-inline generate_create_coin_condition (my_puzzle_hash my_amount)
|
||||
(list CREATE_COIN my_puzzle_hash my_amount))
|
||||
|
||||
; this will check if our immediate parent is the genesis coin or not, and call the relevant assert depending
|
||||
(defun eve_case_parent_check (genesis_id parent_id my_puzzle_hash my_amount)
|
||||
(if (= parent_id genesis_id)
|
||||
(generate_condition_proving_my_parent_is_genesis parent_id my_puzzle_hash my_amount)
|
||||
(generate_condition_proving_my_value_is_zero parent_id my_puzzle_hash)))
|
||||
|
||||
; this adds the create child with my puzzle to the result of the parent check
|
||||
(defun-inline eve_case (genesis_id parent_id my_puzzle_hash my_amount)
|
||||
(list (generate_create_coin_condition my_puzzle_hash
|
||||
my_amount)
|
||||
(eve_case_parent_check genesis_id
|
||||
parent_id
|
||||
my_puzzle_hash
|
||||
my_amount)))
|
||||
|
||||
; this is the final program
|
||||
; it checks if our parent is a coloured coin, and then calls the normal case if so, otherwise it calls the eve case
|
||||
; in eve case puzzlereveal is just actually just your full puzzlehash
|
||||
(if (l parent_info)
|
||||
(normal_case mod_hash
|
||||
genesis_id
|
||||
(conditions_from_inner_puzzle inner_puzzle solution)
|
||||
(calculate_my_id mod_hash
|
||||
genesis_id
|
||||
(f parent_info)
|
||||
(f (r parent_info))
|
||||
(f (r (r parent_info)))
|
||||
(sha256tree1 inner_puzzle)
|
||||
my_amount)
|
||||
(calculate_auditor_id mod_hash
|
||||
genesis_id
|
||||
auditor_parent
|
||||
auditor_inner_puzzle
|
||||
auditor_amount
|
||||
)
|
||||
aggee_list)
|
||||
(eve_case genesis_id parent_info (calculate_puzzle_hash mod_hash genesis_id (sha256tree1 inner_puzzle)) my_amount))
|
||||
)
|
||||
|
||||
;HERE BE TESTS
|
||||
|
||||
;(calculate_my_id ((f parent_info) (f (r parent_info)) (f (r (r parent_info)))) (sha256tree1 inner_puzzle) my_amount corehash)
|
||||
;(calculate_auditor_id (auditor_parent auditor_inner_puzzle auditor_amount) corehash)
|
||||
;(generate_create_e_coin_condition ((f (f ARGS)) (f (r (f ARGS))) (f (r (r (f ARGS))))) (f (r ARGS)) 100)
|
||||
;(create_a_puz_cn ((f (f ARGS)) (f (r (f ARGS))) (f (r (r (f ARGS))))) (f (r ARGS)) (f (r (r ARGS))) (f (r (r (r ARGS)))))
|
||||
;(prepend_additional_conditions (calculate_my_id (parent_parent parent_innerpuz parent_amount) my_innerpuz my_amount corehash) (calculate_auditor_id (auditor_parent auditor_inner_puzzle auditor_amount) corehash) (q 100))
|
||||
;(conditions_from_inner_puzzle inner_puzzle solution)
|
||||
|
||||
;(eve_case_parent_check genesis_id parent_info (sha256tree1 (create_fullpuz (sha256tree1 inner_puzzle) corehash)) my_amount)
|
||||
;(eve_case genesis_id parent_info (sha256tree1 (create_fullpuz (sha256tree1 inner_puzzle) corehash)) my_amount)
|
||||
|
||||
;(get_first_aggee_id genesis_id aggee_list corehash)
|
||||
;(consume_e (get_first_aggee_id genesis_id aggee_list corehash) (calculate_my_id ((f parent_info) (f (r parent_info)) (f (r (r parent_info)))) (sha256tree1 inner_puzzle) my_amount corehash) (f (r (r (r (f aggee_list))))))
|
||||
;(create_a_and_consume_e (sha256 (f (f aggee_list)) (sha256tree1 (create_fullpuz (f (r (f aggee_list))) corehash)) (f (r (r (f aggee_list))))) (calculate_my_id ((f parent_info) (f (r parent_info)) (f (r (r parent_info)))) (sha256tree1 inner_puzzle) my_amount corehash) (f (r (r (r (f aggee_list))))))
|
||||
;(consume_es_generate_as aggee_list (calculate_my_id ((f parent_info) (f (r parent_info)) (f (r (r parent_info)))) (sha256tree1 inner_puzzle) my_amount corehash) corehash () () ())
|
||||
;(normal_case_aggee_checker (conditions_from_inner_puzzle inner_puzzle solution) () corehash 0 (calculate_my_id ((f parent_info) (f (r parent_info)) (f (r (r parent_info)))) (sha256tree1 inner_puzzle) my_amount corehash) (calculate_auditor_id (auditor_parent auditor_inner_puzzle auditor_amount) corehash) aggee_list)
|
||||
|
||||
;(normal_case_everybody genesis_id (conditions_from_inner_puzzle inner_puzzle solution) () corehash 0 (calculate_my_id ((f parent_info) (f (r parent_info)) (f (r (r parent_info)))) (sha256tree1 inner_puzzle) my_amount corehash) (calculate_auditor_id (auditor_parent auditor_inner_puzzle auditor_amount) corehash))
|
||||
;(normal_case (conditions_from_inner_puzzle inner_puzzle solution) () corehash 0 (calculate_my_id ((f parent_info) (f (r parent_info)) (f (r (r parent_info)))) (sha256tree1 inner_puzzle) my_amount corehash) (calculate_auditor_id (auditor_parent auditor_inner_puzzle auditor_amount) corehash) aggee_list)
|
||||
)
|
@ -1 +0,0 @@
|
||||
ffff05ffff01ffffff05ffff04ffff08ff2f80ffff01ffffff05ff26ffff05ff02ffff05ff05ffff05ff0bffff05ffffff05ff17ff8200bf8080ffff05ffff0bffff0bff4fffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ff8200afffff01ff8080808080808080ff82016f80ffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ffffff05ff2effff05ff02ffff05ff17ffff01ff808080808080ffff01ff8080808080808080ff5f80ffff05ffff0bff82027fffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ff82057fffff01ff8080808080808080ff820b7f80ffff05ff8202ffffff01ff808080808080808080808080ffff01ffff05ffff05ff28ffff05ffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ffffff05ff2effff05ff02ffff05ff17ffff01ff808080808080ffff01ff8080808080808080ffff05ff5fffff01ff8080808080ffff05ffffff05ff22ffff05ff02ffff05ff0bffff05ff2fffff05ffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ffffff05ff2effff05ff02ffff05ff17ffff01ff808080808080ffff01ff8080808080808080ffff05ff5fffff01ff808080808080808080ffff01ff808080808080ff01808080ffff05ffff01ffffffffff3435ff33ffff05ff5effff05ff02ffff05ffffff05ff2cffff05ff02ffff05ff05ffff05ffffff05ff2effff05ff02ffff05ff05ffff01ff808080808080ffff05ff0bffff05ff17ffff01ff808080808080808080ffff05ff05ffff05ffffff05ff2effff05ff02ffff05ff05ffff01ff808080808080ffff05ff17ffff01ff808080808080808080ffffffff05ffff04ffff08ff1780ffff01ffffff05ff24ffff05ff02ffff05ff05ffff05ff0bffff05ff37ffff05ff2fffff05ffffff05ff34ffff05ff02ffff05ffffff05ff2affff05ff02ffff05ff05ffff05ff0bffff05ff27ffff01ff8080808080808080ffff05ff2fffff05ff8202e7ffff05ff5fffff01ff808080808080808080ffff05ffff0cff820167ff8200bf80ffff05ffff0cff8202e7ff82017f80ffff01ff80808080808080808080808080ffff01ffffff05ffff04ffff0aff8200bfff82017f80ffff01ff5f80ffff01ffff09808080ff0180808080ff018080ff05ffff05ff20ffff05ffff0bff05ffffff05ff2effff05ff02ffff05ffffff05ff2cffff05ff02ffff05ffff01ffff01ff808080ffff05ff0bffff05ff17ffff01ff8080808080808080ffff01ff808080808080ffff01ff808080ffff01ff80808080ffff05ffff05ff28ffff05ffffff05ff2effff05ff02ffff05ffffff05ff2cffff05ff02ffff05ffff01ffff01ff808080ffff05ff05ffff01ff80808080808080ffff01ff808080808080ffff01ffff8080808080ff2f8080ffffff05ff3cffff05ff02ffff05ff05ffff05ff07ffff01ff80808080808080ff05ffff05ffff01ff0580ffff05ffff05ffff01ff0180ffff05ff05ffff01ff80808080ffff05ffffff05ff32ffff05ff02ffff05ff0bffff01ffff01808080808080ffff01ff8080808080ffff01ff808080ffffffffff05ffff04ffff0aff0bff0580ffff01ffff05ff30ffff05ffff0bff0bff17ff2f80ffff01ff8080808080ffff01ffff05ff30ffff05ffff0bff0bff17ffff01ff808080ffff01ff808080808080ff018080ffff05ffff04ff05ffff01ffff05ffff01ff0580ffff05ffff05ffff01ff0180ffff05ff09ffff01ff80808080ffff05ffffff05ff32ffff05ff02ffff05ff0dffff05ff0bffff01ff80808080808080ffff01ff808080808080ffff01ff0b8080ff018080ffff0bff27ffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ff57ffff01ff8080808080808080ff8200b780ffff05ffff04ff0bffff01ffffff05ffff04ffff0aff05ff1380ffff01ffff01ff018080ffff01ffffff05ff3affff05ff02ffff05ff05ffff05ff1bffff01ff808080808080808080ff01808080ffff01ffff01ff80808080ff018080ffffffff05ffff04ff8200bfffff01ffffff05ff24ffff05ff02ffff05ff05ffff05ff0bffff05ff8200bfffff05ff2fffff05ffffff05ff36ffff05ff02ffff05ff05ffff05ff0bffff05ff17ffff05ffff01ff8080ffff05ffff01ff8080ffff05ff2fffff05ff5fffff01ff808080808080808080808080ffff01ffff80ff808080808080808080808080ffff01ffffff05ff36ffff05ff02ffff05ff05ffff05ff0bffff05ff17ffff05ffff01ff8080ffff05ffff01ff8080ffff05ff2fffff05ff5fffff01ff8080808080808080808080808080ff018080ffff05ffff04ff17ffff01ffffff05ffff04ffff0aff47ff2880ffff01ffffff05ff36ffff05ff02ffff05ff05ffff05ff0bffff05ff37ffff05ffff05ffff05ff28ffff05ffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ff8200a7ffff01ff8080808080808080ffff05ff820167ffff01ff8080808080ff2f80ffff05ffff0cff820167ff5f80ffff05ff8200bfffff05ff82017fffff01ff80808080808080808080808080ffff01ffffff05ff36ffff05ff02ffff05ff05ffff05ff0bffff05ff37ffff05ffff05ff27ff2f80ffff05ff5fffff05ff8200bfffff05ff82017fffff01ff8080808080808080808080808080ff01808080ffff01ffff05ffff05ff30ffff05ff8200bfffff01ff80808080ffff05ffff05ff20ffff05ffff0bff82017fffffff05ff2effff05ff02ffff05ffffff05ff2cffff05ff02ffff05ffff01ffff01ff808080ffff05ff8200bfffff01ff80808080808080ffff01ff808080808080ffff01ff808080ffff01ff80808080ffff05ffff05ff28ffff05ffffff05ff2effff05ff02ffff05ffffff05ff2cffff05ff02ffff05ffff01ffff01ff808080ffff05ff82017fffff05ff5fffff01ff8080808080808080ffff01ff808080808080ffff01ffff8080808080ff2f8080808080ff018080ffffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff2effff05ff02ffff05ff09ffff01ff808080808080ffffff05ff2effff05ff02ffff05ff0dffff01ff8080808080808080ffff01ffff0bffff01ff0180ff05808080ff018080ffffff05ff7effff05ff02ffff05ff05ffff05ff07ffff01ff80808080808080ffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff7effff05ff02ffff05ff09ffff05ff0bffff01ff80808080808080ffffff05ff7effff05ff02ffff05ff0dffff05ff0bffff01ff808080808080808080ffff01ffffff05ffff04ffffff05ff3affff05ff02ffff05ff05ffff05ff0bffff01ff80808080808080ffff01ff0580ffff01ffff0bffff01ff0180ff05808080ff0180808080ff01808080ff01808080
|
@ -9,10 +9,6 @@ def make_assert_aggsig_condition(pubkey):
|
||||
return [ConditionOpcode.AGG_SIG, pubkey]
|
||||
|
||||
|
||||
def make_assert_coin_consumed_condition(coin_name):
|
||||
return [ConditionOpcode.ASSERT_COIN_CONSUMED, coin_name]
|
||||
|
||||
|
||||
def make_assert_my_coin_id_condition(coin_name):
|
||||
return [ConditionOpcode.ASSERT_MY_COIN_ID, coin_name]
|
||||
|
||||
@ -31,3 +27,11 @@ def make_assert_time_exceeds_condition(time):
|
||||
|
||||
def make_assert_fee_condition(fee):
|
||||
return [ConditionOpcode.ASSERT_FEE, fee]
|
||||
|
||||
|
||||
def make_assert_announcement(announcement_hash):
|
||||
return [ConditionOpcode.ASSERT_ANNOUNCEMENT, announcement_hash]
|
||||
|
||||
|
||||
def make_create_announcement(message):
|
||||
return [ConditionOpcode.CREATE_ANNOUNCEMENT, message]
|
||||
|
@ -11,6 +11,7 @@
|
||||
(defconstant CLAWBACK_MODE 3)
|
||||
(defconstant AGGSIG 50)
|
||||
(defconstant CREATE_COIN 51)
|
||||
(defconstant CREATE_ANNOUNCEMENT 60)
|
||||
(defconstant ASSERT_MY_COIN_ID 53)
|
||||
(defconstant ASSERT_BLOCK_AGE_EXCEEDS 56)
|
||||
|
||||
@ -37,14 +38,12 @@
|
||||
(unquote (c or (r args)))))
|
||||
0))
|
||||
|
||||
(include create-lock-puzzlehash.clvm)
|
||||
|
||||
(defun create-lock (consolidating_primary_input consolidating_coin_puzzle_hash outgoing_amount)
|
||||
(list CREATE_COIN
|
||||
(create-lock-puzzlehash (sha256 consolidating_primary_input
|
||||
consolidating_coin_puzzle_hash
|
||||
outgoing_amount))
|
||||
0))
|
||||
(list CREATE_ANNOUNCEMENT
|
||||
(sha256 consolidating_primary_input
|
||||
consolidating_coin_puzzle_hash
|
||||
outgoing_amount))
|
||||
)
|
||||
|
||||
(defun aggregation (origin_id
|
||||
(my_puzzle_hash
|
||||
|
1
src/wallet/puzzles/rl.clvm.hex
Normal file
1
src/wallet/puzzles/rl.clvm.hex
Normal file
@ -0,0 +1 @@
|
||||
ffff05ffff01ffffff05ffff04ffff0aff8200bfff2480ffff01ffff05ffff05ff30ffff05ff5fffff05ffffff05ff3effff05ff02ffff05ffff05ff8200bfff8200ff80ffff01ff808080808080ffff01ff8080808080ff8200ff8080ffff01ffff05ffff05ff30ffff05ff05ffff05ffffff05ff3effff05ff02ffff05ffff05ff8200bfff8200ff80ffff01ff808080808080ffff01ff8080808080ffffff05ffff04ffff0aff8200bfff3c80ffff01ffffff05ff2effff05ff02ffff05ff0bffff05ff17ffff05ff2fffff05ff8200ffffff01ff80808080808080808080ffff01ffffff05ff22ffff05ff02ffff05ff2fffff05ff8200ffffff01ff808080808080808080ff018080808080ff01808080ffff05ffff01ffffffffffffff05ffff04ffff16ff05ff0b80ffff01ffff01ff018080ffff01ffffff05ffff04ffff0aff05ff0b80ffff01ffff01ff018080ffff01ffff01ff80808080ff0180808080ff01808032ff3835ffff033cff3301ffffffffff05ffff04ffffff05ffff04ffff0affff0bff820bfbff13ff8205fb80ff82017b80ffff01ffff01ff018080ffff01ffffff05ffff04ffff0aff05ff82017b80ffff01ffff01ff018080ffff01ffff01ff80808080ff0180808080ff018080ffff01ffff05ffffff05ff26ffff05ff02ffff05ff82017bffff05ff13ffff05ff8202fbffff01ff8080808080808080ffff05ffffff05ff2affff05ff02ffff05ff2bffff05ff5bffff05ff8200bbffff01ff8080808080808080ffff05ffffff05ff3affff05ff02ffff05ff13ffff05ffff0cff8200bbff8202fb80ffff01ff80808080808080ffff01ff808080808080ffff01ffff09808080ff018080ff05ff2cffff05ff05ffff05ffff0dff0bffff0cff17ff2f8080ffff01ff8080808080ffff05ff34ffff05ffff0bff05ff0bff1780ffff01ff80808080ff05ff2cffff05ff05ffff05ff0bffff01ff8080808080ffffff05ff38ffff05ffff0bff05ff0bff1780ffff01ff80808080ffff05ffff04ffffff05ff20ffff05ff02ffff05ffff0eff05ff1780ffff05ffff0eff0bff2f80ffff01ff80808080808080ffff01ffff05ff28ffff05ff05ffff01ff8080808080ffff01ffff09808080ff018080ffffff05ffff04ffffff05ffff04ffff0affff0bff8217efff8200afff822fef80ff4f80ffff01ffff01ff018080ffff01ffffff05ffff04ffff0aff17ff4f80ffff01ffff01ff018080ffff01ffff01ff80808080ff0180808080ff018080ffff01ffff05ffffff05ff36ffff05ff02ffff05ff820befffff05ff8205efffff05ff05ffff05ff0bffff01ff808080808080808080ffff05ffffff05ff32ffff05ff02ffff05ff8200afffff05ff82016fffff05ff8205efffff05ff825fefffff01ff808080808080808080ffff05ffffff05ff26ffff05ff02ffff05ff4fffff05ff8200afffff05ff82016fffff01ff8080808080808080ffff05ffffff05ff3affff05ff02ffff05ff8202efffff05ff8205efffff01ff80808080808080ffff01ff80808080808080ffff01ffff09808080ff018080ffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff3effff05ff02ffff05ff09ffff01ff808080808080ffffff05ff3effff05ff02ffff05ff0dffff01ff8080808080808080ffff01ffff0bffff01ff0180ff05808080ff01808080ff01808080
|
@ -3,7 +3,7 @@
|
||||
wallet-coin-primary-input
|
||||
wallet-coin-amount)
|
||||
|
||||
(defconstant ASSERT_COIN_CONSUMED 52)
|
||||
(defconstant ASSERT_ANNOUNCEMENT 52)
|
||||
(defconstant ASSERT_MY_COIN_ID 53)
|
||||
|
||||
(defun sha256tree (tree)
|
||||
@ -20,7 +20,7 @@
|
||||
(sha256 wallet-coin-primary-input wallet_puzzle wallet-coin-amount))
|
||||
|
||||
(defun-inline input-of-lock ()
|
||||
(list ASSERT_COIN_CONSUMED (sha256 (parent-coin-id) (create-lock-puzzlehash my-id) 0)))
|
||||
(list ASSERT_ANNOUNCEMENT (sha256 (parent-coin-id) my-id)))
|
||||
|
||||
(list (create-my-id-condition)
|
||||
(input-of-lock))
|
||||
|
1
src/wallet/puzzles/rl_aggregation.clvm.hex
Normal file
1
src/wallet/puzzles/rl_aggregation.clvm.hex
Normal file
@ -0,0 +1 @@
|
||||
ffff05ffff01ffff05ffff05ff06ffff05ff0bffff01ff80808080ffff05ffff05ff04ffff05ffff0bffff0bff17ff05ff2f80ff0b80ffff01ff80808080ffff01ff8080808080ffff05ffff01ffff343580ff01808080
|
@ -6,7 +6,6 @@ from typing import Optional, List, Tuple, Any
|
||||
|
||||
import json
|
||||
from blspy import PrivateKey, AugSchemeMPL, G1Element
|
||||
from clvm_tools import binutils
|
||||
from src.types.coin import Coin
|
||||
from src.types.coin_solution import CoinSolution
|
||||
from src.types.program import Program
|
||||
@ -660,18 +659,6 @@ class RLWallet:
|
||||
agg_spend = CoinSolution(consolidating_coin, Program.to([puzzle, solution]))
|
||||
|
||||
list_of_coinsolutions.append(agg_spend)
|
||||
# Spend lock
|
||||
puzstring = f"(r (c (q 0x{consolidating_coin.name().hex()}) (q ())))"
|
||||
|
||||
puzzle = Program.to(binutils.assemble(puzstring))
|
||||
solution = Program.to(binutils.assemble("()"))
|
||||
|
||||
ephemeral = CoinSolution(
|
||||
Coin(self.rl_coin_record.coin.name(), puzzle.get_tree_hash(), uint64(0)),
|
||||
Program.to([puzzle, solution]),
|
||||
)
|
||||
list_of_coinsolutions.append(ephemeral)
|
||||
|
||||
aggsig = AugSchemeMPL.aggregate([signature])
|
||||
|
||||
return SpendBundle(list_of_coinsolutions, aggsig)
|
||||
|
@ -19,9 +19,10 @@ from src.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
|
||||
from src.wallet.puzzles.puzzle_utils import (
|
||||
make_assert_my_coin_id_condition,
|
||||
make_assert_time_exceeds_condition,
|
||||
make_assert_coin_consumed_condition,
|
||||
make_create_coin_condition,
|
||||
make_assert_fee_condition,
|
||||
make_create_announcement,
|
||||
make_assert_announcement,
|
||||
)
|
||||
from src.wallet.secret_key_store import SecretKeyStore
|
||||
from src.wallet.sign_coin_solutions import sign_coin_solutions
|
||||
@ -132,21 +133,32 @@ class Wallet:
|
||||
async def get_new_puzzlehash(self) -> bytes32:
|
||||
return (await self.wallet_state_manager.get_unused_derivation_record(self.id())).puzzle_hash
|
||||
|
||||
def make_solution(self, primaries=None, min_time=0, me=None, consumed=None, fee=0):
|
||||
def make_solution(
|
||||
self,
|
||||
primaries=None,
|
||||
min_time=0,
|
||||
me=None,
|
||||
announcements=None,
|
||||
announcements_to_consume=None,
|
||||
fee=0,
|
||||
):
|
||||
assert fee >= 0
|
||||
condition_list = []
|
||||
if primaries:
|
||||
for primary in primaries:
|
||||
condition_list.append(make_create_coin_condition(primary["puzzlehash"], primary["amount"]))
|
||||
if consumed:
|
||||
for coin in consumed:
|
||||
condition_list.append(make_assert_coin_consumed_condition(coin))
|
||||
if min_time > 0:
|
||||
condition_list.append(make_assert_time_exceeds_condition(min_time))
|
||||
if me:
|
||||
condition_list.append(make_assert_my_coin_id_condition(me["id"]))
|
||||
if fee:
|
||||
condition_list.append(make_assert_fee_condition(fee))
|
||||
if announcements:
|
||||
for announcement in announcements:
|
||||
condition_list.append(make_create_announcement(announcement))
|
||||
if announcements_to_consume:
|
||||
for announcement_hash in announcements_to_consume:
|
||||
condition_list.append(make_assert_announcement(announcement_hash))
|
||||
return solution_for_conditions(condition_list)
|
||||
|
||||
async def select_coins(self, amount, exclude: List[Coin] = None) -> Set[Coin]:
|
||||
@ -320,8 +332,6 @@ class Wallet:
|
||||
primaries = [{"puzzlehash": newpuzhash, "amount": chia_amount}]
|
||||
solution = self.make_solution(primaries=primaries)
|
||||
output_created = coin
|
||||
else:
|
||||
solution = self.make_solution(consumed=[output_created.name()])
|
||||
list_of_solutions.append(CoinSolution(coin, Program.to([puzzle, solution])))
|
||||
|
||||
await self.hack_populate_secret_keys_for_coin_solutions(list_of_solutions)
|
||||
|
@ -14,6 +14,7 @@ from src.util.ints import uint64
|
||||
from tests.core.full_node.test_full_node import connect_and_get_peer
|
||||
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
||||
from src.util.wallet_tools import WalletTool
|
||||
from src.types.announcement import Announcement
|
||||
|
||||
BURN_PUZZLE_HASH = b"0" * 32
|
||||
|
||||
@ -530,7 +531,7 @@ class TestBlockchainTransactions:
|
||||
assert err is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_assert_coin_consumed(self, two_nodes):
|
||||
async def test_assert_announcement_consumed(self, two_nodes):
|
||||
|
||||
num_blocks = 10
|
||||
wallet_a = WALLET_A
|
||||
@ -561,14 +562,20 @@ class TestBlockchainTransactions:
|
||||
spend_coin_block_2 = coin
|
||||
|
||||
# This condition requires block2 coinbase to be spent
|
||||
block1_cvp = ConditionVarPair(ConditionOpcode.ASSERT_COIN_CONSUMED, [spend_coin_block_2.name()])
|
||||
block1_cvp = ConditionVarPair(
|
||||
ConditionOpcode.ASSERT_ANNOUNCEMENT,
|
||||
[Announcement(spend_coin_block_2.name(), bytes("test", "utf-8")).name()],
|
||||
)
|
||||
block1_dic = {block1_cvp.opcode: [block1_cvp]}
|
||||
block1_spend_bundle = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, spend_coin_block_1, block1_dic
|
||||
)
|
||||
|
||||
# This condition requires block1 coinbase to be spent
|
||||
block2_cvp = ConditionVarPair(ConditionOpcode.ASSERT_COIN_CONSUMED, [spend_coin_block_1.name()])
|
||||
block2_cvp = ConditionVarPair(
|
||||
ConditionOpcode.CREATE_ANNOUNCEMENT,
|
||||
[bytes("test", "utf-8")],
|
||||
)
|
||||
block2_dic = {block2_cvp.opcode: [block2_cvp]}
|
||||
block2_spend_bundle = wallet_a.generate_signed_transaction(
|
||||
1000, receiver_puzzlehash, spend_coin_block_2, block2_dic
|
||||
@ -588,7 +595,7 @@ class TestBlockchainTransactions:
|
||||
# Try to validate that block
|
||||
res, err, _ = await full_node_1.blockchain.receive_block(invalid_new_blocks[-1])
|
||||
assert res == ReceiveBlockResult.INVALID_BLOCK
|
||||
assert err == Err.ASSERT_COIN_CONSUMED_FAILED
|
||||
assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED
|
||||
|
||||
# bundle_together contains both transactions
|
||||
bundle_together = SpendBundle.aggregate([block1_spend_bundle, block2_spend_bundle])
|
||||
|
@ -10,6 +10,7 @@ from src.types.coin_solution import CoinSolution
|
||||
from src.types.condition_var_pair import ConditionVarPair
|
||||
from src.types.condition_opcodes import ConditionOpcode
|
||||
from src.types.spend_bundle import SpendBundle
|
||||
from src.types.announcement import Announcement
|
||||
from src.util.condition_tools import conditions_for_solution
|
||||
from src.util.clvm import int_to_bytes
|
||||
from src.util.ints import uint64
|
||||
@ -441,51 +442,13 @@ class TestMempool:
|
||||
assert sb1 is spend_bundle1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_correct_coin_consumed(self, two_nodes):
|
||||
async def test_correct_announcement_consumed(self, two_nodes):
|
||||
reward_ph = WALLET_A.get_new_puzzlehash()
|
||||
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
||||
blocks = await full_node_1.get_all_full_blocks()
|
||||
start_height = blocks[-1].height
|
||||
start_height = blocks[-1].height if len(blocks) > 0 else -1
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
4,
|
||||
block_list_input=blocks,
|
||||
guarantee_block=True,
|
||||
farmer_reward_puzzle_hash=reward_ph,
|
||||
pool_reward_puzzle_hash=reward_ph,
|
||||
)
|
||||
peer = await connect_and_get_peer(server_1, server_2)
|
||||
|
||||
for block in blocks:
|
||||
await full_node_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block))
|
||||
|
||||
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 4)
|
||||
|
||||
coin_1 = list(blocks[-2].get_included_reward_coins())[0]
|
||||
coin_2 = list(blocks[-1].get_included_reward_coins())[0]
|
||||
cvp = ConditionVarPair(ConditionOpcode.ASSERT_COIN_CONSUMED, [coin_2.name()])
|
||||
dic = {cvp.opcode: [cvp]}
|
||||
|
||||
spend_bundle1 = generate_test_spend_bundle(coin_1, dic)
|
||||
|
||||
spend_bundle2 = generate_test_spend_bundle(coin_2)
|
||||
|
||||
bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
||||
|
||||
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(bundle)
|
||||
await full_node_1.respond_transaction(tx1, peer)
|
||||
|
||||
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
||||
|
||||
assert mempool_bundle is bundle
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_coin_consumed(self, two_nodes):
|
||||
reward_ph = WALLET_A.get_new_puzzlehash()
|
||||
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
||||
blocks = await full_node_1.get_all_full_blocks()
|
||||
start_height = blocks[-1].height
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
4,
|
||||
3,
|
||||
block_list_input=blocks,
|
||||
guarantee_block=True,
|
||||
farmer_reward_puzzle_hash=reward_ph,
|
||||
@ -498,21 +461,121 @@ class TestMempool:
|
||||
|
||||
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
|
||||
|
||||
for b in blocks:
|
||||
await full_node_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(b))
|
||||
coin_1 = list(blocks[-2].get_included_reward_coins())[0]
|
||||
coin_2 = list(blocks[-1].get_included_reward_coins())[0]
|
||||
|
||||
announce = Announcement(coin_2.name(), bytes("test", "utf-8"))
|
||||
|
||||
cvp = ConditionVarPair(ConditionOpcode.ASSERT_ANNOUNCEMENT, [announce.name()])
|
||||
|
||||
dic = {cvp.opcode: [cvp]}
|
||||
|
||||
cvp2 = ConditionVarPair(ConditionOpcode.CREATE_ANNOUNCEMENT, [bytes("test", "utf-8")])
|
||||
dic2 = {cvp.opcode: [cvp2]}
|
||||
spend_bundle1 = generate_test_spend_bundle(coin_1, dic)
|
||||
|
||||
spend_bundle2 = generate_test_spend_bundle(coin_2, dic2)
|
||||
|
||||
bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
||||
|
||||
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(bundle)
|
||||
await full_node_1.respond_transaction(tx1, peer)
|
||||
|
||||
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
||||
|
||||
assert mempool_bundle is bundle
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_announcement_consumed(self, two_nodes):
|
||||
reward_ph = WALLET_A.get_new_puzzlehash()
|
||||
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
||||
blocks = await full_node_1.get_all_full_blocks()
|
||||
start_height = blocks[-1].height if len(blocks) > 0 else -1
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
3,
|
||||
block_list_input=blocks,
|
||||
guarantee_block=True,
|
||||
farmer_reward_puzzle_hash=reward_ph,
|
||||
pool_reward_puzzle_hash=reward_ph,
|
||||
)
|
||||
peer = await connect_and_get_peer(server_1, server_2)
|
||||
|
||||
for block in blocks:
|
||||
await full_node_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block))
|
||||
|
||||
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
|
||||
|
||||
coin_1 = list(blocks[-2].get_included_reward_coins())[0]
|
||||
coin_2 = list(blocks[-1].get_included_reward_coins())[0]
|
||||
cvp = ConditionVarPair(ConditionOpcode.ASSERT_COIN_CONSUMED, [coin_2.name()])
|
||||
|
||||
announce = Announcement(coin_2.name(), bytes("test", "utf-8"))
|
||||
|
||||
cvp = ConditionVarPair(ConditionOpcode.ASSERT_ANNOUNCEMENT, [announce.name()])
|
||||
|
||||
dic = {cvp.opcode: [cvp]}
|
||||
|
||||
cvp2 = ConditionVarPair(
|
||||
ConditionOpcode.CREATE_ANNOUNCEMENT,
|
||||
[bytes("wrong test", "utf-8")],
|
||||
)
|
||||
dic2 = {cvp.opcode: [cvp2]}
|
||||
spend_bundle1 = generate_test_spend_bundle(coin_1, dic)
|
||||
|
||||
assert spend_bundle1 is not None
|
||||
spend_bundle2 = generate_test_spend_bundle(coin_2, dic2)
|
||||
|
||||
bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
||||
|
||||
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1)
|
||||
await full_node_1.respond_transaction(tx1, peer)
|
||||
|
||||
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
|
||||
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
||||
|
||||
assert mempool_bundle is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_announcement_consumed_two(self, two_nodes):
|
||||
reward_ph = WALLET_A.get_new_puzzlehash()
|
||||
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
||||
blocks = await full_node_1.get_all_full_blocks()
|
||||
start_height = blocks[-1].height if len(blocks) > 0 else -1
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
3,
|
||||
block_list_input=blocks,
|
||||
guarantee_block=True,
|
||||
farmer_reward_puzzle_hash=reward_ph,
|
||||
pool_reward_puzzle_hash=reward_ph,
|
||||
)
|
||||
peer = await connect_and_get_peer(server_1, server_2)
|
||||
|
||||
for block in blocks:
|
||||
await full_node_1.full_node.respond_sub_block(full_node_protocol.RespondSubBlock(block))
|
||||
|
||||
await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)
|
||||
|
||||
coin_1 = list(blocks[-2].get_included_reward_coins())[0]
|
||||
coin_2 = list(blocks[-1].get_included_reward_coins())[0]
|
||||
|
||||
announce = Announcement(coin_1.name(), bytes("test", "utf-8"))
|
||||
|
||||
cvp = ConditionVarPair(ConditionOpcode.ASSERT_ANNOUNCEMENT, [announce.name()])
|
||||
|
||||
dic = {cvp.opcode: [cvp]}
|
||||
|
||||
cvp2 = ConditionVarPair(
|
||||
ConditionOpcode.CREATE_ANNOUNCEMENT,
|
||||
[bytes("test", "utf-8")],
|
||||
)
|
||||
dic2 = {cvp.opcode: [cvp2]}
|
||||
spend_bundle1 = generate_test_spend_bundle(coin_1, dic)
|
||||
|
||||
spend_bundle2 = generate_test_spend_bundle(coin_2, dic2)
|
||||
|
||||
bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
|
||||
|
||||
tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1)
|
||||
await full_node_1.respond_transaction(tx1, peer)
|
||||
|
||||
mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(bundle.name())
|
||||
|
||||
assert mempool_bundle is None
|
||||
|
||||
|
0
tests/full_node/__init__.py
Normal file
0
tests/full_node/__init__.py
Normal file
Loading…
Reference in New Issue
Block a user