Skip to content

Commit 30eb9f0

Browse files
committed
Added tracking of the Contract state within the manager; refactored all contracts accordingly; fixed a few minor issues
1 parent 8d13bd7 commit 30eb9f0

File tree

9 files changed

+362
-208
lines changed

9 files changed

+362
-208
lines changed

Diff for: examples/game256/game256_contracts.py

+66-45
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11

22

3+
from dataclasses import dataclass
34
from matt import NUMS_KEY
45
from matt.argtypes import BytesType, IntType, SignerType
56
from matt.btctools.common import sha256
6-
from matt.btctools.script import OP_ADD, OP_CHECKSIG, OP_DUP, OP_FROMALTSTACK, OP_ROT, OP_SHA256, OP_SWAP, OP_TOALTSTACK, CScript
7-
from matt.contracts import ClauseOutput, StandardClause, StandardAugmentedP2TR, StandardP2TR
7+
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
8+
from matt.contracts import ClauseOutput, StandardClause, StandardAugmentedP2TR, StandardP2TR, ContractState
89
from matt.hub.fraud import Bisect_1, Computer, Leaf
910
from matt.merkle import MerkleTree
1011
from matt.script_helpers import check_input_contract, check_output_contract, drop, dup, merkle_root, older
@@ -16,23 +17,19 @@
1617
# TODO: how to generalize what the contract does after the leaf? We should be able to compose clauses with some external code.
1718
# Do we need "clause" algebra?
1819

19-
# TODO: Augmented contracts should also specify the "encoder" for its data, so that callers don't have to worry
20-
# about handling Merkle trees by hand.
21-
# Might also be needed to define "higher order contracts" that can be used as a gadget, then provide a result
22-
# to some other contract provided by the caller.
23-
2420
class G256_S0(StandardP2TR):
2521
def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout: int = 10):
2622
self.alice_pk = alice_pk
2723
self.bob_pk = bob_pk
2824
self.forfait_timeout = forfait_timeout
2925

