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:
matt-o-how 2021-02-10 18:28:13 +00:00 committed by GitHub
parent 9d064fea4a
commit b37e6e767b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 284 additions and 475 deletions

View File

@ -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,

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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
View 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))

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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]

View File

@ -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)

View File

@ -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]:

View File

@ -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

View File

@ -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(

View File

@ -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:

View File

@ -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()

View File

@ -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
)
)

View File

@ -1 +1 @@
ffff05ffff01ffffff05ff5affff05ff02ffff05ffff05ff05ffff05ffffff05ff2effff05ff02ffff05ff05ffff01ff808080808080ffff05ff0bffff05ffffff05ff2effff05ff02ffff05ff0bffff01ff808080808080ffff01ff808080808080ffff05ffffff05ff17ff2f8080ffff05ff5fffff05ff8200bfffff05ff82017fffff05ff8202ffffff01ff808080808080808080808080ffff05ffff01ffffffffff3435ff33ffff05ff5effff05ff02ffff05ffffff05ff34ffff05ff02ffff05ff09ffff05ff15ffff05ff5dffff05ff0bffff01ff808080808080808080ffff05ff09ffff05ff15ffff05ff5dffff05ff0bffff01ff80808080808080808080ffffff0bff09ff15ff2d80ffff05ff2cffff05ff02ffff05ff05ffff05ff07ffff01ff80808080808080ffff05ffff05ffff01ff0580ffff05ffff05ffff01ff0180ffff05ff05ffff01ff80808080ffff05ffffff05ff5cffff05ff02ffff05ff0bffff01ffff01808080808080ffff01ff8080808080ffff01ff808080ffffff05ffff04ff05ffff01ffff05ffff01ff0580ffff05ffff05ffff01ff0180ffff05ff09ffff01ff80808080ffff05ffffff05ff5cffff05ff02ffff05ff0dffff05ff0bffff01ff80808080808080ffff01ff808080808080ffff01ff0b8080ff0180802dffffffffff05ffff04ff15ffff01ffffff05ff2affff05ff02ffff05ff0bffff05ff09ffff05ff1dffff01ff808080808080808080ffff01ffffff05ffffff05ff7cffff05ff02ffff05ff0bffff01ff808080808080ffff05ff0bffff05ff09ffff05ff1dffff01ff808080808080808080ff018080ffff05ffff04ff0bffff01ffffff05ffff04ffff0aff05ff1380ffff01ffff01ff018080ffff01ffffff05ff32ffff05ff02ffff05ff05ffff05ff1bffff01ff808080808080808080ff01808080ffff01ffff01ff80808080ff018080ffff0aff13ffff0bff27ffffff05ff38ffff05ff02ffff05ff05ffff05ff57ffff01ff80808080808080ff8200b78080ffffff05ffff04ffffff05ff22ffff05ff02ffff05ff17ffff05ff05ffff01ff80808080808080ffff01ffffff05ffff04ffffff05ff22ffff05ff02ffff05ff2fffff05ff05ffff01ff80808080808080ffff01ffffff05ffff04ffffff05ff22ffff05ff02ffff05ff5fffff05ff05ffff01ff80808080808080ffff01ffff05ffff05ff30ffff05ffffff05ff24ffff05ff02ffff05ff4fffff01ff808080808080ffff01ff80808080ffff05ffff05ff28ffff05ffffff05ff36ffff05ff02ffff05ff27ffff05ff8200bfffff01ff80808080808080ffff01ffff8080808080ffff05ffff05ff20ffff05ffff0bffffff05ff24ffff05ff02ffff05ff82009fffff01ff808080808080ffffff05ff36ffff05ff02ffff05ff4fffff05ffff0cff8200bfffff0dff8202cfffffff05ff26ffff05ff02ffff05ff0bffff01ff8080808080808080ffff01ff80808080808080ffff01ff808080ffff01ff80808080ffffff05ff7affff05ff02ffff05ff0bffff05ff05ffff01ff8080808080808080808080ffff01ffff09808080ff01808080ffff01ffff09808080ff01808080ffff01ffff09808080ff018080ffff05ffff04ff05ffff01ffff05ffffff05ffff04ffff0aff11ff2880ffff01ffff05ff28ffff05ffffff05ff38ffff05ff02ffff05ff0bffff05ff29ffff01ff80808080808080ffff05ff59ffff01ff808080808080ffff01ff098080ff018080ffffff05ff7affff05ff02ffff05ff0dffff05ff0bffff01ff808080808080808080ffff01ffff01ff80808080ff018080ffffffff05ffff04ff05ffff01ffff0cffffff05ffff04ffff0aff11ff2880ffff01ff5980ffff01ffff01ff80808080ff018080ffffff05ff26ffff05ff02ffff05ff0dffff01ff8080808080808080ffff01ffff01ff80808080ff018080ffff05ff2effff05ff02ffff05ffffff05ff34ffff05ff02ffff05ffff01ffff01ff808080ffff05ff05ffff05ff0bffff01ff8080808080808080ffff01ff808080808080ffffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff2effff05ff02ffff05ff09ffff01ff808080808080ffffff05ff2effff05ff02ffff05ff0dffff01ff8080808080808080ffff01ffff0bffff01ff0180ff05808080ff018080ffffff05ff7effff05ff02ffff05ff05ffff05ff07ffff01ff80808080808080ffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff7effff05ff02ffff05ff09ffff05ff0bffff01ff80808080808080ffffff05ff7effff05ff02ffff05ff0dffff05ff0bffff01ff808080808080808080ffff01ffffff05ffff04ffffff05ff32ffff05ff02ffff05ff05ffff05ff0bffff01ff80808080808080ffff01ff0580ffff01ffff0bffff01ff0180ff05808080ff0180808080ff01808080ff01808080
ffff05ffff01ffffff05ff7affff05ff02ffff05ffff05ff05ffff05ffffff05ff2effff05ff02ffff05ff05ffff01ff808080808080ffff05ff0bffff05ffffff05ff2effff05ff02ffff05ff0bffff01ff808080808080ffff01ff808080808080ffff05ffffff05ff17ff2f8080ffff05ff5fffff05ff8200bfffff05ff82017fffff05ff8202ffffff01ff808080808080808080808080ffff05ffff01ffffffffff3435ff3c33ffffffff05ff5effff05ff02ffff05ffffff05ff2cffff05ff02ffff05ff09ffff05ff15ffff05ff5dffff05ff0bffff01ff808080808080808080ffff05ff09ffff05ff15ffff05ff5dffff05ff0bffff01ff80808080808080808080ff0bff09ff15ff2d80ffffff05ff5cffff05ff02ffff05ff05ffff05ff07ffff01ff80808080808080ffff05ffff05ffff01ff0580ffff05ffff05ffff01ff0180ffff05ff05ffff01ff80808080ffff05ffffff05ff7cffff05ff02ffff05ff0bffff01ffff01808080808080ffff01ff8080808080ffff01ff808080ffff05ffff04ff05ffff01ffff05ffff01ff0580ffff05ffff05ffff01ff0180ffff05ff09ffff01ff80808080ffff05ffffff05ff7cffff05ff02ffff05ff0dffff05ff0bffff01ff80808080808080ffff01ff808080808080ffff01ff0b8080ff018080ffffff2dffff05ffff04ff15ffff01ffffff05ff5affff05ff02ffff05ff0bffff05ff09ffff05ff1dffff01ff808080808080808080ffff01ffffff05ffffff05ff22ffff05ff02ffff05ff0bffff01ff808080808080ffff05ff0bffff05ff09ffff05ff1dffff01ff808080808080808080ff018080ffffff05ffff04ff0bffff01ffffff05ffff04ffff0aff05ff1380ffff01ffff01ff018080ffff01ffffff05ff2affff05ff02ffff05ff05ffff05ff1bffff01ff808080808080808080ff01808080ffff01ffff01ff80808080ff018080ffff0aff13ffff0bff27ffffff05ff24ffff05ff02ffff05ff05ffff05ff57ffff01ff80808080808080ff8200b78080ffff05ffff04ffffff05ff32ffff05ff02ffff05ff17ffff05ff05ffff01ff80808080808080ffff01ffffff05ffff04ffffff05ff32ffff05ff02ffff05ff2fffff05ff05ffff01ff80808080808080ffff01ffffff05ffff04ffffff05ff32ffff05ff02ffff05ff5fffff05ff05ffff01ff80808080808080ffff01ffff05ffff05ff30ffff05ffffff05ff34ffff05ff02ffff05ff4fffff01ff808080808080ffff01ff80808080ffff05ffff05ff28ffff05ffffff05ff2effff05ff02ffff05ff27ffff05ff8200bfffff01ff80808080808080ffff01ff80808080ffff05ffff05ff20ffff05ffff0bffffff05ff34ffff05ff02ffff05ff82009fffff01ff808080808080ffffff05ff2effff05ff02ffff05ff4fffff05ffff0cff8200bfffff0dff8202cfffffff05ff36ffff05ff02ffff05ff0bffff01ff8080808080808080ffff01ff8080808080808080ffff01ff80808080ffffff05ff26ffff05ff02ffff05ff0bffff05ff05ffff01ff8080808080808080808080ffff01ffff09808080ff01808080ffff01ffff09808080ff01808080ffff01ffff09808080ff018080ffffffff05ffff04ff05ffff01ffff05ffffff05ffff04ffff0aff11ff3880ffff01ffff05ff38ffff05ffffff05ff24ffff05ff02ffff05ff0bffff05ff29ffff01ff80808080808080ffff05ff59ffff01ff808080808080ffff01ff098080ff018080ffffff05ff26ffff05ff02ffff05ff0dffff05ff0bffff01ff808080808080808080ffff01ffff01ff80808080ff018080ffff05ffff04ff05ffff01ffff0cffffff05ffff04ffff0aff11ff3880ffff01ff5980ffff01ffff01ff80808080ff018080ffffff05ff36ffff05ff02ffff05ff0dffff01ff8080808080808080ffff01ffff01ff80808080ff018080ffffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff2effff05ff02ffff05ff09ffff01ff808080808080ffffff05ff2effff05ff02ffff05ff0dffff01ff8080808080808080ffff01ffff0bffff01ff0180ff05808080ff018080ffffff05ff7effff05ff02ffff05ff05ffff05ff07ffff01ff80808080808080ffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff7effff05ff02ffff05ff09ffff05ff0bffff01ff80808080808080ffffff05ff7effff05ff02ffff05ff0dffff05ff0bffff01ff808080808080808080ffff01ffffff05ffff04ffffff05ff2affff05ff02ffff05ff05ffff05ff0bffff01ff80808080808080ffff01ff0580ffff01ffff0bffff01ff0180ff05808080ff0180808080ff01808080ff01808080

