From dacf99ee6e9be8e1a2ca04d3ed76bfefb792f0d6 Mon Sep 17 00:00:00 2001 From: Dreamf1re <106770791+Dreamf1re@users.noreply.github.com> Date: Sun, 12 Jun 2022 18:07:45 +0800 Subject: [PATCH 1/8] Create README.md --- HeadlineHackathon/Dreamf1re_hack/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 HeadlineHackathon/Dreamf1re_hack/README.md diff --git a/HeadlineHackathon/Dreamf1re_hack/README.md b/HeadlineHackathon/Dreamf1re_hack/README.md new file mode 100644 index 0000000..85a1284 --- /dev/null +++ b/HeadlineHackathon/Dreamf1re_hack/README.md @@ -0,0 +1,12 @@ +# Choice Texts +This is a web app, designed with PyWebIO, which aims to make text files permanent on the Algorand Blockchain. + +## Usage +1. Download this entire folder. +2. Install required packages using ```pip install requirements.txt```. +3. Edit Environment Variables -> add variable name "test_mnemonic"
with variable value which is your test mnemonic. +5. Run deploy.py through ```python deploy.py```. + +Running deploy.py will start a new tab in the user's default +web browser. All functionalities therein are intact and the user +can now start making text files permanent on the Algorand blockchain. From 01575c84f839e3b68dfa9dca937f4544261a7f4a Mon Sep 17 00:00:00 2001 From: Dreamf1re <106770791+Dreamf1re@users.noreply.github.com> Date: Sun, 12 Jun 2022 18:08:46 +0800 Subject: [PATCH 2/8] Added main files These are the main files of Choice Texts web app. --- HeadlineHackathon/Dreamf1re_hack/bridge.py | 31 + HeadlineHackathon/Dreamf1re_hack/checking.py | 40 ++ .../Dreamf1re_hack/choice_logo.jpg | Bin 0 -> 18559 bytes HeadlineHackathon/Dreamf1re_hack/constants.py | 4 + HeadlineHackathon/Dreamf1re_hack/deploy.py | 531 ++++++++++++++++++ HeadlineHackathon/Dreamf1re_hack/download.py | 155 +++++ .../Dreamf1re_hack/requirements.txt | Bin 0 -> 534 bytes HeadlineHackathon/Dreamf1re_hack/stitching.py | 44 ++ HeadlineHackathon/Dreamf1re_hack/upload.py | 242 ++++++++ HeadlineHackathon/Dreamf1re_hack/util.py | 317 +++++++++++ 10 files changed, 1364 insertions(+) create mode 100644 HeadlineHackathon/Dreamf1re_hack/bridge.py create mode 100644 HeadlineHackathon/Dreamf1re_hack/checking.py create mode 100644 HeadlineHackathon/Dreamf1re_hack/choice_logo.jpg create mode 100644 HeadlineHackathon/Dreamf1re_hack/constants.py create mode 100644 HeadlineHackathon/Dreamf1re_hack/deploy.py create mode 100644 HeadlineHackathon/Dreamf1re_hack/download.py create mode 100644 HeadlineHackathon/Dreamf1re_hack/requirements.txt create mode 100644 HeadlineHackathon/Dreamf1re_hack/stitching.py create mode 100644 HeadlineHackathon/Dreamf1re_hack/upload.py create mode 100644 HeadlineHackathon/Dreamf1re_hack/util.py diff --git a/HeadlineHackathon/Dreamf1re_hack/bridge.py b/HeadlineHackathon/Dreamf1re_hack/bridge.py new file mode 100644 index 0000000..f778b3f --- /dev/null +++ b/HeadlineHackathon/Dreamf1re_hack/bridge.py @@ -0,0 +1,31 @@ +from upload import upload, get_file_id +from download import download +from util import TEST_SENDER_ADDRESS, TEST_SENDER_PRIVATE_KEY, init_post_client + + +def upload_pdf(filename: str): + # Upload sample, returns a Transaction ID + # needed to retrieve the file from the blockchain + print("Procedure: Upload file to blockchain.") + txnids = upload( + filename=filename, + sender_address=TEST_SENDER_ADDRESS, + sender_private_key=TEST_SENDER_PRIVATE_KEY + ) + fid = get_file_id( + transaction_ids=txnids, + receiver_address=TEST_SENDER_ADDRESS, + sender_address=TEST_SENDER_ADDRESS, + sender_private_key=TEST_SENDER_PRIVATE_KEY, + post_client=init_post_client(), + filename=filename + ) + print(f"File ID: {fid}") + return fid + + +def download_pdf(file_id: str): + # Download sample, saves to current directory + print("Procedure: Download file from blockchain.") + filedld, filedldname = download(file_id=file_id) + return filedld, filedldname diff --git a/HeadlineHackathon/Dreamf1re_hack/checking.py b/HeadlineHackathon/Dreamf1re_hack/checking.py new file mode 100644 index 0000000..4e8d66d --- /dev/null +++ b/HeadlineHackathon/Dreamf1re_hack/checking.py @@ -0,0 +1,40 @@ +import hashlib + + +# Vital function to check if +# the uploaded file and downloaded +# file are exactly the same. This +# is achieved through checking if +# the hash of the uploaded data is +# equal to the hash of the +# downloaded data +def check_circular(original: str, stitched: str): + """ + Check if hashes of Original File and Downloaded File are + the same. This is to check if the uploaded file is complete + and is the same with the original file that has been uploaded. + + :param original: The original file. + :param stitched: The downloaded file. + :return: Returns True if original and downloaded hashes are the same. + """ + print(f'\nChecking circularity...') + stitched_hash = hashlib.md5(stitched.encode()).hexdigest() + original_hash = hashlib.md5(original.encode()).hexdigest() + if stitched_hash == original_hash: + print('Achieved circularity.') + return True + else: + print(f'Length of original: {len(original)} Hash: {original_hash}') + print(f'Length of stitched: {len(stitched)} Hash: {stitched_hash}') + print(f'Circularity not achieved. Trying again.') + + +# Check if the expected Transaction ID +# location in the note is all in uppercase +def check_if_connection_exists(note: str): + targ = note[(len(note)-1)-51:len(note)] + if targ.isupper(): + return True + else: + return False diff --git a/HeadlineHackathon/Dreamf1re_hack/choice_logo.jpg b/HeadlineHackathon/Dreamf1re_hack/choice_logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ea3bb675de896f42cb1f478747cd713d4afbeceb GIT binary patch literal 18559 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 z zFYE35gzA!IC_RR~Vml1s`Y{wId|0wd!x%NDKOq^e`VnqZ7IAJ5#e>)6fCV-bkD>>h zjLj$+4z@^(O&20aP0CGWT%hVuqCH^rKj7fRIon;gwNxRCm+HKS@pCexdBVA zRwP05-cFDt+#Oc#cVX635PHSCjlq2h>PGbu=%TPmvb|`M02k$ONAezI>nU^BM_$tU zAYqKeN%`_fYr1?lO^q+*izh)>idsC|lU}q*!!8A*2X2xg;KeCxk^|kz-*0nyfhM~h zji}>^O6{L^Vib(-K9)IsOdTcbrh+u##EH#B5OiW8z@CynDfgB6IW_<2t9pFz=7=JcCm2(0Ex!K zCiIa*_~0XEbcFsPO2d^fg1AJSl%aIvY6pLI){F#4QUFci#8mB*Jjo1BrE#v=@E(=y zU``p+hkRvlEaBh^GPoc$F6pahmLO#X$3WmowRp^OigKuAMKJ(+$dT*j^I&y3PwJe& zh7xXjWpM^I7ldth1m|xaDzjM3VylcHqaxItd?o2-Vyg_;@-#dX1~3u;wUbaF1lB1IweR z&;js9W?j@*c;+7uTZTbV?@D1>4C!LYD-b=5H-@|go9RLJm8IyMjHGe!4PKV|V)C;n znOZM&eS0V;$bm?U@lUqM?xnx#Zy)e`|k2S@kgQRYw-I zPhS-e?mM6saaW#kDi4>vI`{ zo#GPZBlJTX{edU#)dLB_X<5{)xZ>S5roSn0!BIW^$7ORb#}qh(X$x5%JdAAJsB&C` z*D5Qbw(lgt&ZKzeCyEFqXenk*R)m!;sEF;9-1|^M>T6S_J;XvLV-{~iA-O{?0Y=ae zbLKjqMtO|Y^u;jq_L$lD6`$k*?*Nt;5&$Bp5gQPppb3}F8%p8BSlUeU0s0{9ZQa|5|=wbtk zJs~^n%s&s+OeD&c`~x`+I4l&5D7>rAR&lbR?3X@1Dq_kDv8p5^XjbZ6rj*(8>k_dP zjjcq5ON#ztF{$z!1x|0CZTI1lykk9!J@`f_k$E61=?Q=e2(>eA`199dOR>R&lLFW_ z&qN`Bg=KisqNfqnp##tES!w?zKRW5C+)&)dDfE#&+%{fROXWh?Khg=B@Tx_!{bDg6 zYWG7hIFJ&I4g1E>I`rzf;6;8Aewkkg2iz>Bp9wTSBJzL8@OzRV1$>|= zB-xa*B8{2|=3`{m4hjA(Fkapk3jIu-WM1m7K!{O!(2fnx)JU;m1!%-1K&R!V@R#Ws z^?$CTxtfak4yFOf#ybjEOH(_(T#3LpQx%S&MEBvt2s#YrBAe5wYIrYydXFiR7U z(*6xBf8)fVzIyf7nn0Qlgbm({M&luIRwsrmJc*|~3N92tTq<7~%b>Z`c*;a{YXyW>L2~^n*VE zU8q5Guq2?EUmuw4^biv>U`G%#*$LZqtjczTH*#b%jBSTFpidmDQmoe_wS-4Zx@;6~ zmZlUJA}9?be>QCNqjr24ZLf$|g~-I5o>!*8LZGD{R^>97#Hj+fVv9Tl_sz!$pT*)MoQwQngZq-z(NsZ6Ah#s2U;|#@i3{M zo?$e&PxnWAefDr4Rei!FsfT##C};@80V8yT-DsgO?z~Z)F{i5b>Qg~$e1ZS&6$k^z zC4L&H!kYhuf@!taoP}FeRke~b%O!ap-O~k1W)}{XCY`osl+)dycSBF>m>Gz4^d;Uq zd<-*%Za0F(qSrgN%Md_DK&JJPjL8@>hlx1oD{O%QH;FTsxZADCkrRv+HsT30-xu}D zj0|kpOJuHUexzsTJ8~mkjxTMa2)(F^##|5wuCphB5iofH(vXsh5h&_L;yO*cOkPI! zq|Udv&=54dh>bHlz(l;m3l7NvfK@s5*zqA!Ah{wSHY7P2$*56&O+`NQ$xgiqchb@62&NXCzQX zi?gvLW1NyTw||B=#K^O z2Frla^q7M-!h=;-t|4M4A(F#xhm$tmAWf`{(icx+fB>R0L~Ee6k}WQ(nnqex(vOV6 zgZ*St8r%s41+5FLHiuJfJn|ImF~KrHX{*(~dnRpS#a`P%ivgE#+Q(caPCOVMeg_mLbz1Ttzu}U~PI4=iGR>Yj z#iLQP>5CO6O~?J3sAkViE>331P1J#Mh(#9*?-^J4`4nKD<$9e9WFcW5EgjDlg#dfg zMGQ7h02!|!!}njo*?;5GQLktC{~tbI-2Z1}E;0&b@*t7_WDemXh7B643GE8~7u)fp zH?vHNo@qEC66SyE>a+j&yR?VlOczi6;ll3^ZEp$-Wm5!#JJLdmX;yaAA2bX>r>;p+ z^yk9i5_J9_u=OCcrfGfmJwn7Fn4HR^A|NTk4F84x?7)GD6UobbO^v`A6pRx8h4X_yk1;Yv%=`dHva1gCGami=fHL_Y?Y7q{2`j48WEEkACCZN-~8OKM_)-OTd!xJ z26VDLS%MN!IQ=bsMgS4MGyI6)3|VY8;C$Fh&LFc3nsA2y%1~b5H)1{s@eq6<4G#A^ zz!B6?aJ(?~&w(jL#>f|J=&%(pfBG{*b_dcW6oF?8Zh1El&Yw@usOUmBU;c_s!;h|i za;_&xkd@cJ5M*`U|E2nicKs~O8?sQk{V%G2Ft7e8`oF(}HaD~&x8&ah2L9JC1OT~) zfTXCbA}S;-#?CAq_?9FWK7tIraf~52B zRaZg{udC01I@|kcr4dTg6T_c-i@Q)0=Q6eEnP$axxzCz*uf(SKlnpz>BKV88Z;3F5 zhlF}9^6*J`aM zQOK2L1HpOA-aW5jcZAQ7ok&1IdFMx#aPGTU5?QDeAP1uPXM7rIyp{;(`d_-3_H0lO zB`RL_WKotU0l*6T-K<#Dvk-6X)%X;i#n?uQISDi`Qp%j-lu3O411!w6#^A7qZg?wK z7U?uvW;CtPF>zTxQtUB$yn^V(P=}#vhM!WxtE}L&W0Kzp3za8Q50KTwqD4FKpw-vN z5mYI(X6NHWkiVH1*;)4lhcgL@mPgcN=d=6(#X*B>5mmjFG=YnlQqV znkgE?iQbkF@DJ`XJ;;!|@M4Ka+;QOyeV1Y23@ub1a1n@<`c}m&Q_w&W+UJQ@clyV3 z_}6U>34j7ck^YSz{i`4U+Wa>3u{bBr2^S~_8SVb#8vNt7Mt$=U!g@$yveUTEp!~fR zEtbkZ=C69%qLW6St4XdjSX_c74PTrV6Rc4GriFq!4DcuAU47@_P&cb8Bn&P*~3oI zuw}AHm%^m`Z&oIa&IC1e7In_mh$j@FR9}maI>~m2T1~MbE+SrPxA_B2CG-On&-l znCTD28J&`P~8|vBxDPeKap}8l(5YLXyAl?EBi1`FLdq{{UK>O;NczMhmCoT(yC`q{$W$9!jAikLF!`~d2H<6I;<Q1iFgxk2CK%X$ zNf%Bol$UNJyBfV+gYz^yCR5l7MBYDGV%W{E{Bmzzx0o|n&ZanUZR=N;W~>Ga-f+nz30tk`^5iAbik6cMkGS0rbm&sfntb%{#e0iUL=w9Stu4kE`Z_(K+3kY*_a!`%Sx7NJ3pGT~|V-2CaG z0`(!P0njr}49cCdooj|biSNW(>v{3QjhOC2p>tg3sn0;+qrrgOV6*L7)0eoaH00*%PkA92*n(cm)>ljZlSz!53iu=YlSgKB-oB$I4Ap~#F_^;28v6LtG{ z3q9gW!SC~>z-uCgpPmw(;wkm8Q~C3oNP%1r`Vx|1|;lKhESZ9jruLn|4{c1nd=WGj#Qzy_EFsF0%5v%wZ8TRpfOe>8EYl#bM%c&de4?7_HC5V1 zC&c-cO0#?+Vd9G7bPG|F&C8GO_}c}mlF!@7pir~SLFYlR?kI>vb$5G zNBbVBSQ4?9EAYt{FXb>+jt>4Xj?hML+Gzn1x!YQ4W?zMZlX)cfsl6pI&M!_R-wt(l zm(Dd!D_T#A#x^@Hi^*q8oZ}U%MKgTKrKWbN8KdCV&~Z&=^Z_UhjGKh09MS1__+r@3 zW1u{NWV9kbW7gi?1<%WqW7+EJbQWQE18eQqLO-vLn<{(F=P z4KT)_7x`Ll;?3i?hx-|4J+pDdDWf~yL|)OGwPW_ZEp1l@36Ap(@17`EYMDhg6^8_6yWNL0=`GKd@}{u&X0 z|G5Nfk5P0nc*FMeo6_X=MILz|WU#e**k-r54=3D@SxOSt#B<&aNFNk<%GD|CX@R}7 z6>!iadioLrQIsA`lpf^Q*(0+OL%sH@YJ=8g$YkM1<0v^FFD27PK(qt|%+@W|h*6~? zd(A0gj{un_6bCf>=#RBiz1im-8Y>PzsrhFcx>jq}4WKyrL2}tbYKOa+OXT3o!{LOR z=hZs6u|HvwCxb%TJ?S)BlQuo$Y5S-9#x++aS0f4Ny*)**qvQO_7VcATdjSPiQP_uj>KuN785kryt+^6#w`y&l{RFR9DCd+tyc`_a*YQdmjvh7d&i;k z>R=^p>mUw36%Wn}Mn7Ji*qYo43j|>mf9>hAYx%r5wzsXyOwm?Uemow5W26iP_Jo{t z%&0Q=WpLS8frmHtm&*^rIT+xvdk;3PQ^OQ&Bp?D{&vYWL@S_uE{a0v_96hQC6-2SLgLDt*X$d>=Wr4 ztNF&&5HWi98f_x9QPVLGp;&S!@!L8k83ap;;@^*nGq6 z%Y{SdH)t|zcOhO8N#9}NF;vRWHHIp)?c1|>(UMR`J^2#pG0w?XxON9u1u%@y`0zk| zL&H(E6C19{c$~=Z*Cm%Ln}A$BFVe%!dNttod}xwzH{8N@nFDcjgl8HSF@z%iNzwX= zhiVkys+BF~Cwce}=eL%RRN8xwUx>w^2aI+<`NxRNdfX8Z@jyVvWgPDq+p$85tGT`cfa~eP zI!}RL+*webx2h1jP3tA_1~4}JaP8*XMGH%!emVB8Bhhla0}Oa>rZ?1pg5;WvTIdZm zJHdIhX}9$qCrem)0XkVT-CxEpbcs;u`Z8kz96w7bp-=0Z&jusZE(-}035k$&%$%o? zIS&}JJY@zBx@Yk?m@Kzbo(d`r_+Jv5Jh0t-Vh!pp9{vgz*w@P#vhx^sF{Knh?q9FK z`pDLHPGv$cwDMa! zv^DypcdxyfbdWlw*xuIw0S4i7J|1yCtzq!zm#KDo1J$8pxa#pK;ZcVZ*Q`o?9Pdp- zu<*i@SK^0~?nby{PkwOFW^y(jr*lF2Yd5&##kyBhij=!1Fh^lyBXieGI>#D|3(K(i%ozq^t*pA@6*fN9S2TDm$;)`?FF>cBbpaMf zxUe2TD$~=o=uK3=a5FF<4|I5z-cQ@kqDi#Cy`jCpTAEx_D!5;7gPR;~{XYV01eE(B zmD=#}Eo%!xI9`#sQJ!0XJjeJeANdCkgY=6Uj3Hom1`}XzWfaEJibs<(sZJ1KPLW6b zUME7@j;F&$2!OSqEUzL~KKYpVZG1|Kgf|F_S|%>fTqW*zUsz1+ zL@?lo9Yc#!v=IQOGo1F|=n?%*XPyUqHz{oZ1y;0_$1pFKHvJR+oMcz(lOZKhe7WGf zDj3}!W~8t}eVGl|QdV`t&*{K8eOdx-z)im*tS|d$S2IX`I>3f81}t+s5vM(KLv@97p0=avxYE zfum9Pnpkx_BOeD|3*OV4=)doF=Pl2(fHpmefSvYU^5(T0oBse4u49n}bgFU=nlF|$ zVQ-L10MHNEvsILJ(Z}LV$oz7$Hv@I`qnHr(M19i!2Qn%-$eK%0QjLr1Ki6n{KF0Eo z#}_b!;a>xS2*t98%fCoOO1kxRPw%urnL^HL>l_O^4C^y361V}N6!G_KV{Klb0GkQT z=DM!yHvOJlO6&ZmzW(&id2D+`-1{_UL7@`Juv{)&8CMGBuin4Bhlz0cqnF_z;eFaa zuQ_SIXZnc0Y% zjF0B-TWa+KYs}p?=I`x$%}@(pMIM?v$5hYR&&Im#d@#LFh`nxC9iwgd7e3LhdX>}$ zg)_y$it&0%`>S_)x=0zxnF+(&u+$L!AY%URpE)CWZR~I^HjAEux@5 zmECgw;;?_Hxg#>4eqip7j(f%|3KyD)u12Gp7Z!>NfVEl(}A`x?Q0Nu`Q2Sb#2 zK~|5Aj;)B=Fz`<CT+#L*-@NhY>k1h8HCslF!p#XH31ca+BGr4ab6A4#e_v^!744KcBUl)>R>os#^6sz9o`C>Y@qP-nI7Z11-?M|JFKk)TC zSE|HdVbt6GK&h)#gxxO>q9{oJ0H`n_gsdU%d=w-4DWpzKH4AxG4Cx&}Y;Y9N%)`IG)+sbBmf|Jke(!EFEl literal 0 HcmV?d00001 diff --git a/HeadlineHackathon/Dreamf1re_hack/constants.py b/HeadlineHackathon/Dreamf1re_hack/constants.py new file mode 100644 index 0000000..03ab12e --- /dev/null +++ b/HeadlineHackathon/Dreamf1re_hack/constants.py @@ -0,0 +1,4 @@ +ALGONODE_NODE_ADDRESS = "http://testnet-api.algonode.network" +ALGONODE_INDX_ADDRESS = "http://testnet-idx.algonode.network" +ALGOEXPL_NODE_ADDRESS = "https://node.testnet.algoexplorerapi.io" +ALGOEXPL_INDX_ADDRESS = "https://algoindexer.testnet.algoexplorerapi.io" diff --git a/HeadlineHackathon/Dreamf1re_hack/deploy.py b/HeadlineHackathon/Dreamf1re_hack/deploy.py new file mode 100644 index 0000000..2fdf2c7 --- /dev/null +++ b/HeadlineHackathon/Dreamf1re_hack/deploy.py @@ -0,0 +1,531 @@ +# In production, an escrow account must be newly generated and +# funded to delegate uploading of .txt files. A user must pay +# the fees required to upload the file +# In this demo, a test account is set and is pre-funded +# to show functionality. + +from upload import * +from pywebio.input import * +from pywebio.output import * +from pywebio.session import * +from pywebio.platform.tornado import start_server +from qrcode import QRCode, constants +from cv2 import cv2 +import base64 +import os +from util import TEST_SENDER_PRIVATE_KEY, \ + TEST_SENDER_ADDRESS, search_note_by_txid, get_lines, init_get_client +from checking import check_if_connection_exists +from PIL import Image + + +class Veritas: + def __init__(self): + self.sender_address = None + self.original_file = None + self.alpha_fn = None + self.alpha = None + self.filename = None + self.transaction_ids = None + self.receiver_address = self.sender_address + self.sender_private_key = TEST_SENDER_PRIVATE_KEY + + @use_scope("upload") + def to_blockchain(self): + self.sender_address = TEST_SENDER_ADDRESS + self.receiver_address = self.sender_address + # Remove scopes + remove("download") + remove("selector") + remove("manage") + put_button( + label="Reload", + onclick=self.reload_page + ) + # Show file upload + file = file_upload( + label="Find your text file", + required=True, + accept=[".txt"] + ) + # Write + self.filename = file['filename'] + open(self.filename, 'wb').write(file['content']) + obtained_from_local = open(self.filename, 'rb').read() + while True: + if (self.filename and obtained_from_local) is not None: + break + # Await payment from user before uploading + # Compute cost - A payment of choice can also + # be included on top of the base cost + cost = self.compute_cost(self.filename) + put_text(f"Estimated Cost: {round(cost, 5)} ALGO") + # Start upload + file_id = self.custom_upload( + filename=self.filename, + sender_address=self.sender_address, + receiver_address=self.receiver_address, + sender_private_key=self.sender_private_key + ) + put_html( + f""" +
+ Successfully made {self.filename} permanent.
+ Scan this QR Code to get the file.
+ Transaction: {file_id} +
+ """ + ) + # Add data to QR Code and make + basewidth = 100 + choice_logo = Image.open("choice_logo.jpg") + + wpercent = (basewidth / float(choice_logo.size[0])) + hsize = int((float(choice_logo.size[1]) * float(wpercent))) + choice_logo = choice_logo.resize((basewidth, hsize)) + qrc = QRCode( + error_correction=constants.ERROR_CORRECT_H + ) + + qrc.add_data(file_id) + qrc.make() + qrimg = qrc.make_image( + fill_color="black", back_color="white").convert('RGB') + pos = ((qrimg.size[0] - choice_logo.size[0]) // 2, + (qrimg.size[1] - choice_logo.size[1]) // 2) + + qrimg.paste(choice_logo, pos) + + # Save QR Code + qr_code_saved_fname = f"{self.filename}-QR.png" + qrimg.save(qr_code_saved_fname) + + # Open QR Code and display + with open(qr_code_saved_fname, 'rb') as qrimg_: + __qr__ = qrimg_.read() + if __qr__ is not None: + put_image( + src=__qr__, + format="png", + title="QR Code", + width="185", + height="185", + ) + put_file( + name=qr_code_saved_fname, + content=__qr__, + label=f"Download QR" + ) + put_link( + name="See on Algoexplorer", + url=f"https://testnet.algoexplorer.io/tx/{file_id}", + new_window=True + ) + os.remove(self.filename) + os.remove(qr_code_saved_fname) + + @use_scope("download") + def from_blockchain(self): + # Remove scopes + remove("upload") + remove("selector") + remove("manage") + put_button( + label="Reload", + onclick=self.reload_page, + position=0 + ) + # Get output + qrcode = file_upload( + label="Locate QR Code.", + accept=".png", + required=True + ) + pb_download = "downloadprog" + put_processbar( + name=pb_download, + init=0, + label="Setting the truth free...", + auto_close=True + ) + filename = qrcode['filename'] + open(filename, 'wb').write(qrcode['content']) + obtained_from_local = open(filename, 'rb').read() + while True: + if (qrcode and obtained_from_local) is not None: + break + # Decode QR + qr_ = cv2.imread(filename) + qr__ = cv2.QRCodeDetector() + file_id, _, _ = qr__.detectAndDecode(qr_) + # Initiate download + # Initialize stuff to be used later + get_client = init_get_client() + remnant = [] + first = True + connection = None + fno = None + file_name_decoded = None + # Repeat until there is no Connection left. + # A Connection is the Transaction ID + # included at the end of a note to serve as + # a link to the preceding note. + set_processbar( + name=pb_download, + value=0.25, + label="Setting the truth free..." + ) + while True: + if first: + gotten = search_note_by_txid( + get_client=get_client, + txid=file_id + ) + first = False + else: + gotten = search_note_by_txid( + get_client=get_client, + txid=connection + ) + if gotten != "": + has_connection = check_if_connection_exists(gotten) + # Check if a Transaction ID is + # expected to be found in the note, + # thus hinting that there is a preceding + # note. if "" is found in the note, + # it means that the there are no more + # preceding notes. + if has_connection and not ("" in gotten): + connection = gotten[(len(gotten) - 1) - 51:] + actual = gotten[:(len(gotten) - 1) - 51] + remnant.append(actual) + else: + actual = gotten[:] + remnant.append(actual) + break + else: + break + # Arrange the reference line + # to link other Transaction IDs + set_processbar( + name=pb_download, + value=0.25, + label="Setting the truth free..." + ) + remnant.reverse() + omega = "" + for particle in remnant: + omega += particle + if "" in particle: + sidx = particle.index("") + idxstart_of_fn = sidx + 4 + idxend_of_fn = idxstart_of_fn + # Get index of end of filename + while particle[idxend_of_fn] != "<": + idxend_of_fn += 1 + # Get filename + fno = particle[idxstart_of_fn:idxend_of_fn] + file_name_decoded = base64.b64decode(fno.encode()).decode('iso-8859-1') + print(f"File name: {file_name_decoded} ") + print(f"File ID: {file_id}") + print(f"File description: ") + # An algorithm can be inserted here to get + # the file description if there is one included + set_processbar( + name=pb_download, + value=0.50, + label=f"Getting {file_name_decoded}..." + ) + filename_whole = f"{fno}" + if filename_whole in omega: + omega = omega.replace(filename_whole, "") + else: + print("Cannot edit omega") + transaction_ids = get_lines( + note=omega, + max_length=52 + ) + while True: + try: + # Get Transaction IDs from the + # Transaction IDs obtained from the File ID + txn_ids = get_txn_ids_from_txn_id( + __txids=transaction_ids, + client=get_client + ) + set_processbar( + name=pb_download, + value=0.75, + label=f"Getting {file_name_decoded}..." + ) + # Download the file from the blockchain + downloaded_file = stitch_records( + get_client=get_client, + txn_ids=txn_ids + ) + set_processbar( + name=pb_download, + value=0.85, + label=f"Getting {file_name_decoded}..." + ) + break + except Exception as err: + print(err.args) + set_processbar( + name=pb_download, + value=1, + label=f"Getting {file_name_decoded}..." + ) + filedld = downloaded_file + filedldname = file_name_decoded + # End download + put_text(f"Successfully obtained {filedldname}.") + put_file( + name=filedldname, + content=base64.b64decode(filedld).decode().encode("ISO-8859-1"), + label=f"Download file" + ) + put_text("Text:") + put_scrollable( + base64.b64decode(filedld).decode().encode("ISO-8859-1").decode(), + height=800 + ) + os.remove(filename) + + @use_scope("selector") + def selector(self): + remove("init") + remove("upload") + remove("download") + # Add buttons + put_button( + label="Make something permanent", + onclick=self.to_blockchain, + ) + put_button( + label="Retrieve document", + onclick=self.from_blockchain, + ) + + def root(self): + remove("main") + remove("connect") + remove("init") + put_scope("main") + put_scope("connect") + # Set title + set_env(title="Choice Texts") + # Introduction + choice_logo = open("choice_logo.jpg", "rb").read() + put_grid( + [ + [ + put_html( + """ + +

