admin.py 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519
  1. # python3 -m pip install pycryptodomex uvarint pyteal web3 coincurve
  2. import os
  3. from os.path import exists
  4. from time import time, sleep
  5. from eth_abi import encode_single, encode_abi
  6. from typing import List, Tuple, Dict, Any, Optional, Union
  7. from base64 import b64decode
  8. import base64
  9. import random
  10. import time
  11. import hashlib
  12. import uuid
  13. import sys
  14. import json
  15. import uvarint
  16. from local_blob import LocalBlob
  17. from wormhole_core import getCoreContracts
  18. from TmplSig import TmplSig
  19. import argparse
  20. from gentest import GenTest
  21. from algosdk.v2client.algod import AlgodClient
  22. from algosdk.kmd import KMDClient
  23. from algosdk import account, mnemonic, abi
  24. from algosdk.encoding import decode_address, encode_address
  25. from algosdk.future import transaction
  26. from pyteal import compileTeal, Mode, Expr
  27. from pyteal import *
  28. from algosdk.logic import get_application_address
  29. from vaa_verify import get_vaa_verify
  30. from Cryptodome.Hash import keccak
  31. from algosdk.future.transaction import LogicSig
  32. from token_bridge import get_token_bridge
  33. from test_contract import get_test_app
  34. from algosdk.v2client import indexer
  35. import pprint
  36. max_keys = 15
  37. max_bytes_per_key = 127
  38. bits_per_byte = 8
  39. bits_per_key = max_bytes_per_key * bits_per_byte
  40. max_bytes = max_bytes_per_key * max_keys
  41. max_bits = bits_per_byte * max_bytes
  42. class Account:
  43. """Represents a private key and address for an Algorand account"""
  44. def __init__(self, privateKey: str) -> None:
  45. self.sk = privateKey
  46. self.addr = account.address_from_private_key(privateKey)
  47. print (privateKey)
  48. print (" " + self.getMnemonic())
  49. print (" " + self.addr)
  50. def getAddress(self) -> str:
  51. return self.addr
  52. def getPrivateKey(self) -> str:
  53. return self.sk
  54. def getMnemonic(self) -> str:
  55. return mnemonic.from_private_key(self.sk)
  56. @classmethod
  57. def FromMnemonic(cls, m: str) -> "Account":
  58. return cls(mnemonic.to_private_key(m))
  59. class PendingTxnResponse:
  60. def __init__(self, response: Dict[str, Any]) -> None:
  61. self.poolError: str = response["pool-error"]
  62. self.txn: Dict[str, Any] = response["txn"]
  63. self.applicationIndex: Optional[int] = response.get("application-index")
  64. self.assetIndex: Optional[int] = response.get("asset-index")
  65. self.closeRewards: Optional[int] = response.get("close-rewards")
  66. self.closingAmount: Optional[int] = response.get("closing-amount")
  67. self.confirmedRound: Optional[int] = response.get("confirmed-round")
  68. self.globalStateDelta: Optional[Any] = response.get("global-state-delta")
  69. self.localStateDelta: Optional[Any] = response.get("local-state-delta")
  70. self.receiverRewards: Optional[int] = response.get("receiver-rewards")
  71. self.senderRewards: Optional[int] = response.get("sender-rewards")
  72. self.innerTxns: List[Any] = response.get("inner-txns", [])
  73. self.logs: List[bytes] = [b64decode(l) for l in response.get("logs", [])]
  74. class PortalCore:
  75. def __init__(self) -> None:
  76. self.gt = None
  77. self.foundation = None
  78. self.devnet = False
  79. self.ALGOD_ADDRESS = "http://localhost:4001"
  80. self.ALGOD_TOKEN = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
  81. self.FUNDING_AMOUNT = 100_000_000_000
  82. self.KMD_ADDRESS = "http://localhost:4002"
  83. self.KMD_TOKEN = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
  84. self.KMD_WALLET_NAME = "unencrypted-default-wallet"
  85. self.KMD_WALLET_PASSWORD = ""
  86. self.INDEXER_TOKEN = "a" * 64
  87. self.INDEXER_ADDRESS = 'http://localhost:8980'
  88. self.INDEXER_ROUND = 0
  89. self.NOTE_PREFIX = 'publishMessage'.encode()
  90. self.myindexer = None
  91. self.seed_amt = int(1002000) # The black magic in this number...
  92. self.cache = {}
  93. self.asset_cache = {}
  94. self.kmdAccounts : Optional[List[Account]] = None
  95. self.accountList : List[Account] = []
  96. self.zeroPadBytes = "00"*32
  97. self.tsig = TmplSig("sig")
  98. def init(self, args) -> None:
  99. self.args = args
  100. self.ALGOD_ADDRESS = args.algod_address
  101. self.ALGOD_TOKEN = args.algod_token
  102. self.KMD_ADDRESS = args.kmd_address
  103. self.KMD_TOKEN = args.kmd_token
  104. self.KMD_WALLET_NAME = args.kmd_name
  105. self.KMD_WALLET_PASSWORD = args.kmd_password
  106. self.TARGET_ACCOUNT = args.mnemonic
  107. self.coreid = args.coreid
  108. self.tokenid = args.tokenid
  109. if exists(self.args.env):
  110. if self.gt == None:
  111. self.gt = GenTest(False)
  112. with open(self.args.env, encoding = 'utf-8') as f:
  113. for line in f:
  114. e = line.rstrip('\n').split("=")
  115. if "INIT_SIGNERS_CSV" in e[0]:
  116. self.gt.guardianKeys = e[1].split(",")
  117. print("guardianKeys=" + str(self.gt.guardianKeys))
  118. if "INIT_SIGNERS_KEYS_CSV" in e[0]:
  119. self.gt.guardianPrivKeys = e[1].split(",")
  120. print("guardianPrivKeys=" + str(self.gt.guardianPrivKeys))
  121. def waitForTransaction(
  122. self, client: AlgodClient, txID: str, timeout: int = 10
  123. ) -> PendingTxnResponse:
  124. lastStatus = client.status()
  125. lastRound = lastStatus["last-round"]
  126. startRound = lastRound
  127. while lastRound < startRound + timeout:
  128. pending_txn = client.pending_transaction_info(txID)
  129. if pending_txn.get("confirmed-round", 0) > 0:
  130. return PendingTxnResponse(pending_txn)
  131. if pending_txn["pool-error"]:
  132. raise Exception("Pool error: {}".format(pending_txn["pool-error"]))
  133. lastStatus = client.status_after_block(lastRound + 1)
  134. lastRound += 1
  135. raise Exception(
  136. "Transaction {} not confirmed after {} rounds".format(txID, timeout)
  137. )
  138. def getKmdClient(self) -> KMDClient:
  139. return KMDClient(self.KMD_TOKEN, self.KMD_ADDRESS)
  140. def getGenesisAccounts(self) -> List[Account]:
  141. if self.kmdAccounts is None:
  142. kmd = self.getKmdClient()
  143. wallets = kmd.list_wallets()
  144. walletID = None
  145. for wallet in wallets:
  146. if wallet["name"] == self.KMD_WALLET_NAME:
  147. walletID = wallet["id"]
  148. break
  149. if walletID is None:
  150. raise Exception("Wallet not found: {}".format(self.KMD_WALLET_NAME))
  151. walletHandle = kmd.init_wallet_handle(walletID, self.KMD_WALLET_PASSWORD)
  152. try:
  153. addresses = kmd.list_keys(walletHandle)
  154. privateKeys = [
  155. kmd.export_key(walletHandle, self.KMD_WALLET_PASSWORD, addr)
  156. for addr in addresses
  157. ]
  158. self.kmdAccounts = [Account(sk) for sk in privateKeys]
  159. finally:
  160. kmd.release_wallet_handle(walletHandle)
  161. return self.kmdAccounts
  162. def getTemporaryAccount(self, client: AlgodClient) -> Account:
  163. if len(self.accountList) == 0:
  164. sks = [account.generate_account()[0] for i in range(3)]
  165. self.accountList = [Account(sk) for sk in sks]
  166. genesisAccounts = self.getGenesisAccounts()
  167. suggestedParams = client.suggested_params()
  168. txns: List[transaction.Transaction] = []
  169. for i, a in enumerate(self.accountList):
  170. fundingAccount = genesisAccounts[i % len(genesisAccounts)]
  171. txns.append(
  172. transaction.PaymentTxn(
  173. sender=fundingAccount.getAddress(),
  174. receiver=a.getAddress(),
  175. amt=self.FUNDING_AMOUNT,
  176. sp=suggestedParams,
  177. )
  178. )
  179. txns = transaction.assign_group_id(txns)
  180. signedTxns = [
  181. txn.sign(genesisAccounts[i % len(genesisAccounts)].getPrivateKey())
  182. for i, txn in enumerate(txns)
  183. ]
  184. client.send_transactions(signedTxns)
  185. self.waitForTransaction(client, signedTxns[0].get_txid())
  186. return self.accountList.pop()
  187. def getAlgodClient(self) -> AlgodClient:
  188. return AlgodClient(self.ALGOD_TOKEN, self.ALGOD_ADDRESS)
  189. def getBalances(self, client: AlgodClient, account: str) -> Dict[int, int]:
  190. balances: Dict[int, int] = dict()
  191. accountInfo = client.account_info(account)
  192. # set key 0 to Algo balance
  193. balances[0] = accountInfo["amount"]
  194. assets: List[Dict[str, Any]] = accountInfo.get("assets", [])
  195. for assetHolding in assets:
  196. assetID = assetHolding["asset-id"]
  197. amount = assetHolding["amount"]
  198. balances[assetID] = amount
  199. return balances
  200. def fullyCompileContract(self, client: AlgodClient, contract: Expr) -> bytes:
  201. teal = compileTeal(contract, mode=Mode.Application, version=6)
  202. response = client.compile(teal)
  203. return response
  204. # helper function that formats global state for printing
  205. def format_state(self, state):
  206. formatted = {}
  207. for item in state:
  208. key = item['key']
  209. value = item['value']
  210. formatted_key = base64.b64decode(key).decode('utf-8')
  211. if value['type'] == 1:
  212. # byte string
  213. if formatted_key == 'voted':
  214. formatted_value = base64.b64decode(value['bytes']).decode('utf-8')
  215. else:
  216. formatted_value = value['bytes']
  217. formatted[formatted_key] = formatted_value
  218. else:
  219. # integer
  220. formatted[formatted_key] = value['uint']
  221. return formatted
  222. # helper function to read app global state
  223. def read_global_state(self, client, addr, app_id):
  224. results = client.account_info(addr)
  225. apps_created = results['created-apps']
  226. for app in apps_created:
  227. if app['id'] == app_id and 'global-state' in app['params']:
  228. return self.format_state(app['params']['global-state'])
  229. return {}
  230. def read_state(self, client, addr, app_id):
  231. results = client.account_info(addr)
  232. apps_created = results['created-apps']
  233. for app in apps_created:
  234. if app['id'] == app_id:
  235. return app
  236. return {}
  237. def encoder(self, type, val):
  238. if type == 'uint8':
  239. return encode_single(type, val).hex()[62:64]
  240. if type == 'uint16':
  241. return encode_single(type, val).hex()[60:64]
  242. if type == 'uint32':
  243. return encode_single(type, val).hex()[56:64]
  244. if type == 'uint64':
  245. return encode_single(type, val).hex()[64-(16):64]
  246. if type == 'uint128':
  247. return encode_single(type, val).hex()[64-(32):64]
  248. if type == 'uint256' or type == 'bytes32':
  249. return encode_single(type, val).hex()[64-(64):64]
  250. raise Exception("invalid type")
  251. def devnetUpgradeVAA(self):
  252. v = self.genUpgradePayload()
  253. print("core payload: " + str(v[0]))
  254. print("token payload: " + str(v[1]))
  255. if self.gt == None:
  256. self.gt = GenTest(False)
  257. emitter = bytes.fromhex(self.zeroPadBytes[0:(31*2)] + "04")
  258. guardianSet = self.getGovSet()
  259. print("guardianSet: " + str(guardianSet))
  260. nonce = int(random.random() * 20000)
  261. ret = [
  262. self.gt.createSignedVAA(guardianSet, self.gt.guardianPrivKeys, int(time.time()), nonce, 1, emitter, int(random.random() * 20000), 32, 8, v[0]),
  263. self.gt.createSignedVAA(guardianSet, self.gt.guardianPrivKeys, int(time.time()), nonce, 1, emitter, int(random.random() * 20000), 32, 8, v[1]),
  264. ]
  265. # pprint.pprint(self.parseVAA(bytes.fromhex(ret[0])))
  266. # pprint.pprint(self.parseVAA(bytes.fromhex(ret[1])))
  267. return ret
  268. def getMessageFee(self):
  269. s = self.client.application_info(self.coreid)["params"]["global-state"]
  270. k = base64.b64encode(b"MessageFee").decode('utf-8')
  271. for x in s:
  272. if x["key"] == k:
  273. return x["value"]["uint"]
  274. return -1
  275. def getGovSet(self):
  276. s = self.client.application_info(self.coreid)["params"]["global-state"]
  277. k = base64.b64encode(b"currentGuardianSetIndex").decode('utf-8')
  278. for x in s:
  279. if x["key"] == k:
  280. return x["value"]["uint"]
  281. return -1
  282. def genUpgradePayload(self):
  283. 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)
  284. b = self.zeroPadBytes[0:(28*2)]
  285. b += self.encoder("uint8", ord("C"))
  286. b += self.encoder("uint8", ord("o"))
  287. b += self.encoder("uint8", ord("r"))
  288. b += self.encoder("uint8", ord("e"))
  289. b += self.encoder("uint8", 1)
  290. b += self.encoder("uint16", 8)
  291. b += decode_address(approval["hash"]).hex()
  292. print("core hash: " + decode_address(approval["hash"]).hex())
  293. ret = [b]
  294. 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)
  295. b = self.zeroPadBytes[0:((32 -11)*2)]
  296. b += self.encoder("uint8", ord("T"))
  297. b += self.encoder("uint8", ord("o"))
  298. b += self.encoder("uint8", ord("k"))
  299. b += self.encoder("uint8", ord("e"))
  300. b += self.encoder("uint8", ord("n"))
  301. b += self.encoder("uint8", ord("B"))
  302. b += self.encoder("uint8", ord("r"))
  303. b += self.encoder("uint8", ord("i"))
  304. b += self.encoder("uint8", ord("d"))
  305. b += self.encoder("uint8", ord("g"))
  306. b += self.encoder("uint8", ord("e"))
  307. b += self.encoder("uint8", 2) # action
  308. b += self.encoder("uint16", 8) # target chain
  309. b += decode_address(approval["hash"]).hex()
  310. print("token hash: " + decode_address(approval["hash"]).hex())
  311. ret.append(b)
  312. return ret
  313. def createPortalCoreApp(
  314. self,
  315. client: AlgodClient,
  316. sender: Account,
  317. ) -> int:
  318. 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)
  319. globalSchema = transaction.StateSchema(num_uints=8, num_byte_slices=40)
  320. localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=16)
  321. app_args = [ ]
  322. txn = transaction.ApplicationCreateTxn(
  323. sender=sender.getAddress(),
  324. on_complete=transaction.OnComplete.NoOpOC,
  325. approval_program=b64decode(approval["result"]),
  326. clear_program=b64decode(clear["result"]),
  327. global_schema=globalSchema,
  328. local_schema=localSchema,
  329. extra_pages = 1,
  330. app_args=app_args,
  331. sp=client.suggested_params(),
  332. )
  333. signedTxn = txn.sign(sender.getPrivateKey())
  334. client.send_transaction(signedTxn)
  335. response = self.waitForTransaction(client, signedTxn.get_txid())
  336. assert response.applicationIndex is not None and response.applicationIndex > 0
  337. # Lets give it a bit of money so that it is not a "ghost" account
  338. txn = transaction.PaymentTxn(sender = sender.getAddress(), sp = client.suggested_params(), receiver = get_application_address(response.applicationIndex), amt = 100000)
  339. signedTxn = txn.sign(sender.getPrivateKey())
  340. client.send_transaction(signedTxn)
  341. return response.applicationIndex
  342. def createTokenBridgeApp(
  343. self,
  344. client: AlgodClient,
  345. sender: Account,
  346. ) -> int:
  347. 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)
  348. if len(b64decode(approval["result"])) > 4060:
  349. print("token bridge contract is too large... This might prevent updates later")
  350. globalSchema = transaction.StateSchema(num_uints=4, num_byte_slices=30)
  351. localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=16)
  352. app_args = [self.coreid, decode_address(get_application_address(self.coreid))]
  353. txn = transaction.ApplicationCreateTxn(
  354. sender=sender.getAddress(),
  355. on_complete=transaction.OnComplete.NoOpOC,
  356. approval_program=b64decode(approval["result"]),
  357. clear_program=b64decode(clear["result"]),
  358. global_schema=globalSchema,
  359. local_schema=localSchema,
  360. app_args=app_args,
  361. extra_pages = 2,
  362. sp=client.suggested_params(),
  363. )
  364. signedTxn = txn.sign(sender.getPrivateKey())
  365. client.send_transaction(signedTxn)
  366. response = self.waitForTransaction(client, signedTxn.get_txid())
  367. #pprint.pprint(response.__dict__)
  368. assert response.applicationIndex is not None and response.applicationIndex > 0
  369. # Lets give it a bit of money so that it is not a "ghost" account
  370. txn = transaction.PaymentTxn(sender = sender.getAddress(), sp = client.suggested_params(), receiver = get_application_address(response.applicationIndex), amt = 100000)
  371. signedTxn = txn.sign(sender.getPrivateKey())
  372. client.send_transaction(signedTxn)
  373. return response.applicationIndex
  374. def createTestApp(
  375. self,
  376. client: AlgodClient,
  377. sender: Account,
  378. ) -> int:
  379. approval, clear = get_test_app(client)
  380. globalSchema = transaction.StateSchema(num_uints=4, num_byte_slices=30)
  381. localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=16)
  382. txn = transaction.ApplicationCreateTxn(
  383. sender=sender.getAddress(),
  384. on_complete=transaction.OnComplete.NoOpOC,
  385. approval_program=b64decode(approval["result"]),
  386. clear_program=b64decode(clear["result"]),
  387. global_schema=globalSchema,
  388. local_schema=localSchema,
  389. sp=client.suggested_params(),
  390. )
  391. signedTxn = txn.sign(sender.getPrivateKey())
  392. client.send_transaction(signedTxn)
  393. response = self.waitForTransaction(client, signedTxn.get_txid())
  394. assert response.applicationIndex is not None and response.applicationIndex > 0
  395. # Lets give it a bit of money so that it is not a "ghost" account
  396. txn = transaction.PaymentTxn(sender = sender.getAddress(), sp = client.suggested_params(), receiver = get_application_address(response.applicationIndex), amt = 100000)
  397. signedTxn = txn.sign(sender.getPrivateKey())
  398. client.send_transaction(signedTxn)
  399. return response.applicationIndex
  400. def account_exists(self, client, app_id, addr):
  401. try:
  402. ai = client.account_info(addr)
  403. if "apps-local-state" not in ai:
  404. return False
  405. for app in ai["apps-local-state"]:
  406. if app["id"] == app_id:
  407. return True
  408. except:
  409. print("Failed to find account {}".format(addr))
  410. return False
  411. def optin(self, client, sender, app_id, idx, emitter, doCreate=True):
  412. aa = decode_address(get_application_address(app_id)).hex()
  413. lsa = self.tsig.populate(
  414. {
  415. "TMPL_APP_ID": app_id,
  416. "TMPL_APP_ADDRESS": aa,
  417. "TMPL_ADDR_IDX": idx,
  418. "TMPL_EMITTER_ID": emitter,
  419. }
  420. )
  421. sig_addr = lsa.address()
  422. if sig_addr not in self.cache and not self.account_exists(client, app_id, sig_addr):
  423. if doCreate:
  424. # pprint.pprint(("Creating", app_id, idx, emitter, sig_addr))
  425. # Create it
  426. sp = client.suggested_params()
  427. seed_txn = transaction.PaymentTxn(sender = sender.getAddress(),
  428. sp = sp,
  429. receiver = sig_addr,
  430. amt = self.seed_amt)
  431. seed_txn.fee = seed_txn.fee * 2
  432. optin_txn = transaction.ApplicationOptInTxn(sig_addr, sp, app_id, rekey_to=get_application_address(app_id))
  433. optin_txn.fee = 0
  434. transaction.assign_group_id([seed_txn, optin_txn])
  435. signed_seed = seed_txn.sign(sender.getPrivateKey())
  436. signed_optin = transaction.LogicSigTransaction(optin_txn, lsa)
  437. client.send_transactions([signed_seed, signed_optin])
  438. self.waitForTransaction(client, signed_optin.get_txid())
  439. self.cache[sig_addr] = True
  440. return sig_addr
  441. def parseSeqFromLog(self, txn):
  442. return int.from_bytes(b64decode(txn.innerTxns[0]["logs"][0]), "big")
  443. def getCreator(self, client, sender, asset_id):
  444. return client.asset_info(asset_id)["params"]["creator"]
  445. def sendTxn(self, client, sender, txns, doWait):
  446. transaction.assign_group_id(txns)
  447. grp = []
  448. pk = sender.getPrivateKey()
  449. for t in txns:
  450. grp.append(t.sign(pk))
  451. client.send_transactions(grp)
  452. if doWait:
  453. return self.waitForTransaction(client, grp[-1].get_txid())
  454. else:
  455. return grp[-1].get_txid()
  456. def bootGuardians(self, vaa, client, sender, coreid):
  457. p = self.parseVAA(vaa)
  458. if "NewGuardianSetIndex" not in p:
  459. raise Exception("invalid guardian VAA")
  460. seq_addr = self.optin(client, sender, coreid, int(p["sequence"] / max_bits), p["chainRaw"].hex() + p["emitter"].hex())
  461. guardian_addr = self.optin(client, sender, coreid, p["index"], b"guardian".hex())
  462. newguardian_addr = self.optin(client, sender, coreid, p["NewGuardianSetIndex"], b"guardian".hex())
  463. # wormhole is not a cheap protocol... we need to buy ourselves
  464. # some extra CPU cycles by having an early txn do nothing.
  465. # This leaves cycles over for later txn's in the same group
  466. sp = client.suggested_params()
  467. txns = [
  468. transaction.ApplicationCallTxn(
  469. sender=sender.getAddress(),
  470. index=coreid,
  471. on_complete=transaction.OnComplete.NoOpOC,
  472. app_args=[b"nop", b"0"],
  473. sp=sp
  474. ),
  475. transaction.ApplicationCallTxn(
  476. sender=sender.getAddress(),
  477. index=coreid,
  478. on_complete=transaction.OnComplete.NoOpOC,
  479. app_args=[b"nop", b"1"],
  480. sp=sp
  481. ),
  482. transaction.ApplicationCallTxn(
  483. sender=sender.getAddress(),
  484. index=coreid,
  485. on_complete=transaction.OnComplete.NoOpOC,
  486. app_args=[b"init", vaa, decode_address(self.vaa_verify["hash"])],
  487. accounts=[seq_addr, guardian_addr, newguardian_addr],
  488. sp=sp
  489. ),
  490. transaction.PaymentTxn(
  491. sender=sender.getAddress(),
  492. receiver=self.vaa_verify["hash"],
  493. amt=100000,
  494. sp=sp
  495. )
  496. ]
  497. return self.sendTxn(client, sender, txns, True)
  498. def decodeLocalState(self, client, sender, appid, addr):
  499. app_state = None
  500. ai = client.account_info(addr)
  501. for app in ai["apps-local-state"]:
  502. if app["id"] == appid:
  503. app_state = app["key-value"]
  504. ret = b''
  505. if None != app_state:
  506. vals = {}
  507. e = bytes.fromhex("00"*127)
  508. for kv in app_state:
  509. k = base64.b64decode(kv["key"])
  510. if k == "meta":
  511. continue
  512. key = int.from_bytes(k, "big")
  513. v = base64.b64decode(kv["value"]["bytes"])
  514. if v != e:
  515. vals[key] = v
  516. for k in sorted(vals.keys()):
  517. ret = ret + vals[k]
  518. return ret
  519. # There is no client side duplicate suppression, error checking, or validity
  520. # checking. We need to be able to detect all failure cases in
  521. # the contract itself and we want to use this to drive the failure test
  522. # cases
  523. def simpleVAA(self, vaa, client, sender, appid):
  524. 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")}
  525. ret["signatures"] = vaa[6:(ret["siglen"] * 66) + 6]
  526. ret["sigs"] = []
  527. for i in range(ret["siglen"]):
  528. ret["sigs"].append(vaa[(6 + (i * 66)):(6 + (i * 66)) + 66].hex())
  529. off = (ret["siglen"] * 66) + 6
  530. ret["digest"] = vaa[off:] # This is what is actually signed...
  531. ret["timestamp"] = int.from_bytes(vaa[off:(off + 4)], "big")
  532. off += 4
  533. ret["nonce"] = int.from_bytes(vaa[off:(off + 4)], "big")
  534. off += 4
  535. ret["chainRaw"] = vaa[off:(off + 2)]
  536. ret["chain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  537. off += 2
  538. ret["emitter"] = vaa[off:(off + 32)]
  539. off += 32
  540. ret["sequence"] = int.from_bytes(vaa[off:(off + 8)], "big")
  541. off += 8
  542. ret["consistency"] = int.from_bytes(vaa[off:(off + 1)], "big")
  543. off += 1
  544. seq_addr = self.optin(client, sender, appid, int(p["sequence"] / max_bits), p["chainRaw"].hex() + p["emitter"].hex())
  545. # And then the signatures to help us verify the vaa_s
  546. guardian_addr = self.optin(client, sender, self.coreid, p["index"], b"guardian".hex())
  547. accts = [seq_addr, guardian_addr]
  548. keys = self.decodeLocalState(client, sender, self.coreid, guardian_addr)
  549. sp = client.suggested_params()
  550. txns = []
  551. # Right now there is not really a good way to estimate the fees,
  552. # in production, on a conjested network, how much verifying
  553. # the signatures is going to cost.
  554. # So, what we do instead
  555. # is we top off the verifier back up to 2A so effectively we
  556. # are paying for the previous persons overrage which on a
  557. # unconjested network should be zero
  558. pmt = 3000
  559. bal = self.getBalances(client, self.vaa_verify["hash"])
  560. if ((200000 - bal[0]) >= pmt):
  561. pmt = 200000 - bal[0]
  562. #print("Sending %d algo to cover fees" % (pmt))
  563. txns.append(
  564. transaction.PaymentTxn(
  565. sender = sender.getAddress(),
  566. sp = sp,
  567. receiver = self.vaa_verify["hash"],
  568. amt = pmt
  569. )
  570. )
  571. # How many signatures can we process in a single txn... we can do 9!
  572. bsize = (9*66)
  573. blocks = int(len(p["signatures"]) / bsize) + 1
  574. # We don't pass the entire payload in but instead just pass it pre digested. This gets around size
  575. # limitations with lsigs AND reduces the cost of the entire operation on a conjested network by reducing the
  576. # bytes passed into the transaction
  577. digest = keccak.new(digest_bits=256).update(keccak.new(digest_bits=256).update(p["digest"]).digest()).digest()
  578. for i in range(blocks):
  579. # Which signatures will we be verifying in this block
  580. sigs = p["signatures"][(i * bsize):]
  581. if (len(sigs) > bsize):
  582. sigs = sigs[:bsize]
  583. # keys
  584. kset = b''
  585. # Grab the key associated the signature
  586. for q in range(int(len(sigs) / 66)):
  587. # Which guardian is this signature associated with
  588. g = sigs[q * 66]
  589. key = keys[((g * 20) + 1) : (((g + 1) * 20) + 1)]
  590. kset = kset + key
  591. txns.append(transaction.ApplicationCallTxn(
  592. sender=self.vaa_verify["hash"],
  593. index=self.coreid,
  594. on_complete=transaction.OnComplete.NoOpOC,
  595. app_args=[b"verifySigs", sigs, kset, digest],
  596. accounts=accts,
  597. sp=sp
  598. ))
  599. txns.append(transaction.ApplicationCallTxn(
  600. sender=sender.getAddress(),
  601. index=self.coreid,
  602. on_complete=transaction.OnComplete.NoOpOC,
  603. app_args=[b"verifyVAA", vaa],
  604. accounts=accts,
  605. sp=sp
  606. ))
  607. return txns
  608. def signVAA(self, client, sender, txns):
  609. transaction.assign_group_id(txns)
  610. grp = []
  611. pk = sender.getPrivateKey()
  612. for t in txns:
  613. if ("app_args" in t.__dict__ and len(t.app_args) > 0 and t.app_args[0] == b"verifySigs"):
  614. grp.append(transaction.LogicSigTransaction(t, self.vaa_verify["lsig"]))
  615. else:
  616. grp.append(t.sign(pk))
  617. client.send_transactions(grp)
  618. ret = []
  619. for x in grp:
  620. response = self.waitForTransaction(client, x.get_txid())
  621. if "logs" in response.__dict__ and len(response.__dict__["logs"]) > 0:
  622. ret.append(response.__dict__["logs"])
  623. return ret
  624. def check_bits_set(self, client, app_id, addr, seq):
  625. bits_set = {}
  626. app_state = None
  627. ai = client.account_info(addr)
  628. for app in ai["apps-local-state"]:
  629. if app["id"] == app_id:
  630. app_state = app["key-value"]
  631. if app_state == None:
  632. return False
  633. start = int(seq / max_bits) * max_bits
  634. s = int((seq - start) / bits_per_key)
  635. b = int(((seq - start) - (s * bits_per_key)) / 8)
  636. k = base64.b64encode(s.to_bytes(1, "big")).decode('utf-8')
  637. for kv in app_state:
  638. if kv["key"] != k:
  639. continue
  640. v = base64.b64decode(kv["value"]["bytes"])
  641. bt = 1 << (seq%8)
  642. return ((v[b] & bt) != 0)
  643. return False
  644. def submitVAA(self, vaa, client, sender, appid):
  645. # A lot of our logic here depends on parseVAA and knowing what the payload is..
  646. p = self.parseVAA(vaa)
  647. #pprint.pprint(p)
  648. seq_addr = self.optin(client, sender, appid, int(p["sequence"] / max_bits), p["chainRaw"].hex() + p["emitter"].hex())
  649. assert self.check_bits_set(client, appid, seq_addr, p["sequence"]) == False
  650. # And then the signatures to help us verify the vaa_s
  651. guardian_addr = self.optin(client, sender, self.coreid, p["index"], b"guardian".hex())
  652. accts = [seq_addr, guardian_addr]
  653. # If this happens to be setting up a new guardian set, we probably need it as well...
  654. if p["Meta"] == "CoreGovernance" and p["action"] == 2:
  655. newguardian_addr = self.optin(client, sender, self.coreid, p["NewGuardianSetIndex"], b"guardian".hex())
  656. accts.append(newguardian_addr)
  657. # When we attest for a new token, we need some place to store the info... later we will need to
  658. # mirror the other way as well
  659. if p["Meta"] == "TokenBridge Attest" or p["Meta"] == "TokenBridge Transfer" or p["Meta"] == "TokenBridge Transfer With Payload":
  660. if p["FromChain"] != 8:
  661. chain_addr = self.optin(client, sender, self.tokenid, p["FromChain"], p["Contract"])
  662. else:
  663. asset_id = int.from_bytes(bytes.fromhex(p["Contract"]), "big")
  664. chain_addr = self.optin(client, sender, self.tokenid, asset_id, b"native".hex())
  665. accts.append(chain_addr)
  666. keys = self.decodeLocalState(client, sender, self.coreid, guardian_addr)
  667. print("keys: " + keys.hex())
  668. sp = client.suggested_params()
  669. txns = []
  670. # How many signatures can we process in a single txn... we can do 9!
  671. bsize = (9*66)
  672. blocks = int(len(p["signatures"]) / bsize) + 1
  673. # We don't pass the entire payload in but instead just pass it pre digested. This gets around size
  674. # limitations with lsigs AND reduces the cost of the entire operation on a conjested network by reducing the
  675. # bytes passed into the transaction
  676. digest = keccak.new(digest_bits=256).update(keccak.new(digest_bits=256).update(p["digest"]).digest()).digest()
  677. for i in range(blocks):
  678. # Which signatures will we be verifying in this block
  679. sigs = p["signatures"][(i * bsize):]
  680. if (len(sigs) > bsize):
  681. sigs = sigs[:bsize]
  682. # keys
  683. kset = b''
  684. # Grab the key associated the signature
  685. for q in range(int(len(sigs) / 66)):
  686. # Which guardian is this signature associated with
  687. g = sigs[q * 66]
  688. key = keys[((g * 20) + 1) : (((g + 1) * 20) + 1)]
  689. kset = kset + key
  690. txns.append(transaction.ApplicationCallTxn(
  691. sender=self.vaa_verify["hash"],
  692. index=self.coreid,
  693. on_complete=transaction.OnComplete.NoOpOC,
  694. app_args=[b"verifySigs", sigs, kset, digest],
  695. accounts=accts,
  696. sp=sp
  697. ))
  698. txns[-1].fee = 0
  699. txns.append(transaction.ApplicationCallTxn(
  700. sender=sender.getAddress(),
  701. index=self.coreid,
  702. on_complete=transaction.OnComplete.NoOpOC,
  703. app_args=[b"verifyVAA", vaa],
  704. accounts=accts,
  705. sp=sp
  706. ))
  707. txns[-1].fee = txns[-1].fee * (1 + blocks)
  708. if p["Meta"] == "CoreGovernance":
  709. txns.append(transaction.ApplicationCallTxn(
  710. sender=sender.getAddress(),
  711. index=self.coreid,
  712. on_complete=transaction.OnComplete.NoOpOC,
  713. app_args=[b"governance", vaa],
  714. accounts=accts,
  715. sp=sp
  716. ))
  717. txns.append(transaction.ApplicationCallTxn(
  718. sender=sender.getAddress(),
  719. index=self.coreid,
  720. on_complete=transaction.OnComplete.NoOpOC,
  721. app_args=[b"nop", 5],
  722. sp=sp
  723. ))
  724. if p["Meta"] == "TokenBridge RegisterChain" or p["Meta"] == "TokenBridge UpgradeContract":
  725. txns.append(transaction.ApplicationCallTxn(
  726. sender=sender.getAddress(),
  727. index=self.tokenid,
  728. on_complete=transaction.OnComplete.NoOpOC,
  729. app_args=[b"governance", vaa],
  730. accounts=accts,
  731. foreign_apps = [self.coreid],
  732. sp=sp
  733. ))
  734. if p["Meta"] == "TokenBridge Attest":
  735. # if we DO decode it, we can do a sanity check... of
  736. # course, the hacker might NOT decode it so we have to
  737. # handle both cases...
  738. asset = (self.decodeLocalState(client, sender, self.tokenid, chain_addr))
  739. foreign_assets = []
  740. if (len(asset) > 8):
  741. foreign_assets.append(int.from_bytes(asset[0:8], "big"))
  742. txns.append(
  743. transaction.PaymentTxn(
  744. sender = sender.getAddress(),
  745. sp = sp,
  746. receiver = chain_addr,
  747. amt = 100000
  748. )
  749. )
  750. txns.append(transaction.ApplicationCallTxn(
  751. sender=sender.getAddress(),
  752. index=self.tokenid,
  753. on_complete=transaction.OnComplete.NoOpOC,
  754. app_args=[b"nop", 1],
  755. sp=sp
  756. ))
  757. txns.append(transaction.ApplicationCallTxn(
  758. sender=sender.getAddress(),
  759. index=self.tokenid,
  760. on_complete=transaction.OnComplete.NoOpOC,
  761. app_args=[b"nop", 2],
  762. sp=sp
  763. ))
  764. txns.append(transaction.ApplicationCallTxn(
  765. sender=sender.getAddress(),
  766. index=self.tokenid,
  767. on_complete=transaction.OnComplete.NoOpOC,
  768. app_args=[b"receiveAttest", vaa],
  769. accounts=accts,
  770. foreign_assets = foreign_assets,
  771. sp=sp
  772. ))
  773. txns[-1].fee = txns[-1].fee * 2
  774. if p["Meta"] == "TokenBridge Transfer" or p["Meta"] == "TokenBridge Transfer With Payload":
  775. foreign_assets = []
  776. a = 0
  777. if p["FromChain"] != 8:
  778. asset = (self.decodeLocalState(client, sender, self.tokenid, chain_addr))
  779. if (len(asset) > 8):
  780. a = int.from_bytes(asset[0:8], "big")
  781. else:
  782. a = int.from_bytes(bytes.fromhex(p["Contract"]), "big")
  783. # The receiver needs to be optin in to receive the coins... Yeah, the relayer pays for this
  784. aid = 0
  785. if p["ToChain"] == 8 and p["Type"] == 3:
  786. aid = int.from_bytes(bytes.fromhex(p["ToAddress"]), "big")
  787. addr = get_application_address(aid)
  788. else:
  789. addr = encode_address(bytes.fromhex(p["ToAddress"]))
  790. if a != 0:
  791. foreign_assets.append(a)
  792. self.asset_optin(client, sender, foreign_assets[0], addr)
  793. # And this is how the relayer gets paid...
  794. if p["Fee"] != self.zeroPadBytes:
  795. self.asset_optin(client, sender, foreign_assets[0], sender.getAddress())
  796. accts.append(addr)
  797. txns.append(transaction.ApplicationCallTxn(
  798. sender=sender.getAddress(),
  799. index=self.tokenid,
  800. on_complete=transaction.OnComplete.NoOpOC,
  801. app_args=[b"completeTransfer", vaa],
  802. accounts=accts,
  803. foreign_assets = foreign_assets,
  804. sp=sp
  805. ))
  806. if aid != 0:
  807. txns[-1].foreign_apps = [aid]
  808. # We need to cover the inner transactions
  809. if p["Fee"] != self.zeroPadBytes:
  810. txns[-1].fee = txns[-1].fee * 3
  811. else:
  812. txns[-1].fee = txns[-1].fee * 2
  813. if p["Meta"] == "TokenBridge Transfer With Payload":
  814. m = abi.Method("portal_transfer", [abi.Argument("byte[]")], abi.Returns("byte[]"))
  815. txns.append(transaction.ApplicationCallTxn(
  816. sender=sender.getAddress(),
  817. index=int.from_bytes(bytes.fromhex(p["ToAddress"]), "big"),
  818. on_complete=transaction.OnComplete.NoOpOC,
  819. app_args=[m.get_selector(), m.args[0].type.encode(vaa)],
  820. foreign_assets = foreign_assets,
  821. sp=sp
  822. ))
  823. transaction.assign_group_id(txns)
  824. grp = []
  825. pk = sender.getPrivateKey()
  826. for t in txns:
  827. if ("app_args" in t.__dict__ and len(t.app_args) > 0 and t.app_args[0] == b"verifySigs"):
  828. grp.append(transaction.LogicSigTransaction(t, self.vaa_verify["lsig"]))
  829. else:
  830. grp.append(t.sign(pk))
  831. client.send_transactions(grp)
  832. ret = []
  833. for x in grp:
  834. response = self.waitForTransaction(client, x.get_txid())
  835. if "logs" in response.__dict__ and len(response.__dict__["logs"]) > 0:
  836. ret.append(response.__dict__["logs"])
  837. assert self.check_bits_set(client, appid, seq_addr, p["sequence"]) == True
  838. return ret
  839. def parseVAA(self, vaa):
  840. # print (vaa.hex())
  841. 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")}
  842. ret["signatures"] = vaa[6:(ret["siglen"] * 66) + 6]
  843. ret["sigs"] = []
  844. for i in range(ret["siglen"]):
  845. ret["sigs"].append(vaa[(6 + (i * 66)):(6 + (i * 66)) + 66].hex())
  846. off = (ret["siglen"] * 66) + 6
  847. ret["digest"] = vaa[off:] # This is what is actually signed...
  848. ret["timestamp"] = int.from_bytes(vaa[off:(off + 4)], "big")
  849. off += 4
  850. ret["nonce"] = int.from_bytes(vaa[off:(off + 4)], "big")
  851. off += 4
  852. ret["chainRaw"] = vaa[off:(off + 2)]
  853. ret["chain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  854. off += 2
  855. ret["emitter"] = vaa[off:(off + 32)]
  856. off += 32
  857. ret["sequence"] = int.from_bytes(vaa[off:(off + 8)], "big")
  858. off += 8
  859. ret["consistency"] = int.from_bytes(vaa[off:(off + 1)], "big")
  860. off += 1
  861. ret["Meta"] = "Unknown"
  862. if vaa[off:(off + 32)].hex() == "000000000000000000000000000000000000000000546f6b656e427269646765":
  863. ret["Meta"] = "TokenBridge"
  864. ret["module"] = vaa[off:(off + 32)].hex()
  865. off += 32
  866. ret["action"] = int.from_bytes(vaa[off:(off + 1)], "big")
  867. off += 1
  868. if ret["action"] == 1:
  869. ret["Meta"] = "TokenBridge RegisterChain"
  870. ret["targetChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  871. off += 2
  872. ret["EmitterChainID"] = int.from_bytes(vaa[off:(off + 2)], "big")
  873. off += 2
  874. ret["targetEmitter"] = vaa[off:(off + 32)].hex()
  875. off += 32
  876. if ret["action"] == 2:
  877. ret["Meta"] = "TokenBridge UpgradeContract"
  878. ret["targetChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  879. off += 2
  880. ret["newContract"] = vaa[off:(off + 32)].hex()
  881. off += 32
  882. pprint.pprint((vaa[off:(off + 32)].hex(), "00000000000000000000000000000000000000000000000000000000436f7265"))
  883. if vaa[off:(off + 32)].hex() == "00000000000000000000000000000000000000000000000000000000436f7265":
  884. ret["Meta"] = "CoreGovernance"
  885. ret["module"] = vaa[off:(off + 32)].hex()
  886. off += 32
  887. ret["action"] = int.from_bytes(vaa[off:(off + 1)], "big")
  888. off += 1
  889. ret["targetChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  890. off += 2
  891. if ret["action"] == 2:
  892. ret["NewGuardianSetIndex"] = int.from_bytes(vaa[off:(off + 4)], "big")
  893. else:
  894. ret["Contract"] = vaa[off:(off + 32)].hex()
  895. if ((len(vaa[off:])) == 100) and int.from_bytes((vaa[off:off+1]), "big") == 2:
  896. ret["Meta"] = "TokenBridge Attest"
  897. ret["Type"] = int.from_bytes((vaa[off:off+1]), "big")
  898. off += 1
  899. ret["Contract"] = vaa[off:(off + 32)].hex()
  900. off += 32
  901. ret["FromChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  902. off += 2
  903. ret["Decimals"] = int.from_bytes((vaa[off:off+1]), "big")
  904. off += 1
  905. ret["Symbol"] = vaa[off:(off + 32)].hex()
  906. off += 32
  907. ret["Name"] = vaa[off:(off + 32)].hex()
  908. if ((len(vaa[off:])) == 133) and int.from_bytes((vaa[off:off+1]), "big") == 1:
  909. ret["Meta"] = "TokenBridge Transfer"
  910. ret["Type"] = int.from_bytes((vaa[off:off+1]), "big")
  911. off += 1
  912. ret["Amount"] = vaa[off:(off + 32)].hex()
  913. off += 32
  914. ret["Contract"] = vaa[off:(off + 32)].hex()
  915. off += 32
  916. ret["FromChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  917. off += 2
  918. ret["ToAddress"] = vaa[off:(off + 32)].hex()
  919. off += 32
  920. ret["ToChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  921. off += 2
  922. ret["Fee"] = vaa[off:(off + 32)].hex()
  923. if int.from_bytes((vaa[off:off+1]), "big") == 3:
  924. ret["Meta"] = "TokenBridge Transfer With Payload"
  925. ret["Type"] = int.from_bytes((vaa[off:off+1]), "big")
  926. off += 1
  927. ret["Amount"] = vaa[off:(off + 32)].hex()
  928. off += 32
  929. ret["Contract"] = vaa[off:(off + 32)].hex()
  930. off += 32
  931. ret["FromChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  932. off += 2
  933. ret["ToAddress"] = vaa[off:(off + 32)].hex()
  934. off += 32
  935. ret["ToChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  936. off += 2
  937. ret["Fee"] = self.zeroPadBytes;
  938. ret["FromAddress"] = vaa[off:(off + 32)].hex()
  939. off += 32
  940. ret["Payload"] = vaa[off:].hex()
  941. return ret
  942. def boot(self):
  943. print("")
  944. print("Creating the PortalCore app")
  945. self.coreid = self.createPortalCoreApp(client=self.client, sender=self.foundation)
  946. pprint.pprint({"wormhole core": str(self.coreid), "address": get_application_address(self.coreid), "emitterAddress": decode_address(get_application_address(self.coreid)).hex()})
  947. print("Create the token bridge")
  948. self.tokenid = self.createTokenBridgeApp(self.client, self.foundation)
  949. pprint.pprint({"token bridge": str(self.tokenid), "address": get_application_address(self.tokenid), "emitterAddress": decode_address(get_application_address(self.tokenid)).hex()})
  950. if self.devnet or self.args.testnet:
  951. if self.devnet:
  952. print("Create test app")
  953. self.testid = self.createTestApp(self.client, self.foundation)
  954. pprint.pprint({"testapp": str(self.testid)})
  955. suggestedParams = self.client.suggested_params()
  956. fundingAccount = self.getGenesisAccounts()[0]
  957. txns: List[transaction.Transaction] = []
  958. 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"
  959. a = Account.FromMnemonic(wallet)
  960. txns.append(
  961. transaction.PaymentTxn(
  962. sender=fundingAccount.getAddress(),
  963. receiver=a.getAddress(),
  964. amt=self.FUNDING_AMOUNT,
  965. sp=suggestedParams,
  966. )
  967. )
  968. txns = transaction.assign_group_id(txns)
  969. signedTxns = [
  970. txn.sign(fundingAccount.getPrivateKey()) for i, txn in enumerate(txns)
  971. ]
  972. self.client.send_transactions(signedTxns)
  973. print("Sent some ALGO to: " + wallet)
  974. print("Creating a Token...")
  975. txn = transaction.AssetConfigTxn(
  976. sender=a.getAddress(),
  977. sp=suggestedParams,
  978. total=1000000,
  979. default_frozen=False,
  980. unit_name="NORIUM",
  981. asset_name="ChuckNorium",
  982. manager=a.getAddress(),
  983. reserve=a.getAddress(),
  984. freeze=a.getAddress(),
  985. clawback=a.getAddress(),
  986. strict_empty_address_check=False,
  987. decimals=6)
  988. stxn = txn.sign(a.getPrivateKey())
  989. txid = self.client.send_transaction(stxn)
  990. print("NORIUM creation transaction ID: {}".format(txid))
  991. confirmed_txn = transaction.wait_for_confirmation(self.client, txid, 4)
  992. print("TXID: ", txid)
  993. print("Result confirmed in round: {}".format(confirmed_txn['confirmed-round']))
  994. print("Creating an NFT...")
  995. # JSON file
  996. dir_path = os.path.dirname(os.path.realpath(__file__))
  997. f = open (dir_path + 'cnNftMetadata.json', "r")
  998. # Reading from file
  999. metadataJSON = json.loads(f.read())
  1000. metadataStr = json.dumps(metadataJSON)
  1001. hash = hashlib.new("sha512_256")
  1002. hash.update(b"arc0003/amj")
  1003. hash.update(metadataStr.encode("utf-8"))
  1004. json_metadata_hash = hash.digest()
  1005. print("json_metadata_hash: ", hash.hexdigest())
  1006. # Create transaction
  1007. txn = transaction.AssetConfigTxn(
  1008. sender=a.getAddress(),
  1009. sp=suggestedParams,
  1010. total=1,
  1011. default_frozen=False,
  1012. unit_name="CNART",
  1013. asset_name="ChuckNoriumArtwork@arc3",
  1014. manager=a.getAddress(),
  1015. reserve=a.getAddress(),
  1016. freeze=a.getAddress(),
  1017. clawback=a.getAddress(),
  1018. strict_empty_address_check=False,
  1019. url="file://cnNftMetadata.json",
  1020. metadata_hash=json_metadata_hash,
  1021. decimals=0)
  1022. stxn = txn.sign(a.getPrivateKey())
  1023. txid = self.client.send_transaction(stxn)
  1024. print("NORIUM NFT creation transaction ID: {}".format(txid))
  1025. confirmed_txn = transaction.wait_for_confirmation(self.client, txid, 4)
  1026. print("TXID: ", txid)
  1027. print("Result confirmed in round: {}".format(confirmed_txn['confirmed-round']))
  1028. if exists(self.args.env):
  1029. if self.gt == None:
  1030. self.gt = GenTest(False)
  1031. with open(self.args.env, encoding = 'utf-8') as f:
  1032. for line in f:
  1033. e = line.rstrip('\n').split("=")
  1034. print(e)
  1035. if "TOKEN_BRIDGE" in e[0]:
  1036. v = bytes.fromhex(e[1])
  1037. self.submitVAA(v, self.client, self.foundation, self.tokenid)
  1038. if "INIT_SIGNERS_CSV" in e[0]:
  1039. self.gt.guardianKeys = e[1].split(",")
  1040. print("guardianKeys: " + str(self.gt.guardianKeys))
  1041. if "INIT_SIGNERS_KEYS_CSV" in e[0]:
  1042. print("bootstrapping the guardian set...")
  1043. self.gt.guardianPrivKeys = e[1].split(",")
  1044. print("guardianPrivKeys: " + str(self.gt.guardianPrivKeys))
  1045. seq = int(random.random() * (2**31))
  1046. bootVAA = self.gt.genGuardianSetUpgrade(self.gt.guardianPrivKeys, self.args.guardianSet, self.args.guardianSet, seq, seq)
  1047. print("dev vaa: " + bootVAA)
  1048. self.bootGuardians(bytes.fromhex(bootVAA), self.client, self.foundation, self.coreid)
  1049. seq = int(random.random() * (2**31))
  1050. regChain = self.gt.genRegisterChain(self.gt.guardianPrivKeys, self.args.guardianSet, seq, seq, 8, decode_address(get_application_address(self.tokenid)).hex())
  1051. print("ALGO_TOKEN_BRIDGE_VAA=" + regChain)
  1052. # if self.args.env != ".env":
  1053. # v = bytes.fromhex(regChain)
  1054. # self.submitVAA(v, self.client, self.foundation, self.tokenid)
  1055. # print("We submitted it!")
  1056. def updateCore(self) -> None:
  1057. print("Updating the core contracts")
  1058. 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)
  1059. print("core " + decode_address(approval["hash"]).hex())
  1060. txn = transaction.ApplicationUpdateTxn(
  1061. index=self.coreid,
  1062. sender=self.foundation.getAddress(),
  1063. approval_program=b64decode(approval["result"]),
  1064. clear_program=b64decode(clear["result"]),
  1065. app_args=[ ],
  1066. sp=self.client.suggested_params(),
  1067. )
  1068. signedTxn = txn.sign(self.foundation.getPrivateKey())
  1069. print("sending transaction")
  1070. self.client.send_transaction(signedTxn)
  1071. resp = self.waitForTransaction(self.client, signedTxn.get_txid())
  1072. for x in resp.__dict__["logs"]:
  1073. print(x.hex())
  1074. print("complete")
  1075. def updateToken(self) -> None:
  1076. 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)
  1077. print("token " + decode_address(approval["hash"]).hex())
  1078. print("Updating the token contracts: " + str(len(b64decode(approval["result"]))))
  1079. txn = transaction.ApplicationUpdateTxn(
  1080. index=self.tokenid,
  1081. sender=self.foundation.getAddress(),
  1082. approval_program=b64decode(approval["result"]),
  1083. clear_program=b64decode(clear["result"]),
  1084. app_args=[ ],
  1085. sp=self.client.suggested_params(),
  1086. )
  1087. signedTxn = txn.sign(self.foundation.getPrivateKey())
  1088. print("sending transaction")
  1089. self.client.send_transaction(signedTxn)
  1090. resp = self.waitForTransaction(self.client, signedTxn.get_txid())
  1091. for x in resp.__dict__["logs"]:
  1092. print(x.hex())
  1093. print("complete")
  1094. def genTeal(self) -> None:
  1095. print((True, self.args.core_approve, self.args.core_clear, self.client, self.seed_amt, self.tsig, self.devnet or self.args.testnet))
  1096. approval, clear = getCoreContracts(True, 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)
  1097. print("Generating the teal for the core contracts")
  1098. approval, clear = get_token_bridge(True, 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)
  1099. print("Generating the teal for the token contracts: " + str(len(b64decode(approval["result"]))))
  1100. def testnet(self):
  1101. self.ALGOD_ADDRESS = self.args.algod_address = "https://testnet-api.algonode.cloud"
  1102. self.INDEXER_ADDRESS = "https://testnet-idx.algonode.cloud"
  1103. self.coreid = self.args.coreid
  1104. self.tokenid = self.args.tokenid
  1105. def mainnet(self):
  1106. self.ALGOD_ADDRESS = self.args.algod_address = "https://mainnet-api.algonode.cloud"
  1107. self.INDEXER_ADDRESS = "https://mainnet-idx.algonode.cloud"
  1108. self.coreid = self.args.coreid
  1109. self.tokenid = self.args.tokenid
  1110. def setup_args(self) -> None:
  1111. parser = argparse.ArgumentParser(description='algorand setup')
  1112. parser.add_argument('--algod_address', type=str, help='algod address (default: http://localhost:4001)',
  1113. default="http://localhost:4001")
  1114. parser.add_argument('--algod_token', type=str, help='algod access token',
  1115. default="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
  1116. parser.add_argument('--kmd_address', type=str, help='kmd wallet address (default: http://localhost:4002)',
  1117. default="http://localhost:4002")
  1118. parser.add_argument('--kmd_token', type=str, help='kmd wallet access token',
  1119. default="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
  1120. parser.add_argument('--kmd_name', type=str, help='kmd wallet name',
  1121. default="unencrypted-default-wallet")
  1122. parser.add_argument('--kmd_password', type=str, help='kmd wallet password', default="")
  1123. parser.add_argument('--mnemonic', type=str, help='account mnemonic', default="")
  1124. parser.add_argument('--core_approve', type=str, help='core approve teal', default="teal/core_approve.teal")
  1125. parser.add_argument('--core_clear', type=str, help='core clear teal', default="teal/core_clear.teal")
  1126. parser.add_argument('--token_approve', type=str, help='token approve teal', default="teal/token_approve.teal")
  1127. parser.add_argument('--token_clear', type=str, help='token clear teal', default="teal/token_clear.teal")
  1128. parser.add_argument('--coreid', type=int, help='core contract', default=4)
  1129. parser.add_argument('--tokenid', type=int, help='token bridge contract', default=6)
  1130. parser.add_argument('--devnet', action='store_true', help='setup devnet')
  1131. parser.add_argument('--boot', action='store_true', help='bootstrap')
  1132. parser.add_argument('--upgradePayload', action='store_true', help='gen the upgrade payload for the guardians to sign')
  1133. parser.add_argument('--vaa', type=str, help='Submit the supplied VAA', default="")
  1134. parser.add_argument('--env', type=str, help='deploying using the supplied .env file', default=".env")
  1135. parser.add_argument('--guardianSet', type=int, help='What guardianSet should I syntheticly create if needed', default=0)
  1136. parser.add_argument('--appid', type=str, help='The appid that the vaa submit is applied to', default="")
  1137. parser.add_argument('--submit', action='store_true', help='submit the synthetic vaas')
  1138. parser.add_argument('--updateCore', action='store_true', help='update the Core contracts')
  1139. parser.add_argument('--updateToken', action='store_true', help='update the Token contracts')
  1140. parser.add_argument('--upgradeVAA', action='store_true', help='generate a upgrade vaa for devnet')
  1141. parser.add_argument('--print', action='store_true', help='print')
  1142. parser.add_argument('--genParts', action='store_true', help='Get tssig parts')
  1143. parser.add_argument('--genTeal', action='store_true', help='Generate all the teal from the pyteal')
  1144. parser.add_argument('--fund', action='store_true', help='Generate some accounts and fund them')
  1145. parser.add_argument('--testnet', action='store_true', help='Connect to testnet')
  1146. parser.add_argument('--mainnet', action='store_true', help='Connect to mainnet')
  1147. parser.add_argument('--bootGuardian', type=str, help='Submit the supplied VAA', default="")
  1148. parser.add_argument('--rpc', type=str, help='RPC address', default="")
  1149. parser.add_argument('--guardianKeys', type=str, help='GuardianKeys', default="")
  1150. parser.add_argument('--guardianPrivKeys', type=str, help='guardianPrivKeys', default="")
  1151. args = parser.parse_args()
  1152. self.init(args)
  1153. self.devnet = args.devnet
  1154. def main(self) -> None:
  1155. self.setup_args()
  1156. args = self.args
  1157. if args.testnet:
  1158. self.testnet()
  1159. if args.mainnet:
  1160. self.mainnet()
  1161. if args.rpc != "":
  1162. self.ALGOD_ADDRESS = self.args.rpc
  1163. self.client = self.getAlgodClient()
  1164. if self.devnet or self.args.testnet:
  1165. self.vaa_verify = self.client.compile(get_vaa_verify())
  1166. else:
  1167. c = AlgodClient("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "https://testnet-api.algonode.cloud")
  1168. self.vaa_verify = c.compile(get_vaa_verify())
  1169. self.vaa_verify["lsig"] = LogicSig(base64.b64decode(self.vaa_verify["result"]))
  1170. if args.genTeal or args.boot:
  1171. self.genTeal()
  1172. # Generate the upgrade payload we need the guardians to sign
  1173. if args.upgradePayload:
  1174. print(self.genUpgradePayload())
  1175. sys.exit(0)
  1176. # This breaks the tsig up into the various parts so that we
  1177. # can embed it into the Typescript code for reassembly
  1178. if args.genParts:
  1179. print("this.ALGO_VERIFY_HASH = \"%s\""%self.vaa_verify["hash"]);
  1180. print("this.ALGO_VERIFY = new Uint8Array([", end='')
  1181. for x in b64decode(self.vaa_verify["result"]):
  1182. print("%d, "%(x), end='')
  1183. print("])")
  1184. parts = [
  1185. self.tsig.get_bytecode_raw(0).hex(),
  1186. self.tsig.get_bytecode_raw(1).hex(),
  1187. self.tsig.get_bytecode_raw(2).hex(),
  1188. self.tsig.get_bytecode_raw(3).hex(),
  1189. self.tsig.get_bytecode_raw(4).hex()
  1190. ]
  1191. pprint.pprint(parts)
  1192. sys.exit(0)
  1193. if args.mnemonic:
  1194. self.foundation = Account.FromMnemonic(args.mnemonic)
  1195. if args.devnet and self.foundation == None:
  1196. print("Generating the foundation account...")
  1197. self.foundation = self.getTemporaryAccount(self.client)
  1198. if self.args.fund:
  1199. sys.exit(0)
  1200. if self.foundation == None:
  1201. print("We dont have a account? Here is a random one I just made up...")
  1202. pk = account.generate_account()[0]
  1203. print(" pk: " + pk)
  1204. print(" address: " + account.address_from_private_key(pk))
  1205. print(" mnemonic: " + mnemonic.from_private_key(pk))
  1206. if args.testnet:
  1207. 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)")
  1208. sys.exit(0)
  1209. bal = self.getBalances(self.client, self.foundation.addr)
  1210. print("foundation address " + self.foundation.addr + " (" + str(float(bal[0]) / 1000000.0) + " ALGO)")
  1211. if bal[0] < 10000000:
  1212. print("you need at least 10 ALGO to do darn near anything...")
  1213. sys.exit(0)
  1214. if args.guardianKeys != "":
  1215. self.gt.guardianKeys = eval(args.guardianKeys)
  1216. if args.guardianPrivKeys != "":
  1217. self.gt.guardianPrivKeyss = eval(args.guardianPrivKeys)
  1218. if args.upgradeVAA:
  1219. ret = self.devnetUpgradeVAA()
  1220. pprint.pprint(ret)
  1221. if (args.submit) :
  1222. print("submitting vaa to upgrade core")
  1223. self.submitVAA(bytes.fromhex(ret[0]), self.client, self.foundation, self.coreid)
  1224. pprint.pprint(self.read_global_state(self.client, self.foundation.addr, self.coreid))
  1225. print("submitting vaa to upgrade token")
  1226. self.submitVAA(bytes.fromhex(ret[1]), self.client, self.foundation, self.tokenid)
  1227. pprint.pprint(self.read_global_state(self.client, self.foundation.addr, self.tokenid))
  1228. if args.boot:
  1229. self.boot()
  1230. if args.updateCore:
  1231. self.updateCore()
  1232. if args.updateToken:
  1233. self.updateToken()
  1234. if args.vaa:
  1235. if self.args.appid == "":
  1236. raise Exception("You need to specifiy the appid when you are submitting vaas")
  1237. vaa = bytes.fromhex(args.vaa)
  1238. pprint.pprint(self.parseVAA(vaa))
  1239. self.submitVAA(vaa, self.client, self.foundation, int(self.args.appid))
  1240. if args.bootGuardian != "":
  1241. vaa = bytes.fromhex(args.bootGuardian)
  1242. pprint.pprint(self.parseVAA(vaa))
  1243. response = self.bootGuardians(vaa, self.client, self.foundation, self.coreid)
  1244. pprint.pprint(response.__dict__)
  1245. if __name__ == "__main__":
  1246. core = PortalCore()
  1247. core.main()