WIP Updated VAA Processor according to latest design ideas
This commit is contained in:
parent
fdc80e9184
commit
ff486b7711
|
@ -0,0 +1,9 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
# The number of signatures verified by each transaction in the group.
|
||||||
|
# Since the last transaction of the group is the VAA processing one,
|
||||||
|
# the total count of required transactions to verify all guardian signatures is
|
||||||
|
#
|
||||||
|
# floor(guardian_count / SIGNATURES_PER_TRANSACTION)
|
||||||
|
#
|
||||||
|
SIGNATURES_PER_VERIFICATION_STEP = 6
|
|
@ -13,8 +13,14 @@ verify-vaa.teal stateless programs.
|
||||||
|
|
||||||
The following application calls are available.
|
The following application calls are available.
|
||||||
|
|
||||||
prepare: Load globals in scatchspace to be used by stateless programs. Must be TX#0.
|
setvphash: Set verify program hash.
|
||||||
verify: Verify guardian signature subset i..j, works in tandem with stateless program
|
|
||||||
|
Must be part of group:
|
||||||
|
|
||||||
|
verify: Verify guardian signature subset i..j, works in tandem with stateless program.
|
||||||
|
Arguments: #0 guardian public keys subset i..j (must match stored in global state)
|
||||||
|
#1 guardian signatures subset i..j
|
||||||
|
#2 payload to verify
|
||||||
commit: Commit verified VAA, processing it according to its payload. Must be last TX.
|
commit: Commit verified VAA, processing it according to its payload. Must be last TX.
|
||||||
|
|
||||||
------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------
|
||||||
|
@ -31,12 +37,12 @@ Stores in scratch:
|
||||||
|
|
||||||
SLOT 254: uint64 with bit field of approved guardians (initial 0)
|
SLOT 254: uint64 with bit field of approved guardians (initial 0)
|
||||||
SLOT 255: number of guardians in set
|
SLOT 255: number of guardians in set
|
||||||
SLOT 4*i: key of guardian i (as of Nov'21 there are 19 guardians)
|
|
||||||
================================================================================================
|
================================================================================================
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from pyteal import (compileTeal, Int, Mode, Txn, OnComplete, Itob, Btoi,
|
from pyteal import (compileTeal, Int, Mode, Txn, OnComplete, Itob, Btoi,
|
||||||
Return, Cond, Bytes, Global, Not, Seq, Approve, App, Assert, For, And)
|
Return, Cond, Bytes, Global, Not, Seq, Approve, App, Assert, For, And,
|
||||||
|
Extract)
|
||||||
import pyteal
|
import pyteal
|
||||||
from pyteal.ast.binaryexpr import ShiftRight
|
from pyteal.ast.binaryexpr import ShiftRight
|
||||||
from pyteal.ast.if_ import If
|
from pyteal.ast.if_ import If
|
||||||
|
@ -45,18 +51,24 @@ from pyteal.ast.scratch import ScratchLoad, ScratchSlot
|
||||||
from pyteal.ast.scratchvar import ScratchVar
|
from pyteal.ast.scratchvar import ScratchVar
|
||||||
from pyteal.ast.subroutine import Subroutine
|
from pyteal.ast.subroutine import Subroutine
|
||||||
from pyteal.ast.txn import TxnType
|
from pyteal.ast.txn import TxnType
|
||||||
|
from pyteal.ast.unaryexpr import Len
|
||||||
from pyteal.ast.while_ import While
|
from pyteal.ast.while_ import While
|
||||||
from pyteal.types import TealType
|
from pyteal.types import TealType
|
||||||
|
|
||||||
|
from globals import SIGNATURES_PER_VERIFICATION_STEP
|
||||||
|
|
||||||
METHOD = Txn.application_args[0]
|
METHOD = Txn.application_args[0]
|
||||||
SLOTID_SCRATCH_0 = 251
|
VERIFY_ARG_GUARDIAN_KEY_SUBSET = Txn.application_args[1]
|
||||||
|
VERIFY_ARG_GUARDIAN_SET_SIZE = Txn.application_args[2]
|
||||||
|
VERIFY_ARG_PAYLOAD = Txn.note
|
||||||
|
SLOTID_TEMP_0 = 251
|
||||||
SLOTID_VERIFIED_GUARDIAN_BITS = 254
|
SLOTID_VERIFIED_GUARDIAN_BITS = 254
|
||||||
SLOTID_GUARDIAN_COUNT = 255
|
SLOTID_GUARDIAN_COUNT = 255
|
||||||
|
STATELESS_LOGIC_HASH = App.globalGet(Bytes("vphash"))
|
||||||
# Bootstrap with the initial list of guardians as application argument
|
|
||||||
|
|
||||||
|
|
||||||
@Subroutine(TealType.uint64)
|
@Subroutine(TealType.uint64)
|
||||||
|
# Bootstrap with the initial list of guardians as application argument
|
||||||
def bootstrap():
|
def bootstrap():
|
||||||
return Seq([
|
return Seq([
|
||||||
App.globalPut(Bytes("vphash"), Txn.application_args[0]),
|
App.globalPut(Bytes("vphash"), Txn.application_args[0]),
|
||||||
|
@ -64,75 +76,114 @@ def bootstrap():
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@Subroutine(TealType.uint64)
|
||||||
|
def verify_from():
|
||||||
|
return Txn.group_index() * Int(SIGNATURES_PER_VERIFICATION_STEP)
|
||||||
|
|
||||||
|
|
||||||
|
@Subroutine(TealType.uint64)
|
||||||
|
def verify_to():
|
||||||
|
return min(VERIFY_ARG_GUARDIAN_SET_SIZE, verify_from +
|
||||||
|
(Int(SIGNATURES_PER_VERIFICATION_STEP) - Int(1)))
|
||||||
|
|
||||||
|
|
||||||
@Subroutine(TealType.uint64)
|
@Subroutine(TealType.uint64)
|
||||||
def is_creator():
|
def is_creator():
|
||||||
return Txn.sender() == Global.creator_address()
|
return Txn.sender() == Global.creator_address()
|
||||||
|
|
||||||
|
|
||||||
|
@Subroutine(TealType.uint64)
|
||||||
|
def min(a, b):
|
||||||
|
If(Int(a) < Int(b), Return(a), Return(b))
|
||||||
|
|
||||||
|
|
||||||
@Subroutine(TealType.uint64)
|
@Subroutine(TealType.uint64)
|
||||||
def is_proper_group_size():
|
def is_proper_group_size():
|
||||||
return Global.group_size() >= Int(3)
|
# Let G be the guardian count, N number of signatures per verification step, group must have CEIL(G/N) + 1 transactions (last one is commit).
|
||||||
|
gssize = App.globalGet(Bytes("gssize"))
|
||||||
|
q = gssize / Int(SIGNATURES_PER_VERIFICATION_STEP)
|
||||||
|
r = gssize % Int(SIGNATURES_PER_VERIFICATION_STEP)
|
||||||
|
return Seq([
|
||||||
|
If(r != Int(0)).Then(
|
||||||
|
Return(Global.group_size() == q + Int(2))
|
||||||
|
).Else(Return(Global.group_size() == q + Int(1)))
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
# @Subroutine(TealType.none)
|
@Subroutine(TealType.uint64)
|
||||||
# # set bitfield bits [from,to]
|
def check_guardian_key_subset():
|
||||||
# def set_bits(i_from, i_to):
|
# Verify that the passed argument for guardian keys [i..j] match the
|
||||||
# count = Int(1) #ScratchVar(TealType.uint64, SLOTID_SCRATCH_0)
|
# global state for the same keys.
|
||||||
# #count.store(Int(i_to) - Int(i_from) + Int(1))
|
#
|
||||||
# # set Verified_bits |= ((2^count) - 1) << i_from
|
i = ScratchVar(TealType.uint64, SLOTID_TEMP_0)
|
||||||
|
return Seq([For(i.store(Int(0)), i.load() < Int(SIGNATURES_PER_VERIFICATION_STEP), i.store(i.load() + Int(1))).Do(
|
||||||
# bitfield = ScratchVar(TealType.uint64, SLOTID_VERIFIED_GUARDIAN_BITS)
|
If(App.globalGet(Itob(i.load())) != Extract(VERIFY_ARG_GUARDIAN_KEY_SUBSET,
|
||||||
# return Seq([
|
i.load() * Int(64), Int(64))).Then(Return(Int(0))) # get and compare stored global key
|
||||||
# bitfield.store(bitfield.load() | (
|
),
|
||||||
# ((Int(2) ** count) - Int(1) << Int(2))
|
Return(Int(1))
|
||||||
# ))
|
])
|
||||||
# ])
|
|
||||||
|
|
||||||
|
|
||||||
def prepare():
|
@Subroutine(TealType.uint64)
|
||||||
# Sender must be owner
|
def check_guardian_set_size():
|
||||||
# This call must be index 0 in a group of minimum 3 (prepare->verify->commit)
|
#
|
||||||
|
# Verify that the passed argument for guardian set size matches the global state.
|
||||||
|
#
|
||||||
|
return App.globalGet(Bytes("gssize")) == Btoi(VERIFY_ARG_GUARDIAN_SET_SIZE)
|
||||||
|
|
||||||
ScratchVar(TealType.uint64, SLOTID_VERIFIED_GUARDIAN_BITS).store(Int(0))
|
|
||||||
i = ScratchVar(TealType.uint64, SLOTID_SCRATCH_0)
|
@Subroutine(TealType.uint64)
|
||||||
gs_count = ScratchVar(TealType.uint64, SLOTID_GUARDIAN_COUNT)
|
def check_txn_note_payload():
|
||||||
num_guardians = App.globalGet(Bytes("gscount"))
|
#
|
||||||
|
# Verify the digest-source section argument of a signed VAA,
|
||||||
|
# consisting of:
|
||||||
|
#
|
||||||
|
# bytes
|
||||||
|
# 4 timestamp
|
||||||
|
# 4 Nonce
|
||||||
|
# 2 emitterChainId
|
||||||
|
# 32 emitterAddress
|
||||||
|
# 8 sequence
|
||||||
|
# 1 consistencyLevel
|
||||||
|
# N payload
|
||||||
|
#
|
||||||
|
|
||||||
|
# Should we validate the fields?
|
||||||
|
return Seq([
|
||||||
|
Assert(Extract(VERIFY_ARG_PAYLOAD, Int(0), Int(4))
|
||||||
|
])
|
||||||
|
return VERIFY_ARG_PAYLOAD
|
||||||
|
|
||||||
|
|
||||||
|
def setvphash():
|
||||||
|
#
|
||||||
|
# Sets the hash of the verification stateless program.
|
||||||
|
#
|
||||||
|
|
||||||
return Seq([
|
return Seq([
|
||||||
Assert(And(is_creator(),
|
Assert(And(is_creator(), Len(Txn.application_args[1]) == Int(32))),
|
||||||
is_proper_group_size(), Txn.group_index() == Int(0))),
|
App.globalPut(Bytes("vphash"), Txn.application_args[1]),
|
||||||
|
|
||||||
If(num_guardians == Int(0), Reject(), Seq([
|
|
||||||
gs_count.store(num_guardians),
|
|
||||||
For(i.store(Int(0)), i.load() < num_guardians, i.store(i.load() + Int(1))).Do(
|
|
||||||
Seq([
|
|
||||||
ScratchVar(TealType.uint64, 1).store(
|
|
||||||
App.globalGet(Itob(i.load())))
|
|
||||||
# (ScratchVar(TealType.uint64, i.load())).store(App.globalGet(Itob(i.load()))
|
|
||||||
]))
|
|
||||||
])),
|
|
||||||
Approve()
|
Approve()
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def verify():
|
def verify():
|
||||||
# Sender must be stateless logic.
|
# * Sender must be stateless logic.
|
||||||
# This call must be not the first or last in a group of minimum 3 (prepare->verify->commit)
|
# * Let N be the number of signatures per verification step, for the TXi in group, we verify signatures [i..j] where i = i*N, j = i*N+(N-1)
|
||||||
|
# * Argument 1 must contain guardian public keys for guardians [i..j] (read by stateless logic)
|
||||||
# First guardian index to verify
|
# * Argument 2 must contain current guardian set size (read by stateless logic)
|
||||||
verify_from = Txn.application_args[0]
|
# * Passed guardian public keys [i..j] must match the current global state.
|
||||||
# Last guardian index to verify
|
# * Note must contain VAA message-in-digest (header+payload) (up to 1KB) (read by stateless logic)
|
||||||
verify_to = Txn.application_args[1]
|
# * This call must be not the last in a group of minimum 2 (prepare, commit)
|
||||||
bitfield = ScratchVar(TealType.uint64, SLOTID_VERIFIED_GUARDIAN_BITS)
|
#
|
||||||
p_val = bitfield.load()
|
# Call will set validated bits to i..j in bitfield.
|
||||||
count = Btoi(verify_to) - Btoi(verify_from) + Int(1)
|
|
||||||
|
|
||||||
return Seq([Assert(And(is_proper_group_size(),
|
return Seq([Assert(And(is_proper_group_size(),
|
||||||
Txn.group_index() < Global.group_size(),
|
Txn.group_index() < (Global.group_size() - Int(1)),
|
||||||
Txn.group_index() > Int(0),
|
Txn.sender() == STATELESS_LOGIC_HASH,
|
||||||
Btoi(verify_to) > Btoi(verify_from),
|
check_guardian_set_size(),
|
||||||
Txn.sender() == App.globalGet(Bytes("vphash")))),
|
check_guardian_key_subset(),
|
||||||
#bitfield.store(bitfield.load() + Int(1)),
|
check_txn_note_payload())),
|
||||||
Approve()])
|
Approve()])
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,13 +191,12 @@ def commit():
|
||||||
# Sender must be owner
|
# Sender must be owner
|
||||||
# This call must be last in a group of minimum 3 (prepare->verify->commit)
|
# This call must be last in a group of minimum 3 (prepare->verify->commit)
|
||||||
# Bitfield must indicate all guardians verified.
|
# Bitfield must indicate all guardians verified.
|
||||||
all_verified = ScratchVar(TealType.uint64, SLOTID_VERIFIED_GUARDIAN_BITS).load() ==
|
# all_verified = ScratchVar(TealType.uint64, SLOTID_VERIFIED_GUARDIAN_BITS).load() ==
|
||||||
return Seq([
|
return Seq([
|
||||||
Assert(And(is_proper_group_size(),
|
Assert(And(is_proper_group_size(),
|
||||||
Txn.group_index() == (Global.group_size() - Int(1)),
|
Txn.group_index() == (Global.group_size() - Int(1)),
|
||||||
all_verified
|
|
||||||
)),
|
)),
|
||||||
handle_vaa(),
|
#handle_vaa(),
|
||||||
Approve()
|
Approve()
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -156,7 +206,7 @@ def vaa_processor_program():
|
||||||
handle_update = Return(is_creator())
|
handle_update = Return(is_creator())
|
||||||
handle_delete = Return(is_creator())
|
handle_delete = Return(is_creator())
|
||||||
handle_noop = Cond(
|
handle_noop = Cond(
|
||||||
[METHOD == Bytes("prepare"), prepare()],
|
[METHOD == Bytes("setvphash"), setvphash()],
|
||||||
[METHOD == Bytes("verify"), verify()],
|
[METHOD == Bytes("verify"), verify()],
|
||||||
[METHOD == Bytes("commit"), commit()]
|
[METHOD == Bytes("commit"), commit()]
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue