Skip to content

Commit

Permalink
Added tracking of the Contract state within the manager; refactored a…
Browse files Browse the repository at this point in the history
…ll contracts accordingly; fixed a few minor issues
  • Loading branch information
bigspider committed Jan 13, 2024
1 parent 8d13bd7 commit 30eb9f0
Show file tree
Hide file tree
Showing 9 changed files with 362 additions and 208 deletions.
111 changes: 66 additions & 45 deletions examples/game256/game256_contracts.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@


from dataclasses import dataclass
from matt import NUMS_KEY
from matt.argtypes import BytesType, IntType, SignerType
from matt.btctools.common import sha256
from matt.btctools.script import OP_ADD, OP_CHECKSIG, OP_DUP, OP_FROMALTSTACK, OP_ROT, OP_SHA256, OP_SWAP, OP_TOALTSTACK, CScript
from matt.contracts import ClauseOutput, StandardClause, StandardAugmentedP2TR, StandardP2TR
from matt.btctools.script import OP_ADD, OP_CHECKSIG, OP_DUP, OP_EQUAL, OP_FROMALTSTACK, OP_NOT, OP_PICK, OP_ROT, OP_SHA256, OP_SWAP, OP_TOALTSTACK, OP_VERIFY, CScript
from matt.contracts import ClauseOutput, StandardClause, StandardAugmentedP2TR, StandardP2TR, ContractState
from matt.hub.fraud import Bisect_1, Computer, Leaf
from matt.merkle import MerkleTree
from matt.script_helpers import check_input_contract, check_output_contract, drop, dup, merkle_root, older
Expand All @@ -16,23 +17,19 @@
# TODO: how to generalize what the contract does after the leaf? We should be able to compose clauses with some external code.
# Do we need "clause" algebra?

# TODO: Augmented contracts should also specify the "encoder" for its data, so that callers don't have to worry
# about handling Merkle trees by hand.
# Might also be needed to define "higher order contracts" that can be used as a gadget, then provide a result
# to some other contract provided by the caller.

class G256_S0(StandardP2TR):
def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout: int = 10):
self.alice_pk = alice_pk
self.bob_pk = bob_pk
self.forfait_timeout = forfait_timeout

g256_s1 = G256_S1(alice_pk, bob_pk, forfait_timeout)
# witness: <bob_sig> <x>
choose = StandardClause(
name="choose",
script=CScript([
OP_SHA256, # sha256(x)
*check_output_contract(G256_S1(alice_pk, bob_pk, forfait_timeout)),
*g256_s1.State.encoder_script(),
*check_output_contract(g256_s1),

bob_pk,
OP_CHECKSIG
Expand All @@ -41,42 +38,46 @@ def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout: int = 10):
('bob_sig', SignerType(bob_pk)),
('x', IntType()),
],
next_output_fn=lambda args: [ClauseOutput(
next_outputs_fn=lambda args, _: [ClauseOutput(
n=-1,
next_contract=G256_S1(alice_pk, bob_pk, forfait_timeout),
next_data=sha256(encode_wit_element(args['x']))
next_contract=g256_s1,
next_state=g256_s1.State(x=args['x'])
)]
)

super().__init__(NUMS_KEY, choose)


class G256_S1(StandardAugmentedP2TR):
@dataclass
class State(ContractState):
x: int

def encode(self):
return sha256(encode_wit_element(self.x))

def encoder_script():
return CScript([OP_SHA256])

def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout):
self.alice_pk = alice_pk
self.bob_pk = bob_pk
self.forfait_timeout = forfait_timeout

g256_s2 = G256_S2(alice_pk, bob_pk, forfait_timeout)

# reveal: <alice_sig> <r_a> <y> <sha256(x)>
# reveal: <alice_sig> <t_a> <y> <sha256(x)>
reveal = StandardClause(
name="reveal",
script=CScript([
OP_DUP,

# check that the top of the stack is the embedded data
*self.State.encoder_script(),
*check_input_contract(),

OP_TOALTSTACK,
OP_SHA256,
OP_FROMALTSTACK,

# <alice_sig> <t_a> <sha256(y)> <sha256(x)>
*merkle_root(3),

# <alice_sig> <merkle_root(t_a, sha256(y), sha256(x))>

# <alice_sig> <t_a> <y> <x>
*g256_s2.State.encoder_script(),
*check_output_contract(g256_s2),

alice_pk,
Expand All @@ -86,12 +87,12 @@ def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout):
('alice_sig', SignerType(alice_pk)),
('t_a', BytesType()),
('y', IntType()),
('sha256_x', BytesType()),
('x', IntType()),
],
next_output_fn=lambda args: [ClauseOutput(
next_outputs_fn=lambda args, _: [ClauseOutput(
n=-1,
next_contract=g256_s2,
next_data=MerkleTree([args['t_a'], sha256(encode_wit_element(args['y'])), args['sha256_x']]).root
next_state=g256_s2.State(t_a=args['t_a'], y=args['y'], x=args['x'])
)]
)

Expand All @@ -113,6 +114,21 @@ def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout):


class G256_S2(StandardAugmentedP2TR):
@dataclass
class State(ContractState):
t_a: bytes
y: int
x: bytes

def encode(self):
return MerkleTree([self.t_a, sha256(encode_wit_element(self.y)), sha256(encode_wit_element(self.x))]).root

def encoder_script():
return CScript([
OP_TOALTSTACK, OP_SHA256, OP_FROMALTSTACK, OP_SHA256,
*merkle_root(3)
])

def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout: int = 10):
self.alice_pk = alice_pk
self.bob_pk = bob_pk
Expand All @@ -133,27 +149,32 @@ def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout: int = 10):
def leaf_factory(i: int): return Leaf(alice_pk, bob_pk, Compute2x)

