test.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. # python3 -m pip install pycryptodomex uvarint pyteal web3 coincurve
  2. import sys
  3. sys.path.append("..")
  4. from admin import PortalCore, Account
  5. from gentest import GenTest
  6. from base64 import b64decode
  7. from typing import List, Tuple, Dict, Any, Optional, Union
  8. import base64
  9. import random
  10. import time
  11. import hashlib
  12. import uuid
  13. import json
  14. from algosdk.v2client.algod import AlgodClient
  15. from algosdk.kmd import KMDClient
  16. from algosdk import account, mnemonic
  17. from algosdk.encoding import decode_address, encode_address
  18. from algosdk.future import transaction
  19. import algosdk
  20. from pyteal import compileTeal, Mode, Expr
  21. from pyteal import *
  22. from algosdk.logic import get_application_address
  23. from vaa_verify import get_vaa_verify
  24. from algosdk.future.transaction import LogicSig
  25. from test_contract import get_test_app
  26. from algosdk.v2client import indexer
  27. import pprint
  28. class AlgoTest(PortalCore):
  29. def __init__(self) -> None:
  30. super().__init__()
  31. def getBalances(self, client: AlgodClient, account: str) -> Dict[int, int]:
  32. balances: Dict[int, int] = dict()
  33. accountInfo = client.account_info(account)
  34. # set key 0 to Algo balance
  35. balances[0] = accountInfo["amount"]
  36. assets: List[Dict[str, Any]] = accountInfo.get("assets", [])
  37. for assetHolding in assets:
  38. assetID = assetHolding["asset-id"]
  39. amount = assetHolding["amount"]
  40. balances[assetID] = amount
  41. return balances
  42. def createTestApp(
  43. self,
  44. client: AlgodClient,
  45. sender: Account,
  46. ) -> int:
  47. approval, clear = get_test_app(client)
  48. globalSchema = transaction.StateSchema(num_uints=4, num_byte_slices=30)
  49. localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=16)
  50. app_args = []
  51. txn = transaction.ApplicationCreateTxn(
  52. sender=sender.getAddress(),
  53. on_complete=transaction.OnComplete.NoOpOC,
  54. approval_program=b64decode(approval["result"]),
  55. clear_program=b64decode(clear["result"]),
  56. global_schema=globalSchema,
  57. local_schema=localSchema,
  58. app_args=app_args,
  59. sp=client.suggested_params(),
  60. )
  61. signedTxn = txn.sign(sender.getPrivateKey())
  62. client.send_transaction(signedTxn)
  63. response = self.waitForTransaction(client, signedTxn.get_txid())
  64. assert response.applicationIndex is not None and response.applicationIndex > 0
  65. txn = transaction.PaymentTxn(sender = sender.getAddress(), sp = client.suggested_params(),
  66. receiver = get_application_address(response.applicationIndex), amt = 300000)
  67. signedTxn = txn.sign(sender.getPrivateKey())
  68. client.send_transaction(signedTxn)
  69. return response.applicationIndex
  70. def parseSeqFromLog(self, txn):
  71. try:
  72. return int.from_bytes(b64decode(txn.innerTxns[-1]["logs"][0]), "big")
  73. except Exception as err:
  74. pprint.pprint(txn.__dict__)
  75. raise
  76. def getVAA(self, client, sender, sid, app):
  77. if sid == None:
  78. raise Exception("getVAA called with a sid of None")
  79. saddr = get_application_address(app)
  80. # SOOO, we send a nop txn through to push the block forward
  81. # one
  82. # This is ONLY needed on a local net... the indexer will sit
  83. # on the last block for 30 to 60 seconds... we don't want this
  84. # log in prod since it is wasteful of gas
  85. if (self.INDEXER_ROUND > 512 and not self.args.testnet): # until they fix it
  86. print("indexer is broken in local net... stop/clean/restart the sandbox")
  87. sys.exit(0)
  88. txns = []
  89. txns.append(
  90. transaction.ApplicationCallTxn(
  91. sender=sender.getAddress(),
  92. index=self.tokenid,
  93. on_complete=transaction.OnComplete.NoOpOC,
  94. app_args=[b"nop"],
  95. sp=client.suggested_params(),
  96. )
  97. )
  98. self.sendTxn(client, sender, txns, False)
  99. if self.myindexer == None:
  100. print("indexer address: " + self.INDEXER_ADDRESS)
  101. self.myindexer = indexer.IndexerClient(indexer_token=self.INDEXER_TOKEN, indexer_address=self.INDEXER_ADDRESS)
  102. while True:
  103. nexttoken = ""
  104. while True:
  105. response = self.myindexer.search_transactions( min_round=self.INDEXER_ROUND, next_page=nexttoken)
  106. # pprint.pprint(response)
  107. for x in response["transactions"]:
  108. # pprint.pprint(x)
  109. if 'inner-txns' not in x:
  110. continue
  111. for y in x["inner-txns"]:
  112. if "application-transaction" not in y:
  113. continue
  114. if y["application-transaction"]["application-id"] != self.coreid:
  115. continue
  116. if len(y["logs"]) == 0:
  117. continue
  118. args = y["application-transaction"]["application-args"]
  119. if len(args) < 2:
  120. continue
  121. if base64.b64decode(args[0]) != b'publishMessage':
  122. continue
  123. seq = int.from_bytes(base64.b64decode(y["logs"][0]), "big")
  124. if seq != sid:
  125. continue
  126. if y["sender"] != saddr:
  127. continue;
  128. emitter = decode_address(y["sender"])
  129. payload = base64.b64decode(args[1])
  130. # pprint.pprint([seq, y["sender"], payload.hex()])
  131. # sys.exit(0)
  132. return self.gt.genVaa(emitter, seq, payload)
  133. if 'next-token' in response:
  134. nexttoken = response['next-token']
  135. else:
  136. self.INDEXER_ROUND = response['current-round'] + 1
  137. break
  138. time.sleep(1)
  139. def publishMessage(self, client, sender, vaa, appid):
  140. aa = decode_address(get_application_address(appid)).hex()
  141. emitter_addr = self.optin(client, sender, self.coreid, 0, aa)
  142. txns = []
  143. sp = client.suggested_params()
  144. a = transaction.ApplicationCallTxn(
  145. sender=sender.getAddress(),
  146. index=appid,
  147. on_complete=transaction.OnComplete.NoOpOC,
  148. app_args=[b"test1", vaa, self.coreid],
  149. foreign_apps = [self.coreid],
  150. accounts=[emitter_addr],
  151. sp=sp
  152. )
  153. a.fee = a.fee * 2
  154. txns.append(a)
  155. resp = self.sendTxn(client, sender, txns, True)
  156. self.INDEXER_ROUND = resp.confirmedRound
  157. return self.parseSeqFromLog(resp)
  158. def createTestAsset(self, client, sender):
  159. txns = []
  160. a = transaction.PaymentTxn(
  161. sender = sender.getAddress(),
  162. sp = client.suggested_params(),
  163. receiver = get_application_address(self.testid),
  164. amt = 300000
  165. )
  166. txns.append(a)
  167. sp = client.suggested_params()
  168. a = transaction.ApplicationCallTxn(
  169. sender=sender.getAddress(),
  170. index=self.testid,
  171. on_complete=transaction.OnComplete.NoOpOC,
  172. app_args=[b"setup"],
  173. sp=sp
  174. )
  175. a.fee = a.fee * 2
  176. txns.append(a)
  177. transaction.assign_group_id(txns)
  178. grp = []
  179. pk = sender.getPrivateKey()
  180. for t in txns:
  181. grp.append(t.sign(pk))
  182. client.send_transactions(grp)
  183. resp = self.waitForTransaction(client, grp[-1].get_txid())
  184. aid = int.from_bytes(resp.__dict__["logs"][0], "big")
  185. print("Opting " + sender.getAddress() + " into " + str(aid))
  186. self.asset_optin(client, sender, aid, sender.getAddress())
  187. txns = []
  188. a = transaction.ApplicationCallTxn(
  189. sender=sender.getAddress(),
  190. index=self.testid,
  191. on_complete=transaction.OnComplete.NoOpOC,
  192. app_args=[b"mint"],
  193. foreign_assets = [aid],
  194. sp=sp
  195. )
  196. a.fee = a.fee * 2
  197. txns.append(a)
  198. resp = self.sendTxn(client, sender, txns, True)
  199. # self.INDEXER_ROUND = resp.confirmedRound
  200. return aid
  201. def getCreator(self, client, sender, asset_id):
  202. return client.asset_info(asset_id)["params"]["creator"]
  203. def testAttest(self, client, sender, asset_id):
  204. taddr = get_application_address(self.tokenid)
  205. aa = decode_address(taddr).hex()
  206. emitter_addr = self.optin(client, sender, self.coreid, 0, aa)
  207. if asset_id != 0:
  208. creator = self.getCreator(client, sender, asset_id)
  209. c = client.account_info(creator)
  210. wormhole = c.get("auth-addr") == taddr
  211. else:
  212. c = None
  213. wormhole = False
  214. if not wormhole:
  215. creator = self.optin(client, sender, self.tokenid, asset_id, b"native".hex())
  216. txns = []
  217. sp = client.suggested_params()
  218. txns.append(transaction.ApplicationCallTxn(
  219. sender=sender.getAddress(),
  220. index=self.tokenid,
  221. on_complete=transaction.OnComplete.NoOpOC,
  222. app_args=[b"nop"],
  223. sp=sp
  224. ))
  225. mfee = self.getMessageFee()
  226. if (mfee > 0):
  227. txns.append(transaction.PaymentTxn(sender = sender.getAddress(), sp = sp, receiver = get_application_address(self.tokenid), amt = mfee))
  228. accts = [emitter_addr, creator, get_application_address(self.coreid)]
  229. if c != None:
  230. accts.append(c["address"])
  231. a = transaction.ApplicationCallTxn(
  232. sender=sender.getAddress(),
  233. index=self.tokenid,
  234. on_complete=transaction.OnComplete.NoOpOC,
  235. app_args=[b"attestToken", asset_id],
  236. foreign_apps = [self.coreid],
  237. foreign_assets = [asset_id],
  238. accounts=accts,
  239. sp=sp
  240. )
  241. if (mfee > 0):
  242. a.fee = a.fee * 3
  243. else:
  244. a.fee = a.fee * 2
  245. txns.append(a)
  246. resp = self.sendTxn(client, sender, txns, True)
  247. # Point us at the correct round
  248. self.INDEXER_ROUND = resp.confirmedRound
  249. # print(encode_address(resp.__dict__["logs"][0]))
  250. # print(encode_address(resp.__dict__["logs"][1]))
  251. # pprint.pprint(resp.__dict__)
  252. return self.parseSeqFromLog(resp)
  253. def transferFromAlgorand(self, client, sender, asset_id, quantity, receiver, chain, fee, payload = None):
  254. # pprint.pprint(["transferFromAlgorand", asset_id, quantity, receiver, chain, fee])
  255. taddr = get_application_address(self.tokenid)
  256. aa = decode_address(taddr).hex()
  257. emitter_addr = self.optin(client, sender, self.coreid, 0, aa)
  258. # asset_id 0 is ALGO
  259. if asset_id == 0:
  260. wormhole = False
  261. else:
  262. creator = self.getCreator(client, sender, asset_id)
  263. c = client.account_info(creator)
  264. wormhole = c.get("auth-addr") == taddr
  265. txns = []
  266. mfee = self.getMessageFee()
  267. if (mfee > 0):
  268. txns.append(transaction.PaymentTxn(sender = sender.getAddress(), sp = sp, receiver = get_application_address(self.tokenid), amt = mfee))
  269. if not wormhole:
  270. creator = self.optin(client, sender, self.tokenid, asset_id, b"native".hex())
  271. print("non wormhole account " + creator)
  272. sp = client.suggested_params()
  273. if (asset_id != 0) and (not self.asset_optin_check(client, sender, asset_id, creator)):
  274. print("Looks like we need to optin")
  275. txns.append(
  276. transaction.PaymentTxn(
  277. sender=sender.getAddress(),
  278. receiver=creator,
  279. amt=100000,
  280. sp=sp
  281. )
  282. )
  283. # The tokenid app needs to do the optin since it has signature authority
  284. a = transaction.ApplicationCallTxn(
  285. sender=sender.getAddress(),
  286. index=self.tokenid,
  287. on_complete=transaction.OnComplete.NoOpOC,
  288. app_args=[b"optin", asset_id],
  289. foreign_assets = [asset_id],
  290. accounts=[creator],
  291. sp=sp
  292. )
  293. a.fee = a.fee * 2
  294. txns.append(a)
  295. self.sendTxn(client, sender, txns, False)
  296. txns = []
  297. txns.insert(0,
  298. transaction.ApplicationCallTxn(
  299. sender=sender.getAddress(),
  300. index=self.tokenid,
  301. on_complete=transaction.OnComplete.NoOpOC,
  302. app_args=[b"nop"],
  303. sp=client.suggested_params(),
  304. )
  305. )
  306. if asset_id == 0:
  307. print("asset_id == 0")
  308. txns.append(transaction.PaymentTxn(
  309. sender=sender.getAddress(),
  310. receiver=creator,
  311. amt=quantity,
  312. sp=sp,
  313. ))
  314. accounts=[emitter_addr, creator, creator]
  315. else:
  316. print("asset_id != 0")
  317. txns.append(
  318. transaction.AssetTransferTxn(
  319. sender = sender.getAddress(),
  320. sp = sp,
  321. receiver = creator,
  322. amt = quantity,
  323. index = asset_id
  324. ))
  325. accounts=[emitter_addr, creator, c["address"]]
  326. args = [b"sendTransfer", asset_id, quantity, receiver, chain, fee]
  327. if None != payload:
  328. args.append(payload)
  329. #pprint.pprint(args)
  330. # print(self.tokenid)
  331. a = transaction.ApplicationCallTxn(
  332. sender=sender.getAddress(),
  333. index=self.tokenid,
  334. on_complete=transaction.OnComplete.NoOpOC,
  335. app_args=args,
  336. foreign_apps = [self.coreid],
  337. foreign_assets = [asset_id],
  338. accounts=accounts,
  339. sp=sp
  340. )
  341. a.fee = a.fee * 2
  342. txns.append(a)
  343. resp = self.sendTxn(client, sender, txns, True)
  344. self.INDEXER_ROUND = resp.confirmedRound
  345. # pprint.pprint([self.coreid, self.tokenid, resp.__dict__,
  346. # int.from_bytes(resp.__dict__["logs"][1], "big"),
  347. # int.from_bytes(resp.__dict__["logs"][2], "big"),
  348. # int.from_bytes(resp.__dict__["logs"][3], "big"),
  349. # int.from_bytes(resp.__dict__["logs"][4], "big"),
  350. # int.from_bytes(resp.__dict__["logs"][5], "big")
  351. # ])
  352. # print(encode_address(resp.__dict__["logs"][0]))
  353. # print(encode_address(resp.__dict__["logs"][1]))
  354. return self.parseSeqFromLog(resp)
  355. def asset_optin_check(self, client, sender, asset, receiver):
  356. if receiver not in self.asset_cache:
  357. self.asset_cache[receiver] = {}
  358. if asset in self.asset_cache[receiver]:
  359. return True
  360. ai = client.account_info(receiver)
  361. if "assets" in ai:
  362. for x in ai["assets"]:
  363. if x["asset-id"] == asset:
  364. self.asset_cache[receiver][asset] = True
  365. return True
  366. return False
  367. def asset_optin(self, client, sender, asset, receiver):
  368. if self.asset_optin_check(client, sender, asset, receiver):
  369. return
  370. pprint.pprint(["asset_optin", asset, receiver])
  371. sp = client.suggested_params()
  372. optin_txn = transaction.AssetTransferTxn(
  373. sender = sender.getAddress(),
  374. sp = sp,
  375. receiver = receiver,
  376. amt = 0,
  377. index = asset
  378. )
  379. transaction.assign_group_id([optin_txn])
  380. signed_optin = optin_txn.sign(sender.getPrivateKey())
  381. client.send_transactions([signed_optin])
  382. resp = self.waitForTransaction(client, signed_optin.get_txid())
  383. assert self.asset_optin_check(client, sender, asset, receiver), "The optin failed"
  384. print("woah! optin succeeded")
  385. def simple_test(self):
  386. #vaa = self.parseVAA(bytes.fromhex("01000000000100ddc6993585b909c3e861830244122e0daf45101663942484aa56ee2b51fa3ff016411102f993935d428a5aa0c3ace74facd60822435893b74b24fadde0fbad49006277c3fe0000000000088edf5b0e108c3a1a0a4b704cc89591f2ad8d50df24e991567e640ed720a94be200000000000000060003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000800080000000000000000000000000000000000000000000000000000000000000000ff"))
  387. #pprint.pprint(vaa)
  388. #sys.exit(0)
  389. # q = bytes.fromhex(gt.genAssetMeta(gt.guardianPrivKeys, 1, 1, 1, bytes.fromhex("4523c3F29447d1f32AEa95BEBD00383c4640F1b4"), 1, 8, b"USDC", b"CircleCoin"))
  390. # pprint.pprint(self.parseVAA(q))
  391. # sys.exit(0)
  392. # vaa = self.parseVAA(bytes.fromhex("0100000001010001ca2fbf60ac6227d47dda4fe2e7bccc087f27d22170a212b9800da5b4cbf0d64c52deb2f65ce58be2267bf5b366437c267b5c7b795cd6cea1ac2fee8a1db3ad006225f801000000010001000000000000000000000000000000000000000000000000000000000000000400000000000000012000000000000000000000000000000000000000000000000000000000436f72650200000000000001beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe"))
  393. # pprint.pprint(vaa)
  394. # vaa = self.parseVAA(bytes.fromhex("01000000010100c22ce0a3c995fca993cb0e91af74d745b6ec1a04b3adf0bb3e432746b3e2ab5e635b65d34d5148726cac10e84bf5932a7f21b9545c362bd512617aa980e0fbf40062607566000000010001000000000000000000000000000000000000000000000000000000000000000400000000000000012000000000000000000000000000000000000000000000000000000000436f72650200000000000101beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe"))
  395. # pprint.pprint(vaa)
  396. # sys.exit(0)
  397. self.setup_args()
  398. gt = GenTest(self.args.bigset)
  399. self.gt = gt
  400. if self.args.testnet:
  401. self.testnet()
  402. else:
  403. self.devnet = True
  404. client = self.client = self.getAlgodClient()
  405. self.genTeal()
  406. self.vaa_verify = self.client.compile(get_vaa_verify())
  407. self.vaa_verify["lsig"] = LogicSig(base64.b64decode(self.vaa_verify["result"]))
  408. vaaLogs = []
  409. args = self.args
  410. if self.args.mnemonic:
  411. self.foundation = Account.FromMnemonic(self.args.mnemonic)
  412. if self.foundation == None:
  413. print("Generating the foundation account...")
  414. self.foundation = self.getTemporaryAccount(self.client)
  415. if self.foundation == None:
  416. print("We dont have a account? ")
  417. sys.exit(1)
  418. foundation = self.foundation
  419. seq = int(time.time())
  420. print("Creating the PortalCore app")
  421. self.coreid = self.createPortalCoreApp(client=client, sender=foundation)
  422. print("coreid = " + str(self.coreid) + " " + get_application_address(self.coreid))
  423. print("bootstrapping the guardian set...")
  424. bootVAA = bytes.fromhex(gt.genGuardianSetUpgrade(gt.guardianPrivKeys, 1, 1, seq, seq))
  425. self.bootGuardians(bootVAA, client, foundation, self.coreid)
  426. seq += 1
  427. print("grabbing a untrusted account")
  428. player = self.getTemporaryAccount(client)
  429. print(player.getAddress())
  430. print("")
  431. bal = self.getBalances(client, player.getAddress())
  432. pprint.pprint(bal)
  433. print("upgrading the the guardian set using untrusted account...")
  434. upgradeVAA = bytes.fromhex(gt.genGuardianSetUpgrade(gt.guardianPrivKeys, 1, 2, seq, seq))
  435. vaaLogs.append(["guardianUpgrade", upgradeVAA.hex()])
  436. self.submitVAA(upgradeVAA, client, player, self.coreid)
  437. bal = self.getBalances(client, player.getAddress())
  438. pprint.pprint(bal)
  439. seq += 1
  440. print("Create the token bridge")
  441. self.tokenid = self.createTokenBridgeApp(client, foundation)
  442. print("token bridge " + str(self.tokenid) + " address " + get_application_address(self.tokenid))
  443. ret = self.devnetUpgradeVAA()
  444. # pprint.pprint(ret)
  445. print("Submitting core")
  446. self.submitVAA(bytes.fromhex(ret[0]), self.client, foundation, self.coreid)
  447. print("Submitting token")
  448. self.submitVAA(bytes.fromhex(ret[1]), self.client, foundation, self.tokenid)
  449. print("successfully sent upgrade requests")
  450. for r in range(1, 6):
  451. print("Registering chain " + str(r))
  452. v = gt.genRegisterChain(gt.guardianPrivKeys, 2, seq, seq, r)
  453. vaa = bytes.fromhex(v)
  454. # pprint.pprint((v, self.parseVAA(vaa)))
  455. if r == 2:
  456. vaaLogs.append(["registerChain", v])
  457. self.submitVAA(vaa, client, player, self.tokenid)
  458. seq += 1
  459. bal = self.getBalances(client, player.getAddress())
  460. pprint.pprint(bal)
  461. print("Create a asset")
  462. attestVAA = bytes.fromhex(gt.genAssetMeta(gt.guardianPrivKeys, 2, seq, seq, bytes.fromhex("4523c3F29447d1f32AEa95BEBD00383c4640F1b4"), 1, 8, b"USDC", b"CircleCoin"))
  463. # paul - createWrappedOnAlgorand
  464. vaaLogs.append(["createWrappedOnAlgorand", attestVAA.hex()])
  465. self.submitVAA(attestVAA, client, player, self.tokenid)
  466. seq += 1
  467. p = self.parseVAA(attestVAA)
  468. chain_addr = self.optin(client, player, self.tokenid, p["FromChain"], p["Contract"])
  469. print("Create the same asset " + str(seq))
  470. # paul - updateWrappedOnAlgorand
  471. attestVAA = bytes.fromhex(gt.genAssetMeta(gt.guardianPrivKeys, 2, seq, seq, bytes.fromhex("4523c3F29447d1f32AEa95BEBD00383c4640F1b4"), 1, 8, b"USD2C", b"Circle2Coin"))
  472. self.submitVAA(attestVAA, client, player, self.tokenid)
  473. seq += 1
  474. print("Transfer the asset " + str(seq))
  475. transferVAA = bytes.fromhex(gt.genTransfer(gt.guardianPrivKeys, 1, 1, 1, 1, bytes.fromhex("4523c3F29447d1f32AEa95BEBD00383c4640F1b4"), 1, decode_address(player.getAddress()), 8, 0))
  476. # paul - redeemOnAlgorand
  477. vaaLogs.append(["redeemOnAlgorand", transferVAA.hex()])
  478. self.submitVAA(transferVAA, client, player, self.tokenid)
  479. seq += 1
  480. def double_submit_transfer_vaa_fails(seq):
  481. """
  482. Resend the same transaction we just send, changing only its nonce.
  483. This should fail _as long as the sequence number is not incremented_
  484. """
  485. # send a nice VAA to begin with. everything but these settings will be random
  486. # so we can be sure this works with many different VAAs -- as long as they are valid
  487. # non-valid vaas fail for other reasons
  488. vaa = bytearray.fromhex(gt.genRandomValidTransfer(
  489. signers=gt.guardianPrivKeys,
  490. guardianSet=1,
  491. seq=seq,
  492. # we set the max_amount, but the actual amount will be between zero and this value
  493. amount_max=self.getBalances(client, player.getAddress())[0], # 0 is the ALGO amount
  494. tokenAddress=bytes.fromhex("4523c3F29447d1f32AEa95BEBD00383c4640F1b4"),
  495. toAddress=decode_address(player.getAddress()),
  496. ))
  497. self.submitVAA(vaa, client, player, self.tokenid)
  498. # Let's make this even stronger: scramble the few bytes we can (len_signatures, signatures)
  499. # so the repeated one is still valid, but different from the first one.
  500. # NOTE: this will only be interesting if we are working with a big validator set,
  501. # don't even botters if it's not
  502. if len(gt.guardianKeys) > 1:
  503. current_signatures_amount = vaa[5]
  504. signatures_len = 66*current_signatures_amount
  505. signatures_offset = 6
  506. rest_offset = signatures_offset+signatures_len
  507. new_signature_amount = random.randint(int(len(gt.guardianKeys)*2/3)+1, current_signatures_amount)
  508. # construct a list of every siganture with its index
  509. signatures = vaa[signatures_offset:rest_offset]
  510. signatures = [signatures[i:i+66] for i in range(0, len(signatures), 66)]
  511. assert len(signatures) == current_signatures_amount
  512. # scramble the signatures so we get new bytes
  513. new_signatures = random.sample(signatures, k=new_signature_amount)
  514. assert len(new_signatures) == new_signature_amount
  515. new_signatures = b''.join(new_signatures)
  516. vaa[5] = new_signature_amount
  517. new_vaa = vaa[:6] + new_signatures + vaa[rest_offset:]
  518. assert(len(new_vaa) == len(vaa)-((current_signatures_amount-new_signature_amount)*66))
  519. vaa = new_vaa
  520. # now try again!
  521. try:
  522. self.submitVAA(vaa, client, player, self.tokenid)
  523. except algosdk.error.AlgodHTTPError as e:
  524. # should fail right at line 963
  525. if "opcodes=pushint 963" in str(e):
  526. return True, vaa, None
  527. return False, vaa, e
  528. return False, vaa, None
  529. for _ in range(self.args.loops):
  530. result, vaa, err = double_submit_transfer_vaa_fails(seq)
  531. if err != None:
  532. assert False, f"!!! ERR: unepexted error. error:\n {err}\noffending vaa hex:\n{vaa.hex()}"
  533. assert result, f"!!! ERR: sending same VAA twice worked. offending vaa hex:\n{vaa.hex()}"
  534. seq+=1
  535. return
  536. def sending_vaa_version_not_one_fails(seq, version):
  537. vaa = bytearray.fromhex(gt.genRandomValidTransfer(
  538. signers=gt.guardianPrivKeys,
  539. guardianSet=1,
  540. seq=seq,
  541. tokenAddress=bytes.fromhex("4523c3F29447d1f32AEa95BEBD00383c4640F1b4"),
  542. toAddress=decode_address(player.getAddress()),
  543. amount_max=self.getBalances(client, player.getAddress())[0], # 0 is the ALGO amount
  544. ))
  545. # we know VAA is malleable in the first four fields:
  546. # version, guardian set index, len of signatures, signatures
  547. vaa[0] = version
  548. try:
  549. self.submitVAA(vaa, client, player, self.tokenid)
  550. except algosdk.error.AlgodHTTPError as e:
  551. # right at the beginning of checkForDuplicate()
  552. if "opcodes=pushint 919" in str(e):
  553. return True, vaa, None
  554. return False, vaa, e
  555. return False, vaa, None
  556. # no need to increase _seq_ after this one as if everything went ok...
  557. # all VAAs should have been invalid!
  558. for _ in range(self.args.loops):
  559. version = random.randint(0, 255)
  560. if version == 1:
  561. continue
  562. ok, vaa, err = sending_vaa_version_not_one_fails(seq, version)
  563. if err != None:
  564. assert False, f"!!! ERR: unepexted error when testing version. error:\n {err}\noffending vaa hex:\n{vaa.hex()}"
  565. assert ok, f"!!! ERR: Invalid version worked. offending version: {version}. offending vaa:\n{vaa}"
  566. print("Create the test app we will use to torture ourselves using a new player")
  567. player2 = self.getTemporaryAccount(client)
  568. print("player2 address " + player2.getAddress())
  569. player3 = self.getTemporaryAccount(client)
  570. print("player3 address " + player3.getAddress())
  571. self.testid = self.createTestApp(client, player2)
  572. print("testid " + str(self.testid) + " address " + get_application_address(self.testid))
  573. print("Sending a message payload to the core contract")
  574. sid = self.publishMessage(client, player, b"you also suck", self.testid)
  575. self.publishMessage(client, player2, b"second suck", self.testid)
  576. self.publishMessage(client, player3, b"last message", self.testid)
  577. print("Lets create a brand new non-wormhole asset and try to attest and send it out")
  578. self.testasset = self.createTestAsset(client, player2)
  579. print("test asset id: " + str(self.testasset))
  580. print("Now lets create an attest of ALGO")
  581. sid = self.testAttest(client, player2, 0)
  582. vaa = self.getVAA(client, player, sid, self.tokenid)
  583. v = self.parseVAA(bytes.fromhex(vaa))
  584. print("We got a " + str(v["Meta"]))
  585. print("Lets try to create an attest for a non-wormhole thing with a huge number of decimals")
  586. # paul - attestFromAlgorand
  587. sid = self.testAttest(client, player2, self.testasset)
  588. print("... track down the generated VAA")
  589. vaa = self.getVAA(client, player, sid, self.tokenid)
  590. v = self.parseVAA(bytes.fromhex(vaa))
  591. print("We got a " + v["Meta"])
  592. pprint.pprint(self.getBalances(client, player.getAddress()))
  593. pprint.pprint(self.getBalances(client, player2.getAddress()))
  594. pprint.pprint(self.getBalances(client, player3.getAddress()))
  595. print("Lets transfer that asset to one of our other accounts... first lets create the vaa")
  596. # paul - transferFromAlgorand
  597. sid = self.transferFromAlgorand(client, player2, self.testasset, 100, decode_address(player3.getAddress()), 8, 0)
  598. print("... track down the generated VAA")
  599. vaa = self.getVAA(client, player, sid, self.tokenid)
  600. print(".. and lets pass that to player3")
  601. vaaLogs.append(["transferFromAlgorand", vaa])
  602. #pprint.pprint(vaaLogs)
  603. self.submitVAA(bytes.fromhex(vaa), client, player3, self.tokenid)
  604. pprint.pprint(["player", self.getBalances(client, player.getAddress())])
  605. pprint.pprint(["player2", self.getBalances(client, player2.getAddress())])
  606. pprint.pprint(["player3", self.getBalances(client, player3.getAddress())])
  607. # Lets split it into two parts... the payload and the fee
  608. print("Lets split it into two parts... the payload and the fee (400 should go to player, 600 should go to player3)")
  609. sid = self.transferFromAlgorand(client, player2, self.testasset, 1000, decode_address(player3.getAddress()), 8, 400)
  610. print("... track down the generated VAA")
  611. vaa = self.getVAA(client, player, sid, self.tokenid)
  612. # pprint.pprint(self.parseVAA(bytes.fromhex(vaa)))
  613. print(".. and lets pass that to player3 with fees being passed to player acting as a relayer (" + str(self.tokenid) + ")")
  614. self.submitVAA(bytes.fromhex(vaa), client, player, self.tokenid)
  615. pprint.pprint(["player", self.getBalances(client, player.getAddress())])
  616. pprint.pprint(["player2", self.getBalances(client, player2.getAddress())])
  617. pprint.pprint(["player3", self.getBalances(client, player3.getAddress())])
  618. # sys.exit(0)
  619. # Now it gets tricky, lets create a virgin account...
  620. pk, addr = account.generate_account()
  621. emptyAccount = Account(pk)
  622. print("How much is in the empty account? (" + addr + ")")
  623. pprint.pprint(self.getBalances(client, emptyAccount.getAddress()))
  624. # paul - transferFromAlgorand
  625. print("Lets transfer algo this time.... first lets create the vaa")
  626. sid = self.transferFromAlgorand(client, player2, 0, 1000000, decode_address(emptyAccount.getAddress()), 8, 0)
  627. print("... track down the generated VAA")
  628. vaa = self.getVAA(client, player, sid, self.tokenid)
  629. # pprint.pprint(vaa)
  630. print(".. and lets pass that to the empty account.. but use somebody else to relay since we cannot pay for it")
  631. # paul - redeemOnAlgorand
  632. self.submitVAA(bytes.fromhex(vaa), client, player, self.tokenid)
  633. print("=================================================")
  634. print("How much is in the source account now?")
  635. pprint.pprint(self.getBalances(client, player2.getAddress()))
  636. print("How much is in the empty account now?")
  637. pprint.pprint(self.getBalances(client, emptyAccount.getAddress()))
  638. print("How much is in the player3 account now?")
  639. pprint.pprint(self.getBalances(client, player3.getAddress()))
  640. print("Lets transfer more algo.. split 40/60 with the relayer.. going to player3")
  641. sid = self.transferFromAlgorand(client, player2, 0, 1000000, decode_address(player3.getAddress()), 8, 400000)
  642. print("... track down the generated VAA")
  643. vaa = self.getVAA(client, player, sid, self.tokenid)
  644. print(".. and lets pass that to player3.. but use the previously empty account to relay it")
  645. self.submitVAA(bytes.fromhex(vaa), client, emptyAccount, self.tokenid)
  646. print("How much is in the source account now?")
  647. pprint.pprint(self.getBalances(client, player2.getAddress()))
  648. print("How much is in the empty account now?")
  649. pprint.pprint(self.getBalances(client, emptyAccount.getAddress()))
  650. print("How much is in the player3 account now?")
  651. pprint.pprint(self.getBalances(client, player3.getAddress()))
  652. print("How about a payload3: " + self.testid.to_bytes(32, "big").hex())
  653. sid = self.transferFromAlgorand(client, player2, 0, 100, self.testid.to_bytes(32, "big"), 8, 0, b'hi mom')
  654. print("... track down the generated VAA")
  655. vaa = self.getVAA(client, player, sid, self.tokenid)
  656. print("player address: " + decode_address(player2.getAddress()).hex())
  657. print("payload3 vaa: "+ vaa)
  658. pprint.pprint(self.parseVAA(bytes.fromhex(vaa)))
  659. print("testid balance before = ", self.getBalances(client, get_application_address(self.testid)))
  660. print(".. Lets let player3 relay it for us")
  661. self.submitVAA(bytes.fromhex(vaa), client, player3, self.tokenid)
  662. print("testid balance after = ", self.getBalances(client, get_application_address(self.testid)))
  663. # sys.exit(0)
  664. print(".. Ok, now it is time to up the message fees")
  665. bal = self.getBalances(client, get_application_address(self.coreid))
  666. print("core contract has " + str(bal) + " algo (" + get_application_address(self.coreid) + ")")
  667. print("core contract has a MessageFee set to " + str(self.getMessageFee()))
  668. seq += 1
  669. v = gt.genGSetFee(gt.guardianPrivKeys, 2, seq, seq, 2000000)
  670. self.submitVAA(bytes.fromhex(v), client, player, self.coreid)
  671. seq += 1
  672. print("core contract now has a MessageFee set to " + str(self.getMessageFee()))
  673. # v = gt.genGSetFee(gt.guardianPrivKeys, 2, seq, seq, 0)
  674. # self.submitVAA(bytes.fromhex(v), client, player, self.coreid)
  675. # seq += 1
  676. # print("core contract is back to " + str(self.getMessageFee()))
  677. print("Generating an attest.. This will cause a message to get published .. which should cause fees to get sent to the core contract")
  678. sid = self.testAttest(client, player2, self.testasset)
  679. print("... track down the generated VAA")
  680. vaa = self.getVAA(client, player, sid, self.tokenid)
  681. v = self.parseVAA(bytes.fromhex(vaa))
  682. print("We got a " + v["Meta"])
  683. bal = self.getBalances(client, get_application_address(self.coreid))
  684. print("core contract has " + str(bal) + " algo (" + get_application_address(self.coreid) + ")")
  685. # print("player account: " + player.getAddress())
  686. # pprint.pprint(client.account_info(player.getAddress()))
  687. # print("player2 account: " + player2.getAddress())
  688. # pprint.pprint(client.account_info(player2.getAddress()))
  689. # print("foundation account: " + foundation.getAddress())
  690. # pprint.pprint(client.account_info(foundation.getAddress()))
  691. #
  692. # print("core app: " + get_application_address(self.coreid))
  693. # pprint.pprint(client.account_info(get_application_address(self.coreid))),
  694. #
  695. # print("token app: " + get_application_address(self.tokenid))
  696. # pprint.pprint(client.account_info(get_application_address(self.tokenid))),
  697. #
  698. # print("asset app: " + chain_addr)
  699. # pprint.pprint(client.account_info(chain_addr))
  700. if __name__ == "__main__":
  701. core = AlgoTest()
  702. core.simple_test()