| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574 |
- # python3 -m pip install pycryptodomex uvarint pyteal web3 coincurve
- import os
- from os.path import exists
- from time import time, sleep
- from eth_abi import encode_single, encode_abi
- from typing import List, Tuple, Dict, Any, Optional, Union
- from base64 import b64decode
- import base64
- import random
- import time
- import hashlib
- import uuid
- import sys
- import json
- import uvarint
- from local_blob import LocalBlob
- from wormhole_core import getCoreContracts
- from TmplSig import TmplSig
- import argparse
- from gentest import GenTest
- from algosdk.v2client.algod import AlgodClient
- from algosdk.kmd import KMDClient
- from algosdk import account, mnemonic, abi
- from algosdk.encoding import decode_address, encode_address
- from algosdk.future import transaction
- from pyteal import compileTeal, Mode, Expr
- from pyteal import *
- from algosdk.logic import get_application_address
- from vaa_verify import get_vaa_verify
- from Cryptodome.Hash import keccak
- from algosdk.future.transaction import LogicSig
- from token_bridge import get_token_bridge
- from test_contract import get_test_app
- from algosdk.v2client import indexer
- import pprint
- max_keys = 15
- max_bytes_per_key = 127
- bits_per_byte = 8
- bits_per_key = max_bytes_per_key * bits_per_byte
- max_bytes = max_bytes_per_key * max_keys
- max_bits = bits_per_byte * max_bytes
- class Account:
- """Represents a private key and address for an Algorand account"""
- def __init__(self, privateKey: str) -> None:
- self.sk = privateKey
- self.addr = account.address_from_private_key(privateKey)
- print (privateKey)
- print (" " + self.getMnemonic())
- print (" " + self.addr)
- def getAddress(self) -> str:
- return self.addr
- def getPrivateKey(self) -> str:
- return self.sk
- def getMnemonic(self) -> str:
- return mnemonic.from_private_key(self.sk)
- @classmethod
- def FromMnemonic(cls, m: str) -> "Account":
- return cls(mnemonic.to_private_key(m))
- class PendingTxnResponse:
- def __init__(self, response: Dict[str, Any]) -> None:
- self.poolError: str = response["pool-error"]
- self.txn: Dict[str, Any] = response["txn"]
- self.applicationIndex: Optional[int] = response.get("application-index")
- self.assetIndex: Optional[int] = response.get("asset-index")
- self.closeRewards: Optional[int] = response.get("close-rewards")
- self.closingAmount: Optional[int] = response.get("closing-amount")
- self.confirmedRound: Optional[int] = response.get("confirmed-round")
- self.globalStateDelta: Optional[Any] = response.get("global-state-delta")
- self.localStateDelta: Optional[Any] = response.get("local-state-delta")
- self.receiverRewards: Optional[int] = response.get("receiver-rewards")
- self.senderRewards: Optional[int] = response.get("sender-rewards")
- self.innerTxns: List[Any] = response.get("inner-txns", [])
- self.logs: List[bytes] = [b64decode(l) for l in response.get("logs", [])]
- class PortalCore:
- def __init__(self) -> None:
- self.gt = None
- self.foundation = None
- self.devnet = False
- self.ALGOD_ADDRESS = "http://localhost:4001"
- self.ALGOD_TOKEN = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
- self.FUNDING_AMOUNT = 100_000_000_000
- self.KMD_ADDRESS = "http://localhost:4002"
- self.KMD_TOKEN = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
- self.KMD_WALLET_NAME = "unencrypted-default-wallet"
- self.KMD_WALLET_PASSWORD = ""
- self.INDEXER_TOKEN = "a" * 64
- self.INDEXER_ADDRESS = 'http://localhost:8980'
- self.INDEXER_ROUND = 0
- self.NOTE_PREFIX = 'publishMessage'.encode()
- self.myindexer = None
- self.seed_amt = int(1002000) # The black magic in this number...
- self.cache = {}
- self.asset_cache = {}
- self.kmdAccounts : Optional[List[Account]] = None
- self.accountList : List[Account] = []
- self.zeroPadBytes = "00"*32
- self.tsig = TmplSig("sig")
- def init(self, args) -> None:
- self.args = args
- self.ALGOD_ADDRESS = args.algod_address
- self.ALGOD_TOKEN = args.algod_token
- self.KMD_ADDRESS = args.kmd_address
- self.KMD_TOKEN = args.kmd_token
- self.KMD_WALLET_NAME = args.kmd_name
- self.KMD_WALLET_PASSWORD = args.kmd_password
- self.TARGET_ACCOUNT = args.mnemonic
- self.coreid = args.coreid
- self.tokenid = args.tokenid
- if exists(self.args.env):
- if self.gt == None:
- self.gt = GenTest(False)
- with open(self.args.env, encoding = 'utf-8') as f:
- for line in f:
- e = line.rstrip('\n').split("=")
- if "INIT_SIGNERS_CSV" in e[0]:
- self.gt.guardianKeys = e[1].split(",")
- print("guardianKeys=" + str(self.gt.guardianKeys))
- if "INIT_SIGNERS_KEYS_CSV" in e[0]:
- self.gt.guardianPrivKeys = e[1].split(",")
- print("guardianPrivKeys=" + str(self.gt.guardianPrivKeys))
- def waitForTransaction(
- self, client: AlgodClient, txID: str, timeout: int = 10
- ) -> PendingTxnResponse:
- lastStatus = client.status()
- lastRound = lastStatus["last-round"]
- startRound = lastRound
-
- while lastRound < startRound + timeout:
- pending_txn = client.pending_transaction_info(txID)
-
- if pending_txn.get("confirmed-round", 0) > 0:
- return PendingTxnResponse(pending_txn)
-
- if pending_txn["pool-error"]:
- raise Exception("Pool error: {}".format(pending_txn["pool-error"]))
-
- lastStatus = client.status_after_block(lastRound + 1)
-
- lastRound += 1
-
- raise Exception(
- "Transaction {} not confirmed after {} rounds".format(txID, timeout)
- )
- def getKmdClient(self) -> KMDClient:
- return KMDClient(self.KMD_TOKEN, self.KMD_ADDRESS)
-
- def getGenesisAccounts(self) -> List[Account]:
- if self.kmdAccounts is None:
- kmd = self.getKmdClient()
-
- wallets = kmd.list_wallets()
- walletID = None
- for wallet in wallets:
- if wallet["name"] == self.KMD_WALLET_NAME:
- walletID = wallet["id"]
- break
-
- if walletID is None:
- raise Exception("Wallet not found: {}".format(self.KMD_WALLET_NAME))
-
- walletHandle = kmd.init_wallet_handle(walletID, self.KMD_WALLET_PASSWORD)
-
- try:
- addresses = kmd.list_keys(walletHandle)
- privateKeys = [
- kmd.export_key(walletHandle, self.KMD_WALLET_PASSWORD, addr)
- for addr in addresses
- ]
- self.kmdAccounts = [Account(sk) for sk in privateKeys]
- finally:
- kmd.release_wallet_handle(walletHandle)
-
- return self.kmdAccounts
-
- def getTemporaryAccount(self, client: AlgodClient) -> Account:
- if len(self.accountList) == 0:
- sks = [account.generate_account()[0] for i in range(3)]
- self.accountList = [Account(sk) for sk in sks]
-
- genesisAccounts = self.getGenesisAccounts()
- suggestedParams = client.suggested_params()
-
- txns: List[transaction.Transaction] = []
- for i, a in enumerate(self.accountList):
- fundingAccount = genesisAccounts[i % len(genesisAccounts)]
- txns.append(
- transaction.PaymentTxn(
- sender=fundingAccount.getAddress(),
- receiver=a.getAddress(),
- amt=self.FUNDING_AMOUNT,
- sp=suggestedParams,
- )
- )
-
- txns = transaction.assign_group_id(txns)
- signedTxns = [
- txn.sign(genesisAccounts[i % len(genesisAccounts)].getPrivateKey())
- for i, txn in enumerate(txns)
- ]
-
- client.send_transactions(signedTxns)
-
- self.waitForTransaction(client, signedTxns[0].get_txid())
-
- return self.accountList.pop()
-
- def getAlgodClient(self) -> AlgodClient:
- return AlgodClient(self.ALGOD_TOKEN, self.ALGOD_ADDRESS)
- def getBalances(self, client: AlgodClient, account: str) -> Dict[int, int]:
- balances: Dict[int, int] = dict()
-
- accountInfo = client.account_info(account)
-
- # set key 0 to Algo balance
- balances[0] = accountInfo["amount"]
-
- assets: List[Dict[str, Any]] = accountInfo.get("assets", [])
- for assetHolding in assets:
- assetID = assetHolding["asset-id"]
- amount = assetHolding["amount"]
- balances[assetID] = amount
-
- return balances
- def fullyCompileContract(self, client: AlgodClient, contract: Expr) -> bytes:
- teal = compileTeal(contract, mode=Mode.Application, version=6)
- response = client.compile(teal)
- return response
- # helper function that formats global state for printing
- def format_state(self, state):
- formatted = {}
- for item in state:
- key = item['key']
- value = item['value']
- formatted_key = base64.b64decode(key).decode('utf-8')
- if value['type'] == 1:
- # byte string
- if formatted_key == 'voted':
- formatted_value = base64.b64decode(value['bytes']).decode('utf-8')
- else:
- formatted_value = value['bytes']
- formatted[formatted_key] = formatted_value
- else:
- # integer
- formatted[formatted_key] = value['uint']
- return formatted
-
- # helper function to read app global state
- def read_global_state(self, client, addr, app_id):
- results = self.client.application_info(app_id)
- return self.format_state(results['params']['global-state'])
- def read_state(self, client, addr, app_id):
- results = client.account_info(addr)
- apps_created = results['created-apps']
- for app in apps_created:
- if app['id'] == app_id:
- return app
- return {}
- def encoder(self, type, val):
- if type == 'uint8':
- return encode_single(type, val).hex()[62:64]
- if type == 'uint16':
- return encode_single(type, val).hex()[60:64]
- if type == 'uint32':
- return encode_single(type, val).hex()[56:64]
- if type == 'uint64':
- return encode_single(type, val).hex()[64-(16):64]
- if type == 'uint128':
- return encode_single(type, val).hex()[64-(32):64]
- if type == 'uint256' or type == 'bytes32':
- return encode_single(type, val).hex()[64-(64):64]
- raise Exception("invalid type")
- def devnetUpgradeVAA(self):
- v = self.genUpgradePayload()
- print("core payload: " + str(v[0]))
- print("token payload: " + str(v[1]))
- if self.gt == None:
- self.gt = GenTest(False)
- emitter = bytes.fromhex(self.zeroPadBytes[0:(31*2)] + "04")
- guardianSet = self.getGovSet()
- print("guardianSet: " + str(guardianSet))
- nonce = int(random.random() * 20000)
- ret = [
- self.gt.createSignedVAA(guardianSet, self.gt.guardianPrivKeys, int(time.time()), nonce, 1, emitter, int(random.random() * 20000), 32, 8, v[0]),
- self.gt.createSignedVAA(guardianSet, self.gt.guardianPrivKeys, int(time.time()), nonce, 1, emitter, int(random.random() * 20000), 32, 8, v[1]),
- ]
-
- # pprint.pprint(self.parseVAA(bytes.fromhex(ret[0])))
- # pprint.pprint(self.parseVAA(bytes.fromhex(ret[1])))
- return ret
- def getMessageFee(self):
- s = self.client.application_info(self.coreid)["params"]["global-state"]
- k = base64.b64encode(b"MessageFee").decode('utf-8')
- for x in s:
- if x["key"] == k:
- return x["value"]["uint"]
- return -1
- def getGovSet(self):
- s = self.client.application_info(self.coreid)["params"]["global-state"]
- k = base64.b64encode(b"currentGuardianSetIndex").decode('utf-8')
- for x in s:
- if x["key"] == k:
- return x["value"]["uint"]
- return -1
- def genUpgradePayload(self):
- approval1, clear1 = getCoreContracts(False, self.args.core_approve, self.args.core_clear, self.client, seed_amt=self.seed_amt, tmpl_sig=self.tsig, devMode = self.devnet or self.args.testnet)
- approval2, clear2 = get_token_bridge(False, self.args.token_approve, self.args.token_clear, self.client, seed_amt=self.seed_amt, tmpl_sig=self.tsig, devMode = self.devnet or self.args.testnet)
- return self.genUpgradePayloadBody(approval1, approval2)
- def genUpgradePayloadBody(self, approval1, approval2):
- b = self.zeroPadBytes[0:(28*2)]
- b += self.encoder("uint8", ord("C"))
- b += self.encoder("uint8", ord("o"))
- b += self.encoder("uint8", ord("r"))
- b += self.encoder("uint8", ord("e"))
- b += self.encoder("uint8", 1)
- b += self.encoder("uint16", 8)
- b += decode_address(approval1["hash"]).hex()
- print("core hash: " + decode_address(approval1["hash"]).hex())
- ret = [b]
- b = self.zeroPadBytes[0:((32 -11)*2)]
- b += self.encoder("uint8", ord("T"))
- b += self.encoder("uint8", ord("o"))
- b += self.encoder("uint8", ord("k"))
- b += self.encoder("uint8", ord("e"))
- b += self.encoder("uint8", ord("n"))
- b += self.encoder("uint8", ord("B"))
- b += self.encoder("uint8", ord("r"))
- b += self.encoder("uint8", ord("i"))
- b += self.encoder("uint8", ord("d"))
- b += self.encoder("uint8", ord("g"))
- b += self.encoder("uint8", ord("e"))
- b += self.encoder("uint8", 2) # action
- b += self.encoder("uint16", 8) # target chain
- b += decode_address(approval2["hash"]).hex()
- print("token hash: " + decode_address(approval2["hash"]).hex())
- ret.append(b)
- return ret
- def createPortalCoreApp(
- self,
- client: AlgodClient,
- sender: Account,
- ) -> int:
- approval, clear = getCoreContracts(False, self.args.core_approve, self.args.core_clear, client, seed_amt=self.seed_amt, tmpl_sig=self.tsig, devMode = self.devnet or self.args.testnet)
- globalSchema = transaction.StateSchema(num_uints=8, num_byte_slices=40)
- localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=16)
-
- app_args = [ ]
-
- txn = transaction.ApplicationCreateTxn(
- sender=sender.getAddress(),
- on_complete=transaction.OnComplete.NoOpOC,
- approval_program=b64decode(approval["result"]),
- clear_program=b64decode(clear["result"]),
- global_schema=globalSchema,
- local_schema=localSchema,
- extra_pages = 1,
- app_args=app_args,
- sp=client.suggested_params(),
- )
-
- signedTxn = txn.sign(sender.getPrivateKey())
-
- client.send_transaction(signedTxn)
-
- response = self.waitForTransaction(client, signedTxn.get_txid())
- assert response.applicationIndex is not None and response.applicationIndex > 0
- # Lets give it a bit of money so that it is not a "ghost" account
- txn = transaction.PaymentTxn(sender = sender.getAddress(), sp = client.suggested_params(), receiver = get_application_address(response.applicationIndex), amt = 100000)
- signedTxn = txn.sign(sender.getPrivateKey())
- client.send_transaction(signedTxn)
- return response.applicationIndex
- def createTokenBridgeApp(
- self,
- client: AlgodClient,
- sender: Account,
- ) -> int:
- approval, clear = get_token_bridge(False, self.args.token_approve, self.args.token_clear, client, seed_amt=self.seed_amt, tmpl_sig=self.tsig, devMode = self.devnet or self.args.testnet)
- if len(b64decode(approval["result"])) > 4060:
- print("token bridge contract is too large... This might prevent updates later")
- globalSchema = transaction.StateSchema(num_uints=4, num_byte_slices=30)
- localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=16)
-
- app_args = [self.coreid, decode_address(get_application_address(self.coreid))]
- txn = transaction.ApplicationCreateTxn(
- sender=sender.getAddress(),
- on_complete=transaction.OnComplete.NoOpOC,
- approval_program=b64decode(approval["result"]),
- clear_program=b64decode(clear["result"]),
- global_schema=globalSchema,
- local_schema=localSchema,
- app_args=app_args,
- extra_pages = 2,
- sp=client.suggested_params(),
- )
-
- signedTxn = txn.sign(sender.getPrivateKey())
-
- client.send_transaction(signedTxn)
-
- response = self.waitForTransaction(client, signedTxn.get_txid())
- #pprint.pprint(response.__dict__)
- assert response.applicationIndex is not None and response.applicationIndex > 0
- # Lets give it a bit of money so that it is not a "ghost" account
- txn = transaction.PaymentTxn(sender = sender.getAddress(), sp = client.suggested_params(), receiver = get_application_address(response.applicationIndex), amt = 100000)
- signedTxn = txn.sign(sender.getPrivateKey())
- client.send_transaction(signedTxn)
- return response.applicationIndex
- def createTestApp(
- self,
- client: AlgodClient,
- sender: Account,
- ) -> int:
- approval, clear = get_test_app(client)
- globalSchema = transaction.StateSchema(num_uints=4, num_byte_slices=30)
- localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=16)
-
- txn = transaction.ApplicationCreateTxn(
- sender=sender.getAddress(),
- on_complete=transaction.OnComplete.NoOpOC,
- approval_program=b64decode(approval["result"]),
- clear_program=b64decode(clear["result"]),
- global_schema=globalSchema,
- local_schema=localSchema,
- sp=client.suggested_params(),
- )
-
- signedTxn = txn.sign(sender.getPrivateKey())
-
- client.send_transaction(signedTxn)
-
- response = self.waitForTransaction(client, signedTxn.get_txid())
- assert response.applicationIndex is not None and response.applicationIndex > 0
- # Lets give it a bit of money so that it is not a "ghost" account
- txn = transaction.PaymentTxn(sender = sender.getAddress(), sp = client.suggested_params(), receiver = get_application_address(response.applicationIndex), amt = 100000)
- signedTxn = txn.sign(sender.getPrivateKey())
- client.send_transaction(signedTxn)
- return response.applicationIndex
- def account_exists(self, client, app_id, addr):
- try:
- ai = client.account_info(addr)
- if "apps-local-state" not in ai:
- return False
-
- for app in ai["apps-local-state"]:
- if app["id"] == app_id:
- return True
- except:
- print("Failed to find account {}".format(addr))
- return False
- def optin(self, client, sender, app_id, idx, emitter, doCreate=True):
- aa = decode_address(get_application_address(app_id)).hex()
- lsa = self.tsig.populate(
- {
- "TMPL_APP_ID": app_id,
- "TMPL_APP_ADDRESS": aa,
- "TMPL_ADDR_IDX": idx,
- "TMPL_EMITTER_ID": emitter,
- }
- )
- sig_addr = lsa.address()
- if sig_addr not in self.cache and not self.account_exists(client, app_id, sig_addr):
- if doCreate:
- # pprint.pprint(("Creating", app_id, idx, emitter, sig_addr))
- # Create it
- sp = client.suggested_params()
-
- seed_txn = transaction.PaymentTxn(sender = sender.getAddress(),
- sp = sp,
- receiver = sig_addr,
- amt = self.seed_amt)
- seed_txn.fee = seed_txn.fee * 2
- optin_txn = transaction.ApplicationOptInTxn(sig_addr, sp, app_id, rekey_to=get_application_address(app_id))
- optin_txn.fee = 0
-
- transaction.assign_group_id([seed_txn, optin_txn])
-
- signed_seed = seed_txn.sign(sender.getPrivateKey())
- signed_optin = transaction.LogicSigTransaction(optin_txn, lsa)
-
- client.send_transactions([signed_seed, signed_optin])
- self.waitForTransaction(client, signed_optin.get_txid())
-
- self.cache[sig_addr] = True
- return sig_addr
- def parseSeqFromLog(self, txn):
- return int.from_bytes(b64decode(txn.innerTxns[0]["logs"][0]), "big")
- def getCreator(self, client, sender, asset_id):
- return client.asset_info(asset_id)["params"]["creator"]
- def sendTxn(self, client, sender, txns, doWait):
- transaction.assign_group_id(txns)
- grp = []
- pk = sender.getPrivateKey()
- for t in txns:
- grp.append(t.sign(pk))
- client.send_transactions(grp)
- if doWait:
- return self.waitForTransaction(client, grp[-1].get_txid())
- else:
- return grp[-1].get_txid()
- def bootGuardians(self, vaa, client, sender, coreid):
- p = self.parseVAA(vaa)
- if "NewGuardianSetIndex" not in p:
- raise Exception("invalid guardian VAA")
- seq_addr = self.optin(client, sender, coreid, int(p["sequence"] / max_bits), p["chainRaw"].hex() + p["emitter"].hex())
- guardian_addr = self.optin(client, sender, coreid, p["index"], b"guardian".hex())
- newguardian_addr = self.optin(client, sender, coreid, p["NewGuardianSetIndex"], b"guardian".hex())
- # wormhole is not a cheap protocol... we need to buy ourselves
- # some extra CPU cycles by having an early txn do nothing.
- # This leaves cycles over for later txn's in the same group
- sp = client.suggested_params()
- txns = [
- transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=coreid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"nop", b"0"],
- sp=sp
- ),
- transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=coreid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"nop", b"1"],
- sp=sp
- ),
- transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=coreid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"init", vaa, decode_address(self.vaa_verify["hash"])],
- accounts=[seq_addr, guardian_addr, newguardian_addr],
- sp=sp
- ),
- transaction.PaymentTxn(
- sender=sender.getAddress(),
- receiver=self.vaa_verify["hash"],
- amt=100000,
- sp=sp
- )
- ]
- return self.sendTxn(client, sender, txns, True)
- def decodeLocalState(self, client, sender, appid, addr):
- app_state = None
- ai = client.account_info(addr)
- for app in ai["apps-local-state"]:
- if app["id"] == appid:
- app_state = app["key-value"]
- ret = b''
- if None != app_state:
- vals = {}
- e = bytes.fromhex("00"*127)
- for kv in app_state:
- k = base64.b64decode(kv["key"])
- if k == "meta":
- continue
- key = int.from_bytes(k, "big")
- v = base64.b64decode(kv["value"]["bytes"])
- if v != e:
- vals[key] = v
- for k in sorted(vals.keys()):
- ret = ret + vals[k]
- return ret
- # There is no client side duplicate suppression, error checking, or validity
- # checking. We need to be able to detect all failure cases in
- # the contract itself and we want to use this to drive the failure test
- # cases
- def simpleVAA(self, vaa, client, sender, appid):
- p = {"version": int.from_bytes(vaa[0:1], "big"), "index": int.from_bytes(vaa[1:5], "big"), "siglen": int.from_bytes(vaa[5:6], "big")}
- ret["signatures"] = vaa[6:(ret["siglen"] * 66) + 6]
- ret["sigs"] = []
- for i in range(ret["siglen"]):
- ret["sigs"].append(vaa[(6 + (i * 66)):(6 + (i * 66)) + 66].hex())
- off = (ret["siglen"] * 66) + 6
- ret["digest"] = vaa[off:] # This is what is actually signed...
- ret["timestamp"] = int.from_bytes(vaa[off:(off + 4)], "big")
- off += 4
- ret["nonce"] = int.from_bytes(vaa[off:(off + 4)], "big")
- off += 4
- ret["chainRaw"] = vaa[off:(off + 2)]
- ret["chain"] = int.from_bytes(vaa[off:(off + 2)], "big")
- off += 2
- ret["emitter"] = vaa[off:(off + 32)]
- off += 32
- ret["sequence"] = int.from_bytes(vaa[off:(off + 8)], "big")
- off += 8
- ret["consistency"] = int.from_bytes(vaa[off:(off + 1)], "big")
- off += 1
- seq_addr = self.optin(client, sender, appid, int(p["sequence"] / max_bits), p["chainRaw"].hex() + p["emitter"].hex())
- # And then the signatures to help us verify the vaa_s
- guardian_addr = self.optin(client, sender, self.coreid, p["index"], b"guardian".hex())
- accts = [seq_addr, guardian_addr]
- keys = self.decodeLocalState(client, sender, self.coreid, guardian_addr)
- sp = client.suggested_params()
- txns = []
- # Right now there is not really a good way to estimate the fees,
- # in production, on a conjested network, how much verifying
- # the signatures is going to cost.
- # So, what we do instead
- # is we top off the verifier back up to 2A so effectively we
- # are paying for the previous persons overrage which on a
- # unconjested network should be zero
- pmt = 3000
- bal = self.getBalances(client, self.vaa_verify["hash"])
- if ((200000 - bal[0]) >= pmt):
- pmt = 200000 - bal[0]
- #print("Sending %d algo to cover fees" % (pmt))
- txns.append(
- transaction.PaymentTxn(
- sender = sender.getAddress(),
- sp = sp,
- receiver = self.vaa_verify["hash"],
- amt = pmt
- )
- )
- # How many signatures can we process in a single txn... we can do 9!
- bsize = (9*66)
- blocks = int(len(p["signatures"]) / bsize) + 1
- # We don't pass the entire payload in but instead just pass it pre digested. This gets around size
- # limitations with lsigs AND reduces the cost of the entire operation on a conjested network by reducing the
- # bytes passed into the transaction
- digest = keccak.new(digest_bits=256).update(keccak.new(digest_bits=256).update(p["digest"]).digest()).digest()
- for i in range(blocks):
- # Which signatures will we be verifying in this block
- sigs = p["signatures"][(i * bsize):]
- if (len(sigs) > bsize):
- sigs = sigs[:bsize]
- # keys
- kset = b''
- # Grab the key associated the signature
- for q in range(int(len(sigs) / 66)):
- # Which guardian is this signature associated with
- g = sigs[q * 66]
- key = keys[((g * 20) + 1) : (((g + 1) * 20) + 1)]
- kset = kset + key
- txns.append(transaction.ApplicationCallTxn(
- sender=self.vaa_verify["hash"],
- index=self.coreid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"verifySigs", sigs, kset, digest],
- accounts=accts,
- sp=sp
- ))
- txns.append(transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=self.coreid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"verifyVAA", vaa],
- accounts=accts,
- sp=sp
- ))
- return txns
- def signVAA(self, client, sender, txns):
- transaction.assign_group_id(txns)
- grp = []
- pk = sender.getPrivateKey()
- for t in txns:
- if ("app_args" in t.__dict__ and len(t.app_args) > 0 and t.app_args[0] == b"verifySigs"):
- grp.append(transaction.LogicSigTransaction(t, self.vaa_verify["lsig"]))
- else:
- grp.append(t.sign(pk))
- client.send_transactions(grp)
- ret = []
- for x in grp:
- response = self.waitForTransaction(client, x.get_txid())
- if "logs" in response.__dict__ and len(response.__dict__["logs"]) > 0:
- ret.append(response.__dict__["logs"])
- return ret
- def check_bits_set(self, client, app_id, addr, seq):
- bits_set = {}
- app_state = None
- ai = client.account_info(addr)
- for app in ai["apps-local-state"]:
- if app["id"] == app_id:
- app_state = app["key-value"]
- if app_state == None:
- return False
- start = int(seq / max_bits) * max_bits
- s = int((seq - start) / bits_per_key)
- b = int(((seq - start) - (s * bits_per_key)) / 8)
- k = base64.b64encode(s.to_bytes(1, "big")).decode('utf-8')
- for kv in app_state:
- if kv["key"] != k:
- continue
- v = base64.b64decode(kv["value"]["bytes"])
- bt = 1 << (seq%8)
- return ((v[b] & bt) != 0)
- return False
- def submitVAA(self, vaa, client, sender, appid):
- # A lot of our logic here depends on parseVAA and knowing what the payload is..
- p = self.parseVAA(vaa)
- #pprint.pprint(p)
- seq_addr = self.optin(client, sender, appid, int(p["sequence"] / max_bits), p["chainRaw"].hex() + p["emitter"].hex())
- assert self.check_bits_set(client, appid, seq_addr, p["sequence"]) == False
- # And then the signatures to help us verify the vaa_s
- guardian_addr = self.optin(client, sender, self.coreid, p["index"], b"guardian".hex())
- accts = [seq_addr, guardian_addr]
- # If this happens to be setting up a new guardian set, we probably need it as well...
- if p["Meta"] == "CoreGovernance" and p["action"] == 2:
- newguardian_addr = self.optin(client, sender, self.coreid, p["NewGuardianSetIndex"], b"guardian".hex())
- accts.append(newguardian_addr)
- # When we attest for a new token, we need some place to store the info... later we will need to
- # mirror the other way as well
- if p["Meta"] == "TokenBridge Attest" or p["Meta"] == "TokenBridge Transfer" or p["Meta"] == "TokenBridge Transfer With Payload":
- if p["FromChain"] != 8:
- chain_addr = self.optin(client, sender, self.tokenid, p["FromChain"], p["Contract"])
- else:
- asset_id = int.from_bytes(bytes.fromhex(p["Contract"]), "big")
- chain_addr = self.optin(client, sender, self.tokenid, asset_id, b"native".hex())
- accts.append(chain_addr)
- keys = self.decodeLocalState(client, sender, self.coreid, guardian_addr)
- print("keys: " + keys.hex())
- sp = client.suggested_params()
- txns = []
- # How many signatures can we process in a single txn... we can do 9!
- bsize = (9*66)
- blocks = int(len(p["signatures"]) / bsize) + 1
- # We don't pass the entire payload in but instead just pass it pre digested. This gets around size
- # limitations with lsigs AND reduces the cost of the entire operation on a conjested network by reducing the
- # bytes passed into the transaction
- digest = keccak.new(digest_bits=256).update(keccak.new(digest_bits=256).update(p["digest"]).digest()).digest()
- for i in range(blocks):
- # Which signatures will we be verifying in this block
- sigs = p["signatures"][(i * bsize):]
- if (len(sigs) > bsize):
- sigs = sigs[:bsize]
- # keys
- kset = b''
- # Grab the key associated the signature
- for q in range(int(len(sigs) / 66)):
- # Which guardian is this signature associated with
- g = sigs[q * 66]
- key = keys[((g * 20) + 1) : (((g + 1) * 20) + 1)]
- kset = kset + key
- txns.append(transaction.ApplicationCallTxn(
- sender=self.vaa_verify["hash"],
- index=self.coreid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"verifySigs", sigs, kset, digest],
- accounts=accts,
- sp=sp
- ))
- txns[-1].fee = 0
- txns.append(transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=self.coreid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"verifyVAA", vaa],
- accounts=accts,
- sp=sp
- ))
- txns[-1].fee = txns[-1].fee * (1 + blocks)
- if p["Meta"] == "CoreGovernance":
- txns.append(transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=self.coreid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"governance", vaa],
- accounts=accts,
- sp=sp
- ))
- txns.append(transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=self.coreid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"nop", 5],
- sp=sp
- ))
- if p["Meta"] == "TokenBridge RegisterChain" or p["Meta"] == "TokenBridge UpgradeContract":
- txns.append(transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=self.tokenid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"governance", vaa],
- accounts=accts,
- foreign_apps = [self.coreid],
- sp=sp
- ))
- if p["Meta"] == "TokenBridge Attest":
- # if we DO decode it, we can do a sanity check... of
- # course, the hacker might NOT decode it so we have to
- # handle both cases...
- asset = (self.decodeLocalState(client, sender, self.tokenid, chain_addr))
- foreign_assets = []
- if (len(asset) > 8):
- foreign_assets.append(int.from_bytes(asset[0:8], "big"))
- txns.append(
- transaction.PaymentTxn(
- sender = sender.getAddress(),
- sp = sp,
- receiver = chain_addr,
- amt = 100000
- )
- )
- txns.append(transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=self.tokenid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"nop", 1],
- sp=sp
- ))
- txns.append(transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=self.tokenid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"nop", 2],
- sp=sp
- ))
- txns.append(transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=self.tokenid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"receiveAttest", vaa],
- accounts=accts,
- foreign_assets = foreign_assets,
- sp=sp
- ))
- txns[-1].fee = txns[-1].fee * 2
- if p["Meta"] == "TokenBridge Transfer" or p["Meta"] == "TokenBridge Transfer With Payload":
- foreign_assets = []
- a = 0
- if p["FromChain"] != 8:
- asset = (self.decodeLocalState(client, sender, self.tokenid, chain_addr))
- if (len(asset) > 8):
- a = int.from_bytes(asset[0:8], "big")
- else:
- a = int.from_bytes(bytes.fromhex(p["Contract"]), "big")
- # The receiver needs to be optin in to receive the coins... Yeah, the relayer pays for this
- aid = 0
- if p["ToChain"] == 8 and p["Type"] == 3:
- aid = int.from_bytes(bytes.fromhex(p["ToAddress"]), "big")
- addr = get_application_address(aid)
- else:
- addr = encode_address(bytes.fromhex(p["ToAddress"]))
- if a != 0:
- foreign_assets.append(a)
- self.asset_optin(client, sender, foreign_assets[0], addr)
- # And this is how the relayer gets paid...
- if p["Fee"] != self.zeroPadBytes:
- self.asset_optin(client, sender, foreign_assets[0], sender.getAddress())
- accts.append(addr)
- txns.append(transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=self.tokenid,
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[b"completeTransfer", vaa],
- accounts=accts,
- foreign_assets = foreign_assets,
- sp=sp
- ))
- if aid != 0:
- txns[-1].foreign_apps = [aid]
- # We need to cover the inner transactions
- if p["Fee"] != self.zeroPadBytes:
- txns[-1].fee = txns[-1].fee * 3
- else:
- txns[-1].fee = txns[-1].fee * 2
- if p["Meta"] == "TokenBridge Transfer With Payload":
- m = abi.Method("portal_transfer", [abi.Argument("byte[]")], abi.Returns("byte[]"))
- txns.append(transaction.ApplicationCallTxn(
- sender=sender.getAddress(),
- index=int.from_bytes(bytes.fromhex(p["ToAddress"]), "big"),
- on_complete=transaction.OnComplete.NoOpOC,
- app_args=[m.get_selector(), m.args[0].type.encode(vaa)],
- foreign_assets = foreign_assets,
- sp=sp
- ))
- transaction.assign_group_id(txns)
- grp = []
- pk = sender.getPrivateKey()
- for t in txns:
- if ("app_args" in t.__dict__ and len(t.app_args) > 0 and t.app_args[0] == b"verifySigs"):
- grp.append(transaction.LogicSigTransaction(t, self.vaa_verify["lsig"]))
- else:
- grp.append(t.sign(pk))
- client.send_transactions(grp)
- ret = []
- for x in grp:
- response = self.waitForTransaction(client, x.get_txid())
- if "logs" in response.__dict__ and len(response.__dict__["logs"]) > 0:
- ret.append(response.__dict__["logs"])
- assert self.check_bits_set(client, appid, seq_addr, p["sequence"]) == True
- return ret
- def parseVAA(self, vaa):
- # print (vaa.hex())
- ret = {"version": int.from_bytes(vaa[0:1], "big"), "index": int.from_bytes(vaa[1:5], "big"), "siglen": int.from_bytes(vaa[5:6], "big")}
- ret["signatures"] = vaa[6:(ret["siglen"] * 66) + 6]
- ret["sigs"] = []
- for i in range(ret["siglen"]):
- ret["sigs"].append(vaa[(6 + (i * 66)):(6 + (i * 66)) + 66].hex())
- off = (ret["siglen"] * 66) + 6
- ret["digest"] = vaa[off:] # This is what is actually signed...
- ret["timestamp"] = int.from_bytes(vaa[off:(off + 4)], "big")
- off += 4
- ret["nonce"] = int.from_bytes(vaa[off:(off + 4)], "big")
- off += 4
- ret["chainRaw"] = vaa[off:(off + 2)]
- ret["chain"] = int.from_bytes(vaa[off:(off + 2)], "big")
- off += 2
- ret["emitter"] = vaa[off:(off + 32)]
- off += 32
- ret["sequence"] = int.from_bytes(vaa[off:(off + 8)], "big")
- off += 8
- ret["consistency"] = int.from_bytes(vaa[off:(off + 1)], "big")
- off += 1
- ret["Meta"] = "Unknown"
- if vaa[off:(off + 32)].hex() == "000000000000000000000000000000000000000000546f6b656e427269646765":
- ret["Meta"] = "TokenBridge"
- ret["module"] = vaa[off:(off + 32)].hex()
- off += 32
- ret["action"] = int.from_bytes(vaa[off:(off + 1)], "big")
- off += 1
- if ret["action"] == 1:
- ret["Meta"] = "TokenBridge RegisterChain"
- ret["targetChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
- off += 2
- ret["EmitterChainID"] = int.from_bytes(vaa[off:(off + 2)], "big")
- off += 2
- ret["targetEmitter"] = vaa[off:(off + 32)].hex()
- off += 32
- if ret["action"] == 2:
- ret["Meta"] = "TokenBridge UpgradeContract"
- ret["targetChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
- off += 2
- ret["newContract"] = vaa[off:(off + 32)].hex()
- off += 32
- pprint.pprint((vaa[off:(off + 32)].hex(), "00000000000000000000000000000000000000000000000000000000436f7265"))
- if vaa[off:(off + 32)].hex() == "00000000000000000000000000000000000000000000000000000000436f7265":
- ret["Meta"] = "CoreGovernance"
- ret["module"] = vaa[off:(off + 32)].hex()
- off += 32
- ret["action"] = int.from_bytes(vaa[off:(off + 1)], "big")
- off += 1
- ret["targetChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
- off += 2
- if ret["action"] == 2:
- ret["NewGuardianSetIndex"] = int.from_bytes(vaa[off:(off + 4)], "big")
- else:
- ret["Contract"] = vaa[off:(off + 32)].hex()
- if ((len(vaa[off:])) == 100) and int.from_bytes((vaa[off:off+1]), "big") == 2:
- ret["Meta"] = "TokenBridge Attest"
- ret["Type"] = int.from_bytes((vaa[off:off+1]), "big")
- off += 1
- ret["Contract"] = vaa[off:(off + 32)].hex()
- off += 32
- ret["FromChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
- off += 2
- ret["Decimals"] = int.from_bytes((vaa[off:off+1]), "big")
- off += 1
- ret["Symbol"] = vaa[off:(off + 32)].hex()
- off += 32
- ret["Name"] = vaa[off:(off + 32)].hex()
- if ((len(vaa[off:])) == 133) and int.from_bytes((vaa[off:off+1]), "big") == 1:
- ret["Meta"] = "TokenBridge Transfer"
- ret["Type"] = int.from_bytes((vaa[off:off+1]), "big")
- off += 1
- ret["Amount"] = vaa[off:(off + 32)].hex()
- off += 32
- ret["Contract"] = vaa[off:(off + 32)].hex()
- off += 32
- ret["FromChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
- off += 2
- ret["ToAddress"] = vaa[off:(off + 32)].hex()
- off += 32
- ret["ToChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
- off += 2
- ret["Fee"] = vaa[off:(off + 32)].hex()
- if int.from_bytes((vaa[off:off+1]), "big") == 3:
- ret["Meta"] = "TokenBridge Transfer With Payload"
- ret["Type"] = int.from_bytes((vaa[off:off+1]), "big")
- off += 1
- ret["Amount"] = vaa[off:(off + 32)].hex()
- off += 32
- ret["Contract"] = vaa[off:(off + 32)].hex()
- off += 32
- ret["FromChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
- off += 2
- ret["ToAddress"] = vaa[off:(off + 32)].hex()
- off += 32
- ret["ToChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
- off += 2
- ret["Fee"] = self.zeroPadBytes;
- ret["FromAddress"] = vaa[off:(off + 32)].hex()
- off += 32
- ret["Payload"] = vaa[off:].hex()
-
- return ret
- def boot(self):
- print("")
- print("Creating the PortalCore app")
- self.coreid = self.createPortalCoreApp(client=self.client, sender=self.foundation)
- pprint.pprint({"wormhole core": str(self.coreid), "address": get_application_address(self.coreid), "emitterAddress": decode_address(get_application_address(self.coreid)).hex()})
- print("Create the token bridge")
- self.tokenid = self.createTokenBridgeApp(self.client, self.foundation)
- pprint.pprint({"token bridge": str(self.tokenid), "address": get_application_address(self.tokenid), "emitterAddress": decode_address(get_application_address(self.tokenid)).hex()})
- if self.devnet or self.args.testnet:
- if self.devnet:
- print("Create test app")
- self.testid = self.createTestApp(self.client, self.foundation)
- pprint.pprint({"testapp": str(self.testid)})
- suggestedParams = self.client.suggested_params()
- fundingAccount = self.getGenesisAccounts()[0]
- txns: List[transaction.Transaction] = []
- wallet = "castle sing ice patrol mixture artist violin someone what access slow wrestle clap hero sausage oyster boost tone receive rapid bike announce pepper absent involve"
- a = Account.FromMnemonic(wallet)
- txns.append(
- transaction.PaymentTxn(
- sender=fundingAccount.getAddress(),
- receiver=a.getAddress(),
- amt=self.FUNDING_AMOUNT,
- sp=suggestedParams,
- )
- )
- txns = transaction.assign_group_id(txns)
- signedTxns = [
- txn.sign(fundingAccount.getPrivateKey()) for i, txn in enumerate(txns)
- ]
-
- self.client.send_transactions(signedTxns)
- print("Sent some ALGO to: " + wallet)
- print("Creating a Token...")
- txn = transaction.AssetConfigTxn(
- sender=a.getAddress(),
- sp=suggestedParams,
- total=1000000,
- default_frozen=False,
- unit_name="NORIUM",
- asset_name="ChuckNorium",
- manager=a.getAddress(),
- reserve=a.getAddress(),
- freeze=a.getAddress(),
- clawback=a.getAddress(),
- strict_empty_address_check=False,
- decimals=6)
- stxn = txn.sign(a.getPrivateKey())
- txid = self.client.send_transaction(stxn)
- print("NORIUM creation transaction ID: {}".format(txid))
- confirmed_txn = transaction.wait_for_confirmation(self.client, txid, 4)
- print("TXID: ", txid)
- print("Result confirmed in round: {}".format(confirmed_txn['confirmed-round']))
- print("Creating an NFT...")
- # JSON file
- dir_path = os.path.dirname(os.path.realpath(__file__))
- f = open (dir_path + 'cnNftMetadata.json', "r")
-
- # Reading from file
- metadataJSON = json.loads(f.read())
- metadataStr = json.dumps(metadataJSON)
- hash = hashlib.new("sha512_256")
- hash.update(b"arc0003/amj")
- hash.update(metadataStr.encode("utf-8"))
- json_metadata_hash = hash.digest()
- print("json_metadata_hash: ", hash.hexdigest())
- # Create transaction
- txn = transaction.AssetConfigTxn(
- sender=a.getAddress(),
- sp=suggestedParams,
- total=1,
- default_frozen=False,
- unit_name="CNART",
- asset_name="ChuckNoriumArtwork@arc3",
- manager=a.getAddress(),
- reserve=a.getAddress(),
- freeze=a.getAddress(),
- clawback=a.getAddress(),
- strict_empty_address_check=False,
- url="file://cnNftMetadata.json",
- metadata_hash=json_metadata_hash,
- decimals=0)
- stxn = txn.sign(a.getPrivateKey())
- txid = self.client.send_transaction(stxn)
- print("NORIUM NFT creation transaction ID: {}".format(txid))
- confirmed_txn = transaction.wait_for_confirmation(self.client, txid, 4)
- print("TXID: ", txid)
- print("Result confirmed in round: {}".format(confirmed_txn['confirmed-round']))
- if exists(self.args.env):
- if self.gt == None:
- self.gt = GenTest(False)
- with open(self.args.env, encoding = 'utf-8') as f:
- for line in f:
- e = line.rstrip('\n').split("=")
- print(e)
- if "TOKEN_BRIDGE" in e[0]:
- v = bytes.fromhex(e[1])
- self.submitVAA(v, self.client, self.foundation, self.tokenid)
- if "INIT_SIGNERS_CSV" in e[0]:
- self.gt.guardianKeys = e[1].split(",")
- print("guardianKeys: " + str(self.gt.guardianKeys))
- if "INIT_SIGNERS_KEYS_CSV" in e[0]:
- print("bootstrapping the guardian set...")
- self.gt.guardianPrivKeys = e[1].split(",")
- print("guardianPrivKeys: " + str(self.gt.guardianPrivKeys))
- seq = int(random.random() * (2**31))
- bootVAA = self.gt.genGuardianSetUpgrade(self.gt.guardianPrivKeys, self.args.guardianSet, self.args.guardianSet, seq, seq)
- print("dev vaa: " + bootVAA)
- self.bootGuardians(bytes.fromhex(bootVAA), self.client, self.foundation, self.coreid)
- seq = int(random.random() * (2**31))
- regChain = self.gt.genRegisterChain(self.gt.guardianPrivKeys, self.args.guardianSet, seq, seq, 8, decode_address(get_application_address(self.tokenid)).hex())
- print("ALGO_TOKEN_BRIDGE_VAA=" + regChain)
- # if self.args.env != ".env":
- # v = bytes.fromhex(regChain)
- # self.submitVAA(v, self.client, self.foundation, self.tokenid)
- # print("We submitted it!")
- def updateCore(self) -> None:
- print("Updating the core contracts")
- if self.args.approve == "" and self.args.clear == "":
- approval, clear = getCoreContracts(False, self.args.core_approve, self.args.core_clear, self.client, seed_amt=self.seed_amt, tmpl_sig=self.tsig, devMode = self.devnet or self.args.testnet)
- print("core approval " + decode_address(approval["hash"]).hex())
- print("core clear " + decode_address(clear["hash"]).hex())
- else:
- pprint.pprint([self.args.approve, self.args.clear])
- with open(self.args.approve, encoding = 'utf-8') as f:
- approval = {"result": f.readlines()[0]}
- pprint.pprint(approval)
- with open(self.args.clear, encoding = 'utf-8') as f:
- clear = {"result": f.readlines()[0]}
- pprint.pprint(clear)
- txn = transaction.ApplicationUpdateTxn(
- index=self.coreid,
- sender=self.foundation.getAddress(),
- approval_program=b64decode(approval["result"]),
- clear_program=b64decode(clear["result"]),
- app_args=[ ],
- sp=self.client.suggested_params(),
- )
-
- signedTxn = txn.sign(self.foundation.getPrivateKey())
- print("sending transaction")
- self.client.send_transaction(signedTxn)
- resp = self.waitForTransaction(self.client, signedTxn.get_txid())
- pprint.pprint(resp)
- for x in resp.__dict__["logs"]:
- print(x.hex())
- print("complete")
- def updateToken(self) -> None:
- approval, clear = get_token_bridge(False, self.args.token_approve, self.args.token_clear, self.client, seed_amt=self.seed_amt, tmpl_sig=self.tsig, devMode = self.devnet or self.args.testnet)
- print("token " + decode_address(approval["hash"]).hex())
- print("Updating the token contracts: " + str(len(b64decode(approval["result"]))))
- txn = transaction.ApplicationUpdateTxn(
- index=self.tokenid,
- sender=self.foundation.getAddress(),
- approval_program=b64decode(approval["result"]),
- clear_program=b64decode(clear["result"]),
- app_args=[ ],
- sp=self.client.suggested_params(),
- )
-
- signedTxn = txn.sign(self.foundation.getPrivateKey())
- print("sending transaction")
- self.client.send_transaction(signedTxn)
- resp = self.waitForTransaction(self.client, signedTxn.get_txid())
- for x in resp.__dict__["logs"]:
- print(x.hex())
- print("complete")
- def genTeal(self) -> None:
- print((True, self.args.core_approve, self.args.core_clear, self.client, self.seed_amt, self.tsig, self.devnet or self.args.testnet))
- devmode = (self.devnet or self.args.testnet) and not self.args.prodTeal
- approval1, clear1 = getCoreContracts(True, self.args.core_approve, self.args.core_clear, self.client, seed_amt=self.seed_amt, tmpl_sig=self.tsig, devMode = devmode)
- print("Generating the teal for the core contracts")
- approval2, clear2 = get_token_bridge(True, self.args.token_approve, self.args.token_clear, self.client, seed_amt=self.seed_amt, tmpl_sig=self.tsig, devMode = devmode)
- print("Generating the teal for the token contracts: " + str(len(b64decode(approval2["result"]))))
- if self.devnet:
- v = self.genUpgradePayloadBody(approval1, approval2)
- if self.gt == None:
- self.gt = GenTest(False)
-
- emitter = bytes.fromhex(self.zeroPadBytes[0:(31*2)] + "04")
-
- guardianSet = 0
-
- nonce = int(random.random() * 20000)
- coreVAA = self.gt.createSignedVAA(guardianSet, self.gt.guardianPrivKeys, int(time.time()), nonce, 1, emitter, int(random.random() * 20000), 32, 8, v[0])
- tokenVAA = self.gt.createSignedVAA(guardianSet, self.gt.guardianPrivKeys, int(time.time()), nonce, 1, emitter, int(random.random() * 20000), 32, 8, v[1])
-
- with open("teal/core_devnet.vaa", "w") as fout:
- fout.write(coreVAA)
-
- with open("teal/token_devnet.vaa", "w") as fout:
- fout.write(tokenVAA)
- def testnet(self):
- self.ALGOD_ADDRESS = self.args.algod_address = "https://testnet-api.algonode.cloud"
- self.INDEXER_ADDRESS = "https://testnet-idx.algonode.cloud"
- self.coreid = self.args.coreid
- self.tokenid = self.args.tokenid
- def mainnet(self):
- self.ALGOD_ADDRESS = self.args.algod_address = "https://mainnet-api.algonode.cloud"
- self.INDEXER_ADDRESS = "https://mainnet-idx.algonode.cloud"
- self.coreid = 842125965
- self.tokenid = 842126029
- if self.args.coreid != 4:
- self.coreid = self.args.coreid
- if self.args.coreid != 6:
- self.tokenid = self.args.tokenid
- def setup_args(self) -> None:
- parser = argparse.ArgumentParser(description='algorand setup')
-
- parser.add_argument('--algod_address', type=str, help='algod address (default: http://localhost:4001)',
- default="http://localhost:4001")
- parser.add_argument('--algod_token', type=str, help='algod access token',
- default="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
- parser.add_argument('--kmd_address', type=str, help='kmd wallet address (default: http://localhost:4002)',
- default="http://localhost:4002")
- parser.add_argument('--kmd_token', type=str, help='kmd wallet access token',
- default="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
- parser.add_argument('--kmd_name', type=str, help='kmd wallet name',
- default="unencrypted-default-wallet")
- parser.add_argument('--kmd_password', type=str, help='kmd wallet password', default="")
- parser.add_argument('--mnemonic', type=str, help='account mnemonic', default="")
- parser.add_argument('--core_approve', type=str, help='core approve teal', default="teal/core_approve.teal")
- parser.add_argument('--core_clear', type=str, help='core clear teal', default="teal/core_clear.teal")
- parser.add_argument('--token_approve', type=str, help='token approve teal', default="teal/token_approve.teal")
- parser.add_argument('--token_clear', type=str, help='token clear teal', default="teal/token_clear.teal")
- parser.add_argument('--coreid', type=int, help='core contract', default=4)
- parser.add_argument('--tokenid', type=int, help='token bridge contract', default=6)
- parser.add_argument('--devnet', action='store_true', help='setup devnet')
- parser.add_argument('--boot', action='store_true', help='bootstrap')
- parser.add_argument('--upgradePayload', action='store_true', help='gen the upgrade payload for the guardians to sign')
- parser.add_argument('--vaa', type=str, help='Submit the supplied VAA', default="")
- parser.add_argument('--env', type=str, help='deploying using the supplied .env file', default=".env")
- parser.add_argument('--guardianSet', type=int, help='What guardianSet should I syntheticly create if needed', default=0)
- parser.add_argument('--appid', type=str, help='The appid that the vaa submit is applied to', default="")
- parser.add_argument('--submit', action='store_true', help='submit the synthetic vaas')
- parser.add_argument('--updateCore', action='store_true', help='update the Core contracts')
- parser.add_argument('--updateToken', action='store_true', help='update the Token contracts')
- parser.add_argument('--upgradeVAA', action='store_true', help='generate a upgrade vaa for devnet')
- parser.add_argument('--print', action='store_true', help='print')
- parser.add_argument('--genParts', action='store_true', help='Get tssig parts')
- parser.add_argument('--prodTeal', action='store_true', help='use Production Deal')
- parser.add_argument('--genTeal', action='store_true', help='Generate all the teal from the pyteal')
- parser.add_argument('--fund', action='store_true', help='Generate some accounts and fund them')
- parser.add_argument('--testnet', action='store_true', help='Connect to testnet')
- parser.add_argument('--mainnet', action='store_true', help='Connect to mainnet')
- parser.add_argument('--bootGuardian', type=str, help='Submit the supplied VAA', default="")
- parser.add_argument('--rpc', type=str, help='RPC address', default="")
- parser.add_argument('--guardianKeys', type=str, help='GuardianKeys', default="")
- parser.add_argument('--guardianPrivKeys', type=str, help='guardianPrivKeys', default="")
- parser.add_argument('--approve', type=str, help='compiled approve contract', default="")
- parser.add_argument('--clear', type=str, help='compiled clear contract', default="")
- args = parser.parse_args()
- self.init(args)
- self.devnet = args.devnet
- def main(self) -> None:
- self.setup_args()
- args = self.args
- if args.testnet:
- self.testnet()
- if args.mainnet:
- self.mainnet()
- if args.rpc != "":
- self.ALGOD_ADDRESS = self.args.rpc
-
- self.client = self.getAlgodClient()
- if self.devnet or self.args.testnet:
- self.vaa_verify = self.client.compile(get_vaa_verify())
- else:
- c = AlgodClient("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "https://testnet-api.algonode.cloud")
- self.vaa_verify = c.compile(get_vaa_verify())
- self.vaa_verify["lsig"] = LogicSig(base64.b64decode(self.vaa_verify["result"]))
- if args.genTeal or args.boot:
- self.genTeal()
- # Generate the upgrade payload we need the guardians to sign
- if args.upgradePayload:
- print(self.genUpgradePayload())
- sys.exit(0)
- # This breaks the tsig up into the various parts so that we
- # can embed it into the Typescript code for reassembly
- if args.genParts:
- print("this.ALGO_VERIFY_HASH = \"%s\""%self.vaa_verify["hash"]);
- print("this.ALGO_VERIFY = new Uint8Array([", end='')
- for x in b64decode(self.vaa_verify["result"]):
- print("%d, "%(x), end='')
- print("])")
-
- parts = [
- self.tsig.get_bytecode_raw(0).hex(),
- self.tsig.get_bytecode_raw(1).hex(),
- self.tsig.get_bytecode_raw(2).hex(),
- self.tsig.get_bytecode_raw(3).hex(),
- self.tsig.get_bytecode_raw(4).hex()
- ]
- pprint.pprint(parts)
- sys.exit(0)
- if args.mnemonic:
- self.foundation = Account.FromMnemonic(args.mnemonic)
- if args.devnet and self.foundation == None:
- print("Generating the foundation account...")
- self.foundation = self.getTemporaryAccount(self.client)
- if self.args.fund:
- sys.exit(0)
- if self.foundation == None:
- print("We dont have a account? Here is a random one I just made up...")
- pk = account.generate_account()[0]
- print(" pk: " + pk)
- print(" address: " + account.address_from_private_key(pk))
- print(" mnemonic: " + mnemonic.from_private_key(pk))
- if args.testnet:
- print("go to https://bank.testnet.algorand.network/ to fill it up (You will probably want to send at least 2 loads to the wallet)")
- sys.exit(0)
- bal = self.getBalances(self.client, self.foundation.addr)
- print("foundation address " + self.foundation.addr + " (" + str(float(bal[0]) / 1000000.0) + " ALGO)")
- if bal[0] < 10000000:
- print("you need at least 10 ALGO to do darn near anything...")
- sys.exit(0)
- if args.guardianKeys != "":
- self.gt.guardianKeys = eval(args.guardianKeys)
- if args.guardianPrivKeys != "":
- self.gt.guardianPrivKeyss = eval(args.guardianPrivKeys)
- if args.upgradeVAA:
- ret = self.devnetUpgradeVAA()
- pprint.pprint(ret)
- if (args.submit) :
- print("submitting vaa to upgrade core: " + str(self.coreid))
- state = self.read_global_state(self.client, self.foundation.addr, self.coreid)
- pprint.pprint( {
- "validUpdateApproveHash": b64decode(state["validUpdateApproveHash"]).hex(),
- "validUpdateClearHash": b64decode(state["validUpdateClearHash"]).hex()
- })
- self.submitVAA(bytes.fromhex(ret[0]), self.client, self.foundation, self.coreid)
- state = self.read_global_state(self.client, self.foundation.addr, self.coreid)
- pprint.pprint( {
- "validUpdateApproveHash": b64decode(state["validUpdateApproveHash"]).hex(),
- "validUpdateClearHash": b64decode(state["validUpdateClearHash"]).hex()
- })
- print("submitting vaa to upgrade token: " + str(self.tokenid))
- state = self.read_global_state(self.client, self.foundation.addr, self.tokenid)
- pprint.pprint( {
- "validUpdateApproveHash": b64decode(state["validUpdateApproveHash"]).hex(),
- "validUpdateClearHash": b64decode(state["validUpdateClearHash"]).hex()
- })
- self.submitVAA(bytes.fromhex(ret[1]), self.client, self.foundation, self.tokenid)
- state = self.read_global_state(self.client, self.foundation.addr, self.tokenid)
- pprint.pprint( {
- "validUpdateApproveHash": b64decode(state["validUpdateApproveHash"]).hex(),
- "validUpdateClearHash": b64decode(state["validUpdateClearHash"]).hex()
- })
- if args.boot:
- self.boot()
- if args.updateCore:
- self.updateCore()
- if args.updateToken:
- self.updateToken()
- if args.vaa:
- if self.args.appid == "":
- raise Exception("You need to specifiy the appid when you are submitting vaas")
- vaa = bytes.fromhex(args.vaa)
- pprint.pprint(self.parseVAA(vaa))
- self.submitVAA(vaa, self.client, self.foundation, int(self.args.appid))
- if args.bootGuardian != "":
- vaa = bytes.fromhex(args.bootGuardian)
- pprint.pprint(self.parseVAA(vaa))
- response = self.bootGuardians(vaa, self.client, self.foundation, self.coreid)
- pprint.pprint(response.__dict__)
- if __name__ == "__main__":
- core = PortalCore()
- core.main()
|