146 lines
5.3 KiB
Python
146 lines
5.3 KiB
Python
from time import time, sleep
|
|
from typing import List, Tuple, Dict, Any, Optional, Union
|
|
from base64 import b64decode
|
|
import base64
|
|
import random
|
|
import hashlib
|
|
import uuid
|
|
import sys
|
|
import json
|
|
import uvarint
|
|
import pprint
|
|
|
|
from local_blob import LocalBlob
|
|
|
|
from algosdk.v2client.algod import AlgodClient
|
|
from algosdk.kmd import KMDClient
|
|
from algosdk import account, mnemonic
|
|
from algosdk.encoding import decode_address
|
|
from algosdk.future import transaction
|
|
from pyteal import compileTeal, Mode, Expr
|
|
from pyteal import *
|
|
from algosdk.logic import get_application_address
|
|
|
|
from algosdk.future.transaction import LogicSigAccount
|
|
|
|
class TmplSig:
|
|
"""KeySig class reads in a json map containing assembly details of a template smart signature and allows you to populate it with the variables
|
|
In this case we are only interested in a single variable, the key which is a byte string to make the address unique.
|
|
In this demo we're using random strings but in practice you can choose something meaningful to your application
|
|
"""
|
|
|
|
def __init__(self, name):
|
|
# Read the source map
|
|
|
|
# with open("{}.json".format(name)) as f:
|
|
# self.map = json.loads(f.read())
|
|
|
|
|
|
self.map = {"name":"lsig.teal","version":6,"source":"","bytecode":"BiABAYEASIAASDEQgQYSRDEZIhJEMRiBABJEMSCAABJEMQGBABJEMQkyAxJEMRUyAxJEIg==",
|
|
"template_labels":{
|
|
"TMPL_ADDR_IDX":{"source_line":3,"position":5,"bytes":False},
|
|
"TMPL_EMITTER_ID":{"source_line":5,"position":8,"bytes":True},
|
|
"TMPL_APP_ID":{"source_line":16,"position":24,"bytes":False},
|
|
"TMPL_APP_ADDRESS":{"source_line":20,"position":30,"bytes":True}
|
|
},
|
|
"label_map":{},"line_map":[0,1,4,6,7,9,10,12,14,15,16,18,19,20,21,23,25,26,27,29,31,32,33,35,37,38,39,41,43,44,45,47,49,50,51]
|
|
}
|
|
|
|
|
|
self.src = base64.b64decode(self.map["bytecode"])
|
|
self.sorted = dict(
|
|
sorted(
|
|
self.map["template_labels"].items(),
|
|
key=lambda item: item[1]["position"],
|
|
)
|
|
)
|
|
|
|
def populate(self, values: Dict[str, Union[str, int]]) -> LogicSigAccount:
|
|
"""populate uses the map to fill in the variable of the bytecode and returns a logic sig with the populated bytecode"""
|
|
# Get the template source
|
|
contract = list(base64.b64decode(self.map["bytecode"]))
|
|
|
|
shift = 0
|
|
for k, v in self.sorted.items():
|
|
if k in values:
|
|
pos = v["position"] + shift
|
|
if v["bytes"]:
|
|
val = bytes.fromhex(values[k])
|
|
lbyte = uvarint.encode(len(val))
|
|
# -1 to account for the existing 00 byte for length
|
|
shift += (len(lbyte) - 1) + len(val)
|
|
# +1 to overwrite the existing 00 byte for length
|
|
contract[pos : pos + 1] = lbyte + val
|
|
else:
|
|
val = uvarint.encode(values[k])
|
|
# -1 to account for existing 00 byte
|
|
shift += len(val) - 1
|
|
# +1 to overwrite existing 00 byte
|
|
contract[pos : pos + 1] = val
|
|
|
|
# Create a new LogicSigAccount given the populated bytecode,
|
|
|
|
#pprint.pprint({"values": values, "contract": bytes(contract).hex()})
|
|
|
|
return LogicSigAccount(bytes(contract))
|
|
|
|
def get_bytecode_chunk(self, idx: int) -> Bytes:
|
|
start = 0
|
|
if idx > 0:
|
|
start = list(self.sorted.values())[idx - 1]["position"] + 1
|
|
|
|
stop = len(self.src)
|
|
if idx < len(self.sorted):
|
|
stop = list(self.sorted.values())[idx]["position"]
|
|
|
|
chunk = self.src[start:stop]
|
|
return Bytes(chunk)
|
|
|
|
def get_bytecode_raw(self, idx: int):
|
|
start = 0
|
|
if idx > 0:
|
|
start = list(self.sorted.values())[idx - 1]["position"] + 1
|
|
|
|
stop = len(self.src)
|
|
if idx < len(self.sorted):
|
|
stop = list(self.sorted.values())[idx]["position"]
|
|
|
|
chunk = self.src[start:stop]
|
|
return chunk
|
|
|
|
def get_sig_tmpl(self):
|
|
def sig_tmpl():
|
|
admin_app_id = ScratchVar()
|
|
admin_address= ScratchVar()
|
|
|
|
return Seq(
|
|
# Just putting adding this as a tmpl var to make the address unique and deterministic
|
|
# We don't actually care what the value is, pop it
|
|
Pop(Tmpl.Int("TMPL_ADDR_IDX")),
|
|
Pop(Tmpl.Bytes("TMPL_EMITTER_ID")),
|
|
|
|
Assert(Txn.type_enum() == TxnType.ApplicationCall),
|
|
Assert(Txn.on_completion() == OnComplete.OptIn),
|
|
Assert(Txn.application_id() == Tmpl.Int("TMPL_APP_ID")),
|
|
Assert(Txn.rekey_to() == Tmpl.Bytes("TMPL_APP_ADDRESS")),
|
|
|
|
Assert(Txn.fee() == Int(0)),
|
|
Assert(Txn.close_remainder_to() == Global.zero_address()),
|
|
Assert(Txn.asset_close_to() == Global.zero_address()),
|
|
|
|
Approve()
|
|
)
|
|
|
|
return compileTeal(sig_tmpl(), mode=Mode.Signature, version=6, assembleConstants=True)
|
|
|
|
if __name__ == '__main__':
|
|
core = TmplSig("sig")
|
|
|
|
if len(sys.argv) == 1:
|
|
file_name = "sig.tmpl.teal"
|
|
else:
|
|
file_name = sys.argv[1]
|
|
|
|
with open(file_name, "w") as f:
|
|
f.write(core.get_sig_tmpl())
|