+ Immutable, transparent
+ records on the blockchain
+

+

+ Choice Texts allows you
+ to make text files permanent
+ on the Algorand public blockchain.
+
+ Now there is a way to preserve
+ our stories for generations
+ and generations to come. +

+ + """, + ), + put_image( + src=choice_logo, + format=".jpg", + width="250", + height="250", + title="Choice Logo" + ) + ] + ], + scope="main" + ) + + with use_scope("init") as init: + put_button( + label="Begin", + onclick=self.selector, + scope=init + ) + + def custom_upload( + self, + filename: str, + sender_address: str, + receiver_address: str, + sender_private_key: str + ): + # Init POST Client + post_client = init_post_client() + # Init process bar + process_bar_name = "uploadprog" + pbvi = 1 / 7 + put_processbar( + name=process_bar_name, + init=0, + label="Making sure things will never change...", + auto_close=True + ) + # Open file + # ( Progress - Task #1 ) + set_processbar(name=process_bar_name, value=pbvi) + with open(filename, 'rb') as o: + self.original_file = o.read().decode('ISO-8859-1') + self.original_file = base64.b64encode(self.original_file.encode()).decode() + if self.original_file is not None: + + # Get Transaction IDs submitted to the blockchain + # ( Progress - Task #2 ) + pbvi += pbvi + set_processbar( + name=process_bar_name, value=pbvi, + label="Making sure things will never change..." + ) + self.transaction_ids = process_publishing( + feed=self.original_file, + receiver_address=receiver_address, + sender_address=sender_address, + sender_private_key=self.sender_private_key + ) + # Initialize GET Client + get_client = init_get_client() + # Loop until downloaded data is exactly + # the same with the uploaded data + # ( Progress - Task #3 ) + pbvi += pbvi + set_processbar( + name=process_bar_name, value=pbvi, + label="Making sure things will never change..." + ) + while True: + # Get Transaction IDs from Transaction IDs + txn_ids = get_txn_ids_from_txn_id( + __txids=self.transaction_ids, + client=get_client + ) + # Download the uploaded file from the blockchain + downloaded_file = stitch_records( + get_client=get_client, + txn_ids=txn_ids + ) + # Check if the uploaded file + # and downloaded file are the same + # and return Transaction IDs from + # upload procedure if so + circular = check_circular( + original=self.original_file, + stitched=downloaded_file + ) + if circular: + print('File successfully uploaded to blockchain.') + break + # ( Progress - Task #4 ) + # Get File ID and second cost + pbvi += pbvi + set_processbar( + name=process_bar_name, value=pbvi, + label="Making sure things will never change..." + ) + print(f"Assigning File ID...Please Wait.") + alpha_fn = base64.b64encode(filename.encode()).decode() + alpha = f"{alpha_fn}" + for txid in self.transaction_ids: + alpha += txid + # ( Progress - Task #5 ) + pbvi += pbvi + set_processbar( + name=process_bar_name, value=pbvi, + label="Making sure things will never change..." + ) + feed = get_lines( + note=alpha, + max_length=947 + ) + txid = None + # ( Progress - Task #6 ) + pbvi += pbvi + set_processbar( + name=process_bar_name, value=pbvi, + label="Making sure things will never change..." + ) + for each in feed: + if len(each) != 0: + if len(feed) > 1: + if txid is None: + txn = create_transaction( + post_client, + receiver_address, + sender_address, + message=each + ) + else: + txn = create_transaction( + post_client, + receiver_address, + sender_address, + message=each + txid + ) + sgd = txn.sign(sender_private_key) + txid = post_client.send_transaction(sgd) + transaction.wait_for_confirmation( + algod_client=post_client, + txid=txid + ) + else: + txn = create_transaction( + post_client, + receiver_address, + sender_address, + message=each + ) + sgd = txn.sign(sender_private_key) + txid = post_client.send_transaction(sgd) + transaction.wait_for_confirmation( + algod_client=post_client, + txid=txid + ) + # ( Progress - Task #7 ) + pbvi += pbvi + set_processbar( + name=process_bar_name, value=1, + label="Making sure things will never change..." + ) + return txid + else: + raise Exception("Error: original file is {None}") + + @staticmethod + def reload_page(): + run_js("window.location.reload();") + + def compute_cost(self, filename): + with open(filename, 'rb') as o: + self.original_file = o.read().decode('ISO-8859-1') + self.original_file = base64.b64encode(self.original_file.encode()).decode() + lines = get_lines(self.original_file, max_length=947) + algocost1 = len(lines) * 0.001 + groups = len(lines) / 16 + algocost2 = groups * 0.001 + total_cost = algocost2 + algocost1 + return total_cost + + +if __name__ == "__main__": + v = Veritas() + start_server( + applications=v.root, + port=0, + host="", + debug=True, + auto_open_webbrowser=True + ) diff --git a/HeadlineHackathon/Dreamf1re_hack/download.py b/HeadlineHackathon/Dreamf1re_hack/download.py new file mode 100644 index 0000000..bed3128 --- /dev/null +++ b/HeadlineHackathon/Dreamf1re_hack/download.py @@ -0,0 +1,155 @@ +from util import init_get_client, search_note_by_txid, get_lines, get_txn_ids_from_txn_id +import base64 +from checking import check_if_connection_exists +from stitching import stitch_records +from pywebio.output import set_processbar, put_processbar + + +# Main download procedure +def download(file_id: str): + """ + Download file from blockchain using the + File ID generated from upload procedure + + :param file_id: The link which is a File ID generated from upload. + :return: None, after downloading, the downloaded file will be in the same directory. + """ + # Initialize stuff to be used later + process_bar_name = "downloadprog" + put_processbar( + name=process_bar_name, + init=0, + label="Setting the truth free...", + auto_close=True + ) + pbd = 1/4 + set_processbar( + name=process_bar_name, + value=pbd, + label="Setting the truth free..." + ) + get_client = init_get_client() + remnant = [] + first = True + connection = None + fno = None + file_name_decoded = None + # Repeat until there is no Connection left. + # A Connection is the Transaction ID + # included at the end of a note to serve as + # a link to the preceding note. + pbd += pbd + set_processbar( + name=process_bar_name, + value=pbd, + label="Setting the truth free..." + ) + while True: + if first: + gotten = search_note_by_txid( + get_client=get_client, + txid=file_id + ) + first = False + else: + gotten = search_note_by_txid( + get_client=get_client, + txid=connection + ) + if gotten != "": + has_connection = check_if_connection_exists(gotten) + # Check if a Transaction ID is + # expected to be found in the note, + # thus hinting that there is a preceding + # note. if "" is found in the note, + # it means that the there are no more + # preceding notes. + if has_connection and not ("" in gotten): + connection = gotten[(len(gotten)-1)-51:] + actual = gotten[:(len(gotten)-1)-51] + remnant.append(actual) + else: + actual = gotten[:] + remnant.append(actual) + break + else: + break + # Arrange the reference line + # to link other Transaction IDs + pbd += pbd + set_processbar( + name=process_bar_name, + value=pbd, + label="Setting the truth free..." + ) + remnant.reverse() + omega = "" + for particle in remnant: + omega += particle + if "" in particle: + sidx = particle.index("") + idxstart_of_fn = sidx + 4 + idxend_of_fn = idxstart_of_fn + # Get index of end of filename + while particle[idxend_of_fn] != "<": + idxend_of_fn += 1 + # Get filename + fno = particle[idxstart_of_fn:idxend_of_fn] + file_name_decoded = base64.b64decode(fno.encode()).decode('iso-8859-1') + print(f"File name: {file_name_decoded} ") + print(f"File ID: {file_id}") + print(f"File description: ") + # An algorithm can be inserted here to get + # the file description if there is one included + pbd += pbd + set_processbar( + name=process_bar_name, + value=pbd, + label="Setting the truth free..." + ) + filename_whole = f"{fno}" + if filename_whole in omega: + omega = omega.replace(filename_whole, "") + else: + print("Cannot edit omega") + transaction_ids = get_lines( + note=omega, + max_length=52 + ) + pbd += pbd + set_processbar( + name=process_bar_name, + value=pbd, + label="Setting the truth free..." + ) + while True: + try: + # Get Transaction IDs from the + # Transaction IDs obtained from the File ID + txn_ids = get_txn_ids_from_txn_id( + __txids=transaction_ids, + client=get_client + ) + # Download the file from the blockchain + downloaded_file = stitch_records( + get_client=get_client, + txn_ids=txn_ids + ) + # Return if finished + return downloaded_file, file_name_decoded + except Exception as err: + print(err.args) + + +# Writes data to disk +def write_to_file(input_data: str, file_name_out: str): + """ + Write the downloaded file from blockchain to disk. + + :param input_data: The downloaded data from blockchain + :param file_name_out: Filename of output file + """ + with open(file_name_out, 'wb') as f: + to_be = base64.b64decode(input_data).decode() + f.write(to_be.encode("ISO-8859-1")) + print(f'\nDownloaded {file_name_out} to current directory') diff --git a/HeadlineHackathon/Dreamf1re_hack/requirements.txt b/HeadlineHackathon/Dreamf1re_hack/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..dbe3af128d4ad20c224c7b21c63878e69a0e96d4 GIT binary patch literal 534 zcmY+B%}&EW5QBY2;!%p407V=)^^W=qA%TL(Pe~d|Umo~8OA?{g>|yP(J+nXGrCvC1 zdf;TbqHej@y3~bAb*iaeg&NA7HRw&08I1<*bpR{%K5Hb7@m3?8gU&OSE1$bNkY_lh zKD7$gQLp&5!M2ODxHfoNZ%5KPGM$2R50+9JwaJ9#%!~c-QkELnHzZt!wJsvHZLpY_0r+@ziRu@fp literal 0 HcmV?d00001 diff --git a/HeadlineHackathon/Dreamf1re_hack/stitching.py b/HeadlineHackathon/Dreamf1re_hack/stitching.py new file mode 100644 index 0000000..2080440 --- /dev/null +++ b/HeadlineHackathon/Dreamf1re_hack/stitching.py @@ -0,0 +1,44 @@ +from util import search_note_by_txid +from algosdk.v2client.algod import AlgodClient + + +# This function is to assemble +# the notes from each transaction +# in the upload procedure to +# produce the same uploaded file +def stitch_records( + get_client: AlgodClient, + txn_ids: list, +) -> str: + """ + Stitches notes from raw Transaction IDs + obtained from the upload procedure + + :param get_client: AlgodClient (GET) + :param txn_ids: Transaction IDs from upload procedure + :return: stitched - the Stitched Records (string) + """ + stitched = "" + stitched_initial_list = [] + for specific_txid in txn_ids: + while True: + # Search note based on given Transaction ID + obtained_note = search_note_by_txid( + get_client=get_client, + txid=specific_txid + ) + if obtained_note is not None: + # Append to list if note is found + stitched_initial_list.append(obtained_note) + now = txn_ids.index(specific_txid)+1 + mot = len(txn_ids) + num = round((now/mot)*100, 3) + print(f"\r({num}%) Stitching... ", end="") + + break + # Once all the notes are obtained, + # place them all in one single + # string and return + for sil in stitched_initial_list: + stitched += sil + return stitched diff --git a/HeadlineHackathon/Dreamf1re_hack/upload.py b/HeadlineHackathon/Dreamf1re_hack/upload.py new file mode 100644 index 0000000..85bc547 --- /dev/null +++ b/HeadlineHackathon/Dreamf1re_hack/upload.py @@ -0,0 +1,242 @@ +from algosdk.v2client import algod +from algosdk.future import transaction +from util import init_post_client, init_get_client, \ + get_lines, create_transaction, \ + get_txn_ids_from_txn_id +from stitching import stitch_records +from checking import check_circular +import base64 + + +# Core upload function +def process_publishing( + feed: str, + receiver_address: str, + sender_address: str, + sender_private_key: str +) -> list: + """ + This is the core upload procedure using the feed which is the string of + encoded bytes from the file to be uploaded. This returns the + Transaction IDs from submitted group transactions. + + :param feed: The raw string that is base64 encoded with encoding ISO-8859-1 + :param receiver_address: Algorand address of receiver + :param sender_address: Algorand address of sender + :param sender_private_key: Algorand private key of sender + :return: txids - the Transaction IDs of submitted atomic transactions + """ + # Initiate Algorand Client + post_client = init_post_client() + # Get lines from the feed + lines = get_lines(note=feed, max_length=947) + # Create transactions and append + transactions = [] + # The maximum number of transactions + # in a group transaction is 16. + if len(lines) > 16: + # If number of lines exceeds 16, even + # (empty) lines are included in transaction + # creation. Transactions are then appended + # to the transactions list + for line in lines: + created_txn = create_transaction( + post_client=post_client, + receiver_address=receiver_address, + sender_address=sender_address, + message=line + ) + transactions.append(created_txn) + progs = round((len(transactions)/len(lines))*100, ndigits=3) + print(f"\rPreparing..{progs}%", end="") + else: + # If number of lines did not exceed 16, + # each line is included in transaction + # creation if the length of line is not 0 + transactions = [ + create_transaction( + post_client=post_client, + receiver_address=receiver_address, + sender_address=sender_address, + message=line + ) for line in lines if line != "" + ] + # Group created transactions by 16 to comply + # with the group transaction limit of 16 + # individual transactions per group + init_lines = [] + actual_lines = [] + if len(lines) > 16: + for eachtxn in transactions: + init_lines.append(eachtxn) + if len(init_lines) == 16: + actual_lines.append(init_lines) + init_lines = [] + progs = round(((transactions.index(eachtxn)+1)/len(transactions)*100), ndigits=3) + print(f"\rAppending..{progs}%", end="") + # If the last batch of transactions are out and + # their number did not reach 16, append to + # actual lines nonetheless. + if len(init_lines) != 0: + actual_lines.append(init_lines) + else: + # If the length of main line is less than 16, + # bypass the sorting so that the actual lines + # becomes the list of transactions + actual_lines = [transactions] + # Calculate Group IDs for + # each subgroup in transactions list + # and append to a signed transactions list + signed_transactions = [] + signed_transactions_process = [] + print(f'\nCalculating GIDs..') + for group in actual_lines: + # Calculate Group ID for each batch of transactions + cgid = transaction.calculate_group_id(txns=group) + for inner_item in group: + # Assign calculated Group ID + # to each transaction + inner_item.group = cgid + # Sign transaction + signed_txn = inner_item.sign(sender_private_key) + # Add to processing list + signed_transactions_process.append(signed_txn) + # Append to final signed_transactions list + signed_transactions.append(signed_transactions_process) + # Reset helper list + signed_transactions_process = [] + # Send signed group transactions to the Algorand blockchain + txids = [] + for signed_group in signed_transactions: + txid = post_client.send_transactions(signed_group) + txids.append(txid) + # Do not wait for confirmation + progs = round(((signed_transactions.index(signed_group)+1)/len(signed_transactions)) * 100, 3) + print(f"\rSubmitted transactions..{progs}% ", end="") + return txids + + +# Main upload function +def upload( + filename: str, + sender_address: str, + sender_private_key: str +): + """ + Uploads certain local file to blockchain and returns only if + the uploaded file is found to be the same with the downloaded file. + + :param filename: The filename of the file to be uploaded + :param sender_address: Algorand address of sender + :param sender_private_key: Algorand private key of sender + :return: downloaded_file: The stitched records from Algorand blockchain + :return: transaction_ids: Transaction IDs submitted to the blockchain + """ + # Base64 encode the bytes and decode to get the string + with open(filename, 'rb') as o: + original_file = o.read().decode('ISO-8859-1') + original_file = base64.b64encode(original_file.encode()).decode() + print(f'Uploading {filename} to Algorand blockchain..') + # Get Transaction IDs submitted to the blockchain + transaction_ids = process_publishing( + feed=original_file, + receiver_address=sender_address, + sender_address=sender_address, + sender_private_key=sender_private_key + ) + # Initialize GET Client + get_client = init_get_client() + # Loop until downloaded data is exactly + # the same with the uploaded data + while True: + # Get Transaction IDs from Transaction IDs + txn_ids = get_txn_ids_from_txn_id( + __txids=transaction_ids, + client=get_client + ) + # Download the uploaded file from the blockchain + downloaded_file = stitch_records( + get_client=get_client, + txn_ids=txn_ids + ) + # Check if the uploaded file + # and downloaded file are the same + # and return Transaction IDs from + # upload procedure if so + circular = check_circular( + original=original_file, + stitched=downloaded_file + ) + if circular: + print('File successfully uploaded to blockchain.') + return transaction_ids + + +# Link getter +def get_file_id( + transaction_ids: list, + receiver_address: str, + sender_address: str, + sender_private_key: str, + post_client: algod.AlgodClient, + filename: str, +) -> str: + """ + Returns a link which is essentially a Transaction ID + that can be used to download the uploaded file. + + :param transaction_ids: Transaction IDs from upload + :param receiver_address: Algorand address of receiver + :param sender_address: Algorand address of sender + :param sender_private_key: Private key of sender + :param post_client: AlgodClient to node (not indexer) + :param filename: filename of the uploaded file + :return: File ID (a Transaction ID) used for stitching + """ + print(f"Assigning File ID...Please Wait.") + alpha_fn = base64.b64encode(filename.encode()).decode() + alpha = f"{alpha_fn}" + for txid in transaction_ids: + alpha += txid + feed = get_lines( + note=alpha, + max_length=947 + ) + txid = None + for each in feed: + if len(each) != 0: + if len(feed) > 1: + if txid is None: + txn = create_transaction( + post_client, + receiver_address, + sender_address, + message=each + ) + else: + txn = create_transaction( + post_client, + receiver_address, + sender_address, + message=each+txid + ) + sgd = txn.sign(sender_private_key) + txid = post_client.send_transaction(sgd) + transaction.wait_for_confirmation( + algod_client=post_client, + txid=txid + ) + else: + txn = create_transaction( + post_client, + receiver_address, + sender_address, + message=each + ) + sgd = txn.sign(sender_private_key) + txid = post_client.send_transaction(sgd) + transaction.wait_for_confirmation( + algod_client=post_client, + txid=txid + ) + return txid diff --git a/HeadlineHackathon/Dreamf1re_hack/util.py b/HeadlineHackathon/Dreamf1re_hack/util.py new file mode 100644 index 0000000..125461e --- /dev/null +++ b/HeadlineHackathon/Dreamf1re_hack/util.py @@ -0,0 +1,317 @@ +from algosdk.v2client import algod +from algosdk.future import transaction +from algosdk.mnemonic import to_private_key +from algosdk.account import address_from_private_key +from urllib.request import Request, urlopen +import json +import base64 + +from constants import * +import os + + +TEST_SENDER_MNEMONIC = os.environ["test_mnemonic"] +TEST_SENDER_PRIVATE_KEY = to_private_key(TEST_SENDER_MNEMONIC) +TEST_SENDER_ADDRESS = address_from_private_key(TEST_SENDER_PRIVATE_KEY) + + +def init_post_client(): + """ + Initializes an Algorand Client for posting data + + :return: algod_client - algod.AlgodClient (POST) + """ + algod_address = ALGONODE_NODE_ADDRESS + algod_token = '' + headers = {'User-Agent': 'algosdk'} + algod_client = algod.AlgodClient(algod_token, algod_address, headers) + return algod_client + + +def init_get_client(): + """ + Initializes an Algorand Client for getting data + + :return: algod_client - algod.AlgodClient (GET) + """ + algod_address = ALGONODE_INDX_ADDRESS + algod_token = '' + headers = {'User-Agent': 'algosdk'} + algod_client = algod.AlgodClient(algod_token, algod_address, headers) + return algod_client + + +def get_account_info( + get_client: algod.AlgodClient, + account_address: str +): + """ + Gets account information from the given account address. + + :param get_client: algod.AlgodClient (GET) + :param account_address: Algorand public address of target account + :return: info - Account information + """ + info = None + while True: + try: + info = get_client.account_info(address=account_address) + if info is not None: + return info + except Exception as err: + print(err.args) + finally: + if info is None: + info = get_client.account_info(address=account_address) + if info is not None: + return info + + +# Notes in payment transactions are +# utilized to store data in the blockchain +def create_transaction( + post_client: algod.AlgodClient, + receiver_address: str, + sender_address: str, + message +): + """ + Creates a payment transaction for a given message. + + :param post_client: algod.AlgodClient (POST) + :param receiver_address: Algorand receiver address + :param sender_address: Algorand sender address + :param message: The note + :return: A payment transaction (PaymentTxn) + """ + return transaction.PaymentTxn( + sender=sender_address, + sp=post_client.suggested_params(), + receiver=receiver_address, + amt=0, + note=message + ) + + +# Because data is stored in notes, +# the following function gets the note +# of a given transaction. +def search_note_by_txid( + get_client: algod.AlgodClient, + txid: str +): + """ + Gets note based on the specified Transaction ID + + :param get_client: algod.AlgodClient (GET) + :param txid: Transaction ID from which to get the note + :return: note - the note of a given transaction + """ + try: + while True: + req = f'/v2/transactions/{txid}' + url = get_client.algod_address + req + request = Request(url=url, headers=get_client.headers) + resp = urlopen(request) + json_loaded = json.load(resp) + note = str(json_loaded['transaction']['note']) + # Notes are base64 decoded as it is + # base64 encoded before uploading to the + # blockchain. This is to add a layer of + # obfuscation to the contents of the actual + # note. This can be edited so as not to + # do base64 encoding before uploading thereby + # not needing to decode when it is fetched from + # the blockchain. + note = base64.b64decode(note).decode() + if len(note) != 0: + return note + except Exception as e: + print(e.args) + + +# Since there is limited length of bytes per note in the +# transaction which is 1024 bytes or 1 kilobyte, the main feed +# of bytes obtained from encoding of the file-to-be-uploaded is +# divided into separate lines +def get_lines( + note: str, + max_length: int +) -> list: + """ + Get lines for each transaction. Each line, by design, is 947 bytes in length, + max length is 1024 bytes for the Algorand note field. + + :param note: The main feed which is base64 encoded with ISO-8859-1 encoding + :param max_length: The intended line length + :return: list_of_notes - A list of notes + """ + # Do first append + list_of_notes = [note[0:max_length]] + new_note = note[max_length:] + # Repeat succeeding appends + while True: + list_of_notes.append(new_note[0:max_length]) + new_note = new_note[max_length:] + # Do append if final line is reached + if len(new_note) < max_length: + list_of_notes.append(new_note[0:]) + break + return list_of_notes + + +def get_transaction_info( + txids: list, + client: algod.AlgodClient +): + """ + Gets transaction infos from transaction IDs + + :param txids: Transaction IDs + :param client: algod.AlgodClient (GET -> directed to indexer) + :return: jsons: Transaction Infos + """ + jsons = [] + for txid in txids: + try: + req = f'/v2/transactions/{txid}' + url = client.algod_address + req + request = Request(url, headers=client.headers) + while True: + resp = urlopen(request) + json_loaded = json.load(resp) + if len(str(json_loaded)) > 0 and str(json_loaded) != "()": + jsons.append(json_loaded) + print(f"\rFetched infos...", end="") + break + except Exception as e: + print(e.args) + return jsons + + +def get_confirmed_rounds_from_txid( + txids: list, + client: algod.AlgodClient +): + confirmed_rounds = [] + # Get confirmed round + try: + while True: + tx_infos = get_transaction_info(txids=txids, client=client) + if len(tx_infos) != 0: + break + for txinfo in tx_infos: + while True: + conrnd = str(txinfo['transaction']['confirmed-round']) + if len(conrnd) > 0: + confirmed_rounds.append(conrnd) + break + except Exception as e: + print(e.args) + return confirmed_rounds + + +def get_txn_ids_from_txn_id( + __txids: list, + client: algod.AlgodClient +): + """ + Gets Transaction IDs from a Transaction ID, + leverages Confirmed Rounds and Group IDs. + + :param __txids: Transaction IDs + :param client: algod.AlgodClient (GET -> directed to indexer) + :return: txids: Transaction IDs + """ + txids = [] + initial = [] + bridge_for_reverse = [] + block_infos = [] + while True: + try: + # Get confirmed rounds + while True: + confirmed_rounds = get_confirmed_rounds_from_txid( + txids=__txids, + client=client + ) + if len(confirmed_rounds) > 0: + break + print("\rGoing through blocks... ", end="") + while True: + # Get block infos + for cround in confirmed_rounds: + req = f'/v2/transactions?round={cround}' + url = client.algod_address + req + request = Request(url, headers=client.headers) + while True: + resp = urlopen(request) + json_loaded = str(json.load(resp)) + if len(json_loaded) > 0: + block_infos.append(json_loaded) + break + # Get Group ID list + gids = get_group_id(client=client, txids=__txids) + # Get Transaction IDs + for index, block_info in enumerate(block_infos, start=0): + while gids[index] in block_info: + gid_index = block_info.find(gids[index]) + full_gid_index = gid_index + len(gids[index]) + gid_ = block_info[gid_index:full_gid_index] + gid_and_ = gid_ + '","id":"' + txid_start_index = (gid_index + 1) + (len(gid_and_) + 1) + txid_end_index = txid_start_index + 52 + txid_extract = block_info[txid_start_index:txid_end_index] + initial.append(txid_extract) + block_info = block_info[txid_end_index+1:] + txids.append(initial) + initial = [] + break + break + except Exception as e: + print(e.args) + txids.reverse() + for sublist in txids: + bridge_for_reverse.append(sublist) + txids = [] + bridge_for_reverse.reverse() + for superior in bridge_for_reverse: + for inferior in superior: + txids.append(inferior) + return txids + + +# Group IDs are used to find +# other Transaction IDs from +# the given Transaction IDs +# obtained from the upload +# procedure +def get_group_id( + client: algod.AlgodClient, + txids: list +) -> list: + """ + Gets Group IDs from Transaction IDs + + :param client: an algod.AlgodClient (GET) + :param txids: Transaction IDs + :return: gids - Group IDs + """ + # Get Group IDs + gids = [] + print("Getting gids...") + try: + while True: + txn_infos = get_transaction_info( + txids=txids, + client=client + ) + if len(txn_infos) != 0: + for txn_info in txn_infos: + gid = txn_info['transaction']['group'] + if len(gid) > 0: + gids.append(gid) + break + except Exception as e: + print(e.args) + return gids From 96a470aaa2810ae40ed34e82be1c4b9da9a0179d Mon Sep 17 00:00:00 2001 From: Dreamf1re <106770791+Dreamf1re@users.noreply.github.com> Date: Sun, 12 Jun 2022 18:13:56 +0800 Subject: [PATCH 3/8] Update README.md --- HeadlineHackathon/Dreamf1re_hack/README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/HeadlineHackathon/Dreamf1re_hack/README.md b/HeadlineHackathon/Dreamf1re_hack/README.md index 85a1284..4776ecf 100644 --- a/HeadlineHackathon/Dreamf1re_hack/README.md +++ b/HeadlineHackathon/Dreamf1re_hack/README.md @@ -7,6 +7,11 @@ This is a web app, designed with PyWebIO, which aims to make text files permanen 3. Edit Environment Variables -> add variable name "test_mnemonic"
with variable value which is your test mnemonic. 5. Run deploy.py through ```python deploy.py```. -Running deploy.py will start a new tab in the user's default -web browser. All functionalities therein are intact and the user -can now start making text files permanent on the Algorand blockchain. +Running deploy.py will start a new tab in the user's default
+web browser. All functionalities therein are intact and the user
+can now start making text files permanent on the Algorand blockchain.
+ +## Notes +Only use text files that are allowed to be accessed publicly. Never use this web app with
+text files containing private information as it will be made permanent in the Algorand blockchain
+which is a ```public``` blockchain. From 945265ace939bdc1589011c474869f92f35e43e1 Mon Sep 17 00:00:00 2001 From: Dreamf1re <106770791+Dreamf1re@users.noreply.github.com> Date: Sun, 12 Jun 2022 18:19:17 +0800 Subject: [PATCH 4/8] Update README.md --- HeadlineHackathon/Dreamf1re_hack/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HeadlineHackathon/Dreamf1re_hack/README.md b/HeadlineHackathon/Dreamf1re_hack/README.md index 4776ecf..6c8a81f 100644 --- a/HeadlineHackathon/Dreamf1re_hack/README.md +++ b/HeadlineHackathon/Dreamf1re_hack/README.md @@ -3,7 +3,7 @@ This is a web app, designed with PyWebIO, which aims to make text files permanen ## Usage 1. Download this entire folder. -2. Install required packages using ```pip install requirements.txt```. +2. Install required packages using ```pip install -r requirements.txt```. 3. Edit Environment Variables -> add variable name "test_mnemonic"
with variable value which is your test mnemonic. 5. Run deploy.py through ```python deploy.py```. From 3ad6c51ea0382a312ae2bcead73606b2c5d95072 Mon Sep 17 00:00:00 2001 From: Dreamf1re <106770791+Dreamf1re@users.noreply.github.com> Date: Sun, 12 Jun 2022 20:31:38 +0800 Subject: [PATCH 5/8] Update README.md --- HeadlineHackathon/Dreamf1re_hack/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HeadlineHackathon/Dreamf1re_hack/README.md b/HeadlineHackathon/Dreamf1re_hack/README.md index 6c8a81f..990f0c1 100644 --- a/HeadlineHackathon/Dreamf1re_hack/README.md +++ b/HeadlineHackathon/Dreamf1re_hack/README.md @@ -7,9 +7,9 @@ This is a web app, designed with PyWebIO, which aims to make text files permanen 3. Edit Environment Variables -> add variable name "test_mnemonic"
with variable value which is your test mnemonic. 5. Run deploy.py through ```python deploy.py```. -Running deploy.py will start a new tab in the user's default
-web browser. All functionalities therein are intact and the user
-can now start making text files permanent on the Algorand blockchain.
+Running deploy.py will start a new tab in the user's default +web browser. All functionalities therein are intact and the user +can now start making text files permanent on the Algorand blockchain. ## Notes Only use text files that are allowed to be accessed publicly. Never use this web app with
From 093507b3a5b95b837fdcb01f2042dcefe28ba186 Mon Sep 17 00:00:00 2001 From: Dreamf1re <106770791+Dreamf1re@users.noreply.github.com> Date: Mon, 13 Jun 2022 11:47:22 +0800 Subject: [PATCH 6/8] Update README.md --- HeadlineHackathon/Dreamf1re_hack/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HeadlineHackathon/Dreamf1re_hack/README.md b/HeadlineHackathon/Dreamf1re_hack/README.md index 990f0c1..e9d874d 100644 --- a/HeadlineHackathon/Dreamf1re_hack/README.md +++ b/HeadlineHackathon/Dreamf1re_hack/README.md @@ -13,5 +13,5 @@ can now start making text files permanent on the Algorand blockchain. ## Notes Only use text files that are allowed to be accessed publicly. Never use this web app with
-text files containing private information as it will be made permanent in the Algorand blockchain
+text files containing private information as they will be made permanent in the Algorand blockchain
which is a ```public``` blockchain. From b4156af2560b114ff4d64026e3ae7b72f3cc092d Mon Sep 17 00:00:00 2001 From: Dreamf1re <106770791+Dreamf1re@users.noreply.github.com> Date: Tue, 14 Jun 2022 04:11:00 +0800 Subject: [PATCH 7/8] Update README.md --- HeadlineHackathon/Dreamf1re_hack/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HeadlineHackathon/Dreamf1re_hack/README.md b/HeadlineHackathon/Dreamf1re_hack/README.md index e9d874d..c7f3cd0 100644 --- a/HeadlineHackathon/Dreamf1re_hack/README.md +++ b/HeadlineHackathon/Dreamf1re_hack/README.md @@ -4,7 +4,7 @@ This is a web app, designed with PyWebIO, which aims to make text files permanen ## Usage 1. Download this entire folder. 2. Install required packages using ```pip install -r requirements.txt```. -3. Edit Environment Variables -> add variable name "test_mnemonic"
with variable value which is your test mnemonic. +3. Edit Environment Variables -> add variable name "test_mnemonic" with variable value which is your test mnemonic. Make sure to fund this account with TestNet ALGOs 5. Run deploy.py through ```python deploy.py```. Running deploy.py will start a new tab in the user's default From 91083f82e66ea13a7b3b95201ed4fc5407bb286d Mon Sep 17 00:00:00 2001 From: Dreamf1re <106770791+Dreamf1re@users.noreply.github.com> Date: Tue, 14 Jun 2022 04:11:47 +0800 Subject: [PATCH 8/8] Update README.md --- HeadlineHackathon/Dreamf1re_hack/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HeadlineHackathon/Dreamf1re_hack/README.md b/HeadlineHackathon/Dreamf1re_hack/README.md index c7f3cd0..3ee3c8b 100644 --- a/HeadlineHackathon/Dreamf1re_hack/README.md +++ b/HeadlineHackathon/Dreamf1re_hack/README.md @@ -4,7 +4,7 @@ This is a web app, designed with PyWebIO, which aims to make text files permanen ## Usage 1. Download this entire folder. 2. Install required packages using ```pip install -r requirements.txt```. -3. Edit Environment Variables -> add variable name "test_mnemonic" with variable value which is your test mnemonic. Make sure to fund this account with TestNet ALGOs +3. Edit Environment Variables -> add variable name "test_mnemonic" with variable value which is your test mnemonic.
Make sure to fund this account with TestNet ALGOs. 5. Run deploy.py through ```python deploy.py```. Running deploy.py will start a new tab in the user's default