bisectg256_0 = Bisect_1(alice_pk, bob_pk, 0, 7, leaf_factory, forfait_timeout)
# start_challenge: <bob_sig> <t_a> <sha256(y)> <sha256(x)> <z> <t_b>
# start_challenge: <bob_sig> <t_a> <y> <x> <z> <t_b>
start_challenge = StandardClause(
name="start_challenge",
script=CScript([
OP_TOALTSTACK,
OP_SHA256, OP_TOALTSTACK,

# <bob_sig> <t_a> <sha256(y)> <sha256(x)> --- <t_b> <sha256(z)>
# check that y != z
OP_DUP, 3, OP_PICK, OP_EQUAL, OP_NOT, OP_VERIFY,

OP_TOALTSTACK,

# <bob_sig> <t_a> <y> <x> --- <t_b> <z>

# verify the embedded data
*dup(3),
*merkle_root(3),

# verify the embedded data
*self.State.encoder_script(),
*check_input_contract(),

# <bob_sig> <t_a> <sha256(y)> <sha256(x)> --- <t_b> <sha256(z)>
OP_SWAP,
# <bob_sig> <t_a> <sha256(x)> <sha256(y)> --- <t_b> <sha256(z)>
# <bob_sig> <t_a> <y> <x> --- <t_b> <z>
OP_SHA256, OP_SWAP, OP_SHA256,
# <bob_sig> <t_a> <sha256(x)> <sha256(y)> --- <t_b> <z>
OP_ROT,
# <bob_sig> <sha256(x)> <sha256(y)> <t_a> --- <t_b> <sha256(z)>

OP_FROMALTSTACK,
OP_FROMALTSTACK, OP_SHA256,
# <bob_sig> <sha256(x)> <sha256(y)> <t_a> <sha256(z)> --- <t_b>
OP_SWAP,
# <bob_sig> <sha256(x)> <sha256(y)> <sha256(z)> <t_a> --- <t_b>
Expand All @@ -162,7 +183,7 @@ def leaf_factory(i: int): return Leaf(alice_pk, bob_pk, Compute2x)

# <bob_sig> <sha256(x)> <sha256(y)> <sha256(z)> <t_a> <t_b>

*merkle_root(5),
*bisectg256_0.State.encoder_script(),
*check_output_contract(bisectg256_0),

bob_pk,
Expand All @@ -171,21 +192,21 @@ def leaf_factory(i: int): return Leaf(alice_pk, bob_pk, Compute2x)
arg_specs=[
('bob_sig', SignerType(bob_pk)),
('t_a', BytesType()),
('sha256_y', BytesType()),
('sha256_x', BytesType()),
('y', IntType()),
('x', IntType()),
('z', IntType()),
('t_b', BytesType()),
],
next_output_fn=lambda args: [ClauseOutput(
next_outputs_fn=lambda args, _: [ClauseOutput(
n=-1,
next_contract=bisectg256_0,
next_data=MerkleTree([
args['sha256_x'],
args['sha256_y'],
sha256(encode_wit_element(args['z'])),
args['t_a'],
args['t_b'],
]).root
next_state=bisectg256_0.State(
h_i=sha256(encode_wit_element(args['x'])),
h_j_plus_1_a=sha256(encode_wit_element(args['y'])),
h_j_plus_1_b=sha256(encode_wit_element(args['z'])),
t_i_j_a=args['t_a'],
t_i_j_b=args['t_b'],
)
)]
)

Expand Down
115 changes: 67 additions & 48 deletions examples/ram/ram_contracts.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
from dataclasses import dataclass
from typing import List

from matt import CCV_FLAG_CHECK_INPUT, NUMS_KEY
from matt.argtypes import BytesType, MerkleProofType
from matt.btctools.script import OP_CAT, OP_CHECKCONTRACTVERIFY, OP_DUP, OP_ELSE, OP_ENDIF, OP_EQUAL, OP_EQUALVERIFY, OP_FROMALTSTACK, OP_IF, OP_NOTIF, OP_PICK, OP_ROLL, OP_ROT, OP_SHA256, OP_SWAP, OP_TOALTSTACK, OP_TRUE, CScript
from matt.contracts import ClauseOutput, StandardClause, StandardAugmentedP2TR
from matt.merkle import is_power_of_2, floor_lg
from matt.contracts import ClauseOutput, StandardClause, StandardAugmentedP2TR, ContractState
from matt.merkle import MerkleTree, is_power_of_2, floor_lg
from matt.script_helpers import merkle_root


class RAM(StandardAugmentedP2TR):
def __init__(self, size: List[bytes]):
@dataclass
class State(ContractState):
leaves: List[bytes]

def encode(self):
return MerkleTree(self.leaves).root

def encoder_script(size: int):
return merkle_root(size)

def __init__(self, size: int):
assert is_power_of_2(size)

self.size = size
Expand Down Expand Up @@ -57,6 +69,19 @@ def __init__(self, size: List[bytes]):
]
)

def next_outputs_fn(args: dict, state: RAM.State):
i: int = args["merkle_proof"].get_leaf_index()

return [
ClauseOutput(
n=-1,
next_contract=self,
next_state=self.State(
leaves=state.leaves[:i] + [args["new_value"]] + state.leaves[i+1:]
)
)
]

# witness: <h_1> <d_1> <h_2> <d_2> ... <h_n> <d_n> <x_old> <x_new> <root>
write = StandardClause(
name="write",
Expand Down Expand Up @@ -86,44 +111,44 @@ def __init__(self, size: List[bytes]):
# TODO: seems too verbose, there should be a way of optimizing it
# top of stack is now: <h> <x_old> <x_new> <d>
OP_IF,
# top of stack is now: <h> <x_old> <x_new>
# right child: we want h || x
2, OP_PICK,
# top of stack is now: <h> <x_old> <x_new> <h>
OP_SWAP,
OP_CAT,
OP_SHA256,
# top of stack is now: <h> <x_old> <SHA(h || x_new)>

OP_SWAP,
# top of stack is now: <h> <SHA(h || x_new)> <x_old>
OP_ROT,
# top of stack is now: <SHA(h || x_new)> <x_old> <h>
OP_SWAP,
# OP_CAT,
# OP_SHA256,
# # top of stack is now: <SHA(h || x_new)> <SHA(h || x_old)>

# OP_SWAP,
# # top of stack is now: <SHA(h || x_old)> <SHA(h || x_new)>
# top of stack is now: <h> <x_old> <x_new>
# right child: we want h || x
2, OP_PICK,
# top of stack is now: <h> <x_old> <x_new> <h>
OP_SWAP,
OP_CAT,
OP_SHA256,
# top of stack is now: <h> <x_old> <SHA(h || x_new)>

OP_SWAP,
# top of stack is now: <h> <SHA(h || x_new)> <x_old>
OP_ROT,
# top of stack is now: <SHA(h || x_new)> <x_old> <h>
OP_SWAP,
# OP_CAT,
# OP_SHA256,
# # top of stack is now: <SHA(h || x_new)> <SHA(h || x_old)>

# OP_SWAP,
# # top of stack is now: <SHA(h || x_old)> <SHA(h || x_new)>
OP_ELSE,
# top of stack is now: <h> <x_old> <x_new>
2, OP_PICK,
# top of stack is now: <h> <x_old> <x_new> <h>
OP_CAT,
OP_SHA256,
# top of stack is now: <h> <x_old> <SHA(x_new || h)>

OP_SWAP,
OP_ROT,
# top of stack is now: <SHA(x_new || h)> <x_old> <h>

# OP_CAT,
# OP_SHA256,
# # top of stack is now: <SHA(x_new || h)> <SHA(x_old || h)>

# OP_SWAP,
# # top of stack is now: <SHA(x_old || h)> <SHA(x_new || h)>
# top of stack is now: <h> <x_old> <x_new>
2, OP_PICK,
# top of stack is now: <h> <x_old> <x_new> <h>
OP_CAT,
OP_SHA256,
# top of stack is now: <h> <x_old> <SHA(x_new || h)>

OP_SWAP,
OP_ROT,
# top of stack is now: <SHA(x_new || h)> <x_old> <h>

# OP_CAT,
# OP_SHA256,
# # top of stack is now: <SHA(x_new || h)> <SHA(x_old || h)>

# OP_SWAP,
# # top of stack is now: <SHA(x_old || h)> <SHA(x_new || h)>
OP_ENDIF,

# this is in common between the two branches, so we can put it here
Expand All @@ -144,7 +169,7 @@ def __init__(self, size: List[bytes]):
# stack: <new_root>

# Check that new_root is committed in the next output,
0, # index
-1, # index
0, # NUMS
-1, # keep current taptree
0, # default, preserve amount
Expand All @@ -158,13 +183,7 @@ def __init__(self, size: List[bytes]):
('new_value', BytesType()),
('merkle_root', BytesType()),
],
next_output_fn=lambda args: [
ClauseOutput(
n=0,
next_contract=self,
next_data=args["merkle_proof"].get_new_root_after_update(args["new_value"])
)
]
next_outputs_fn=next_outputs_fn
)

super().__init__(NUMS_KEY, [withdraw, write])
Loading

0 comments on commit 30eb9f0

Please sign in to comment.