Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fraud proofs #3

Merged
merged 16 commits into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
- name: Add repository root to PYTHONPATH
run: echo "PYTHONPATH=$PWD" >> $GITHUB_ENV
- name: Run tests and capture output
run: pytest -vv >test_output.txt
run: pytest -vv
- name: Upload test output as artifact
uses: actions/upload-artifact@v2
with:
Expand Down
213 changes: 213 additions & 0 deletions examples/game256/game256_contracts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@


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_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
from matt.utils import encode_wit_element


# TODO: add forfait clauses whenever needed

# 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?

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([
*g256_s1.State.encoder_script(),
*check_output_contract(g256_s1),

bob_pk,
OP_CHECKSIG
]),
arg_specs=[
('bob_sig', SignerType(bob_pk)),
('x', IntType()),
],
next_outputs_fn=lambda args, _: [ClauseOutput(
n=-1,
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> <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(),

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

alice_pk,
OP_CHECKSIG
]),
arg_specs=[
('alice_sig', SignerType(alice_pk)),
('t_a', BytesType()),
('y', IntType()),
('x', IntType()),
],
next_outputs_fn=lambda args, _: [ClauseOutput(
n=-1,
next_contract=g256_s2,
next_state=g256_s2.State(t_a=args['t_a'], y=args['y'], x=args['x'])
)]
)

super().__init__(NUMS_KEY, reveal)


Compute2x = Computer(
encoder=CScript([OP_SHA256]),
func=CScript([OP_DUP, OP_ADD]),
specs=[('x', IntType())],
)


NopInt = Computer(
encoder=CScript([]),
func=CScript([]),
specs=[('x', IntType())],
)


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
self.forfait_timeout = forfait_timeout

# reveal: <alice_sig>
withdraw = StandardClause(
name="withdraw",
script=CScript([
*older(forfait_timeout),

alice_pk,
OP_CHECKSIG
]),
arg_specs=[('alice_sig', SignerType(alice_pk))]
)

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> <y> <x> <z> <t_b>
start_challenge = StandardClause(
name="start_challenge",
script=CScript([
OP_TOALTSTACK,

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

*dup(3),

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

# <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_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>

OP_FROMALTSTACK,

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

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

bob_pk,
OP_CHECKSIG
]),
arg_specs=[
('bob_sig', SignerType(bob_pk)),
('t_a', BytesType()),
('y', IntType()),
('x', IntType()),
('z', IntType()),
('t_b', BytesType()),
],
next_outputs_fn=lambda args, _: [ClauseOutput(
n=-1,
next_contract=bisectg256_0,
next_state=bisectg256_0.State(
h_start=sha256(encode_wit_element(args['x'])),
h_end_a=sha256(encode_wit_element(args['y'])),
h_end_b=sha256(encode_wit_element(args['z'])),
trace_a=args['t_a'],
trace_b=args['t_b'],
)
)]
)

super().__init__(NUMS_KEY, [withdraw, start_challenge])
6 changes: 3 additions & 3 deletions examples/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ if [[ -z $NEW_ADDRESS ]]; then
exit 1
fi

# Generate 101 blocks, sending the block reward to the new address
$BITCOIN_CLI generatetoaddress 110 $NEW_ADDRESS
# Generate 300 blocks, sending the block reward to the new address
$BITCOIN_CLI generatetoaddress 300 $NEW_ADDRESS

echo "Generated 101 blocks to address: $NEW_ADDRESS"
echo "Generated 300 blocks to address: $NEW_ADDRESS"
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
Loading