26+
g256_s1 = G256_S1(alice_pk, bob_pk, forfait_timeout)
3027
# witness: <bob_sig> <x>
3128
choose = StandardClause(
3229
name="choose",
3330
script=CScript([
34-
OP_SHA256, # sha256(x)
35-
*check_output_contract(G256_S1(alice_pk, bob_pk, forfait_timeout)),
31+
*g256_s1.State.encoder_script(),
32+
*check_output_contract(g256_s1),
3633

3734
bob_pk,
3835
OP_CHECKSIG
@@ -41,42 +38,46 @@ def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout: int = 10):
4138
('bob_sig', SignerType(bob_pk)),
4239
('x', IntType()),
4340
],
44-
next_output_fn=lambda args: [ClauseOutput(
41+
next_outputs_fn=lambda args, _: [ClauseOutput(
4542
n=-1,
46-
next_contract=G256_S1(alice_pk, bob_pk, forfait_timeout),
47-
next_data=sha256(encode_wit_element(args['x']))
43+
next_contract=g256_s1,
44+
next_state=g256_s1.State(x=args['x'])
4845
)]
4946
)
5047

5148
super().__init__(NUMS_KEY, choose)
5249

5350

5451
class G256_S1(StandardAugmentedP2TR):
52+
@dataclass
53+
class State(ContractState):
54+
x: int
55+
56+
def encode(self):
57+
return sha256(encode_wit_element(self.x))
58+
59+
def encoder_script():
60+
return CScript([OP_SHA256])
61+
5562
def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout):
5663
self.alice_pk = alice_pk
5764
self.bob_pk = bob_pk
5865
self.forfait_timeout = forfait_timeout
5966

6067
g256_s2 = G256_S2(alice_pk, bob_pk, forfait_timeout)
6168

62-
# reveal: <alice_sig> <r_a> <y> <sha256(x)>
69+
# reveal: <alice_sig> <t_a> <y> <sha256(x)>
6370
reveal = StandardClause(
6471
name="reveal",
6572
script=CScript([
6673
OP_DUP,
6774

6875
# check that the top of the stack is the embedded data
76+
*self.State.encoder_script(),
6977
*check_input_contract(),
7078

71-
OP_TOALTSTACK,
72-
OP_SHA256,
73-
OP_FROMALTSTACK,
74-
75-
# <alice_sig> <t_a> <sha256(y)> <sha256(x)>
76-
*merkle_root(3),
77-
78-
# <alice_sig> <merkle_root(t_a, sha256(y), sha256(x))>
79-
79+
# <alice_sig> <t_a> <y> <x>
80+
*g256_s2.State.encoder_script(),
8081
*check_output_contract(g256_s2),
8182

8283
alice_pk,
@@ -86,12 +87,12 @@ def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout):
8687
('alice_sig', SignerType(alice_pk)),
8788
('t_a', BytesType()),
8889
('y', IntType()),
89-
('sha256_x', BytesType()),
90+
('x', IntType()),
9091
],
91-
next_output_fn=lambda args: [ClauseOutput(
92+
next_outputs_fn=lambda args, _: [ClauseOutput(
9293
n=-1,
9394
next_contract=g256_s2,
94-
next_data=MerkleTree([args['t_a'], sha256(encode_wit_element(args['y'])), args['sha256_x']]).root
95+
next_state=g256_s2.State(t_a=args['t_a'], y=args['y'], x=args['x'])
9596
)]
9697
)
9798

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

114115

115116
class G256_S2(StandardAugmentedP2TR):
117+
@dataclass
118+
class State(ContractState):
119+
t_a: bytes
120+
y: int
121+
x: bytes
122+
123+
def encode(self):
124+
return MerkleTree([self.t_a, sha256(encode_wit_element(self.y)), sha256(encode_wit_element(self.x))]).root
125+
126+
def encoder_script():
127+
return CScript([
128+
OP_TOALTSTACK, OP_SHA256, OP_FROMALTSTACK, OP_SHA256,
129+
*merkle_root(3)
130+
])
131+
116132
def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout: int = 10):
117133
self.alice_pk = alice_pk
118134
self.bob_pk = bob_pk
@@ -133,27 +149,32 @@ def __init__(self, alice_pk: bytes, bob_pk: bytes, forfait_timeout: int = 10):
133149
def leaf_factory(i: int): return Leaf(alice_pk, bob_pk, Compute2x)
134150

135151
bisectg256_0 = Bisect_1(alice_pk, bob_pk, 0, 7, leaf_factory, forfait_timeout)
136-
# start_challenge: <bob_sig> <t_a> <sha256(y)> <sha256(x)> <z> <t_b>
152+
# start_challenge: <bob_sig> <t_a> <y> <x> <z> <t_b>
137153
start_challenge = StandardClause(
138154
name="start_challenge",
139155
script=CScript([
140156
OP_TOALTSTACK,
141-
OP_SHA256, OP_TOALTSTACK,
142157

143-
# <bob_sig> <t_a> <sha256(y)> <sha256(x)> --- <t_b> <sha256(z)>
158+
# check that y != z
159+
OP_DUP, 3, OP_PICK, OP_EQUAL, OP_NOT, OP_VERIFY,
160+
161+
OP_TOALTSTACK,
162+
163+
# <bob_sig> <t_a> <y> <x> --- <t_b> <z>
144164

145-
# verify the embedded data
146165
*dup(3),
147-
*merkle_root(3),
166+
167+
# verify the embedded data
168+
*self.State.encoder_script(),
148169
*check_input_contract(),
149170

150-
# <bob_sig> <t_a> <sha256(y)> <sha256(x)> --- <t_b> <sha256(z)>
151-
OP_SWAP,
152-
# <bob_sig> <t_a> <sha256(x)> <sha256(y)> --- <t_b> <sha256(z)>
171+
# <bob_sig> <t_a> <y> <x> --- <t_b> <z>
172+
OP_SHA256, OP_SWAP, OP_SHA256,
173+
# <bob_sig> <t_a> <sha256(x)> <sha256(y)> --- <t_b> <z>
153174
OP_ROT,
154175
# <bob_sig> <sha256(x)> <sha256(y)> <t_a> --- <t_b> <sha256(z)>
155176

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

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

165-
*merkle_root(5),
186+
*bisectg256_0.State.encoder_script(),
166187
*check_output_contract(bisectg256_0),
167188

168189
bob_pk,
@@ -171,21 +192,21 @@ def leaf_factory(i: int): return Leaf(alice_pk, bob_pk, Compute2x)
171192
arg_specs=[
172193
('bob_sig', SignerType(bob_pk)),
173194
('t_a', BytesType()),
174-
('sha256_y', BytesType()),
175-
('sha256_x', BytesType()),
195+
('y', IntType()),
196+
('x', IntType()),
176197
('z', IntType()),
177198
('t_b', BytesType()),
178199
],
179-
next_output_fn=lambda args: [ClauseOutput(
200+
next_outputs_fn=lambda args, _: [ClauseOutput(
180201
n=-1,
181202
next_contract=bisectg256_0,
182-
next_data=MerkleTree([
183-
args['sha256_x'],
184-
args['sha256_y'],
185-
sha256(encode_wit_element(args['z'])),
186-
args['t_a'],
187-
args['t_b'],
188-
]).root
203+
next_state=bisectg256_0.State(
204+
h_i=sha256(encode_wit_element(args['x'])),
205+
h_j_plus_1_a=sha256(encode_wit_element(args['y'])),
206+
h_j_plus_1_b=sha256(encode_wit_element(args['z'])),
207+
t_i_j_a=args['t_a'],
208+
t_i_j_b=args['t_b'],
209+
)
189210
)]
190211
)
191212

Diff for: examples/ram/ram_contracts.py

+67-48
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
1+
from dataclasses import dataclass
12
from typing import List
23

34
from matt import CCV_FLAG_CHECK_INPUT, NUMS_KEY
45
from matt.argtypes import BytesType, MerkleProofType
56
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
6-
from matt.contracts import ClauseOutput, StandardClause, StandardAugmentedP2TR
7-
from matt.merkle import is_power_of_2, floor_lg
7+
from matt.contracts import ClauseOutput, StandardClause, StandardAugmentedP2TR, ContractState
8+
from matt.merkle import MerkleTree, is_power_of_2, floor_lg
9+
from matt.script_helpers import merkle_root
810

911

1012
class RAM(StandardAugmentedP2TR):
11-
def __init__(self, size: List[bytes]):
13+
@dataclass
14+
class State(ContractState):
15+
leaves: List[bytes]
16+
17+
def encode(self):
18+
return MerkleTree(self.leaves).root
19+
20+
def encoder_script(size: int):
21+
return merkle_root(size)
22+
23+
def __init__(self, size: int):
1224
assert is_power_of_2(size)
1325

1426
self.size = size
@@ -57,6 +69,19 @@ def __init__(self, size: List[bytes]):
5769
]
5870
)
5971

72+
def next_outputs_fn(args: dict, state: RAM.State):
73+
i: int = args["merkle_proof"].get_leaf_index()
74+
75+
return [
76+
ClauseOutput(
77+
n=-1,
78+
next_contract=self,
79+
next_state=self.State(
80+
leaves=state.leaves[:i] + [args["new_value"]] + state.leaves[i+1:]
81+
)
82+
)
83+
]
84+
6085
# witness: <h_1> <d_1> <h_2> <d_2> ... <h_n> <d_n> <x_old> <x_new> <root>
6186
write = StandardClause(
6287
name="write",
@@ -86,44 +111,44 @@ def __init__(self, size: List[bytes]):
86111
# TODO: seems too verbose, there should be a way of optimizing it
87112
# top of stack is now: <h> <x_old> <x_new> <d>
88113
OP_IF,
89-
# top of stack is now: <h> <x_old> <x_new>
90-
# right child: we want h || x
91-
2, OP_PICK,
92-
# top of stack is now: <h> <x_old> <x_new> <h>
93-
OP_SWAP,
94-
OP_CAT,
95-
OP_SHA256,
96-
# top of stack is now: <h> <x_old> <SHA(h || x_new)>
97-
98-
OP_SWAP,
99-
# top of stack is now: <h> <SHA(h || x_new)> <x_old>
100-
OP_ROT,
101-
# top of stack is now: <SHA(h || x_new)> <x_old> <h>
102-
OP_SWAP,
103-
# OP_CAT,
104-
# OP_SHA256,
105-
# # top of stack is now: <SHA(h || x_new)> <SHA(h || x_old)>
106-
107-
# OP_SWAP,
108-
# # top of stack is now: <SHA(h || x_old)> <SHA(h || x_new)>
114+
# top of stack is now: <h> <x_old> <x_new>
115+
# right child: we want h || x
116+
2, OP_PICK,
117+
# top of stack is now: <h> <x_old> <x_new> <h>
118+
OP_SWAP,
119+
OP_CAT,
120+
OP_SHA256,
121+
# top of stack is now: <h> <x_old> <SHA(h || x_new)>
122+
123+
OP_SWAP,
124+
# top of stack is now: <h> <SHA(h || x_new)> <x_old>
125+
OP_ROT,
126+
# top of stack is now: <SHA(h || x_new)> <x_old> <h>
127+
OP_SWAP,
128+
# OP_CAT,
129+
# OP_SHA256,
130+
# # top of stack is now: <SHA(h || x_new)> <SHA(h || x_old)>
131+
132+
# OP_SWAP,
133+
# # top of stack is now: <SHA(h || x_old)> <SHA(h || x_new)>
109134
OP_ELSE,
110-
# top of stack is now: <h> <x_old> <x_new>
111-
2, OP_PICK,
112-
# top of stack is now: <h> <x_old> <x_new> <h>
113-
OP_CAT,
114-
OP_SHA256,
115-
# top of stack is now: <h> <x_old> <SHA(x_new || h)>
116-
117-
OP_SWAP,
118-
OP_ROT,
119-
# top of stack is now: <SHA(x_new || h)> <x_old> <h>
120-
121-
# OP_CAT,
122-
# OP_SHA256,
123-
# # top of stack is now: <SHA(x_new || h)> <SHA(x_old || h)>
124-
125-
# OP_SWAP,
126-
# # top of stack is now: <SHA(x_old || h)> <SHA(x_new || h)>
135+
# top of stack is now: <h> <x_old> <x_new>
136+
2, OP_PICK,
137+
# top of stack is now: <h> <x_old> <x_new> <h>
138+
OP_CAT,
139+
OP_SHA256,
140+
# top of stack is now: <h> <x_old> <SHA(x_new || h)>
141+
142+
OP_SWAP,
143+
OP_ROT,
144+
# top of stack is now: <SHA(x_new || h)> <x_old> <h>
145+
146+
# OP_CAT,
147+
# OP_SHA256,
148+
# # top of stack is now: <SHA(x_new || h)> <SHA(x_old || h)>
149+
150+
# OP_SWAP,
151+
# # top of stack is now: <SHA(x_old || h)> <SHA(x_new || h)>
127152
OP_ENDIF,
128153

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

146171
# Check that new_root is committed in the next output,
147-
0, # index
172+
-1, # index
148173
0, # NUMS
149174
-1, # keep current taptree
150175
0, # default, preserve amount
@@ -158,13 +183,7 @@ def __init__(self, size: List[bytes]):
158183
('new_value', BytesType()),
159184
('merkle_root', BytesType()),
160185
],
161-
next_output_fn=lambda args: [
162-
ClauseOutput(
163-
n=0,
164-
next_contract=self,
165-
next_data=args["merkle_proof"].get_new_root_after_update(args["new_value"])
166-
)
167-
]
186+
next_outputs_fn=next_outputs_fn
168187
)
169188

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

0 commit comments

Comments
 (0)