View File

@ -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)
)

View File

@ -1 +0,0 @@
ffff05ffff01ffffff05ffff04ffff08ff2f80ffff01ffffff05ff26ffff05ff02ffff05ff05ffff05ff0bffff05ffffff05ff17ff8200bf8080ffff05ffff0bffff0bff4fffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ff8200afffff01ff8080808080808080ff82016f80ffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ffffff05ff2effff05ff02ffff05ff17ffff01ff808080808080ffff01ff8080808080808080ff5f80ffff05ffff0bff82027fffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ff82057fffff01ff8080808080808080ff820b7f80ffff05ff8202ffffff01ff808080808080808080808080ffff01ffff05ffff05ff28ffff05ffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ffffff05ff2effff05ff02ffff05ff17ffff01ff808080808080ffff01ff8080808080808080ffff05ff5fffff01ff8080808080ffff05ffffff05ff22ffff05ff02ffff05ff0bffff05ff2fffff05ffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ffffff05ff2effff05ff02ffff05ff17ffff01ff808080808080ffff01ff8080808080808080ffff05ff5fffff01ff808080808080808080ffff01ff808080808080ff01808080ffff05ffff01ffffffffff3435ff33ffff05ff5effff05ff02ffff05ffffff05ff2cffff05ff02ffff05ff05ffff05ffffff05ff2effff05ff02ffff05ff05ffff01ff808080808080ffff05ff0bffff05ff17ffff01ff808080808080808080ffff05ff05ffff05ffffff05ff2effff05ff02ffff05ff05ffff01ff808080808080ffff05ff17ffff01ff808080808080808080ffffffff05ffff04ffff08ff1780ffff01ffffff05ff24ffff05ff02ffff05ff05ffff05ff0bffff05ff37ffff05ff2fffff05ffffff05ff34ffff05ff02ffff05ffffff05ff2affff05ff02ffff05ff05ffff05ff0bffff05ff27ffff01ff8080808080808080ffff05ff2fffff05ff8202e7ffff05ff5fffff01ff808080808080808080ffff05ffff0cff820167ff8200bf80ffff05ffff0cff8202e7ff82017f80ffff01ff80808080808080808080808080ffff01ffffff05ffff04ffff0aff8200bfff82017f80ffff01ff5f80ffff01ffff09808080ff0180808080ff018080ff05ffff05ff20ffff05ffff0bff05ffffff05ff2effff05ff02ffff05ffffff05ff2cffff05ff02ffff05ffff01ffff01ff808080ffff05ff0bffff05ff17ffff01ff8080808080808080ffff01ff808080808080ffff01ff808080ffff01ff80808080ffff05ffff05ff28ffff05ffffff05ff2effff05ff02ffff05ffffff05ff2cffff05ff02ffff05ffff01ffff01ff808080ffff05ff05ffff01ff80808080808080ffff01ff808080808080ffff01ffff8080808080ff2f8080ffffff05ff3cffff05ff02ffff05ff05ffff05ff07ffff01ff80808080808080ff05ffff05ffff01ff0580ffff05ffff05ffff01ff0180ffff05ff05ffff01ff80808080ffff05ffffff05ff32ffff05ff02ffff05ff0bffff01ffff01808080808080ffff01ff8080808080ffff01ff808080ffffffffff05ffff04ffff0aff0bff0580ffff01ffff05ff30ffff05ffff0bff0bff17ff2f80ffff01ff8080808080ffff01ffff05ff30ffff05ffff0bff0bff17ffff01ff808080ffff01ff808080808080ff018080ffff05ffff04ff05ffff01ffff05ffff01ff0580ffff05ffff05ffff01ff0180ffff05ff09ffff01ff80808080ffff05ffffff05ff32ffff05ff02ffff05ff0dffff05ff0bffff01ff80808080808080ffff01ff808080808080ffff01ff0b8080ff018080ffff0bff27ffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ff57ffff01ff8080808080808080ff8200b780ffff05ffff04ff0bffff01ffffff05ffff04ffff0aff05ff1380ffff01ffff01ff018080ffff01ffffff05ff3affff05ff02ffff05ff05ffff05ff1bffff01ff808080808080808080ff01808080ffff01ffff01ff80808080ff018080ffffffff05ffff04ff8200bfffff01ffffff05ff24ffff05ff02ffff05ff05ffff05ff0bffff05ff8200bfffff05ff2fffff05ffffff05ff36ffff05ff02ffff05ff05ffff05ff0bffff05ff17ffff05ffff01ff8080ffff05ffff01ff8080ffff05ff2fffff05ff5fffff01ff808080808080808080808080ffff01ffff80ff808080808080808080808080ffff01ffffff05ff36ffff05ff02ffff05ff05ffff05ff0bffff05ff17ffff05ffff01ff8080ffff05ffff01ff8080ffff05ff2fffff05ff5fffff01ff8080808080808080808080808080ff018080ffff05ffff04ff17ffff01ffffff05ffff04ffff0aff47ff2880ffff01ffffff05ff36ffff05ff02ffff05ff05ffff05ff0bffff05ff37ffff05ffff05ffff05ff28ffff05ffffff05ff38ffff05ff02ffff05ff05ffff05ff0bffff05ff8200a7ffff01ff8080808080808080ffff05ff820167ffff01ff8080808080ff2f80ffff05ffff0cff820167ff5f80ffff05ff8200bfffff05ff82017fffff01ff80808080808080808080808080ffff01ffffff05ff36ffff05ff02ffff05ff05ffff05ff0bffff05ff37ffff05ffff05ff27ff2f80ffff05ff5fffff05ff8200bfffff05ff82017fffff01ff8080808080808080808080808080ff01808080ffff01ffff05ffff05ff30ffff05ff8200bfffff01ff80808080ffff05ffff05ff20ffff05ffff0bff82017fffffff05ff2effff05ff02ffff05ffffff05ff2cffff05ff02ffff05ffff01ffff01ff808080ffff05ff8200bfffff01ff80808080808080ffff01ff808080808080ffff01ff808080ffff01ff80808080ffff05ffff05ff28ffff05ffffff05ff2effff05ff02ffff05ffffff05ff2cffff05ff02ffff05ffff01ffff01ff808080ffff05ff82017fffff05ff5fffff01ff8080808080808080ffff01ff808080808080ffff01ffff8080808080ff2f8080808080ff018080ffffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff2effff05ff02ffff05ff09ffff01ff808080808080ffffff05ff2effff05ff02ffff05ff0dffff01ff8080808080808080ffff01ffff0bffff01ff0180ff05808080ff018080ffffff05ff7effff05ff02ffff05ff05ffff05ff07ffff01ff80808080808080ffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff7effff05ff02ffff05ff09ffff05ff0bffff01ff80808080808080ffffff05ff7effff05ff02ffff05ff0dffff05ff0bffff01ff808080808080808080ffff01ffffff05ffff04ffffff05ff3affff05ff02ffff05ff05ffff05ff0bffff01ff80808080808080ffff01ff0580ffff01ffff0bffff01ff0180ff05808080ff0180808080ff01808080ff01808080

