├── .gitattributes
├── tela.png
├── .gitignore
├── tela_tests
├── contracts
│ ├── test2.bas
│ ├── test4.bas
│ ├── test5.bas
│ ├── test6.bas
│ ├── test3.bas
│ └── test1.bas
├── images
│ ├── test2.svg
│ └── test1.svg
├── app2
│ └── index.html
└── app1
│ ├── index.html
│ ├── style.css
│ └── main.js
├── shards
├── keys.go
├── values.go
├── boltdb.go
├── gravdb.go
├── shards.go
└── shards_test.go
├── TELA-MOD-1
├── vs
│ ├── TELA-MOD-1-VSOOIM.bas
│ ├── TELA-MOD-1-VSOO.bas
│ ├── TELA-MOD-1-VSPUBIM.bas
│ ├── TELA-MOD-1-VSPUBOW.bas
│ └── TELA-MOD-1-VSPUBSU.bas
└── tx
│ ├── TELA-MOD-1-TXTO.bas
│ ├── TELA-MOD-1-TXDWD.bas
│ └── TELA-MOD-1-TXDWA.bas
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── pull_request_template.md
├── LICENSE
├── TELA-DOC-1
├── TELA-DOC-1.bas
└── README.md
├── CHANGELOG.md
├── TELA-INDEX-1
├── TELA-INDEX-1.bas
└── README.md
├── logger
├── colors.go
├── ascii.go
├── logger.go
├── ascii_test.go
└── logger_test.go
├── headers.go
├── go.mod
├── compression.go
├── ratings.go
├── cmd
└── tela-cli
│ ├── gnomon.go
│ └── functions_test.go
├── mods.go
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.bas linguist-language=BASIC
--------------------------------------------------------------------------------
/tela.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/civilware/tela/HEAD/tela.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | datashards*
2 | mainnet*
3 | testnet*
4 | tela_sim_wallets*
--------------------------------------------------------------------------------
/tela_tests/contracts/test2.bas:
--------------------------------------------------------------------------------
1 | // Test code for TELA parser, should error when parsing with dvm.ParseSmartContract
2 | Function Initialize() bool // wrong return type errors dvm.ParseSmartContract()
3 | 10 RETURN 0
4 | End Function
--------------------------------------------------------------------------------
/tela_tests/contracts/test4.bas:
--------------------------------------------------------------------------------
1 | // Test code for TELA parser, should be valid to parse with dvm.ParseSmartContract
2 | Function Initialize(u Uint64, s String) String
3 | 10 RETURN 0
4 | End Function
5 |
6 | Function test()
7 | 10 RETURN 0
8 | End Function
--------------------------------------------------------------------------------
/tela_tests/contracts/test5.bas:
--------------------------------------------------------------------------------
1 | // Test code for TELA parser, should be valid to parse with dvm.ParseSmartContract
2 | Function Initialize(u Uint64, s String) String
3 | 10 RETURN 0
4 | End Function
5 |
6 | Function test()
7 | 15 RETURN 0
8 | End Function
--------------------------------------------------------------------------------
/tela_tests/contracts/test6.bas:
--------------------------------------------------------------------------------
1 | // Test code for TELA parser, should be valid to parse with dvm.ParseSmartContract
2 | Function Initialize(u Uint64, s String) String
3 | 10 RETURN 0
4 | End Function
5 |
6 | Function test2()
7 | 10 RETURN 0
8 | End Function
--------------------------------------------------------------------------------
/tela_tests/contracts/test3.bas:
--------------------------------------------------------------------------------
1 | // Test code for TELA parser, should be valid to parse with dvm.ParseSmartContract
2 | Function Initialize(u Uint64, s String) String
3 | 10 RETURN 0
4 | End Function
5 |
6 | Function test() Uint64
7 | 10 IF EXISTS() THEN GOTO 10
8 | End Function
--------------------------------------------------------------------------------
/tela_tests/images/test2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tela_tests/contracts/test1.bas:
--------------------------------------------------------------------------------
1 | // Test code for TELA parser, should be valid to parse with dvm.ParseSmartContract
2 | Function Initialize(u Uint64, s String) String
3 | 10 RETURN 0
4 | End Function
5 |
6 | Function test()
7 | 10 RETURN ! // ! hits add single operator
8 | End Function
--------------------------------------------------------------------------------
/shards/keys.go:
--------------------------------------------------------------------------------
1 | package shards
2 |
3 | type keys struct{}
4 |
5 | // Common storage keys
6 | var Key keys
7 |
8 | // Endpoint DB storage key
9 | func (keys) Endpoint() []byte {
10 | return []byte("endpoint")
11 | }
12 |
13 | // Network DB storage key
14 | func (keys) Network() []byte {
15 | return []byte("network")
16 | }
17 |
--------------------------------------------------------------------------------
/TELA-MOD-1/vs/TELA-MOD-1-VSOOIM.bas:
--------------------------------------------------------------------------------
1 | // Copyright 2024. Civilware. All rights reserved.
2 | // TELA Decentralized Web Standard Module (TELA-MOD-1)
3 |
4 | Function SetVar(k String, v String) Uint64
5 | 10 IF LOAD("owner") == "anon" THEN GOTO 20
6 | 11 IF EXISTS("ivar_"+k) THEN GOTO 20
7 | 12 IF LOAD("owner") == address() THEN GOTO 30
8 | 20 RETURN 1
9 | 30 STORE("ivar_"+k, v)
10 | 40 RETURN 0
11 | End Function
--------------------------------------------------------------------------------
/TELA-MOD-1/vs/TELA-MOD-1-VSOO.bas:
--------------------------------------------------------------------------------
1 | // Copyright 2024. Civilware. All rights reserved.
2 | // TELA Decentralized Web Standard Module (TELA-MOD-1)
3 |
4 | Function SetVar(k String, v String) Uint64
5 | 10 IF LOAD("owner") == "anon" THEN GOTO 20
6 | 11 IF LOAD("owner") == address() THEN GOTO 30
7 | 20 RETURN 1
8 | 30 STORE("var_"+k, v)
9 | 40 RETURN 0
10 | End Function
11 |
12 | Function DeleteVar(k String) Uint64
13 | 10 IF EXISTS("var_"+k) == 0 THEN GOTO 20
14 | 11 IF LOAD("owner") == "anon" THEN GOTO 20
15 | 12 IF LOAD("owner") == address() THEN GOTO 30
16 | 20 RETURN 1
17 | 30 DELETE("var_"+k)
18 | 40 RETURN 0
19 | End Function
--------------------------------------------------------------------------------
/TELA-MOD-1/vs/TELA-MOD-1-VSPUBIM.bas:
--------------------------------------------------------------------------------
1 | // Copyright 2024. Civilware. All rights reserved.
2 | // TELA Decentralized Web Standard Module (TELA-MOD-1)
3 |
4 | Function SetVar(k String, v String) Uint64
5 | 10 IF LOAD("owner") == "anon" THEN GOTO 20
6 | 11 DIM addr as String
7 | 12 LET addr = address()
8 | 13 IF LOAD("owner") == addr THEN GOTO 30
9 | 14 IF addr == "anon" THEN GOTO 20
10 | 15 IF STRLEN(k) > 256 || STRLEN(v) > 256 THEN GOTO 20
11 | 16 IF EXISTS("ivar_"+addr+"_"+k) == 0 THEN GOTO 50
12 | 20 RETURN 1
13 | 30 IF EXISTS("ivar_"+k) THEN GOTO 20
14 | 31 STORE("ivar_"+k, v)
15 | 40 RETURN 0
16 | 50 STORE("ivar_"+addr+"_"+k, v)
17 | 90 RETURN 0
18 | End Function
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Provide a code example, or steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Enter this '....'
17 | 3. Enter that '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Additional context**
27 | Add any other context about the problem here.
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[FEATURE]"
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/tela_tests/images/test1.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/TELA-MOD-1/tx/TELA-MOD-1-TXTO.bas:
--------------------------------------------------------------------------------
1 | // Copyright 2024. Civilware. All rights reserved.
2 | // TELA Decentralized Web Standard Module (TELA-MOD-1)
3 |
4 | Function TransferOwnership(addr String) Uint64
5 | 10 IF LOAD("owner") == "anon" THEN GOTO 20
6 | 11 IF IS_ADDRESS_VALID(ADDRESS_RAW(addr)) == 0 THEN GOTO 20
7 | 12 IF LOAD("owner") == address() THEN GOTO 30 // address() comes from TELA base code
8 | 20 RETURN 1
9 | 30 STORE("tmpowner", addr) // Use the string to match address()
10 | 40 RETURN 0
11 | End Function
12 |
13 | Function ClaimOwnership() Uint64
14 | 10 IF LOAD("owner") == "anon" THEN GOTO 20
15 | 11 IF EXISTS("tmpowner") == 0 THEN GOTO 20
16 | 12 IF LOAD("tmpowner") == address() THEN GOTO 30
17 | 20 RETURN 1
18 | 30 STORE("owner", address())
19 | 40 DELETE("tmpowner")
20 | 50 RETURN 0
21 | End Function
--------------------------------------------------------------------------------
/shards/values.go:
--------------------------------------------------------------------------------
1 | package shards
2 |
3 | type network struct{}
4 |
5 | type values struct {
6 | Network network
7 | }
8 |
9 | // Common storage values
10 | var Value values
11 |
12 | // Valid DERO networks
13 | var validNetworks = []string{
14 | "Mainnet",
15 | "Testnet",
16 | "Simulator",
17 | }
18 |
19 | // Is network a valid DERO network
20 | func isNetworkValid(network string) bool {
21 | for _, n := range validNetworks {
22 | if network == n {
23 | return true
24 | }
25 | }
26 |
27 | return false
28 | }
29 |
30 | // Mainnet network value
31 | func (network) Mainnet() string {
32 | return validNetworks[0]
33 | }
34 |
35 | // Testnet network value
36 | func (network) Testnet() string {
37 | return validNetworks[1]
38 | }
39 |
40 | // Simulator network value
41 | func (network) Simulator() string {
42 | return validNetworks[2]
43 | }
44 |
--------------------------------------------------------------------------------
/TELA-MOD-1/vs/TELA-MOD-1-VSPUBOW.bas:
--------------------------------------------------------------------------------
1 | // Copyright 2024. Civilware. All rights reserved.
2 | // TELA Decentralized Web Standard Module (TELA-MOD-1)
3 |
4 | Function SetVar(k String, v String) Uint64
5 | 10 IF LOAD("owner") == "anon" THEN GOTO 20
6 | 11 DIM addr as String
7 | 12 LET addr = address()
8 | 13 IF LOAD("owner") == addr THEN GOTO 30
9 | 14 IF addr == "anon" THEN GOTO 20
10 | 15 IF STRLEN(k) > 256 || STRLEN(v) > 256 THEN GOTO 20
11 | 16 GOTO 50 // Match the flow of vspubsu
12 | 20 RETURN 1
13 | 30 STORE("var_"+k, v)
14 | 40 RETURN 0
15 | 50 STORE("var_"+addr+"_"+k, v)
16 | 90 RETURN 0
17 | End Function
18 |
19 | Function DeleteVar(k String) Uint64
20 | 10 IF EXISTS("var_"+k) == 0 THEN GOTO 20
21 | 11 IF LOAD("owner") == "anon" THEN GOTO 20
22 | 12 IF LOAD("owner") == address() THEN GOTO 30
23 | 20 RETURN 1
24 | 30 DELETE("var_"+k)
25 | 40 RETURN 0
26 | End Function
--------------------------------------------------------------------------------
/TELA-MOD-1/tx/TELA-MOD-1-TXDWD.bas:
--------------------------------------------------------------------------------
1 | // Copyright 2024. Civilware. All rights reserved.
2 | // TELA Decentralized Web Standard Module (TELA-MOD-1)
3 |
4 | Function DepositDero() Uint64
5 | 10 IF LOAD("owner") == "anon" THEN GOTO 20
6 | 11 IF DEROVALUE() > 0 THEN GOTO 30
7 | 20 RETURN 1
8 | 30 IF EXISTS("balance_dero") THEN GOTO 60
9 | 40 STORE("balance_dero", DEROVALUE())
10 | 50 GOTO 90
11 | 60 STORE("balance_dero", LOAD("balance_dero")+DEROVALUE())
12 | 90 RETURN 0
13 | End Function
14 |
15 | Function WithdrawDero(amt Uint64) Uint64
16 | 10 IF LOAD("owner") == "anon" THEN GOTO 20
17 | 12 IF EXISTS("balance_dero") == 0 THEN GOTO 20
18 | 13 IF LOAD("balance_dero") < amt THEN GOTO 20
19 | 14 IF LOAD("owner") == address() THEN GOTO 30
20 | 20 RETURN 1
21 | 30 SEND_DERO_TO_ADDRESS(SIGNER(), amt)
22 | 40 STORE("balance_dero", LOAD("balance_dero")-amt)
23 | 90 RETURN 0
24 | End Function
--------------------------------------------------------------------------------
/TELA-MOD-1/vs/TELA-MOD-1-VSPUBSU.bas:
--------------------------------------------------------------------------------
1 | // Copyright 2024. Civilware. All rights reserved.
2 | // TELA Decentralized Web Standard Module (TELA-MOD-1)
3 |
4 | Function SetVar(k String, v String) Uint64
5 | 10 IF LOAD("owner") == "anon" THEN GOTO 20
6 | 11 DIM addr as String
7 | 12 LET addr = address()
8 | 13 IF LOAD("owner") == addr THEN GOTO 30
9 | 14 IF addr == "anon" THEN GOTO 20
10 | 15 IF STRLEN(k) > 256 || STRLEN(v) > 256 THEN GOTO 20
11 | 16 IF EXISTS("var_"+addr+"_"+k) == 0 THEN GOTO 50
12 | 20 RETURN 1
13 | 30 STORE("var_"+k, v)
14 | 40 RETURN 0
15 | 50 STORE("var_"+addr+"_"+k, v)
16 | 90 RETURN 0
17 | End Function
18 |
19 | Function DeleteVar(k String) Uint64
20 | 10 IF EXISTS("var_"+k) == 0 THEN GOTO 20
21 | 11 IF LOAD("owner") == "anon" THEN GOTO 20
22 | 12 IF LOAD("owner") == address() THEN GOTO 30
23 | 20 RETURN 1
24 | 30 DELETE("var_"+k)
25 | 40 RETURN 0
26 | End Function
--------------------------------------------------------------------------------
/TELA-MOD-1/tx/TELA-MOD-1-TXDWA.bas:
--------------------------------------------------------------------------------
1 | // Copyright 2024. Civilware. All rights reserved.
2 | // TELA Decentralized Web Standard Module (TELA-MOD-1)
3 |
4 | Function DepositAsset(scid String) Uint64
5 | 10 IF LOAD("owner") == "anon" THEN GOTO 20
6 | 11 IF ASSETVALUE(HEXDECODE(scid)) > 0 THEN GOTO 30
7 | 20 RETURN 1
8 | 30 IF EXISTS("balance_"+scid) THEN GOTO 60
9 | 40 STORE("balance_"+scid, ASSETVALUE(HEXDECODE(scid)))
10 | 50 GOTO 90
11 | 60 STORE("balance_"+scid, LOAD("balance_"+scid)+ASSETVALUE(HEXDECODE(scid)))
12 | 90 RETURN 0
13 | End Function
14 |
15 | Function WithdrawAsset(scid String, amt Uint64) Uint64
16 | 10 IF LOAD("owner") == "anon" THEN GOTO 20
17 | 12 IF EXISTS("balance_"+scid) == 0 THEN GOTO 20
18 | 13 IF LOAD("balance_"+scid) < amt THEN GOTO 20
19 | 14 IF LOAD("owner") == address() THEN GOTO 30
20 | 20 RETURN 1
21 | 30 SEND_ASSET_TO_ADDRESS(SIGNER(), amt, HEXDECODE(scid))
22 | 40 STORE("balance_"+scid, LOAD("balance_"+scid)-amt)
23 | 90 RETURN 0
24 | End Function
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Civilware
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 | Please include a summary of the changes and the related issue or feature.
4 |
5 | **NOTE**: The merge process is as follows:
6 | - Your pull request should be directed to `dev` branch.
7 | - When it will be merged in `dev`, we will compile and merge within a `release/` branch and then push into `main` for final release.
8 |
9 | Fixes # (issue)
10 |
11 | ## Type of change
12 |
13 | Please select the right one.
14 |
15 | - [ ] (Patch) Bug fix (non-breaking change which fixes an issue)
16 | - [ ] (Minor) New feature (non-breaking change which adds functionality)
17 | - [ ] (Major) Breaking change (modification that would disrupt existing functionality and require changes to maintain expected behavior)
18 | - [ ] This change requires a documentation update
19 |
20 | ## Which package(s) are impacted ?
21 |
22 | - [ ] cmd
23 | - [ ] tela
24 | - [ ] shards
25 | - [ ] logger
26 | - [ ] Misc (documentation, etc...)
27 |
28 | ## Checklist:
29 |
30 | - [ ] I have performed a self-review of my code
31 | - [ ] I have commented my code
32 | - [ ] I have test coverage for my changes
33 | - [ ] My changes generate no new warnings
34 |
35 | ## License
36 |
37 | I am contributing & releasing the code under the [MIT License](https://raw.githubusercontent.com/civilware/tela/main/LICENSE).
--------------------------------------------------------------------------------
/TELA-DOC-1/TELA-DOC-1.bas:
--------------------------------------------------------------------------------
1 | // Copyright 2024. Civilware. All rights reserved.
2 | // TELA Decentralized Web Document (TELA-DOC-1)
3 |
4 | Function InitializePrivate() Uint64
5 | 10 IF init() == 0 THEN GOTO 30
6 | 20 RETURN 1
7 | 30 STORE("var_header_name", "")
8 | 31 STORE("var_header_description", "")
9 | 32 STORE("var_header_icon", "")
10 | 33 STORE("dURL", "")
11 | 34 STORE("docType", "")
12 | 35 STORE("subDir", "")
13 | 36 STORE("fileCheckC", "")
14 | 37 STORE("fileCheckS", "")
15 | 100 RETURN 0
16 | End Function
17 |
18 | Function init() Uint64
19 | 10 IF EXISTS("owner") == 0 THEN GOTO 30
20 | 20 RETURN 1
21 | 30 STORE("owner", address())
22 | 50 STORE("docVersion", "1.0.0") // DOC SC version
23 | 60 STORE("hash", HEX(TXID()))
24 | 70 STORE("likes", 0)
25 | 80 STORE("dislikes", 0)
26 | 100 RETURN 0
27 | End Function
28 |
29 | Function address() String
30 | 10 DIM s as String
31 | 20 LET s = SIGNER()
32 | 30 IF IS_ADDRESS_VALID(s) THEN GOTO 50
33 | 40 RETURN "anon"
34 | 50 RETURN ADDRESS_STRING(s)
35 | End Function
36 |
37 | Function Rate(r Uint64) Uint64
38 | 10 DIM addr as String
39 | 15 LET addr = address()
40 | 16 IF r < 100 && EXISTS(addr) == 0 && addr != "anon" THEN GOTO 30
41 | 20 RETURN 1
42 | 30 STORE(addr, ""+r+"_"+BLOCK_HEIGHT())
43 | 40 IF r < 50 THEN GOTO 70
44 | 50 STORE("likes", LOAD("likes")+1)
45 | 60 RETURN 0
46 | 70 STORE("dislikes", LOAD("dislikes")+1)
47 | 100 RETURN 0
48 | End Function
49 |
50 | /*
51 | docType code goes in this comment section
52 | */
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## INDEX v1.1.0 - In Progress
4 |
5 | * Update `TELA-INDEX-1` SC to v1.1.0
6 | * Add `TELA-MOD-1` DVM function modules and related package functions.
7 | * Update `tela` package to handle smart contract versioning.
8 | * Update `tela` package to handle DocShards.
9 | * Update `tela` package to handle DOC compression.
10 | * Add `TELA-GO-1` as explicit golang docType.
11 | * Add CLI `functions_test.go` file
12 | * Add CLI `search exclude` commands.
13 | * Add CLI `set-var` and `delete-var` commands.
14 | * Add CLI `search` by key, value, all variables and line of code.
15 | * Add CLI `balance`, `sc-execute` and `gnomon add` commands.
16 | * Add CLI `file-info`, `file-shard` and `file-construct` commands.
17 | * Add CLI `file-diff` and `scid-diff` commands.
18 | * Add CLI `MODs` commands, search information and install/updating options.
19 | * Add CLI Git clone.
20 | * Fix CLI `--testnet` flag overwrite
21 | * Add CLI close handling on daemon disconnect
22 | * Add CLI file compress option and handling in applicable commands.
23 | * Add CLI gas estimate display.
24 | * Add `.bootstrap` INDEX dURL tag.
25 | * Add validation for on and off chain images.
26 | * Add DOC helper methods for extracting DocCode, SVGs and `` tags.
27 | * Add `var_` standard header support in addition to the existing ART-NFA standard headers.
28 | * Update test site webkit-scrollbar and favIcon
29 | * Update test site websocket methods.
30 | * Update repo documentation.
31 |
32 |
33 | ## TELA v1.0.0 - August 23 2024
34 |
35 | * Publish TELA go package source code along with TELA-CLI cmd.
36 |
--------------------------------------------------------------------------------
/TELA-INDEX-1/TELA-INDEX-1.bas:
--------------------------------------------------------------------------------
1 | // Copyright 2024. Civilware. All rights reserved.
2 | // TELA Decentralized Web Standard (TELA-INDEX-1)
3 |
4 | Function InitializePrivate() Uint64
5 | 10 IF init() == 0 THEN GOTO 30
6 | 20 RETURN 1
7 | 30 STORE("var_header_name", "")
8 | 31 STORE("var_header_description", "")
9 | 32 STORE("var_header_icon", "")
10 | 33 STORE("dURL", "")
11 | 34 STORE("mods", "")
12 | 40 STORE("DOC1", "")
13 | // 41 STORE("DOC2", "")
14 | // 42 STORE("DOC3", "")
15 | 1000 RETURN 0
16 | End Function
17 |
18 | Function init() Uint64
19 | 10 IF EXISTS("owner") == 0 THEN GOTO 30
20 | 20 RETURN 1
21 | 30 STORE("owner", address())
22 | 50 STORE("telaVersion", "1.1.0") // TELA SC version
23 | 60 STORE("commit", 0) // The initial commit
24 | 70 STORE(0, HEX(TXID())) // SCID commit hash
25 | 80 STORE("hash", HEX(TXID()))
26 | 85 STORE("likes", 0)
27 | 90 STORE("dislikes", 0)
28 | 100 RETURN 0
29 | End Function
30 |
31 | Function address() String
32 | 10 DIM s as String
33 | 20 LET s = SIGNER()
34 | 30 IF IS_ADDRESS_VALID(s) THEN GOTO 50
35 | 40 RETURN "anon"
36 | 50 RETURN ADDRESS_STRING(s)
37 | End Function
38 |
39 | Function Rate(r Uint64) Uint64
40 | 10 DIM addr as String
41 | 15 LET addr = address()
42 | 16 IF r < 100 && EXISTS(addr) == 0 && addr != "anon" THEN GOTO 30
43 | 20 RETURN 1
44 | 30 STORE(addr, ""+r+"_"+BLOCK_HEIGHT())
45 | 40 IF r < 50 THEN GOTO 70
46 | 50 STORE("likes", LOAD("likes")+1)
47 | 60 RETURN 0
48 | 70 STORE("dislikes", LOAD("dislikes")+1)
49 | 100 RETURN 0
50 | End Function
51 |
52 | Function UpdateCode(code String, mods String) Uint64
53 | 10 IF LOAD("owner") == "anon" THEN GOTO 20
54 | 15 IF code == "" THEN GOTO 20
55 | 16 IF LOAD("owner") == address() THEN GOTO 30
56 | 20 RETURN 1
57 | 30 UPDATE_SC_CODE(code)
58 | 40 STORE("commit", LOAD("commit")+1) // New commit
59 | 50 STORE(LOAD("commit"), HEX(TXID())) // New hash
60 | 60 STORE("hash", HEX(TXID()))
61 | 70 STORE("mods", mods)
62 | 100 RETURN 0
63 | End Function
--------------------------------------------------------------------------------
/logger/colors.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | // ANSI colors
4 | const (
5 | ANSIblack = "\033[30m"
6 | ANSIred = "\033[31m"
7 | ANSIgreen = "\033[32m"
8 | ANSIyellow = "\033[33m"
9 | ANSIblue = "\033[34m"
10 | ANSImagenta = "\033[35m"
11 | ANSIcyan = "\033[36m"
12 | ANSIwhite = "\033[37m"
13 | ANSIdefault = "\033[39m"
14 | ANSIgrey = "\033[90m"
15 | ANSIend = "\033[0m"
16 | )
17 |
18 | type colors struct {
19 | black string
20 | red string
21 | green string
22 | yellow string
23 | blue string
24 | magenta string
25 | cyan string
26 | white string
27 | fallback string
28 | grey string
29 | end string
30 | }
31 |
32 | // Colors for logger package, enabled or disabled with EnableColors()
33 | var Color = colors{
34 | black: ANSIblack,
35 | red: ANSIred,
36 | green: ANSIgreen,
37 | yellow: ANSIyellow,
38 | blue: ANSIblue,
39 | magenta: ANSImagenta,
40 | cyan: ANSIcyan,
41 | white: ANSIwhite,
42 | fallback: ANSIdefault,
43 | grey: ANSIgrey,
44 | end: ANSIend,
45 | }
46 |
47 | // ANSI color black
48 | func (c *colors) Black() string {
49 | return c.black
50 | }
51 |
52 | // ANSI color red
53 | func (c *colors) Red() string {
54 | return c.red
55 | }
56 |
57 | // ANSI color green
58 | func (c *colors) Green() string {
59 | return c.green
60 | }
61 |
62 | // ANSI color yellow
63 | func (c *colors) Yellow() string {
64 | return c.yellow
65 | }
66 |
67 | // ANSI color blue
68 | func (c *colors) Blue() string {
69 | return c.blue
70 | }
71 |
72 | // ANSI color magenta
73 | func (c *colors) Magenta() string {
74 | return c.magenta
75 | }
76 |
77 | // ANSI color cyan
78 | func (c *colors) Cyan() string {
79 | return c.cyan
80 | }
81 |
82 | // ANSI color white
83 | func (c *colors) White() string {
84 | return c.white
85 | }
86 |
87 | // ANSI color default
88 | func (c *colors) Default() string {
89 | return c.fallback
90 | }
91 |
92 | // ANSI color grey
93 | func (c *colors) Grey() string {
94 | return c.grey
95 | }
96 |
97 | // ANSI color end
98 | func (c *colors) End() string {
99 | return c.end
100 | }
101 |
--------------------------------------------------------------------------------
/logger/ascii.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | // Small TELA ASCII logo
9 | var ASCIISmall = []string{
10 | Color.red + ` ;;;` + Color.end,
11 | Color.red + ` ;;;` + Color.end,
12 |
13 | ` .WWW`,
14 | `::::::::::kMMM`,
15 | `WMMMMMMMMMMMMM`,
16 | `WMM, 'MMM`,
17 | `WMMx......dMMM`,
18 | `WMMMMMMMMMMMMM`,
19 | `WMM;`,
20 | `NWW'`,
21 |
22 | Color.green + `ooo. ` + Color.end,
23 | Color.green + `''' ` + Color.end,
24 | }
25 |
26 | // Main TELA ASCII logo
27 | var ASCIIMain = []string{
28 | Color.red + ` ;;;;;` + Color.end,
29 | Color.red + ` ;;;;;` + Color.end,
30 | Color.red + ` ooooo` + Color.end,
31 | ` MMMMM`,
32 | ` MMMMM`,
33 | `NWWWWWWWWWWWWWWMMMMM`,
34 | `WMMMMMMMMMMMMMMMMMMM`,
35 | `WMMMM MMMMM`,
36 | `WMMMM MMMMM`,
37 | `WMMMM;;;;;;;;;;MMMMM`,
38 | `WMMMMMMMMMMMMMMMMMMM`,
39 | `WMMMM`,
40 | `WMMMM`,
41 | `WMMMM`,
42 |
43 | Color.green + `dxxxx ` + Color.end,
44 | Color.green + `loooo ` + Color.end,
45 | Color.green + `''''' ` + Color.end,
46 | }
47 |
48 | // Blend ASCII into existing string for display purposes
49 | func ASCIIBlend(ascii, info []string) {
50 | if info == nil {
51 | return
52 | }
53 |
54 | // find longest line and use it as margin
55 | var marginLen int
56 | for _, line := range info {
57 | l := len(line)
58 | if l > marginLen {
59 | marginLen = l + 4
60 | }
61 | }
62 |
63 | var printed int
64 | asciiLen := len(ascii)
65 | if marginLen < len(ascii[0]) {
66 | marginLen = len(ascii[0]) + 11
67 | }
68 |
69 | for i, line := range info {
70 | var margin string
71 | if i < asciiLen {
72 | linePad := marginLen - len(line)
73 | margin = strings.Repeat(" ", linePad)
74 | fmt.Println(line + margin + ascii[i])
75 | } else {
76 | fmt.Println(line)
77 | }
78 | printed++
79 | }
80 |
81 | for printed < asciiLen {
82 | fmt.Println(strings.Repeat(" ", marginLen-1), ascii[printed])
83 | printed++
84 | }
85 | }
86 |
87 | // Print TELA ASCII main or small logo
88 | func ASCIIPrint(small bool) {
89 | ascii := ASCIIMain
90 | if small {
91 | ascii = ASCIISmall
92 | }
93 |
94 | for _, line := range ascii {
95 | fmt.Println(line)
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/tela_tests/app2/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TELA Demo Application 2
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | dero.io
44 |
45 |
52 |
53 |
54 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/headers.go:
--------------------------------------------------------------------------------
1 | package tela
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | // Standard SC header value stores
9 | type Headers struct {
10 | NameHdr string `json:"nameHdr"` // On-chain name of SC. For TELA-DOCs, they are recreated using this as the file name, it should include the file extension
11 | DescrHdr string `json:"descrHdr"` // On-chain description of DOC, INDEX or Asset SC
12 | IconHdr string `json:"iconHdr"` // On-chain icon URL, (size 100x100)
13 | }
14 |
15 | // DERO signature
16 | type Signature struct {
17 | CheckC string `json:"checkC"` // C signature value
18 | CheckS string `json:"checkS"` // S signature value
19 | }
20 |
21 | // Standard SC header keys
22 | type Header string
23 |
24 | const (
25 | HEADER_NAME Header = `"nameHdr"`
26 | HEADER_DESCRIPTION Header = `"descrHdr"`
27 | HEADER_ICON_URL Header = `"iconURLHdr"`
28 | HEADER_CHECK_C Header = `"fileCheckC"`
29 | HEADER_CHECK_S Header = `"fileCheckS"`
30 | HEADER_MODS Header = `"mods"`
31 | HEADER_DURL Header = `"dURL"`
32 | HEADER_DOCUMENT Header = `"DOC` // append with Number()
33 | HEADER_SUBDIR Header = `"subDir"`
34 | HEADER_DOCTYPE Header = `"docType"`
35 | HEADER_COLLECTION Header = `"collection"`
36 | HEADER_TYPE Header = `"typeHdr"`
37 | HEADER_TAGS Header = `"tagsHdr"`
38 | HEADER_FILE_URL Header = `"fileURL"`
39 | HEADER_SIGN_URL Header = `"fileSignURL"`
40 | HEADER_COVER_URL Header = `"coverURL"`
41 | HEADER_ART_FEE Header = `"artificerFee"`
42 | HEADER_ROYALTY Header = `"royalty"`
43 | HEADER_OWNER Header = `"owner"`
44 | HEADER_OWNER_UPDATE Header = `"ownerCanUpdate"`
45 |
46 | HEADER_NAME_V2 Header = `"var_header_name"`
47 | HEADER_DESCRIPTION_V2 Header = `"var_header_description"`
48 | HEADER_ICON_URL_V2 Header = `"var_header_icon"`
49 |
50 | LINE_MODS_STORE = `34 STORE("mods", `
51 | LINE_DOC_VERSION = `50 STORE("docVersion", `
52 | LINE_INDEX_VERSION = `50 STORE("telaVersion", `
53 | )
54 |
55 | // Trim any `"` from Header and return string
56 | func (h Header) Trim() string {
57 | return strings.Trim(string(h), `"`)
58 | }
59 |
60 | // Returns if Header can be appended. Headers ending in `"` or with len < 2 will return false
61 | func (h Header) CanAppend() bool {
62 | i := len(h) - 1
63 | if i < 1 {
64 | return false
65 | }
66 |
67 | return h[i] != byte(0x22)
68 | }
69 |
70 | // Append a number to Header if applicable, otherwise return unchanged Header
71 | func (h Header) Number(i int) Header {
72 | if !h.CanAppend() {
73 | return h
74 | }
75 |
76 | return Header(fmt.Sprintf(`%s%d"`, h, i))
77 | }
78 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/civilware/tela
2 |
3 | go 1.21
4 |
5 | toolchain go1.21.5
6 |
7 | require (
8 | github.com/chzyer/readline v1.5.1
9 | github.com/civilware/Gnomon v0.0.0-20240403103529-8b2fdb2b3106
10 | github.com/creachadair/jrpc2 v0.35.4
11 | github.com/deroproject/derohe v0.0.0-20240405032004-bd300c0e086e
12 | github.com/deroproject/graviton v0.0.0-20220130070622-2c248a53b2e1
13 | github.com/gorilla/websocket v1.5.0
14 | github.com/stretchr/testify v1.8.4
15 | go.etcd.io/bbolt v1.3.7
16 | )
17 |
18 | replace github.com/deroproject/derohe => github.com/civilware/derohe v0.0.0-20240909003240-fa76d6016cc6
19 |
20 | require (
21 | github.com/VictoriaMetrics/metrics v1.23.1 // indirect
22 | github.com/beevik/ntp v1.2.0 // indirect
23 | github.com/blang/semver/v4 v4.0.0 // indirect
24 | github.com/caarlos0/env/v6 v6.10.1 // indirect
25 | github.com/cenkalti/hub v1.0.1 // indirect
26 | github.com/cenkalti/rpc2 v0.0.0-20210604223624-c1acbc6ec984 // indirect
27 | github.com/cespare/xxhash v1.1.0 // indirect
28 | github.com/coder/websocket v1.8.12 // indirect
29 | github.com/davecgh/go-spew v1.1.1 // indirect
30 | github.com/dchest/siphash v1.2.3 // indirect
31 | github.com/dustin/go-humanize v1.0.1 // indirect
32 | github.com/fsnotify/fsnotify v1.7.0 // indirect
33 | github.com/fxamacker/cbor/v2 v2.4.0 // indirect
34 | github.com/go-logr/logr v1.2.3 // indirect
35 | github.com/go-logr/zapr v1.2.3 // indirect
36 | github.com/hashicorp/golang-lru v0.5.1 // indirect
37 | github.com/klauspost/cpuid/v2 v2.2.6 // indirect
38 | github.com/klauspost/reedsolomon v1.11.5 // indirect
39 | github.com/lesismal/llib v1.1.10 // indirect
40 | github.com/lesismal/nbio v1.3.9 // indirect
41 | github.com/mattn/go-colorable v0.1.13 // indirect
42 | github.com/mattn/go-isatty v0.0.16 // indirect
43 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
44 | github.com/minio/sha256-simd v1.0.0 // indirect
45 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
46 | github.com/nxadm/tail v1.4.8 // indirect
47 | github.com/pmezard/go-difflib v1.0.0 // indirect
48 | github.com/robfig/cron/v3 v3.0.1 // indirect
49 | github.com/satori/go.uuid v1.2.0 // indirect
50 | github.com/segmentio/fasthash v1.0.3 // indirect
51 | github.com/sirupsen/logrus v1.9.3 // indirect
52 | github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
53 | github.com/x448/float16 v0.8.4 // indirect
54 | github.com/xtaci/kcp-go/v5 v5.6.2 // indirect
55 | go.uber.org/atomic v1.10.0 // indirect
56 | go.uber.org/multierr v1.9.0 // indirect
57 | go.uber.org/zap v1.24.0 // indirect
58 | golang.org/x/crypto v0.17.0 // indirect
59 | golang.org/x/net v0.19.0 // indirect
60 | golang.org/x/sync v0.5.0 // indirect
61 | golang.org/x/sys v0.15.0 // indirect
62 | golang.org/x/term v0.15.0 // indirect
63 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
64 | golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
65 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
66 | gopkg.in/yaml.v3 v3.0.1 // indirect
67 | )
68 |
--------------------------------------------------------------------------------
/compression.go:
--------------------------------------------------------------------------------
1 | package tela
2 |
3 | import (
4 | "bytes"
5 | "encoding/base64"
6 | "fmt"
7 | "io"
8 | "strings"
9 |
10 | "compress/gzip"
11 | )
12 |
13 | const (
14 | COMPRESSION_GZIP = ".gz"
15 | )
16 |
17 | // Compression formats this package will use
18 | var compressionFormats = []string{
19 | COMPRESSION_GZIP,
20 | }
21 |
22 | // TrimCompressedExt removes known compression extensions from the filename
23 | func TrimCompressedExt(fileName string) string {
24 | if fileName == "" {
25 | return fileName
26 | }
27 |
28 | for _, comp := range compressionFormats {
29 | fileName = strings.TrimSuffix(fileName, comp)
30 | }
31 |
32 | return fileName
33 | }
34 |
35 | // IsCompressedExt returns true if ext is a valid TELA compression format
36 | func IsCompressedExt(ext string) bool {
37 | if ext == "" {
38 | return false
39 | }
40 |
41 | for _, comp := range compressionFormats {
42 | if ext == comp {
43 | return true
44 | }
45 | }
46 |
47 | return false
48 | }
49 |
50 | // Decompress TELA data using the given compression format, if compression is "" result will return the original data
51 | func Decompress(data []byte, compression string) (result []byte, err error) {
52 | switch compression {
53 | case COMPRESSION_GZIP:
54 | result, err = decompressGzip(data)
55 | if err != nil {
56 | return
57 | }
58 | case "":
59 | result = data
60 | default:
61 | err = fmt.Errorf("unknown decompression format %q", compression)
62 | }
63 |
64 | return
65 | }
66 |
67 | // Compress TELA data using the given compression format
68 | func Compress(data []byte, compression string) (result string, err error) {
69 | switch compression {
70 | case COMPRESSION_GZIP:
71 | result, err = compressGzip(data)
72 | if err != nil {
73 | return
74 | }
75 | default:
76 | err = fmt.Errorf("unknown compression format %q", compression)
77 | }
78 |
79 | return
80 | }
81 |
82 | // Compress data as gzip then encode it in base64 and return the result
83 | func compressGzip(data []byte) (result string, err error) {
84 | var buf bytes.Buffer
85 | var gz *gzip.Writer
86 | gz, err = gzip.NewWriterLevel(&buf, gzip.BestCompression)
87 | if err != nil {
88 | return
89 | }
90 | defer gz.Close()
91 |
92 | _, err = gz.Write(data)
93 | if err != nil {
94 | return
95 | }
96 |
97 | // Ensure all data is written to the buffer
98 | err = gz.Close()
99 | if err != nil {
100 | return
101 | }
102 |
103 | result = base64.StdEncoding.EncodeToString(buf.Bytes())
104 |
105 | return
106 | }
107 |
108 | // Decompress base64 encoded gzip data and return the result
109 | func decompressGzip(data []byte) (result []byte, err error) {
110 | var decoded []byte
111 | decoded, err = base64.StdEncoding.DecodeString(string(data))
112 | if err != nil {
113 | return
114 | }
115 |
116 | var gz *gzip.Reader
117 | gz, err = gzip.NewReader(bytes.NewReader(decoded))
118 | if err != nil {
119 | return
120 | }
121 | defer gz.Close()
122 |
123 | var decompressed []byte
124 | decompressed, err = io.ReadAll(gz)
125 | if err != nil {
126 | return
127 | }
128 |
129 | result = decompressed
130 |
131 | return
132 | }
133 |
--------------------------------------------------------------------------------
/logger/logger.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 | "time"
8 |
9 | "github.com/deroproject/derohe/globals"
10 | )
11 |
12 | const (
13 | timestampFormat = "01/02/2006 15:04:05"
14 |
15 | DEBUG = "DEBUG"
16 | INFO = "INFO"
17 | WARN = "WARN"
18 | ERROR = "ERROR"
19 | FATAL = "FATAL"
20 | )
21 |
22 | type level struct {
23 | debug string
24 | info string
25 | warn string
26 | err string
27 | fatal string
28 | }
29 |
30 | var tag = level{
31 | debug: Color.blue + DEBUG + Color.end,
32 | info: Color.green + INFO + Color.end,
33 | warn: Color.yellow + WARN + Color.end,
34 | err: Color.red + ERROR + Color.end,
35 | fatal: Color.red + FATAL + Color.end,
36 | }
37 |
38 | // Enable or disable colors
39 | func EnableColors(b bool) {
40 | if b {
41 | Color.black = ANSIblack
42 | Color.red = ANSIred
43 | Color.green = ANSIgreen
44 | Color.yellow = ANSIyellow
45 | Color.blue = ANSIblue
46 | Color.magenta = ANSImagenta
47 | Color.cyan = ANSIcyan
48 | Color.white = ANSIwhite
49 | Color.fallback = ANSIdefault
50 | Color.grey = ANSIgrey
51 | Color.end = ANSIend
52 |
53 | tag.debug = Color.blue + DEBUG + Color.end
54 | tag.info = Color.green + INFO + Color.end
55 | tag.warn = Color.yellow + WARN + Color.end
56 | tag.err = Color.red + ERROR + Color.end
57 | tag.fatal = Color.red + FATAL + Color.end
58 | } else {
59 | Color.black = ""
60 | Color.red = ""
61 | Color.green = ""
62 | Color.yellow = ""
63 | Color.blue = ""
64 | Color.magenta = ""
65 | Color.cyan = ""
66 | Color.white = ""
67 | Color.fallback = ""
68 | Color.grey = ""
69 | Color.end = ""
70 |
71 | tag.debug = DEBUG
72 | tag.info = INFO
73 | tag.warn = WARN
74 | tag.err = ERROR
75 | tag.fatal = FATAL
76 | }
77 | }
78 |
79 | // Timestamp returns the current local timestamp as a formatted string
80 | func Timestamp() string {
81 | return fmt.Sprintf("%s[%s]%s", Color.grey, time.Now().Format(timestampFormat), Color.end)
82 | }
83 |
84 | // sprint formats a log string with [source] if provided otherwise returns given string
85 | func sprint(s string) string {
86 | start := strings.Index(s, "[")
87 | end := strings.Index(s, "]")
88 | if start != -1 && end != -1 {
89 | if source := s[start+1:end] + ":"; source != ":" {
90 | s = fmt.Sprintf("%s%s%s %s", Color.cyan, source, Color.end, s[end+2:])
91 | }
92 | }
93 |
94 | return s
95 | }
96 |
97 | // printf formats log text with a message tag and prints to console
98 | func printf(tag, text string) {
99 | fmt.Printf("%s %s %s", Timestamp(), tag, sprint(text))
100 | }
101 |
102 | // Debugf prints a format specified string with DEBUG message tag if globals.Arguments["--debug"]
103 | func Debugf(format string, a ...any) {
104 | if globals.Arguments["--debug"] != nil && globals.Arguments["--debug"].(bool) {
105 | text := fmt.Sprintf(format, a...)
106 | printf(tag.debug, text)
107 | }
108 | }
109 |
110 | // Printf prints a format specified string with INFO message tag
111 | func Printf(format string, a ...any) {
112 | text := fmt.Sprintf(format, a...)
113 | printf(tag.info, text)
114 | }
115 |
116 | // Warnf prints a format specified string with WARN message tag
117 | func Warnf(format string, a ...any) {
118 | text := fmt.Sprintf(format, a...)
119 | printf(tag.warn, text)
120 | }
121 |
122 | // Errorf prints a format specified string with ERROR message tag
123 | func Errorf(format string, a ...any) {
124 | text := fmt.Sprintf(format, a...)
125 | printf(tag.err, text)
126 | }
127 |
128 | // Fatalf prints a format specified string with FATAL message tag followed by a call to [os.Exit](1)
129 | func Fatalf(format string, a ...any) {
130 | text := fmt.Sprintf(format, a...)
131 | printf(tag.fatal, text)
132 | os.Exit(1)
133 | }
134 |
--------------------------------------------------------------------------------
/logger/ascii_test.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestASCIIBlend(t *testing.T) {
11 | var margin []string
12 | margin = append(margin, "------------------------------")
13 | margin = append(margin, "------------------------")
14 |
15 | defaultSpace := " "
16 |
17 | var info [][]string
18 | info = append(info, []string{margin[0]})
19 | info = append(info, []string{margin[1]})
20 | info = append(info, []string{" "})
21 | info = append(info, []string{""})
22 |
23 | expectedOutput1 := margin[0] + ` ` + ASCIISmall[0] + "\n" +
24 | `Some info 0 ` + ASCIISmall[1] + "\n" +
25 | `Some info 1 ` + ASCIISmall[2] + "\n" +
26 | `Some info 2 ` + ASCIISmall[3] + "\n" +
27 | defaultSpace + ASCIISmall[4] + "\n" +
28 | defaultSpace + ASCIISmall[5] + "\n" +
29 | defaultSpace + ASCIISmall[6] + "\n" +
30 | defaultSpace + ASCIISmall[7] + "\n" +
31 | defaultSpace + ASCIISmall[8] + "\n" +
32 | defaultSpace + ASCIISmall[9] + "\n" +
33 | defaultSpace + ASCIISmall[10] + "\n" +
34 | defaultSpace + ASCIISmall[11] + "\n"
35 |
36 | // Match info line for expectedOutput1
37 | for i := 0; i < 3; i++ {
38 | info[0] = append(info[0], fmt.Sprintf("Some info %d", i))
39 | }
40 |
41 | expectedOutput2 := margin[1] + ` ` + ASCIISmall[0] + "\n" +
42 | `Some info 0 ` + ASCIISmall[1] + "\n" +
43 | `Some info 1 ` + ASCIISmall[2] + "\n" +
44 | `Some info 2 ` + ASCIISmall[3] + "\n" +
45 | `Some info 3 ` + ASCIISmall[4] + "\n" +
46 | `Some info 4 ` + ASCIISmall[5] + "\n" +
47 | `Some info 5 ` + ASCIISmall[6] + "\n" +
48 | `Some info 6 ` + ASCIISmall[7] + "\n" +
49 | `Some info 7 ` + ASCIISmall[8] + "\n" +
50 | `Some info 8 ` + ASCIISmall[9] + "\n" +
51 | `Some info 9 ` + ASCIISmall[10] + "\n" +
52 | `Some info 10 ` + ASCIISmall[11] + "\n" +
53 | `Some info 11` + "\n" + `Some info 12` + "\n" + `Some info 13` + "\n" + `Some info 14` + "\n"
54 |
55 | // Match info line for expectedOutput2
56 | for i := 0; i < 15; i++ {
57 | info[1] = append(info[1], fmt.Sprintf("Some info %d", i))
58 | }
59 |
60 | var expectedOutput3 string
61 | for _, line := range ASCIISmall {
62 | expectedOutput3 = expectedOutput3 + defaultSpace + line + "\n"
63 | }
64 |
65 | var expectedOutput4 string
66 | for _, line := range ASCIISmall {
67 | expectedOutput4 = expectedOutput4 + defaultSpace + line + "\n"
68 | }
69 |
70 | var expectedOutputs []string
71 | expectedOutputs = append(expectedOutputs, expectedOutput1)
72 | expectedOutputs = append(expectedOutputs, expectedOutput2)
73 | expectedOutputs = append(expectedOutputs, expectedOutput3)
74 | expectedOutputs = append(expectedOutputs, expectedOutput4)
75 |
76 | for i, eo := range expectedOutputs {
77 | output := captureOutput(func() {
78 | ASCIIBlend(ASCIISmall, info[i])
79 | })
80 |
81 | assert.Equal(t, eo, output, "Blend output %d should be the same", i)
82 |
83 | // Test with nil info
84 | output = captureOutput(func() {
85 | ASCIIBlend(ASCIISmall, nil)
86 | })
87 |
88 | assert.Empty(t, output, "Blending nil %d should be empty", i)
89 | }
90 | }
91 |
92 | func TestASCIIPrint(t *testing.T) {
93 | var expectedOutputMain string
94 | for _, line := range ASCIIMain {
95 | expectedOutputMain = expectedOutputMain + line + "\n"
96 | }
97 |
98 | var expectedOutputSmall string
99 | for _, line := range ASCIISmall {
100 | expectedOutputSmall = expectedOutputSmall + line + "\n"
101 | }
102 |
103 | output := captureOutput(func() {
104 | ASCIIPrint(false)
105 | })
106 |
107 | assert.Equal(t, expectedOutputMain, output, "Main output should be the same")
108 |
109 | output = captureOutput(func() {
110 | ASCIIPrint(true)
111 | })
112 |
113 | assert.Equal(t, expectedOutputSmall, output, "Small output should be the same")
114 | }
115 |
--------------------------------------------------------------------------------
/ratings.go:
--------------------------------------------------------------------------------
1 | package tela
2 |
3 | import "fmt"
4 |
5 | // Common detail tags
6 | const (
7 | detail_Nothing = "Nothing"
8 | detail_Needs_Review = "Needs review"
9 | detail_Needs_Improvement = "Needs improvement"
10 | detail_Bugs = "Bugs"
11 | detail_Errors = "Errors"
12 | )
13 |
14 | // A TELA SC rating
15 | type Rating struct {
16 | Address string `json:"address"` // Address of the rater
17 | Rating uint64 `json:"rating"` // The 0-99 rating number
18 | Height uint64 `json:"height"` // The block height this rating occurred
19 | }
20 |
21 | // TELA SC rating structure
22 | type Rating_Result struct {
23 | Ratings []Rating `json:"ratings,omitempty"` // All ratings for a SC
24 | Likes uint64 `json:"likes"` // Likes for a SC
25 | Dislikes uint64 `json:"dislikes"` // Dislikes for a SC
26 | Average float64 `json:"average"` // Average category value of all ratings, will be 0-10
27 | }
28 |
29 | // TELA ratings variable structure
30 | type ratings struct {
31 | categories map[uint64]string
32 | negativeDetails map[uint64]string
33 | positiveDetails map[uint64]string
34 | }
35 |
36 | // Access TELA ratings variables and functions
37 | var Ratings ratings
38 |
39 | // Initialize the rating values
40 | func initRatings() {
41 | Ratings.categories = map[uint64]string{
42 | 0: "Do not use",
43 | 1: "Broken",
44 | 2: "Major issues",
45 | 3: "Minor issues",
46 | 4: "Should be improved",
47 | 5: "Could be improved",
48 | 6: "Average",
49 | 7: "Good",
50 | 8: "Very good",
51 | 9: "Exceptional",
52 | }
53 |
54 | Ratings.negativeDetails = map[uint64]string{
55 | 0: detail_Nothing,
56 | 1: detail_Needs_Review,
57 | 2: detail_Needs_Improvement,
58 | 3: detail_Bugs,
59 | 4: detail_Errors,
60 | 5: "Inappropriate",
61 | 6: "Incomplete",
62 | 7: "Corrupted",
63 | 8: "Plagiarized",
64 | 9: "Malicious",
65 | }
66 |
67 | Ratings.positiveDetails = map[uint64]string{
68 | 0: detail_Nothing,
69 | 1: detail_Needs_Review,
70 | 2: detail_Needs_Improvement,
71 | 3: detail_Bugs,
72 | 4: detail_Errors,
73 | 5: "Visually appealing",
74 | 6: "In depth",
75 | 7: "Works well",
76 | 8: "Unique",
77 | 9: "Benevolent",
78 | }
79 | }
80 |
81 | // Returns all TELA rating categories
82 | func (rate *ratings) Categories() (categories map[uint64]string) {
83 | categories = map[uint64]string{}
84 | for u, c := range rate.categories {
85 | categories[u] = c
86 | }
87 |
88 | return
89 | }
90 |
91 | // Returns a TELA rating category if exists, otherwise empty string
92 | func (rate *ratings) Category(r uint64) (category string) {
93 | category = rate.categories[r]
94 |
95 | return
96 | }
97 |
98 | // Returns all negative TELA rating detail tags
99 | func (rate *ratings) NegativeDetails() (details map[uint64]string) {
100 | details = map[uint64]string{}
101 | for u, c := range rate.negativeDetails {
102 | details[u] = c
103 | }
104 |
105 | return
106 | }
107 |
108 | // Returns all positive TELA rating detail tags
109 | func (rate *ratings) PositiveDetails() (details map[uint64]string) {
110 | details = map[uint64]string{}
111 | for u, c := range rate.positiveDetails {
112 | details[u] = c
113 | }
114 |
115 | return
116 | }
117 |
118 | // Returns a TELA detail tag if exists, otherwise empty string
119 | func (rate *ratings) Detail(r uint64, isPositive bool) (detail string) {
120 | if isPositive {
121 | detail = rate.positiveDetails[r]
122 | } else {
123 | detail = rate.negativeDetails[r]
124 | }
125 |
126 | return
127 | }
128 |
129 | // Parse r for its corresponding TELA rating category and detail
130 | func (rate *ratings) Parse(r uint64) (category string, detail string, err error) {
131 | fP := r / 10
132 | sP := r % 10
133 |
134 | var ok bool
135 | if category, ok = rate.categories[fP]; !ok {
136 | err = fmt.Errorf("unknown category")
137 | return
138 | }
139 |
140 | isPositive := fP >= 5
141 | detail = rate.Detail(sP, isPositive)
142 | if detail == "" {
143 | err = fmt.Errorf("unknown detail")
144 | }
145 |
146 | return
147 | }
148 |
149 | // Parse r for its corresponding TELA rating string
150 | func (rate *ratings) ParseString(r uint64) (rating string, err error) {
151 | category, detail, err := rate.Parse(r)
152 | if err != nil {
153 | return
154 | }
155 |
156 | if detail == detail_Nothing {
157 | rating = category
158 | } else {
159 | rating = fmt.Sprintf("%s (%s)", category, detail)
160 | }
161 |
162 | return
163 | }
164 |
165 | // Parse the average rating result for its category string
166 | func (res *Rating_Result) ParseAverage() (category string) {
167 | category, _, _ = Ratings.Parse(uint64(res.Average))
168 | return
169 | }
170 |
--------------------------------------------------------------------------------
/logger/logger_test.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "os"
7 | "os/exec"
8 | "testing"
9 |
10 | "github.com/deroproject/derohe/globals"
11 | "github.com/stretchr/testify/assert"
12 | )
13 |
14 | // Capture the console output
15 | func captureOutput(f func()) string {
16 | var buf bytes.Buffer
17 | stdout := os.Stdout
18 | r, w, _ := os.Pipe()
19 | os.Stdout = w
20 |
21 | f()
22 |
23 | w.Close()
24 | os.Stdout = stdout
25 | buf.ReadFrom(r)
26 | return buf.String()
27 | }
28 |
29 | func TestEnableColors(t *testing.T) {
30 | EnableColors(false)
31 | assert.Empty(t, Color.Red(), "Color red should be empty when colors are disabled")
32 | assert.Empty(t, Color.End(), "Color end should be empty when colors are disabled")
33 | assert.Equal(t, INFO, tag.info, "INFO tag should match when colors are disabled")
34 | assert.Equal(t, ERROR, tag.err, "ERROR tag should match when colors are disabled")
35 |
36 | EnableColors(true)
37 | expectedInfo := ANSIgreen + INFO + ANSIend
38 | expectedError := ANSIred + ERROR + ANSIend
39 | assert.Equal(t, ANSIblack, Color.Black(), "Color black should match ANSI when colors are enabled")
40 | assert.Equal(t, ANSIred, Color.Red(), "Color red should match ANSI when colors are enabled")
41 | assert.Equal(t, ANSIgreen, Color.Green(), "Color green should match ANSI when colors are enabled")
42 | assert.Equal(t, ANSIyellow, Color.Yellow(), "Color yellow should match ANSI when colors are enabled")
43 | assert.Equal(t, ANSIblue, Color.Blue(), "Color blue should match ANSI when colors are enabled")
44 | assert.Equal(t, ANSImagenta, Color.Magenta(), "Color magenta should match ANSI when colors are enabled")
45 | assert.Equal(t, ANSIcyan, Color.Cyan(), "Color cyan should match ANSI when colors are enabled")
46 | assert.Equal(t, ANSIwhite, Color.White(), "Color white should match ANSI when colors are enabled")
47 | assert.Equal(t, ANSIdefault, Color.Default(), "Color default should match ANSI when colors are enabled")
48 | assert.Equal(t, ANSIgrey, Color.Grey(), "Color grey should match ANSI when colors are enabled")
49 | assert.Equal(t, ANSIend, Color.End(), "Color end should match ANSI when colors are enabled")
50 | assert.Equal(t, expectedInfo, tag.info, "INFO tag should match when colors are enabled")
51 | assert.Equal(t, expectedError, tag.err, "ERROR tag should match when colors are enabled")
52 | }
53 |
54 | func TestTimestamp(t *testing.T) {
55 | stamp := Timestamp()
56 | assert.Contains(t, stamp, "[", "Timestamp missing left format")
57 | assert.Contains(t, stamp, "]", "Timestamp missing right format")
58 | }
59 |
60 | func TestSprint(t *testing.T) {
61 | result := sprint("[source] message")
62 | expected := fmt.Sprintf("%s%s:%s message", Color.Cyan(), "source", Color.End())
63 | assert.Equal(t, expected, result, "Expected sprint to parse source tag")
64 |
65 | message := "message"
66 | result = sprint(message)
67 | assert.Equal(t, message, result, "Expected message without source to remain the same")
68 | }
69 |
70 | func TestDebugf(t *testing.T) {
71 | format := "Hello %s %d %t"
72 | a := []any{"TELA", 1, true}
73 |
74 | globals.Arguments["--debug"] = true
75 | output := captureOutput(func() {
76 | Debugf(format, a...)
77 | })
78 |
79 | assert.Contains(t, output, DEBUG, "Expected output to contain DEBUG tag")
80 | assert.Contains(t, output, fmt.Sprintf(format, a...), "Expecting output to match format")
81 |
82 | globals.Arguments["--debug"] = false
83 | output = captureOutput(func() {
84 | Debugf(format, a...)
85 | })
86 |
87 | assert.Empty(t, output, "Expected empty output when --debug is not enabled")
88 | }
89 |
90 | func TestPrintf(t *testing.T) {
91 | format := "Hello %s %d %t"
92 | a := []any{"TELA", 1, true}
93 |
94 | output := captureOutput(func() {
95 | Printf(format, a...)
96 | })
97 |
98 | assert.Contains(t, output, INFO, "Expected output to contain INFO tag")
99 | assert.Contains(t, output, fmt.Sprintf(format, a...), "Expecting output to match format")
100 | }
101 |
102 | func TestWarnf(t *testing.T) {
103 | format := "Warning %s %d %t"
104 | a := []any{"issued", 1, true}
105 |
106 | output := captureOutput(func() {
107 | Warnf(format, a...)
108 | })
109 |
110 | assert.Contains(t, output, WARN, "Expected output to contain WARN tag")
111 | assert.Contains(t, output, fmt.Sprintf(format, a...), "Expecting output to match format")
112 | }
113 |
114 | func TestErrorf(t *testing.T) {
115 | format := "Error %s %d %t"
116 | a := []any{"occurred", 1, true}
117 |
118 | output := captureOutput(func() {
119 | Errorf(format, a...)
120 | })
121 |
122 | assert.Contains(t, output, ERROR, "Expected output to contain ERROR tag")
123 | assert.Contains(t, output, fmt.Sprintf(format, a...), "Expecting output to match format")
124 | }
125 |
126 | func TestFatalf(t *testing.T) {
127 | if os.Getenv("FLAG") == "1" {
128 | Fatalf("Fatalf test")
129 | }
130 |
131 | // Run the test in a subprocess
132 | cmd := exec.Command(os.Args[0], "-test.run=TestFatalf")
133 | cmd.Env = append(os.Environ(), "FLAG=1")
134 | err := cmd.Run()
135 |
136 | _, ok := err.(*exec.ExitError)
137 | expectedErrorString := "exit status 1"
138 | assert.True(t, ok, "Error should be %T", new(exec.ExitError))
139 | assert.EqualError(t, err, expectedErrorString, "Error string should be equal")
140 | }
141 |
--------------------------------------------------------------------------------
/shards/boltdb.go:
--------------------------------------------------------------------------------
1 | package shards
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "os"
7 | "path/filepath"
8 |
9 | "github.com/deroproject/derohe/walletapi"
10 | "go.etcd.io/bbolt"
11 | )
12 |
13 | // Get shard and make directory for DB
14 | func boltGetShard(disk *walletapi.Wallet_Disk) (shard string, err error) {
15 | shard = GetShard(disk)
16 | err = os.MkdirAll(shard, os.ModePerm)
17 | return
18 | }
19 |
20 | // Encrypt a key-value and then store it in a bbolt DB
21 | func boltStoreEncryptedValue(disk *walletapi.Wallet_Disk, bucket string, key []byte, value []byte) (err error) {
22 | var shard string
23 | shard, err = boltGetShard(disk)
24 | if err != nil {
25 | return
26 | }
27 |
28 | var db *bbolt.DB
29 | db, err = bbolt.Open(filepath.Join(shard, filepath.Base(shard)+".db"), 0600, nil)
30 | if err != nil {
31 | return
32 | }
33 |
34 | err = db.Update(func(tx *bbolt.Tx) (err error) {
35 | b, err := tx.CreateBucketIfNotExists([]byte(bucket))
36 | if err != nil {
37 | return
38 | }
39 |
40 | eValue, err := disk.Encrypt(value)
41 | if err != nil {
42 | return
43 | }
44 |
45 | return b.Put(key, eValue)
46 | })
47 |
48 | db.Close()
49 |
50 | return
51 | }
52 |
53 | // Store a key-value in a bbolt DB
54 | func boltStoreValue(bucket string, key []byte, value []byte) (err error) {
55 | var shard string
56 | shard, err = boltGetShard(nil)
57 | if err != nil {
58 | return
59 | }
60 |
61 | var db *bbolt.DB
62 | db, err = bbolt.Open(filepath.Join(shard, "settings.db"), 0600, nil)
63 | if err != nil {
64 | return
65 | }
66 |
67 | err = db.Update(func(tx *bbolt.Tx) (err error) {
68 | b, err := tx.CreateBucketIfNotExists([]byte(bucket))
69 | if err != nil {
70 | return
71 | }
72 |
73 | mar, err := json.Marshal(&value)
74 | if err != nil {
75 | return
76 | }
77 |
78 | return b.Put(key, mar)
79 | })
80 |
81 | db.Close()
82 |
83 | return
84 | }
85 |
86 | // Get a key-value from a bbolt DB
87 | func boltGetValue(bucket string, key []byte) (result []byte, err error) {
88 | var shard string
89 | shard, err = boltGetShard(nil)
90 | if err != nil {
91 | return
92 | }
93 |
94 | var db *bbolt.DB
95 | db, err = bbolt.Open(filepath.Join(shard, "settings.db"), 0600, nil)
96 | if err != nil {
97 | return
98 | }
99 |
100 | err = db.View(func(tx *bbolt.Tx) (err error) {
101 | b := tx.Bucket([]byte(bucket))
102 | if b == nil {
103 | return fmt.Errorf("bucket %q not found", bucket)
104 | }
105 |
106 | stored := b.Get(key)
107 | if stored != nil {
108 | return json.Unmarshal(stored, &result)
109 | }
110 |
111 | return fmt.Errorf("key %q not found", key)
112 | })
113 |
114 | db.Close()
115 |
116 | return
117 | }
118 |
119 | // Store a key-value in bbolt DB settings bucket
120 | func boltStoreSettingsValue(key, value []byte) (err error) {
121 | return boltStoreValue("settings", key, value)
122 | }
123 |
124 | // Get a key-value from bbolt DB settings bucket
125 | func boltGetSettingsValue(key []byte) (result []byte, err error) {
126 | return boltGetValue("settings", key)
127 | }
128 |
129 | // Store endpoint value in bbolt DB settings bucket
130 | func boltStoreEndpoint(value []byte) (err error) {
131 | return boltStoreSettingsValue(Key.Endpoint(), value)
132 | }
133 |
134 | // Get endpoint value from bbolt DB settings bucket
135 | func boltGetEndpoint() (result []byte, err error) {
136 | return boltGetSettingsValue(Key.Endpoint())
137 | }
138 |
139 | // Store network value in bbolt DB settings bucket
140 | func boltStoreNetwork(value []byte) (err error) {
141 | return boltStoreSettingsValue(Key.Network(), value)
142 | }
143 |
144 | // Get network value from bbolt DB settings bucket
145 | func boltGetNetwork() (result []byte, err error) {
146 | return boltGetSettingsValue(Key.Network())
147 | }
148 |
149 | // Get an encrypted key-value from a bbolt DB and then decrypt it
150 | func boltGetEncryptedValue(disk *walletapi.Wallet_Disk, bucket string, key []byte) (result []byte, err error) {
151 | var shard string
152 | shard, err = boltGetShard(disk)
153 | if err != nil {
154 | return
155 | }
156 |
157 | var db *bbolt.DB
158 | db, err = bbolt.Open(filepath.Join(shard, filepath.Base(shard)+".db"), 0600, nil)
159 | if err != nil {
160 | return
161 | }
162 |
163 | err = db.View(func(tx *bbolt.Tx) (err error) {
164 | b := tx.Bucket([]byte(bucket))
165 | if b == nil {
166 | return fmt.Errorf("bucket %q not found", bucket)
167 | }
168 |
169 | stored := b.Get(key)
170 | if stored == nil {
171 | return fmt.Errorf("key %q not found", key)
172 | }
173 |
174 | eValue, err := disk.Decrypt(stored)
175 | if err != nil {
176 | return
177 | }
178 |
179 | result = eValue
180 |
181 | return
182 | })
183 |
184 | db.Close()
185 |
186 | return
187 | }
188 |
189 | // Delete a key-value in a bbolt DB
190 | func boltDeleteKey(disk *walletapi.Wallet_Disk, bucket string, key []byte) (err error) {
191 | shard := GetShard(disk)
192 |
193 | var db *bbolt.DB
194 | db, err = bbolt.Open(filepath.Join(shard, filepath.Base(shard)+".db"), 0600, nil)
195 | if err != nil {
196 | return
197 | }
198 |
199 | err = db.Update(func(tx *bbolt.Tx) (err error) {
200 | b := tx.Bucket([]byte(bucket))
201 | if b == nil {
202 | return fmt.Errorf("bucket %q not found", bucket)
203 | }
204 |
205 | return b.Delete(key)
206 | })
207 |
208 | db.Close()
209 |
210 | return
211 | }
212 |
--------------------------------------------------------------------------------
/shards/gravdb.go:
--------------------------------------------------------------------------------
1 | package shards
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/deroproject/derohe/walletapi"
7 | "github.com/deroproject/graviton"
8 | )
9 |
10 | // Encrypt a key-value and then store it in a Graviton tree
11 | func gravitonStoreEncryptedValue(disk *walletapi.Wallet_Disk, t string, key, value []byte) (err error) {
12 | if disk == nil {
13 | err = fmt.Errorf("no active account found")
14 | return
15 | }
16 |
17 | if t == "" {
18 | err = fmt.Errorf("missing graviton tree input")
19 | return
20 | }
21 |
22 | if key == nil {
23 | err = fmt.Errorf("missing graviton key input")
24 | return
25 | }
26 |
27 | if value == nil {
28 | err = fmt.Errorf("missing graviton value input")
29 | return
30 | }
31 |
32 | eValue, err := disk.Encrypt(value)
33 | if err != nil {
34 | return
35 | }
36 |
37 | store, err := graviton.NewDiskStore(GetShard(disk))
38 | if err != nil {
39 | return
40 | }
41 |
42 | ss, err := store.LoadSnapshot(0)
43 | if err != nil {
44 | return
45 | }
46 |
47 | tree, err := ss.GetTree(t)
48 | if err != nil {
49 | return
50 | }
51 |
52 | err = tree.Put(key, eValue)
53 | if err != nil {
54 | return
55 | }
56 |
57 | _, err = graviton.Commit(tree)
58 | if err != nil {
59 | return
60 | }
61 |
62 | return
63 | }
64 |
65 | // Store a key-value in a Graviton tree
66 | func gravitonStoreValue(t string, key, value []byte) (err error) {
67 | if t == "" {
68 | err = fmt.Errorf("missing graviton tree input")
69 | return
70 | }
71 |
72 | if key == nil {
73 | err = fmt.Errorf("missing graviton key input")
74 | return
75 | }
76 |
77 | if value == nil {
78 | err = fmt.Errorf("missing graviton value input")
79 | return
80 | }
81 |
82 | store, err := graviton.NewDiskStore(GetShard(nil))
83 | if err != nil {
84 | return
85 | }
86 |
87 | ss, err := store.LoadSnapshot(0)
88 | if err != nil {
89 | return
90 | }
91 |
92 | tree, err := ss.GetTree(t)
93 | if err != nil {
94 | return
95 | }
96 |
97 | err = tree.Put(key, value)
98 | if err != nil {
99 | return
100 | }
101 |
102 | _, err = graviton.Commit(tree)
103 | if err != nil {
104 | return
105 | }
106 |
107 | return
108 | }
109 |
110 | // Get a key-value from a Graviton tree
111 | func gravitonGetValue(t string, key []byte) (result []byte, err error) {
112 | result = []byte("")
113 | if t == "" {
114 | err = fmt.Errorf("missing graviton tree input")
115 | return
116 | }
117 |
118 | if key == nil {
119 | err = fmt.Errorf("missing graviton key input")
120 | return
121 | }
122 |
123 | store, err := graviton.NewDiskStore(GetShard(nil))
124 | if err != nil {
125 | return
126 | }
127 |
128 | ss, err := store.LoadSnapshot(0)
129 | if err != nil {
130 | return
131 | }
132 |
133 | tree, err := ss.GetTree(t)
134 | if err != nil {
135 | return
136 | }
137 |
138 | result, err = tree.Get(key)
139 | if err != nil {
140 | return
141 | }
142 |
143 | return
144 | }
145 |
146 | // Store a key-value in Graviton settings tree
147 | func gravitonStoreSettingsValue(key, value []byte) (err error) {
148 | return gravitonStoreValue("settings", key, value)
149 | }
150 |
151 | // Get a key-value from settings Graviton tree
152 | func gravitonGetSettingsValue(key []byte) (result []byte, err error) {
153 | return gravitonGetValue("settings", key)
154 | }
155 |
156 | // Store endpoint value in Graviton settings tree
157 | func gravitonStoreEndpoint(value []byte) (err error) {
158 | return gravitonStoreSettingsValue(Key.Endpoint(), value)
159 | }
160 |
161 | // Get endpoint value from settings Graviton tree
162 | func gravitonGetEndpoint() (result []byte, err error) {
163 | return gravitonGetSettingsValue(Key.Endpoint())
164 | }
165 |
166 | // Store network value in Graviton settings tree
167 | func gravitonStoreNetwork(value []byte) (err error) {
168 | return gravitonStoreSettingsValue(Key.Network(), value)
169 | }
170 |
171 | // Get network value from settings Graviton tree
172 | func gravitonGetNetwork() (result []byte, err error) {
173 | return gravitonGetSettingsValue(Key.Network())
174 | }
175 |
176 | // Get an encrypted key-value from a Graviton tree and then decrypt it
177 | func gravitonGetEncryptedValue(disk *walletapi.Wallet_Disk, t string, key []byte) (result []byte, err error) {
178 | result = []byte("")
179 | if disk == nil {
180 | err = fmt.Errorf("no active account found")
181 | return
182 | }
183 |
184 | if t == "" {
185 | err = fmt.Errorf("missing graviton tree input")
186 | return
187 | }
188 |
189 | if key == nil {
190 | err = fmt.Errorf("missing graviton key input")
191 | return
192 | }
193 |
194 | store, err := graviton.NewDiskStore(GetShard(disk))
195 | if err != nil {
196 | return
197 | }
198 |
199 | ss, err := store.LoadSnapshot(0)
200 | if err != nil {
201 | return
202 | }
203 |
204 | tree, err := ss.GetTree(t)
205 | if err != nil {
206 | return
207 | }
208 |
209 | eValue, err := tree.Get(key)
210 | if err != nil {
211 | return
212 | }
213 |
214 | result, err = disk.Decrypt(eValue)
215 | if err != nil {
216 | return
217 | }
218 |
219 | return
220 | }
221 |
222 | // Delete a key-value in a Graviton tree
223 | func gravitonDeleteKey(disk *walletapi.Wallet_Disk, t string, key []byte) (err error) {
224 | if t == "" {
225 | err = fmt.Errorf("missing graviton tree input")
226 | return
227 | }
228 |
229 | if key == nil {
230 | err = fmt.Errorf("missing graviton key input")
231 | return
232 | }
233 |
234 | store, err := graviton.NewDiskStore(GetShard(disk))
235 | if err != nil {
236 | return
237 | }
238 |
239 | ss, err := store.LoadSnapshot(0)
240 | if err != nil {
241 | return
242 | }
243 |
244 | tree, err := ss.GetTree(t)
245 | if err != nil {
246 | return
247 | }
248 |
249 | err = tree.Delete(key)
250 | if err != nil {
251 | return
252 | }
253 |
254 | _, err = graviton.Commit(tree)
255 | if err != nil {
256 | return
257 | }
258 |
259 | return
260 | }
261 |
--------------------------------------------------------------------------------
/cmd/tela-cli/gnomon.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 | "strings"
8 |
9 | "github.com/civilware/Gnomon/indexer"
10 | "github.com/civilware/Gnomon/storage"
11 | "github.com/civilware/Gnomon/structures"
12 | "github.com/civilware/tela"
13 | "github.com/civilware/tela/logger"
14 | "github.com/civilware/tela/shards"
15 | "github.com/deroproject/derohe/globals"
16 | "github.com/deroproject/derohe/walletapi"
17 | )
18 |
19 | type gnomes struct {
20 | Indexer *indexer.Indexer
21 | fastsync bool
22 | parallelBlocks int
23 | }
24 |
25 | var gnomon gnomes
26 |
27 | const maxParallelBlocks = 10
28 |
29 | const gnomonSearchFilterDOC = `Function init() Uint64
30 | 10 IF EXISTS("owner") == 0 THEN GOTO 30
31 | 20 RETURN 1
32 | 30 STORE("owner", address())
33 | 50 STORE("docVersion", "1`
34 |
35 | const gnomonSearchFilterINDEX = `Function init() Uint64
36 | 10 IF EXISTS("owner") == 0 THEN GOTO 30
37 | 20 RETURN 1
38 | 30 STORE("owner", address())
39 | 50 STORE("telaVersion", "1`
40 |
41 | // Stop all indexers and close Gnomon
42 | func stopGnomon() (stopped bool) {
43 | if gnomon.Indexer != nil {
44 | gnomon.Indexer.Close()
45 | gnomon.Indexer = nil
46 | logger.Printf("[Gnomon] Closed all indexers\n")
47 | stopped = true
48 | }
49 |
50 | return
51 | }
52 |
53 | // Start the Gnomon indexer
54 | func startGnomon(endpoint string) {
55 | if walletapi.Connected {
56 | if gnomon.Indexer == nil {
57 | dir, _ := os.Getwd()
58 | path := filepath.Join(dir, "datashards", "gnomon")
59 |
60 | switch getNetworkInfo() {
61 | case shards.Value.Network.Testnet():
62 | path = filepath.Join(dir, "datashards", "gnomon_testnet")
63 | case shards.Value.Network.Simulator():
64 | path = filepath.Join(dir, "datashards", "gnomon_simulator")
65 | }
66 |
67 | boltDB, boltErr := storage.NewBBoltDB(path, "gnomon")
68 |
69 | gravDB, gravErr := storage.NewGravDB(path, "25ms")
70 |
71 | dbType := shards.GetDBType()
72 |
73 | var err error
74 | var height int64
75 | switch dbType {
76 | case "boltdb":
77 | if boltErr != nil {
78 | if !strings.HasPrefix(boltErr.Error(), "[") {
79 | boltErr = fmt.Errorf("[NewBBoltDB] %s", boltErr)
80 | }
81 | logger.Errorf("%s\n", boltErr)
82 | return
83 | }
84 |
85 | height, err = boltDB.GetLastIndexHeight()
86 | if err != nil {
87 | height = 0
88 | }
89 | default:
90 | if gravErr != nil {
91 | logger.Errorf("%s\n", gravErr)
92 | return
93 | }
94 |
95 | height, err = gravDB.GetLastIndexHeight()
96 | if err != nil {
97 | height = 0
98 | }
99 | }
100 |
101 | exclusions := []string{"bb43c3eb626ee767c9f305772a6666f7c7300441a0ad8538a0799eb4f12ebcd2"}
102 | filter := []string{gnomonSearchFilterDOC, gnomonSearchFilterINDEX}
103 |
104 | // Fastsync Config
105 | config := &structures.FastSyncConfig{
106 | Enabled: gnomon.fastsync,
107 | SkipFSRecheck: false,
108 | ForceFastSync: true,
109 | ForceFastSyncDiff: 100,
110 | NoCode: false,
111 | }
112 |
113 | gnomon.Indexer = indexer.NewIndexer(gravDB, boltDB, dbType, filter, height, endpoint, "daemon", false, false, config, exclusions)
114 |
115 | indexer.InitLog(globals.Arguments, os.Stdout)
116 |
117 | go gnomon.Indexer.StartDaemonMode(gnomon.parallelBlocks)
118 |
119 | logger.Printf("[Gnomon] Scan Status: [%d / %d]\n", gnomon.Indexer.LastIndexedHeight, walletapi.Get_Daemon_Height())
120 | }
121 | }
122 | }
123 |
124 | // Method of Gnomon GetAllOwnersAndSCIDs() where DB type is defined by Indexer.DBType
125 | func (g *gnomes) GetAllOwnersAndSCIDs() (scids map[string]string) {
126 | switch g.Indexer.DBType {
127 | case "gravdb":
128 | return g.Indexer.GravDBBackend.GetAllOwnersAndSCIDs()
129 | case "boltdb":
130 | return g.Indexer.BBSBackend.GetAllOwnersAndSCIDs()
131 | default:
132 | return
133 | }
134 | }
135 |
136 | // Method of Gnomon GetAllSCIDVariableDetails() where DB type is defined by Indexer.DBType
137 | func (g *gnomes) GetAllSCIDVariableDetails(scid string) (vars []*structures.SCIDVariable) {
138 | switch g.Indexer.DBType {
139 | case "gravdb":
140 | return g.Indexer.GravDBBackend.GetAllSCIDVariableDetails(scid)
141 | case "boltdb":
142 | return g.Indexer.BBSBackend.GetAllSCIDVariableDetails(scid)
143 | default:
144 | return
145 | }
146 | }
147 |
148 | // Method of Gnomon GetSCIDValuesByKey() where DB type is defined by Indexer.DBType
149 | func (g *gnomes) GetSCIDValuesByKey(scid string, key interface{}) (valuesstring []string, valuesuint64 []uint64) {
150 | switch g.Indexer.DBType {
151 | case "gravdb":
152 | return g.Indexer.GravDBBackend.GetSCIDValuesByKey(scid, key, g.Indexer.ChainHeight, true)
153 | case "boltdb":
154 | return g.Indexer.BBSBackend.GetSCIDValuesByKey(scid, key, g.Indexer.ChainHeight, true)
155 | default:
156 | return
157 | }
158 | }
159 |
160 | // Method of Gnomon GetSCIDKeysByValue() where DB type is defined by Indexer.DBType
161 | func (g *gnomes) GetSCIDKeysByValue(scid string, key interface{}) (valuesstring []string, valuesuint64 []uint64) {
162 | switch g.Indexer.DBType {
163 | case "gravdb":
164 | return g.Indexer.GravDBBackend.GetSCIDKeysByValue(scid, key, g.Indexer.ChainHeight, true)
165 | case "boltdb":
166 | return g.Indexer.BBSBackend.GetSCIDKeysByValue(scid, key, g.Indexer.ChainHeight, true)
167 | default:
168 | return
169 | }
170 | }
171 |
172 | // Get the "owner" store from a SCID
173 | func (g *gnomes) GetOwnerAddress(scid string) (owner string) {
174 | if g.Indexer == nil {
175 | return
176 | }
177 |
178 | address, _ := g.GetSCIDValuesByKey(scid, "owner")
179 | if address != nil && address[0] != "anon" {
180 | owner = address[0]
181 | }
182 |
183 | return
184 | }
185 |
186 | // Method of Gnomon Indexer.AddSCIDToIndex() configured for TELA-CLI
187 | func (g *gnomes) AddSCIDToIndex(scids map[string]*structures.FastSyncImport) error {
188 | return g.Indexer.AddSCIDToIndex(scids, false, false)
189 | }
190 |
191 | // Manually add SCID(s) to Gnomon index if they can be validated as TELA contracts
192 | func (t *tela_cli) addToIndex(scids []string) (err error) {
193 | for _, scid := range scids {
194 | if len(scid) == 64 {
195 | _, err := tela.GetDOCInfo(scid, t.endpoint)
196 | if err != nil {
197 | _, errr := tela.GetINDEXInfo(scid, t.endpoint)
198 | if errr != nil {
199 | logger.Errorf("[%s] GetDOCInfo: %s\n", appName, err)
200 | logger.Errorf("[%s] GetINDEXInfo: %s\n", appName, errr)
201 | return fmt.Errorf("could not validate %s as TELA INDEX or DOC", scid)
202 | }
203 | }
204 | }
205 | }
206 |
207 | filters := gnomon.Indexer.SearchFilter
208 | gnomon.Indexer.SearchFilter = []string{}
209 | scidsToAdd := map[string]*structures.FastSyncImport{}
210 |
211 | for _, sc := range scids {
212 | scidsToAdd[sc] = &structures.FastSyncImport{}
213 | }
214 |
215 | err = gnomon.AddSCIDToIndex(scidsToAdd)
216 | gnomon.Indexer.SearchFilter = filters
217 |
218 | return
219 | }
220 |
221 | // Get the nameHdr value for a smart contract
222 | func getNameHdr(scid string) (name string) {
223 | nameHdr, _ := gnomon.GetSCIDValuesByKey(scid, tela.HEADER_NAME_V2.Trim())
224 | if nameHdr == nil {
225 | nameHdr, _ = gnomon.GetSCIDValuesByKey(scid, tela.HEADER_NAME.Trim())
226 | if nameHdr == nil {
227 | nameHdr = append(nameHdr, "?")
228 | }
229 | }
230 |
231 | return nameHdr[0]
232 | }
233 |
--------------------------------------------------------------------------------
/shards/shards.go:
--------------------------------------------------------------------------------
1 | package shards
2 |
3 | import (
4 | "crypto/sha1"
5 | "fmt"
6 | "os"
7 | "path/filepath"
8 | "sync"
9 |
10 | "github.com/deroproject/derohe/walletapi"
11 | )
12 |
13 | type dbstore struct {
14 | path string
15 | dbType string
16 | sync.RWMutex
17 | }
18 |
19 | var shards dbstore
20 | var dbTypes = []string{"gravdb", "boltdb"}
21 |
22 | // Initialize package defaults and datashard storage location
23 | func init() {
24 | shards.dbType = "gravdb"
25 | dir, err := os.Getwd()
26 | if err != nil {
27 | fmt.Printf("[shards] %s\n", err)
28 | // Fallback location
29 | shards.path = "datashards"
30 | } else {
31 | shards.path = filepath.Join(dir, "datashards")
32 | }
33 | }
34 |
35 | // SetPath can be used to set a custom path for datashard storage
36 | func SetPath(path string) (datashards string, err error) {
37 | dir := filepath.Dir(path)
38 | if _, err = os.Stat(dir); os.IsNotExist(err) {
39 | err = fmt.Errorf("%s does not exists", path)
40 | return
41 | }
42 |
43 | datashards = filepath.Join(path, "datashards")
44 | shards.Lock()
45 | shards.path = datashards
46 | shards.Unlock()
47 |
48 | return
49 | }
50 |
51 | // Get the current datashards storage path
52 | func GetPath() string {
53 | shards.RLock()
54 | defer shards.RUnlock()
55 |
56 | return shards.path
57 | }
58 |
59 | // Is db a valid type for datashard storage
60 | func IsValidDBType(db string) bool {
61 | shards.RLock()
62 | defer shards.RUnlock()
63 |
64 | for _, t := range dbTypes {
65 | if db == t {
66 | return true
67 | }
68 | }
69 |
70 | return false
71 | }
72 |
73 | // Set datashards DB type if IsValidDBType
74 | func SetDBType(db string) (err error) {
75 | shards.Lock()
76 | defer shards.Unlock()
77 |
78 | switch db {
79 | case "gravdb":
80 | shards.dbType = db
81 | case "boltdb":
82 | shards.dbType = db
83 | default:
84 | return fmt.Errorf("unknown db type %q", db)
85 | }
86 |
87 | return
88 | }
89 |
90 | // Get datashards DB type
91 | func GetDBType() string {
92 | shards.RLock()
93 | defer shards.RUnlock()
94 |
95 | return shards.dbType
96 | }
97 |
98 | // Get a datashard's path, synced with Engram's DB structure
99 | func GetShard(disk *walletapi.Wallet_Disk) (result string) {
100 | dir := GetPath()
101 | if disk == nil {
102 | result = filepath.Join(dir, "settings")
103 | } else {
104 | address := disk.GetAddress().String()
105 | result = filepath.Join(dir, fmt.Sprintf("%x", sha1.Sum([]byte(address))))
106 | }
107 |
108 | return
109 | }
110 |
111 | // Encrypt a key-value and then store it in datashards
112 | func StoreEncryptedValue(disk *walletapi.Wallet_Disk, t string, key, value []byte) (err error) {
113 | db := GetDBType()
114 | switch db {
115 | case "gravdb":
116 | err = gravitonStoreEncryptedValue(disk, t, key, value)
117 | case "boltdb":
118 | err = boltStoreEncryptedValue(disk, t, key, value)
119 | default:
120 | err = fmt.Errorf("unknown db type %q", db)
121 | }
122 |
123 | return
124 | }
125 |
126 | // Store a key-value in datashards
127 | func StoreValue(t string, key, value []byte) (err error) {
128 | db := GetDBType()
129 | switch db {
130 | case "gravdb":
131 | err = gravitonStoreValue(t, key, value)
132 | case "boltdb":
133 | err = boltStoreValue(t, key, value)
134 | default:
135 | err = fmt.Errorf("unknown db type %q", db)
136 | }
137 |
138 | return
139 | }
140 |
141 | // Get a key-value from datashards
142 | func GetValue(t string, key []byte) (result []byte, err error) {
143 | db := GetDBType()
144 | switch db {
145 | case "gravdb":
146 | result, err = gravitonGetValue(t, key)
147 | case "boltdb":
148 | result, err = boltGetValue(t, key)
149 | default:
150 | err = fmt.Errorf("unknown db type %q", db)
151 | }
152 |
153 | return
154 | }
155 |
156 | // Store a key-value setting in datashards
157 | func StoreSettingsValue(key, value []byte) (err error) {
158 | db := GetDBType()
159 | switch db {
160 | case "gravdb":
161 | err = gravitonStoreSettingsValue(key, value)
162 | case "boltdb":
163 | err = boltStoreSettingsValue(key, value)
164 | default:
165 | err = fmt.Errorf("unknown db type %q", db)
166 | }
167 |
168 | return
169 | }
170 |
171 | // Get a key-value setting from datashards
172 | func GetSettingsValue(key []byte) (result []byte, err error) {
173 | db := GetDBType()
174 | switch db {
175 | case "gravdb":
176 | result, err = gravitonGetSettingsValue(key)
177 | case "boltdb":
178 | result, err = boltGetSettingsValue(key)
179 | default:
180 | err = fmt.Errorf("unknown db type %q", db)
181 | }
182 |
183 | return
184 | }
185 |
186 | // Store endpoint value in datashards
187 | func StoreEndpoint(value string) (err error) {
188 | db := GetDBType()
189 | switch db {
190 | case "gravdb":
191 | err = gravitonStoreEndpoint([]byte(value))
192 | case "boltdb":
193 | err = boltStoreEndpoint([]byte(value))
194 | default:
195 | err = fmt.Errorf("unknown db type %q", db)
196 | }
197 |
198 | return
199 | }
200 |
201 | // Get endpoint value from datashards
202 | func GetEndpoint() (endpoint string, err error) {
203 | var result []byte
204 | db := GetDBType()
205 | switch db {
206 | case "gravdb":
207 | result, err = gravitonGetEndpoint()
208 | case "boltdb":
209 | result, err = boltGetEndpoint()
210 | default:
211 | err = fmt.Errorf("unknown db type %q", db)
212 | return
213 | }
214 |
215 | endpoint = string(result)
216 |
217 | return
218 | }
219 |
220 | // Store network value in datashards
221 | func StoreNetwork(value string) (err error) {
222 | if !isNetworkValid(value) {
223 | err = fmt.Errorf("invalid network")
224 | return
225 | }
226 |
227 | db := GetDBType()
228 | switch db {
229 | case "gravdb":
230 | err = gravitonStoreNetwork([]byte(value))
231 | case "boltdb":
232 | err = boltStoreNetwork([]byte(value))
233 | default:
234 | err = fmt.Errorf("unknown db type %q", db)
235 | }
236 |
237 | return
238 | }
239 |
240 | // Get network value from datashards
241 | func GetNetwork() (network string, err error) {
242 | var result []byte
243 | db := GetDBType()
244 | switch db {
245 | case "gravdb":
246 | result, err = gravitonGetNetwork()
247 | case "boltdb":
248 | result, err = boltGetNetwork()
249 | default:
250 | err = fmt.Errorf("unknown db type %q", db)
251 | return
252 | }
253 |
254 | network = string(result)
255 |
256 | return
257 | }
258 |
259 | // Get an encrypted key-value from datashards and then decrypt it
260 | func GetEncryptedValue(disk *walletapi.Wallet_Disk, t string, key []byte) (result []byte, err error) {
261 | db := GetDBType()
262 | switch db {
263 | case "gravdb":
264 | result, err = gravitonGetEncryptedValue(disk, t, key)
265 | case "boltdb":
266 | result, err = boltGetEncryptedValue(disk, t, key)
267 | default:
268 | err = fmt.Errorf("unknown db type %q", db)
269 | }
270 |
271 | return
272 | }
273 |
274 | // Delete a key-value from datashards
275 | func DeleteKey(disk *walletapi.Wallet_Disk, t string, key []byte) (err error) {
276 | db := GetDBType()
277 | switch db {
278 | case "gravdb":
279 | err = gravitonDeleteKey(disk, t, key)
280 | case "boltdb":
281 | err = boltDeleteKey(disk, t, key)
282 | default:
283 | err = fmt.Errorf("unknown db type %q", db)
284 | }
285 |
286 | return
287 | }
288 |
289 | // Delete a key-value setting from datashards
290 | func DeleteSettingsKey(key []byte) (err error) {
291 | db := GetDBType()
292 | switch db {
293 | case "gravdb":
294 | err = gravitonDeleteKey(nil, "settings", key)
295 | case "boltdb":
296 | err = boltDeleteKey(nil, "settings", key)
297 | default:
298 | err = fmt.Errorf("unknown db type %q", db)
299 | }
300 |
301 | return
302 | }
303 |
--------------------------------------------------------------------------------
/TELA-INDEX-1/README.md:
--------------------------------------------------------------------------------
1 | # TELA-INDEX-1 - TELA Decentralized Web Standard
2 |
3 | ## Introduction
4 | TELA introduces a standard for decentralized browser-based applications that can be executed locally, eliminating the reliance on third-party servers.
5 |
6 | This portion of the documentation will focus on `TELA-INDEX-1`. This is the recommended starting point for learning about installing TELA applications and content.
7 |
8 | ## Getting Started with TELA-INDEX-1
9 |
10 | ### Table of Contents
11 | - [Initialization](#initialization)
12 | - [TELA-INDEX-1 Template](#tela-index-1-template)
13 | - [Utilization](#utilization)
14 | - [Install TELA-INDEX-1](#install-tela-index-1)
15 | - [Update TELA-INDEX-1](#update-tela-index-1)
16 | - [Rate TELA-INDEX-1](#rate-tela-index-1)
17 | - [TELA-DOC-1](../TELA-DOC-1/README.md)
18 |
19 | ### Initialization
20 | It is recommended to use a compliant host application such as [TELA-CLI](../cmd/tela-cli/README.md) when installing a `TELA-INDEX-1`, which will automate the process and help to avoid errors during installation. To deploy a `TELA-INDEX-1` manually, developers can fill out the input fields inside of `InitializePrivate()`. The minimum requirement for deployment is a valid `TELA-DOC-1` SCID, which must be input as the "DOC1" STORE value, and will serve as the entrypoint for the TELA application.
21 |
22 | ```go
23 | Function InitializePrivate() Uint64
24 | ...
25 | // Input fields starts at line 30
26 | 30 STORE("var_header_name", "App Name") // var_header_name defines the name of the TELA application.
27 | 31 STORE("var_header_description", "A TELA App") // var_header_description defines the description of the TELA application.
28 | 32 STORE("var_header_icon", "https://raw.githubusercontent.com/civilware/.github/main/CVLWR.png") // var_header_icon defines the URL or SCID for the icon representing the TELA application. This should be of size 100x100.
29 | 33 STORE("dURL", "app.tela") // dURL is unique identifier for the TELA application, ex myapp.tela.
30 | 34 STORE("mods", "") // mods is an optional store and can be an empty string. Its usage is to store any tags for enabled TELA-MOD-1's within this smart contract, for more information on MODs reference the TELA-MOD-1 documentation.
31 | 40 STORE("DOC1", "a891299086d218840d1eb71ae759ddc08f1e85cbf35801cc34ef64b4b07939c9") // DOC#s are installed TELA-DOC-1 SCIDs to be used in this TELA application. DOC1 will be used as the entrypoint for the application, a valid DOC1 k/v store is the minimum requirement for a TELA application.
32 | 41 STORE("DOC2", "b891299086d218840d1eb71ae759ddc08f1e85cbf35801cc34ef64b4b07939c8") // Further DOCs can be added/removed as required. All DOC stores that are used on the contract must have a valid SCID value otherwise the INDEX will not be validated when serving.
33 | // For any further DOCs needed, make sure the smart contract line number and DOC# increment++
34 | // 42 STORE("DOC3", "c891299086d218840d1eb71ae759ddc08f1e85cbf35801cc34ef64b4b07939c7")
35 | // 43 STORE("DOC4", "d891299086d218840d1eb71ae759ddc08f1e85cbf35801cc34ef64b4b07939c6")
36 | // ..
37 | // Input fields end at the last DOC#
38 | ...
39 | 1000 RETURN 0
40 | End Function
41 | ```
42 | It is recommended to keep `TELA-INDEX-1` contracts below 9KB total file size if updating is to be required at any point in the future. If staying within the 9KB size limit, developers are able to embed ~90 (may vary slightly depending on header values) `TELA-DOC-1` SCIDs into a single `TELA-INDEX-1` and maintain its ability to be updated with more DOCs than it was originally installed with. Current test show the maximum limit of DOCs that a `TELA-INDEX-1` can be successfully installed with is ~120 (may vary slightly depending on header values). Library usage and DocShard creation are documented within the `TELA-DOC-1` section and can increase the total capacity beyond these figures.
43 |
44 | ### TELA-INDEX-1 Template
45 | * [TELA-INDEX-1](TELA-INDEX-1.bas)
46 |
47 | The majority of comments on the smart contract template have been removed to utilize the maximum allowable contract space, refer to the above [Initialization](#initialization) documentation for guidance.
48 |
49 | ### Utilization
50 | It is in your best interest to always run a getgasestimate ahead of any direct curls below. This will 1) reduce your errors and 2) ensure you are utilizing the proper amount for fees from your specific parameters and ringsize.
51 |
52 | #### Install TELA-INDEX-1
53 | ```json
54 | curl --request POST --data-binary @TELA-INDEX-1.bas http://127.0.0.1:30000/install_sc;
55 | ```
56 |
57 | The following manual transaction portions will be split with a getgasestimate example call first, and then the follow-up with the respective fees that were present in that exact scenario. It's up to you to modify the fees parameter to reflect the 'gasstorage' return of getgasestimate.
58 |
59 | #### Update TELA-INDEX-1
60 | Publicly deployed `TELA-INDEX-1` contracts can be updated by their owners. If a `TELA-INDEX-1` is deployed anonymously (*ringsize above 2*) it is immutable. Updating allows for creators to make changes to their applications and content. Each execution of a `TELA-INDEX-1` smart contracts UpdateCode function will store its TXID as a uint64 key in the smart contract allowing for past states to be retrieved from mutable contracts.
61 |
62 | - GetGasEstimate
63 | ```json
64 | curl -X POST\
65 | http://127.0.0.1:20000/json_rpc\
66 | -H 'content-type: application/json'\
67 | -d '{
68 | "jsonrpc": "2.0",
69 | "id": "1",
70 | "method": "DERO.GetGasEstimate",
71 | "params": {
72 | "transfers": [],
73 | "signer": "deto1qyre7td6x9r88y4cavdgpv6k7lvx6j39lfsx420hpvh3ydpcrtxrxqg8v8e3z",
74 | "sc_rpc": [
75 | {
76 | "name": "SC_ACTION",
77 | "datatype": "U",
78 | "value": 0
79 | },
80 | {
81 | "name": "SC_ID",
82 | "datatype": "H",
83 | "value": "ce25b92083f089357d72295f4cf51cc58fed7439500792b94c85244f1067279e"
84 | },
85 | {
86 | "name": "entrypoint",
87 | "datatype": "S",
88 | "value": "UpdateCode"
89 | },
90 | {
91 | "name": "mods",
92 | "datatype": "S",
93 | "value": ""
94 | },
95 | {
96 | "name": "code",
97 | "datatype": "S",
98 | "value": "NEW_TELA_INDEX_SC_CODE_GOES_HERE"
99 | }
100 | ]
101 | }
102 | }'
103 | ```
104 |
105 | - Txn
106 | ```json
107 | curl -X POST\
108 | http://127.0.0.1:30000/json_rpc\
109 | -H 'content-type: application/json'\
110 | -d '{
111 | "jsonrpc": "2.0",
112 | "id": "0",
113 | "method": "Transfer",
114 | "params": {
115 | "ringsize": 2,
116 | "fees": 320,
117 | "sc_rpc": [
118 | {
119 | "name": "entrypoint",
120 | "datatype": "S",
121 | "value": "UpdateCode"
122 | },
123 | {
124 | "name": "mods",
125 | "datatype": "S",
126 | "value": ""
127 | },
128 | {
129 | "name": "code",
130 | "datatype": "S",
131 | "value": "NEW_TELA_INDEX_SC_CODE_GOES_HERE"
132 | },
133 | {
134 | "name": "SC_ACTION",
135 | "datatype": "U",
136 | "value": 0
137 | },
138 | {
139 | "name": "SC_ID",
140 | "datatype": "H",
141 | "value": "ce25b92083f089357d72295f4cf51cc58fed7439500792b94c85244f1067279e"
142 | }
143 | ]
144 | }
145 | }'
146 | ```
147 |
148 | #### Rate TELA-INDEX-1
149 | TELA content can be rated by any DERO wallet that executes the Rate function on the smart contract. The following Rate command also applies to `TELA-DOC-1` contracts.
150 |
151 | - GetGasEstimate
152 | ```json
153 | curl -X POST\
154 | http://127.0.0.1:20000/json_rpc\
155 | -H 'content-type: application/json'\
156 | -d '{
157 | "jsonrpc": "2.0",
158 | "id": "1",
159 | "method": "DERO.GetGasEstimate",
160 | "params": {
161 | "transfers": [],
162 | "signer": "deto1qyre7td6x9r88y4cavdgpv6k7lvx6j39lfsx420hpvh3ydpcrtxrxqg8v8e3z",
163 | "sc_rpc": [
164 | {
165 | "name": "SC_ACTION",
166 | "datatype": "U",
167 | "value": 0
168 | },
169 | {
170 | "name": "SC_ID",
171 | "datatype": "H",
172 | "value": "f2815b442d62a055e4bb8913167e3dbce3208f300d7006aaa3a2f127b06de29d"
173 | },
174 | {
175 | "name": "entrypoint",
176 | "datatype": "S",
177 | "value": "Rate"
178 | },
179 | {
180 | "name": "r",
181 | "datatype": "U",
182 | "value": 0
183 | }
184 | ]
185 | }
186 | }'
187 | ```
188 |
189 | - Txn
190 | ```json
191 | curl -X POST\
192 | http://127.0.0.1:30002/json_rpc\
193 | -H 'content-type: application/json'\
194 | -d '{
195 | "jsonrpc": "2.0",
196 | "id": "0",
197 | "method": "Transfer",
198 | "params": {
199 | "ringsize": 2,
200 | "fees": 90,
201 | "sc_rpc": [
202 | {
203 | "name": "entrypoint",
204 | "datatype": "S",
205 | "value": "Rate"
206 | },
207 | {
208 | "name": "r",
209 | "datatype": "U",
210 | "value": 0
211 | },
212 | {
213 | "name": "SC_ACTION",
214 | "datatype": "U",
215 | "value": 0
216 | },
217 | {
218 | "name": "SC_ID",
219 | "datatype": "H",
220 | "value": "f2815b442d62a055e4bb8913167e3dbce3208f300d7006aaa3a2f127b06de29d"
221 | }
222 | ]
223 | }
224 | }'
225 | ```
226 |
227 | ### TELA-DOC-1
228 | * [TELA-DOC-1](../TELA-DOC-1/README.md)
--------------------------------------------------------------------------------
/TELA-DOC-1/README.md:
--------------------------------------------------------------------------------
1 | # TELA-DOC-1 - TELA Decentralized Web Standard Document
2 |
3 | ## Introduction
4 | TELA introduces a standard for decentralized browser-based applications that can be executed locally, eliminating the reliance on third-party servers.
5 |
6 | This portion of the documentation will focus on `TELA-DOC-1`.
7 |
8 | `TELA-INDEX-1` info can be found [here](../TELA-INDEX-1/README.md) and it is recommended to have reviewed it prior to `TELA-DOC-1`.
9 |
10 | ## Creating TELA-DOC-1's
11 |
12 | ### Table of Contents
13 | - [Languages](#languages)
14 | - [Preparation](#preparation)
15 | - [Guidelines](#guidelines)
16 | - [JavaScript](#javascript)
17 | - [docTypes](#doctypes)
18 | - [Initialization](#initialization)
19 | - [TELA Libraries](#tela-libraries)
20 | - [Library Usage](#library-usage)
21 | - [Library Creation](#library-creation)
22 | - [DocShards](#docshards)
23 | - [DocShard Usage](#docshard-usage)
24 | - [DocShard Creation](#docshard-creation)
25 | - [Compression](#compression)
26 | - [TELA-DOC-1 Template](#tela-doc-1-template)
27 | - [Utilization](#utilization)
28 | - [Install TELA-DOC-1](#install-tela-doc-1)
29 | - [Rate TELA-DOC-1](#rate-tela-doc-1)
30 | - [TELA-INDEX-1](#tela-index-1)
31 |
32 | ### Languages
33 | The `civilware/tela` go package's accepted languages are:
34 |
35 | - HTML, JSON, JavaScript, CSS and Markdown.
36 | - A Static type is also defined to encompass desired files that are not listed as an accepted language such as asset or build files.
37 |
38 | ### Preparation
39 | Prepare all the code (or text) that will be required for the document. Ensure you are using a language accepted by the host application that will be serving this document. There are some minimal guidelines to be followed when preparing the code to be added to the `TELA-DOC-1` smart contract which will help reduce errors. Outside of these guidelines, syntax and formatting can be defined by the author of the document. References and execution can be assumed to perform as if code were being served from any public server.
40 |
41 | #### Guidelines
42 | - One `TELA-DOC-1` cannot exceed 20KB in total size.
43 | - If `/* */` multiline comments or non ASCII characters are used in the document code it should be encoded to avoid errors during contract installation. See [compression](#compression) for more details.
44 | - Example application code can be found in [tela_tests](../tela_tests/).
45 | - The dURL can be used to help indexers query details beyond the defined contract stores, examples:
46 | - Appending `.lib` to a dURL will mark it as a library for indexes such as in [TELA-CLI](../cmd/tela-cli/README.md).
47 | - DocShards append `.shard` and `.shards` to their DOC and INDEX dURLs respectively to indicate it is a shard or requires reconstruction.
48 |
49 | ##### JavaScript
50 | - Accurate origin URLs for web socket connections can be generated using:
51 |
52 | ```javascript
53 | const applicationData = {
54 | "url": "http://localhost:" + location.port,
55 | };
56 | ```
57 | #### docTypes
58 | ```go
59 | "TELA-STATIC-1"
60 | "TELA-HTML-1"
61 | "TELA-JSON-1"
62 | "TELA-CSS-1"
63 | "TELA-JS-1"
64 | "TELA-MD-1"
65 | "TELA-GO-1"
66 | ```
67 |
68 | ### Initialization
69 | It is recommended to use a compliant host application such as [TELA-CLI](../cmd/tela-cli/README.md) when installing a `TELA-DOC-1`, which will automate the process and help to avoid errors during installation. To deploy a `TELA-DOC-1` manually, developers can fill out the input fields inside of `InitializePrivate()` and input the document code in the designated multiline comment section at the bottom of the smart contract template.
70 |
71 | ```go
72 | Function InitializePrivate() Uint64
73 | ...
74 | // Input fields starts at line 30
75 | 30 STORE("var_header_name", "index.html") // var_header_name defines the name of the TELA document.
76 | 31 STORE("var_header_description", "A HTML index") // var_header_description defines the description of the TELA document.
77 | 32 STORE("var_header_icon", "https://raw.githubusercontent.com/civilware/.github/main/CVLWR.png") // var_header_icon defines the URL or SCID for the icon representing the TELA document. This should be of size 100x100.
78 | 33 STORE("dURL", "app.tela") // dURL is a unique identifier for the TELA document likely linking to the TELA-INDEX-1 where this document is being used or to its corresponding library.
79 | 34 STORE("docType", "TELA-HTML-1") // docType is the language or file type being used, ex TELA-JS-1, TELA-CSS-1... see docTypes list for all store values
80 | 35 STORE("subDir", "") // subDir adds this file to a sub directory, it can be left empty if file location is in root directory, separators should always be / ex: sub1/sub2/sub3
81 | 36 STORE("fileCheckC", "1c37f9e61f15a9526ba680dce0baa567e642ca2cd0ddea71649dab415dad8cb2") // C and S from DERO signature
82 | 37 STORE("fileCheckS", "7e642ca2cd0ddea71649dab415dad8cb21c37f9e61f15a9526ba680dce0baa56") // signature is of the docType code in the multiline comment section
83 | // Input fields ends at line 37
84 | 100 RETURN 0
85 | End Function
86 | ...
87 | /*
88 |
89 |
90 |
91 |
92 |
93 | TELA-DOC-1 Template
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | */
103 | ```
104 |
105 | ### TELA Libraries
106 | TELA libraries are in essence any development library that is installed in TELA format. A TELA library consists of `TELA-DOC-1` contracts that have been designed for universal use. Once installed, these libraries are intended to be application-agnostic, allowing their functionality to be leveraged by any TELA application. This promotes code reuse for faster development, helps to reduce chain bloat, drives community-tested solutions, and helps maintain consistency across different projects. To assist developers in discovering and utilizing installed libraries, some indexes like [TELA-CLI](../cmd/tela-cli/README.md) provide specific queries to make it easier to find and propagate universal libraries within the TELA ecosystem.
107 |
108 | #### Library Usage
109 | At its core, a TELA library is a `TELA-DOC-1` contract or a collection of them. This means that using a library follows the same principles as using any `TELA-DOC-1`.
110 |
111 | To consume a library:
112 | - Identify the SCID of the library you want to use in your application.
113 | - While in the development stages, libraries can be cloned to get a local copy of the files so its functions and variables can be referenced and tested in the application before it is installed on-chain.
114 | - When development is finished, input any `TELA-INDEX-1` or `TELA-DOC-1` library SCID(s) used while developing, into your applications `TELA-INDEX-1` contract before it is installed. The application will now contain those libraries when served.
115 |
116 | #### Library Creation
117 | The codebase being installed as a TELA library might exceed the total size of a single `TELA-DOC-1` smart contract. In this case multiple `TELA-DOC-1`'s can be deployed and embedded within a `TELA-INDEX-1` to create a multi-part TELA library which can be referenced by a single SCID for further use.
118 |
119 | To create a library:
120 | - Write all the docType code needed for the `TELA-DOC-1` smart contract or contracts.
121 | - Install the `TELA-DOC-1` contracts, ensuring that all the dURLs match and have a `.lib` suffix.
122 | - Optionally, all the `TELA-DOC-1` contracts can then be embedded within a `TELA-INDEX-1` to reference it with a single SCID.
123 |
124 | ### DocShards
125 | DocShards provide developers with an alternative method for packaging TELA content. They are similar to libraries in their construction; however, embedded DocShards are recreated as a single file when cloned or served. This allows a single piece of TELA content to exceed the smart contract installation maximum size. Additionally, DocShards can be embedded into libraries to enhance their utility.
126 |
127 | #### DocShard Usage
128 | Like TELA libraries, DocShards consist of a collection of `TELA-DOC-1` contracts. It is important to note that a DocShard cannot serve as the entrypoint of a `TELA-INDEX-1`.
129 |
130 | To consume a DocShard:
131 | - Identify the SCID of the DocShard you want to use in your application.
132 | - While in the development stages, DocShards can be cloned to get a local copy of the file so its functions and variables can be referenced and tested in the application before it is installed on-chain. DocShards are cloned to `dURL/nameHdr` in the target directory.
133 | - When development is finished, input any `TELA-INDEX-1` DocShard SCID(s) used while developing, into your applications `TELA-INDEX-1` contract before it is installed. The application will now contain those DocShards when served.
134 |
135 | #### DocShard Creation
136 | To avoid formatting errors during the cloning or serving of content, it is recommended to use the `civilware/tela` go package when creating DocShards. `TELA-CLI` has extended the package's tooling to simplify the creation of DocShards. Creation is limited to files containing only ASCII characters.
137 |
138 | To create a DocShard:
139 | - Write the docType code that will be recreated as a single source file.
140 | - Using the appropriate tools, create the DocShard smart contracts from the source file.
141 | - Install the `TELA-DOC-1` DocShard contracts. The `.shard` tag should be appended to the DOC dURL's to indicate they are DocShards.
142 | - Embed all installed `TELA-DOC-1` DocShard contracts into a `TELA-INDEX-1` and append `.shards` to its dURL to signify it requires reconstruction.
143 |
144 | #### Compression
145 | Document code can be compressed and encoded to maximize the storage capacity of a single DOC. Additionally, applying compression when creating DocShards can reduce the number of shards needed for large files. Adding the compression extension to the end of the nameHdr will signal that the content is compressed, ensuring it is processed appropriately when served.
146 |
147 | For optimal results, it is recommended to use a host application such as `TELA-CLI`, which automates the compression process during DOC installation. The `civilware/tela` package currently supports the following compression formats:
148 |
149 | | Format | Extension |
150 | |--------------|-----------|
151 | | gzip | `.gz` |
152 |
153 | ### TELA-DOC-1 Template
154 | * [TELA-DOC-1](TELA-DOC-1.bas)
155 |
156 | The majority of comments on the smart contract template have been removed to utilize the maximum allowable contract space, refer to the above [Initialization](#initialization) documentation for guidance.
157 |
158 | ### Utilization
159 | It is in your best interest to always run a getgasestimate ahead of any direct curls below. This will 1) reduce your errors and 2) ensure you are utilizing the proper amount for fees from your specific parameters and ringsize.
160 |
161 | #### Install TELA-DOC-1
162 | ```json
163 | curl --request POST --data-binary @TELA-DOC-1.bas http://127.0.0.1:30000/install_sc;
164 | ```
165 |
166 | #### Rate TELA-DOC-1
167 | Rating `TELA-DOC-1` contracts can be done the same as `TELA-INDEX-1`'s. Manual rating procedures can be found [here](../TELA-INDEX-1/README.md#rate-tela-index-1).
168 |
169 | ### TELA-INDEX-1
170 | * [TELA-INDEX-1](../TELA-INDEX-1/README.md)
--------------------------------------------------------------------------------
/tela_tests/app1/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TELA Demo Application
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
46 |
47 |
48 |
TELA-DWEB
49 |
50 |
51 |
TELA Decentralized Web Standard Demo
52 |
53 |
54 | This demo shows how a TELA application can be connected to a DERO wallet through XSWD.
55 | Below are various request examples that can be tried if connected to a wallet.
56 | The source code for this application can be found
57 | here.
59 |