admin.py 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626
  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 _fundFromGenesis(self, accountList, fundingAmt, client):
  163. genesisAccounts = self.getGenesisAccounts()
  164. suggestedParams = client.suggested_params()
  165. txns: List[transaction.Transaction] = []
  166. for i, a in enumerate(accountList):
  167. fundingAccount = genesisAccounts[i % len(genesisAccounts)]
  168. txns.append(
  169. transaction.PaymentTxn(
  170. sender=fundingAccount.getAddress(),
  171. receiver=a.getAddress(),
  172. amt=fundingAmt,
  173. sp=suggestedParams,
  174. )
  175. )
  176. txns = transaction.assign_group_id(txns)
  177. signedTxns = [
  178. txn.sign(genesisAccounts[i % len(genesisAccounts)].getPrivateKey())
  179. for i, txn in enumerate(txns)
  180. ]
  181. client.send_transactions(signedTxns)
  182. self.waitForTransaction(client, signedTxns[0].get_txid())
  183. def getTemporaryAccount(self, client: AlgodClient) -> Account:
  184. if len(self.accountList) == 0:
  185. sks = [account.generate_account()[0] for i in range(3)]
  186. self.accountList = [Account(sk) for sk in sks]
  187. self._fundFromGenesis(self.accountList, self.FUNDING_AMOUNT, client)
  188. return self.accountList.pop()
  189. def fundDevAccounts(self, client: AlgodClient):
  190. devAcctsMnemonics = [
  191. "provide warfare better filter glory civil help jacket alpha penalty van fiber code upgrade web more curve sauce merit bike satoshi blame orphan absorb modify",
  192. "album neglect very nasty input trick annual arctic spray task candy unfold letter drill glove sword flock omit dial rather session mesh slow abandon slab",
  193. "blue spring teach silent cheap grace desk crack agree leave tray lady chair reopen midnight lottery glove congress lounge arrow fine junior mirror above purchase",
  194. "front rifle urge write push dynamic oil vital section blast protect suffer shoulder base address teach sight trap trial august mechanic border leaf absorb attract",
  195. "fat pet option agree father glue range ancient curtain pottery search raven club save crane sting gift seven butter decline image toward kidney above balance"
  196. ]
  197. accountList = []
  198. accountFunding = 400000000000000 # 400M algos
  199. for mnemo in devAcctsMnemonics:
  200. acc = Account.FromMnemonic(mnemo)
  201. print('Funding dev account {} with {} uALGOs'.format(acc.addr, accountFunding))
  202. accountList.append(acc)
  203. self._fundFromGenesis(accountList, accountFunding, client)
  204. def getAlgodClient(self) -> AlgodClient:
  205. return AlgodClient(self.ALGOD_TOKEN, self.ALGOD_ADDRESS)
  206. def getBalances(self, client: AlgodClient, account: str) -> Dict[int, int]:
  207. balances: Dict[int, int] = dict()
  208. accountInfo = client.account_info(account)
  209. # set key 0 to Algo balance
  210. balances[0] = accountInfo["amount"]
  211. assets: List[Dict[str, Any]] = accountInfo.get("assets", [])
  212. for assetHolding in assets:
  213. assetID = assetHolding["asset-id"]
  214. amount = assetHolding["amount"]
  215. balances[assetID] = amount
  216. return balances
  217. def fullyCompileContract(self, client: AlgodClient, contract: Expr) -> bytes:
  218. teal = compileTeal(contract, mode=Mode.Application, version=6)
  219. response = client.compile(teal)
  220. return response
  221. # helper function that formats global state for printing
  222. def format_state(self, state):
  223. formatted = {}
  224. for item in state:
  225. key = item['key']
  226. value = item['value']
  227. formatted_key = base64.b64decode(key).decode('utf-8')
  228. if value['type'] == 1:
  229. # byte string
  230. if formatted_key == 'voted':
  231. formatted_value = base64.b64decode(value['bytes']).decode('utf-8')
  232. else:
  233. formatted_value = value['bytes']
  234. formatted[formatted_key] = formatted_value
  235. else:
  236. # integer
  237. formatted[formatted_key] = value['uint']
  238. return formatted
  239. # helper function to read app global state
  240. def read_global_state(self, client, addr, app_id):
  241. results = self.client.application_info(app_id)
  242. return self.format_state(results['params']['global-state'])
  243. def read_state(self, client, addr, app_id):
  244. results = client.account_info(addr)
  245. apps_created = results['created-apps']
  246. for app in apps_created:
  247. if app['id'] == app_id:
  248. return app
  249. return {}
  250. def encoder(self, type, val):
  251. if type == 'uint8':
  252. return encode_single(type, val).hex()[62:64]
  253. if type == 'uint16':
  254. return encode_single(type, val).hex()[60:64]
  255. if type == 'uint32':
  256. return encode_single(type, val).hex()[56:64]
  257. if type == 'uint64':
  258. return encode_single(type, val).hex()[64-(16):64]
  259. if type == 'uint128':
  260. return encode_single(type, val).hex()[64-(32):64]
  261. if type == 'uint256' or type == 'bytes32':
  262. return encode_single(type, val).hex()[64-(64):64]
  263. raise Exception("invalid type")
  264. def devnetUpgradeVAA(self):
  265. v = self.genUpgradePayload()
  266. print("core payload: " + str(v[0]))
  267. print("token payload: " + str(v[1]))
  268. if self.gt == None:
  269. self.gt = GenTest(False)
  270. emitter = bytes.fromhex(self.zeroPadBytes[0:(31*2)] + "04")
  271. guardianSet = self.getGovSet()
  272. print("guardianSet: " + str(guardianSet))
  273. nonce = int(random.random() * 20000)
  274. ret = [
  275. self.gt.createSignedVAA(guardianSet, self.gt.guardianPrivKeys, int(time.time()), nonce, 1, emitter, int(random.random() * 20000), 32, 8, v[0]),
  276. self.gt.createSignedVAA(guardianSet, self.gt.guardianPrivKeys, int(time.time()), nonce, 1, emitter, int(random.random() * 20000), 32, 8, v[1]),
  277. ]
  278. # pprint.pprint(self.parseVAA(bytes.fromhex(ret[0])))
  279. # pprint.pprint(self.parseVAA(bytes.fromhex(ret[1])))
  280. return ret
  281. def getMessageFee(self):
  282. s = self.client.application_info(self.coreid)["params"]["global-state"]
  283. k = base64.b64encode(b"MessageFee").decode('utf-8')
  284. for x in s:
  285. if x["key"] == k:
  286. return x["value"]["uint"]
  287. return -1
  288. def getGovSet(self):
  289. s = self.client.application_info(self.coreid)["params"]["global-state"]
  290. k = base64.b64encode(b"currentGuardianSetIndex").decode('utf-8')
  291. for x in s:
  292. if x["key"] == k:
  293. return x["value"]["uint"]
  294. return -1
  295. def genUpgradePayload(self):
  296. 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)
  297. 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)
  298. return self.genUpgradePayloadBody(approval1, approval2)
  299. def genUpgradePayloadBody(self, approval1, approval2):
  300. b = self.zeroPadBytes[0:(28*2)]
  301. b += self.encoder("uint8", ord("C"))
  302. b += self.encoder("uint8", ord("o"))
  303. b += self.encoder("uint8", ord("r"))
  304. b += self.encoder("uint8", ord("e"))
  305. b += self.encoder("uint8", 1)
  306. b += self.encoder("uint16", 8)
  307. b += decode_address(approval1["hash"]).hex()
  308. print("core hash: " + decode_address(approval1["hash"]).hex())
  309. ret = [b]
  310. b = self.zeroPadBytes[0:((32 -11)*2)]
  311. b += self.encoder("uint8", ord("T"))
  312. b += self.encoder("uint8", ord("o"))
  313. b += self.encoder("uint8", ord("k"))
  314. b += self.encoder("uint8", ord("e"))
  315. b += self.encoder("uint8", ord("n"))
  316. b += self.encoder("uint8", ord("B"))
  317. b += self.encoder("uint8", ord("r"))
  318. b += self.encoder("uint8", ord("i"))
  319. b += self.encoder("uint8", ord("d"))
  320. b += self.encoder("uint8", ord("g"))
  321. b += self.encoder("uint8", ord("e"))
  322. b += self.encoder("uint8", 2) # action
  323. b += self.encoder("uint16", 8) # target chain
  324. b += decode_address(approval2["hash"]).hex()
  325. print("token hash: " + decode_address(approval2["hash"]).hex())
  326. ret.append(b)
  327. return ret
  328. def createPortalCoreApp(
  329. self,
  330. client: AlgodClient,
  331. sender: Account,
  332. ) -> int:
  333. 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)
  334. globalSchema = transaction.StateSchema(num_uints=8, num_byte_slices=40)
  335. localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=16)
  336. app_args = [ ]
  337. txn = transaction.ApplicationCreateTxn(
  338. sender=sender.getAddress(),
  339. on_complete=transaction.OnComplete.NoOpOC,
  340. approval_program=b64decode(approval["result"]),
  341. clear_program=b64decode(clear["result"]),
  342. global_schema=globalSchema,
  343. local_schema=localSchema,
  344. extra_pages = 1,
  345. app_args=app_args,
  346. sp=client.suggested_params(),
  347. )
  348. signedTxn = txn.sign(sender.getPrivateKey())
  349. client.send_transaction(signedTxn)
  350. response = self.waitForTransaction(client, signedTxn.get_txid())
  351. assert response.applicationIndex is not None and response.applicationIndex > 0
  352. # Lets give it a bit of money so that it is not a "ghost" account
  353. txn = transaction.PaymentTxn(sender = sender.getAddress(), sp = client.suggested_params(), receiver = get_application_address(response.applicationIndex), amt = 100000)
  354. signedTxn = txn.sign(sender.getPrivateKey())
  355. client.send_transaction(signedTxn)
  356. return response.applicationIndex
  357. def createTokenBridgeApp(
  358. self,
  359. client: AlgodClient,
  360. sender: Account,
  361. ) -> int:
  362. 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)
  363. if len(b64decode(approval["result"])) > 4060:
  364. print("token bridge contract is too large... This might prevent updates later")
  365. globalSchema = transaction.StateSchema(num_uints=4, num_byte_slices=30)
  366. localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=16)
  367. app_args = [self.coreid, decode_address(get_application_address(self.coreid))]
  368. txn = transaction.ApplicationCreateTxn(
  369. sender=sender.getAddress(),
  370. on_complete=transaction.OnComplete.NoOpOC,
  371. approval_program=b64decode(approval["result"]),
  372. clear_program=b64decode(clear["result"]),
  373. global_schema=globalSchema,
  374. local_schema=localSchema,
  375. app_args=app_args,
  376. extra_pages = 2,
  377. sp=client.suggested_params(),
  378. )
  379. signedTxn = txn.sign(sender.getPrivateKey())
  380. client.send_transaction(signedTxn)
  381. response = self.waitForTransaction(client, signedTxn.get_txid())
  382. #pprint.pprint(response.__dict__)
  383. assert response.applicationIndex is not None and response.applicationIndex > 0
  384. # Lets give it a bit of money so that it is not a "ghost" account
  385. txn = transaction.PaymentTxn(sender = sender.getAddress(), sp = client.suggested_params(), receiver = get_application_address(response.applicationIndex), amt = 100000)
  386. signedTxn = txn.sign(sender.getPrivateKey())
  387. client.send_transaction(signedTxn)
  388. return response.applicationIndex
  389. def createTestApp(
  390. self,
  391. client: AlgodClient,
  392. sender: Account,
  393. ) -> int:
  394. approval, clear = get_test_app(client)
  395. globalSchema = transaction.StateSchema(num_uints=4, num_byte_slices=30)
  396. localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=16)
  397. txn = transaction.ApplicationCreateTxn(
  398. sender=sender.getAddress(),
  399. on_complete=transaction.OnComplete.NoOpOC,
  400. approval_program=b64decode(approval["result"]),
  401. clear_program=b64decode(clear["result"]),
  402. global_schema=globalSchema,
  403. local_schema=localSchema,
  404. sp=client.suggested_params(),
  405. )
  406. signedTxn = txn.sign(sender.getPrivateKey())
  407. client.send_transaction(signedTxn)
  408. response = self.waitForTransaction(client, signedTxn.get_txid())
  409. assert response.applicationIndex is not None and response.applicationIndex > 0
  410. # Lets give it a bit of money so that it is not a "ghost" account
  411. txn = transaction.PaymentTxn(sender = sender.getAddress(), sp = client.suggested_params(), receiver = get_application_address(response.applicationIndex), amt = 100000)
  412. signedTxn = txn.sign(sender.getPrivateKey())
  413. client.send_transaction(signedTxn)
  414. return response.applicationIndex
  415. def account_exists(self, client, app_id, addr):
  416. try:
  417. ai = client.account_info(addr)
  418. if "apps-local-state" not in ai:
  419. return False
  420. for app in ai["apps-local-state"]:
  421. if app["id"] == app_id:
  422. return True
  423. except:
  424. print("Failed to find account {}".format(addr))
  425. return False
  426. def optin(self, client, sender, app_id, idx, emitter, doCreate=True):
  427. aa = decode_address(get_application_address(app_id)).hex()
  428. lsa = self.tsig.populate(
  429. {
  430. "TMPL_APP_ID": app_id,
  431. "TMPL_APP_ADDRESS": aa,
  432. "TMPL_ADDR_IDX": idx,
  433. "TMPL_EMITTER_ID": emitter,
  434. }
  435. )
  436. sig_addr = lsa.address()
  437. if sig_addr not in self.cache and not self.account_exists(client, app_id, sig_addr):
  438. if doCreate:
  439. # pprint.pprint(("Creating", app_id, idx, emitter, sig_addr))
  440. # Create it
  441. sp = client.suggested_params()
  442. seed_txn = transaction.PaymentTxn(sender = sender.getAddress(),
  443. sp = sp,
  444. receiver = sig_addr,
  445. amt = self.seed_amt)
  446. seed_txn.fee = seed_txn.fee * 2
  447. optin_txn = transaction.ApplicationOptInTxn(sig_addr, sp, app_id, rekey_to=get_application_address(app_id))
  448. optin_txn.fee = 0
  449. transaction.assign_group_id([seed_txn, optin_txn])
  450. signed_seed = seed_txn.sign(sender.getPrivateKey())
  451. signed_optin = transaction.LogicSigTransaction(optin_txn, lsa)
  452. client.send_transactions([signed_seed, signed_optin])
  453. self.waitForTransaction(client, signed_optin.get_txid())
  454. self.cache[sig_addr] = True
  455. return sig_addr
  456. def parseSeqFromLog(self, txn):
  457. return int.from_bytes(b64decode(txn.innerTxns[0]["logs"][0]), "big")
  458. def getCreator(self, client, sender, asset_id):
  459. return client.asset_info(asset_id)["params"]["creator"]
  460. def sendTxn(self, client, sender, txns, doWait):
  461. transaction.assign_group_id(txns)
  462. grp = []
  463. pk = sender.getPrivateKey()
  464. for t in txns:
  465. grp.append(t.sign(pk))
  466. client.send_transactions(grp)
  467. if doWait:
  468. return self.waitForTransaction(client, grp[-1].get_txid())
  469. else:
  470. return grp[-1].get_txid()
  471. def bootGuardians(self, vaa, client, sender, coreid):
  472. p = self.parseVAA(vaa)
  473. if "NewGuardianSetIndex" not in p:
  474. raise Exception("invalid guardian VAA")
  475. seq_addr = self.optin(client, sender, coreid, int(p["sequence"] / max_bits), p["chainRaw"].hex() + p["emitter"].hex())
  476. guardian_addr = self.optin(client, sender, coreid, p["index"], b"guardian".hex())
  477. newguardian_addr = self.optin(client, sender, coreid, p["NewGuardianSetIndex"], b"guardian".hex())
  478. # wormhole is not a cheap protocol... we need to buy ourselves
  479. # some extra CPU cycles by having an early txn do nothing.
  480. # This leaves cycles over for later txn's in the same group
  481. sp = client.suggested_params()
  482. txns = [
  483. transaction.ApplicationCallTxn(
  484. sender=sender.getAddress(),
  485. index=coreid,
  486. on_complete=transaction.OnComplete.NoOpOC,
  487. app_args=[b"nop", b"0"],
  488. sp=sp
  489. ),
  490. transaction.ApplicationCallTxn(
  491. sender=sender.getAddress(),
  492. index=coreid,
  493. on_complete=transaction.OnComplete.NoOpOC,
  494. app_args=[b"nop", b"1"],
  495. sp=sp
  496. ),
  497. transaction.ApplicationCallTxn(
  498. sender=sender.getAddress(),
  499. index=coreid,
  500. on_complete=transaction.OnComplete.NoOpOC,
  501. app_args=[b"init", vaa, decode_address(self.vaa_verify["hash"])],
  502. accounts=[seq_addr, guardian_addr, newguardian_addr],
  503. sp=sp
  504. ),
  505. transaction.PaymentTxn(
  506. sender=sender.getAddress(),
  507. receiver=self.vaa_verify["hash"],
  508. amt=100000,
  509. sp=sp
  510. )
  511. ]
  512. return self.sendTxn(client, sender, txns, True)
  513. def decodeLocalState(self, client, sender, appid, addr):
  514. app_state = None
  515. ai = client.account_info(addr)
  516. for app in ai["apps-local-state"]:
  517. if app["id"] == appid:
  518. app_state = app["key-value"]
  519. ret = b''
  520. if None != app_state:
  521. vals = {}
  522. e = bytes.fromhex("00"*127)
  523. for kv in app_state:
  524. k = base64.b64decode(kv["key"])
  525. if k == "meta":
  526. continue
  527. key = int.from_bytes(k, "big")
  528. v = base64.b64decode(kv["value"]["bytes"])
  529. if v != e:
  530. vals[key] = v
  531. for k in sorted(vals.keys()):
  532. ret = ret + vals[k]
  533. return ret
  534. # There is no client side duplicate suppression, error checking, or validity
  535. # checking. We need to be able to detect all failure cases in
  536. # the contract itself and we want to use this to drive the failure test
  537. # cases
  538. def simpleVAA(self, vaa, client, sender, appid):
  539. 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")}
  540. ret["signatures"] = vaa[6:(ret["siglen"] * 66) + 6]
  541. ret["sigs"] = []
  542. for i in range(ret["siglen"]):
  543. ret["sigs"].append(vaa[(6 + (i * 66)):(6 + (i * 66)) + 66].hex())
  544. off = (ret["siglen"] * 66) + 6
  545. ret["digest"] = vaa[off:] # This is what is actually signed...
  546. ret["timestamp"] = int.from_bytes(vaa[off:(off + 4)], "big")
  547. off += 4
  548. ret["nonce"] = int.from_bytes(vaa[off:(off + 4)], "big")
  549. off += 4
  550. ret["chainRaw"] = vaa[off:(off + 2)]
  551. ret["chain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  552. off += 2
  553. ret["emitter"] = vaa[off:(off + 32)]
  554. off += 32
  555. ret["sequence"] = int.from_bytes(vaa[off:(off + 8)], "big")
  556. off += 8
  557. ret["consistency"] = int.from_bytes(vaa[off:(off + 1)], "big")
  558. off += 1
  559. seq_addr = self.optin(client, sender, appid, int(p["sequence"] / max_bits), p["chainRaw"].hex() + p["emitter"].hex())
  560. # And then the signatures to help us verify the vaa_s
  561. guardian_addr = self.optin(client, sender, self.coreid, p["index"], b"guardian".hex())
  562. accts = [seq_addr, guardian_addr]
  563. keys = self.decodeLocalState(client, sender, self.coreid, guardian_addr)
  564. sp = client.suggested_params()
  565. txns = []
  566. # Right now there is not really a good way to estimate the fees,
  567. # in production, on a conjested network, how much verifying
  568. # the signatures is going to cost.
  569. # So, what we do instead
  570. # is we top off the verifier back up to 2A so effectively we
  571. # are paying for the previous persons overrage which on a
  572. # unconjested network should be zero
  573. pmt = 3000
  574. bal = self.getBalances(client, self.vaa_verify["hash"])
  575. if ((200000 - bal[0]) >= pmt):
  576. pmt = 200000 - bal[0]
  577. #print("Sending %d algo to cover fees" % (pmt))
  578. txns.append(
  579. transaction.PaymentTxn(
  580. sender = sender.getAddress(),
  581. sp = sp,
  582. receiver = self.vaa_verify["hash"],
  583. amt = pmt
  584. )
  585. )
  586. # How many signatures can we process in a single txn... we can do 9!
  587. bsize = (9*66)
  588. blocks = int(len(p["signatures"]) / bsize) + 1
  589. # We don't pass the entire payload in but instead just pass it pre digested. This gets around size
  590. # limitations with lsigs AND reduces the cost of the entire operation on a conjested network by reducing the
  591. # bytes passed into the transaction
  592. digest = keccak.new(digest_bits=256).update(keccak.new(digest_bits=256).update(p["digest"]).digest()).digest()
  593. for i in range(blocks):
  594. # Which signatures will we be verifying in this block
  595. sigs = p["signatures"][(i * bsize):]
  596. if (len(sigs) > bsize):
  597. sigs = sigs[:bsize]
  598. # keys
  599. kset = b''
  600. # Grab the key associated the signature
  601. for q in range(int(len(sigs) / 66)):
  602. # Which guardian is this signature associated with
  603. g = sigs[q * 66]
  604. key = keys[((g * 20) + 1) : (((g + 1) * 20) + 1)]
  605. kset = kset + key
  606. txns.append(transaction.ApplicationCallTxn(
  607. sender=self.vaa_verify["hash"],
  608. index=self.coreid,
  609. on_complete=transaction.OnComplete.NoOpOC,
  610. app_args=[b"verifySigs", sigs, kset, digest],
  611. accounts=accts,
  612. sp=sp
  613. ))
  614. txns.append(transaction.ApplicationCallTxn(
  615. sender=sender.getAddress(),
  616. index=self.coreid,
  617. on_complete=transaction.OnComplete.NoOpOC,
  618. app_args=[b"verifyVAA", vaa],
  619. accounts=accts,
  620. sp=sp
  621. ))
  622. return txns
  623. def signVAA(self, client, sender, txns):
  624. transaction.assign_group_id(txns)
  625. grp = []
  626. pk = sender.getPrivateKey()
  627. for t in txns:
  628. if ("app_args" in t.__dict__ and len(t.app_args) > 0 and t.app_args[0] == b"verifySigs"):
  629. grp.append(transaction.LogicSigTransaction(t, self.vaa_verify["lsig"]))
  630. else:
  631. grp.append(t.sign(pk))
  632. client.send_transactions(grp)
  633. ret = []
  634. for x in grp:
  635. response = self.waitForTransaction(client, x.get_txid())
  636. if "logs" in response.__dict__ and len(response.__dict__["logs"]) > 0:
  637. ret.append(response.__dict__["logs"])
  638. return ret
  639. def check_bits_set(self, client, app_id, addr, seq):
  640. bits_set = {}
  641. app_state = None
  642. ai = client.account_info(addr)
  643. for app in ai["apps-local-state"]:
  644. if app["id"] == app_id:
  645. app_state = app["key-value"]
  646. if app_state == None:
  647. return False
  648. start = int(seq / max_bits) * max_bits
  649. s = int((seq - start) / bits_per_key)
  650. b = int(((seq - start) - (s * bits_per_key)) / 8)
  651. k = base64.b64encode(s.to_bytes(1, "big")).decode('utf-8')
  652. for kv in app_state:
  653. if kv["key"] != k:
  654. continue
  655. v = base64.b64decode(kv["value"]["bytes"])
  656. bt = 1 << (seq%8)
  657. return ((v[b] & bt) != 0)
  658. return False
  659. def submitVAA(self, vaa, client, sender, appid):
  660. # A lot of our logic here depends on parseVAA and knowing what the payload is..
  661. p = self.parseVAA(vaa)
  662. #pprint.pprint(p)
  663. seq_addr = self.optin(client, sender, appid, int(p["sequence"] / max_bits), p["chainRaw"].hex() + p["emitter"].hex())
  664. # assert self.check_bits_set(client, appid, seq_addr, p["sequence"]) == False
  665. # And then the signatures to help us verify the vaa_s
  666. guardian_addr = self.optin(client, sender, self.coreid, p["index"], b"guardian".hex())
  667. accts = [seq_addr, guardian_addr]
  668. # If this happens to be setting up a new guardian set, we probably need it as well...
  669. if p["Meta"] == "CoreGovernance" and p["action"] == 2:
  670. newguardian_addr = self.optin(client, sender, self.coreid, p["NewGuardianSetIndex"], b"guardian".hex())
  671. accts.append(newguardian_addr)
  672. # When we attest for a new token, we need some place to store the info... later we will need to
  673. # mirror the other way as well
  674. if p["Meta"] == "TokenBridge Attest" or p["Meta"] == "TokenBridge Transfer" or p["Meta"] == "TokenBridge Transfer With Payload":
  675. if p["FromChain"] != 8:
  676. chain_addr = self.optin(client, sender, self.tokenid, p["FromChain"], p["Contract"])
  677. else:
  678. asset_id = int.from_bytes(bytes.fromhex(p["Contract"]), "big")
  679. chain_addr = self.optin(client, sender, self.tokenid, asset_id, b"native".hex())
  680. accts.append(chain_addr)
  681. keys = self.decodeLocalState(client, sender, self.coreid, guardian_addr)
  682. print("keys: " + keys.hex())
  683. sp = client.suggested_params()
  684. txns = []
  685. # How many signatures can we process in a single txn... we can do 9!
  686. bsize = (9*66)
  687. # audit: this was incorrectly adding an extra, empty block when the amount
  688. # of signatures was a multiple of 9. fixed.
  689. blocks = int(len(p["signatures"]) / bsize) + int(vaa[5] % 9 != 0)
  690. # We don't pass the entire payload in but instead just pass it pre digested. This gets around size
  691. # limitations with lsigs AND reduces the cost of the entire operation on a conjested network by reducing the
  692. # bytes passed into the transaction
  693. digest = keccak.new(digest_bits=256).update(keccak.new(digest_bits=256).update(p["digest"]).digest()).digest()
  694. for i in range(blocks):
  695. # Which signatures will we be verifying in this block
  696. sigs = p["signatures"][(i * bsize):]
  697. if (len(sigs) > bsize):
  698. sigs = sigs[:bsize]
  699. # keys
  700. kset = b''
  701. # Grab the key associated the signature
  702. for q in range(int(len(sigs) / 66)):
  703. # Which guardian is this signature associated with
  704. g = sigs[q * 66]
  705. key = keys[((g * 20) + 1) : (((g + 1) * 20) + 1)]
  706. kset = kset + key
  707. txns.append(transaction.ApplicationCallTxn(
  708. sender=self.vaa_verify["hash"],
  709. index=self.coreid,
  710. on_complete=transaction.OnComplete.NoOpOC,
  711. app_args=[b"verifySigs", sigs, kset, digest],
  712. accounts=accts,
  713. sp=sp
  714. ))
  715. txns[-1].fee = 0
  716. txns.append(transaction.ApplicationCallTxn(
  717. sender=sender.getAddress(),
  718. index=self.coreid,
  719. on_complete=transaction.OnComplete.NoOpOC,
  720. app_args=[b"verifyVAA", vaa],
  721. accounts=accts,
  722. sp=sp
  723. ))
  724. txns[-1].fee = txns[-1].fee * (1 + blocks)
  725. if p["Meta"] == "CoreGovernance":
  726. txns.append(transaction.ApplicationCallTxn(
  727. sender=sender.getAddress(),
  728. index=self.coreid,
  729. on_complete=transaction.OnComplete.NoOpOC,
  730. app_args=[b"governance", vaa],
  731. accounts=accts,
  732. sp=sp
  733. ))
  734. txns.append(transaction.ApplicationCallTxn(
  735. sender=sender.getAddress(),
  736. index=self.coreid,
  737. on_complete=transaction.OnComplete.NoOpOC,
  738. app_args=[b"nop", 5],
  739. sp=sp
  740. ))
  741. if p["Meta"] == "TokenBridge RegisterChain" or p["Meta"] == "TokenBridge UpgradeContract":
  742. txns.append(transaction.ApplicationCallTxn(
  743. sender=sender.getAddress(),
  744. index=self.tokenid,
  745. on_complete=transaction.OnComplete.NoOpOC,
  746. app_args=[b"governance", vaa],
  747. accounts=accts,
  748. foreign_apps = [self.coreid],
  749. sp=sp
  750. ))
  751. if p["Meta"] == "TokenBridge Attest":
  752. # if we DO decode it, we can do a sanity check... of
  753. # course, the hacker might NOT decode it so we have to
  754. # handle both cases...
  755. asset = (self.decodeLocalState(client, sender, self.tokenid, chain_addr))
  756. foreign_assets = []
  757. if (len(asset) > 8):
  758. foreign_assets.append(int.from_bytes(asset[0:8], "big"))
  759. txns.append(
  760. transaction.PaymentTxn(
  761. sender = sender.getAddress(),
  762. sp = sp,
  763. receiver = chain_addr,
  764. amt = 100000
  765. )
  766. )
  767. txns.append(transaction.ApplicationCallTxn(
  768. sender=sender.getAddress(),
  769. index=self.tokenid,
  770. on_complete=transaction.OnComplete.NoOpOC,
  771. app_args=[b"nop", 1],
  772. sp=sp
  773. ))
  774. txns.append(transaction.ApplicationCallTxn(
  775. sender=sender.getAddress(),
  776. index=self.tokenid,
  777. on_complete=transaction.OnComplete.NoOpOC,
  778. app_args=[b"nop", 2],
  779. sp=sp
  780. ))
  781. txns.append(transaction.ApplicationCallTxn(
  782. sender=sender.getAddress(),
  783. index=self.tokenid,
  784. on_complete=transaction.OnComplete.NoOpOC,
  785. app_args=[b"receiveAttest", vaa],
  786. accounts=accts,
  787. foreign_assets = foreign_assets,
  788. sp=sp
  789. ))
  790. txns[-1].fee = txns[-1].fee * 2
  791. if p["Meta"] == "TokenBridge Transfer" or p["Meta"] == "TokenBridge Transfer With Payload":
  792. foreign_assets = []
  793. a = 0
  794. if p["FromChain"] != 8:
  795. asset = (self.decodeLocalState(client, sender, self.tokenid, chain_addr))
  796. if (len(asset) > 8):
  797. a = int.from_bytes(asset[0:8], "big")
  798. else:
  799. a = int.from_bytes(bytes.fromhex(p["Contract"]), "big")
  800. # The receiver needs to be optin in to receive the coins... Yeah, the relayer pays for this
  801. aid = 0
  802. if p["ToChain"] == 8 and p["Type"] == 3:
  803. aid = int.from_bytes(bytes.fromhex(p["ToAddress"]), "big")
  804. addr = get_application_address(aid)
  805. else:
  806. addr = encode_address(bytes.fromhex(p["ToAddress"]))
  807. if a != 0:
  808. foreign_assets.append(a)
  809. self.asset_optin(client, sender, foreign_assets[0], addr)
  810. # And this is how the relayer gets paid...
  811. if p["Fee"] != self.zeroPadBytes:
  812. self.asset_optin(client, sender, foreign_assets[0], sender.getAddress())
  813. accts.append(addr)
  814. txns.append(transaction.ApplicationCallTxn(
  815. sender=sender.getAddress(),
  816. index=self.tokenid,
  817. on_complete=transaction.OnComplete.NoOpOC,
  818. app_args=[b"completeTransfer", vaa],
  819. accounts=accts,
  820. foreign_assets = foreign_assets,
  821. sp=sp
  822. ))
  823. if aid != 0:
  824. txns[-1].foreign_apps = [aid]
  825. # We need to cover the inner transactions
  826. if p["Fee"] != self.zeroPadBytes:
  827. txns[-1].fee = txns[-1].fee * 3
  828. else:
  829. txns[-1].fee = txns[-1].fee * 2
  830. if p["Meta"] == "TokenBridge Transfer With Payload":
  831. m = abi.Method("portal_transfer", [abi.Argument("byte[]")], abi.Returns("byte[]"))
  832. txns.append(transaction.ApplicationCallTxn(
  833. sender=sender.getAddress(),
  834. index=int.from_bytes(bytes.fromhex(p["ToAddress"])[24:], "big"),
  835. on_complete=transaction.OnComplete.NoOpOC,
  836. app_args=[m.get_selector(), m.args[0].type.encode(vaa)],
  837. foreign_assets = foreign_assets,
  838. sp=sp
  839. ))
  840. transaction.assign_group_id(txns)
  841. grp = []
  842. pk = sender.getPrivateKey()
  843. for t in txns:
  844. if ("app_args" in t.__dict__ and len(t.app_args) > 0 and t.app_args[0] == b"verifySigs"):
  845. grp.append(transaction.LogicSigTransaction(t, self.vaa_verify["lsig"]))
  846. else:
  847. grp.append(t.sign(pk))
  848. client.send_transactions(grp)
  849. ret = []
  850. for x in grp:
  851. response = self.waitForTransaction(client, x.get_txid())
  852. if "logs" in response.__dict__ and len(response.__dict__["logs"]) > 0:
  853. ret.append(response.__dict__["logs"])
  854. # assert self.check_bits_set(client, appid, seq_addr, p["sequence"]) == True
  855. return ret
  856. def parseVAA(self, vaa):
  857. # print (vaa.hex())
  858. 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")}
  859. ret["signatures"] = vaa[6:(ret["siglen"] * 66) + 6]
  860. ret["sigs"] = []
  861. for i in range(ret["siglen"]):
  862. ret["sigs"].append(vaa[(6 + (i * 66)):(6 + (i * 66)) + 66].hex())
  863. off = (ret["siglen"] * 66) + 6
  864. ret["digest"] = vaa[off:] # This is what is actually signed...
  865. ret["timestamp"] = int.from_bytes(vaa[off:(off + 4)], "big")
  866. off += 4
  867. ret["nonce"] = int.from_bytes(vaa[off:(off + 4)], "big")
  868. off += 4
  869. ret["chainRaw"] = vaa[off:(off + 2)]
  870. ret["chain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  871. off += 2
  872. ret["emitter"] = vaa[off:(off + 32)]
  873. off += 32
  874. ret["sequence"] = int.from_bytes(vaa[off:(off + 8)], "big")
  875. off += 8
  876. ret["consistency"] = int.from_bytes(vaa[off:(off + 1)], "big")
  877. off += 1
  878. ret["Meta"] = "Unknown"
  879. if vaa[off:(off + 32)].hex() == "000000000000000000000000000000000000000000546f6b656e427269646765":
  880. ret["Meta"] = "TokenBridge"
  881. ret["module"] = vaa[off:(off + 32)].hex()
  882. off += 32
  883. ret["action"] = int.from_bytes(vaa[off:(off + 1)], "big")
  884. off += 1
  885. if ret["action"] == 1:
  886. ret["Meta"] = "TokenBridge RegisterChain"
  887. ret["targetChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  888. off += 2
  889. ret["EmitterChainID"] = int.from_bytes(vaa[off:(off + 2)], "big")
  890. off += 2
  891. ret["targetEmitter"] = vaa[off:(off + 32)].hex()
  892. off += 32
  893. if ret["action"] == 2:
  894. ret["Meta"] = "TokenBridge UpgradeContract"
  895. ret["targetChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  896. off += 2
  897. ret["newContract"] = vaa[off:(off + 32)].hex()
  898. off += 32
  899. pprint.pprint((vaa[off:(off + 32)].hex(), "00000000000000000000000000000000000000000000000000000000436f7265"))
  900. if vaa[off:(off + 32)].hex() == "00000000000000000000000000000000000000000000000000000000436f7265":
  901. ret["Meta"] = "CoreGovernance"
  902. ret["module"] = vaa[off:(off + 32)].hex()
  903. off += 32
  904. ret["action"] = int.from_bytes(vaa[off:(off + 1)], "big")
  905. off += 1
  906. ret["targetChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  907. off += 2
  908. if ret["action"] == 2:
  909. ret["NewGuardianSetIndex"] = int.from_bytes(vaa[off:(off + 4)], "big")
  910. else:
  911. ret["Contract"] = vaa[off:(off + 32)].hex()
  912. if ((len(vaa[off:])) == 100) and int.from_bytes((vaa[off:off+1]), "big") == 2:
  913. ret["Meta"] = "TokenBridge Attest"
  914. ret["Type"] = int.from_bytes((vaa[off:off+1]), "big")
  915. off += 1
  916. ret["Contract"] = vaa[off:(off + 32)].hex()
  917. off += 32
  918. ret["FromChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  919. off += 2
  920. ret["Decimals"] = int.from_bytes((vaa[off:off+1]), "big")
  921. off += 1
  922. ret["Symbol"] = vaa[off:(off + 32)].hex()
  923. off += 32
  924. ret["Name"] = vaa[off:(off + 32)].hex()
  925. if ((len(vaa[off:])) == 133) and int.from_bytes((vaa[off:off+1]), "big") == 1:
  926. ret["Meta"] = "TokenBridge Transfer"
  927. ret["Type"] = int.from_bytes((vaa[off:off+1]), "big")
  928. off += 1
  929. ret["Amount"] = vaa[off:(off + 32)].hex()
  930. off += 32
  931. ret["Contract"] = vaa[off:(off + 32)].hex()
  932. off += 32
  933. ret["FromChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  934. off += 2
  935. ret["ToAddress"] = vaa[off:(off + 32)].hex()
  936. off += 32
  937. ret["ToChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  938. off += 2
  939. ret["Fee"] = vaa[off:(off + 32)].hex()
  940. if int.from_bytes((vaa[off:off+1]), "big") == 3:
  941. ret["Meta"] = "TokenBridge Transfer With Payload"
  942. ret["Type"] = int.from_bytes((vaa[off:off+1]), "big")
  943. off += 1
  944. ret["Amount"] = vaa[off:(off + 32)].hex()
  945. off += 32
  946. ret["Contract"] = vaa[off:(off + 32)].hex()
  947. off += 32
  948. ret["FromChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  949. off += 2
  950. ret["ToAddress"] = vaa[off:(off + 32)].hex()
  951. off += 32
  952. ret["ToChain"] = int.from_bytes(vaa[off:(off + 2)], "big")
  953. off += 2
  954. ret["Fee"] = self.zeroPadBytes;
  955. ret["FromAddress"] = vaa[off:(off + 32)].hex()
  956. off += 32
  957. ret["Payload"] = vaa[off:].hex()
  958. return ret
  959. def boot(self):
  960. print("")
  961. print("Creating the PortalCore app")
  962. self.coreid = self.createPortalCoreApp(client=self.client, sender=self.foundation)
  963. pprint.pprint({"wormhole core": str(self.coreid), "address": get_application_address(self.coreid), "emitterAddress": decode_address(get_application_address(self.coreid)).hex()})
  964. print("Create the token bridge")
  965. self.tokenid = self.createTokenBridgeApp(self.client, self.foundation)
  966. pprint.pprint({"token bridge": str(self.tokenid), "address": get_application_address(self.tokenid), "emitterAddress": decode_address(get_application_address(self.tokenid)).hex()})
  967. if self.devnet or self.args.testnet:
  968. if self.devnet:
  969. print("Create test app")
  970. self.testid = self.createTestApp(self.client, self.foundation)
  971. pprint.pprint({"testapp": str(self.testid)})
  972. suggestedParams = self.client.suggested_params()
  973. fundingAccount = self.getGenesisAccounts()[0]
  974. txns: List[transaction.Transaction] = []
  975. 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"
  976. a = Account.FromMnemonic(wallet)
  977. txns.append(
  978. transaction.PaymentTxn(
  979. sender=fundingAccount.getAddress(),
  980. receiver=a.getAddress(),
  981. amt=self.FUNDING_AMOUNT,
  982. sp=suggestedParams,
  983. )
  984. )
  985. txns = transaction.assign_group_id(txns)
  986. signedTxns = [
  987. txn.sign(fundingAccount.getPrivateKey()) for i, txn in enumerate(txns)
  988. ]
  989. self.client.send_transactions(signedTxns)
  990. print("Sent some ALGO to: " + wallet)
  991. print("Creating a Token...")
  992. txn = transaction.AssetConfigTxn(
  993. sender=a.getAddress(),
  994. sp=suggestedParams,
  995. total=1000000,
  996. default_frozen=False,
  997. unit_name="NORIUM",
  998. asset_name="ChuckNorium",
  999. manager=a.getAddress(),
  1000. reserve=a.getAddress(),
  1001. freeze=a.getAddress(),
  1002. clawback=a.getAddress(),
  1003. strict_empty_address_check=False,
  1004. decimals=6)
  1005. stxn = txn.sign(a.getPrivateKey())
  1006. txid = self.client.send_transaction(stxn)
  1007. print("NORIUM creation transaction ID: {}".format(txid))
  1008. confirmed_txn = transaction.wait_for_confirmation(self.client, txid, 4)
  1009. print("TXID: ", txid)
  1010. print("Result confirmed in round: {}".format(confirmed_txn['confirmed-round']))
  1011. print("Creating an NFT...")
  1012. # JSON file
  1013. dir_path = os.path.dirname(os.path.realpath(__file__))
  1014. f = open (dir_path + 'cnNftMetadata.json', "r")
  1015. # Reading from file
  1016. metadataJSON = json.loads(f.read())
  1017. metadataStr = json.dumps(metadataJSON)
  1018. hash = hashlib.new("sha512_256")
  1019. hash.update(b"arc0003/amj")
  1020. hash.update(metadataStr.encode("utf-8"))
  1021. json_metadata_hash = hash.digest()
  1022. print("json_metadata_hash: ", hash.hexdigest())
  1023. # Create transaction
  1024. txn = transaction.AssetConfigTxn(
  1025. sender=a.getAddress(),
  1026. sp=suggestedParams,
  1027. total=1,
  1028. default_frozen=False,
  1029. unit_name="CNART",
  1030. asset_name="ChuckNoriumArtwork@arc3",
  1031. manager=a.getAddress(),
  1032. reserve=a.getAddress(),
  1033. freeze=a.getAddress(),
  1034. clawback=a.getAddress(),
  1035. strict_empty_address_check=False,
  1036. url="file://cnNftMetadata.json",
  1037. metadata_hash=json_metadata_hash,
  1038. decimals=0)
  1039. stxn = txn.sign(a.getPrivateKey())
  1040. txid = self.client.send_transaction(stxn)
  1041. print("NORIUM NFT creation transaction ID: {}".format(txid))
  1042. confirmed_txn = transaction.wait_for_confirmation(self.client, txid, 4)
  1043. print("TXID: ", txid)
  1044. print("Result confirmed in round: {}".format(confirmed_txn['confirmed-round']))
  1045. if exists(self.args.env):
  1046. if self.gt == None:
  1047. self.gt = GenTest(False)
  1048. with open(self.args.env, encoding = 'utf-8') as f:
  1049. for line in f:
  1050. e = line.rstrip('\n').split("=")
  1051. print(e)
  1052. if "TOKEN_BRIDGE" in e[0]:
  1053. v = bytes.fromhex(e[1])
  1054. self.submitVAA(v, self.client, self.foundation, self.tokenid)
  1055. if "INIT_SIGNERS_CSV" in e[0]:
  1056. self.gt.guardianKeys = e[1].split(",")
  1057. print("guardianKeys: " + str(self.gt.guardianKeys))
  1058. if "INIT_SIGNERS_KEYS_CSV" in e[0]:
  1059. print("bootstrapping the guardian set...")
  1060. self.gt.guardianPrivKeys = e[1].split(",")
  1061. print("guardianPrivKeys: " + str(self.gt.guardianPrivKeys))
  1062. seq = int(random.random() * (2**31))
  1063. bootVAA = self.gt.genGuardianSetUpgrade(self.gt.guardianPrivKeys, self.args.guardianSet, self.args.guardianSet, seq, seq)
  1064. print("dev vaa: " + bootVAA)
  1065. self.bootGuardians(bytes.fromhex(bootVAA), self.client, self.foundation, self.coreid)
  1066. seq = int(random.random() * (2**31))
  1067. regChain = self.gt.genRegisterChain(self.gt.guardianPrivKeys, self.args.guardianSet, seq, seq, 8, decode_address(get_application_address(self.tokenid)).hex())
  1068. print("ALGO_TOKEN_BRIDGE_VAA=" + regChain)
  1069. # if self.args.env != ".env":
  1070. # v = bytes.fromhex(regChain)
  1071. # self.submitVAA(v, self.client, self.foundation, self.tokenid)
  1072. # print("We submitted it!")
  1073. def updateCore(self) -> None:
  1074. print("Updating the core contracts")
  1075. if self.args.approve == "" and self.args.clear == "":
  1076. 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)
  1077. print("core approval " + decode_address(approval["hash"]).hex())
  1078. print("core clear " + decode_address(clear["hash"]).hex())
  1079. else:
  1080. pprint.pprint([self.args.approve, self.args.clear])
  1081. with open(self.args.approve, encoding = 'utf-8') as f:
  1082. approval = {"result": f.readlines()[0]}
  1083. pprint.pprint(approval)
  1084. with open(self.args.clear, encoding = 'utf-8') as f:
  1085. clear = {"result": f.readlines()[0]}
  1086. pprint.pprint(clear)
  1087. txn = transaction.ApplicationUpdateTxn(
  1088. index=self.coreid,
  1089. sender=self.foundation.getAddress(),
  1090. approval_program=b64decode(approval["result"]),
  1091. clear_program=b64decode(clear["result"]),
  1092. app_args=[ ],
  1093. sp=self.client.suggested_params(),
  1094. )
  1095. signedTxn = txn.sign(self.foundation.getPrivateKey())
  1096. print("sending transaction")
  1097. self.client.send_transaction(signedTxn)
  1098. resp = self.waitForTransaction(self.client, signedTxn.get_txid())
  1099. pprint.pprint(resp)
  1100. for x in resp.__dict__["logs"]:
  1101. print(x.hex())
  1102. print("complete")
  1103. def updateToken(self) -> None:
  1104. if self.args.approve == "" and self.args.clear == "":
  1105. 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)
  1106. else:
  1107. pprint.pprint([self.args.approve, self.args.clear])
  1108. with open(self.args.approve, encoding = 'utf-8') as f:
  1109. approval = {"result": f.readlines()[0]}
  1110. pprint.pprint(approval)
  1111. with open(self.args.clear, encoding = 'utf-8') as f:
  1112. clear = {"result": f.readlines()[0]}
  1113. pprint.pprint(clear)
  1114. # print("token " + decode_address(approval["hash"]).hex())
  1115. print("Updating the token contracts: " + str(len(b64decode(approval["result"]))))
  1116. state = self.read_global_state(self.client, self.foundation.addr, self.tokenid)
  1117. pprint.pprint( {
  1118. "validUpdateApproveHash": b64decode(state["validUpdateApproveHash"]).hex(),
  1119. "validUpdateClearHash": b64decode(state["validUpdateClearHash"]).hex()
  1120. })
  1121. txn = transaction.ApplicationUpdateTxn(
  1122. index=self.tokenid,
  1123. sender=self.foundation.getAddress(),
  1124. approval_program=b64decode(approval["result"]),
  1125. clear_program=b64decode(clear["result"]),
  1126. app_args=[ ],
  1127. sp=self.client.suggested_params(),
  1128. )
  1129. signedTxn = txn.sign(self.foundation.getPrivateKey())
  1130. print("sending transaction")
  1131. self.client.send_transaction(signedTxn)
  1132. resp = self.waitForTransaction(self.client, signedTxn.get_txid())
  1133. for x in resp.__dict__["logs"]:
  1134. print(x.hex())
  1135. print("complete")
  1136. def genTeal(self) -> None:
  1137. print((True, self.args.core_approve, self.args.core_clear, self.client, self.seed_amt, self.tsig, self.devnet or self.args.testnet))
  1138. devmode = (self.devnet or self.args.testnet) and not self.args.prodTeal
  1139. 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)
  1140. print("Generating the teal for the core contracts")
  1141. 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)
  1142. print("Generating the teal for the token contracts: " + str(len(b64decode(approval2["result"]))))
  1143. if self.devnet:
  1144. v = self.genUpgradePayloadBody(approval1, approval2)
  1145. if self.gt == None:
  1146. self.gt = GenTest(False)
  1147. emitter = bytes.fromhex(self.zeroPadBytes[0:(31*2)] + "04")
  1148. guardianSet = 0
  1149. nonce = int(random.random() * 20000)
  1150. coreVAA = self.gt.createSignedVAA(guardianSet, self.gt.guardianPrivKeys, int(time.time()), nonce, 1, emitter, int(random.random() * 20000), 32, 8, v[0])
  1151. tokenVAA = self.gt.createSignedVAA(guardianSet, self.gt.guardianPrivKeys, int(time.time()), nonce, 1, emitter, int(random.random() * 20000), 32, 8, v[1])
  1152. with open("teal/core_devnet.vaa", "w") as fout:
  1153. fout.write(coreVAA)
  1154. with open("teal/token_devnet.vaa", "w") as fout:
  1155. fout.write(tokenVAA)
  1156. def testnet(self):
  1157. self.ALGOD_ADDRESS = self.args.algod_address = "https://testnet-api.algonode.cloud"
  1158. self.INDEXER_ADDRESS = "https://testnet-idx.algonode.cloud"
  1159. self.coreid = self.args.coreid
  1160. self.tokenid = self.args.tokenid
  1161. def mainnet(self):
  1162. self.ALGOD_ADDRESS = self.args.algod_address = "https://mainnet-api.algonode.cloud"
  1163. self.INDEXER_ADDRESS = "https://mainnet-idx.algonode.cloud"
  1164. self.coreid = 842125965
  1165. self.tokenid = 842126029
  1166. if self.args.coreid != 4:
  1167. self.coreid = self.args.coreid
  1168. if self.args.tokenid != 6:
  1169. self.tokenid = self.args.tokenid
  1170. def setup_args(self) -> None:
  1171. parser = argparse.ArgumentParser(description='algorand setup')
  1172. parser.add_argument('--algod_address', type=str, help='algod address (default: http://localhost:4001)',
  1173. default="http://localhost:4001")
  1174. parser.add_argument('--algod_token', type=str, help='algod access token',
  1175. default="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
  1176. parser.add_argument('--kmd_address', type=str, help='kmd wallet address (default: http://localhost:4002)',
  1177. default="http://localhost:4002")
  1178. parser.add_argument('--kmd_token', type=str, help='kmd wallet access token',
  1179. default="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
  1180. parser.add_argument('--kmd_name', type=str, help='kmd wallet name',
  1181. default="unencrypted-default-wallet")
  1182. parser.add_argument('--kmd_password', type=str, help='kmd wallet password', default="")
  1183. parser.add_argument('--mnemonic', type=str, help='account mnemonic', default="")
  1184. parser.add_argument('--fundDevAccounts', action='store_true', help='Fund predetermined set of devnet accounts')
  1185. parser.add_argument('--core_approve', type=str, help='core approve teal', default="teal/core_approve.teal")
  1186. parser.add_argument('--core_clear', type=str, help='core clear teal', default="teal/core_clear.teal")
  1187. parser.add_argument('--token_approve', type=str, help='token approve teal', default="teal/token_approve.teal")
  1188. parser.add_argument('--token_clear', type=str, help='token clear teal', default="teal/token_clear.teal")
  1189. parser.add_argument('--coreid', type=int, help='core contract', default=4)
  1190. parser.add_argument('--tokenid', type=int, help='token bridge contract', default=6)
  1191. parser.add_argument('--devnet', action='store_true', help='setup devnet')
  1192. parser.add_argument('--boot', action='store_true', help='bootstrap')
  1193. parser.add_argument('--upgradePayload', action='store_true', help='gen the upgrade payload for the guardians to sign')
  1194. parser.add_argument('--vaa', type=str, help='Submit the supplied VAA', default="")
  1195. parser.add_argument('--env', type=str, help='deploying using the supplied .env file', default=".env")
  1196. parser.add_argument('--guardianSet', type=int, help='What guardianSet should I syntheticly create if needed', default=0)
  1197. parser.add_argument('--appid', type=str, help='The appid that the vaa submit is applied to', default="")
  1198. parser.add_argument('--submit', action='store_true', help='submit the synthetic vaas')
  1199. parser.add_argument('--updateCore', action='store_true', help='update the Core contracts')
  1200. parser.add_argument('--updateToken', action='store_true', help='update the Token contracts')
  1201. parser.add_argument('--upgradeVAA', action='store_true', help='generate a upgrade vaa for devnet')
  1202. parser.add_argument('--print', action='store_true', help='print')
  1203. parser.add_argument('--genParts', action='store_true', help='Get tssig parts')
  1204. parser.add_argument('--prodTeal', action='store_true', help='use Production Deal')
  1205. parser.add_argument('--genTeal', action='store_true', help='Generate all the teal from the pyteal')
  1206. parser.add_argument('--fund', action='store_true', help='Generate some accounts and fund them')
  1207. parser.add_argument('--testnet', action='store_true', help='Connect to testnet')
  1208. parser.add_argument('--mainnet', action='store_true', help='Connect to mainnet')
  1209. parser.add_argument('--bootGuardian', type=str, help='Submit the supplied VAA', default="")
  1210. parser.add_argument('--rpc', type=str, help='RPC address', default="")
  1211. parser.add_argument('--guardianKeys', type=str, help='GuardianKeys', default="")
  1212. parser.add_argument('--guardianPrivKeys', type=str, help='guardianPrivKeys', default="")
  1213. parser.add_argument('--approve', type=str, help='compiled approve contract', default="")
  1214. parser.add_argument('--clear', type=str, help='compiled clear contract', default="")
  1215. parser.add_argument("--loops", type=int, help="testing: how many iterations should randomized tests run for. defaults to 1 for faster testing.", default="1")
  1216. parser.add_argument("--bigset", action="store_true", help="testing: use the big set of validators", default="1")
  1217. args = parser.parse_args()
  1218. self.init(args)
  1219. self.devnet = args.devnet
  1220. def main(self) -> None:
  1221. self.setup_args()
  1222. args = self.args
  1223. if args.testnet:
  1224. self.testnet()
  1225. if args.mainnet:
  1226. self.mainnet()
  1227. if args.rpc != "":
  1228. self.ALGOD_ADDRESS = self.args.rpc
  1229. self.client = self.getAlgodClient()
  1230. if self.devnet or self.args.testnet:
  1231. self.vaa_verify = self.client.compile(get_vaa_verify())
  1232. else:
  1233. c = AlgodClient("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "https://testnet-api.algonode.cloud")
  1234. self.vaa_verify = c.compile(get_vaa_verify())
  1235. self.vaa_verify["lsig"] = LogicSig(base64.b64decode(self.vaa_verify["result"]))
  1236. if args.genTeal or args.boot:
  1237. self.genTeal()
  1238. # Generate the upgrade payload we need the guardians to sign
  1239. if args.upgradePayload:
  1240. print(self.genUpgradePayload())
  1241. sys.exit(0)
  1242. # This breaks the tsig up into the various parts so that we
  1243. # can embed it into the Typescript code for reassembly
  1244. if args.genParts:
  1245. print("this.ALGO_VERIFY_HASH = \"%s\""%self.vaa_verify["hash"]);
  1246. print("this.ALGO_VERIFY = new Uint8Array([", end='')
  1247. for x in b64decode(self.vaa_verify["result"]):
  1248. print("%d, "%(x), end='')
  1249. print("])")
  1250. parts = [
  1251. self.tsig.get_bytecode_raw(0).hex(),
  1252. self.tsig.get_bytecode_raw(1).hex(),
  1253. self.tsig.get_bytecode_raw(2).hex(),
  1254. self.tsig.get_bytecode_raw(3).hex(),
  1255. self.tsig.get_bytecode_raw(4).hex()
  1256. ]
  1257. pprint.pprint(parts)
  1258. sys.exit(0)
  1259. if args.mnemonic:
  1260. self.foundation = Account.FromMnemonic(args.mnemonic)
  1261. if args.devnet and self.foundation == None:
  1262. print("Generating the foundation account...")
  1263. self.foundation = self.getTemporaryAccount(self.client)
  1264. print("Foundation account: " + self.foundation.getMnemonic())
  1265. if self.args.fund:
  1266. sys.exit(0)
  1267. if self.foundation == None:
  1268. print("We dont have a account? Here is a random one I just made up...")
  1269. pk = account.generate_account()[0]
  1270. print(" pk: " + pk)
  1271. print(" address: " + account.address_from_private_key(pk))
  1272. print(" mnemonic: " + mnemonic.from_private_key(pk))
  1273. if args.testnet:
  1274. 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)")
  1275. sys.exit(0)
  1276. bal = self.getBalances(self.client, self.foundation.addr)
  1277. print("foundation address " + self.foundation.addr + " (" + str(float(bal[0]) / 1000000.0) + " ALGO)")
  1278. if bal[0] < 10000000:
  1279. print("you need at least 10 ALGO to do darn near anything...")
  1280. sys.exit(0)
  1281. if args.guardianKeys != "":
  1282. self.gt.guardianKeys = eval(args.guardianKeys)
  1283. if args.guardianPrivKeys != "":
  1284. self.gt.guardianPrivKeyss = eval(args.guardianPrivKeys)
  1285. if args.upgradeVAA:
  1286. ret = self.devnetUpgradeVAA()
  1287. pprint.pprint(ret)
  1288. if (args.submit) :
  1289. print("submitting vaa to upgrade core: " + str(self.coreid))
  1290. state = self.read_global_state(self.client, self.foundation.addr, self.coreid)
  1291. pprint.pprint( {
  1292. "validUpdateApproveHash": b64decode(state["validUpdateApproveHash"]).hex(),
  1293. "validUpdateClearHash": b64decode(state["validUpdateClearHash"]).hex()
  1294. })
  1295. self.submitVAA(bytes.fromhex(ret[0]), self.client, self.foundation, self.coreid)
  1296. state = self.read_global_state(self.client, self.foundation.addr, self.coreid)
  1297. pprint.pprint( {
  1298. "validUpdateApproveHash": b64decode(state["validUpdateApproveHash"]).hex(),
  1299. "validUpdateClearHash": b64decode(state["validUpdateClearHash"]).hex()
  1300. })
  1301. print("submitting vaa to upgrade token: " + str(self.tokenid))
  1302. state = self.read_global_state(self.client, self.foundation.addr, self.tokenid)
  1303. pprint.pprint( {
  1304. "validUpdateApproveHash": b64decode(state["validUpdateApproveHash"]).hex(),
  1305. "validUpdateClearHash": b64decode(state["validUpdateClearHash"]).hex()
  1306. })
  1307. self.submitVAA(bytes.fromhex(ret[1]), self.client, self.foundation, self.tokenid)
  1308. state = self.read_global_state(self.client, self.foundation.addr, self.tokenid)
  1309. pprint.pprint( {
  1310. "validUpdateApproveHash": b64decode(state["validUpdateApproveHash"]).hex(),
  1311. "validUpdateClearHash": b64decode(state["validUpdateClearHash"]).hex()
  1312. })
  1313. if args.boot:
  1314. self.boot()
  1315. if args.updateCore:
  1316. self.updateCore()
  1317. if args.updateToken:
  1318. self.updateToken()
  1319. if args.vaa:
  1320. if self.args.appid == "":
  1321. raise Exception("You need to specifiy the appid when you are submitting vaas")
  1322. vaa = bytes.fromhex(args.vaa)
  1323. pprint.pprint(self.parseVAA(vaa))
  1324. self.submitVAA(vaa, self.client, self.foundation, int(self.args.appid))
  1325. if args.bootGuardian != "":
  1326. vaa = bytes.fromhex(args.bootGuardian)
  1327. pprint.pprint(self.parseVAA(vaa))
  1328. response = self.bootGuardians(vaa, self.client, self.foundation, self.coreid)
  1329. pprint.pprint(response.__dict__)
  1330. if args.fundDevAccounts:
  1331. if not args.devnet:
  1332. print("Missing required parameter: --devnet")
  1333. sys.exit(0)
  1334. self.fundDevAccounts(self.client)
  1335. if __name__ == "__main__":
  1336. core = PortalCore()
  1337. core.main()