View File

@ -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]

View File

@ -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

View File

@ -0,0 +1 @@
ffff05ffff01ffffff05ffff04ffff0aff8200bfff2480ffff01ffff05ffff05ff30ffff05ff5fffff05ffffff05ff3effff05ff02ffff05ffff05ff8200bfff8200ff80ffff01ff808080808080ffff01ff8080808080ff8200ff8080ffff01ffff05ffff05ff30ffff05ff05ffff05ffffff05ff3effff05ff02ffff05ffff05ff8200bfff8200ff80ffff01ff808080808080ffff01ff8080808080ffffff05ffff04ffff0aff8200bfff3c80ffff01ffffff05ff2effff05ff02ffff05ff0bffff05ff17ffff05ff2fffff05ff8200ffffff01ff80808080808080808080ffff01ffffff05ff22ffff05ff02ffff05ff2fffff05ff8200ffffff01ff808080808080808080ff018080808080ff01808080ffff05ffff01ffffffffffffff05ffff04ffff16ff05ff0b80ffff01ffff01ff018080ffff01ffffff05ffff04ffff0aff05ff0b80ffff01ffff01ff018080ffff01ffff01ff80808080ff0180808080ff01808032ff3835ffff033cff3301ffffffffff05ffff04ffffff05ffff04ffff0affff0bff820bfbff13ff8205fb80ff82017b80ffff01ffff01ff018080ffff01ffffff05ffff04ffff0aff05ff82017b80ffff01ffff01ff018080ffff01ffff01ff80808080ff0180808080ff018080ffff01ffff05ffffff05ff26ffff05ff02ffff05ff82017bffff05ff13ffff05ff8202fbffff01ff8080808080808080ffff05ffffff05ff2affff05ff02ffff05ff2bffff05ff5bffff05ff8200bbffff01ff8080808080808080ffff05ffffff05ff3affff05ff02ffff05ff13ffff05ffff0cff8200bbff8202fb80ffff01ff80808080808080ffff01ff808080808080ffff01ffff09808080ff018080ff05ff2cffff05ff05ffff05ffff0dff0bffff0cff17ff2f8080ffff01ff8080808080ffff05ff34ffff05ffff0bff05ff0bff1780ffff01ff80808080ff05ff2cffff05ff05ffff05ff0bffff01ff8080808080ffffff05ff38ffff05ffff0bff05ff0bff1780ffff01ff80808080ffff05ffff04ffffff05ff20ffff05ff02ffff05ffff0eff05ff1780ffff05ffff0eff0bff2f80ffff01ff80808080808080ffff01ffff05ff28ffff05ff05ffff01ff8080808080ffff01ffff09808080ff018080ffffff05ffff04ffffff05ffff04ffff0affff0bff8217efff8200afff822fef80ff4f80ffff01ffff01ff018080ffff01ffffff05ffff04ffff0aff17ff4f80ffff01ffff01ff018080ffff01ffff01ff80808080ff0180808080ff018080ffff01ffff05ffffff05ff36ffff05ff02ffff05ff820befffff05ff8205efffff05ff05ffff05ff0bffff01ff808080808080808080ffff05ffffff05ff32ffff05ff02ffff05ff8200afffff05ff82016fffff05ff8205efffff05ff825fefffff01ff808080808080808080ffff05ffffff05ff26ffff05ff02ffff05ff4fffff05ff8200afffff05ff82016fffff01ff8080808080808080ffff05ffffff05ff3affff05ff02ffff05ff8202efffff05ff8205efffff01ff80808080808080ffff01ff80808080808080ffff01ffff09808080ff018080ffff05ffff04ffff08ff0580ffff01ffff0bffff01ff0280ffffff05ff3effff05ff02ffff05ff09ffff01ff808080808080ffffff05ff3effff05ff02ffff05ff0dffff01ff8080808080808080ffff01ffff0bffff01ff0180ff05808080ff01808080ff01808080

View File

@ -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))

View File

@ -0,0 +1 @@
ffff05ffff01ffff05ffff05ff06ffff05ff0bffff01ff80808080ffff05ffff05ff04ffff05ffff0bffff0bff17ff05ff2f80ff0b80ffff01ff80808080ffff01ff8080808080ffff05ffff01ffff343580ff01808080

View File

@ -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)

View File

@ -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)

View File

@ -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])

View File

@ -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

View File