Skip to content

Commit

Permalink
Transaction matching (Chia-Network#1932)
Browse files Browse the repository at this point in the history
Co-authored-by: Adam Kelly <[email protected]>
  • Loading branch information
aqk and aqk authored Apr 15, 2021
1 parent 065a539 commit 24fdd37
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-test-macos-generator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ jobs:
- name: Test generator code with pytest
run: |
. ./activate
./venv/bin/py.test tests/generator/test_compression.py tests/generator/test_generator_types.py -s -v --durations 0
./venv/bin/py.test tests/generator/test_compression.py tests/generator/test_generator_types.py tests/generator/test_scan.py -s -v --durations 0
2 changes: 1 addition & 1 deletion .github/workflows/build-test-ubuntu-generator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,4 @@ jobs:
- name: Test generator code with pytest
run: |
. ./activate
./venv/bin/py.test tests/generator/test_compression.py tests/generator/test_generator_types.py -s -v --durations 0
./venv/bin/py.test tests/generator/test_compression.py tests/generator/test_generator_types.py tests/generator/test_scan.py -s -v --durations 0
25 changes: 25 additions & 0 deletions chia/full_node/bundle_tools.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import re
from typing import Optional, Tuple

from clvm import SExp
from clvm_tools import binutils

from chia.types.blockchain_format.program import SerializedProgram
from chia.types.spend_bundle import SpendBundle
from chia.util.byte_types import hexstr_to_bytes


def best_solution_program(bundle: SpendBundle) -> SerializedProgram:
Expand All @@ -20,3 +24,24 @@ def best_solution_program(bundle: SpendBundle) -> SerializedProgram:
]
r.append(entry)
return SerializedProgram.from_bytes(SExp.to((binutils.assemble("#q"), r)).as_bin())


STANDARD_TRANSACTION_PUZZLE_PATTERN = re.compile(
r"""ff02ffff01ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080ffff04ffff01(b0[a-f0-9]{96})ff018080""" # noqa
)


# match_standard_transaction_anywhere
def match_standard_transaction_at_any_index(generator_body: bytes) -> Optional[Tuple[int, int]]:
"Return (start, end) of match, or None if pattern could not be found"
m = STANDARD_TRANSACTION_PUZZLE_PATTERN.search(generator_body.hex())
if m:
assert m.start() % 2 == 0 and m.end() % 2 == 0
return (m.start() // 2, m.end() // 2)
else:
return None


def match_standard_transaction_exactly_and_return_pubkey(transaction: bytes) -> Optional[bytes]:
m = STANDARD_TRANSACTION_PUZZLE_PATTERN.fullmatch(transaction.hex())
return None if m is None else hexstr_to_bytes(m.group(1))
59 changes: 59 additions & 0 deletions tests/generator/test_scan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from unittest import TestCase
from chia.full_node.bundle_tools import (
match_standard_transaction_at_any_index,
match_standard_transaction_exactly_and_return_pubkey,
)
from chia.util.byte_types import hexstr_to_bytes


gen1 = hexstr_to_bytes(
"ff01ffffffa00000000000000000000000000000000000000000000000000000000000000000ff830186a080ffffff02ffff01ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080ffff04ffff01b081963921826355dcb6c355ccf9c2637c18adf7d38ee44d803ea9ca41587e48c913d8d46896eb830aeadfc13144a8eac3ff018080ffff80ffff01ffff33ffa06b7a83babea1eec790c947db4464ab657dbe9b887fe9acc247062847b8c2a8a9ff830186a08080ff8080808080" # noqa
)

EXPECTED_START = 46
EXPECTED_END = 337

STANDARD_TRANSACTION_1 = hexstr_to_bytes(
"""ff02ffff01ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080ffff04ffff01b0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaff018080""" # noqa
)

STANDARD_TRANSACTION_2 = hexstr_to_bytes(
"""ff02ffff01ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080ffff04ffff01b0bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbff018080""" # noqa
)


class TestScan(TestCase):
def test_match_generator(self):
# match_standard_transaction_at_any_index(generator_body: bytes) -> (int,int):
m = match_standard_transaction_at_any_index(gen1)
assert m == (EXPECTED_START, EXPECTED_END)

m = match_standard_transaction_at_any_index(b"\xff" + gen1 + b"\x80")
assert m == (EXPECTED_START + 1, EXPECTED_END + 1)

m = match_standard_transaction_at_any_index(gen1[47:])
assert m is None

def test_match_transaction(self):
# match_standard_transaction_exactly_and_return_pubkey(transaction: bytes) -> Optional[bytes]:
m = match_standard_transaction_exactly_and_return_pubkey(STANDARD_TRANSACTION_1)
assert m == hexstr_to_bytes(
"b0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
)

m = match_standard_transaction_exactly_and_return_pubkey(STANDARD_TRANSACTION_1 + b"\xfa")
assert m is None

m = match_standard_transaction_exactly_and_return_pubkey(b"\xba" + STANDARD_TRANSACTION_1 + b"\xfa")
assert m is None

m = match_standard_transaction_exactly_and_return_pubkey(b"\xba" + STANDARD_TRANSACTION_1)
assert m is None

m = match_standard_transaction_exactly_and_return_pubkey(gen1[EXPECTED_START:EXPECTED_END])
assert m == hexstr_to_bytes(
"b081963921826355dcb6c355ccf9c2637c18adf7d38ee44d803ea9ca41587e48c913d8d46896eb830aeadfc13144a8eac3"
)

m = match_standard_transaction_exactly_and_return_pubkey(gen1)
assert m is None

0 comments on commit 24fdd37

Please sign in to comment.