aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--.gitmodules3
-rw-r--r--Cargo.lock384
-rw-r--r--Cargo.toml3
-rw-r--r--TODO.md32
-rw-r--r--log.conf.yml41
-rw-r--r--site/config.toml28
-rw-r--r--site/content/JWT.md41
-rw-r--r--site/content/_index.md85
-rw-r--r--site/content/block_docs.md30
-rw-r--r--site/content/register_docs.md39
-rw-r--r--site/content/transaction_docs.md26
-rw-r--r--site/public/404.html3
-rw-r--r--site/public/android-chrome-192x192.png (renamed from static/android-chrome-192x192.png)bin26249 -> 26249 bytes
-rw-r--r--site/public/android-chrome-512x512.png (renamed from static/android-chrome-512x512.png)bin80376 -> 80376 bytes
-rw-r--r--site/public/apple-touch-icon.png (renamed from static/apple-touch-icon.png)bin24023 -> 24023 bytes
-rw-r--r--site/public/block-docs/index.html168
-rw-r--r--site/public/elasticlunr.min.js10
-rw-r--r--site/public/favicon-16x16.png (renamed from static/favicon-16x16.png)bin878 -> 878 bytes
-rw-r--r--site/public/favicon-32x32.png (renamed from static/favicon-32x32.png)bin2463 -> 2463 bytes
-rw-r--r--site/public/favicon.ico (renamed from static/favicon.ico)bin15406 -> 15406 bytes
-rw-r--r--site/public/gradecoin.png (renamed from static/gradecoin.png)bin197656 -> 197656 bytes
-rw-r--r--site/public/index.html319
-rw-r--r--site/public/juice.css1
-rw-r--r--site/public/jwt/index.html179
-rw-r--r--site/public/normalize.css349
-rw-r--r--site/public/register-docs/index.html168
-rw-r--r--site/public/robots.txt3
-rw-r--r--site/public/search_index.en.js1
-rw-r--r--site/public/sitemap.xml18
-rw-r--r--site/public/transaction-docs/index.html164
-rw-r--r--site/static/android-chrome-192x192.pngbin0 -> 26249 bytes
-rw-r--r--site/static/android-chrome-512x512.pngbin0 -> 80376 bytes
-rw-r--r--site/static/apple-touch-icon.pngbin0 -> 24023 bytes
-rw-r--r--site/static/favicon-16x16.pngbin0 -> 878 bytes
-rw-r--r--site/static/favicon-32x32.pngbin0 -> 2463 bytes
-rw-r--r--site/static/favicon.icobin0 -> 15406 bytes
-rw-r--r--site/static/gradecoin.pngbin0 -> 197656 bytes
-rw-r--r--site/templates/_variables.html15
-rw-r--r--site/templates/index.html50
m---------site/themes/juice0
-rw-r--r--src/bin/main.rs3
-rw-r--r--src/error.rs38
-rw-r--r--src/handlers.rs223
-rw-r--r--src/lib.rs5
-rw-r--r--src/routes.rs11
-rw-r--r--src/schema.rs73
-rw-r--r--templates/css.html8
-rw-r--r--templates/footer.html2
-rw-r--r--templates/header.html11
-rw-r--r--templates/menu.html5
-rw-r--r--templates/welcome.html8
52 files changed, 2365 insertions, 186 deletions
diff --git a/.gitignore b/.gitignore
index 7b71fac..216626e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
1/target 1log/
2target/
2blocks/ 3blocks/
3users/ 4users/
5public/
4tags.lock 6tags.lock
5tags.temp 7tags.temp
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..3c79e9c
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
1[submodule "site/themes/juice"]
2 path = site/themes/juice
3 url = https://github.com/huhu/juice
diff --git a/Cargo.lock b/Cargo.lock
index b548774..5538eee 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -41,14 +41,67 @@ dependencies = [
41] 41]
42 42
43[[package]] 43[[package]]
44name = "atty" 44name = "anyhow"
45version = "0.2.14" 45version = "1.0.40"
46source = "registry+https://github.com/rust-lang/crates.io-index"
47checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
48
49[[package]]
50name = "arc-swap"
51version = "0.4.8"
46source = "registry+https://github.com/rust-lang/crates.io-index" 52source = "registry+https://github.com/rust-lang/crates.io-index"
47checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 53checksum = "dabe5a181f83789739c194cbe5a897dde195078fac08568d09221fd6137a7ba8"
54
55[[package]]
56name = "arrayvec"
57version = "0.5.2"
58source = "registry+https://github.com/rust-lang/crates.io-index"
59checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
60
61[[package]]
62name = "askama"
63version = "0.10.5"
64source = "registry+https://github.com/rust-lang/crates.io-index"
65checksum = "d298738b6e47e1034e560e5afe63aa488fea34e25ec11b855a76f0d7b8e73134"
48dependencies = [ 66dependencies = [
49 "hermit-abi", 67 "askama_derive",
50 "libc", 68 "askama_escape",
51 "winapi 0.3.9", 69 "askama_shared",
70]
71
72[[package]]
73name = "askama_derive"
74version = "0.10.5"
75source = "registry+https://github.com/rust-lang/crates.io-index"
76checksum = "ca2925c4c290382f9d2fa3d1c1b6a63fa1427099721ecca4749b154cc9c25522"
77dependencies = [
78 "askama_shared",
79 "proc-macro2",
80 "syn",
81]
82
83[[package]]
84name = "askama_escape"
85version = "0.10.1"
86source = "registry+https://github.com/rust-lang/crates.io-index"
87checksum = "90c108c1a94380c89d2215d0ac54ce09796823cca0fd91b299cfff3b33e346fb"
88
89[[package]]
90name = "askama_shared"
91version = "0.11.1"
92source = "registry+https://github.com/rust-lang/crates.io-index"
93checksum = "2582b77e0f3c506ec4838a25fa8a5f97b9bed72bb6d3d272ea1c031d8bd373bc"
94dependencies = [
95 "askama_escape",
96 "humansize",
97 "nom",
98 "num-traits",
99 "percent-encoding",
100 "proc-macro2",
101 "quote",
102 "serde",
103 "syn",
104 "toml",
52] 105]
53 106
54[[package]] 107[[package]]
@@ -82,6 +135,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
82checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 135checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
83 136
84[[package]] 137[[package]]
138name = "bitvec"
139version = "0.19.5"
140source = "registry+https://github.com/rust-lang/crates.io-index"
141checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321"
142dependencies = [
143 "funty",
144 "radium",
145 "tap",
146 "wyz",
147]
148
149[[package]]
85name = "blake2" 150name = "blake2"
86version = "0.9.1" 151version = "0.9.1"
87source = "registry+https://github.com/rust-lang/crates.io-index" 152source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -218,6 +283,17 @@ dependencies = [
218] 283]
219 284
220[[package]] 285[[package]]
286name = "derivative"
287version = "2.2.0"
288source = "registry+https://github.com/rust-lang/crates.io-index"
289checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
290dependencies = [
291 "proc-macro2",
292 "quote",
293 "syn",
294]
295
296[[package]]
221name = "digest" 297name = "digest"
222version = "0.9.0" 298version = "0.9.0"
223source = "registry+https://github.com/rust-lang/crates.io-index" 299source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -233,19 +309,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
233checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" 309checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
234 310
235[[package]] 311[[package]]
236name = "env_logger"
237version = "0.6.2"
238source = "registry+https://github.com/rust-lang/crates.io-index"
239checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
240dependencies = [
241 "atty",
242 "humantime",
243 "log",
244 "regex",
245 "termcolor",
246]
247
248[[package]]
249name = "fnv" 312name = "fnv"
250version = "1.0.7" 313version = "1.0.7"
251source = "registry+https://github.com/rust-lang/crates.io-index" 314source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -278,6 +341,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
278checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 341checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
279 342
280[[package]] 343[[package]]
344name = "funty"
345version = "1.1.0"
346source = "registry+https://github.com/rust-lang/crates.io-index"
347checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
348
349[[package]]
281name = "futures" 350name = "futures"
282version = "0.3.14" 351version = "0.3.14"
283source = "registry+https://github.com/rust-lang/crates.io-index" 352source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -376,6 +445,7 @@ name = "gradecoin"
376version = "0.1.0" 445version = "0.1.0"
377dependencies = [ 446dependencies = [
378 "aes", 447 "aes",
448 "askama",
379 "base64 0.13.0", 449 "base64 0.13.0",
380 "blake2", 450 "blake2",
381 "block-modes", 451 "block-modes",
@@ -384,9 +454,9 @@ dependencies = [
384 "jsonwebtoken", 454 "jsonwebtoken",
385 "lazy_static", 455 "lazy_static",
386 "log", 456 "log",
457 "log4rs",
387 "md-5", 458 "md-5",
388 "parking_lot", 459 "parking_lot 0.10.2",
389 "pretty_env_logger",
390 "rsa", 460 "rsa",
391 "serde", 461 "serde",
392 "serde_json", 462 "serde_json",
@@ -448,15 +518,6 @@ dependencies = [
448] 518]
449 519
450[[package]] 520[[package]]
451name = "hermit-abi"
452version = "0.1.18"
453source = "registry+https://github.com/rust-lang/crates.io-index"
454checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
455dependencies = [
456 "libc",
457]
458
459[[package]]
460name = "hex-literal" 521name = "hex-literal"
461version = "0.3.1" 522version = "0.3.1"
462source = "registry+https://github.com/rust-lang/crates.io-index" 523source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -496,13 +557,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
496checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" 557checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
497 558
498[[package]] 559[[package]]
560name = "humansize"
561version = "1.1.0"
562source = "registry+https://github.com/rust-lang/crates.io-index"
563checksum = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e"
564
565[[package]]
499name = "humantime" 566name = "humantime"
500version = "1.3.0" 567version = "2.1.0"
501source = "registry+https://github.com/rust-lang/crates.io-index" 568source = "registry+https://github.com/rust-lang/crates.io-index"
502checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 569checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
503dependencies = [
504 "quick-error",
505]
506 570
507[[package]] 571[[package]]
508name = "hyper" 572name = "hyper"
@@ -559,6 +623,15 @@ dependencies = [
559] 623]
560 624
561[[package]] 625[[package]]
626name = "instant"
627version = "0.1.9"
628source = "registry+https://github.com/rust-lang/crates.io-index"
629checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
630dependencies = [
631 "cfg-if 1.0.0",
632]
633
634[[package]]
562name = "iovec" 635name = "iovec"
563version = "0.1.4" 636version = "0.1.4"
564source = "registry+https://github.com/rust-lang/crates.io-index" 637source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -616,6 +689,19 @@ dependencies = [
616] 689]
617 690
618[[package]] 691[[package]]
692name = "lexical-core"
693version = "0.7.5"
694source = "registry+https://github.com/rust-lang/crates.io-index"
695checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374"
696dependencies = [
697 "arrayvec",
698 "bitflags",
699 "cfg-if 1.0.0",
700 "ryu",
701 "static_assertions",
702]
703
704[[package]]
619name = "libc" 705name = "libc"
620version = "0.2.93" 706version = "0.2.93"
621source = "registry+https://github.com/rust-lang/crates.io-index" 707source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -628,6 +714,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
628checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" 714checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
629 715
630[[package]] 716[[package]]
717name = "linked-hash-map"
718version = "0.5.4"
719source = "registry+https://github.com/rust-lang/crates.io-index"
720checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
721
722[[package]]
631name = "lock_api" 723name = "lock_api"
632version = "0.3.4" 724version = "0.3.4"
633source = "registry+https://github.com/rust-lang/crates.io-index" 725source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -637,12 +729,55 @@ dependencies = [
637] 729]
638 730
639[[package]] 731[[package]]
732name = "lock_api"
733version = "0.4.3"
734source = "registry+https://github.com/rust-lang/crates.io-index"
735checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
736dependencies = [
737 "scopeguard",
738]
739
740[[package]]
640name = "log" 741name = "log"
641version = "0.4.14" 742version = "0.4.14"
642source = "registry+https://github.com/rust-lang/crates.io-index" 743source = "registry+https://github.com/rust-lang/crates.io-index"
643checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 744checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
644dependencies = [ 745dependencies = [
645 "cfg-if 1.0.0", 746 "cfg-if 1.0.0",
747 "serde",
748]
749
750[[package]]
751name = "log-mdc"
752version = "0.1.0"
753source = "registry+https://github.com/rust-lang/crates.io-index"
754checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7"
755
756[[package]]
757name = "log4rs"
758version = "1.0.0"
759source = "registry+https://github.com/rust-lang/crates.io-index"
760checksum = "d1572a880d1115ff867396eee7ae2bc924554225e67a0d3c85c745b3e60ca211"
761dependencies = [
762 "anyhow",
763 "arc-swap",
764 "chrono",
765 "derivative",
766 "fnv",
767 "humantime",
768 "libc",
769 "log",
770 "log-mdc",
771 "parking_lot 0.11.1",
772 "regex",
773 "serde",
774 "serde-value",
775 "serde_json",
776 "serde_yaml",
777 "thiserror",
778 "thread-id",
779 "typemap",
780 "winapi 0.3.9",
646] 781]
647 782
648[[package]] 783[[package]]
@@ -745,6 +880,19 @@ dependencies = [
745] 880]
746 881
747[[package]] 882[[package]]
883name = "nom"
884version = "6.1.2"
885source = "registry+https://github.com/rust-lang/crates.io-index"
886checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
887dependencies = [
888 "bitvec",
889 "funty",
890 "lexical-core",
891 "memchr",
892 "version_check",
893]
894
895[[package]]
748name = "num-bigint" 896name = "num-bigint"
749version = "0.2.6" 897version = "0.2.6"
750source = "registry+https://github.com/rust-lang/crates.io-index" 898source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -828,13 +976,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
828checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 976checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
829 977
830[[package]] 978[[package]]
979name = "ordered-float"
980version = "2.1.1"
981source = "registry+https://github.com/rust-lang/crates.io-index"
982checksum = "766f840da25490628d8e63e529cd21c014f6600c6b8517add12a6fa6167a6218"
983dependencies = [
984 "num-traits",
985]
986
987[[package]]
831name = "parking_lot" 988name = "parking_lot"
832version = "0.10.2" 989version = "0.10.2"
833source = "registry+https://github.com/rust-lang/crates.io-index" 990source = "registry+https://github.com/rust-lang/crates.io-index"
834checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" 991checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
835dependencies = [ 992dependencies = [
836 "lock_api", 993 "lock_api 0.3.4",
837 "parking_lot_core", 994 "parking_lot_core 0.7.2",
995]
996
997[[package]]
998name = "parking_lot"
999version = "0.11.1"
1000source = "registry+https://github.com/rust-lang/crates.io-index"
1001checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
1002dependencies = [
1003 "instant",
1004 "lock_api 0.4.3",
1005 "parking_lot_core 0.8.3",
838] 1006]
839 1007
840[[package]] 1008[[package]]
@@ -852,6 +1020,20 @@ dependencies = [
852] 1020]
853 1021
854[[package]] 1022[[package]]
1023name = "parking_lot_core"
1024version = "0.8.3"
1025source = "registry+https://github.com/rust-lang/crates.io-index"
1026checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
1027dependencies = [
1028 "cfg-if 1.0.0",
1029 "instant",
1030 "libc",
1031 "redox_syscall 0.2.6",
1032 "smallvec",
1033 "winapi 0.3.9",
1034]
1035
1036[[package]]
855name = "pem" 1037name = "pem"
856version = "0.8.3" 1038version = "0.8.3"
857source = "registry+https://github.com/rust-lang/crates.io-index" 1039source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -933,17 +1115,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
933checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 1115checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
934 1116
935[[package]] 1117[[package]]
936name = "pretty_env_logger"
937version = "0.3.1"
938source = "registry+https://github.com/rust-lang/crates.io-index"
939checksum = "717ee476b1690853d222af4634056d830b5197ffd747726a9a1eee6da9f49074"
940dependencies = [
941 "chrono",
942 "env_logger",
943 "log",
944]
945
946[[package]]
947name = "proc-macro2" 1118name = "proc-macro2"
948version = "1.0.26" 1119version = "1.0.26"
949source = "registry+https://github.com/rust-lang/crates.io-index" 1120source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -968,6 +1139,12 @@ dependencies = [
968] 1139]
969 1140
970[[package]] 1141[[package]]
1142name = "radium"
1143version = "0.5.3"
1144source = "registry+https://github.com/rust-lang/crates.io-index"
1145checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
1146
1147[[package]]
971name = "rand" 1148name = "rand"
972version = "0.7.3" 1149version = "0.7.3"
973source = "registry+https://github.com/rust-lang/crates.io-index" 1150source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1158,6 +1335,16 @@ dependencies = [
1158] 1335]
1159 1336
1160[[package]] 1337[[package]]
1338name = "serde-value"
1339version = "0.7.0"
1340source = "registry+https://github.com/rust-lang/crates.io-index"
1341checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
1342dependencies = [
1343 "ordered-float",
1344 "serde",
1345]
1346
1347[[package]]
1161name = "serde_derive" 1348name = "serde_derive"
1162version = "1.0.125" 1349version = "1.0.125"
1163source = "registry+https://github.com/rust-lang/crates.io-index" 1350source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1201,6 +1388,18 @@ dependencies = [
1201] 1388]
1202 1389
1203[[package]] 1390[[package]]
1391name = "serde_yaml"
1392version = "0.8.17"
1393source = "registry+https://github.com/rust-lang/crates.io-index"
1394checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
1395dependencies = [
1396 "dtoa",
1397 "linked-hash-map",
1398 "serde",
1399 "yaml-rust",
1400]
1401
1402[[package]]
1204name = "sha-1" 1403name = "sha-1"
1205version = "0.9.4" 1404version = "0.9.4"
1206source = "registry+https://github.com/rust-lang/crates.io-index" 1405source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1279,6 +1478,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1279checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 1478checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
1280 1479
1281[[package]] 1480[[package]]
1481name = "static_assertions"
1482version = "1.1.0"
1483source = "registry+https://github.com/rust-lang/crates.io-index"
1484checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
1485
1486[[package]]
1282name = "subtle" 1487name = "subtle"
1283version = "2.4.0" 1488version = "2.4.0"
1284source = "registry+https://github.com/rust-lang/crates.io-index" 1489source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1308,6 +1513,12 @@ dependencies = [
1308] 1513]
1309 1514
1310[[package]] 1515[[package]]
1516name = "tap"
1517version = "1.0.1"
1518source = "registry+https://github.com/rust-lang/crates.io-index"
1519checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
1520
1521[[package]]
1311name = "tempfile" 1522name = "tempfile"
1312version = "3.2.0" 1523version = "3.2.0"
1313source = "registry+https://github.com/rust-lang/crates.io-index" 1524source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1322,15 +1533,6 @@ dependencies = [
1322] 1533]
1323 1534
1324[[package]] 1535[[package]]
1325name = "termcolor"
1326version = "1.1.2"
1327source = "registry+https://github.com/rust-lang/crates.io-index"
1328checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
1329dependencies = [
1330 "winapi-util",
1331]
1332
1333[[package]]
1334name = "thiserror" 1536name = "thiserror"
1335version = "1.0.24" 1537version = "1.0.24"
1336source = "registry+https://github.com/rust-lang/crates.io-index" 1538source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1351,6 +1553,17 @@ dependencies = [
1351] 1553]
1352 1554
1353[[package]] 1555[[package]]
1556name = "thread-id"
1557version = "3.3.0"
1558source = "registry+https://github.com/rust-lang/crates.io-index"
1559checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1"
1560dependencies = [
1561 "libc",
1562 "redox_syscall 0.1.57",
1563 "winapi 0.3.9",
1564]
1565
1566[[package]]
1354name = "time" 1567name = "time"
1355version = "0.1.43" 1568version = "0.1.43"
1356source = "registry+https://github.com/rust-lang/crates.io-index" 1569source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1432,6 +1645,15 @@ dependencies = [
1432] 1645]
1433 1646
1434[[package]] 1647[[package]]
1648name = "toml"
1649version = "0.5.8"
1650source = "registry+https://github.com/rust-lang/crates.io-index"
1651checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
1652dependencies = [
1653 "serde",
1654]
1655
1656[[package]]
1435name = "tower-service" 1657name = "tower-service"
1436version = "0.3.1" 1658version = "0.3.1"
1437source = "registry+https://github.com/rust-lang/crates.io-index" 1659source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1469,6 +1691,12 @@ dependencies = [
1469] 1691]
1470 1692
1471[[package]] 1693[[package]]
1694name = "traitobject"
1695version = "0.1.0"
1696source = "registry+https://github.com/rust-lang/crates.io-index"
1697checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
1698
1699[[package]]
1472name = "try-lock" 1700name = "try-lock"
1473version = "0.2.3" 1701version = "0.2.3"
1474source = "registry+https://github.com/rust-lang/crates.io-index" 1702source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1503,6 +1731,15 @@ dependencies = [
1503] 1731]
1504 1732
1505[[package]] 1733[[package]]
1734name = "typemap"
1735version = "0.3.3"
1736source = "registry+https://github.com/rust-lang/crates.io-index"
1737checksum = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6"
1738dependencies = [
1739 "unsafe-any",
1740]
1741
1742[[package]]
1506name = "typenum" 1743name = "typenum"
1507version = "1.13.0" 1744version = "1.13.0"
1508source = "registry+https://github.com/rust-lang/crates.io-index" 1745source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1542,6 +1779,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1542checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1779checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
1543 1780
1544[[package]] 1781[[package]]
1782name = "unsafe-any"
1783version = "0.4.2"
1784source = "registry+https://github.com/rust-lang/crates.io-index"
1785checksum = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f"
1786dependencies = [
1787 "traitobject",
1788]
1789
1790[[package]]
1545name = "untrusted" 1791name = "untrusted"
1546version = "0.7.1" 1792version = "0.7.1"
1547source = "registry+https://github.com/rust-lang/crates.io-index" 1793source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1720,15 +1966,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1720checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1966checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1721 1967
1722[[package]] 1968[[package]]
1723name = "winapi-util"
1724version = "0.1.5"
1725source = "registry+https://github.com/rust-lang/crates.io-index"
1726checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
1727dependencies = [
1728 "winapi 0.3.9",
1729]
1730
1731[[package]]
1732name = "winapi-x86_64-pc-windows-gnu" 1969name = "winapi-x86_64-pc-windows-gnu"
1733version = "0.4.0" 1970version = "0.4.0"
1734source = "registry+https://github.com/rust-lang/crates.io-index" 1971source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1745,6 +1982,21 @@ dependencies = [
1745] 1982]
1746 1983
1747[[package]] 1984[[package]]
1985name = "wyz"
1986version = "0.2.0"
1987source = "registry+https://github.com/rust-lang/crates.io-index"
1988checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
1989
1990[[package]]
1991name = "yaml-rust"
1992version = "0.4.5"
1993source = "registry+https://github.com/rust-lang/crates.io-index"
1994checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
1995dependencies = [
1996 "linked-hash-map",
1997]
1998
1999[[package]]
1748name = "zeroize" 2000name = "zeroize"
1749version = "1.2.0" 2001version = "1.2.0"
1750source = "registry+https://github.com/rust-lang/crates.io-index" 2002source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index b5733b2..9c1d5f1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@ tokio = { version = "0.2.9", features = ["macros"] }
12serde = { version = "1.0.104", features = ["derive"] } 12serde = { version = "1.0.104", features = ["derive"] }
13chrono = { version = "0.4.10", features = ["serde"] } 13chrono = { version = "0.4.10", features = ["serde"] }
14log = "0.4.8" 14log = "0.4.8"
15pretty_env_logger = "0.3.1" 15log4rs = "1.0.0"
16parking_lot = "0.10.0" 16parking_lot = "0.10.0"
17serde_json = "1.0.59" 17serde_json = "1.0.59"
18lazy_static = "1.4.0" 18lazy_static = "1.4.0"
@@ -25,6 +25,7 @@ base64 = "0.13.0"
25sha2 = "0.9.3" 25sha2 = "0.9.3"
26block-modes = "0.7.0" 26block-modes = "0.7.0"
27aes = "0.6.0" 27aes = "0.6.0"
28askama = "0.10.5"
28 29
29[dev-dependencies] 30[dev-dependencies]
30serde_test = "1.0.117" 31serde_test = "1.0.117"
diff --git a/TODO.md b/TODO.md
index b429097..33d6f8e 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,5 +1,32 @@
1# TODO 1# TODO
2 2
3- [ ] IV is array, should be hex, implement checks
4- [ ] Restore students from disk
5
6## Office Hour/Recitation
7- [ ] Should give a little pointers but not too much, I think at first this is going to seem hard to many students but it should become fairly easy after some little pointers
8
9## Docs
10- [ ] Make a better explanation of authorization schema
11- [ ] register: give the register message schema(passwd is missing)
12- [ ] how to bank works
13- [ ] register should have AuthRequest in the explanation
14- [ ] link all types in schema.rs to the docs, they need to understand why we have them
15- [ ] explain hash type(MD5 is missing in Claims)
16- [ ] Initial auth request needs more explanation
17- [ ] Explain JSON Wrapped
18- [ ] Give links to the functions, their docs are very good. For example, it seems impossible to understand authentication from the first page, but when you go to handlers::authenticate_user many things are clarified.
19- [ ] authorized_propose_transaction and authorized_propose_block may have more explanation as in the case of
20- [x] how to start(possibly some pointers and links -- blockchain, rest, jwt, rsa, public key)
21- [x] There is todo at handlers::authorized_propose_transaction, fix that
22- [x] gradecoin: give narrative explanation
23- [x] bank public key
24- [X] delete CONSTANTS
25
26### Authorization
27- [x] Pointer to JWT
28- [x] Pointer to Public Key Sign
29
3## Tests 30## Tests
4- [ ] User Authentication/Authentication Tests 31- [ ] User Authentication/Authentication Tests
5- [ ] Route Tests 32- [ ] Route Tests
@@ -7,11 +34,9 @@
7 - [ ] Valid JSON with missing fields 34 - [ ] Valid JSON with missing fields
8 - [ ] Valid JSON with extra fields 35 - [ ] Valid JSON with extra fields
9 36
10## Please
11- [ ] use [juice](https://www.getzola.org/themes/juice/) theme with [template rendering](https://blog.logrocket.com/template-rendering-in-rust/) to create a landing page. I want it to look handmade & _scammy_, I'm talking [verylegit](https://verylegit.link) shortened urls, botched [this person does not exist](https://www.thispersondoesnotexist.com/) user stories etc.
12
13## Testnet 37## Testnet
14- [ ] CHAOS MODE, 3 different coins, combine them to make 1 gradecoin 38- [ ] CHAOS MODE, 3 different coins, combine them to make 1 gradecoin
39- [ ] [SQLite](https://unixsheikh.com/articles/sqlite-the-only-database-you-will-ever-need-in-most-cases.html), not PostgreSQL
15 40
16## Done & Brag 41## Done & Brag
17- [x] Switch to RwLock (parking_lot) (done at 2021-04-07 03:43, two possible schemes to represent inner Db (ledger) in code) 42- [x] Switch to RwLock (parking_lot) (done at 2021-04-07 03:43, two possible schemes to represent inner Db (ledger) in code)
@@ -34,3 +59,4 @@
34- [X] Blocks should "play out" the transactions and execute transactions (2021-04-14 21:29) 59- [X] Blocks should "play out" the transactions and execute transactions (2021-04-14 21:29)
35- [X] "Coinbase" ("by" of the first transaction of the block) should get rewarded for their efforts (2021-04-14 21:48) 60- [X] "Coinbase" ("by" of the first transaction of the block) should get rewarded for their efforts (2021-04-14 21:48)
36- [X] Implemented Bank Account (2021-04-14 23:28) 61- [X] Implemented Bank Account (2021-04-14 23:28)
62- [x] use [juice](https://www.getzola.org/themes/juice/) theme ~~with [template rendering](https://blog.logrocket.com/template-rendering-in-rust/)~~ zola to create a landing page. (done at 2021-04-15 03:41, in the most hilarious way possible)
diff --git a/log.conf.yml b/log.conf.yml
new file mode 100644
index 0000000..1940669
--- /dev/null
+++ b/log.conf.yml
@@ -0,0 +1,41 @@
1# Scan this file for changes every 2 minutes
2refresh_rate: 2 minutes
3
4appenders:
5 # An appender named "stdout" that writes to stdout with pretty colours
6 stdout:
7 kind: console
8 encoder:
9 pattern: "[{d(%Y-%m-%d %H:%M:%S)} {h({l})}] - {m}\n"
10
11 # And this guy writes to file, also rolls the files (when they get too large)
12 gradecoin:
13 kind: rolling_file
14 path: "log/gradecoin.log"
15 append: true
16 encoder:
17 kind: pattern
18 pattern: "[{d(%Y-%m-%d %H:%M:%S)} {l}] - {m}\n"
19
20 policy:
21 kind: compound
22
23 trigger:
24 kind: size
25 limit: 4 mb
26
27 roller:
28 kind: fixed_window
29 pattern: "log/gradecoin.{}.old.log"
30 count: 5
31 base: 1
32
33loggers:
34 gradecoin:
35 level: info
36 appenders:
37 - stdout
38 - gradecoin
39 additive: false
40
41
diff --git a/site/config.toml b/site/config.toml
new file mode 100644
index 0000000..070b762
--- /dev/null
+++ b/site/config.toml
@@ -0,0 +1,28 @@
1# The URL the site will be built for
2base_url = "https://gradecoin.xyz"
3
4theme = "juice"
5
6title = "Gradecoin"
7description = "Mine Your Grades"
8
9# Whether to automatically compile all Sass files in the sass directory
10compile_sass = true
11
12# Whether to build a search index to be used later on by a JavaScript library
13build_search_index = true
14
15[markdown]
16# Whether to do syntax highlighting
17# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
18highlight_code = true
19highlight_theme = "subway-moscow"
20
21[extra]
22# Put all your custom variables here
23juice_logo_name = "Gradecoin"
24juice_logo_path = "gradecoin.png"
25juice_extra_menu = [
26 { title = "why?", link = "https://github.com/zhuowei/nft_ptr#why"}
27]
28
diff --git a/site/content/JWT.md b/site/content/JWT.md
new file mode 100644
index 0000000..46da1a2
--- /dev/null
+++ b/site/content/JWT.md
@@ -0,0 +1,41 @@
1+++
2title = "JWT"
3description = "JSON Web Token Documentation"
4weight = 4
5+++
6
7> JSON Web Tokens are representations of claims, or authorization proofs that fit into the `Header` of HTTP requests.
8
9# How?
10
11JWTs are used as the [MAC](https://en.wikipedia.org/wiki/Message_authentication_code) of operations that require authorization:
12- block proposal
13- transaction proposal.
14
15They are send alongside the JSON request body in the `Header`;
16
17```html
18Authorization: Bearer aaaaaa.bbbbbb.ccccc
19```
20
21Gradecoin uses 3 fields for the JWTs;
22
23```json
24{
25"tha": "Hash of the payload, check invididual references",
26"iat": "Issued At, Unix Time",
27"exp": "Expiration Time, epoch"
28}
29```
30
31- `tha` is explained in [blocks](@/block_docs.md) and [transactions](@/transaction_docs.md) documentations.
32- `iat` when the JWT was created in [Unix Time](https://en.wikipedia.org/wiki/Unix_time) format
33- `exp` when the JWT will expire & be rejected in [Unix Time](https://en.wikipedia.org/wiki/Unix_time)
34
35# Algorithm
36We are using [RS256](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1), `RSASSA-PKCS1-v1_5 using SHA-256`. The JWTs you encode with your private RSA key will be decoded using the public key you have authenticated with. You can see how the process works [here](https://jwt.io/).
37
38# References
39- [RFC, the ultimate reference](https://tools.ietf.org/html/rfc7519)
40- [JWT Debugger](https://jwt.io/)
41
diff --git a/site/content/_index.md b/site/content/_index.md
new file mode 100644
index 0000000..4ad0544
--- /dev/null
+++ b/site/content/_index.md
@@ -0,0 +1,85 @@
1+++
2title = "Gradecoin"
3sort_by = "weight"
4+++
5
6# Welcome to Gradecoin!
7
8Blockchains are incredibly simple yet can appear very complicated, we will see how they work and practice programming _production_ cryptography code.
9
10This server is the sandbox for the PA1, it's currently running the Gradecoin application. Gradecoin is the faux currency we will use to simulate a blockchain network. At the end of the simulation, the amount of Gradecoin you hold will be your PA1 grade.
11
12**A quick summary**: authenticate yourself to the system using public key encryption.
13Craft [Transaction](@/transaction_docs.md) proposals and tag them using [JWTs](@/JWT.md).
14When there are enough transactions then you can propose [Blocks](@/block_docs.md) in the same way.
15Blocks need to be _mined_ beforehand using Proof-of-work, or brute force.
16
17Gradecoin offers 3 endpoints at [/register](/register), [/block](/block) and [/transaction](/transaction). You can only send GET requests to /block and /transaction without authorization.
18The server is programmed in [RESTful](https://www.service-architecture.com/articles/web-services/representational_state_transfer_rest.html) architecture, there are no `DELETE`, `PUT` or `UPDATE` operations, though.
19
20Gradecoin uses a Proof-of-work block accepting mechanism. It uses single round [Blake2s](https://www.blake2.net/) hashing which produces 256-bit (64 hexadecimal characters) output. The [target](https://wiki.bitcoinsv.io/index.php/Target) hash is _24 bits_ or _6 hexadecimal characters_ of 0. During testing, I could mine a block on average around 2-7 minutes.
21
22> We're expecting you to use existing tools and implementations. Standards are hard. [Don't roll your own crypto](https://www.reddit.com/r/crypto/comments/2coqsy/dont_roll_your_own/). Feel free to ask questions. Collaborate.
23
24You might ask,
25
26> But if nobody has any Gradecoin then how do we have transactions?
27
28There is a bank! Their public key is `31415926535897932384626433832795028841971693993751058209749445923` and they have some amount of Gradecoin preloaded. It's also the only account that you can send transactions requests _to_ yourself.
29
30# Coinbase
31The first transactions of a block is called the `coinbase`. They are the **author** of the block proposal and if the block is accepted then they get compensated for their efforts with some Gradecoin.
32
33# Public Key Signatures
34Gradecoin uses 2048 bit RSA keyspairs.
35
36# Services
37## /register
38- Student creates their own 2048 bit RSA `keypair`
39- Downloads `Gradecoin`'s Public Key from [Moodle](https://odtuclass.metu.edu.tr/my/)
40- Encrypts their JSON wrapped `Public Key`, `Student ID` and one time `passwd` using Gradecoin's Public Key
41- Their public key is now in our database and can be used to sign their JWT's during requests
42
43## /transaction
44- You can offer a [Transaction](/transaction) - POST request
45 - The request should have `Authorization`
46 - The request header should be signed by the Public Key of the `by` field in the transaction
47- fetch the list of `Transaction`s - GET request
48
49## /block
50- offer a [`schema::Block`] - POST request
51 - The request should have `Authorization`
52 - The [`schema::Block::transaction_list`] of the block should be a subset of [`schema::Db::pending_transactions`]
53- fetch the last accepted [`schema::Block`] - GET request
54
55`Authorization`: The request header should have Bearer JWT.Token signed with Student Public Key
56
57# Questions
58## This all sound complicated!
59- I've drawn inspiration from [actual Bitcoin transactions](https://explorer.bitcoin.com/btc) and [warp](https://github.com/seanmonstar/warp/blob/master/examples/todos.rs). The simplicity of the system is how little interfaces it has.
60- Don't know where to start? Gradecoin uses RESTful API; simple `curl` commands or even your browser will work! [This website can help as well](https://curl.trillworks.com/).
61- [JWT Debugger](https://jwt.io) and the corresponding [RFC](https://tools.ietf.org/html/rfc7519)
62- Remember that you are absolutely encouraged to grab off-the-shelf implementations for every cryptography primitive you will use. You can start by finding a code snippet to generate a RSA keypair?
63
64## I found a bug!
65Thank you! Please [let me know](mailto:yigit@ceng.metu.edu.tr) so we can solve it.
66
67## I hacked the server!
68That wasn't supposed to happen :( I did not place any intentional vulnerabilities to the system so if you cracked something, it was not intended. Please don't abuse it and let me know so I can patch it.
69
70## Submission?
71At the end of the _simulation_, your Gradecoin balance will be your grade. I will also expect a unique client programmed in either;
72- c
73- c++
74- perl
75- rust
76- python
77- random assortment of bash scripts
78
79If your favourite programming language is missing please let me know 🤷?
80
81## Can my friends play?
82Sadly, no. Student's who are enrolled to the class will receive one-time-passwords for authentication.
83
84## How and or Why?
85- [Built](https://xkcd.com/2314/), [with](https://lofi.cafe/) [Rust](https://xkcd.com/2418/)
diff --git a/site/content/block_docs.md b/site/content/block_docs.md
new file mode 100644
index 0000000..c1d61e9
--- /dev/null
+++ b/site/content/block_docs.md
@@ -0,0 +1,30 @@
1+++
2title = "Blocks"
3description = "Block Documentation"
4weight = 10
5+++
6
7A block that was proposed to commit Transactions in `transaction_list` to the
8ledger with a nonce that made `hash` valid; 6 zeroes at the left hand side of the
9hash (24 bytes).
10
11We are _mining_ using [blake2s](https://www.blake2.net/) algorithm, which produces 256 bit hashes. Hash/second is roughly 20x10^3 on my machine, a new block can be mined in around 4-6 minutes.
12
13# Requests
14
15## GET
16A HTTP `GET` request to [/block](/block) endpoint will return the latest mined block.
17
18## POST
19
20A HTTP `POST` request with Authorization using JWT will allow you to propose your own blocks.
21
22# Fields
23```
24transaction_list: [array of Fingerprints]
25nonce: unsigned 32-bit integer
26timestamp: ISO 8601 <date>T<time>
27hash: String
28```
29
30[ISO 8601 Reference](https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations)
diff --git a/site/content/register_docs.md b/site/content/register_docs.md
new file mode 100644
index 0000000..83aef7f
--- /dev/null
+++ b/site/content/register_docs.md
@@ -0,0 +1,39 @@
1+++
2title = "Register"
3description = "Register Documentation"
4weight = 3
5+++
6
7POST request to /register endpoint
8
9Lets a user to authenticate themselves to the system.
10Only people who are enrolled to the class can open Gradecoin accounts.
11This is enforced with your Student ID and a one time password you will receive.
12
13# Authentication Process
14- Gradecoin's Public Key (`gradecoin_public_key`) is listed on our Moodle page.
15- You pick a short temporary key (`k_temp`)
16- Create a JSON object (`auth_plaintext`) with your `metu_id` and `public key` in base64 (PEM) format (`S_PK`) [reference](https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem)
17```json
18{
19 "student_id": "e12345",
20 "passwd": "15 char secret",
21 "public_key": "---BEGIN PUBLIC KEY..."
22}
23```
24
25- Pick a random IV.
26- Encrypt the serialized string of `auth_plaintext` with 128 bit block AES in CBC mode with Pkcs7 padding using the temporary key (`k_temp`), the result is `auth_ciphertext`. Encode this with base64.
27- The temporary key you have picked `k_temp` is encrypted using RSA with OAEP padding scheme
28using SHA-256 with `gradecoin_public_key`, giving us `key_ciphertext`. Encode this with base 64.
29- The payload JSON object (`auth_request`) can be serialized now:
30
31```json
32{
33 "c": "auth_ciphertext",
34 "iv": "hexadecimal",
35 "key": "key_ciphertext"
36}
37```
38
39If your authentication process was valid, you will be given access and your public key fingerprint that is your address.
diff --git a/site/content/transaction_docs.md b/site/content/transaction_docs.md
new file mode 100644
index 0000000..820c35f
--- /dev/null
+++ b/site/content/transaction_docs.md
@@ -0,0 +1,26 @@
1+++
2title = "Transactions"
3description = "Transaction documentation"
4weight = 6
5+++
6
7A transaction request between `source` and `target` to move `amount` Gradecoin.
8
9# Requests
10
11## GET
12A HTTP `GET` request to [/transaction](/transaction) endpoint will return the current list of pending transactions.
13
14## POST
15
16A HTTP `POST` request with Authorization using JWT to [/transaction](/transactions) will allow you to propose your own transactions.
17
18
19# Fields
20```
21by: Fingerprint
22source: Fingerprint
23target: Fingerprint
24amount: unsigned 16 bit integer
25timestamp: ISO 8601 <date>T<time>
26```
diff --git a/site/public/404.html b/site/public/404.html
new file mode 100644
index 0000000..f8414f0
--- /dev/null
+++ b/site/public/404.html
@@ -0,0 +1,3 @@
1<!doctype html>
2<title>404 Not Found</title>
3<h1>404 Not Found</h1>
diff --git a/static/android-chrome-192x192.png b/site/public/android-chrome-192x192.png
index 023ddbd..023ddbd 100644
--- a/static/android-chrome-192x192.png
+++ b/site/public/android-chrome-192x192.png
Binary files differ
diff --git a/static/android-chrome-512x512.png b/site/public/android-chrome-512x512.png
index 4251933..4251933 100644
--- a/static/android-chrome-512x512.png
+++ b/site/public/android-chrome-512x512.png
Binary files differ
diff --git a/static/apple-touch-icon.png b/site/public/apple-touch-icon.png
index cd8e4c8..cd8e4c8 100644
--- a/static/apple-touch-icon.png
+++ b/site/public/apple-touch-icon.png
Binary files differ
diff --git a/site/public/block-docs/index.html b/site/public/block-docs/index.html
new file mode 100644
index 0000000..de4d4e2
--- /dev/null
+++ b/site/public/block-docs/index.html
@@ -0,0 +1,168 @@
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5 <meta charset="UTF-8">
6 <title>Blocks | Gradecoin </title>
7 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
8 <style>
9 :root {
10 /* Primary theme color */
11 --primary-color: #F8D12F;
12 /* Primary theme text color */
13 --primary-text-color: #1E2329;
14 /* Primary theme link color */
15 --primary-link-color: #2F57F7;
16 /* Secondary color: the background body color */
17 --secondary-color: #FAFAFA;
18 --secondary-text-color: #303030;
19 /* Highlight text color of table of content */
20 --toc-highlight-text-color: #d46e13;
21 }
22</style>
23
24 <link href="https://fonts.googleapis.com/css?family=Alfa+Slab+One&display=swap" rel="stylesheet">
25 <link href="https://fonts.googleapis.com/css?family=Fira+Sans:400,500,600&display=swap" rel="stylesheet">
26 <link rel="stylesheet" href="/normalize.css">
27 <link rel="stylesheet" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;juice.css">
28
29<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
30
31</head>
32
33<body>
34
35<header class="box-shadow">
36
37
38<a href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;">
39 <div class="logo">
40 <img src="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;gradecoin.png" alt="logo">
41 Gradecoin
42 </div>
43</a>
44
45<nav>
46
47 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;register-docs&#x2F;">Register</a>
48
49 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;jwt&#x2F;">JWT</a>
50
51 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;transaction-docs&#x2F;">Transactions</a>
52
53 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;block-docs&#x2F;">Blocks</a>
54
55
56
57 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;github.com&#x2F;zhuowei&#x2F;nft_ptr#why">why?</a>
58
59
60</nav>
61
62</header>
63
64
65 <main>
66
67
68
69
70
71 <div class="toc">
72 <div class="toc-sticky">
73
74 <div class="toc-item">
75 <a class="subtext" href="https://gradecoin.xyz/block-docs/#requests">Requests</a>
76 </div>
77
78
79 <div class="toc-item-child">
80 <a class="subtext" href="https://gradecoin.xyz/block-docs/#get"><small>- GET</small></a>
81 </div>
82
83 <div class="toc-item-child">
84 <a class="subtext" href="https://gradecoin.xyz/block-docs/#post"><small>- POST</small></a>
85 </div>
86
87
88
89 <div class="toc-item">
90 <a class="subtext" href="https://gradecoin.xyz/block-docs/#fields">Fields</a>
91 </div>
92
93
94 </div>
95 </div>
96
97
98
99 <div class="content text">
100
101<div class="heading-text">Block Documentation</div>
102<p>A block that was proposed to commit Transactions in <code>transaction_list</code> to the
103ledger with a nonce that made <code>hash</code> valid; 6 zeroes at the left hand side of the
104hash (24 bytes).</p>
105<p>We are <em>mining</em> using <a href="https://www.blake2.net/">blake2s</a> algorithm, which produces 256 bit hashes. Hash/second is roughly 20x10^3 on my machine, a new block can be mined in around 4-6 minutes.</p>
106<h1 id="requests">Requests</h1>
107<h2 id="get">GET</h2>
108<p>A HTTP <code>GET</code> request to <a href="/block">/block</a> endpoint will return the latest mined block.</p>
109<h2 id="post">POST</h2>
110<p>A HTTP <code>POST</code> request with Authorization using JWT will allow you to propose your own blocks.</p>
111<h1 id="fields">Fields</h1>
112<pre style="background-color:#ffffff;">
113<code><span style="color:#545052;">transaction_list: [array of Fingerprints]
114nonce: unsigned 32-bit integer
115timestamp: ISO 8601 &lt;date&gt;T&lt;time&gt;
116hash: String
117</span></code></pre>
118<p><a href="https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations">ISO 8601 Reference</a></p>
119
120
121 </div>
122
123
124
125 </main>
126
127
128<footer>
129Built For ⁂ CENG489 ⁂ Introduction to Computer Security
130</footer>
131
132</body>
133<script>
134 function highlightNav(heading) {
135 let pathname = location.pathname;
136 document.querySelectorAll(".toc a").forEach((item) => {
137 item.classList.remove("active");
138 });
139 document.querySelector(".toc a[href$='" + pathname + "#" + heading + "']").classList.add("active");
140 }
141
142 let currentHeading = "";
143 window.onscroll = function () {
144 let h = document.querySelectorAll("h1,h2,h3,h4,h5,h6");
145 let elementArr = [];
146
147 h.forEach(item => {
148 if (item.id !== "") {
149 elementArr[item.id] = item.getBoundingClientRect().top;
150 }
151 });
152 elementArr.sort();
153 for (let key in elementArr) {
154 if (!elementArr.hasOwnProperty(key)) {
155 continue;
156 }
157 if (elementArr[key] > 0 && elementArr[key] < 300) {
158 if (currentHeading !== key) {
159 highlightNav(key);
160 currentHeading = key;
161 }
162 break;
163 }
164 }
165 }
166</script>
167
168</html>
diff --git a/site/public/elasticlunr.min.js b/site/public/elasticlunr.min.js
new file mode 100644
index 0000000..79dad65
--- /dev/null
+++ b/site/public/elasticlunr.min.js
@@ -0,0 +1,10 @@
1/**
2 * elasticlunr - http://weixsong.github.io
3 * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.6
4 *
5 * Copyright (C) 2017 Oliver Nightingale
6 * Copyright (C) 2017 Wei Song
7 * MIT Licensed
8 * @license
9 */
10!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u<s.length;u++){var a=s[u];r[a]=this.pipeline.run(t.tokenizer(e[a]))}var l={};for(var c in o){var d=r[c]||r.any;if(d){var f=this.fieldSearch(d,c,o),h=o[c].boost;for(var p in f)f[p]=f[p]*h;for(var p in f)p in l?l[p]+=f[p]:l[p]=f[p]}}var v,g=[];for(var p in l)v={ref:p,score:l[p]},this.documentStore.hasDoc(p)&&(v.doc=this.documentStore.getDoc(p)),g.push(v);return g.sort(function(e,t){return t.score-e.score}),g},t.Index.prototype.fieldSearch=function(e,t,n){var i=n[t].bool,o=n[t].expand,r=n[t].boost,s=null,u={};return 0!==r?(e.forEach(function(e){var n=[e];1==o&&(n=this.index[t].expandToken(e));var r={};n.forEach(function(n){var o=this.index[t].getDocs(n),a=this.idf(n,t);if(s&&"AND"==i){var l={};for(var c in s)c in o&&(l[c]=o[c]);o=l}n==e&&this.fieldSearchStats(u,n,o);for(var c in o){var d=this.index[t].getTermFrequency(n,c),f=this.documentStore.getFieldLength(c,t),h=1;0!=f&&(h=1/Math.sqrt(f));var p=1;n!=e&&(p=.15*(1-(n.length-e.length)/n.length));var v=d*a*h*p;c in r?r[c]+=v:r[c]=v}},this),s=this.mergeScores(s,r,i)},this),s=this.coordNorm(s,u,e.length)):void 0},t.Index.prototype.mergeScores=function(e,t,n){if(!e)return t;if("AND"==n){var i={};for(var o in t)o in e&&(i[o]=e[o]+t[o]);return i}for(var o in t)o in e?e[o]+=t[o]:e[o]=t[o];return e},t.Index.prototype.fieldSearchStats=function(e,t,n){for(var i in n)i in e?e[i].push(t):e[i]=[t]},t.Index.prototype.coordNorm=function(e,t,n){for(var i in e)if(i in t){var o=t[i].length;e[i]=e[i]*o/n}return e},t.Index.prototype.toJSON=function(){var e={};return this._fields.forEach(function(t){e[t]=this.index[t].toJSON()},this),{version:t.version,fields:this._fields,ref:this._ref,documentStore:this.documentStore.toJSON(),index:e,pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(e){var t=Array.prototype.slice.call(arguments,1);t.unshift(this),e.apply(this,t)},t.DocumentStore=function(e){this._save=null===e||void 0===e?!0:e,this.docs={},this.docInfo={},this.length=0},t.DocumentStore.load=function(e){var t=new this;return t.length=e.length,t.docs=e.docs,t.docInfo=e.docInfo,t._save=e.save,t},t.DocumentStore.prototype.isDocStored=function(){return this._save},t.DocumentStore.prototype.addDoc=function(t,n){this.hasDoc(t)||this.length++,this.docs[t]=this._save===!0?e(n):null},t.DocumentStore.prototype.getDoc=function(e){return this.hasDoc(e)===!1?null:this.docs[e]},t.DocumentStore.prototype.hasDoc=function(e){return e in this.docs},t.DocumentStore.prototype.removeDoc=function(e){this.hasDoc(e)&&(delete this.docs[e],delete this.docInfo[e],this.length--)},t.DocumentStore.prototype.addFieldLength=function(e,t,n){null!==e&&void 0!==e&&0!=this.hasDoc(e)&&(this.docInfo[e]||(this.docInfo[e]={}),this.docInfo[e][t]=n)},t.DocumentStore.prototype.updateFieldLength=function(e,t,n){null!==e&&void 0!==e&&0!=this.hasDoc(e)&&this.addFieldLength(e,t,n)},t.DocumentStore.prototype.getFieldLength=function(e,t){return null===e||void 0===e?0:e in this.docs&&t in this.docInfo[e]?this.docInfo[e][t]:0},t.DocumentStore.prototype.toJSON=function(){return{docs:this.docs,docInfo:this.docInfo,length:this.length,save:this._save}},t.stemmer=function(){var e={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},t={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",o=n+"[^aeiouy]*",r=i+"[aeiou]*",s="^("+o+")?"+r+o,u="^("+o+")?"+r+o+"("+r+")?$",a="^("+o+")?"+r+o+r+o,l="^("+o+")?"+i,c=new RegExp(s),d=new RegExp(a),f=new RegExp(u),h=new RegExp(l),p=/^(.+?)(ss|i)es$/,v=/^(.+?)([^s])s$/,g=/^(.+?)eed$/,m=/^(.+?)(ed|ing)$/,y=/.$/,S=/(at|bl|iz)$/,x=new RegExp("([^aeiouylsz])\\1$"),w=new RegExp("^"+o+i+"[^aeiouwxy]$"),I=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,D=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,F=/^(.+?)(s|t)(ion)$/,_=/^(.+?)e$/,P=/ll$/,k=new RegExp("^"+o+i+"[^aeiouwxy]$"),z=function(n){var i,o,r,s,u,a,l;if(n.length<3)return n;if(r=n.substr(0,1),"y"==r&&(n=r.toUpperCase()+n.substr(1)),s=p,u=v,s.test(n)?n=n.replace(s,"$1$2"):u.test(n)&&(n=n.replace(u,"$1$2")),s=g,u=m,s.test(n)){var z=s.exec(n);s=c,s.test(z[1])&&(s=y,n=n.replace(s,""))}else if(u.test(n)){var z=u.exec(n);i=z[1],u=h,u.test(i)&&(n=i,u=S,a=x,l=w,u.test(n)?n+="e":a.test(n)?(s=y,n=n.replace(s,"")):l.test(n)&&(n+="e"))}if(s=I,s.test(n)){var z=s.exec(n);i=z[1],n=i+"i"}if(s=b,s.test(n)){var z=s.exec(n);i=z[1],o=z[2],s=c,s.test(i)&&(n=i+e[o])}if(s=E,s.test(n)){var z=s.exec(n);i=z[1],o=z[2],s=c,s.test(i)&&(n=i+t[o])}if(s=D,u=F,s.test(n)){var z=s.exec(n);i=z[1],s=d,s.test(i)&&(n=i)}else if(u.test(n)){var z=u.exec(n);i=z[1]+z[2],u=d,u.test(i)&&(n=i)}if(s=_,s.test(n)){var z=s.exec(n);i=z[1],s=d,u=f,a=k,(s.test(i)||u.test(i)&&!a.test(i))&&(n=i)}return s=P,u=d,s.test(n)&&u.test(n)&&(s=y,n=n.replace(s,"")),"y"==r&&(n=r.toLowerCase()+n.substr(1)),n};return z}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.stopWordFilter=function(e){return e&&t.stopWordFilter.stopWords[e]!==!0?e:void 0},t.clearStopWords=function(){t.stopWordFilter.stopWords={}},t.addStopWords=function(e){null!=e&&Array.isArray(e)!==!1&&e.forEach(function(e){t.stopWordFilter.stopWords[e]=!0},this)},t.resetStopWords=function(){t.stopWordFilter.stopWords=t.defaultStopWords},t.defaultStopWords={"":!0,a:!0,able:!0,about:!0,across:!0,after:!0,all:!0,almost:!0,also:!0,am:!0,among:!0,an:!0,and:!0,any:!0,are:!0,as:!0,at:!0,be:!0,because:!0,been:!0,but:!0,by:!0,can:!0,cannot:!0,could:!0,dear:!0,did:!0,"do":!0,does:!0,either:!0,"else":!0,ever:!0,every:!0,"for":!0,from:!0,get:!0,got:!0,had:!0,has:!0,have:!0,he:!0,her:!0,hers:!0,him:!0,his:!0,how:!0,however:!0,i:!0,"if":!0,"in":!0,into:!0,is:!0,it:!0,its:!0,just:!0,least:!0,let:!0,like:!0,likely:!0,may:!0,me:!0,might:!0,most:!0,must:!0,my:!0,neither:!0,no:!0,nor:!0,not:!0,of:!0,off:!0,often:!0,on:!0,only:!0,or:!0,other:!0,our:!0,own:!0,rather:!0,said:!0,say:!0,says:!0,she:!0,should:!0,since:!0,so:!0,some:!0,than:!0,that:!0,the:!0,their:!0,them:!0,then:!0,there:!0,these:!0,they:!0,"this":!0,tis:!0,to:!0,too:!0,twas:!0,us:!0,wants:!0,was:!0,we:!0,were:!0,what:!0,when:!0,where:!0,which:!0,"while":!0,who:!0,whom:!0,why:!0,will:!0,"with":!0,would:!0,yet:!0,you:!0,your:!0},t.stopWordFilter.stopWords=t.defaultStopWords,t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(e){if(null===e||void 0===e)throw new Error("token should not be undefined");return e.replace(/^\W+/,"").replace(/\W+$/,"")},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.InvertedIndex=function(){this.root={docs:{},df:0}},t.InvertedIndex.load=function(e){var t=new this;return t.root=e.root,t},t.InvertedIndex.prototype.addToken=function(e,t,n){for(var n=n||this.root,i=0;i<=e.length-1;){var o=e[i];o in n||(n[o]={docs:{},df:0}),i+=1,n=n[o]}var r=t.ref;n.docs[r]?n.docs[r]={tf:t.tf}:(n.docs[r]={tf:t.tf},n.df+=1)},t.InvertedIndex.prototype.hasToken=function(e){if(!e)return!1;for(var t=this.root,n=0;n<e.length;n++){if(!t[e[n]])return!1;t=t[e[n]]}return!0},t.InvertedIndex.prototype.getNode=function(e){if(!e)return null;for(var t=this.root,n=0;n<e.length;n++){if(!t[e[n]])return null;t=t[e[n]]}return t},t.InvertedIndex.prototype.getDocs=function(e){var t=this.getNode(e);return null==t?{}:t.docs},t.InvertedIndex.prototype.getTermFrequency=function(e,t){var n=this.getNode(e);return null==n?0:t in n.docs?n.docs[t].tf:0},t.InvertedIndex.prototype.getDocFreq=function(e){var t=this.getNode(e);return null==t?0:t.df},t.InvertedIndex.prototype.removeToken=function(e,t){if(e){var n=this.getNode(e);null!=n&&t in n.docs&&(delete n.docs[t],n.df-=1)}},t.InvertedIndex.prototype.expandToken=function(e,t,n){if(null==e||""==e)return[];var t=t||[];if(void 0==n&&(n=this.getNode(e),null==n))return t;n.df>0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e<arguments.length;e++)t=arguments[e],~this.indexOf(t)||this.elements.splice(this.locationFor(t),0,t);this.length=this.elements.length},lunr.SortedSet.prototype.toArray=function(){return this.elements.slice()},lunr.SortedSet.prototype.map=function(e,t){return this.elements.map(e,t)},lunr.SortedSet.prototype.forEach=function(e,t){return this.elements.forEach(e,t)},lunr.SortedSet.prototype.indexOf=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]<u[i]?n++:s[n]>u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o<r.length;o++)i.add(r[o]);return i},lunr.SortedSet.prototype.toJSON=function(){return this.toArray()},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.elasticlunr=t()}(this,function(){return t})}();
diff --git a/static/favicon-16x16.png b/site/public/favicon-16x16.png
index bd63d34..bd63d34 100644
--- a/static/favicon-16x16.png
+++ b/site/public/favicon-16x16.png
Binary files differ
diff --git a/static/favicon-32x32.png b/site/public/favicon-32x32.png
index e343587..e343587 100644
--- a/static/favicon-32x32.png
+++ b/site/public/favicon-32x32.png
Binary files differ
diff --git a/static/favicon.ico b/site/public/favicon.ico
index 45d8bfe..45d8bfe 100644
--- a/static/favicon.ico
+++ b/site/public/favicon.ico
Binary files differ
diff --git a/static/gradecoin.png b/site/public/gradecoin.png
index eeb670c..eeb670c 100644
--- a/static/gradecoin.png
+++ b/site/public/gradecoin.png
Binary files differ
diff --git a/site/public/index.html b/site/public/index.html
new file mode 100644
index 0000000..8986841
--- /dev/null
+++ b/site/public/index.html
@@ -0,0 +1,319 @@
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5 <meta charset="UTF-8">
6 <title>Gradecoin</title>
7 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
8 <style>
9 :root {
10 /* Primary theme color */
11 --primary-color: #F8D12F;
12 /* Primary theme text color */
13 --primary-text-color: #1E2329;
14 /* Primary theme link color */
15 --primary-link-color: #2F57F7;
16 /* Secondary color: the background body color */
17 --secondary-color: #FAFAFA;
18 --secondary-text-color: #303030;
19 /* Highlight text color of table of content */
20 --toc-highlight-text-color: #d46e13;
21 }
22</style>
23
24 <link href="https://fonts.googleapis.com/css?family=Alfa+Slab+One&display=swap" rel="stylesheet">
25 <link href="https://fonts.googleapis.com/css?family=Fira+Sans:400,500,600&display=swap" rel="stylesheet">
26 <link rel="stylesheet" href="/normalize.css">
27 <link rel="stylesheet" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;juice.css">
28
29<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
30
31</head>
32
33<body>
34
35
36 <header class="pos-absolute" style="background-color: transparent">
37
38
39<a href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;">
40 <div class="logo">
41 <img src="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;gradecoin.png" alt="logo">
42 Gradecoin
43 </div>
44</a>
45
46<nav>
47
48 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;register-docs&#x2F;">Register</a>
49
50 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;jwt&#x2F;">JWT</a>
51
52 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;transaction-docs&#x2F;">Transactions</a>
53
54 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;block-docs&#x2F;">Blocks</a>
55
56
57
58 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;github.com&#x2F;zhuowei&#x2F;nft_ptr#why">why?</a>
59
60
61</nav>
62
63 </header>
64
65 <div class="hero">
66
67<section class="text-center">
68 <h1 class="heading-text animate__animated animate__jackInTheBox" style="font-size: 50px">
69 Mine your own grades
70 </h1>
71 <h3 class="title-text">
72 <b>Gradecoin</b> is the latest cutting edge blockchain technology agile grading framework that drives organic engagement and other buzzwords, with big data mining search engine optimization
73 </h3>
74 <div>
75 </div>
76</section>
77<img class="hero-image" style="width: 40%" src="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;gradecoin.png">
78
79<div class="explore-more text"
80 onclick="document.getElementById('features').scrollIntoView({behavior: 'smooth'})">
81 ⇩ Learn How ⇩
82</div>
83<style>
84
85.hero section {
86 padding: 0 5rem;
87}
88
89@media screen and (max-width: 768px) {
90 .hero section {
91 padding: 0 2rem;
92 }
93
94 .hero-image {
95 display: none
96 }
97
98}
99footer {
100 color: #8b8b8b;
101}
102</style>
103
104 </div>
105
106
107
108 <main>
109
110
111
112
113
114 <div class="toc">
115 <div class="toc-sticky">
116
117 <div class="toc-item">
118 <a class="subtext" href="https://gradecoin.xyz/#welcome-to-gradecoin">Welcome to Gradecoin!</a>
119 </div>
120
121
122 <div class="toc-item">
123 <a class="subtext" href="https://gradecoin.xyz/#coinbase">Coinbase</a>
124 </div>
125
126
127 <div class="toc-item">
128 <a class="subtext" href="https://gradecoin.xyz/#public-key-signatures">Public Key Signatures</a>
129 </div>
130
131
132 <div class="toc-item">
133 <a class="subtext" href="https://gradecoin.xyz/#services">Services</a>
134 </div>
135
136
137 <div class="toc-item-child">
138 <a class="subtext" href="https://gradecoin.xyz/#register"><small>- &#x2F;register</small></a>
139 </div>
140
141 <div class="toc-item-child">
142 <a class="subtext" href="https://gradecoin.xyz/#transaction"><small>- &#x2F;transaction</small></a>
143 </div>
144
145 <div class="toc-item-child">
146 <a class="subtext" href="https://gradecoin.xyz/#block"><small>- &#x2F;block</small></a>
147 </div>
148
149
150
151 <div class="toc-item">
152 <a class="subtext" href="https://gradecoin.xyz/#questions">Questions</a>
153 </div>
154
155
156 <div class="toc-item-child">
157 <a class="subtext" href="https://gradecoin.xyz/#this-all-sound-complicated"><small>- This all sound complicated!</small></a>
158 </div>
159
160 <div class="toc-item-child">
161 <a class="subtext" href="https://gradecoin.xyz/#i-found-a-bug"><small>- I found a bug!</small></a>
162 </div>
163
164 <div class="toc-item-child">
165 <a class="subtext" href="https://gradecoin.xyz/#i-hacked-the-server"><small>- I hacked the server!</small></a>
166 </div>
167
168 <div class="toc-item-child">
169 <a class="subtext" href="https://gradecoin.xyz/#submission"><small>- Submission?</small></a>
170 </div>
171
172 <div class="toc-item-child">
173 <a class="subtext" href="https://gradecoin.xyz/#can-my-friends-play"><small>- Can my friends play?</small></a>
174 </div>
175
176 <div class="toc-item-child">
177 <a class="subtext" href="https://gradecoin.xyz/#how-and-or-why"><small>- How and or Why?</small></a>
178 </div>
179
180
181
182 </div>
183 </div>
184
185
186
187 <div class="content text">
188
189 <div id="features" class="heading-text">Overview</div>
190 <h1 id="welcome-to-gradecoin">Welcome to Gradecoin!</h1>
191<p>Blockchains are incredibly simple yet can appear very complicated, we will see how they work and practice programming <em>production</em> cryptography code.</p>
192<p>This server is the sandbox for the PA1, it's currently running the Gradecoin application. Gradecoin is the faux currency we will use to simulate a blockchain network. At the end of the simulation, the amount of Gradecoin you hold will be your PA1 grade.</p>
193<p><strong>A quick summary</strong>: authenticate yourself to the system using public key encryption.
194Craft <a href="https://gradecoin.xyz/transaction-docs/">Transaction</a> proposals and tag them using <a href="https://gradecoin.xyz/jwt/">JWTs</a>.
195When there are enough transactions then you can propose <a href="https://gradecoin.xyz/block-docs/">Blocks</a> in the same way.
196Blocks need to be <em>mined</em> beforehand using Proof-of-work, or brute force.</p>
197<p>Gradecoin offers 3 endpoints at <a href="/register">/register</a>, <a href="/block">/block</a> and <a href="/transaction">/transaction</a>. You can only send GET requests to /block and /transaction without authorization.
198The server is programmed in <a href="https://www.service-architecture.com/articles/web-services/representational_state_transfer_rest.html">RESTful</a> architecture, there are no <code>DELETE</code>, <code>PUT</code> or <code>UPDATE</code> operations, though.</p>
199<p>Gradecoin uses a Proof-of-work block accepting mechanism. It uses single round <a href="https://www.blake2.net/">Blake2s</a> hashing which produces 256-bit (64 hexadecimal characters) output. The <a href="https://wiki.bitcoinsv.io/index.php/Target">target</a> hash is <em>24 bits</em> or <em>6 hexadecimal characters</em> of 0. During testing, I could mine a block on average around 2-7 minutes.</p>
200<blockquote>
201<p>We're expecting you to use existing tools and implementations. Standards are hard. <a href="https://www.reddit.com/r/crypto/comments/2coqsy/dont_roll_your_own/">Don't roll your own crypto</a>. Feel free to ask questions. Collaborate.</p>
202</blockquote>
203<p>You might ask,</p>
204<blockquote>
205<p>But if nobody has any Gradecoin then how do we have transactions?</p>
206</blockquote>
207<p>There is a bank! Their public key is <code>31415926535897932384626433832795028841971693993751058209749445923</code> and they have some amount of Gradecoin preloaded. It's also the only account that you can send transactions requests <em>to</em> yourself.</p>
208<h1 id="coinbase">Coinbase</h1>
209<p>The first transactions of a block is called the <code>coinbase</code>. They are the <strong>author</strong> of the block proposal and if the block is accepted then they get compensated for their efforts with some Gradecoin.</p>
210<h1 id="public-key-signatures">Public Key Signatures</h1>
211<p>Gradecoin uses 2048 bit RSA keyspairs.</p>
212<h1 id="services">Services</h1>
213<h2 id="register">/register</h2>
214<ul>
215<li>Student creates their own 2048 bit RSA <code>keypair</code></li>
216<li>Downloads <code>Gradecoin</code>'s Public Key from <a href="https://odtuclass.metu.edu.tr/my/">Moodle</a></li>
217<li>Encrypts their JSON wrapped <code>Public Key</code>, <code>Student ID</code> and one time <code>passwd</code> using Gradecoin's Public Key</li>
218<li>Their public key is now in our database and can be used to sign their JWT's during requests</li>
219</ul>
220<h2 id="transaction">/transaction</h2>
221<ul>
222<li>You can offer a <a href="/transaction">Transaction</a> - POST request
223<ul>
224<li>The request should have <code>Authorization</code></li>
225<li>The request header should be signed by the Public Key of the <code>by</code> field in the transaction</li>
226</ul>
227</li>
228<li>fetch the list of <code>Transaction</code>s - GET request</li>
229</ul>
230<h2 id="block">/block</h2>
231<ul>
232<li>offer a [<code>schema::Block</code>] - POST request
233<ul>
234<li>The request should have <code>Authorization</code></li>
235<li>The [<code>schema::Block::transaction_list</code>] of the block should be a subset of [<code>schema::Db::pending_transactions</code>]</li>
236</ul>
237</li>
238<li>fetch the last accepted [<code>schema::Block</code>] - GET request</li>
239</ul>
240<p><code>Authorization</code>: The request header should have Bearer JWT.Token signed with Student Public Key</p>
241<h1 id="questions">Questions</h1>
242<h2 id="this-all-sound-complicated">This all sound complicated!</h2>
243<ul>
244<li>I've drawn inspiration from <a href="https://explorer.bitcoin.com/btc">actual Bitcoin transactions</a> and <a href="https://github.com/seanmonstar/warp/blob/master/examples/todos.rs">warp</a>. The simplicity of the system is how little interfaces it has.</li>
245<li>Don't know where to start? Gradecoin uses RESTful API; simple <code>curl</code> commands or even your browser will work! <a href="https://curl.trillworks.com/">This website can help as well</a>.</li>
246<li><a href="https://jwt.io">JWT Debugger</a> and the corresponding <a href="https://tools.ietf.org/html/rfc7519">RFC</a></li>
247<li>Remember that you are absolutely encouraged to grab off-the-shelf implementations for every cryptography primitive you will use. You can start by finding a code snippet to generate a RSA keypair?</li>
248</ul>
249<h2 id="i-found-a-bug">I found a bug!</h2>
250<p>Thank you! Please <a href="mailto:yigit@ceng.metu.edu.tr">let me know</a> so we can solve it.</p>
251<h2 id="i-hacked-the-server">I hacked the server!</h2>
252<p>That wasn't supposed to happen :( I did not place any intentional vulnerabilities to the system so if you cracked something, it was not intended. Please don't abuse it and let me know so I can patch it.</p>
253<h2 id="submission">Submission?</h2>
254<p>At the end of the <em>simulation</em>, your Gradecoin balance will be your grade. I will also expect a unique client programmed in either;</p>
255<ul>
256<li>c</li>
257<li>c++</li>
258<li>perl</li>
259<li>rust</li>
260<li>python</li>
261<li>random assortment of bash scripts</li>
262</ul>
263<p>If your favourite programming language is missing please let me know 🤷?</p>
264<h2 id="can-my-friends-play">Can my friends play?</h2>
265<p>Sadly, no. Student's who are enrolled to the class will receive one-time-passwords for authentication.</p>
266<h2 id="how-and-or-why">How and or Why?</h2>
267<ul>
268<li><a href="https://xkcd.com/2314/">Built</a>, <a href="https://lofi.cafe/">with</a> <a href="https://xkcd.com/2418/">Rust</a></li>
269</ul>
270
271
272 </div>
273
274
275
276 </main>
277
278
279<footer>
280Built For ⁂ CENG489 ⁂ Introduction to Computer Security
281</footer>
282
283</body>
284<script>
285 function highlightNav(heading) {
286 let pathname = location.pathname;
287 document.querySelectorAll(".toc a").forEach((item) => {
288 item.classList.remove("active");
289 });
290 document.querySelector(".toc a[href$='" + pathname + "#" + heading + "']").classList.add("active");
291 }
292
293 let currentHeading = "";
294 window.onscroll = function () {
295 let h = document.querySelectorAll("h1,h2,h3,h4,h5,h6");
296 let elementArr = [];
297
298 h.forEach(item => {
299 if (item.id !== "") {
300 elementArr[item.id] = item.getBoundingClientRect().top;
301 }
302 });
303 elementArr.sort();
304 for (let key in elementArr) {
305 if (!elementArr.hasOwnProperty(key)) {
306 continue;
307 }
308 if (elementArr[key] > 0 && elementArr[key] < 300) {
309 if (currentHeading !== key) {
310 highlightNav(key);
311 currentHeading = key;
312 }
313 break;
314 }
315 }
316 }
317</script>
318
319</html>
diff --git a/site/public/juice.css b/site/public/juice.css
new file mode 100644
index 0000000..219f864
--- /dev/null
+++ b/site/public/juice.css
@@ -0,0 +1 @@
.text-center{text-align:center}.pos-absolute{right:0;left:0;position:absolute}.box-shadow{box-shadow:0 2px 10px 2px #ddd}.heading-text{font-family:"Fira Sans", sans-serif;font-size:32px;font-weight:600;padding:10px 0 25px 0;color:var(--primary-text-color)}h1,.title-text{font-family:"Fira Sans", sans-serif;font-size:25px;font-weight:500;color:var(--primary-text-color);border-left:var(--primary-color) 8px solid;padding-left:10px}h2,.subtitle-text{font-family:"Fira Sans", sans-serif;font-size:20px;font-weight:500;color:var(--primary-text-color)}.text{font-family:"Fira Sans", sans-serif;font-size:18px;font-weight:400;line-height:26px;letter-spacing:0.2px;color:var(--primary-text-color)}.subtext{font-family:"Fira Sans", sans-serif;font-size:16px;font-weight:400;letter-spacing:0.1px}.content{padding:0 40px;display:flex;flex-direction:column;justify-content:center;overflow-x:auto}.content pre{overflow-x:auto;padding:1.25em 1.5em;white-space:pre;word-wrap:normal;background-color:white;color:#4a4a4a;font-size:.875em;font-family:monospace}.content code{background-color:white;color:#4a4a4a;font-size:.875em;font-weight:normal;padding:0.25em 0.5em;font-family:monospace}.content pre code{padding:0}.content a{color:var(--primary-link-color)}.content a:hover{text-decoration:underline}.content blockquote{border-left:#e2dede 8px solid;margin:0;background-color:#f2f1f0;padding:0 20px}body{padding:0;margin:0;box-sizing:border-box;background-color:var(--secondary-color)}a{text-decoration:none}ul{margin-top:0.5rem}ul>li{padding:0.3rem 0}p>img{width:100%;height:auto}header{background-color:var(--primary-color);color:black;padding:20px 50px;display:flex;align-items:center;justify-content:space-between}.logo{font-family:"Alfa Slab One", serif;font-size:32px;color:var(--primary-text-color);display:flex;align-items:center;margin:0 40px}.logo img{width:60px;margin:0 25px}.nav-item{margin:0 10px;text-decoration:none;font-size:18px;font-weight:bold}.nav-item:hover{color:#000;text-decoration:underline}.hero{display:flex;align-items:center;justify-content:space-evenly;height:100vh;background-color:var(--primary-color);overflow-x:hidden;padding:0 40px}.hero .explore-more{position:absolute;bottom:20px;cursor:pointer}main{display:flex;padding:50px 100px}main .toc{max-width:260px;min-width:240px}main .toc-item{padding:10px 20px;color:#424242}main .toc-item a,main .toc-item-child a{color:var(--secondary-text-color)}main .toc-item a:hover,main .toc-item-child a:hover{cursor:pointer;text-decoration:underline}main .toc-item a.active,main .toc-item-child a.active{color:var(--toc-highlight-text-color)}main .toc-item-child{padding:0 30px 5px;color:#424242}.toc-sticky{border-radius:3px;border-top:5px solid var(--primary-color);background-color:white;position:sticky;position:-webkit-sticky;position:-moz-sticky;position:-ms-sticky;position:-o-sticky;top:10px;padding:10px 0}footer{padding:50px;display:flex;flex-direction:column;justify-content:center;align-items:center;background-color:#202020;color:#fcfcfc}footer a{color:#fcfcfc;text-decoration:underline}@media screen and (min-width: 1280px){.content{max-width:60%;min-width:800px}}@media screen and (max-width: 768px){header{padding:10px 30px;flex-direction:column;align-items:center;justify-content:center}.logo{font-size:28px;margin:10px}.logo img{width:45px;margin:0 10px 0 0}.nav-item{margin:0 5px;font-size:14px}.hero{padding:40px 30px}main{padding:30px}.content{padding:0}.explore-more,.toc{display:none}}
diff --git a/site/public/jwt/index.html b/site/public/jwt/index.html
new file mode 100644
index 0000000..d06d45a
--- /dev/null
+++ b/site/public/jwt/index.html
@@ -0,0 +1,179 @@
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5 <meta charset="UTF-8">
6 <title>JWT | Gradecoin </title>
7 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
8 <style>
9 :root {
10 /* Primary theme color */
11 --primary-color: #F8D12F;
12 /* Primary theme text color */
13 --primary-text-color: #1E2329;
14 /* Primary theme link color */
15 --primary-link-color: #2F57F7;
16 /* Secondary color: the background body color */
17 --secondary-color: #FAFAFA;
18 --secondary-text-color: #303030;
19 /* Highlight text color of table of content */
20 --toc-highlight-text-color: #d46e13;
21 }
22</style>
23
24 <link href="https://fonts.googleapis.com/css?family=Alfa+Slab+One&display=swap" rel="stylesheet">
25 <link href="https://fonts.googleapis.com/css?family=Fira+Sans:400,500,600&display=swap" rel="stylesheet">
26 <link rel="stylesheet" href="/normalize.css">
27 <link rel="stylesheet" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;juice.css">
28
29<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
30
31</head>
32
33<body>
34
35<header class="box-shadow">
36
37
38<a href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;">
39 <div class="logo">
40 <img src="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;gradecoin.png" alt="logo">
41 Gradecoin
42 </div>
43</a>
44
45<nav>
46
47 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;register-docs&#x2F;">Register</a>
48
49 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;jwt&#x2F;">JWT</a>
50
51 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;transaction-docs&#x2F;">Transactions</a>
52
53 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;block-docs&#x2F;">Blocks</a>
54
55
56
57 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;github.com&#x2F;zhuowei&#x2F;nft_ptr#why">why?</a>
58
59
60</nav>
61
62</header>
63
64
65 <main>
66
67
68
69
70
71 <div class="toc">
72 <div class="toc-sticky">
73
74 <div class="toc-item">
75 <a class="subtext" href="https://gradecoin.xyz/jwt/#how">How?</a>
76 </div>
77
78
79 <div class="toc-item">
80 <a class="subtext" href="https://gradecoin.xyz/jwt/#algorithm">Algorithm</a>
81 </div>
82
83
84 <div class="toc-item">
85 <a class="subtext" href="https://gradecoin.xyz/jwt/#references">References</a>
86 </div>
87
88
89 </div>
90 </div>
91
92
93
94 <div class="content text">
95
96<div class="heading-text">JSON Web Token Documentation</div>
97<blockquote>
98<p>JSON Web Tokens are representations of claims, or authorization proofs that fit into the <code>Header</code> of HTTP requests.</p>
99</blockquote>
100<h1 id="how">How?</h1>
101<p>JWTs are used as the <a href="https://en.wikipedia.org/wiki/Message_authentication_code">MAC</a> of operations that require authorization:</p>
102<ul>
103<li>block proposal</li>
104<li>transaction proposal.</li>
105</ul>
106<p>They are send alongside the JSON request body in the <code>Header</code>;</p>
107<pre style="background-color:#ffffff;">
108<code class="language-html" data-lang="html"><span style="color:#545052;">Authorization: Bearer aaaaaa.bbbbbb.ccccc
109</span></code></pre>
110<p>Gradecoin uses 3 fields for the JWTs;</p>
111<pre style="background-color:#ffffff;">
112<code class="language-json" data-lang="json"><span style="color:#545052;">{
113&quot;</span><span style="color:#009854;">tha</span><span style="color:#545052;">&quot;: &quot;</span><span style="color:#009854;">Hash of the payload, check invididual references</span><span style="color:#545052;">&quot;,
114&quot;</span><span style="color:#009854;">iat</span><span style="color:#545052;">&quot;: &quot;</span><span style="color:#009854;">Issued At, Unix Time</span><span style="color:#545052;">&quot;,
115&quot;</span><span style="color:#009854;">exp</span><span style="color:#545052;">&quot;: &quot;</span><span style="color:#009854;">Expiration Time, epoch</span><span style="color:#545052;">&quot;
116}
117</span></code></pre>
118<ul>
119<li><code>tha</code> is explained in <a href="https://gradecoin.xyz/block-docs/">blocks</a> and <a href="https://gradecoin.xyz/transaction-docs/">transactions</a> documentations.</li>
120<li><code>iat</code> when the JWT was created in <a href="https://en.wikipedia.org/wiki/Unix_time">Unix Time</a> format</li>
121<li><code>exp</code> when the JWT will expire &amp; be rejected in <a href="https://en.wikipedia.org/wiki/Unix_time">Unix Time</a></li>
122</ul>
123<h1 id="algorithm">Algorithm</h1>
124<p>We are using <a href="https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1">RS256</a>, <code>RSASSA-PKCS1-v1_5 using SHA-256</code>. The JWTs you encode with your private RSA key will be decoded using the public key you have authenticated with. You can see how the process works <a href="https://jwt.io/">here</a>.</p>
125<h1 id="references">References</h1>
126<ul>
127<li><a href="https://tools.ietf.org/html/rfc7519">RFC, the ultimate reference</a></li>
128<li><a href="https://jwt.io/">JWT Debugger</a></li>
129</ul>
130
131
132 </div>
133
134
135
136 </main>
137
138
139<footer>
140Built For ⁂ CENG489 ⁂ Introduction to Computer Security
141</footer>
142
143</body>
144<script>
145 function highlightNav(heading) {
146 let pathname = location.pathname;
147 document.querySelectorAll(".toc a").forEach((item) => {
148 item.classList.remove("active");
149 });
150 document.querySelector(".toc a[href$='" + pathname + "#" + heading + "']").classList.add("active");
151 }
152
153 let currentHeading = "";
154 window.onscroll = function () {
155 let h = document.querySelectorAll("h1,h2,h3,h4,h5,h6");
156 let elementArr = [];
157
158 h.forEach(item => {
159 if (item.id !== "") {
160 elementArr[item.id] = item.getBoundingClientRect().top;
161 }
162 });
163 elementArr.sort();
164 for (let key in elementArr) {
165 if (!elementArr.hasOwnProperty(key)) {
166 continue;
167 }
168 if (elementArr[key] > 0 && elementArr[key] < 300) {
169 if (currentHeading !== key) {
170 highlightNav(key);
171 currentHeading = key;
172 }
173 break;
174 }
175 }
176 }
177</script>
178
179</html>
diff --git a/site/public/normalize.css b/site/public/normalize.css
new file mode 100644
index 0000000..192eb9c
--- /dev/null
+++ b/site/public/normalize.css
@@ -0,0 +1,349 @@
1/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
2
3/* Document
4 ========================================================================== */
5
6/**
7 * 1. Correct the line height in all browsers.
8 * 2. Prevent adjustments of font size after orientation changes in iOS.
9 */
10
11html {
12 line-height: 1.15; /* 1 */
13 -webkit-text-size-adjust: 100%; /* 2 */
14}
15
16/* Sections
17 ========================================================================== */
18
19/**
20 * Remove the margin in all browsers.
21 */
22
23body {
24 margin: 0;
25}
26
27/**
28 * Render the `main` element consistently in IE.
29 */
30
31main {
32 display: block;
33}
34
35/**
36 * Correct the font size and margin on `h1` elements within `section` and
37 * `article` contexts in Chrome, Firefox, and Safari.
38 */
39
40h1 {
41 font-size: 2em;
42 margin: 0.67em 0;
43}
44
45/* Grouping content
46 ========================================================================== */
47
48/**
49 * 1. Add the correct box sizing in Firefox.
50 * 2. Show the overflow in Edge and IE.
51 */
52
53hr {
54 box-sizing: content-box; /* 1 */
55 height: 0; /* 1 */
56 overflow: visible; /* 2 */
57}
58
59/**
60 * 1. Correct the inheritance and scaling of font size in all browsers.
61 * 2. Correct the odd `em` font sizing in all browsers.
62 */
63
64pre {
65 font-family: monospace, monospace; /* 1 */
66 font-size: 1em; /* 2 */
67}
68
69/* Text-level semantics
70 ========================================================================== */
71
72/**
73 * Remove the gray background on active links in IE 10.
74 */
75
76a {
77 background-color: transparent;
78}
79
80/**
81 * 1. Remove the bottom border in Chrome 57-
82 * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
83 */
84
85abbr[title] {
86 border-bottom: none; /* 1 */
87 text-decoration: underline; /* 2 */
88 text-decoration: underline dotted; /* 2 */
89}
90
91/**
92 * Add the correct font weight in Chrome, Edge, and Safari.
93 */
94
95b,
96strong {
97 font-weight: bolder;
98}
99
100/**
101 * 1. Correct the inheritance and scaling of font size in all browsers.
102 * 2. Correct the odd `em` font sizing in all browsers.
103 */
104
105code,
106kbd,
107samp {
108 font-family: monospace, monospace; /* 1 */
109 font-size: 1em; /* 2 */
110}
111
112/**
113 * Add the correct font size in all browsers.
114 */
115
116small {
117 font-size: 80%;
118}
119
120/**
121 * Prevent `sub` and `sup` elements from affecting the line height in
122 * all browsers.
123 */
124
125sub,
126sup {
127 font-size: 75%;
128 line-height: 0;
129 position: relative;
130 vertical-align: baseline;
131}
132
133sub {
134 bottom: -0.25em;
135}
136
137sup {
138 top: -0.5em;
139}
140
141/* Embedded content
142 ========================================================================== */
143
144/**
145 * Remove the border on images inside links in IE 10.
146 */
147
148img {
149 border-style: none;
150}
151
152/* Forms
153 ========================================================================== */
154
155/**
156 * 1. Change the font styles in all browsers.
157 * 2. Remove the margin in Firefox and Safari.
158 */
159
160button,
161input,
162optgroup,
163select,
164textarea {
165 font-family: inherit; /* 1 */
166 font-size: 100%; /* 1 */
167 line-height: 1.15; /* 1 */
168 margin: 0; /* 2 */
169}
170
171/**
172 * Show the overflow in IE.
173 * 1. Show the overflow in Edge.
174 */
175
176button,
177input { /* 1 */
178 overflow: visible;
179}
180
181/**
182 * Remove the inheritance of text transform in Edge, Firefox, and IE.
183 * 1. Remove the inheritance of text transform in Firefox.
184 */
185
186button,
187select { /* 1 */
188 text-transform: none;
189}
190
191/**
192 * Correct the inability to style clickable types in iOS and Safari.
193 */
194
195button,
196[type="button"],
197[type="reset"],
198[type="submit"] {
199 -webkit-appearance: button;
200}
201
202/**
203 * Remove the inner border and padding in Firefox.
204 */
205
206button::-moz-focus-inner,
207[type="button"]::-moz-focus-inner,
208[type="reset"]::-moz-focus-inner,
209[type="submit"]::-moz-focus-inner {
210 border-style: none;
211 padding: 0;
212}
213
214/**
215 * Restore the focus styles unset by the previous rule.
216 */
217
218button:-moz-focusring,
219[type="button"]:-moz-focusring,
220[type="reset"]:-moz-focusring,
221[type="submit"]:-moz-focusring {
222 outline: 1px dotted ButtonText;
223}
224
225/**
226 * Correct the padding in Firefox.
227 */
228
229fieldset {
230 padding: 0.35em 0.75em 0.625em;
231}
232
233/**
234 * 1. Correct the text wrapping in Edge and IE.
235 * 2. Correct the color inheritance from `fieldset` elements in IE.
236 * 3. Remove the padding so developers are not caught out when they zero out
237 * `fieldset` elements in all browsers.
238 */
239
240legend {
241 box-sizing: border-box; /* 1 */
242 color: inherit; /* 2 */
243 display: table; /* 1 */
244 max-width: 100%; /* 1 */
245 padding: 0; /* 3 */
246 white-space: normal; /* 1 */
247}
248
249/**
250 * Add the correct vertical alignment in Chrome, Firefox, and Opera.
251 */
252
253progress {
254 vertical-align: baseline;
255}
256
257/**
258 * Remove the default vertical scrollbar in IE 10+.
259 */
260
261textarea {
262 overflow: auto;
263}
264
265/**
266 * 1. Add the correct box sizing in IE 10.
267 * 2. Remove the padding in IE 10.
268 */
269
270[type="checkbox"],
271[type="radio"] {
272 box-sizing: border-box; /* 1 */
273 padding: 0; /* 2 */
274}
275
276/**
277 * Correct the cursor style of increment and decrement buttons in Chrome.
278 */
279
280[type="number"]::-webkit-inner-spin-button,
281[type="number"]::-webkit-outer-spin-button {
282 height: auto;
283}
284
285/**
286 * 1. Correct the odd appearance in Chrome and Safari.
287 * 2. Correct the outline style in Safari.
288 */
289
290[type="search"] {
291 -webkit-appearance: textfield; /* 1 */
292 outline-offset: -2px; /* 2 */
293}
294
295/**
296 * Remove the inner padding in Chrome and Safari on macOS.
297 */
298
299[type="search"]::-webkit-search-decoration {
300 -webkit-appearance: none;
301}
302
303/**
304 * 1. Correct the inability to style clickable types in iOS and Safari.
305 * 2. Change font properties to `inherit` in Safari.
306 */
307
308::-webkit-file-upload-button {
309 -webkit-appearance: button; /* 1 */
310 font: inherit; /* 2 */
311}
312
313/* Interactive
314 ========================================================================== */
315
316/*
317 * Add the correct display in Edge, IE 10+, and Firefox.
318 */
319
320details {
321 display: block;
322}
323
324/*
325 * Add the correct display in all browsers.
326 */
327
328summary {
329 display: list-item;
330}
331
332/* Misc
333 ========================================================================== */
334
335/**
336 * Add the correct display in IE 10+.
337 */
338
339template {
340 display: none;
341}
342
343/**
344 * Add the correct display in IE 10.
345 */
346
347[hidden] {
348 display: none;
349}
diff --git a/site/public/register-docs/index.html b/site/public/register-docs/index.html
new file mode 100644
index 0000000..fdc5237
--- /dev/null
+++ b/site/public/register-docs/index.html
@@ -0,0 +1,168 @@
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5 <meta charset="UTF-8">
6 <title>Register | Gradecoin </title>
7 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
8 <style>
9 :root {
10 /* Primary theme color */
11 --primary-color: #F8D12F;
12 /* Primary theme text color */
13 --primary-text-color: #1E2329;
14 /* Primary theme link color */
15 --primary-link-color: #2F57F7;
16 /* Secondary color: the background body color */
17 --secondary-color: #FAFAFA;
18 --secondary-text-color: #303030;
19 /* Highlight text color of table of content */
20 --toc-highlight-text-color: #d46e13;
21 }
22</style>
23
24 <link href="https://fonts.googleapis.com/css?family=Alfa+Slab+One&display=swap" rel="stylesheet">
25 <link href="https://fonts.googleapis.com/css?family=Fira+Sans:400,500,600&display=swap" rel="stylesheet">
26 <link rel="stylesheet" href="/normalize.css">
27 <link rel="stylesheet" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;juice.css">
28
29<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
30
31</head>
32
33<body>
34
35<header class="box-shadow">
36
37
38<a href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;">
39 <div class="logo">
40 <img src="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;gradecoin.png" alt="logo">
41 Gradecoin
42 </div>
43</a>
44
45<nav>
46
47 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;register-docs&#x2F;">Register</a>
48
49 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;jwt&#x2F;">JWT</a>
50
51 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;transaction-docs&#x2F;">Transactions</a>
52
53 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;block-docs&#x2F;">Blocks</a>
54
55
56
57 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;github.com&#x2F;zhuowei&#x2F;nft_ptr#why">why?</a>
58
59
60</nav>
61
62</header>
63
64
65 <main>
66
67
68
69
70
71 <div class="toc">
72 <div class="toc-sticky">
73
74 <div class="toc-item">
75 <a class="subtext" href="https://gradecoin.xyz/register-docs/#authentication-process">Authentication Process</a>
76 </div>
77
78
79 </div>
80 </div>
81
82
83
84 <div class="content text">
85
86<div class="heading-text">Register Documentation</div>
87<p>POST request to /register endpoint</p>
88<p>Lets a user to authenticate themselves to the system.
89Only people who are enrolled to the class can open Gradecoin accounts.
90This is enforced with your Student ID and a one time password you will receive.</p>
91<h1 id="authentication-process">Authentication Process</h1>
92<ul>
93<li>Gradecoin's Public Key (<code>gradecoin_public_key</code>) is listed on our Moodle page.</li>
94<li>You pick a short temporary key (<code>k_temp</code>)</li>
95<li>Create a JSON object (<code>auth_plaintext</code>) with your <code>metu_id</code> and <code>public key</code> in base64 (PEM) format (<code>S_PK</code>) <a href="https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem">reference</a></li>
96</ul>
97<pre style="background-color:#ffffff;">
98<code class="language-json" data-lang="json"><span style="color:#545052;">{
99 &quot;</span><span style="color:#009854;">student_id</span><span style="color:#545052;">&quot;: &quot;</span><span style="color:#009854;">e12345</span><span style="color:#545052;">&quot;,
100 &quot;</span><span style="color:#009854;">passwd</span><span style="color:#545052;">&quot;: &quot;</span><span style="color:#009854;">15 char secret</span><span style="color:#545052;">&quot;,
101 &quot;</span><span style="color:#009854;">public_key</span><span style="color:#545052;">&quot;: &quot;</span><span style="color:#009854;">---BEGIN PUBLIC KEY...</span><span style="color:#545052;">&quot;
102}
103</span></code></pre>
104<ul>
105<li>Pick a random IV.</li>
106<li>Encrypt the serialized string of <code>auth_plaintext</code> with 128 bit block AES in CBC mode with Pkcs7 padding using the temporary key (<code>k_temp</code>), the result is <code>auth_ciphertext</code>. Encode this with base64.</li>
107<li>The temporary key you have picked <code>k_temp</code> is encrypted using RSA with OAEP padding scheme
108using SHA-256 with <code>gradecoin_public_key</code>, giving us <code>key_ciphertext</code>. Encode this with base 64.</li>
109<li>The payload JSON object (<code>auth_request</code>) can be serialized now:</li>
110</ul>
111<pre style="background-color:#ffffff;">
112<code class="language-json" data-lang="json"><span style="color:#545052;">{
113 &quot;</span><span style="color:#009854;">c</span><span style="color:#545052;">&quot;: &quot;</span><span style="color:#009854;">auth_ciphertext</span><span style="color:#545052;">&quot;,
114 &quot;</span><span style="color:#009854;">iv</span><span style="color:#545052;">&quot;: &quot;</span><span style="color:#009854;">hexadecimal</span><span style="color:#545052;">&quot;,
115 &quot;</span><span style="color:#009854;">key</span><span style="color:#545052;">&quot;: &quot;</span><span style="color:#009854;">key_ciphertext</span><span style="color:#545052;">&quot;
116}
117</span></code></pre>
118<p>If your authentication process was valid, you will be given access and your public key fingerprint that is your address.</p>
119
120
121 </div>
122
123
124
125 </main>
126
127
128<footer>
129Built For ⁂ CENG489 ⁂ Introduction to Computer Security
130</footer>
131
132</body>
133<script>
134 function highlightNav(heading) {
135 let pathname = location.pathname;
136 document.querySelectorAll(".toc a").forEach((item) => {
137 item.classList.remove("active");
138 });
139 document.querySelector(".toc a[href$='" + pathname + "#" + heading + "']").classList.add("active");
140 }
141
142 let currentHeading = "";
143 window.onscroll = function () {
144 let h = document.querySelectorAll("h1,h2,h3,h4,h5,h6");
145 let elementArr = [];
146
147 h.forEach(item => {
148 if (item.id !== "") {
149 elementArr[item.id] = item.getBoundingClientRect().top;
150 }
151 });
152 elementArr.sort();
153 for (let key in elementArr) {
154 if (!elementArr.hasOwnProperty(key)) {
155 continue;
156 }
157 if (elementArr[key] > 0 && elementArr[key] < 300) {
158 if (currentHeading !== key) {
159 highlightNav(key);
160 currentHeading = key;
161 }
162 break;
163 }
164 }
165 }
166</script>
167
168</html>
diff --git a/site/public/robots.txt b/site/public/robots.txt
new file mode 100644
index 0000000..7b7f367
--- /dev/null
+++ b/site/public/robots.txt
@@ -0,0 +1,3 @@
1User-agent: *
2Allow: /
3Sitemap: https://gradecoin.xyz/sitemap.xml
diff --git a/site/public/search_index.en.js b/site/public/search_index.en.js
new file mode 100644
index 0000000..a5eeec0
--- /dev/null
+++ b/site/public/search_index.en.js
@@ -0,0 +1 @@
window.searchIndex = {"fields":["title","body"],"pipeline":["trimmer","stopWordFilter","stemmer"],"ref":"id","version":"0.9.5","index":{"body":{"root":{"docs":{},"df":0,"0":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"1":{"docs":{},"df":0,"2":{"docs":{},"df":0,"8":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}},"5":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1},"6":{"docs":{"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":1}},"2":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1,"0":{"docs":{},"df":0,"4":{"docs":{},"df":0,"8":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}},"x":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,"^":{"docs":{},"df":0,"3":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}}}}}},"4":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":2},"5":{"docs":{},"df":0,"6":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":4}}},"3":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":2,"1":{"docs":{},"df":0,"4":{"docs":{},"df":0,"1":{"docs":{},"df":0,"5":{"docs":{},"df":0,"9":{"docs":{},"df":0,"2":{"docs":{},"df":0,"6":{"docs":{},"df":0,"5":{"docs":{},"df":0,"3":{"docs":{},"df":0,"5":{"docs":{},"df":0,"8":{"docs":{},"df":0,"9":{"docs":{},"df":0,"7":{"docs":{},"df":0,"9":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"3":{"docs":{},"df":0,"8":{"docs":{},"df":0,"4":{"docs":{},"df":0,"6":{"docs":{},"df":0,"2":{"docs":{},"df":0,"6":{"docs":{},"df":0,"4":{"docs":{},"df":0,"3":{"docs":{},"df":0,"3":{"docs":{},"df":0,"8":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"7":{"docs":{},"df":0,"9":{"docs":{},"df":0,"5":{"docs":{},"df":0,"0":{"docs":{},"df":0,"2":{"docs":{},"df":0,"8":{"docs":{},"df":0,"8":{"docs":{},"df":0,"4":{"docs":{},"df":0,"1":{"docs":{},"df":0,"9":{"docs":{},"df":0,"7":{"docs":{},"df":0,"1":{"docs":{},"df":0,"6":{"docs":{},"df":0,"9":{"docs":{},"df":0,"3":{"docs":{},"df":0,"9":{"docs":{},"df":0,"9":{"docs":{},"df":0,"3":{"docs":{},"df":0,"7":{"docs":{},"df":0,"5":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,"5":{"docs":{},"df":0,"8":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"9":{"docs":{},"df":0,"7":{"docs":{},"df":0,"4":{"docs":{},"df":0,"9":{"docs":{},"df":0,"4":{"docs":{},"df":0,"4":{"docs":{},"df":0,"5":{"docs":{},"df":0,"9":{"docs":{},"df":0,"2":{"docs":{},"df":0,"3":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"2":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}},"4":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1},"6":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/block-docs/":{"tf":1.4142135623730951}},"df":2,"4":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}},"7":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"8":{"docs":{},"df":0,"6":{"docs":{},"df":0,"0":{"docs":{},"df":0,"1":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.4142135623730951},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":2}}}},"a":{"docs":{},"df":0,"a":{"docs":{},"df":0,"a":{"docs":{},"df":0,"a":{"docs":{},"df":0,"a":{"docs":{},"df":0,"a":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"b":{"docs":{},"df":0,"b":{"docs":{},"df":0,"b":{"docs":{},"df":0,"b":{"docs":{},"df":0,"b":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"b":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.7320508075688772}},"df":1}},"s":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"d":{"docs":{},"df":0,"d":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1},"l":{"docs":{},"df":0,"g":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"m":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":2}}}}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}}}}},"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/transaction-docs/":{"tf":1.4142135623730951}},"df":2}}}},"p":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"i":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":2}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1},"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}},"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":1}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.7320508075688772}},"df":3}}},"o":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":2.23606797749979},"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.7320508075688772},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":4}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1,"6":{"docs":{},"df":0,"4":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":1}}},"h":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":2}}}},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}}}},"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":2.0},"https://gradecoin.xyz/block-docs/":{"tf":1.4142135623730951},"https://gradecoin.xyz/register-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":4,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"2":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":2}}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/":{"tf":3.3166247903554},"https://gradecoin.xyz/block-docs/":{"tf":2.449489742783178},"https://gradecoin.xyz/jwt/":{"tf":1.4142135623730951},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":4,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}}}}}}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"g":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"y":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}}}},"c":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"b":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}}}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}}}},"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"f":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":3}}},"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1,"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}},"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":2}}}}}}},"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"g":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":2}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}}}},"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.7320508075688772}},"df":1}}},"w":{"docs":{},"df":0,"n":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}}},"e":{"docs":{},"df":0,"1":{"docs":{},"df":0,"2":{"docs":{},"df":0,"3":{"docs":{},"df":0,"4":{"docs":{},"df":0,"5":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":2},"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":2}}}}},"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":4}}}}}},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"x":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"p":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}},"i":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.4142135623730951}},"df":1}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}}}}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"x":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}}},"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":4}}},"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.7320508075688772}},"df":3}}}}}}}}},"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"t":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1,"n":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"d":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":3.605551275463989},"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":4,"'":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2},"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"_":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"y":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"h":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/block-docs/":{"tf":2.0},"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":3,"/":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}}}}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/jwt/":{"tf":1.4142135623730951}},"df":2}}}},"l":{"docs":{},"df":0,"p":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}},"x":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}}}}}}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.4142135623730951},"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.4142135623730951}},"df":3}}}},"i":{"docs":{},"df":0,"'":{"docs":{},"df":0,"v":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"a":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.4142135623730951}},"df":1}},"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2},"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"g":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":2},"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{},"df":0,"o":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.4142135623730951},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":2},"s":{"docs":{},"df":0,"u":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"'":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}},"v":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":1}},"j":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.4142135623730951},"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":3}}},"w":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":2.6457513110645907},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":4,"'":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},".":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}}}}},"k":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.7320508075688772}},"df":1}}}}},"e":{"docs":{},"df":0,"y":{"docs":{"https://gradecoin.xyz/":{"tf":3.0},"https://gradecoin.xyz/jwt/":{"tf":1.4142135623730951},"https://gradecoin.xyz/register-docs/":{"tf":2.8284271247461903}},"df":3,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://gradecoin.xyz/":{"tf":2.0}},"df":1}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}}}},"f":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}},"t":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":3}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"&":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,";":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":2}}}}}}}}}}}}}}}}}}}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/block-docs/":{"tf":1.7320508075688772}},"df":2},"u":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":2}}},"s":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}},"v":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"w":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"w":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.4142135623730951}},"df":1}},"w":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}},"b":{"docs":{},"df":0,"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":1}}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.7320508075688772}},"df":1}}}},"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1},"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":2}}},"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"1":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1},"d":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":1},"g":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"w":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}}}}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"y":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}}}}},"e":{"docs":{},"df":0,"m":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1},"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.7320508075688772}},"df":1}}},"k":{"docs":{},"df":0,"c":{"docs":{},"df":0,"s":{"docs":{},"df":0,"1":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1},"7":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"y":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/":{"tf":1.7320508075688772}},"df":1}}}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/block-docs/":{"tf":1.4142135623730951},"https://gradecoin.xyz/register-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.4142135623730951}},"df":4}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":2}}}},"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":2,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://gradecoin.xyz/":{"tf":2.0}},"df":1}}}},"o":{"docs":{},"df":0,"f":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":2}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/":{"tf":1.7320508075688772},"https://gradecoin.xyz/block-docs/":{"tf":1.4142135623730951},"https://gradecoin.xyz/jwt/":{"tf":1.4142135623730951},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":4}}}}},"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/":{"tf":3.0},"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":2.0}},"df":3,"_":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"y":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}}}}}},"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"y":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.7320508075688772},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":3}}},"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":2}}}},"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}}}}},"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":3.3166247903554},"https://gradecoin.xyz/block-docs/":{"tf":1.7320508075688772},"https://gradecoin.xyz/jwt/":{"tf":1.4142135623730951},"https://gradecoin.xyz/register-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":2.0}},"df":5}}},"i":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":2}}}}},"f":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":2}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"2":{"docs":{},"df":0,"5":{"docs":{},"df":0,"6":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}},"a":{"docs":{"https://gradecoin.xyz/":{"tf":1.7320508075688772},"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":3,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}}},"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}},"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"x":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}},"d":{"docs":{},"df":0,"b":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}},"e":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":2},"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":2}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.4142135623730951}},"df":1}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.7320508075688772}},"df":1}},"i":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"h":{"docs":{},"df":0,"a":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2},"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}},"g":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.7320508075688772}},"df":1,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1,"i":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.7320508075688772}},"df":1}}},"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"v":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"c":{"docs":{"https://gradecoin.xyz/transaction-docs/":{"tf":1.4142135623730951}},"df":1}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}}},"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.7320508075688772},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2,"'":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}}}}}}},"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://gradecoin.xyz/":{"tf":1.7320508075688772},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.4142135623730951}},"df":2}}}}},"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.7320508075688772}},"df":1}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"a":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.4142135623730951}},"df":1,"n":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"v":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951},"https://gradecoin.xyz/jwt/":{"tf":2.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":3,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":2}}}}}}}},"o":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":3.4641016151377544},"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/jwt/":{"tf":1.4142135623730951},"https://gradecoin.xyz/transaction-docs/":{"tf":2.449489742783178}},"df":4,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"x":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.7320508075688772}},"df":1}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":2}}}}},"p":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"s":{"docs":{"https://gradecoin.xyz/":{"tf":3.4641016151377544},"https://gradecoin.xyz/block-docs/":{"tf":1.4142135623730951},"https://gradecoin.xyz/jwt/":{"tf":2.23606797749979},"https://gradecoin.xyz/register-docs/":{"tf":1.7320508075688772},"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":5,"e":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}},"v":{"docs":{},"df":0,"1":{"docs":{},"df":0,"_":{"docs":{},"df":0,"5":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}},"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0},"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":2}}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"p":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"y":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"'":{"docs":{},"df":0,"r":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}},"b":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"l":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/":{"tf":2.0},"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":2}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}},"y":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"https://gradecoin.xyz/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}}}}}},"title":{"root":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://gradecoin.xyz/block-docs/":{"tf":1.0}},"df":1}}}}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://gradecoin.xyz/":{"tf":1.0}},"df":1}}}}}}}}},"j":{"docs":{},"df":0,"w":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/jwt/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/register-docs/":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://gradecoin.xyz/transaction-docs/":{"tf":1.0}},"df":1}}}}}}}}}}},"documentStore":{"save":true,"docs":{"https://gradecoin.xyz/":{"body":"Welcome to Gradecoin!\nBlockchains are incredibly simple yet can appear very complicated, we will see how they work and practice programming production cryptography code.\nThis server is the sandbox for the PA1, it's currently running the Gradecoin application. Gradecoin is the faux currency we will use to simulate a blockchain network. At the end of the simulation, the amount of Gradecoin you hold will be your PA1 grade.\nA quick summary: authenticate yourself to the system using public key encryption.\nCraft Transaction proposals and tag them using JWTs.\nWhen there are enough transactions then you can propose Blocks in the same way.\nBlocks need to be mined beforehand using Proof-of-work, or brute force.\nGradecoin offers 3 endpoints at /register, /block and /transaction. You can only send GET requests to /block and /transaction without authorization.\nThe server is programmed in RESTful architecture, there are no DELETE, PUT or UPDATE operations, though.\nGradecoin uses a Proof-of-work block accepting mechanism. It uses single round Blake2s hashing which produces 256-bit (64 hexadecimal characters) output. The target hash is 24 bits or 6 hexadecimal characters of 0. During testing, I could mine a block on average around 2-7 minutes.\n\nWe're expecting you to use existing tools and implementations. Standards are hard. Don't roll your own crypto. Feel free to ask questions. Collaborate.\n\nYou might ask,\n\nBut if nobody has any Gradecoin then how do we have transactions?\n\nThere is a bank! Their public key is 31415926535897932384626433832795028841971693993751058209749445923 and they have some amount of Gradecoin preloaded. It's also the only account that you can send transactions requests to yourself.\nCoinbase\nThe first transactions of a block is called the coinbase. They are the author of the block proposal and if the block is accepted then they get compensated for their efforts with some Gradecoin.\nPublic Key Signatures\nGradecoin uses 2048 bit RSA keyspairs.\nServices\n/register\n\nStudent creates their own 2048 bit RSA keypair\nDownloads Gradecoin's Public Key from Moodle\nEncrypts their JSON wrapped Public Key, Student ID and one time passwd using Gradecoin's Public Key\nTheir public key is now in our database and can be used to sign their JWT's during requests\n\n/transaction\n\nYou can offer a Transaction - POST request\n\nThe request should have Authorization\nThe request header should be signed by the Public Key of the by field in the transaction\n\n\nfetch the list of Transactions - GET request\n\n/block\n\noffer a [schema::Block] - POST request\n\nThe request should have Authorization\nThe [schema::Block::transaction_list] of the block should be a subset of [schema::Db::pending_transactions]\n\n\nfetch the last accepted [schema::Block] - GET request\n\nAuthorization: The request header should have Bearer JWT.Token signed with Student Public Key\nQuestions\nThis all sound complicated!\n\nI've drawn inspiration from actual Bitcoin transactions and warp. The simplicity of the system is how little interfaces it has.\nDon't know where to start? Gradecoin uses RESTful API; simple curl commands or even your browser will work! This website can help as well.\nJWT Debugger and the corresponding RFC\nRemember that you are absolutely encouraged to grab off-the-shelf implementations for every cryptography primitive you will use. You can start by finding a code snippet to generate a RSA keypair?\n\nI found a bug!\nThank you! Please let me know so we can solve it.\nI hacked the server!\nThat wasn't supposed to happen :( I did not place any intentional vulnerabilities to the system so if you cracked something, it was not intended. Please don't abuse it and let me know so I can patch it.\nSubmission?\nAt the end of the simulation, your Gradecoin balance will be your grade. I will also expect a unique client programmed in either;\n\nc\nc++\nperl\nrust\npython\nrandom assortment of bash scripts\n\nIf your favourite programming language is missing please let me know 🤷?\nCan my friends play?\nSadly, no. Student's who are enrolled to the class will receive one-time-passwords for authentication.\nHow and or Why?\n\nBuilt, with Rust\n\n","id":"https://gradecoin.xyz/","title":"Gradecoin"},"https://gradecoin.xyz/block-docs/":{"body":"A block that was proposed to commit Transactions in transaction_list to the\nledger with a nonce that made hash valid; 6 zeroes at the left hand side of the\nhash (24 bytes).\nWe are mining using blake2s algorithm, which produces 256 bit hashes. Hash/second is roughly 20x10^3 on my machine, a new block can be mined in around 4-6 minutes.\nRequests\nGET\nA HTTP GET request to /block endpoint will return the latest mined block.\nPOST\nA HTTP POST request with Authorization using JWT will allow you to propose your own blocks.\nFields\ntransaction_list: [array of Fingerprints]\nnonce: unsigned 32-bit integer\ntimestamp: ISO 8601 &lt;date&gt;T&lt;time&gt;\nhash: String\n\nISO 8601 Reference\n","id":"https://gradecoin.xyz/block-docs/","title":"Blocks"},"https://gradecoin.xyz/jwt/":{"body":"\nJSON Web Tokens are representations of claims, or authorization proofs that fit into the Header of HTTP requests.\n\nHow?\nJWTs are used as the MAC of operations that require authorization:\n\nblock proposal\ntransaction proposal.\n\nThey are send alongside the JSON request body in the Header;\nAuthorization: Bearer aaaaaa.bbbbbb.ccccc\n\nGradecoin uses 3 fields for the JWTs;\n{\n\"tha\": \"Hash of the payload, check invididual references\",\n\"iat\": \"Issued At, Unix Time\",\n\"exp\": \"Expiration Time, epoch\"\n}\n\n\ntha is explained in blocks and transactions documentations.\niat when the JWT was created in Unix Time format\nexp when the JWT will expire &amp; be rejected in Unix Time\n\nAlgorithm\nWe are using RS256, RSASSA-PKCS1-v1_5 using SHA-256. The JWTs you encode with your private RSA key will be decoded using the public key you have authenticated with. You can see how the process works here.\nReferences\n\nRFC, the ultimate reference\nJWT Debugger\n\n","id":"https://gradecoin.xyz/jwt/","title":"JWT"},"https://gradecoin.xyz/register-docs/":{"body":"POST request to /register endpoint\nLets a user to authenticate themselves to the system.\nOnly people who are enrolled to the class can open Gradecoin accounts.\nThis is enforced with your Student ID and a one time password you will receive.\nAuthentication Process\n\nGradecoin's Public Key (gradecoin_public_key) is listed on our Moodle page.\nYou pick a short temporary key (k_temp)\nCreate a JSON object (auth_plaintext) with your metu_id and public key in base64 (PEM) format (S_PK) reference\n\n{\n \"student_id\": \"e12345\",\n \"passwd\": \"15 char secret\",\n \"public_key\": \"---BEGIN PUBLIC KEY...\"\n}\n\n\nPick a random IV.\nEncrypt the serialized string of auth_plaintext with 128 bit block AES in CBC mode with Pkcs7 padding using the temporary key (k_temp), the result is auth_ciphertext. Encode this with base64.\nThe temporary key you have picked k_temp is encrypted using RSA with OAEP padding scheme\nusing SHA-256 with gradecoin_public_key, giving us key_ciphertext. Encode this with base 64.\nThe payload JSON object (auth_request) can be serialized now:\n\n{\n \"c\": \"auth_ciphertext\",\n \"iv\": \"hexadecimal\",\n \"key\": \"key_ciphertext\"\n}\n\nIf your authentication process was valid, you will be given access and your public key fingerprint that is your address.\n","id":"https://gradecoin.xyz/register-docs/","title":"Register"},"https://gradecoin.xyz/transaction-docs/":{"body":"A transaction request between source and target to move amount Gradecoin.\nRequests\nGET\nA HTTP GET request to /transaction endpoint will return the current list of pending transactions.\nPOST\nA HTTP POST request with Authorization using JWT to /transaction will allow you to propose your own transactions.\nFields\nby: Fingerprint\nsource: Fingerprint\ntarget: Fingerprint\namount: unsigned 16 bit integer\ntimestamp: ISO 8601 &lt;date&gt;T&lt;time&gt;\n","id":"https://gradecoin.xyz/transaction-docs/","title":"Transactions"}},"docInfo":{"https://gradecoin.xyz/":{"body":371,"title":1},"https://gradecoin.xyz/block-docs/":{"body":74,"title":1},"https://gradecoin.xyz/jwt/":{"body":96,"title":1},"https://gradecoin.xyz/register-docs/":{"body":121,"title":1},"https://gradecoin.xyz/transaction-docs/":{"body":44,"title":1}},"length":5},"lang":"English"}; \ No newline at end of file
diff --git a/site/public/sitemap.xml b/site/public/sitemap.xml
new file mode 100644
index 0000000..1f2e2d9
--- /dev/null
+++ b/site/public/sitemap.xml
@@ -0,0 +1,18 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
3 <url>
4 <loc>https://gradecoin.xyz/</loc>
5 </url>
6 <url>
7 <loc>https://gradecoin.xyz/block-docs/</loc>
8 </url>
9 <url>
10 <loc>https://gradecoin.xyz/jwt/</loc>
11 </url>
12 <url>
13 <loc>https://gradecoin.xyz/register-docs/</loc>
14 </url>
15 <url>
16 <loc>https://gradecoin.xyz/transaction-docs/</loc>
17 </url>
18</urlset>
diff --git a/site/public/transaction-docs/index.html b/site/public/transaction-docs/index.html
new file mode 100644
index 0000000..67b9505
--- /dev/null
+++ b/site/public/transaction-docs/index.html
@@ -0,0 +1,164 @@
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5 <meta charset="UTF-8">
6 <title>Transactions | Gradecoin </title>
7 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
8 <style>
9 :root {
10 /* Primary theme color */
11 --primary-color: #F8D12F;
12 /* Primary theme text color */
13 --primary-text-color: #1E2329;
14 /* Primary theme link color */
15 --primary-link-color: #2F57F7;
16 /* Secondary color: the background body color */
17 --secondary-color: #FAFAFA;
18 --secondary-text-color: #303030;
19 /* Highlight text color of table of content */
20 --toc-highlight-text-color: #d46e13;
21 }
22</style>
23
24 <link href="https://fonts.googleapis.com/css?family=Alfa+Slab+One&display=swap" rel="stylesheet">
25 <link href="https://fonts.googleapis.com/css?family=Fira+Sans:400,500,600&display=swap" rel="stylesheet">
26 <link rel="stylesheet" href="/normalize.css">
27 <link rel="stylesheet" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;juice.css">
28
29<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
30
31</head>
32
33<body>
34
35<header class="box-shadow">
36
37
38<a href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;">
39 <div class="logo">
40 <img src="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;gradecoin.png" alt="logo">
41 Gradecoin
42 </div>
43</a>
44
45<nav>
46
47 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;register-docs&#x2F;">Register</a>
48
49 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;jwt&#x2F;">JWT</a>
50
51 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;transaction-docs&#x2F;">Transactions</a>
52
53 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;gradecoin.xyz&#x2F;block-docs&#x2F;">Blocks</a>
54
55
56
57 <a class="nav-item subtitle-text" href="https:&#x2F;&#x2F;github.com&#x2F;zhuowei&#x2F;nft_ptr#why">why?</a>
58
59
60</nav>
61
62</header>
63
64
65 <main>
66
67
68
69
70
71 <div class="toc">
72 <div class="toc-sticky">
73
74 <div class="toc-item">
75 <a class="subtext" href="https://gradecoin.xyz/transaction-docs/#requests">Requests</a>
76 </div>
77
78
79 <div class="toc-item-child">
80 <a class="subtext" href="https://gradecoin.xyz/transaction-docs/#get"><small>- GET</small></a>
81 </div>
82
83 <div class="toc-item-child">
84 <a class="subtext" href="https://gradecoin.xyz/transaction-docs/#post"><small>- POST</small></a>
85 </div>
86
87
88
89 <div class="toc-item">
90 <a class="subtext" href="https://gradecoin.xyz/transaction-docs/#fields">Fields</a>
91 </div>
92
93
94 </div>
95 </div>
96
97
98
99 <div class="content text">
100
101<div class="heading-text">Transaction documentation</div>
102<p>A transaction request between <code>source</code> and <code>target</code> to move <code>amount</code> Gradecoin.</p>
103<h1 id="requests">Requests</h1>
104<h2 id="get">GET</h2>
105<p>A HTTP <code>GET</code> request to <a href="/transaction">/transaction</a> endpoint will return the current list of pending transactions.</p>
106<h2 id="post">POST</h2>
107<p>A HTTP <code>POST</code> request with Authorization using JWT to <a href="/transactions">/transaction</a> will allow you to propose your own transactions.</p>
108<h1 id="fields">Fields</h1>
109<pre style="background-color:#ffffff;">
110<code><span style="color:#545052;">by: Fingerprint
111source: Fingerprint
112target: Fingerprint
113amount: unsigned 16 bit integer
114timestamp: ISO 8601 &lt;date&gt;T&lt;time&gt;
115</span></code></pre>
116
117 </div>
118
119
120
121 </main>
122
123
124<footer>
125Built For ⁂ CENG489 ⁂ Introduction to Computer Security
126</footer>
127
128</body>
129<script>
130 function highlightNav(heading) {
131 let pathname = location.pathname;
132 document.querySelectorAll(".toc a").forEach((item) => {
133 item.classList.remove("active");
134 });
135 document.querySelector(".toc a[href$='" + pathname + "#" + heading + "']").classList.add("active");
136 }
137
138 let currentHeading = "";
139 window.onscroll = function () {
140 let h = document.querySelectorAll("h1,h2,h3,h4,h5,h6");
141 let elementArr = [];
142
143 h.forEach(item => {
144 if (item.id !== "") {
145 elementArr[item.id] = item.getBoundingClientRect().top;
146 }
147 });
148 elementArr.sort();
149 for (let key in elementArr) {
150 if (!elementArr.hasOwnProperty(key)) {
151 continue;
152 }
153 if (elementArr[key] > 0 && elementArr[key] < 300) {
154 if (currentHeading !== key) {
155 highlightNav(key);
156 currentHeading = key;
157 }
158 break;
159 }
160 }
161 }
162</script>
163
164</html>
diff --git a/site/static/android-chrome-192x192.png b/site/static/android-chrome-192x192.png
new file mode 100644
index 0000000..023ddbd
--- /dev/null
+++ b/site/static/android-chrome-192x192.png
Binary files differ
diff --git a/site/static/android-chrome-512x512.png b/site/static/android-chrome-512x512.png
new file mode 100644
index 0000000..4251933
--- /dev/null
+++ b/site/static/android-chrome-512x512.png
Binary files differ
diff --git a/site/static/apple-touch-icon.png b/site/static/apple-touch-icon.png
new file mode 100644
index 0000000..cd8e4c8
--- /dev/null
+++ b/site/static/apple-touch-icon.png
Binary files differ
diff --git a/site/static/favicon-16x16.png b/site/static/favicon-16x16.png
new file mode 100644
index 0000000..bd63d34
--- /dev/null
+++ b/site/static/favicon-16x16.png
Binary files differ
diff --git a/site/static/favicon-32x32.png b/site/static/favicon-32x32.png
new file mode 100644
index 0000000..e343587
--- /dev/null
+++ b/site/static/favicon-32x32.png
Binary files differ
diff --git a/site/static/favicon.ico b/site/static/favicon.ico
new file mode 100644
index 0000000..45d8bfe
--- /dev/null
+++ b/site/static/favicon.ico
Binary files differ
diff --git a/site/static/gradecoin.png b/site/static/gradecoin.png
new file mode 100644
index 0000000..eeb670c
--- /dev/null
+++ b/site/static/gradecoin.png
Binary files differ
diff --git a/site/templates/_variables.html b/site/templates/_variables.html
new file mode 100644
index 0000000..3fd05b4
--- /dev/null
+++ b/site/templates/_variables.html
@@ -0,0 +1,15 @@
1<style>
2 :root {
3 /* Primary theme color */
4 --primary-color: #F8D12F;
5 /* Primary theme text color */
6 --primary-text-color: #1E2329;
7 /* Primary theme link color */
8 --primary-link-color: #2F57F7;
9 /* Secondary color: the background body color */
10 --secondary-color: #FAFAFA;
11 --secondary-text-color: #303030;
12 /* Highlight text color of table of content */
13 --toc-highlight-text-color: #d46e13;
14 }
15</style>
diff --git a/site/templates/index.html b/site/templates/index.html
new file mode 100644
index 0000000..1256b2a
--- /dev/null
+++ b/site/templates/index.html
@@ -0,0 +1,50 @@
1{% extends "juice/templates/index.html" %}
2
3{% block head %}
4<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
5{% endblock head %}
6
7{% block hero %}
8<section class="text-center">
9 <h1 class="heading-text animate__animated animate__jackInTheBox" style="font-size: 50px">
10 Mine your own grades
11 </h1>
12 <h3 class="title-text">
13 <b>Gradecoin</b> is the latest cutting edge blockchain technology agile grading framework that drives organic engagement and other buzzwords, with big data mining search engine optimization
14 </h3>
15 <div>
16 </div>
17</section>
18<img class="hero-image" style="width: 40%" src="{{ get_url(path="gradecoin.png") }}">
19
20<div class="explore-more text"
21 onclick="document.getElementById('features').scrollIntoView({behavior: 'smooth'})">
22 ⇩ Learn How ⇩
23</div>
24<style>
25
26.hero section {
27 padding: 0 5rem;
28}
29
30@media screen and (max-width: 768px) {
31 .hero section {
32 padding: 0 2rem;
33 }
34
35 .hero-image {
36 display: none
37 }
38
39}
40footer {
41 color: #8b8b8b;
42}
43</style>
44{% endblock hero %}
45
46{% block footer %}
47<footer>
48Built For ⁂ CENG489 ⁂ Introduction to Computer Security
49</footer>
50{% endblock footer %}
diff --git a/site/themes/juice b/site/themes/juice
new file mode 160000
Subproject 07310323f85e7851c0a1498021b0a02cfb2e215
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 8b61e5c..882fdc6 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -10,11 +10,10 @@ use gradecoin::schema::create_database;
10#[tokio::main] 10#[tokio::main]
11async fn main() { 11async fn main() {
12 // Show debug logs by default by setting `RUST_LOG=gradecoin=debug` 12 // Show debug logs by default by setting `RUST_LOG=gradecoin=debug`
13 // TODO: write logs to file? <13-04-21, yigit> //
14 if env::var_os("RUST_LOG").is_none() { 13 if env::var_os("RUST_LOG").is_none() {
15 env::set_var("RUST_LOG", "gradecoin=debug"); 14 env::set_var("RUST_LOG", "gradecoin=debug");
16 } 15 }
17 pretty_env_logger::init(); 16 log4rs::init_file("log.conf.yml", Default::default()).unwrap();
18 17
19 let db = create_database(); 18 let db = create_database();
20 19
diff --git a/src/error.rs b/src/error.rs
deleted file mode 100644
index 7339a06..0000000
--- a/src/error.rs
+++ /dev/null
@@ -1,38 +0,0 @@
1use log::warn;
2use serde::Serialize;
3use std::convert::Infallible;
4use warp::{http::StatusCode, Rejection, Reply};
5
6#[derive(Serialize)]
7struct ErrorResponse {
8 message: String,
9}
10
11pub async fn handle_rejection(err: Rejection) -> std::result::Result<impl Reply, Infallible> {
12 let code;
13 let message;
14
15 if err.is_not_found() {
16 code = StatusCode::NOT_FOUND;
17 message = "Requested resource is not found";
18 } else if let Some(_) = err.find::<warp::filters::body::BodyDeserializeError>() {
19 code = StatusCode::BAD_REQUEST;
20 message = "Error: JSON body is not formatted correctly, check your payload";
21 } else if let Some(_) = err.find::<warp::reject::MissingHeader>() {
22 code = StatusCode::METHOD_NOT_ALLOWED;
23 message = "Error: Authorization header missing, cannot authorize";
24 } else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
25 code = StatusCode::METHOD_NOT_ALLOWED;
26 message = "Error: method not allowed on this endpoint";
27 } else {
28 warn!("unhandled error: {:?}", err);
29 code = StatusCode::INTERNAL_SERVER_ERROR;
30 message = "Internal Server Error";
31 }
32
33 let json = warp::reply::json(&ErrorResponse {
34 message: message.to_owned(),
35 });
36
37 Ok(warp::reply::with_status(json, code))
38}
diff --git a/src/handlers.rs b/src/handlers.rs
index a8c9947..fe60ded 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -1,6 +1,6 @@
1/// API handlers, the ends of each filter chain
2use aes::Aes128; 1use aes::Aes128;
3use base64; 2/// API handlers, the ends of each filter chain
3use askama::Template;
4use blake2::{Blake2s, Digest}; 4use blake2::{Blake2s, Digest};
5use block_modes::block_padding::Pkcs7; 5use block_modes::block_padding::Pkcs7;
6use block_modes::{BlockMode, Cbc}; 6use block_modes::{BlockMode, Cbc};
@@ -8,10 +8,8 @@ use jsonwebtoken::errors::ErrorKind;
8use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation}; 8use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation};
9use log::{debug, warn}; 9use log::{debug, warn};
10use md5::Md5; 10use md5::Md5;
11use parking_lot::RwLockUpgradableReadGuard;
12use rsa::{PaddingScheme, RSAPrivateKey}; 11use rsa::{PaddingScheme, RSAPrivateKey};
13use serde::Serialize; 12use serde::Serialize;
14use serde_json;
15use sha2::Sha256; 13use sha2::Sha256;
16use std::collections::HashMap; 14use std::collections::HashMap;
17use std::convert::Infallible; 15use std::convert::Infallible;
@@ -39,6 +37,7 @@ enum ResponseType {
39 37
40use crate::schema::{ 38use crate::schema::{
41 AuthRequest, Block, Claims, Db, InitialAuthRequest, MetuId, NakedBlock, Transaction, User, 39 AuthRequest, Block, Claims, Db, InitialAuthRequest, MetuId, NakedBlock, Transaction, User,
40 UserAtRest,
42}; 41};
43 42
44const BEARER: &str = "Bearer "; 43const BEARER: &str = "Bearer ";
@@ -61,9 +60,9 @@ const BEARER: &str = "Bearer ";
61/// public_key: "---BEGIN PUBLIC KEY..." 60/// public_key: "---BEGIN PUBLIC KEY..."
62/// } 61/// }
63/// 62///
64/// - Encrypts the serialized string of `auth_plaintext` with 128 bit block AES in CBC mode with Pkcs7 padding using the temporary key (`k_temp`), the result is `auth_ciphertext` TODO should this be base64'd? 63/// - Encrypts the serialized string of `auth_plaintext` with 128 bit block AES in CBC mode with Pkcs7 padding using the temporary key (`k_temp`), the result is `auth_ciphertext`
65/// - The temporary key student has picked `k_temp` is encrypted using RSA with OAEP padding scheme 64/// - The temporary key student has picked `k_temp` is encrypted using RSA with OAEP padding scheme
66/// using sha256 with `gradecoin_public_key` (TODO base64? same as above), giving us `key_ciphertext` 65/// using sha256 with `gradecoin_public_key`, giving us `key_ciphertext`
67/// - The payload JSON object (`auth_request`) can be JSON serialized now: 66/// - The payload JSON object (`auth_request`) can be JSON serialized now:
68/// { 67/// {
69/// c: "auth_ciphertext" 68/// c: "auth_ciphertext"
@@ -92,7 +91,7 @@ pub async fn authenticate_user(
92 // Load our RSA Private Key as DER 91 // Load our RSA Private Key as DER
93 let der_encoded = PRIVATE_KEY 92 let der_encoded = PRIVATE_KEY
94 .lines() 93 .lines()
95 .filter(|line| !line.starts_with("-")) 94 .filter(|line| !line.starts_with('-'))
96 .fold(String::new(), |mut data, line| { 95 .fold(String::new(), |mut data, line| {
97 data.push_str(&line); 96 data.push_str(&line);
98 data 97 data
@@ -104,18 +103,126 @@ pub async fn authenticate_user(
104 let gradecoin_private_key = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key"); 103 let gradecoin_private_key = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key");
105 104
106 let padding = PaddingScheme::new_oaep::<sha2::Sha256>(); 105 let padding = PaddingScheme::new_oaep::<sha2::Sha256>();
107 let temp_key = gradecoin_private_key
108 .decrypt(padding, &request.key.as_bytes())
109 .expect("failed to decrypt");
110 106
111 // decrypt c using key dec_key 107 let key_ciphertext = match base64::decode(&request.key) {
112 let cipher = Aes128Cbc::new_var(&temp_key, &request.iv).unwrap(); 108 Ok(c) => c,
113 let auth_plaintext = cipher 109 Err(err) => {
114 .decrypt_vec(&base64::decode(request.c).unwrap()) 110 debug!(
115 .unwrap(); 111 "The ciphertext of the key was not base64 encoded {}, {}",
112 &request.key, err
113 );
114
115 let res_json = warp::reply::json(&GradeCoinResponse {
116 res: ResponseType::Error,
117 message: "The ciphertext of the key was not base64 encoded {}, {}".to_owned(),
118 });
119
120 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
121 }
122 };
123
124 let temp_key = match gradecoin_private_key.decrypt(padding, &key_ciphertext) {
125 Ok(k) => k,
126 Err(err) => {
127 debug!(
128 "Failed to decrypt ciphertext {:?}, {}",
129 &key_ciphertext, err
130 );
131
132 let res_json = warp::reply::json(&GradeCoinResponse {
133 res: ResponseType::Error,
134 message: "Failed to decrypt the ciphertext of the temporary key".to_owned(),
135 });
136
137 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
138 }
139 };
140
141 let cipher = match Aes128Cbc::new_var(&temp_key, &request.iv.as_bytes()) {
142 Ok(c) => c,
143 Err(err) => {
144 debug!(
145 "Could not create a cipher from temp_key and request.iv {:?}, {}, {}",
146 &temp_key, &request.iv, err
147 );
148
149 let res_json = warp::reply::json(&GradeCoinResponse {
150 res: ResponseType::Error,
151 message: "Given IV has invalid length".to_owned(),
152 });
153
154 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
155 }
156 };
116 157
117 let request: AuthRequest = 158 let auth_packet = match base64::decode(&request.c) {
118 serde_json::from_str(&String::from_utf8(auth_plaintext).unwrap()).unwrap(); 159 Ok(a) => a,
160
161 Err(err) => {
162 debug!(
163 "The auth_packet (c field) did not base64 decode {} {}",
164 &request.c, err
165 );
166
167 let res_json = warp::reply::json(&GradeCoinResponse {
168 res: ResponseType::Error,
169 message: "The c field was not correctly base64 encoded".to_owned(),
170 });
171
172 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
173 }
174 };
175
176 let auth_plaintext = match cipher.decrypt_vec(&auth_packet) {
177 Ok(p) => p,
178 Err(err) => {
179 debug!(
180 "Base64 decoded auth request did not decrypt correctly {:?} {}",
181 &auth_packet, err
182 );
183
184 let res_json = warp::reply::json(&GradeCoinResponse {
185 res: ResponseType::Error,
186 message: "The Bas64 decoded auth request did not decrypt correctly".to_owned(),
187 });
188
189 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
190 }
191 };
192
193 let utf8_auth_plaintext = match String::from_utf8(auth_plaintext.clone()) {
194 Ok(text) => text,
195 Err(err) => {
196 debug!(
197 "Auth plaintext did not convert into utf8 {:?} {}",
198 &auth_plaintext, err
199 );
200
201 let res_json = warp::reply::json(&GradeCoinResponse {
202 res: ResponseType::Error,
203 message: "Auth plaintext couldn't get converted to UTF-8".to_owned(),
204 });
205
206 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
207 }
208 };
209
210 let request: AuthRequest = match serde_json::from_str(&utf8_auth_plaintext) {
211 Ok(req) => req,
212 Err(err) => {
213 debug!(
214 "Auth plaintext did not serialize correctly {:?} {}",
215 &utf8_auth_plaintext, err
216 );
217
218 let res_json = warp::reply::json(&GradeCoinResponse {
219 res: ResponseType::Error,
220 message: "The auth request JSON did not serialize correctly".to_owned(),
221 });
222
223 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
224 }
225 };
119 226
120 let provided_id = request.student_id.clone(); 227 let provided_id = request.student_id.clone();
121 228
@@ -131,33 +238,24 @@ pub async fn authenticate_user(
131 } 238 }
132 }; 239 };
133 240
134 let userlist = db.users.upgradable_read(); 241 {
135 242 let userlist = db.users.read();
136 if userlist.contains_key(&provided_id) {
137 let res_json = warp::reply::json(&GradeCoinResponse {
138 res: ResponseType::Error,
139 message:
140 "This user is already authenticated, do you think this is a mistake? Contact me"
141 .to_owned(),
142 });
143
144 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
145 }
146 243
147 // We're using this as the validator 244 if userlist.contains_key(&provided_id) {
148 // I hate myself 245 let res_json = warp::reply::json(&GradeCoinResponse {
149 if let Err(_) = DecodingKey::from_rsa_pem(request.public_key.as_bytes()) { 246 res: ResponseType::Error,
150 let res_json = warp::reply::json(&GradeCoinResponse { 247 message:
151 res: ResponseType::Error, 248 "This user is already authenticated, do you think this is a mistake? Contact me"
152 message: "The supplied RSA public key is not in valid PEM format".to_owned(), 249 .to_owned(),
153 }); 250 });
154 251
155 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); 252 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
253 }
156 } 254 }
157 255
158 // We're using this as the validator 256 // We're using this as the validator
159 // I hate myself 257 // I hate myself
160 if let Err(_) = DecodingKey::from_rsa_pem(request.public_key.as_bytes()) { 258 if DecodingKey::from_rsa_pem(request.public_key.as_bytes()).is_err() {
161 let res_json = warp::reply::json(&GradeCoinResponse { 259 let res_json = warp::reply::json(&GradeCoinResponse {
162 res: ResponseType::Error, 260 res: ResponseType::Error,
163 message: "The supplied RSA public key is not in valid PEM format".to_owned(), 261 message: "The supplied RSA public key is not in valid PEM format".to_owned(),
@@ -174,11 +272,19 @@ pub async fn authenticate_user(
174 balance: 0, 272 balance: 0,
175 }; 273 };
176 274
177 let user_json = serde_json::to_string(&new_user).unwrap(); 275 let user_at_rest_json = serde_json::to_string(&UserAtRest {
276 user: User {
277 user_id: new_user.user_id.clone(),
278 public_key: new_user.public_key.clone(),
279 balance: 0,
280 },
281 fingerprint: fingerprint.clone(),
282 })
283 .unwrap();
178 284
179 fs::write(format!("users/{}.guy", new_user.user_id), user_json).unwrap(); 285 fs::write(format!("users/{}.guy", new_user.user_id), user_at_rest_json).unwrap();
180 286
181 let mut userlist = RwLockUpgradableReadGuard::upgrade(userlist); 287 let mut userlist = db.users.write();
182 288
183 userlist.insert(fingerprint.clone(), new_user); 289 userlist.insert(fingerprint.clone(), new_user);
184 290
@@ -193,17 +299,6 @@ pub async fn authenticate_user(
193 Ok(warp::reply::with_status(res_json, StatusCode::CREATED)) 299 Ok(warp::reply::with_status(res_json, StatusCode::CREATED))
194} 300}
195 301
196// fn shed_pem_header_footer(maybe_key: String) -> Result<Vec<u8>, String> {
197// let der_encoded = maybe_key
198// .lines()
199// .filter(|line| !line.starts_with("-"))
200// .fold(String::new(), |mut data, line| {
201// data.push_str(&line);
202// data
203// });
204// Ok(base64::decode(&der_encoded).expect("failed to decode base64 content"))
205// }
206
207/// GET /transaction 302/// GET /transaction
208/// Returns JSON array of transactions 303/// Returns JSON array of transactions
209/// Cannot fail 304/// Cannot fail
@@ -241,7 +336,7 @@ pub async fn authorized_propose_block(
241 336
242 println!("{:?}", &new_block); 337 println!("{:?}", &new_block);
243 338
244 if new_block.transaction_list.len() < 1 { 339 if new_block.transaction_list.is_empty() {
245 let res_json = warp::reply::json(&GradeCoinResponse { 340 let res_json = warp::reply::json(&GradeCoinResponse {
246 res: ResponseType::Error, 341 res: ResponseType::Error,
247 message: format!( 342 message: format!(
@@ -322,8 +417,8 @@ pub async fn authorized_propose_block(
322 417
323 let naked_block = NakedBlock { 418 let naked_block = NakedBlock {
324 transaction_list: new_block.transaction_list.clone(), 419 transaction_list: new_block.transaction_list.clone(),
325 nonce: new_block.nonce.clone(), 420 nonce: new_block.nonce,
326 timestamp: new_block.timestamp.clone(), 421 timestamp: new_block.timestamp,
327 }; 422 };
328 423
329 let naked_block_flat = serde_json::to_vec(&naked_block).unwrap(); 424 let naked_block_flat = serde_json::to_vec(&naked_block).unwrap();
@@ -556,7 +651,7 @@ pub async fn list_blocks(db: Db) -> Result<impl warp::Reply, Infallible> {
556/// *[`jwt_token`]: The raw JWT token, "Bearer aaa.bbb.ccc" 651/// *[`jwt_token`]: The raw JWT token, "Bearer aaa.bbb.ccc"
557/// *[`user_pem`]: User Public Key, "BEGIN RSA" 652/// *[`user_pem`]: User Public Key, "BEGIN RSA"
558/// NOT async, might look into it if this becomes a bottleneck 653/// NOT async, might look into it if this becomes a bottleneck
559fn authorize_proposer(jwt_token: String, user_pem: &String) -> Result<TokenData<Claims>, String> { 654fn authorize_proposer(jwt_token: String, user_pem: &str) -> Result<TokenData<Claims>, String> {
560 // Throw away the "Bearer " part 655 // Throw away the "Bearer " part
561 let raw_jwt = jwt_token.trim_start_matches(BEARER).to_owned(); 656 let raw_jwt = jwt_token.trim_start_matches(BEARER).to_owned();
562 debug!("raw_jwt: {:?}", raw_jwt); 657 debug!("raw_jwt: {:?}", raw_jwt);
@@ -599,3 +694,19 @@ fn authorize_proposer(jwt_token: String, user_pem: &String) -> Result<TokenData<
599 694
600 Ok(token_payload) 695 Ok(token_payload)
601} 696}
697
698#[derive(Template)]
699#[template(path = "welcome.html")]
700struct WelcomeTemplate<'a> {
701 title: &'a str,
702 body: &'a str,
703}
704
705pub async fn welcome_handler() -> Result<impl warp::Reply, warp::Rejection> {
706 let template = WelcomeTemplate {
707 title: "Welcome",
708 body: "To The Bookstore!",
709 };
710 let res = template.render().unwrap();
711 Ok(warp::reply::html(res))
712}
diff --git a/src/lib.rs b/src/lib.rs
index 7a24f9f..5442c6b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -22,12 +22,11 @@
22//! `Authorization`: The request header should have Bearer JWT.Token signed with Student Public Key 22//! `Authorization`: The request header should have Bearer JWT.Token signed with Student Public Key
23 23
24pub mod custom_filters; 24pub mod custom_filters;
25pub mod error;
26pub mod handlers; 25pub mod handlers;
27pub mod routes; 26pub mod routes;
28pub mod schema; 27pub mod schema;
29 28
30pub const PRIVATE_KEY: &'static str = "-----BEGIN RSA PRIVATE KEY----- 29pub const PRIVATE_KEY: &str = "-----BEGIN RSA PRIVATE KEY-----
31MIIEogIBAAKCAQEAyGuqiCPGcguy+Y9TH7Bl7XlEsalyqb9bYlzpbV0dnqZ3lPkE 30MIIEogIBAAKCAQEAyGuqiCPGcguy+Y9TH7Bl7XlEsalyqb9bYlzpbV0dnqZ3lPkE
32PkuOhkN+GcuiV6iXtSwyh7nB+xTRXKJFRUBO/jbN8jfcxVwBu0JxjF3v1YRBxbOH 31PkuOhkN+GcuiV6iXtSwyh7nB+xTRXKJFRUBO/jbN8jfcxVwBu0JxjF3v1YRBxbOH
33hz2A295mbKD9xHQCKxkfYBNkUXxj8gd+GaDvQiSW5NdrX/lEkvqfGtdEX1m2+Hdc 32hz2A295mbKD9xHQCKxkfYBNkUXxj8gd+GaDvQiSW5NdrX/lEkvqfGtdEX1m2+Hdc
@@ -55,7 +54,7 @@ PDYHM9dfQ8xn51U0fTeaXjy/8Km8fyX2Jtxntlm6puyhSTJ8AX+FEgJkC4ajNEvA
55mJ1Gsy2fXKUyyZdI2b74MLqOpzr9cvS60tmTIScuiHFzg/SJgiA= 54mJ1Gsy2fXKUyyZdI2b74MLqOpzr9cvS60tmTIScuiHFzg/SJgiA=
56-----END RSA PRIVATE KEY-----"; 55-----END RSA PRIVATE KEY-----";
57 56
58pub const PUB_KEY: &'static str = "-----BEGIN PUBLIC KEY----- 57pub const PUB_KEY: &str = "-----BEGIN PUBLIC KEY-----
59MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyGuqiCPGcguy+Y9TH7Bl 58MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyGuqiCPGcguy+Y9TH7Bl
607XlEsalyqb9bYlzpbV0dnqZ3lPkEPkuOhkN+GcuiV6iXtSwyh7nB+xTRXKJFRUBO 597XlEsalyqb9bYlzpbV0dnqZ3lPkEPkuOhkN+GcuiV6iXtSwyh7nB+xTRXKJFRUBO
61/jbN8jfcxVwBu0JxjF3v1YRBxbOHhz2A295mbKD9xHQCKxkfYBNkUXxj8gd+GaDv 60/jbN8jfcxVwBu0JxjF3v1YRBxbOHhz2A295mbKD9xHQCKxkfYBNkUXxj8gd+GaDv
diff --git a/src/routes.rs b/src/routes.rs
index 280de35..52d357a 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -7,11 +7,19 @@ use crate::schema::Db;
7 7
8/// Every route combined 8/// Every route combined
9pub fn consensus_routes(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { 9pub fn consensus_routes(db: Db) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
10 // Remember when we wanted to implement templating
11 // Why would we? Just put a staic webpage under /public (next to Cargo.toml) and place it and
12 // the end of the filter chain
13
14 // Fully fledged website support, phew!
15 let static_route = warp::any().and(warp::fs::dir("public"));
16
10 transaction_list(db.clone()) 17 transaction_list(db.clone())
11 .or(register_user(db.clone())) 18 .or(register_user(db.clone()))
12 .or(auth_transaction_propose(db.clone())) 19 .or(auth_transaction_propose(db.clone()))
13 .or(auth_block_propose(db.clone())) 20 .or(auth_block_propose(db.clone()))
14 .or(block_list(db.clone())) 21 .or(block_list(db))
22 .or(static_route)
15} 23}
16 24
17/// POST /register warp route 25/// POST /register warp route
@@ -60,4 +68,3 @@ pub fn auth_block_propose(db: Db) -> impl Filter<Extract = impl Reply, Error = R
60 .and(custom_filters::with_db(db)) 68 .and(custom_filters::with_db(db))
61 .and_then(handlers::authorized_propose_block) 69 .and_then(handlers::authorized_propose_block)
62} 70}
63
diff --git a/src/schema.rs b/src/schema.rs
index 264d2bd..cb353e4 100644
--- a/src/schema.rs
+++ b/src/schema.rs
@@ -9,6 +9,7 @@
9//! Users are held in memory and they're also backed up to text files 9//! Users are held in memory and they're also backed up to text files
10use chrono::{NaiveDate, NaiveDateTime}; 10use chrono::{NaiveDate, NaiveDateTime};
11use lazy_static::lazy_static; 11use lazy_static::lazy_static;
12use log::debug;
12use parking_lot::RwLock; 13use parking_lot::RwLock;
13use serde::{Deserialize, Serialize}; 14use serde::{Deserialize, Serialize};
14use std::collections::{HashMap, HashSet}; 15use std::collections::{HashMap, HashSet};
@@ -23,8 +24,6 @@ use std::vec::Vec;
23 24
24pub type Fingerprint = String; 25pub type Fingerprint = String;
25 26
26// TODO: start by reading users from file too <14-04-21, yigit> //
27
28fn block_parser(path: String) -> u64 { 27fn block_parser(path: String) -> u64 {
29 let end_pos = path.find(".block").unwrap(); 28 let end_pos = path.find(".block").unwrap();
30 let block_str = path[9..end_pos].to_string(); 29 let block_str = path[9..end_pos].to_string();
@@ -63,16 +62,49 @@ fn read_block_name() -> io::Result<Vec<PathBuf>> {
63 Ok(entries) 62 Ok(entries)
64} 63}
65 64
66fn create_db_with_last_block(path: String) -> Db { 65fn read_users() -> io::Result<Vec<PathBuf>> {
66 let entries = fs::read_dir("./users")?
67 .map(|res| res.map(|e| e.path()))
68 .collect::<Result<Vec<_>, io::Error>>()?;
69
70 Ok(entries)
71}
72
73fn populate_db_with_last_block(db: &mut Db, path: String) -> &mut Db {
74 debug!("Populating db with last block {}", path);
67 let file = fs::read(path).unwrap(); 75 let file = fs::read(path).unwrap();
68 let json = std::str::from_utf8(&file).unwrap(); 76 let json = std::str::from_utf8(&file).unwrap();
69 let block: Block = serde_json::from_str(json).unwrap(); 77 let block: Block = serde_json::from_str(json).unwrap();
70 let db = Db::new();
71 *db.blockchain.write() = block; 78 *db.blockchain.write() = block;
72 return db; 79
80 db
81}
82
83#[derive(Debug, Serialize, Deserialize, PartialEq)]
84pub struct UserAtRest {
85 pub fingerprint: Fingerprint,
86 pub user: User,
73} 87}
74 88
75/// Creates a new database, uses the previous last block if one exists 89fn populate_db_with_users(db: &mut Db, files: Vec<PathBuf>) -> &mut Db {
90 for fs in files {
91 if let Ok(file_content) = fs::read(fs) {
92 let json =
93 String::from_utf8(file_content).expect("we have written a malformed user file");
94 let user_at_rest: UserAtRest = serde_json::from_str(&json).unwrap();
95
96 debug!("Populating db with user: {:?}", user_at_rest);
97 db.users
98 .write()
99 .insert(user_at_rest.fingerprint, user_at_rest.user);
100 }
101 }
102
103 db
104}
105
106/// Creates a new database, uses the previous last block if one exists and attempts the populate
107/// the users
76pub fn create_database() -> Db { 108pub fn create_database() -> Db {
77 fs::create_dir_all("blocks").unwrap(); 109 fs::create_dir_all("blocks").unwrap();
78 fs::create_dir_all("users").unwrap(); 110 fs::create_dir_all("users").unwrap();
@@ -82,6 +114,12 @@ pub fn create_database() -> Db {
82 } else { 114 } else {
83 return Db::new(); 115 return Db::new();
84 } 116 }
117
118 if let Ok(users_path) = read_users() {
119 populate_db_with_users(&mut db, users_path);
120 }
121
122 db
85} 123}
86 124
87/// A JWT Payload/Claims representation 125/// A JWT Payload/Claims representation
@@ -181,13 +219,23 @@ impl Block {
181 Block { 219 Block {
182 transaction_list: vec!["gradecoin_bank".to_owned()], 220 transaction_list: vec!["gradecoin_bank".to_owned()],
183 nonce: 0, 221 nonce: 0,
184 timestamp: NaiveDate::from_ymd(2021, 04, 11).and_hms(20, 45, 00), 222 timestamp: NaiveDate::from_ymd(2021, 4, 11).and_hms(20, 45, 00),
185 hash: String::from("not_actually_mined"), 223 hash: String::from("not_actually_mined"),
186 } 224 }
187 } 225 }
188} 226}
189 227
190/// Simply a Student 228impl Default for Block {
229 fn default() -> Self {
230 Self::new()
231 }
232}
233
234/// A Student
235///
236/// * [`user_id`]: Can only be one of the repopulated
237/// * [`public_key`]: A PEM format public key "---- BEGIN" and all
238/// * [`balance`]: User's current Gradecoin amount
191#[derive(Serialize, Deserialize, Debug, PartialEq)] 239#[derive(Serialize, Deserialize, Debug, PartialEq)]
192pub struct User { 240pub struct User {
193 pub user_id: MetuId, 241 pub user_id: MetuId,
@@ -196,7 +244,7 @@ pub struct User {
196} 244}
197 245
198/// The values are hard coded in [`OUR_STUDENTS`] so MetuId::new() can accept/reject values based on that 246/// The values are hard coded in [`OUR_STUDENTS`] so MetuId::new() can accept/reject values based on that
199#[derive(Serialize, Deserialize, Debug, PartialEq)] 247#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
200pub struct MetuId { 248pub struct MetuId {
201 id: String, 249 id: String,
202 passwd: String, 250 passwd: String,
@@ -214,7 +262,7 @@ pub struct AuthRequest {
214#[derive(Serialize, Deserialize, Debug)] 262#[derive(Serialize, Deserialize, Debug)]
215pub struct InitialAuthRequest { 263pub struct InitialAuthRequest {
216 pub c: String, 264 pub c: String,
217 pub iv: [u8; 32], 265 pub iv: String,
218 pub key: String, 266 pub key: String,
219} 267}
220 268
@@ -265,10 +313,7 @@ impl fmt::Display for MetuId {
265impl MetuId { 313impl MetuId {
266 pub fn new(id: String, pwd: String) -> Option<Self> { 314 pub fn new(id: String, pwd: String) -> Option<Self> {
267 if OUR_STUDENTS.contains(&(&*id, &*pwd)) { 315 if OUR_STUDENTS.contains(&(&*id, &*pwd)) {
268 Some(MetuId { 316 Some(MetuId { id, passwd: pwd })
269 id: id,
270 passwd: pwd,
271 })
272 } else { 317 } else {
273 None 318 None
274 } 319 }
diff --git a/templates/css.html b/templates/css.html
new file mode 100644
index 0000000..c9d54e3
--- /dev/null
+++ b/templates/css.html
@@ -0,0 +1,8 @@
1<style>
2 td, th {
3 padding: 8px;
4 }
5 th {
6 text-align: left;
7 }
8</style>
diff --git a/templates/footer.html b/templates/footer.html
new file mode 100644
index 0000000..2ab5c0d
--- /dev/null
+++ b/templates/footer.html
@@ -0,0 +1,2 @@
1 </body>
2</html>
diff --git a/templates/header.html b/templates/header.html
new file mode 100644
index 0000000..fffbefe
--- /dev/null
+++ b/templates/header.html
@@ -0,0 +1,11 @@
1<html>
2 <head>
3 <title>Bookstore</title>
4 {% include "css.html" %}
5 </head>
6 <body>
7 <div>
8 <h1>Bookstore</h1>
9 </div>
10 {% include "menu.html" %}
11 <hr />
diff --git a/templates/menu.html b/templates/menu.html
new file mode 100644
index 0000000..0f4e85f
--- /dev/null
+++ b/templates/menu.html
@@ -0,0 +1,5 @@
1<div class="menu">
2 <span class="menuitem">
3 <a href = "/books/list">Books</a>
4 </span>
5</div>
diff --git a/templates/welcome.html b/templates/welcome.html
new file mode 100644
index 0000000..ca60f33
--- /dev/null
+++ b/templates/welcome.html
@@ -0,0 +1,8 @@
1{% include "header.html" %}
2<div class="entry">
3 <h1>{{title}}</h1>
4 <div class="body">
5 {{body}}
6 </div>
7</div>
8{% include "footer.html" %}